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