psCharStrings.py revision 4e5af60930726d06a58a30bae45bb27ae50aea77
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, subrs=None, globalSubrs=None): 212 if program is None: 213 program = [] 214 self.bytecode = bytecode 215 self.program = program 216 self.subrs = subrs 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 decompiler = SimpleT2Decompiler(self.subrs, self.globalSubrs) 229 decompiler.reset() 230 decompiler.execute(self) 231 232 def compile(self): 233 if self.bytecode is not None: 234 return 235 if self.program[-1] not in ("endchar", "return", "callsubr", "callgsubr", "seac"): 236 print "XXX", self.program 237 assert 0, "illegal CharString" 238 bytecode = [] 239 opcodes = self.opcodes 240 program = self.program 241 i = 0 242 end = len(program) 243 while i < end: 244 token = program[i] 245 i = i + 1 246 tp = type(token) 247 if tp == types.StringType: 248 try: 249 bytecode.extend(map(chr, opcodes[token])) 250 except KeyError: 251 raise CharStringCompileError, "illegal operator: %s" % token 252 if token in ('hintmask', 'cntrmask'): 253 bytecode.append(program[i]) # hint mask 254 i = i + 1 255 elif tp == types.IntType: 256 bytecode.append(encodeIntT2(token)) 257 else: 258 assert 0, "unsupported type: %s" % tp 259 try: 260 bytecode = "".join(bytecode) 261 except TypeError: 262 print bytecode 263 raise 264 self.setBytecode(bytecode) 265 266 def needsDecompilation(self): 267 return self.bytecode is not None 268 269 def setProgram(self, program): 270 self.program = program 271 self.bytecode = None 272 273 def setBytecode(self, bytecode): 274 self.bytecode = bytecode 275 self.program = None 276 277 def getToken(self, index, 278 len=len, ord=ord, getattr=getattr, type=type, StringType=types.StringType): 279 if self.bytecode is not None: 280 if index >= len(self.bytecode): 281 return None, 0, 0 282 b0 = ord(self.bytecode[index]) 283 index = index + 1 284 code = self.operandEncoding[b0] 285 handler = getattr(self, code) 286 token, index = handler(b0, self.bytecode, index) 287 else: 288 if index >= len(self.program): 289 return None, 0, 0 290 token = self.program[index] 291 index = index + 1 292 isOperator = type(token) == StringType 293 return token, isOperator, index 294 295 def getBytes(self, index, nBytes): 296 if self.bytecode is not None: 297 newIndex = index + nBytes 298 bytes = self.bytecode[index:newIndex] 299 index = newIndex 300 else: 301 bytes = self.program[index] 302 index = index + 1 303 assert len(bytes) == nBytes 304 return bytes, index 305 306 def do_operator(self, b0, data, index): 307 if b0 == 12: 308 op = (b0, ord(data[index])) 309 index = index+1 310 else: 311 op = b0 312 operator = self.operators[op] 313 return operator, index 314 315 def toXML(self, xmlWriter): 316 from fontTools.misc.textTools import num2binary 317 if self.bytecode is not None: 318 xmlWriter.dumphex(self.bytecode) 319 else: 320 index = 0 321 args = [] 322 while 1: 323 token, isOperator, index = self.getToken(index) 324 if token is None: 325 break 326 if isOperator: 327 args = map(str, args) 328 if token in ('hintmask', 'cntrmask'): 329 hintMask, isOperator, index = self.getToken(index) 330 bits = [] 331 for byte in hintMask: 332 bits.append(num2binary(ord(byte), 8)) 333 hintMask = string.join(bits, "") 334 line = string.join(args + [token, hintMask], " ") 335 else: 336 line = string.join(args + [token], " ") 337 xmlWriter.write(line) 338 xmlWriter.newline() 339 args = [] 340 else: 341 args.append(token) 342 343 def fromXML(self, (name, attrs, content)): 344 from fontTools.misc.textTools import binary2num 345 content = "".join(content) 346 content = content.split() 347 program = [] 348 end = len(content) 349 i = 0 350 while i < end: 351 token = content[i] 352 i = i + 1 353 try: 354 token = int(token) 355 except ValueError: 356 program.append(token) 357 if token in ('hintmask', 'cntrmask'): 358 mask = content[i] 359 maskBytes = "" 360 for j in range(0, len(mask), 8): 361 maskBytes = maskBytes + chr(binary2num(mask[j:j+8])) 362 program.append(maskBytes) 363 i = i + 1 364 else: 365 program.append(token) 366 self.setProgram(program) 367 368 369t1Operators = [ 370# opcode name 371 (1, 'hstem'), 372 (3, 'vstem'), 373 (4, 'vmoveto'), 374 (5, 'rlineto'), 375 (6, 'hlineto'), 376 (7, 'vlineto'), 377 (8, 'rrcurveto'), 378 (9, 'closepath'), 379 (10, 'callsubr'), 380 (11, 'return'), 381 (13, 'hsbw'), 382 (14, 'endchar'), 383 (21, 'rmoveto'), 384 (22, 'hmoveto'), 385 (30, 'vhcurveto'), 386 (31, 'hvcurveto'), 387 ((12, 0), 'dotsection'), 388 ((12, 1), 'vstem3'), 389 ((12, 2), 'hstem3'), 390 ((12, 6), 'seac'), 391 ((12, 7), 'sbw'), 392 ((12, 12), 'div'), 393 ((12, 16), 'callothersubr'), 394 ((12, 17), 'pop'), 395 ((12, 33), 'setcurrentpoint'), 396] 397 398class T1CharString(T2CharString): 399 400 operandEncoding = t1OperandEncoding 401 operators, opcodes = buildOperatorDict(t1Operators) 402 403 def decompile(self): 404 if self.program is not None: 405 return 406 program = [] 407 index = 0 408 while 1: 409 token, isOperator, index = self.getToken(index) 410 if token is None: 411 break 412 program.append(token) 413 self.setProgram(program) 414 415 416class SimpleT2Decompiler: 417 418 def __init__(self, localSubrs, globalSubrs): 419 self.localSubrs = localSubrs 420 self.localBias = calcSubrBias(localSubrs) 421 self.globalSubrs = globalSubrs 422 self.globalBias = calcSubrBias(globalSubrs) 423 self.reset() 424 425 def reset(self): 426 self.callingStack = [] 427 self.operandStack = [] 428 self.hintCount = 0 429 self.hintMaskBytes = 0 430 431 def execute(self, charString): 432 self.callingStack.append(charString) 433 needsDecompilation = charString.needsDecompilation() 434 if needsDecompilation: 435 program = [] 436 pushToProgram = program.append 437 else: 438 pushToProgram = lambda x: None 439 pushToStack = self.operandStack.append 440 index = 0 441 while 1: 442 token, isOperator, index = charString.getToken(index) 443 if token is None: 444 break # we're done! 445 pushToProgram(token) 446 if isOperator: 447 handlerName = "op_" + token 448 if hasattr(self, handlerName): 449 handler = getattr(self, handlerName) 450 rv = handler(index) 451 if rv: 452 hintMaskBytes, index = rv 453 pushToProgram(hintMaskBytes) 454 else: 455 self.popall() 456 else: 457 pushToStack(token) 458 if needsDecompilation: 459 charString.setProgram(program) 460 if program[-1] not in ("endchar", "return", "callsubr", "callgsubr", "seac"): 461 print "XXX", program 462 assert program[-1] in ("endchar", "return", "callsubr", "callgsubr", "seac") 463 del self.callingStack[-1] 464 465 def pop(self): 466 value = self.operandStack[-1] 467 del self.operandStack[-1] 468 return value 469 470 def popall(self): 471 stack = self.operandStack[:] 472 self.operandStack[:] = [] 473 return stack 474 475 def push(self, value): 476 self.operandStack.append(value) 477 478 def op_return(self, index): 479 if self.operandStack: 480 pass 481 482 def op_endchar(self, index): 483 pass 484 485 def op_callsubr(self, index): 486 subrIndex = self.pop() 487 subr = self.localSubrs[subrIndex+self.localBias] 488 self.execute(subr) 489 490 def op_callgsubr(self, index): 491 subrIndex = self.pop() 492 subr = self.globalSubrs[subrIndex+self.globalBias] 493 self.execute(subr) 494 495 def op_hstem(self, index): 496 self.countHints() 497 def op_vstem(self, index): 498 self.countHints() 499 def op_hstemhm(self, index): 500 self.countHints() 501 def op_vstemhm(self, index): 502 self.countHints() 503 504 def op_hintmask(self, index): 505 if not self.hintMaskBytes: 506 self.countHints() 507 self.hintMaskBytes = (self.hintCount + 7) / 8 508 hintMaskBytes, index = self.callingStack[-1].getBytes(index, self.hintMaskBytes) 509 return hintMaskBytes, index 510 511 op_cntrmask = op_hintmask 512 513 def countHints(self): 514 args = self.popall() 515 self.hintCount = self.hintCount + len(args) / 2 516 517 518class T2OutlineExtractor(SimpleT2Decompiler): 519 520 def __init__(self, localSubrs, globalSubrs, nominalWidthX, defaultWidthX): 521 SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs) 522 self.nominalWidthX = nominalWidthX 523 self.defaultWidthX = defaultWidthX 524 525 def reset(self): 526 import Numeric 527 SimpleT2Decompiler.reset(self) 528 self.hints = [] 529 self.gotWidth = 0 530 self.width = 0 531 self.currentPoint = Numeric.array((0, 0), Numeric.Int16) 532 self.contours = [] 533 534 def getContours(self): 535 return self.contours 536 537 def newPath(self): 538 self.contours.append([[], [], 0]) 539 540 def closePath(self): 541 if self.contours and self.contours[-1][2] == 0: 542 self.contours[-1][2] = 1 543 544 def appendPoint(self, point, isPrimary): 545 import Numeric 546 point = self.currentPoint + Numeric.array(point, Numeric.Int16) 547 if not self.contours or self.contours[-1][2]: 548 # The subpath doesn't start with a moveto. Not sure whether 549 # this is legal, but apparently it usually works. 550 self.newPath() 551 self.appendPoint((0, 0), 1) 552 self.currentPoint = point 553 points, flags, isClosed = self.contours[-1] 554 points.append(point) 555 flags.append(isPrimary) 556 557 def popallWidth(self, evenOdd=0): 558 args = self.popall() 559 if not self.gotWidth: 560 if evenOdd ^ (len(args) % 2): 561 self.width = self.nominalWidthX + args[0] 562 args = args[1:] 563 else: 564 self.width = self.defaultWidthX 565 self.gotWidth = 1 566 return args 567 568 def countHints(self): 569 args = self.popallWidth() 570 self.hintCount = self.hintCount + len(args) / 2 571 572 # 573 # hint operators 574 # 575 #def op_hstem(self, index): 576 # self.countHints() 577 #def op_vstem(self, index): 578 # self.countHints() 579 #def op_hstemhm(self, index): 580 # self.countHints() 581 #def op_vstemhm(self, index): 582 # self.countHints() 583 #def op_hintmask(self, index): 584 # self.countHints() 585 #def op_cntrmask(self, index): 586 # self.countHints() 587 588 # 589 # path constructors, moveto 590 # 591 def op_rmoveto(self, index): 592 self.closePath() 593 self.newPath() 594 self.appendPoint(self.popallWidth(), 1) 595 def op_hmoveto(self, index): 596 self.closePath() 597 self.newPath() 598 self.appendPoint((self.popallWidth(1)[0], 0), 1) 599 def op_vmoveto(self, index): 600 self.closePath() 601 self.newPath() 602 self.appendPoint((0, self.popallWidth(1)[0]), 1) 603 def op_endchar(self, index): 604 self.closePath() 605 606 # 607 # path constructors, lines 608 # 609 def op_rlineto(self, index): 610 args = self.popall() 611 for i in range(0, len(args), 2): 612 point = args[i:i+2] 613 self.appendPoint(point, 1) 614 615 def op_hlineto(self, index): 616 self.alternatingLineto(1) 617 def op_vlineto(self, index): 618 self.alternatingLineto(0) 619 620 # 621 # path constructors, curves 622 # 623 def op_rrcurveto(self, index): 624 """{dxa dya dxb dyb dxc dyc}+ rrcurveto""" 625 args = self.popall() 626 for i in range(0, len(args), 6): 627 dxa, dya, dxb, dyb, dxc, dyc, = args[i:i+6] 628 self.rrcurveto((dxa, dya), (dxb, dyb), (dxc, dyc)) 629 630 def op_rcurveline(self, index): 631 """{dxa dya dxb dyb dxc dyc}+ dxd dyd rcurveline""" 632 args = self.popall() 633 for i in range(0, len(args)-2, 6): 634 dxb, dyb, dxc, dyc, dxd, dyd = args[i:i+6] 635 self.rrcurveto((dxb, dyb), (dxc, dyc), (dxd, dyd)) 636 self.appendPoint(args[-2:], 1) 637 638 def op_rlinecurve(self, index): 639 """{dxa dya}+ dxb dyb dxc dyc dxd dyd rlinecurve""" 640 args = self.popall() 641 lineArgs = args[:-6] 642 for i in range(0, len(lineArgs), 2): 643 self.appendPoint(lineArgs[i:i+2], 1) 644 dxb, dyb, dxc, dyc, dxd, dyd = args[-6:] 645 self.rrcurveto((dxb, dyb), (dxc, dyc), (dxd, dyd)) 646 647 def op_vvcurveto(self, index): 648 "dx1? {dya dxb dyb dyc}+ vvcurveto" 649 args = self.popall() 650 if len(args) % 2: 651 dx1 = args[0] 652 args = args[1:] 653 else: 654 dx1 = 0 655 for i in range(0, len(args), 4): 656 dya, dxb, dyb, dyc = args[i:i+4] 657 self.rrcurveto((dx1, dya), (dxb, dyb), (0, dyc)) 658 dx1 = 0 659 660 def op_hhcurveto(self, index): 661 """dy1? {dxa dxb dyb dxc}+ hhcurveto""" 662 args = self.popall() 663 if len(args) % 2: 664 dy1 = args[0] 665 args = args[1:] 666 else: 667 dy1 = 0 668 for i in range(0, len(args), 4): 669 dxa, dxb, dyb, dxc = args[i:i+4] 670 self.rrcurveto((dxa, dy1), (dxb, dyb), (dxc, 0)) 671 dy1 = 0 672 673 def op_vhcurveto(self, index): 674 """dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf? vhcurveto (30) 675 {dya dxb dyb dxc dxd dxe dye dyf}+ dxf? vhcurveto 676 """ 677 args = self.popall() 678 while args: 679 args = self.vcurveto(args) 680 if args: 681 args = self.hcurveto(args) 682 683 def op_hvcurveto(self, index): 684 """dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf? 685 {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf? 686 """ 687 args = self.popall() 688 while args: 689 args = self.hcurveto(args) 690 if args: 691 args = self.vcurveto(args) 692 693 # 694 # path constructors, flex 695 # 696 def op_hflex(self, index): 697 XXX 698 def op_flex(self, index): 699 XXX 700 def op_hflex1(self, index): 701 XXX 702 def op_flex1(self, index): 703 XXX 704 705 # 706 # MultipleMaster. Well... 707 # 708 def op_blend(self, index): 709 XXX 710 711 # misc 712 def op_and(self, index): 713 XXX 714 def op_or(self, index): 715 XXX 716 def op_not(self, index): 717 XXX 718 def op_store(self, index): 719 XXX 720 def op_abs(self, index): 721 XXX 722 def op_add(self, index): 723 XXX 724 def op_sub(self, index): 725 XXX 726 def op_div(self, index): 727 num2 = self.pop() 728 num1 = self.pop() 729 d1 = num1/num2 730 d2 = float(num1)/num2 731 if d1 == d2: 732 self.push(d1) 733 else: 734 self.push(d2) 735 def op_load(self, index): 736 XXX 737 def op_neg(self, index): 738 XXX 739 def op_eq(self, index): 740 XXX 741 def op_drop(self, index): 742 XXX 743 def op_put(self, index): 744 XXX 745 def op_get(self, index): 746 XXX 747 def op_ifelse(self, index): 748 XXX 749 def op_random(self, index): 750 XXX 751 def op_mul(self, index): 752 XXX 753 def op_sqrt(self, index): 754 XXX 755 def op_dup(self, index): 756 XXX 757 def op_exch(self, index): 758 XXX 759 def op_index(self, index): 760 XXX 761 def op_roll(self, index): 762 XXX 763 764 # 765 # miscelaneous helpers 766 # 767 def alternatingLineto(self, isHorizontal): 768 args = self.popall() 769 for arg in args: 770 if isHorizontal: 771 point = (arg, 0) 772 else: 773 point = (0, arg) 774 self.appendPoint(point, 1) 775 isHorizontal = not isHorizontal 776 777 def rrcurveto(self, p1, p2, p3): 778 self.appendPoint(p1, 0) 779 self.appendPoint(p2, 0) 780 self.appendPoint(p3, 1) 781 782 def vcurveto(self, args): 783 dya, dxb, dyb, dxc = args[:4] 784 args = args[4:] 785 if len(args) == 1: 786 dyc = args[0] 787 args = [] 788 else: 789 dyc = 0 790 self.rrcurveto((0, dya), (dxb, dyb), (dxc, dyc)) 791 return args 792 793 def hcurveto(self, args): 794 dxa, dxb, dyb, dyc = args[:4] 795 args = args[4:] 796 if len(args) == 1: 797 dxc = args[0] 798 args = [] 799 else: 800 dxc = 0 801 self.rrcurveto((dxa, 0), (dxb, dyb), (dxc, dyc)) 802 return args 803 804 805class T1OutlineExtractor(T2OutlineExtractor): 806 807 def __init__(self, subrs): 808 self.subrs = subrs 809 self.reset() 810 811 def reset(self): 812 self.flexing = 0 813 self.width = 0 814 self.sbx = 0 815 T2OutlineExtractor.reset(self) 816 817 def popallWidth(self, evenOdd=0): 818 return self.popall() 819 820 def exch(self): 821 stack = self.operandStack 822 stack[-1], stack[-2] = stack[-2], stack[-1] 823 824 # 825 # path constructors 826 # 827 def op_rmoveto(self, index): 828 if self.flexing: 829 return 830 self.newPath() 831 self.appendPoint(self.popall(), 1) 832 def op_hmoveto(self, index): 833 if self.flexing: 834 # We must add a parameter to the stack if we are flexing 835 self.push(0) 836 return 837 self.newPath() 838 self.appendPoint((self.popall()[0], 0), 1) 839 def op_vmoveto(self, index): 840 if self.flexing: 841 # We must add a parameter to the stack if we are flexing 842 self.push(0) 843 self.exch() 844 return 845 self.newPath() 846 self.appendPoint((0, self.popall()[0]), 1) 847 def op_closepath(self, index): 848 self.closePath() 849 def op_setcurrentpoint(self, index): 850 args = self.popall() 851 x, y = args 852 self.currentPoint[0] = x 853 self.currentPoint[1] = y 854 855 def op_endchar(self, index): 856 self.closePath() 857 858 def op_hsbw(self, index): 859 sbx, wx = self.popall() 860 self.width = wx 861 self.sbx = sbx 862 self.currentPoint[0] = sbx 863 def op_sbw(self, index): 864 self.popall() # XXX 865 866 # 867 def op_callsubr(self, index): 868 subrIndex = self.pop() 869 subr = self.subrs[subrIndex] 870 self.execute(subr) 871 def op_callothersubr(self, index): 872 subrIndex = self.pop() 873 nArgs = self.pop() 874 #print nArgs, subrIndex, "callothersubr" 875 if subrIndex == 0 and nArgs == 3: 876 self.doFlex() 877 self.flexing = 0 878 elif subrIndex == 1 and nArgs == 0: 879 self.flexing = 1 880 # ignore... 881 def op_pop(self, index): 882 pass # ignore... 883 884 def doFlex(self): 885 finaly = self.pop() 886 finalx = self.pop() 887 self.pop() # flex height is unused 888 889 p3y = self.pop() 890 p3x = self.pop() 891 bcp4y = self.pop() 892 bcp4x = self.pop() 893 bcp3y = self.pop() 894 bcp3x = self.pop() 895 p2y = self.pop() 896 p2x = self.pop() 897 bcp2y = self.pop() 898 bcp2x = self.pop() 899 bcp1y = self.pop() 900 bcp1x = self.pop() 901 rpy = self.pop() 902 rpx = self.pop() 903 904 # call rrcurveto 905 self.push(bcp1x+rpx) 906 self.push(bcp1y+rpy) 907 self.push(bcp2x) 908 self.push(bcp2y) 909 self.push(p2x) 910 self.push(p2y) 911 self.op_rrcurveto(None) 912 913 # call rrcurveto 914 self.push(bcp3x) 915 self.push(bcp3y) 916 self.push(bcp4x) 917 self.push(bcp4y) 918 self.push(p3x) 919 self.push(p3y) 920 self.op_rrcurveto(None) 921 922 # Push back final coords so subr 0 can find them 923 self.push(finalx) 924 self.push(finaly) 925 926 def op_dotsection(self, index): 927 self.popall() # XXX 928 def op_hstem3(self, index): 929 self.popall() # XXX 930 def op_seac(self, index): 931 "asb adx ady bchar achar seac" 932 asb, adx, ady, bchar, achar = self.popall() # XXX 933 self.contours.append([(asb, adx, ady, bchar, achar), None, -1]) 934 def op_vstem3(self, index): 935 self.popall() # XXX 936 937 938class DictDecompiler(ByteCodeBase): 939 940 operandEncoding = cffDictOperandEncoding 941 942 def __init__(self, strings): 943 self.stack = [] 944 self.strings = strings 945 self.dict = {} 946 947 def getDict(self): 948 assert len(self.stack) == 0, "non-empty stack" 949 return self.dict 950 951 def decompile(self, data): 952 index = 0 953 lenData = len(data) 954 push = self.stack.append 955 while index < lenData: 956 b0 = ord(data[index]) 957 index = index + 1 958 code = self.operandEncoding[b0] 959 handler = getattr(self, code) 960 value, index = handler(b0, data, index) 961 if value is not None: 962 push(value) 963 964 def pop(self): 965 value = self.stack[-1] 966 del self.stack[-1] 967 return value 968 969 def popall(self): 970 all = self.stack[:] 971 del self.stack[:] 972 return all 973 974 def do_operator(self, b0, data, index): 975 if b0 == 12: 976 op = (b0, ord(data[index])) 977 index = index+1 978 else: 979 op = b0 980 operator, argType = self.operators[op] 981 self.handle_operator(operator, argType) 982 return None, index 983 984 def handle_operator(self, operator, argType): 985 if type(argType) == type(()): 986 value = () 987 for i in range(len(argType)-1, -1, -1): 988 arg = argType[i] 989 arghandler = getattr(self, "arg_" + arg) 990 value = (arghandler(operator),) + value 991 else: 992 arghandler = getattr(self, "arg_" + argType) 993 value = arghandler(operator) 994 self.dict[operator] = value 995 996 def arg_number(self, name): 997 return self.pop() 998 def arg_SID(self, name): 999 return self.strings[self.pop()] 1000 def arg_array(self, name): 1001 return self.popall() 1002 def arg_delta(self, name): 1003 out = [] 1004 current = 0 1005 for v in self.popall(): 1006 current = current + v 1007 out.append(current) 1008 return out 1009 1010 1011def calcSubrBias(subrs): 1012 nSubrs = len(subrs) 1013 if nSubrs < 1240: 1014 bias = 107 1015 elif nSubrs < 33900: 1016 bias = 1131 1017 else: 1018 bias = 32768 1019 return bias 1020 1021