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 948 def __repr__(self): 949 if self.bytecode is None: 950 return "<%s (source) at %x>" % (self.__class__.__name__, id(self)) 951 else: 952 return "<%s (bytecode) at %x>" % (self.__class__.__name__, id(self)) 953 954 def getIntEncoder(self): 955 return encodeIntT2 956 957 def getFixedEncoder(self): 958 return encodeFixed 959 960 def decompile(self): 961 if not self.needsDecompilation(): 962 return 963 subrs = getattr(self.private, "Subrs", []) 964 decompiler = self.decompilerClass(subrs, self.globalSubrs, self.private) 965 decompiler.execute(self) 966 967 def draw(self, pen): 968 subrs = getattr(self.private, "Subrs", []) 969 extractor = self.outlineExtractor(pen, subrs, self.globalSubrs, 970 self.private.nominalWidthX, self.private.defaultWidthX, 971 self.private) 972 extractor.execute(self) 973 self.width = extractor.width 974 975 def calcBounds(self, glyphSet): 976 boundsPen = BoundsPen(glyphSet) 977 self.draw(boundsPen) 978 return boundsPen.bounds 979 980 def compile(self, isCFF2=False): 981 if self.bytecode is not None: 982 return 983 opcodes = self.opcodes 984 program = self.program 985 986 if isCFF2: 987 # If present, remove return and endchar operators. 988 if program and program[-1] in ("return", "endchar"): 989 program = program[:-1] 990 elif program and not isinstance(program[-1], basestring): 991 raise CharStringCompileError( 992 "T2CharString or Subr has items on the stack after last operator." 993 ) 994 995 bytecode = [] 996 encodeInt = self.getIntEncoder() 997 encodeFixed = self.getFixedEncoder() 998 i = 0 999 end = len(program) 1000 while i < end: 1001 token = program[i] 1002 i = i + 1 1003 if isinstance(token, basestring): 1004 try: 1005 bytecode.extend(bytechr(b) for b in opcodes[token]) 1006 except KeyError: 1007 raise CharStringCompileError("illegal operator: %s" % token) 1008 if token in ('hintmask', 'cntrmask'): 1009 bytecode.append(program[i]) # hint mask 1010 i = i + 1 1011 elif isinstance(token, int): 1012 bytecode.append(encodeInt(token)) 1013 elif isinstance(token, float): 1014 bytecode.append(encodeFixed(token)) 1015 else: 1016 assert 0, "unsupported type: %s" % type(token) 1017 try: 1018 bytecode = bytesjoin(bytecode) 1019 except TypeError: 1020 log.error(bytecode) 1021 raise 1022 self.setBytecode(bytecode) 1023 1024 def needsDecompilation(self): 1025 return self.bytecode is not None 1026 1027 def setProgram(self, program): 1028 self.program = program 1029 self.bytecode = None 1030 1031 def setBytecode(self, bytecode): 1032 self.bytecode = bytecode 1033 self.program = None 1034 1035 def getToken(self, index, 1036 len=len, byteord=byteord, basestring=basestring, 1037 isinstance=isinstance): 1038 if self.bytecode is not None: 1039 if index >= len(self.bytecode): 1040 return None, 0, 0 1041 b0 = byteord(self.bytecode[index]) 1042 index = index + 1 1043 handler = self.operandEncoding[b0] 1044 token, index = handler(self, b0, self.bytecode, index) 1045 else: 1046 if index >= len(self.program): 1047 return None, 0, 0 1048 token = self.program[index] 1049 index = index + 1 1050 isOperator = isinstance(token, basestring) 1051 return token, isOperator, index 1052 1053 def getBytes(self, index, nBytes): 1054 if self.bytecode is not None: 1055 newIndex = index + nBytes 1056 bytes = self.bytecode[index:newIndex] 1057 index = newIndex 1058 else: 1059 bytes = self.program[index] 1060 index = index + 1 1061 assert len(bytes) == nBytes 1062 return bytes, index 1063 1064 def handle_operator(self, operator): 1065 return operator 1066 1067 def toXML(self, xmlWriter): 1068 from fontTools.misc.textTools import num2binary 1069 if self.bytecode is not None: 1070 xmlWriter.dumphex(self.bytecode) 1071 else: 1072 index = 0 1073 args = [] 1074 while True: 1075 token, isOperator, index = self.getToken(index) 1076 if token is None: 1077 break 1078 if isOperator: 1079 args = [str(arg) for arg in args] 1080 if token in ('hintmask', 'cntrmask'): 1081 hintMask, isOperator, index = self.getToken(index) 1082 bits = [] 1083 for byte in hintMask: 1084 bits.append(num2binary(byteord(byte), 8)) 1085 hintMask = strjoin(bits) 1086 line = ' '.join(args + [token, hintMask]) 1087 else: 1088 line = ' '.join(args + [token]) 1089 xmlWriter.write(line) 1090 xmlWriter.newline() 1091 args = [] 1092 else: 1093 args.append(token) 1094 if args: 1095 # NOTE: only CFF2 charstrings/subrs can have numeric arguments on 1096 # the stack after the last operator. Compiling this would fail if 1097 # this is part of CFF 1.0 table. 1098 args = [str(arg) for arg in args] 1099 line = ' '.join(args) 1100 xmlWriter.write(line) 1101 1102 def fromXML(self, name, attrs, content): 1103 from fontTools.misc.textTools import binary2num, readHex 1104 if attrs.get("raw"): 1105 self.setBytecode(readHex(content)) 1106 return 1107 content = strjoin(content) 1108 content = content.split() 1109 program = [] 1110 end = len(content) 1111 i = 0 1112 while i < end: 1113 token = content[i] 1114 i = i + 1 1115 try: 1116 token = int(token) 1117 except ValueError: 1118 try: 1119 token = float(token) 1120 except ValueError: 1121 program.append(token) 1122 if token in ('hintmask', 'cntrmask'): 1123 mask = content[i] 1124 maskBytes = b"" 1125 for j in range(0, len(mask), 8): 1126 maskBytes = maskBytes + bytechr(binary2num(mask[j:j+8])) 1127 program.append(maskBytes) 1128 i = i + 1 1129 else: 1130 program.append(token) 1131 else: 1132 program.append(token) 1133 self.setProgram(program) 1134 1135class T1CharString(T2CharString): 1136 1137 operandEncoding = t1OperandEncoding 1138 operators, opcodes = buildOperatorDict(t1Operators) 1139 1140 def __init__(self, bytecode=None, program=None, subrs=None): 1141 if program is None: 1142 program = [] 1143 self.bytecode = bytecode 1144 self.program = program 1145 self.subrs = subrs 1146 1147 def getIntEncoder(self): 1148 return encodeIntT1 1149 1150 def getFixedEncoder(self): 1151 def encodeFixed(value): 1152 raise TypeError("Type 1 charstrings don't support floating point operands") 1153 1154 def decompile(self): 1155 if self.bytecode is None: 1156 return 1157 program = [] 1158 index = 0 1159 while True: 1160 token, isOperator, index = self.getToken(index) 1161 if token is None: 1162 break 1163 program.append(token) 1164 self.setProgram(program) 1165 1166 def draw(self, pen): 1167 extractor = T1OutlineExtractor(pen, self.subrs) 1168 extractor.execute(self) 1169 self.width = extractor.width 1170 1171class DictDecompiler(object): 1172 1173 operandEncoding = cffDictOperandEncoding 1174 1175 def __init__(self, strings, parent=None): 1176 self.stack = [] 1177 self.strings = strings 1178 self.dict = {} 1179 self.parent = parent 1180 1181 def getDict(self): 1182 assert len(self.stack) == 0, "non-empty stack" 1183 return self.dict 1184 1185 def decompile(self, data): 1186 index = 0 1187 lenData = len(data) 1188 push = self.stack.append 1189 while index < lenData: 1190 b0 = byteord(data[index]) 1191 index = index + 1 1192 handler = self.operandEncoding[b0] 1193 value, index = handler(self, b0, data, index) 1194 if value is not None: 1195 push(value) 1196 def pop(self): 1197 value = self.stack[-1] 1198 del self.stack[-1] 1199 return value 1200 1201 def popall(self): 1202 args = self.stack[:] 1203 del self.stack[:] 1204 return args 1205 1206 def handle_operator(self, operator): 1207 operator, argType = operator 1208 if isinstance(argType, tuple): 1209 value = () 1210 for i in range(len(argType)-1, -1, -1): 1211 arg = argType[i] 1212 arghandler = getattr(self, "arg_" + arg) 1213 value = (arghandler(operator),) + value 1214 else: 1215 arghandler = getattr(self, "arg_" + argType) 1216 value = arghandler(operator) 1217 if operator == "blend": 1218 self.stack.extend(value) 1219 else: 1220 self.dict[operator] = value 1221 1222 def arg_number(self, name): 1223 if isinstance(self.stack[0], list): 1224 out = self.arg_blend_number(self.stack) 1225 else: 1226 out = self.pop() 1227 return out 1228 1229 def arg_blend_number(self, name): 1230 out = [] 1231 blendArgs = self.pop() 1232 numMasters = len(blendArgs) 1233 out.append(blendArgs) 1234 out.append("blend") 1235 dummy = self.popall() 1236 return blendArgs 1237 1238 def arg_SID(self, name): 1239 return self.strings[self.pop()] 1240 def arg_array(self, name): 1241 return self.popall() 1242 def arg_blendList(self, name): 1243 """ 1244 There may be non-blend args at the top of the stack. We first calculate 1245 where the blend args start in the stack. These are the last 1246 numMasters*numBlends) +1 args. 1247 The blend args starts with numMasters relative coordinate values, the BlueValues in the list from the default master font. This is followed by 1248 numBlends list of values. Each of value in one of these lists is the 1249 Variable Font delta for the matching region. 1250 1251 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 1252 the delta values. We then convert the default values, the first item in each entry, to an absolute value. 1253 """ 1254 vsindex = self.dict.get('vsindex', 0) 1255 numMasters = self.parent.getNumRegions(vsindex) + 1 # only a PrivateDict has blended ops. 1256 numBlends = self.pop() 1257 args = self.popall() 1258 numArgs = len(args) 1259 # The spec says that there should be no non-blended Blue Values,. 1260 assert(numArgs == numMasters * numBlends) 1261 value = [None]*numBlends 1262 numDeltas = numMasters-1 1263 i = 0 1264 prevVal = 0 1265 while i < numBlends: 1266 newVal = args[i] + prevVal 1267 prevVal = newVal 1268 masterOffset = numBlends + (i* numDeltas) 1269 blendList = [newVal] + args[masterOffset:masterOffset+numDeltas] 1270 value[i] = blendList 1271 i += 1 1272 return value 1273 1274 def arg_delta(self, name): 1275 valueList = self.popall() 1276 out = [] 1277 if valueList and isinstance(valueList[0], list): 1278 # arg_blendList() has already converted these to absolute values. 1279 out = valueList 1280 else: 1281 current = 0 1282 for v in valueList: 1283 current = current + v 1284 out.append(current) 1285 return out 1286 1287 1288def calcSubrBias(subrs): 1289 nSubrs = len(subrs) 1290 if nSubrs < 1240: 1291 bias = 107 1292 elif nSubrs < 33900: 1293 bias = 1131 1294 else: 1295 bias = 32768 1296 return bias 1297