psCharStrings.py revision 30e691edd056ba22fa8970280e986747817bec3d
1"""psCharStrings.py -- module implementing various kinds of CharStrings: 2CFF dictionary data and Type1/Type2 CharStrings. 3""" 4 5from __future__ import print_function 6from fontTools.misc.py23 import * 7import struct 8 9 10DEBUG = 0 11 12 13t1OperandEncoding = [None] * 256 14t1OperandEncoding[0:32] = (32) * ["do_operator"] 15t1OperandEncoding[32:247] = (247 - 32) * ["read_byte"] 16t1OperandEncoding[247:251] = (251 - 247) * ["read_smallInt1"] 17t1OperandEncoding[251:255] = (255 - 251) * ["read_smallInt2"] 18t1OperandEncoding[255] = "read_longInt" 19assert len(t1OperandEncoding) == 256 20 21t2OperandEncoding = t1OperandEncoding[:] 22t2OperandEncoding[28] = "read_shortInt" 23t2OperandEncoding[255] = "read_fixed1616" 24 25cffDictOperandEncoding = t2OperandEncoding[:] 26cffDictOperandEncoding[29] = "read_longInt" 27cffDictOperandEncoding[30] = "read_realNumber" 28cffDictOperandEncoding[255] = "reserved" 29 30 31realNibbles = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 32 '.', 'E', 'E-', None, '-'] 33realNibblesDict = {} 34for _i in range(len(realNibbles)): 35 realNibblesDict[realNibbles[_i]] = _i 36 37 38class ByteCodeBase: 39 40 def read_byte(self, b0, data, index): 41 return b0 - 139, index 42 43 def read_smallInt1(self, b0, data, index): 44 b1 = ord(data[index]) 45 return (b0-247)*256 + b1 + 108, index+1 46 47 def read_smallInt2(self, b0, data, index): 48 b1 = ord(data[index]) 49 return -(b0-251)*256 - b1 - 108, index+1 50 51 def read_shortInt(self, b0, data, index): 52 bin = data[index] + data[index+1] 53 value, = struct.unpack(">h", bin) 54 return value, index+2 55 56 def read_longInt(self, b0, data, index): 57 bin = data[index] + data[index+1] + data[index+2] + data[index+3] 58 value, = struct.unpack(">l", bin) 59 return value, index+4 60 61 def read_fixed1616(self, b0, data, index): 62 bin = data[index] + data[index+1] + data[index+2] + data[index+3] 63 value, = struct.unpack(">l", bin) 64 return value / 65536.0, index+4 65 66 def read_realNumber(self, b0, data, index): 67 number = '' 68 while True: 69 b = ord(data[index]) 70 index = index + 1 71 nibble0 = (b & 0xf0) >> 4 72 nibble1 = b & 0x0f 73 if nibble0 == 0xf: 74 break 75 number = number + realNibbles[nibble0] 76 if nibble1 == 0xf: 77 break 78 number = number + realNibbles[nibble1] 79 return float(number), index 80 81 82def buildOperatorDict(operatorList): 83 oper = {} 84 opc = {} 85 for item in operatorList: 86 if len(item) == 2: 87 oper[item[0]] = item[1] 88 else: 89 oper[item[0]] = item[1:] 90 if isinstance(item[0], tuple): 91 opc[item[1]] = item[0] 92 else: 93 opc[item[1]] = (item[0],) 94 return oper, opc 95 96 97t2Operators = [ 98# opcode name 99 (1, 'hstem'), 100 (3, 'vstem'), 101 (4, 'vmoveto'), 102 (5, 'rlineto'), 103 (6, 'hlineto'), 104 (7, 'vlineto'), 105 (8, 'rrcurveto'), 106 (10, 'callsubr'), 107 (11, 'return'), 108 (14, 'endchar'), 109 (16, 'blend'), 110 (18, 'hstemhm'), 111 (19, 'hintmask'), 112 (20, 'cntrmask'), 113 (21, 'rmoveto'), 114 (22, 'hmoveto'), 115 (23, 'vstemhm'), 116 (24, 'rcurveline'), 117 (25, 'rlinecurve'), 118 (26, 'vvcurveto'), 119 (27, 'hhcurveto'), 120# (28, 'shortint'), # not really an operator 121 (29, 'callgsubr'), 122 (30, 'vhcurveto'), 123 (31, 'hvcurveto'), 124 ((12, 0), 'ignore'), # dotsection. Yes, there a few very early OTF/CFF 125 # fonts with this deprecated operator. Just ignore it. 126 ((12, 3), 'and'), 127 ((12, 4), 'or'), 128 ((12, 5), 'not'), 129 ((12, 8), 'store'), 130 ((12, 9), 'abs'), 131 ((12, 10), 'add'), 132 ((12, 11), 'sub'), 133 ((12, 12), 'div'), 134 ((12, 13), 'load'), 135 ((12, 14), 'neg'), 136 ((12, 15), 'eq'), 137 ((12, 18), 'drop'), 138 ((12, 20), 'put'), 139 ((12, 21), 'get'), 140 ((12, 22), 'ifelse'), 141 ((12, 23), 'random'), 142 ((12, 24), 'mul'), 143 ((12, 26), 'sqrt'), 144 ((12, 27), 'dup'), 145 ((12, 28), 'exch'), 146 ((12, 29), 'index'), 147 ((12, 30), 'roll'), 148 ((12, 34), 'hflex'), 149 ((12, 35), 'flex'), 150 ((12, 36), 'hflex1'), 151 ((12, 37), 'flex1'), 152] 153 154 155def getIntEncoder(format): 156 if format == "cff": 157 fourByteOp = bytechr(29) 158 elif format == "t1": 159 fourByteOp = bytechr(255) 160 else: 161 assert format == "t2" 162 fourByteOp = None 163 164 def encodeInt(value, fourByteOp=fourByteOp, bytechr=bytechr, 165 pack=struct.pack, unpack=struct.unpack): 166 if -107 <= value <= 107: 167 code = bytechr(value + 139) 168 elif 108 <= value <= 1131: 169 value = value - 108 170 code = bytechr((value >> 8) + 247) + bytechr(value & 0xFF) 171 elif -1131 <= value <= -108: 172 value = -value - 108 173 code = bytechr((value >> 8) + 251) + bytechr(value & 0xFF) 174 elif fourByteOp is None: 175 # T2 only supports 2 byte ints 176 if -32768 <= value <= 32767: 177 code = bytechr(28) + pack(">h", value) 178 else: 179 # Backwards compatible hack: due to a previous bug in FontTools, 180 # 16.16 fixed numbers were written out as 4-byte ints. When 181 # these numbers were small, they were wrongly written back as 182 # small ints instead of 4-byte ints, breaking round-tripping. 183 # This here workaround doesn't do it any better, since we can't 184 # distinguish anymore between small ints that were supposed to 185 # be small fixed numbers and small ints that were just small 186 # ints. Hence the warning. 187 import sys 188 sys.stderr.write("Warning: 4-byte T2 number got passed to the " 189 "IntType handler. This should happen only when reading in " 190 "old XML files.\n") 191 code = bytechr(255) + pack(">l", value) 192 else: 193 code = fourByteOp + pack(">l", value) 194 return code 195 196 return encodeInt 197 198 199encodeIntCFF = getIntEncoder("cff") 200encodeIntT1 = getIntEncoder("t1") 201encodeIntT2 = getIntEncoder("t2") 202 203def encodeFixed(f, pack=struct.pack): 204 # For T2 only 205 return "\xff" + pack(">l", int(round(f * 65536))) 206 207def encodeFloat(f): 208 # For CFF only, used in cffLib 209 s = str(f).upper() 210 if s[:2] == "0.": 211 s = s[1:] 212 elif s[:3] == "-0.": 213 s = "-" + s[2:] 214 nibbles = [] 215 while s: 216 c = s[0] 217 s = s[1:] 218 if c == "E" and s[:1] == "-": 219 s = s[1:] 220 c = "E-" 221 nibbles.append(realNibblesDict[c]) 222 nibbles.append(0xf) 223 if len(nibbles) % 2: 224 nibbles.append(0xf) 225 d = bytechr(30) 226 for i in range(0, len(nibbles), 2): 227 d = d + bytechr(nibbles[i] << 4 | nibbles[i+1]) 228 return d 229 230 231class CharStringCompileError(Exception): pass 232 233 234class T2CharString(ByteCodeBase): 235 236 operandEncoding = t2OperandEncoding 237 operators, opcodes = buildOperatorDict(t2Operators) 238 239 def __init__(self, bytecode=None, program=None, private=None, globalSubrs=None): 240 if program is None: 241 program = [] 242 self.bytecode = bytecode 243 self.program = program 244 self.private = private 245 self.globalSubrs = globalSubrs if globalSubrs is not None else [] 246 247 def __repr__(self): 248 if self.bytecode is None: 249 return "<%s (source) at %x>" % (self.__class__.__name__, id(self)) 250 else: 251 return "<%s (bytecode) at %x>" % (self.__class__.__name__, id(self)) 252 253 def getIntEncoder(self): 254 return encodeIntT2 255 256 def getFixedEncoder(self): 257 return encodeFixed 258 259 def decompile(self): 260 if not self.needsDecompilation(): 261 return 262 subrs = getattr(self.private, "Subrs", []) 263 decompiler = SimpleT2Decompiler(subrs, self.globalSubrs) 264 decompiler.execute(self) 265 266 def draw(self, pen): 267 subrs = getattr(self.private, "Subrs", []) 268 extractor = T2OutlineExtractor(pen, subrs, self.globalSubrs, 269 self.private.nominalWidthX, self.private.defaultWidthX) 270 extractor.execute(self) 271 self.width = extractor.width 272 273 def compile(self): 274 if self.bytecode is not None: 275 return 276 assert self.program, "illegal CharString: decompiled to empty program" 277 assert self.program[-1] in ("endchar", "return", "callsubr", "callgsubr", 278 "seac"), "illegal CharString" 279 bytecode = [] 280 opcodes = self.opcodes 281 program = self.program 282 encodeInt = self.getIntEncoder() 283 encodeFixed = self.getFixedEncoder() 284 i = 0 285 end = len(program) 286 while i < end: 287 token = program[i] 288 i = i + 1 289 tp = type(token) 290 if tp == str: 291 try: 292 bytecode.extend(bytechr(b) for b in opcodes[token]) 293 except KeyError: 294 raise CharStringCompileError("illegal operator: %s" % token) 295 if token in ('hintmask', 'cntrmask'): 296 bytecode.append(program[i]) # hint mask 297 i = i + 1 298 elif tp == int: 299 bytecode.append(encodeInt(token)) 300 elif tp == float: 301 bytecode.append(encodeFixed(token)) 302 else: 303 assert 0, "unsupported type: %s" % tp 304 try: 305 bytecode = "".join(bytecode) 306 except TypeError: 307 print(bytecode) 308 raise 309 self.setBytecode(bytecode) 310 311 def needsDecompilation(self): 312 return self.bytecode is not None 313 314 def setProgram(self, program): 315 self.program = program 316 self.bytecode = None 317 318 def setBytecode(self, bytecode): 319 self.bytecode = bytecode 320 self.program = None 321 322 def getToken(self, index, 323 len=len, ord=ord, getattr=getattr, type=type, StringType=str): 324 if self.bytecode is not None: 325 if index >= len(self.bytecode): 326 return None, 0, 0 327 b0 = ord(self.bytecode[index]) 328 index = index + 1 329 code = self.operandEncoding[b0] 330 handler = getattr(self, code) 331 token, index = handler(b0, self.bytecode, index) 332 else: 333 if index >= len(self.program): 334 return None, 0, 0 335 token = self.program[index] 336 index = index + 1 337 isOperator = isinstance(token, StringType) 338 return token, isOperator, index 339 340 def getBytes(self, index, nBytes): 341 if self.bytecode is not None: 342 newIndex = index + nBytes 343 bytes = self.bytecode[index:newIndex] 344 index = newIndex 345 else: 346 bytes = self.program[index] 347 index = index + 1 348 assert len(bytes) == nBytes 349 return bytes, index 350 351 def do_operator(self, b0, data, index): 352 if b0 == 12: 353 op = (b0, ord(data[index])) 354 index = index+1 355 else: 356 op = b0 357 operator = self.operators[op] 358 return operator, index 359 360 def toXML(self, xmlWriter): 361 from fontTools.misc.textTools import num2binary 362 if self.bytecode is not None: 363 xmlWriter.dumphex(self.bytecode) 364 else: 365 index = 0 366 args = [] 367 while True: 368 token, isOperator, index = self.getToken(index) 369 if token is None: 370 break 371 if isOperator: 372 args = [str(arg) for arg in args] 373 if token in ('hintmask', 'cntrmask'): 374 hintMask, isOperator, index = self.getToken(index) 375 bits = [] 376 for byte in hintMask: 377 bits.append(num2binary(ord(byte), 8)) 378 hintMask = ''.join(bits) 379 line = ' '.join(args + [token, hintMask]) 380 else: 381 line = ' '.join(args + [token]) 382 xmlWriter.write(line) 383 xmlWriter.newline() 384 args = [] 385 else: 386 args.append(token) 387 388 def fromXML(self, name, attrs, content): 389 from fontTools.misc.textTools import binary2num, readHex 390 if attrs.get("raw"): 391 self.setBytecode(readHex(content)) 392 return 393 content = "".join(content) 394 content = content.split() 395 program = [] 396 end = len(content) 397 i = 0 398 while i < end: 399 token = content[i] 400 i = i + 1 401 try: 402 token = int(token) 403 except ValueError: 404 try: 405 token = float(token) 406 except ValueError: 407 program.append(token) 408 if token in ('hintmask', 'cntrmask'): 409 mask = content[i] 410 maskBytes = "" 411 for j in range(0, len(mask), 8): 412 maskBytes = maskBytes + bytechr(binary2num(mask[j:j+8])) 413 program.append(maskBytes) 414 i = i + 1 415 else: 416 program.append(token) 417 else: 418 program.append(token) 419 self.setProgram(program) 420 421 422t1Operators = [ 423# opcode name 424 (1, 'hstem'), 425 (3, 'vstem'), 426 (4, 'vmoveto'), 427 (5, 'rlineto'), 428 (6, 'hlineto'), 429 (7, 'vlineto'), 430 (8, 'rrcurveto'), 431 (9, 'closepath'), 432 (10, 'callsubr'), 433 (11, 'return'), 434 (13, 'hsbw'), 435 (14, 'endchar'), 436 (21, 'rmoveto'), 437 (22, 'hmoveto'), 438 (30, 'vhcurveto'), 439 (31, 'hvcurveto'), 440 ((12, 0), 'dotsection'), 441 ((12, 1), 'vstem3'), 442 ((12, 2), 'hstem3'), 443 ((12, 6), 'seac'), 444 ((12, 7), 'sbw'), 445 ((12, 12), 'div'), 446 ((12, 16), 'callothersubr'), 447 ((12, 17), 'pop'), 448 ((12, 33), 'setcurrentpoint'), 449] 450 451class T1CharString(T2CharString): 452 453 operandEncoding = t1OperandEncoding 454 operators, opcodes = buildOperatorDict(t1Operators) 455 456 def __init__(self, bytecode=None, program=None, subrs=None): 457 if program is None: 458 program = [] 459 self.bytecode = bytecode 460 self.program = program 461 self.subrs = subrs 462 463 def getIntEncoder(self): 464 return encodeIntT1 465 466 def getFixedEncoder(self): 467 def encodeFixed(value): 468 raise TypeError("Type 1 charstrings don't support floating point operands") 469 470 def decompile(self): 471 if self.program is not None: 472 return 473 program = [] 474 index = 0 475 while True: 476 token, isOperator, index = self.getToken(index) 477 if token is None: 478 break 479 program.append(token) 480 self.setProgram(program) 481 482 def draw(self, pen): 483 extractor = T1OutlineExtractor(pen, self.subrs) 484 extractor.execute(self) 485 self.width = extractor.width 486 487 488class SimpleT2Decompiler: 489 490 def __init__(self, localSubrs, globalSubrs): 491 self.localSubrs = localSubrs 492 self.localBias = calcSubrBias(localSubrs) 493 self.globalSubrs = globalSubrs 494 self.globalBias = calcSubrBias(globalSubrs) 495 self.reset() 496 497 def reset(self): 498 self.callingStack = [] 499 self.operandStack = [] 500 self.hintCount = 0 501 self.hintMaskBytes = 0 502 503 def execute(self, charString): 504 self.callingStack.append(charString) 505 needsDecompilation = charString.needsDecompilation() 506 if needsDecompilation: 507 program = [] 508 pushToProgram = program.append 509 else: 510 pushToProgram = lambda x: None 511 pushToStack = self.operandStack.append 512 index = 0 513 while True: 514 token, isOperator, index = charString.getToken(index) 515 if token is None: 516 break # we're done! 517 pushToProgram(token) 518 if isOperator: 519 handlerName = "op_" + token 520 if hasattr(self, handlerName): 521 handler = getattr(self, handlerName) 522 rv = handler(index) 523 if rv: 524 hintMaskBytes, index = rv 525 pushToProgram(hintMaskBytes) 526 else: 527 self.popall() 528 else: 529 pushToStack(token) 530 if needsDecompilation: 531 assert program, "illegal CharString: decompiled to empty program" 532 assert program[-1] in ("endchar", "return", "callsubr", "callgsubr", 533 "seac"), "illegal CharString" 534 charString.setProgram(program) 535 del self.callingStack[-1] 536 537 def pop(self): 538 value = self.operandStack[-1] 539 del self.operandStack[-1] 540 return value 541 542 def popall(self): 543 stack = self.operandStack[:] 544 self.operandStack[:] = [] 545 return stack 546 547 def push(self, value): 548 self.operandStack.append(value) 549 550 def op_return(self, index): 551 if self.operandStack: 552 pass 553 554 def op_endchar(self, index): 555 pass 556 557 def op_ignore(self, index): 558 pass 559 560 def op_callsubr(self, index): 561 subrIndex = self.pop() 562 subr = self.localSubrs[subrIndex+self.localBias] 563 self.execute(subr) 564 565 def op_callgsubr(self, index): 566 subrIndex = self.pop() 567 subr = self.globalSubrs[subrIndex+self.globalBias] 568 self.execute(subr) 569 570 def op_hstem(self, index): 571 self.countHints() 572 def op_vstem(self, index): 573 self.countHints() 574 def op_hstemhm(self, index): 575 self.countHints() 576 def op_vstemhm(self, index): 577 self.countHints() 578 579 def op_hintmask(self, index): 580 if not self.hintMaskBytes: 581 self.countHints() 582 self.hintMaskBytes = (self.hintCount + 7) / 8 583 hintMaskBytes, index = self.callingStack[-1].getBytes(index, self.hintMaskBytes) 584 return hintMaskBytes, index 585 586 op_cntrmask = op_hintmask 587 588 def countHints(self): 589 args = self.popall() 590 self.hintCount = self.hintCount + len(args) / 2 591 592 # misc 593 def op_and(self, index): 594 raise NotImplementedError 595 def op_or(self, index): 596 raise NotImplementedError 597 def op_not(self, index): 598 raise NotImplementedError 599 def op_store(self, index): 600 raise NotImplementedError 601 def op_abs(self, index): 602 raise NotImplementedError 603 def op_add(self, index): 604 raise NotImplementedError 605 def op_sub(self, index): 606 raise NotImplementedError 607 def op_div(self, index): 608 raise NotImplementedError 609 def op_load(self, index): 610 raise NotImplementedError 611 def op_neg(self, index): 612 raise NotImplementedError 613 def op_eq(self, index): 614 raise NotImplementedError 615 def op_drop(self, index): 616 raise NotImplementedError 617 def op_put(self, index): 618 raise NotImplementedError 619 def op_get(self, index): 620 raise NotImplementedError 621 def op_ifelse(self, index): 622 raise NotImplementedError 623 def op_random(self, index): 624 raise NotImplementedError 625 def op_mul(self, index): 626 raise NotImplementedError 627 def op_sqrt(self, index): 628 raise NotImplementedError 629 def op_dup(self, index): 630 raise NotImplementedError 631 def op_exch(self, index): 632 raise NotImplementedError 633 def op_index(self, index): 634 raise NotImplementedError 635 def op_roll(self, index): 636 raise NotImplementedError 637 638class T2OutlineExtractor(SimpleT2Decompiler): 639 640 def __init__(self, pen, localSubrs, globalSubrs, nominalWidthX, defaultWidthX): 641 SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs) 642 self.pen = pen 643 self.nominalWidthX = nominalWidthX 644 self.defaultWidthX = defaultWidthX 645 646 def reset(self): 647 SimpleT2Decompiler.reset(self) 648 self.hints = [] 649 self.gotWidth = 0 650 self.width = 0 651 self.currentPoint = (0, 0) 652 self.sawMoveTo = 0 653 654 def _nextPoint(self, point): 655 x, y = self.currentPoint 656 point = x + point[0], y + point[1] 657 self.currentPoint = point 658 return point 659 660 def rMoveTo(self, point): 661 self.pen.moveTo(self._nextPoint(point)) 662 self.sawMoveTo = 1 663 664 def rLineTo(self, point): 665 if not self.sawMoveTo: 666 self.rMoveTo((0, 0)) 667 self.pen.lineTo(self._nextPoint(point)) 668 669 def rCurveTo(self, pt1, pt2, pt3): 670 if not self.sawMoveTo: 671 self.rMoveTo((0, 0)) 672 nextPoint = self._nextPoint 673 self.pen.curveTo(nextPoint(pt1), nextPoint(pt2), nextPoint(pt3)) 674 675 def closePath(self): 676 if self.sawMoveTo: 677 self.pen.closePath() 678 self.sawMoveTo = 0 679 680 def endPath(self): 681 # In T2 there are no open paths, so always do a closePath when 682 # finishing a sub path. 683 self.closePath() 684 685 def popallWidth(self, evenOdd=0): 686 args = self.popall() 687 if not self.gotWidth: 688 if evenOdd ^ (len(args) % 2): 689 self.width = self.nominalWidthX + args[0] 690 args = args[1:] 691 else: 692 self.width = self.defaultWidthX 693 self.gotWidth = 1 694 return args 695 696 def countHints(self): 697 args = self.popallWidth() 698 self.hintCount = self.hintCount + len(args) / 2 699 700 # 701 # hint operators 702 # 703 #def op_hstem(self, index): 704 # self.countHints() 705 #def op_vstem(self, index): 706 # self.countHints() 707 #def op_hstemhm(self, index): 708 # self.countHints() 709 #def op_vstemhm(self, index): 710 # self.countHints() 711 #def op_hintmask(self, index): 712 # self.countHints() 713 #def op_cntrmask(self, index): 714 # self.countHints() 715 716 # 717 # path constructors, moveto 718 # 719 def op_rmoveto(self, index): 720 self.endPath() 721 self.rMoveTo(self.popallWidth()) 722 def op_hmoveto(self, index): 723 self.endPath() 724 self.rMoveTo((self.popallWidth(1)[0], 0)) 725 def op_vmoveto(self, index): 726 self.endPath() 727 self.rMoveTo((0, self.popallWidth(1)[0])) 728 def op_endchar(self, index): 729 self.endPath() 730 args = self.popallWidth() 731 if args: 732 from fontTools.encodings.StandardEncoding import StandardEncoding 733 # endchar can do seac accent bulding; The T2 spec says it's deprecated, 734 # but recent software that shall remain nameless does output it. 735 adx, ady, bchar, achar = args 736 baseGlyph = StandardEncoding[bchar] 737 self.pen.addComponent(baseGlyph, (1, 0, 0, 1, 0, 0)) 738 accentGlyph = StandardEncoding[achar] 739 self.pen.addComponent(accentGlyph, (1, 0, 0, 1, adx, ady)) 740 741 # 742 # path constructors, lines 743 # 744 def op_rlineto(self, index): 745 args = self.popall() 746 for i in range(0, len(args), 2): 747 point = args[i:i+2] 748 self.rLineTo(point) 749 750 def op_hlineto(self, index): 751 self.alternatingLineto(1) 752 def op_vlineto(self, index): 753 self.alternatingLineto(0) 754 755 # 756 # path constructors, curves 757 # 758 def op_rrcurveto(self, index): 759 """{dxa dya dxb dyb dxc dyc}+ rrcurveto""" 760 args = self.popall() 761 for i in range(0, len(args), 6): 762 dxa, dya, dxb, dyb, dxc, dyc, = args[i:i+6] 763 self.rCurveTo((dxa, dya), (dxb, dyb), (dxc, dyc)) 764 765 def op_rcurveline(self, index): 766 """{dxa dya dxb dyb dxc dyc}+ dxd dyd rcurveline""" 767 args = self.popall() 768 for i in range(0, len(args)-2, 6): 769 dxb, dyb, dxc, dyc, dxd, dyd = args[i:i+6] 770 self.rCurveTo((dxb, dyb), (dxc, dyc), (dxd, dyd)) 771 self.rLineTo(args[-2:]) 772 773 def op_rlinecurve(self, index): 774 """{dxa dya}+ dxb dyb dxc dyc dxd dyd rlinecurve""" 775 args = self.popall() 776 lineArgs = args[:-6] 777 for i in range(0, len(lineArgs), 2): 778 self.rLineTo(lineArgs[i:i+2]) 779 dxb, dyb, dxc, dyc, dxd, dyd = args[-6:] 780 self.rCurveTo((dxb, dyb), (dxc, dyc), (dxd, dyd)) 781 782 def op_vvcurveto(self, index): 783 "dx1? {dya dxb dyb dyc}+ vvcurveto" 784 args = self.popall() 785 if len(args) % 2: 786 dx1 = args[0] 787 args = args[1:] 788 else: 789 dx1 = 0 790 for i in range(0, len(args), 4): 791 dya, dxb, dyb, dyc = args[i:i+4] 792 self.rCurveTo((dx1, dya), (dxb, dyb), (0, dyc)) 793 dx1 = 0 794 795 def op_hhcurveto(self, index): 796 """dy1? {dxa dxb dyb dxc}+ hhcurveto""" 797 args = self.popall() 798 if len(args) % 2: 799 dy1 = args[0] 800 args = args[1:] 801 else: 802 dy1 = 0 803 for i in range(0, len(args), 4): 804 dxa, dxb, dyb, dxc = args[i:i+4] 805 self.rCurveTo((dxa, dy1), (dxb, dyb), (dxc, 0)) 806 dy1 = 0 807 808 def op_vhcurveto(self, index): 809 """dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf? vhcurveto (30) 810 {dya dxb dyb dxc dxd dxe dye dyf}+ dxf? vhcurveto 811 """ 812 args = self.popall() 813 while args: 814 args = self.vcurveto(args) 815 if args: 816 args = self.hcurveto(args) 817 818 def op_hvcurveto(self, index): 819 """dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf? 820 {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf? 821 """ 822 args = self.popall() 823 while args: 824 args = self.hcurveto(args) 825 if args: 826 args = self.vcurveto(args) 827 828 # 829 # path constructors, flex 830 # 831 def op_hflex(self, index): 832 dx1, dx2, dy2, dx3, dx4, dx5, dx6 = self.popall() 833 dy1 = dy3 = dy4 = dy6 = 0 834 dy5 = -dy2 835 self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3)) 836 self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6)) 837 def op_flex(self, index): 838 dx1, dy1, dx2, dy2, dx3, dy3, dx4, dy4, dx5, dy5, dx6, dy6, fd = self.popall() 839 self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3)) 840 self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6)) 841 def op_hflex1(self, index): 842 dx1, dy1, dx2, dy2, dx3, dx4, dx5, dy5, dx6 = self.popall() 843 dy3 = dy4 = 0 844 dy6 = -(dy1 + dy2 + dy3 + dy4 + dy5) 845 846 self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3)) 847 self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6)) 848 def op_flex1(self, index): 849 dx1, dy1, dx2, dy2, dx3, dy3, dx4, dy4, dx5, dy5, d6 = self.popall() 850 dx = dx1 + dx2 + dx3 + dx4 + dx5 851 dy = dy1 + dy2 + dy3 + dy4 + dy5 852 if abs(dx) > abs(dy): 853 dx6 = d6 854 dy6 = -dy 855 else: 856 dx6 = -dx 857 dy6 = d6 858 self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3)) 859 self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6)) 860 861 # 862 # MultipleMaster. Well... 863 # 864 def op_blend(self, index): 865 args = self.popall() 866 867 # misc 868 def op_and(self, index): 869 raise NotImplementedError 870 def op_or(self, index): 871 raise NotImplementedError 872 def op_not(self, index): 873 raise NotImplementedError 874 def op_store(self, index): 875 raise NotImplementedError 876 def op_abs(self, index): 877 raise NotImplementedError 878 def op_add(self, index): 879 raise NotImplementedError 880 def op_sub(self, index): 881 raise NotImplementedError 882 def op_div(self, index): 883 num2 = self.pop() 884 num1 = self.pop() 885 d1 = num1/num2 886 d2 = float(num1)/num2 887 if d1 == d2: 888 self.push(d1) 889 else: 890 self.push(d2) 891 def op_load(self, index): 892 raise NotImplementedError 893 def op_neg(self, index): 894 raise NotImplementedError 895 def op_eq(self, index): 896 raise NotImplementedError 897 def op_drop(self, index): 898 raise NotImplementedError 899 def op_put(self, index): 900 raise NotImplementedError 901 def op_get(self, index): 902 raise NotImplementedError 903 def op_ifelse(self, index): 904 raise NotImplementedError 905 def op_random(self, index): 906 raise NotImplementedError 907 def op_mul(self, index): 908 raise NotImplementedError 909 def op_sqrt(self, index): 910 raise NotImplementedError 911 def op_dup(self, index): 912 raise NotImplementedError 913 def op_exch(self, index): 914 raise NotImplementedError 915 def op_index(self, index): 916 raise NotImplementedError 917 def op_roll(self, index): 918 raise NotImplementedError 919 920 # 921 # miscelaneous helpers 922 # 923 def alternatingLineto(self, isHorizontal): 924 args = self.popall() 925 for arg in args: 926 if isHorizontal: 927 point = (arg, 0) 928 else: 929 point = (0, arg) 930 self.rLineTo(point) 931 isHorizontal = not isHorizontal 932 933 def vcurveto(self, args): 934 dya, dxb, dyb, dxc = args[:4] 935 args = args[4:] 936 if len(args) == 1: 937 dyc = args[0] 938 args = [] 939 else: 940 dyc = 0 941 self.rCurveTo((0, dya), (dxb, dyb), (dxc, dyc)) 942 return args 943 944 def hcurveto(self, args): 945 dxa, dxb, dyb, dyc = args[:4] 946 args = args[4:] 947 if len(args) == 1: 948 dxc = args[0] 949 args = [] 950 else: 951 dxc = 0 952 self.rCurveTo((dxa, 0), (dxb, dyb), (dxc, dyc)) 953 return args 954 955 956class T1OutlineExtractor(T2OutlineExtractor): 957 958 def __init__(self, pen, subrs): 959 self.pen = pen 960 self.subrs = subrs 961 self.reset() 962 963 def reset(self): 964 self.flexing = 0 965 self.width = 0 966 self.sbx = 0 967 T2OutlineExtractor.reset(self) 968 969 def endPath(self): 970 if self.sawMoveTo: 971 self.pen.endPath() 972 self.sawMoveTo = 0 973 974 def popallWidth(self, evenOdd=0): 975 return self.popall() 976 977 def exch(self): 978 stack = self.operandStack 979 stack[-1], stack[-2] = stack[-2], stack[-1] 980 981 # 982 # path constructors 983 # 984 def op_rmoveto(self, index): 985 if self.flexing: 986 return 987 self.endPath() 988 self.rMoveTo(self.popall()) 989 def op_hmoveto(self, index): 990 if self.flexing: 991 # We must add a parameter to the stack if we are flexing 992 self.push(0) 993 return 994 self.endPath() 995 self.rMoveTo((self.popall()[0], 0)) 996 def op_vmoveto(self, index): 997 if self.flexing: 998 # We must add a parameter to the stack if we are flexing 999 self.push(0) 1000 self.exch() 1001 return 1002 self.endPath() 1003 self.rMoveTo((0, self.popall()[0])) 1004 def op_closepath(self, index): 1005 self.closePath() 1006 def op_setcurrentpoint(self, index): 1007 args = self.popall() 1008 x, y = args 1009 self.currentPoint = x, y 1010 1011 def op_endchar(self, index): 1012 self.endPath() 1013 1014 def op_hsbw(self, index): 1015 sbx, wx = self.popall() 1016 self.width = wx 1017 self.sbx = sbx 1018 self.currentPoint = sbx, self.currentPoint[1] 1019 def op_sbw(self, index): 1020 self.popall() # XXX 1021 1022 # 1023 def op_callsubr(self, index): 1024 subrIndex = self.pop() 1025 subr = self.subrs[subrIndex] 1026 self.execute(subr) 1027 def op_callothersubr(self, index): 1028 subrIndex = self.pop() 1029 nArgs = self.pop() 1030 #print nArgs, subrIndex, "callothersubr" 1031 if subrIndex == 0 and nArgs == 3: 1032 self.doFlex() 1033 self.flexing = 0 1034 elif subrIndex == 1 and nArgs == 0: 1035 self.flexing = 1 1036 # ignore... 1037 def op_pop(self, index): 1038 pass # ignore... 1039 1040 def doFlex(self): 1041 finaly = self.pop() 1042 finalx = self.pop() 1043 self.pop() # flex height is unused 1044 1045 p3y = self.pop() 1046 p3x = self.pop() 1047 bcp4y = self.pop() 1048 bcp4x = self.pop() 1049 bcp3y = self.pop() 1050 bcp3x = self.pop() 1051 p2y = self.pop() 1052 p2x = self.pop() 1053 bcp2y = self.pop() 1054 bcp2x = self.pop() 1055 bcp1y = self.pop() 1056 bcp1x = self.pop() 1057 rpy = self.pop() 1058 rpx = self.pop() 1059 1060 # call rrcurveto 1061 self.push(bcp1x+rpx) 1062 self.push(bcp1y+rpy) 1063 self.push(bcp2x) 1064 self.push(bcp2y) 1065 self.push(p2x) 1066 self.push(p2y) 1067 self.op_rrcurveto(None) 1068 1069 # call rrcurveto 1070 self.push(bcp3x) 1071 self.push(bcp3y) 1072 self.push(bcp4x) 1073 self.push(bcp4y) 1074 self.push(p3x) 1075 self.push(p3y) 1076 self.op_rrcurveto(None) 1077 1078 # Push back final coords so subr 0 can find them 1079 self.push(finalx) 1080 self.push(finaly) 1081 1082 def op_dotsection(self, index): 1083 self.popall() # XXX 1084 def op_hstem3(self, index): 1085 self.popall() # XXX 1086 def op_seac(self, index): 1087 "asb adx ady bchar achar seac" 1088 from fontTools.encodings.StandardEncoding import StandardEncoding 1089 asb, adx, ady, bchar, achar = self.popall() 1090 baseGlyph = StandardEncoding[bchar] 1091 self.pen.addComponent(baseGlyph, (1, 0, 0, 1, 0, 0)) 1092 accentGlyph = StandardEncoding[achar] 1093 adx = adx + self.sbx - asb # seac weirdness 1094 self.pen.addComponent(accentGlyph, (1, 0, 0, 1, adx, ady)) 1095 def op_vstem3(self, index): 1096 self.popall() # XXX 1097 1098 1099class DictDecompiler(ByteCodeBase): 1100 1101 operandEncoding = cffDictOperandEncoding 1102 1103 def __init__(self, strings): 1104 self.stack = [] 1105 self.strings = strings 1106 self.dict = {} 1107 1108 def getDict(self): 1109 assert len(self.stack) == 0, "non-empty stack" 1110 return self.dict 1111 1112 def decompile(self, data): 1113 index = 0 1114 lenData = len(data) 1115 push = self.stack.append 1116 while index < lenData: 1117 b0 = ord(data[index]) 1118 index = index + 1 1119 code = self.operandEncoding[b0] 1120 handler = getattr(self, code) 1121 value, index = handler(b0, data, index) 1122 if value is not None: 1123 push(value) 1124 1125 def pop(self): 1126 value = self.stack[-1] 1127 del self.stack[-1] 1128 return value 1129 1130 def popall(self): 1131 all = self.stack[:] 1132 del self.stack[:] 1133 return all 1134 1135 def do_operator(self, b0, data, index): 1136 if b0 == 12: 1137 op = (b0, ord(data[index])) 1138 index = index+1 1139 else: 1140 op = b0 1141 operator, argType = self.operators[op] 1142 self.handle_operator(operator, argType) 1143 return None, index 1144 1145 def handle_operator(self, operator, argType): 1146 if isinstance(argType, type(())): 1147 value = () 1148 for i in range(len(argType)-1, -1, -1): 1149 arg = argType[i] 1150 arghandler = getattr(self, "arg_" + arg) 1151 value = (arghandler(operator),) + value 1152 else: 1153 arghandler = getattr(self, "arg_" + argType) 1154 value = arghandler(operator) 1155 self.dict[operator] = value 1156 1157 def arg_number(self, name): 1158 return self.pop() 1159 def arg_SID(self, name): 1160 return self.strings[self.pop()] 1161 def arg_array(self, name): 1162 return self.popall() 1163 def arg_delta(self, name): 1164 out = [] 1165 current = 0 1166 for v in self.popall(): 1167 current = current + v 1168 out.append(current) 1169 return out 1170 1171 1172def calcSubrBias(subrs): 1173 nSubrs = len(subrs) 1174 if nSubrs < 1240: 1175 bias = 107 1176 elif nSubrs < 33900: 1177 bias = 1131 1178 else: 1179 bias = 32768 1180 return bias 1181