1from fontTools.misc import sstruct 2from fontTools.misc.fixedTools import floatToFixedToStr 3from fontTools.misc.textTools import byteord, safeEval 4 5# from itertools import * 6from . import DefaultTable 7from . import grUtils 8from array import array 9from functools import reduce 10import struct, re, sys 11 12Silf_hdr_format = """ 13 > 14 version: 16.16F 15""" 16 17Silf_hdr_format_3 = """ 18 > 19 version: 16.16F 20 compilerVersion: L 21 numSilf: H 22 x 23 x 24""" 25 26Silf_part1_format_v3 = """ 27 > 28 ruleVersion: 16.16F 29 passOffset: H 30 pseudosOffset: H 31""" 32 33Silf_part1_format = """ 34 > 35 maxGlyphID: H 36 extraAscent: h 37 extraDescent: h 38 numPasses: B 39 iSubst: B 40 iPos: B 41 iJust: B 42 iBidi: B 43 flags: B 44 maxPreContext: B 45 maxPostContext: B 46 attrPseudo: B 47 attrBreakWeight: B 48 attrDirectionality: B 49 attrMirroring: B 50 attrSkipPasses: B 51 numJLevels: B 52""" 53 54Silf_justify_format = """ 55 > 56 attrStretch: B 57 attrShrink: B 58 attrStep: B 59 attrWeight: B 60 runto: B 61 x 62 x 63 x 64""" 65 66Silf_part2_format = """ 67 > 68 numLigComp: H 69 numUserDefn: B 70 maxCompPerLig: B 71 direction: B 72 attCollisions: B 73 x 74 x 75 x 76 numCritFeatures: B 77""" 78 79Silf_pseudomap_format = """ 80 > 81 unicode: L 82 nPseudo: H 83""" 84 85Silf_pseudomap_format_h = """ 86 > 87 unicode: H 88 nPseudo: H 89""" 90 91Silf_classmap_format = """ 92 > 93 numClass: H 94 numLinear: H 95""" 96 97Silf_lookupclass_format = """ 98 > 99 numIDs: H 100 searchRange: H 101 entrySelector: H 102 rangeShift: H 103""" 104 105Silf_lookuppair_format = """ 106 > 107 glyphId: H 108 index: H 109""" 110 111Silf_pass_format = """ 112 > 113 flags: B 114 maxRuleLoop: B 115 maxRuleContext: B 116 maxBackup: B 117 numRules: H 118 fsmOffset: H 119 pcCode: L 120 rcCode: L 121 aCode: L 122 oDebug: L 123 numRows: H 124 numTransitional: H 125 numSuccess: H 126 numColumns: H 127""" 128 129aCode_info = ( 130 ("NOP", 0), 131 ("PUSH_BYTE", "b"), 132 ("PUSH_BYTE_U", "B"), 133 ("PUSH_SHORT", ">h"), 134 ("PUSH_SHORT_U", ">H"), 135 ("PUSH_LONG", ">L"), 136 ("ADD", 0), 137 ("SUB", 0), 138 ("MUL", 0), 139 ("DIV", 0), 140 ("MIN", 0), 141 ("MAX", 0), 142 ("NEG", 0), 143 ("TRUNC8", 0), 144 ("TRUNC16", 0), 145 ("COND", 0), 146 ("AND", 0), # x10 147 ("OR", 0), 148 ("NOT", 0), 149 ("EQUAL", 0), 150 ("NOT_EQ", 0), 151 ("LESS", 0), 152 ("GTR", 0), 153 ("LESS_EQ", 0), 154 ("GTR_EQ", 0), 155 ("NEXT", 0), 156 ("NEXT_N", "b"), 157 ("COPY_NEXT", 0), 158 ("PUT_GLYPH_8BIT_OBS", "B"), 159 ("PUT_SUBS_8BIT_OBS", "bBB"), 160 ("PUT_COPY", "b"), 161 ("INSERT", 0), 162 ("DELETE", 0), # x20 163 ("ASSOC", -1), 164 ("CNTXT_ITEM", "bB"), 165 ("ATTR_SET", "B"), 166 ("ATTR_ADD", "B"), 167 ("ATTR_SUB", "B"), 168 ("ATTR_SET_SLOT", "B"), 169 ("IATTR_SET_SLOT", "BB"), 170 ("PUSH_SLOT_ATTR", "Bb"), 171 ("PUSH_GLYPH_ATTR_OBS", "Bb"), 172 ("PUSH_GLYPH_METRIC", "Bbb"), 173 ("PUSH_FEAT", "Bb"), 174 ("PUSH_ATT_TO_GATTR_OBS", "Bb"), 175 ("PUSH_ATT_TO_GLYPH_METRIC", "Bbb"), 176 ("PUSH_ISLOT_ATTR", "Bbb"), 177 ("PUSH_IGLYPH_ATTR", "Bbb"), 178 ("POP_RET", 0), # x30 179 ("RET_ZERO", 0), 180 ("RET_TRUE", 0), 181 ("IATTR_SET", "BB"), 182 ("IATTR_ADD", "BB"), 183 ("IATTR_SUB", "BB"), 184 ("PUSH_PROC_STATE", "B"), 185 ("PUSH_VERSION", 0), 186 ("PUT_SUBS", ">bHH"), 187 ("PUT_SUBS2", 0), 188 ("PUT_SUBS3", 0), 189 ("PUT_GLYPH", ">H"), 190 ("PUSH_GLYPH_ATTR", ">Hb"), 191 ("PUSH_ATT_TO_GLYPH_ATTR", ">Hb"), 192 ("BITOR", 0), 193 ("BITAND", 0), 194 ("BITNOT", 0), # x40 195 ("BITSET", ">HH"), 196 ("SET_FEAT", "Bb"), 197) 198aCode_map = dict([(x[0], (i, x[1])) for i, x in enumerate(aCode_info)]) 199 200 201def disassemble(aCode): 202 codelen = len(aCode) 203 pc = 0 204 res = [] 205 while pc < codelen: 206 opcode = byteord(aCode[pc : pc + 1]) 207 if opcode > len(aCode_info): 208 instr = aCode_info[0] 209 else: 210 instr = aCode_info[opcode] 211 pc += 1 212 if instr[1] != 0 and pc >= codelen: 213 return res 214 if instr[1] == -1: 215 count = byteord(aCode[pc]) 216 fmt = "%dB" % count 217 pc += 1 218 elif instr[1] == 0: 219 fmt = "" 220 else: 221 fmt = instr[1] 222 if fmt == "": 223 res.append(instr[0]) 224 continue 225 parms = struct.unpack_from(fmt, aCode[pc:]) 226 res.append(instr[0] + "(" + ", ".join(map(str, parms)) + ")") 227 pc += struct.calcsize(fmt) 228 return res 229 230 231instre = re.compile(r"^\s*([^(]+)\s*(?:\(([^)]+)\))?") 232 233 234def assemble(instrs): 235 res = b"" 236 for inst in instrs: 237 m = instre.match(inst) 238 if not m or not m.group(1) in aCode_map: 239 continue 240 opcode, parmfmt = aCode_map[m.group(1)] 241 res += struct.pack("B", opcode) 242 if m.group(2): 243 if parmfmt == 0: 244 continue 245 parms = [int(x) for x in re.split(r",\s*", m.group(2))] 246 if parmfmt == -1: 247 l = len(parms) 248 res += struct.pack(("%dB" % (l + 1)), l, *parms) 249 else: 250 res += struct.pack(parmfmt, *parms) 251 return res 252 253 254def writecode(tag, writer, instrs): 255 writer.begintag(tag) 256 writer.newline() 257 for l in disassemble(instrs): 258 writer.write(l) 259 writer.newline() 260 writer.endtag(tag) 261 writer.newline() 262 263 264def readcode(content): 265 res = [] 266 for e in content_string(content).split("\n"): 267 e = e.strip() 268 if not len(e): 269 continue 270 res.append(e) 271 return assemble(res) 272 273 274attrs_info = ( 275 "flags", 276 "extraAscent", 277 "extraDescent", 278 "maxGlyphID", 279 "numLigComp", 280 "numUserDefn", 281 "maxCompPerLig", 282 "direction", 283 "lbGID", 284) 285attrs_passindexes = ("iSubst", "iPos", "iJust", "iBidi") 286attrs_contexts = ("maxPreContext", "maxPostContext") 287attrs_attributes = ( 288 "attrPseudo", 289 "attrBreakWeight", 290 "attrDirectionality", 291 "attrMirroring", 292 "attrSkipPasses", 293 "attCollisions", 294) 295pass_attrs_info = ( 296 "flags", 297 "maxRuleLoop", 298 "maxRuleContext", 299 "maxBackup", 300 "minRulePreContext", 301 "maxRulePreContext", 302 "collisionThreshold", 303) 304pass_attrs_fsm = ("numRows", "numTransitional", "numSuccess", "numColumns") 305 306 307def writesimple(tag, self, writer, *attrkeys): 308 attrs = dict([(k, getattr(self, k)) for k in attrkeys]) 309 writer.simpletag(tag, **attrs) 310 writer.newline() 311 312 313def getSimple(self, attrs, *attr_list): 314 for k in attr_list: 315 if k in attrs: 316 setattr(self, k, int(safeEval(attrs[k]))) 317 318 319def content_string(contents): 320 res = "" 321 for element in contents: 322 if isinstance(element, tuple): 323 continue 324 res += element 325 return res.strip() 326 327 328def wrapline(writer, dat, length=80): 329 currline = "" 330 for d in dat: 331 if len(currline) > length: 332 writer.write(currline[:-1]) 333 writer.newline() 334 currline = "" 335 currline += d + " " 336 if len(currline): 337 writer.write(currline[:-1]) 338 writer.newline() 339 340 341class _Object: 342 pass 343 344 345class table_S__i_l_f(DefaultTable.DefaultTable): 346 """Silf table support""" 347 348 def __init__(self, tag=None): 349 DefaultTable.DefaultTable.__init__(self, tag) 350 self.silfs = [] 351 352 def decompile(self, data, ttFont): 353 sstruct.unpack2(Silf_hdr_format, data, self) 354 self.version = float(floatToFixedToStr(self.version, precisionBits=16)) 355 if self.version >= 5.0: 356 (data, self.scheme) = grUtils.decompress(data) 357 sstruct.unpack2(Silf_hdr_format_3, data, self) 358 base = sstruct.calcsize(Silf_hdr_format_3) 359 elif self.version < 3.0: 360 self.numSilf = struct.unpack(">H", data[4:6]) 361 self.scheme = 0 362 self.compilerVersion = 0 363 base = 8 364 else: 365 self.scheme = 0 366 sstruct.unpack2(Silf_hdr_format_3, data, self) 367 base = sstruct.calcsize(Silf_hdr_format_3) 368 369 silfoffsets = struct.unpack_from((">%dL" % self.numSilf), data[base:]) 370 for offset in silfoffsets: 371 s = Silf() 372 self.silfs.append(s) 373 s.decompile(data[offset:], ttFont, self.version) 374 375 def compile(self, ttFont): 376 self.numSilf = len(self.silfs) 377 if self.version < 3.0: 378 hdr = sstruct.pack(Silf_hdr_format, self) 379 hdr += struct.pack(">HH", self.numSilf, 0) 380 else: 381 hdr = sstruct.pack(Silf_hdr_format_3, self) 382 offset = len(hdr) + 4 * self.numSilf 383 data = b"" 384 for s in self.silfs: 385 hdr += struct.pack(">L", offset) 386 subdata = s.compile(ttFont, self.version) 387 offset += len(subdata) 388 data += subdata 389 if self.version >= 5.0: 390 return grUtils.compress(self.scheme, hdr + data) 391 return hdr + data 392 393 def toXML(self, writer, ttFont): 394 writer.comment("Attributes starting with _ are informative only") 395 writer.newline() 396 writer.simpletag( 397 "version", 398 version=self.version, 399 compilerVersion=self.compilerVersion, 400 compressionScheme=self.scheme, 401 ) 402 writer.newline() 403 for s in self.silfs: 404 writer.begintag("silf") 405 writer.newline() 406 s.toXML(writer, ttFont, self.version) 407 writer.endtag("silf") 408 writer.newline() 409 410 def fromXML(self, name, attrs, content, ttFont): 411 if name == "version": 412 self.scheme = int(safeEval(attrs["compressionScheme"])) 413 self.version = float(safeEval(attrs["version"])) 414 self.compilerVersion = int(safeEval(attrs["compilerVersion"])) 415 return 416 if name == "silf": 417 s = Silf() 418 self.silfs.append(s) 419 for element in content: 420 if not isinstance(element, tuple): 421 continue 422 tag, attrs, subcontent = element 423 s.fromXML(tag, attrs, subcontent, ttFont, self.version) 424 425 426class Silf(object): 427 """A particular Silf subtable""" 428 429 def __init__(self): 430 self.passes = [] 431 self.scriptTags = [] 432 self.critFeatures = [] 433 self.jLevels = [] 434 self.pMap = {} 435 436 def decompile(self, data, ttFont, version=2.0): 437 if version >= 3.0: 438 _, data = sstruct.unpack2(Silf_part1_format_v3, data, self) 439 self.ruleVersion = float( 440 floatToFixedToStr(self.ruleVersion, precisionBits=16) 441 ) 442 _, data = sstruct.unpack2(Silf_part1_format, data, self) 443 for jlevel in range(self.numJLevels): 444 j, data = sstruct.unpack2(Silf_justify_format, data, _Object()) 445 self.jLevels.append(j) 446 _, data = sstruct.unpack2(Silf_part2_format, data, self) 447 if self.numCritFeatures: 448 self.critFeatures = struct.unpack_from( 449 (">%dH" % self.numCritFeatures), data 450 ) 451 data = data[self.numCritFeatures * 2 + 1 :] 452 (numScriptTag,) = struct.unpack_from("B", data) 453 if numScriptTag: 454 self.scriptTags = [ 455 struct.unpack("4s", data[x : x + 4])[0].decode("ascii") 456 for x in range(1, 1 + 4 * numScriptTag, 4) 457 ] 458 data = data[1 + 4 * numScriptTag :] 459 (self.lbGID,) = struct.unpack(">H", data[:2]) 460 if self.numPasses: 461 self.oPasses = struct.unpack( 462 (">%dL" % (self.numPasses + 1)), data[2 : 6 + 4 * self.numPasses] 463 ) 464 data = data[6 + 4 * self.numPasses :] 465 (numPseudo,) = struct.unpack(">H", data[:2]) 466 for i in range(numPseudo): 467 if version >= 3.0: 468 pseudo = sstruct.unpack( 469 Silf_pseudomap_format, data[8 + 6 * i : 14 + 6 * i], _Object() 470 ) 471 else: 472 pseudo = sstruct.unpack( 473 Silf_pseudomap_format_h, data[8 + 4 * i : 12 + 4 * i], _Object() 474 ) 475 self.pMap[pseudo.unicode] = ttFont.getGlyphName(pseudo.nPseudo) 476 data = data[8 + 6 * numPseudo :] 477 currpos = ( 478 sstruct.calcsize(Silf_part1_format) 479 + sstruct.calcsize(Silf_justify_format) * self.numJLevels 480 + sstruct.calcsize(Silf_part2_format) 481 + 2 * self.numCritFeatures 482 + 1 483 + 1 484 + 4 * numScriptTag 485 + 6 486 + 4 * self.numPasses 487 + 8 488 + 6 * numPseudo 489 ) 490 if version >= 3.0: 491 currpos += sstruct.calcsize(Silf_part1_format_v3) 492 self.classes = Classes() 493 self.classes.decompile(data, ttFont, version) 494 for i in range(self.numPasses): 495 p = Pass() 496 self.passes.append(p) 497 p.decompile( 498 data[self.oPasses[i] - currpos : self.oPasses[i + 1] - currpos], 499 ttFont, 500 version, 501 ) 502 503 def compile(self, ttFont, version=2.0): 504 self.numPasses = len(self.passes) 505 self.numJLevels = len(self.jLevels) 506 self.numCritFeatures = len(self.critFeatures) 507 numPseudo = len(self.pMap) 508 data = b"" 509 if version >= 3.0: 510 hdroffset = sstruct.calcsize(Silf_part1_format_v3) 511 else: 512 hdroffset = 0 513 data += sstruct.pack(Silf_part1_format, self) 514 for j in self.jLevels: 515 data += sstruct.pack(Silf_justify_format, j) 516 data += sstruct.pack(Silf_part2_format, self) 517 if self.numCritFeatures: 518 data += struct.pack((">%dH" % self.numCritFeaturs), *self.critFeatures) 519 data += struct.pack("BB", 0, len(self.scriptTags)) 520 if len(self.scriptTags): 521 tdata = [struct.pack("4s", x.encode("ascii")) for x in self.scriptTags] 522 data += b"".join(tdata) 523 data += struct.pack(">H", self.lbGID) 524 self.passOffset = len(data) 525 526 data1 = grUtils.bininfo(numPseudo, 6) 527 currpos = hdroffset + len(data) + 4 * (self.numPasses + 1) 528 self.pseudosOffset = currpos + len(data1) 529 for u, p in sorted(self.pMap.items()): 530 data1 += struct.pack( 531 (">LH" if version >= 3.0 else ">HH"), u, ttFont.getGlyphID(p) 532 ) 533 data1 += self.classes.compile(ttFont, version) 534 currpos += len(data1) 535 data2 = b"" 536 datao = b"" 537 for i, p in enumerate(self.passes): 538 base = currpos + len(data2) 539 datao += struct.pack(">L", base) 540 data2 += p.compile(ttFont, base, version) 541 datao += struct.pack(">L", currpos + len(data2)) 542 543 if version >= 3.0: 544 data3 = sstruct.pack(Silf_part1_format_v3, self) 545 else: 546 data3 = b"" 547 return data3 + data + datao + data1 + data2 548 549 def toXML(self, writer, ttFont, version=2.0): 550 if version >= 3.0: 551 writer.simpletag("version", ruleVersion=self.ruleVersion) 552 writer.newline() 553 writesimple("info", self, writer, *attrs_info) 554 writesimple("passindexes", self, writer, *attrs_passindexes) 555 writesimple("contexts", self, writer, *attrs_contexts) 556 writesimple("attributes", self, writer, *attrs_attributes) 557 if len(self.jLevels): 558 writer.begintag("justifications") 559 writer.newline() 560 jformat, jnames, jfixes = sstruct.getformat(Silf_justify_format) 561 for i, j in enumerate(self.jLevels): 562 attrs = dict([(k, getattr(j, k)) for k in jnames]) 563 writer.simpletag("justify", **attrs) 564 writer.newline() 565 writer.endtag("justifications") 566 writer.newline() 567 if len(self.critFeatures): 568 writer.begintag("critFeatures") 569 writer.newline() 570 writer.write(" ".join(map(str, self.critFeatures))) 571 writer.newline() 572 writer.endtag("critFeatures") 573 writer.newline() 574 if len(self.scriptTags): 575 writer.begintag("scriptTags") 576 writer.newline() 577 writer.write(" ".join(self.scriptTags)) 578 writer.newline() 579 writer.endtag("scriptTags") 580 writer.newline() 581 if self.pMap: 582 writer.begintag("pseudoMap") 583 writer.newline() 584 for k, v in sorted(self.pMap.items()): 585 writer.simpletag("pseudo", unicode=hex(k), pseudo=v) 586 writer.newline() 587 writer.endtag("pseudoMap") 588 writer.newline() 589 self.classes.toXML(writer, ttFont, version) 590 if len(self.passes): 591 writer.begintag("passes") 592 writer.newline() 593 for i, p in enumerate(self.passes): 594 writer.begintag("pass", _index=i) 595 writer.newline() 596 p.toXML(writer, ttFont, version) 597 writer.endtag("pass") 598 writer.newline() 599 writer.endtag("passes") 600 writer.newline() 601 602 def fromXML(self, name, attrs, content, ttFont, version=2.0): 603 if name == "version": 604 self.ruleVersion = float(safeEval(attrs.get("ruleVersion", "0"))) 605 if name == "info": 606 getSimple(self, attrs, *attrs_info) 607 elif name == "passindexes": 608 getSimple(self, attrs, *attrs_passindexes) 609 elif name == "contexts": 610 getSimple(self, attrs, *attrs_contexts) 611 elif name == "attributes": 612 getSimple(self, attrs, *attrs_attributes) 613 elif name == "justifications": 614 for element in content: 615 if not isinstance(element, tuple): 616 continue 617 (tag, attrs, subcontent) = element 618 if tag == "justify": 619 j = _Object() 620 for k, v in attrs.items(): 621 setattr(j, k, int(v)) 622 self.jLevels.append(j) 623 elif name == "critFeatures": 624 self.critFeatures = [] 625 element = content_string(content) 626 self.critFeatures.extend(map(int, element.split())) 627 elif name == "scriptTags": 628 self.scriptTags = [] 629 element = content_string(content) 630 for n in element.split(): 631 self.scriptTags.append(n) 632 elif name == "pseudoMap": 633 self.pMap = {} 634 for element in content: 635 if not isinstance(element, tuple): 636 continue 637 (tag, attrs, subcontent) = element 638 if tag == "pseudo": 639 k = int(attrs["unicode"], 16) 640 v = attrs["pseudo"] 641 self.pMap[k] = v 642 elif name == "classes": 643 self.classes = Classes() 644 for element in content: 645 if not isinstance(element, tuple): 646 continue 647 tag, attrs, subcontent = element 648 self.classes.fromXML(tag, attrs, subcontent, ttFont, version) 649 elif name == "passes": 650 for element in content: 651 if not isinstance(element, tuple): 652 continue 653 tag, attrs, subcontent = element 654 if tag == "pass": 655 p = Pass() 656 for e in subcontent: 657 if not isinstance(e, tuple): 658 continue 659 p.fromXML(e[0], e[1], e[2], ttFont, version) 660 self.passes.append(p) 661 662 663class Classes(object): 664 def __init__(self): 665 self.linear = [] 666 self.nonLinear = [] 667 668 def decompile(self, data, ttFont, version=2.0): 669 sstruct.unpack2(Silf_classmap_format, data, self) 670 if version >= 4.0: 671 oClasses = struct.unpack( 672 (">%dL" % (self.numClass + 1)), data[4 : 8 + 4 * self.numClass] 673 ) 674 else: 675 oClasses = struct.unpack( 676 (">%dH" % (self.numClass + 1)), data[4 : 6 + 2 * self.numClass] 677 ) 678 for s, e in zip(oClasses[: self.numLinear], oClasses[1 : self.numLinear + 1]): 679 self.linear.append( 680 ttFont.getGlyphName(x) 681 for x in struct.unpack((">%dH" % ((e - s) / 2)), data[s:e]) 682 ) 683 for s, e in zip( 684 oClasses[self.numLinear : self.numClass], 685 oClasses[self.numLinear + 1 : self.numClass + 1], 686 ): 687 nonLinids = [ 688 struct.unpack(">HH", data[x : x + 4]) for x in range(s + 8, e, 4) 689 ] 690 nonLin = dict([(ttFont.getGlyphName(x[0]), x[1]) for x in nonLinids]) 691 self.nonLinear.append(nonLin) 692 693 def compile(self, ttFont, version=2.0): 694 data = b"" 695 oClasses = [] 696 if version >= 4.0: 697 offset = 8 + 4 * (len(self.linear) + len(self.nonLinear)) 698 else: 699 offset = 6 + 2 * (len(self.linear) + len(self.nonLinear)) 700 for l in self.linear: 701 oClasses.append(len(data) + offset) 702 gs = [ttFont.getGlyphID(x) for x in l] 703 data += struct.pack((">%dH" % len(l)), *gs) 704 for l in self.nonLinear: 705 oClasses.append(len(data) + offset) 706 gs = [(ttFont.getGlyphID(x[0]), x[1]) for x in l.items()] 707 data += grUtils.bininfo(len(gs)) 708 data += b"".join([struct.pack(">HH", *x) for x in sorted(gs)]) 709 oClasses.append(len(data) + offset) 710 self.numClass = len(oClasses) - 1 711 self.numLinear = len(self.linear) 712 return ( 713 sstruct.pack(Silf_classmap_format, self) 714 + struct.pack( 715 ((">%dL" if version >= 4.0 else ">%dH") % len(oClasses)), *oClasses 716 ) 717 + data 718 ) 719 720 def toXML(self, writer, ttFont, version=2.0): 721 writer.begintag("classes") 722 writer.newline() 723 writer.begintag("linearClasses") 724 writer.newline() 725 for i, l in enumerate(self.linear): 726 writer.begintag("linear", _index=i) 727 writer.newline() 728 wrapline(writer, l) 729 writer.endtag("linear") 730 writer.newline() 731 writer.endtag("linearClasses") 732 writer.newline() 733 writer.begintag("nonLinearClasses") 734 writer.newline() 735 for i, l in enumerate(self.nonLinear): 736 writer.begintag("nonLinear", _index=i + self.numLinear) 737 writer.newline() 738 for inp, ind in l.items(): 739 writer.simpletag("map", glyph=inp, index=ind) 740 writer.newline() 741 writer.endtag("nonLinear") 742 writer.newline() 743 writer.endtag("nonLinearClasses") 744 writer.newline() 745 writer.endtag("classes") 746 writer.newline() 747 748 def fromXML(self, name, attrs, content, ttFont, version=2.0): 749 if name == "linearClasses": 750 for element in content: 751 if not isinstance(element, tuple): 752 continue 753 tag, attrs, subcontent = element 754 if tag == "linear": 755 l = content_string(subcontent).split() 756 self.linear.append(l) 757 elif name == "nonLinearClasses": 758 for element in content: 759 if not isinstance(element, tuple): 760 continue 761 tag, attrs, subcontent = element 762 if tag == "nonLinear": 763 l = {} 764 for e in subcontent: 765 if not isinstance(e, tuple): 766 continue 767 tag, attrs, subsubcontent = e 768 if tag == "map": 769 l[attrs["glyph"]] = int(safeEval(attrs["index"])) 770 self.nonLinear.append(l) 771 772 773class Pass(object): 774 def __init__(self): 775 self.colMap = {} 776 self.rules = [] 777 self.rulePreContexts = [] 778 self.ruleSortKeys = [] 779 self.ruleConstraints = [] 780 self.passConstraints = b"" 781 self.actions = [] 782 self.stateTrans = [] 783 self.startStates = [] 784 785 def decompile(self, data, ttFont, version=2.0): 786 _, data = sstruct.unpack2(Silf_pass_format, data, self) 787 (numRange, _, _, _) = struct.unpack(">4H", data[:8]) 788 data = data[8:] 789 for i in range(numRange): 790 (first, last, col) = struct.unpack(">3H", data[6 * i : 6 * i + 6]) 791 for g in range(first, last + 1): 792 self.colMap[ttFont.getGlyphName(g)] = col 793 data = data[6 * numRange :] 794 oRuleMap = struct.unpack_from((">%dH" % (self.numSuccess + 1)), data) 795 data = data[2 + 2 * self.numSuccess :] 796 rules = struct.unpack_from((">%dH" % oRuleMap[-1]), data) 797 self.rules = [rules[s:e] for (s, e) in zip(oRuleMap, oRuleMap[1:])] 798 data = data[2 * oRuleMap[-1] :] 799 (self.minRulePreContext, self.maxRulePreContext) = struct.unpack("BB", data[:2]) 800 numStartStates = self.maxRulePreContext - self.minRulePreContext + 1 801 self.startStates = struct.unpack( 802 (">%dH" % numStartStates), data[2 : 2 + numStartStates * 2] 803 ) 804 data = data[2 + numStartStates * 2 :] 805 self.ruleSortKeys = struct.unpack( 806 (">%dH" % self.numRules), data[: 2 * self.numRules] 807 ) 808 data = data[2 * self.numRules :] 809 self.rulePreContexts = struct.unpack( 810 ("%dB" % self.numRules), data[: self.numRules] 811 ) 812 data = data[self.numRules :] 813 (self.collisionThreshold, pConstraint) = struct.unpack(">BH", data[:3]) 814 oConstraints = list( 815 struct.unpack( 816 (">%dH" % (self.numRules + 1)), data[3 : 5 + self.numRules * 2] 817 ) 818 ) 819 data = data[5 + self.numRules * 2 :] 820 oActions = list( 821 struct.unpack((">%dH" % (self.numRules + 1)), data[: 2 + self.numRules * 2]) 822 ) 823 data = data[2 * self.numRules + 2 :] 824 for i in range(self.numTransitional): 825 a = array( 826 "H", data[i * self.numColumns * 2 : (i + 1) * self.numColumns * 2] 827 ) 828 if sys.byteorder != "big": 829 a.byteswap() 830 self.stateTrans.append(a) 831 data = data[self.numTransitional * self.numColumns * 2 + 1 :] 832 self.passConstraints = data[:pConstraint] 833 data = data[pConstraint:] 834 for i in range(len(oConstraints) - 2, -1, -1): 835 if oConstraints[i] == 0: 836 oConstraints[i] = oConstraints[i + 1] 837 self.ruleConstraints = [ 838 (data[s:e] if (e - s > 1) else b"") 839 for (s, e) in zip(oConstraints, oConstraints[1:]) 840 ] 841 data = data[oConstraints[-1] :] 842 self.actions = [ 843 (data[s:e] if (e - s > 1) else "") for (s, e) in zip(oActions, oActions[1:]) 844 ] 845 data = data[oActions[-1] :] 846 # not using debug 847 848 def compile(self, ttFont, base, version=2.0): 849 # build it all up backwards 850 oActions = reduce( 851 lambda a, x: (a[0] + len(x), a[1] + [a[0]]), self.actions + [b""], (0, []) 852 )[1] 853 oConstraints = reduce( 854 lambda a, x: (a[0] + len(x), a[1] + [a[0]]), 855 self.ruleConstraints + [b""], 856 (1, []), 857 )[1] 858 constraintCode = b"\000" + b"".join(self.ruleConstraints) 859 transes = [] 860 for t in self.stateTrans: 861 if sys.byteorder != "big": 862 t.byteswap() 863 transes.append(t.tobytes()) 864 if sys.byteorder != "big": 865 t.byteswap() 866 if not len(transes): 867 self.startStates = [0] 868 oRuleMap = reduce( 869 lambda a, x: (a[0] + len(x), a[1] + [a[0]]), self.rules + [[]], (0, []) 870 )[1] 871 passRanges = [] 872 gidcolmap = dict([(ttFont.getGlyphID(x[0]), x[1]) for x in self.colMap.items()]) 873 for e in grUtils.entries(gidcolmap, sameval=True): 874 if e[1]: 875 passRanges.append((e[0], e[0] + e[1] - 1, e[2][0])) 876 self.numRules = len(self.actions) 877 self.fsmOffset = ( 878 sstruct.calcsize(Silf_pass_format) 879 + 8 880 + len(passRanges) * 6 881 + len(oRuleMap) * 2 882 + 2 * oRuleMap[-1] 883 + 2 884 + 2 * len(self.startStates) 885 + 3 * self.numRules 886 + 3 887 + 4 * self.numRules 888 + 4 889 ) 890 self.pcCode = ( 891 self.fsmOffset + 2 * self.numTransitional * self.numColumns + 1 + base 892 ) 893 self.rcCode = self.pcCode + len(self.passConstraints) 894 self.aCode = self.rcCode + len(constraintCode) 895 self.oDebug = 0 896 # now generate output 897 data = sstruct.pack(Silf_pass_format, self) 898 data += grUtils.bininfo(len(passRanges), 6) 899 data += b"".join(struct.pack(">3H", *p) for p in passRanges) 900 data += struct.pack((">%dH" % len(oRuleMap)), *oRuleMap) 901 flatrules = reduce(lambda a, x: a + x, self.rules, []) 902 data += struct.pack((">%dH" % oRuleMap[-1]), *flatrules) 903 data += struct.pack("BB", self.minRulePreContext, self.maxRulePreContext) 904 data += struct.pack((">%dH" % len(self.startStates)), *self.startStates) 905 data += struct.pack((">%dH" % self.numRules), *self.ruleSortKeys) 906 data += struct.pack(("%dB" % self.numRules), *self.rulePreContexts) 907 data += struct.pack(">BH", self.collisionThreshold, len(self.passConstraints)) 908 data += struct.pack((">%dH" % (self.numRules + 1)), *oConstraints) 909 data += struct.pack((">%dH" % (self.numRules + 1)), *oActions) 910 return ( 911 data 912 + b"".join(transes) 913 + struct.pack("B", 0) 914 + self.passConstraints 915 + constraintCode 916 + b"".join(self.actions) 917 ) 918 919 def toXML(self, writer, ttFont, version=2.0): 920 writesimple("info", self, writer, *pass_attrs_info) 921 writesimple("fsminfo", self, writer, *pass_attrs_fsm) 922 writer.begintag("colmap") 923 writer.newline() 924 wrapline( 925 writer, 926 [ 927 "{}={}".format(*x) 928 for x in sorted( 929 self.colMap.items(), key=lambda x: ttFont.getGlyphID(x[0]) 930 ) 931 ], 932 ) 933 writer.endtag("colmap") 934 writer.newline() 935 writer.begintag("staterulemap") 936 writer.newline() 937 for i, r in enumerate(self.rules): 938 writer.simpletag( 939 "state", 940 number=self.numRows - self.numSuccess + i, 941 rules=" ".join(map(str, r)), 942 ) 943 writer.newline() 944 writer.endtag("staterulemap") 945 writer.newline() 946 writer.begintag("rules") 947 writer.newline() 948 for i in range(len(self.actions)): 949 writer.begintag( 950 "rule", 951 index=i, 952 precontext=self.rulePreContexts[i], 953 sortkey=self.ruleSortKeys[i], 954 ) 955 writer.newline() 956 if len(self.ruleConstraints[i]): 957 writecode("constraint", writer, self.ruleConstraints[i]) 958 writecode("action", writer, self.actions[i]) 959 writer.endtag("rule") 960 writer.newline() 961 writer.endtag("rules") 962 writer.newline() 963 if len(self.passConstraints): 964 writecode("passConstraint", writer, self.passConstraints) 965 if len(self.stateTrans): 966 writer.begintag("fsm") 967 writer.newline() 968 writer.begintag("starts") 969 writer.write(" ".join(map(str, self.startStates))) 970 writer.endtag("starts") 971 writer.newline() 972 for i, s in enumerate(self.stateTrans): 973 writer.begintag("row", _i=i) 974 # no newlines here 975 writer.write(" ".join(map(str, s))) 976 writer.endtag("row") 977 writer.newline() 978 writer.endtag("fsm") 979 writer.newline() 980 981 def fromXML(self, name, attrs, content, ttFont, version=2.0): 982 if name == "info": 983 getSimple(self, attrs, *pass_attrs_info) 984 elif name == "fsminfo": 985 getSimple(self, attrs, *pass_attrs_fsm) 986 elif name == "colmap": 987 e = content_string(content) 988 for w in e.split(): 989 x = w.split("=") 990 if len(x) != 2 or x[0] == "" or x[1] == "": 991 continue 992 self.colMap[x[0]] = int(x[1]) 993 elif name == "staterulemap": 994 for e in content: 995 if not isinstance(e, tuple): 996 continue 997 tag, a, c = e 998 if tag == "state": 999 self.rules.append([int(x) for x in a["rules"].split(" ")]) 1000 elif name == "rules": 1001 for element in content: 1002 if not isinstance(element, tuple): 1003 continue 1004 tag, a, c = element 1005 if tag != "rule": 1006 continue 1007 self.rulePreContexts.append(int(a["precontext"])) 1008 self.ruleSortKeys.append(int(a["sortkey"])) 1009 con = b"" 1010 act = b"" 1011 for e in c: 1012 if not isinstance(e, tuple): 1013 continue 1014 tag, a, subc = e 1015 if tag == "constraint": 1016 con = readcode(subc) 1017 elif tag == "action": 1018 act = readcode(subc) 1019 self.actions.append(act) 1020 self.ruleConstraints.append(con) 1021 elif name == "passConstraint": 1022 self.passConstraints = readcode(content) 1023 elif name == "fsm": 1024 for element in content: 1025 if not isinstance(element, tuple): 1026 continue 1027 tag, a, c = element 1028 if tag == "row": 1029 s = array("H") 1030 e = content_string(c) 1031 s.extend(map(int, e.split())) 1032 self.stateTrans.append(s) 1033 elif tag == "starts": 1034 s = [] 1035 e = content_string(c) 1036 s.extend(map(int, e.split())) 1037 self.startStates = s 1038