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