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