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