1from fontTools.misc.py23 import byteord 2from fontTools.misc import sstruct 3from fontTools.misc.fixedTools import floatToFixedToStr 4from fontTools.misc.textTools import safeEval 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 200def disassemble(aCode): 201 codelen = len(aCode) 202 pc = 0 203 res = [] 204 while pc < codelen: 205 opcode = byteord(aCode[pc:pc+1]) 206 if opcode > len(aCode_info): 207 instr = aCode_info[0] 208 else: 209 instr = aCode_info[opcode] 210 pc += 1 211 if instr[1] != 0 and pc >= codelen : return res 212 if instr[1] == -1: 213 count = byteord(aCode[pc]) 214 fmt = "%dB" % count 215 pc += 1 216 elif instr[1] == 0: 217 fmt = "" 218 else : 219 fmt = instr[1] 220 if fmt == "": 221 res.append(instr[0]) 222 continue 223 parms = struct.unpack_from(fmt, aCode[pc:]) 224 res.append(instr[0] + "(" + ", ".join(map(str, parms)) + ")") 225 pc += struct.calcsize(fmt) 226 return res 227 228instre = re.compile(r"^\s*([^(]+)\s*(?:\(([^)]+)\))?") 229def assemble(instrs): 230 res = b"" 231 for inst in instrs: 232 m = instre.match(inst) 233 if not m or not m.group(1) in aCode_map: 234 continue 235 opcode, parmfmt = aCode_map[m.group(1)] 236 res += struct.pack("B", opcode) 237 if m.group(2): 238 if parmfmt == 0: 239 continue 240 parms = [int(x) for x in re.split(r",\s*", m.group(2))] 241 if parmfmt == -1: 242 l = len(parms) 243 res += struct.pack(("%dB" % (l+1)), l, *parms) 244 else: 245 res += struct.pack(parmfmt, *parms) 246 return res 247 248def writecode(tag, writer, instrs): 249 writer.begintag(tag) 250 writer.newline() 251 for l in disassemble(instrs): 252 writer.write(l) 253 writer.newline() 254 writer.endtag(tag) 255 writer.newline() 256 257def readcode(content): 258 res = [] 259 for e in content_string(content).split('\n'): 260 e = e.strip() 261 if not len(e): continue 262 res.append(e) 263 return assemble(res) 264 265attrs_info=('flags', 'extraAscent', 'extraDescent', 'maxGlyphID', 266 'numLigComp', 'numUserDefn', 'maxCompPerLig', 'direction', 'lbGID') 267attrs_passindexes = ('iSubst', 'iPos', 'iJust', 'iBidi') 268attrs_contexts = ('maxPreContext', 'maxPostContext') 269attrs_attributes = ('attrPseudo', 'attrBreakWeight', 'attrDirectionality', 270 'attrMirroring', 'attrSkipPasses', 'attCollisions') 271pass_attrs_info = ('flags', 'maxRuleLoop', 'maxRuleContext', 'maxBackup', 272 'minRulePreContext', 'maxRulePreContext', 'collisionThreshold') 273pass_attrs_fsm = ('numRows', 'numTransitional', 'numSuccess', 'numColumns') 274 275def writesimple(tag, self, writer, *attrkeys): 276 attrs = dict([(k, getattr(self, k)) for k in attrkeys]) 277 writer.simpletag(tag, **attrs) 278 writer.newline() 279 280def getSimple(self, attrs, *attr_list): 281 for k in attr_list: 282 if k in attrs: 283 setattr(self, k, int(safeEval(attrs[k]))) 284 285def content_string(contents): 286 res = "" 287 for element in contents: 288 if isinstance(element, tuple): continue 289 res += element 290 return res.strip() 291 292def wrapline(writer, dat, length=80): 293 currline = "" 294 for d in dat: 295 if len(currline) > length: 296 writer.write(currline[:-1]) 297 writer.newline() 298 currline = "" 299 currline += d + " " 300 if len(currline): 301 writer.write(currline[:-1]) 302 writer.newline() 303 304class _Object() : 305 pass 306 307class table_S__i_l_f(DefaultTable.DefaultTable): 308 '''Silf table support''' 309 310 def __init__(self, tag=None): 311 DefaultTable.DefaultTable.__init__(self, tag) 312 self.silfs = [] 313 314 def decompile(self, data, ttFont): 315 sstruct.unpack2(Silf_hdr_format, data, self) 316 self.version = float(floatToFixedToStr(self.version, precisionBits=16)) 317 if self.version >= 5.0: 318 (data, self.scheme) = grUtils.decompress(data) 319 sstruct.unpack2(Silf_hdr_format_3, data, self) 320 base = sstruct.calcsize(Silf_hdr_format_3) 321 elif self.version < 3.0: 322 self.numSilf = struct.unpack('>H', data[4:6]) 323 self.scheme = 0 324 self.compilerVersion = 0 325 base = 8 326 else: 327 self.scheme = 0 328 sstruct.unpack2(Silf_hdr_format_3, data, self) 329 base = sstruct.calcsize(Silf_hdr_format_3) 330 331 silfoffsets = struct.unpack_from(('>%dL' % self.numSilf), data[base:]) 332 for offset in silfoffsets: 333 s = Silf() 334 self.silfs.append(s) 335 s.decompile(data[offset:], ttFont, self.version) 336 337 def compile(self, ttFont): 338 self.numSilf = len(self.silfs) 339 if self.version < 3.0: 340 hdr = sstruct.pack(Silf_hdr_format, self) 341 hdr += struct.pack(">HH", self.numSilf, 0) 342 else: 343 hdr = sstruct.pack(Silf_hdr_format_3, self) 344 offset = len(hdr) + 4 * self.numSilf 345 data = b"" 346 for s in self.silfs: 347 hdr += struct.pack(">L", offset) 348 subdata = s.compile(ttFont, self.version) 349 offset += len(subdata) 350 data += subdata 351 if self.version >= 5.0: 352 return grUtils.compress(self.scheme, hdr+data) 353 return hdr+data 354 355 def toXML(self, writer, ttFont): 356 writer.comment('Attributes starting with _ are informative only') 357 writer.newline() 358 writer.simpletag('version', version=self.version, 359 compilerVersion=self.compilerVersion, compressionScheme=self.scheme) 360 writer.newline() 361 for s in self.silfs: 362 writer.begintag('silf') 363 writer.newline() 364 s.toXML(writer, ttFont, self.version) 365 writer.endtag('silf') 366 writer.newline() 367 368 def fromXML(self, name, attrs, content, ttFont): 369 if name == 'version': 370 self.scheme=int(safeEval(attrs['compressionScheme'])) 371 self.version = float(safeEval(attrs['version'])) 372 self.compilerVersion = int(safeEval(attrs['compilerVersion'])) 373 return 374 if name == 'silf': 375 s = Silf() 376 self.silfs.append(s) 377 for element in content: 378 if not isinstance(element, tuple): continue 379 tag, attrs, subcontent = element 380 s.fromXML(tag, attrs, subcontent, ttFont, self.version) 381 382class Silf(object): 383 '''A particular Silf subtable''' 384 385 def __init__(self): 386 self.passes = [] 387 self.scriptTags = [] 388 self.critFeatures = [] 389 self.jLevels = [] 390 self.pMap = {} 391 392 def decompile(self, data, ttFont, version=2.0): 393 if version >= 3.0 : 394 _, data = sstruct.unpack2(Silf_part1_format_v3, data, self) 395 self.ruleVersion = float(floatToFixedToStr(self.ruleVersion, precisionBits=16)) 396 _, data = sstruct.unpack2(Silf_part1_format, data, self) 397 for jlevel in range(self.numJLevels): 398 j, data = sstruct.unpack2(Silf_justify_format, data, _Object()) 399 self.jLevels.append(j) 400 _, data = sstruct.unpack2(Silf_part2_format, data, self) 401 if self.numCritFeatures: 402 self.critFeatures = struct.unpack_from(('>%dH' % self.numCritFeatures), data) 403 data = data[self.numCritFeatures * 2 + 1:] 404 (numScriptTag,) = struct.unpack_from('B', data) 405 if numScriptTag: 406 self.scriptTags = [struct.unpack("4s", data[x:x+4])[0].decode("ascii") for x in range(1, 1 + 4 * numScriptTag, 4)] 407 data = data[1 + 4 * numScriptTag:] 408 (self.lbGID,) = struct.unpack('>H', data[:2]) 409 if self.numPasses: 410 self.oPasses = struct.unpack(('>%dL' % (self.numPasses+1)), data[2:6+4*self.numPasses]) 411 data = data[6 + 4 * self.numPasses:] 412 (numPseudo,) = struct.unpack(">H", data[:2]) 413 for i in range(numPseudo): 414 if version >= 3.0: 415 pseudo = sstruct.unpack(Silf_pseudomap_format, data[8+6*i:14+6*i], _Object()) 416 else: 417 pseudo = sstruct.unpack(Silf_pseudomap_format_h, data[8+4*i:12+4*i], _Object()) 418 self.pMap[pseudo.unicode] = ttFont.getGlyphName(pseudo.nPseudo) 419 data = data[8 + 6 * numPseudo:] 420 currpos = (sstruct.calcsize(Silf_part1_format) 421 + sstruct.calcsize(Silf_justify_format) * self.numJLevels 422 + sstruct.calcsize(Silf_part2_format) + 2 * self.numCritFeatures 423 + 1 + 1 + 4 * numScriptTag + 6 + 4 * self.numPasses + 8 + 6 * numPseudo) 424 if version >= 3.0: 425 currpos += sstruct.calcsize(Silf_part1_format_v3) 426 self.classes = Classes() 427 self.classes.decompile(data, ttFont, version) 428 for i in range(self.numPasses): 429 p = Pass() 430 self.passes.append(p) 431 p.decompile(data[self.oPasses[i]-currpos:self.oPasses[i+1]-currpos], 432 ttFont, version) 433 434 def compile(self, ttFont, version=2.0): 435 self.numPasses = len(self.passes) 436 self.numJLevels = len(self.jLevels) 437 self.numCritFeatures = len(self.critFeatures) 438 numPseudo = len(self.pMap) 439 data = b"" 440 if version >= 3.0: 441 hdroffset = sstruct.calcsize(Silf_part1_format_v3) 442 else: 443 hdroffset = 0 444 data += sstruct.pack(Silf_part1_format, self) 445 for j in self.jLevels: 446 data += sstruct.pack(Silf_justify_format, j) 447 data += sstruct.pack(Silf_part2_format, self) 448 if self.numCritFeatures: 449 data += struct.pack((">%dH" % self.numCritFeaturs), *self.critFeatures) 450 data += struct.pack("BB", 0, len(self.scriptTags)) 451 if len(self.scriptTags): 452 tdata = [struct.pack("4s", x.encode("ascii")) for x in self.scriptTags] 453 data += b"".join(tdata) 454 data += struct.pack(">H", self.lbGID) 455 self.passOffset = len(data) 456 457 data1 = grUtils.bininfo(numPseudo, 6) 458 currpos = hdroffset + len(data) + 4 * (self.numPasses + 1) 459 self.pseudosOffset = currpos + len(data1) 460 for u, p in sorted(self.pMap.items()): 461 data1 += struct.pack((">LH" if version >= 3.0 else ">HH"), 462 u, ttFont.getGlyphID(p)) 463 data1 += self.classes.compile(ttFont, version) 464 currpos += len(data1) 465 data2 = b"" 466 datao = b"" 467 for i, p in enumerate(self.passes): 468 base = currpos + len(data2) 469 datao += struct.pack(">L", base) 470 data2 += p.compile(ttFont, base, version) 471 datao += struct.pack(">L", currpos + len(data2)) 472 473 if version >= 3.0: 474 data3 = sstruct.pack(Silf_part1_format_v3, self) 475 else: 476 data3 = b"" 477 return data3 + data + datao + data1 + data2 478 479 480 def toXML(self, writer, ttFont, version=2.0): 481 if version >= 3.0: 482 writer.simpletag('version', ruleVersion=self.ruleVersion) 483 writer.newline() 484 writesimple('info', self, writer, *attrs_info) 485 writesimple('passindexes', self, writer, *attrs_passindexes) 486 writesimple('contexts', self, writer, *attrs_contexts) 487 writesimple('attributes', self, writer, *attrs_attributes) 488 if len(self.jLevels): 489 writer.begintag('justifications') 490 writer.newline() 491 jformat, jnames, jfixes = sstruct.getformat(Silf_justify_format) 492 for i, j in enumerate(self.jLevels): 493 attrs = dict([(k, getattr(j, k)) for k in jnames]) 494 writer.simpletag('justify', **attrs) 495 writer.newline() 496 writer.endtag('justifications') 497 writer.newline() 498 if len(self.critFeatures): 499 writer.begintag('critFeatures') 500 writer.newline() 501 writer.write(" ".join(map(str, self.critFeatures))) 502 writer.newline() 503 writer.endtag('critFeatures') 504 writer.newline() 505 if len(self.scriptTags): 506 writer.begintag('scriptTags') 507 writer.newline() 508 writer.write(" ".join(self.scriptTags)) 509 writer.newline() 510 writer.endtag('scriptTags') 511 writer.newline() 512 if self.pMap: 513 writer.begintag('pseudoMap') 514 writer.newline() 515 for k, v in sorted(self.pMap.items()): 516 writer.simpletag('pseudo', unicode=hex(k), pseudo=v) 517 writer.newline() 518 writer.endtag('pseudoMap') 519 writer.newline() 520 self.classes.toXML(writer, ttFont, version) 521 if len(self.passes): 522 writer.begintag('passes') 523 writer.newline() 524 for i, p in enumerate(self.passes): 525 writer.begintag('pass', _index=i) 526 writer.newline() 527 p.toXML(writer, ttFont, version) 528 writer.endtag('pass') 529 writer.newline() 530 writer.endtag('passes') 531 writer.newline() 532 533 def fromXML(self, name, attrs, content, ttFont, version=2.0): 534 if name == 'version': 535 self.ruleVersion = float(safeEval(attrs.get('ruleVersion', "0"))) 536 if name == 'info': 537 getSimple(self, attrs, *attrs_info) 538 elif name == 'passindexes': 539 getSimple(self, attrs, *attrs_passindexes) 540 elif name == 'contexts': 541 getSimple(self, attrs, *attrs_contexts) 542 elif name == 'attributes': 543 getSimple(self, attrs, *attrs_attributes) 544 elif name == 'justifications': 545 for element in content: 546 if not isinstance(element, tuple): continue 547 (tag, attrs, subcontent) = element 548 if tag == 'justify': 549 j = _Object() 550 for k, v in attrs.items(): 551 setattr(j, k, int(v)) 552 self.jLevels.append(j) 553 elif name == 'critFeatures': 554 self.critFeatures = [] 555 element = content_string(content) 556 self.critFeatures.extend(map(int, element.split())) 557 elif name == 'scriptTags': 558 self.scriptTags = [] 559 element = content_string(content) 560 for n in element.split(): 561 self.scriptTags.append(n) 562 elif name == 'pseudoMap': 563 self.pMap = {} 564 for element in content: 565 if not isinstance(element, tuple): continue 566 (tag, attrs, subcontent) = element 567 if tag == 'pseudo': 568 k = int(attrs['unicode'], 16) 569 v = attrs['pseudo'] 570 self.pMap[k] = v 571 elif name == 'classes': 572 self.classes = Classes() 573 for element in content: 574 if not isinstance(element, tuple): continue 575 tag, attrs, subcontent = element 576 self.classes.fromXML(tag, attrs, subcontent, ttFont, version) 577 elif name == 'passes': 578 for element in content: 579 if not isinstance(element, tuple): continue 580 tag, attrs, subcontent = element 581 if tag == 'pass': 582 p = Pass() 583 for e in subcontent: 584 if not isinstance(e, tuple): continue 585 p.fromXML(e[0], e[1], e[2], ttFont, version) 586 self.passes.append(p) 587 588 589class Classes(object): 590 591 def __init__(self): 592 self.linear = [] 593 self.nonLinear = [] 594 595 def decompile(self, data, ttFont, version=2.0): 596 sstruct.unpack2(Silf_classmap_format, data, self) 597 if version >= 4.0 : 598 oClasses = struct.unpack((">%dL" % (self.numClass+1)), 599 data[4:8+4*self.numClass]) 600 else: 601 oClasses = struct.unpack((">%dH" % (self.numClass+1)), 602 data[4:6+2*self.numClass]) 603 for s,e in zip(oClasses[:self.numLinear], oClasses[1:self.numLinear+1]): 604 self.linear.append(ttFont.getGlyphName(x) for x in 605 struct.unpack((">%dH" % ((e-s)/2)), data[s:e])) 606 for s,e in zip(oClasses[self.numLinear:self.numClass], 607 oClasses[self.numLinear+1:self.numClass+1]): 608 nonLinids = [struct.unpack(">HH", data[x:x+4]) for x in range(s+8, e, 4)] 609 nonLin = dict([(ttFont.getGlyphName(x[0]), x[1]) for x in nonLinids]) 610 self.nonLinear.append(nonLin) 611 612 def compile(self, ttFont, version=2.0): 613 data = b"" 614 oClasses = [] 615 if version >= 4.0: 616 offset = 8 + 4 * (len(self.linear) + len(self.nonLinear)) 617 else: 618 offset = 6 + 2 * (len(self.linear) + len(self.nonLinear)) 619 for l in self.linear: 620 oClasses.append(len(data) + offset) 621 gs = [ttFont.getGlyphID(x) for x in l] 622 data += struct.pack((">%dH" % len(l)), *gs) 623 for l in self.nonLinear: 624 oClasses.append(len(data) + offset) 625 gs = [(ttFont.getGlyphID(x[0]), x[1]) for x in l.items()] 626 data += grUtils.bininfo(len(gs)) 627 data += b"".join([struct.pack(">HH", *x) for x in sorted(gs)]) 628 oClasses.append(len(data) + offset) 629 self.numClass = len(oClasses) - 1 630 self.numLinear = len(self.linear) 631 return sstruct.pack(Silf_classmap_format, self) + \ 632 struct.pack(((">%dL" if version >= 4.0 else ">%dH") % len(oClasses)), 633 *oClasses) + data 634 635 def toXML(self, writer, ttFont, version=2.0): 636 writer.begintag('classes') 637 writer.newline() 638 writer.begintag('linearClasses') 639 writer.newline() 640 for i,l in enumerate(self.linear): 641 writer.begintag('linear', _index=i) 642 writer.newline() 643 wrapline(writer, l) 644 writer.endtag('linear') 645 writer.newline() 646 writer.endtag('linearClasses') 647 writer.newline() 648 writer.begintag('nonLinearClasses') 649 writer.newline() 650 for i, l in enumerate(self.nonLinear): 651 writer.begintag('nonLinear', _index=i + self.numLinear) 652 writer.newline() 653 for inp, ind in l.items(): 654 writer.simpletag('map', glyph=inp, index=ind) 655 writer.newline() 656 writer.endtag('nonLinear') 657 writer.newline() 658 writer.endtag('nonLinearClasses') 659 writer.newline() 660 writer.endtag('classes') 661 writer.newline() 662 663 def fromXML(self, name, attrs, content, ttFont, version=2.0): 664 if name == 'linearClasses': 665 for element in content: 666 if not isinstance(element, tuple): continue 667 tag, attrs, subcontent = element 668 if tag == 'linear': 669 l = content_string(subcontent).split() 670 self.linear.append(l) 671 elif name == 'nonLinearClasses': 672 for element in content: 673 if not isinstance(element, tuple): continue 674 tag, attrs, subcontent = element 675 if tag =='nonLinear': 676 l = {} 677 for e in subcontent: 678 if not isinstance(e, tuple): continue 679 tag, attrs, subsubcontent = e 680 if tag == 'map': 681 l[attrs['glyph']] = int(safeEval(attrs['index'])) 682 self.nonLinear.append(l) 683 684class Pass(object): 685 686 def __init__(self): 687 self.colMap = {} 688 self.rules = [] 689 self.rulePreContexts = [] 690 self.ruleSortKeys = [] 691 self.ruleConstraints = [] 692 self.passConstraints = b"" 693 self.actions = [] 694 self.stateTrans = [] 695 self.startStates = [] 696 697 def decompile(self, data, ttFont, version=2.0): 698 _, data = sstruct.unpack2(Silf_pass_format, data, self) 699 (numRange, _, _, _) = struct.unpack(">4H", data[:8]) 700 data = data[8:] 701 for i in range(numRange): 702 (first, last, col) = struct.unpack(">3H", data[6*i:6*i+6]) 703 for g in range(first, last+1): 704 self.colMap[ttFont.getGlyphName(g)] = col 705 data = data[6*numRange:] 706 oRuleMap = struct.unpack_from((">%dH" % (self.numSuccess + 1)), data) 707 data = data[2+2*self.numSuccess:] 708 rules = struct.unpack_from((">%dH" % oRuleMap[-1]), data) 709 self.rules = [rules[s:e] for (s,e) in zip(oRuleMap, oRuleMap[1:])] 710 data = data[2*oRuleMap[-1]:] 711 (self.minRulePreContext, self.maxRulePreContext) = struct.unpack('BB', data[:2]) 712 numStartStates = self.maxRulePreContext - self.minRulePreContext + 1 713 self.startStates = struct.unpack((">%dH" % numStartStates), 714 data[2:2 + numStartStates * 2]) 715 data = data[2+numStartStates*2:] 716 self.ruleSortKeys = struct.unpack((">%dH" % self.numRules), data[:2 * self.numRules]) 717 data = data[2*self.numRules:] 718 self.rulePreContexts = struct.unpack(("%dB" % self.numRules), data[:self.numRules]) 719 data = data[self.numRules:] 720 (self.collisionThreshold, pConstraint) = struct.unpack(">BH", data[:3]) 721 oConstraints = list(struct.unpack((">%dH" % (self.numRules + 1)), 722 data[3:5 + self.numRules * 2])) 723 data = data[5 + self.numRules * 2:] 724 oActions = list(struct.unpack((">%dH" % (self.numRules + 1)), 725 data[:2 + self.numRules * 2])) 726 data = data[2 * self.numRules + 2:] 727 for i in range(self.numTransitional): 728 a = array("H", data[i*self.numColumns*2:(i+1)*self.numColumns*2]) 729 if sys.byteorder != "big": a.byteswap() 730 self.stateTrans.append(a) 731 data = data[self.numTransitional * self.numColumns * 2 + 1:] 732 self.passConstraints = data[:pConstraint] 733 data = data[pConstraint:] 734 for i in range(len(oConstraints)-2,-1,-1): 735 if oConstraints[i] == 0 : 736 oConstraints[i] = oConstraints[i+1] 737 self.ruleConstraints = [(data[s:e] if (e-s > 1) else b"") for (s,e) in zip(oConstraints, oConstraints[1:])] 738 data = data[oConstraints[-1]:] 739 self.actions = [(data[s:e] if (e-s > 1) else "") for (s,e) in zip(oActions, oActions[1:])] 740 data = data[oActions[-1]:] 741 # not using debug 742 743 def compile(self, ttFont, base, version=2.0): 744 # build it all up backwards 745 oActions = reduce(lambda a, x: (a[0]+len(x), a[1]+[a[0]]), self.actions + [b""], (0, []))[1] 746 oConstraints = reduce(lambda a, x: (a[0]+len(x), a[1]+[a[0]]), self.ruleConstraints + [b""], (1, []))[1] 747 constraintCode = b"\000" + b"".join(self.ruleConstraints) 748 transes = [] 749 for t in self.stateTrans: 750 if sys.byteorder != "big": t.byteswap() 751 transes.append(t.tobytes()) 752 if sys.byteorder != "big": t.byteswap() 753 if not len(transes): 754 self.startStates = [0] 755 oRuleMap = reduce(lambda a, x: (a[0]+len(x), a[1]+[a[0]]), self.rules+[[]], (0, []))[1] 756 passRanges = [] 757 gidcolmap = dict([(ttFont.getGlyphID(x[0]), x[1]) for x in self.colMap.items()]) 758 for e in grUtils.entries(gidcolmap, sameval = True): 759 if e[1]: 760 passRanges.append((e[0], e[0]+e[1]-1, e[2][0])) 761 self.numRules = len(self.actions) 762 self.fsmOffset = (sstruct.calcsize(Silf_pass_format) + 8 + len(passRanges) * 6 763 + len(oRuleMap) * 2 + 2 * oRuleMap[-1] + 2 764 + 2 * len(self.startStates) + 3 * self.numRules + 3 765 + 4 * self.numRules + 4) 766 self.pcCode = self.fsmOffset + 2*self.numTransitional*self.numColumns + 1 + base 767 self.rcCode = self.pcCode + len(self.passConstraints) 768 self.aCode = self.rcCode + len(constraintCode) 769 self.oDebug = 0 770 # now generate output 771 data = sstruct.pack(Silf_pass_format, self) 772 data += grUtils.bininfo(len(passRanges), 6) 773 data += b"".join(struct.pack(">3H", *p) for p in passRanges) 774 data += struct.pack((">%dH" % len(oRuleMap)), *oRuleMap) 775 flatrules = reduce(lambda a,x: a+x, self.rules, []) 776 data += struct.pack((">%dH" % oRuleMap[-1]), *flatrules) 777 data += struct.pack("BB", self.minRulePreContext, self.maxRulePreContext) 778 data += struct.pack((">%dH" % len(self.startStates)), *self.startStates) 779 data += struct.pack((">%dH" % self.numRules), *self.ruleSortKeys) 780 data += struct.pack(("%dB" % self.numRules), *self.rulePreContexts) 781 data += struct.pack(">BH", self.collisionThreshold, len(self.passConstraints)) 782 data += struct.pack((">%dH" % (self.numRules+1)), *oConstraints) 783 data += struct.pack((">%dH" % (self.numRules+1)), *oActions) 784 return data + b"".join(transes) + struct.pack("B", 0) + \ 785 self.passConstraints + constraintCode + b"".join(self.actions) 786 787 def toXML(self, writer, ttFont, version=2.0): 788 writesimple('info', self, writer, *pass_attrs_info) 789 writesimple('fsminfo', self, writer, *pass_attrs_fsm) 790 writer.begintag('colmap') 791 writer.newline() 792 wrapline(writer, ["{}={}".format(*x) for x in sorted(self.colMap.items(), 793 key=lambda x:ttFont.getGlyphID(x[0]))]) 794 writer.endtag('colmap') 795 writer.newline() 796 writer.begintag('staterulemap') 797 writer.newline() 798 for i, r in enumerate(self.rules): 799 writer.simpletag('state', number = self.numRows - self.numSuccess + i, 800 rules = " ".join(map(str, r))) 801 writer.newline() 802 writer.endtag('staterulemap') 803 writer.newline() 804 writer.begintag('rules') 805 writer.newline() 806 for i in range(len(self.actions)): 807 writer.begintag('rule', index=i, precontext=self.rulePreContexts[i], 808 sortkey=self.ruleSortKeys[i]) 809 writer.newline() 810 if len(self.ruleConstraints[i]): 811 writecode('constraint', writer, self.ruleConstraints[i]) 812 writecode('action', writer, self.actions[i]) 813 writer.endtag('rule') 814 writer.newline() 815 writer.endtag('rules') 816 writer.newline() 817 if len(self.passConstraints): 818 writecode('passConstraint', writer, self.passConstraints) 819 if len(self.stateTrans): 820 writer.begintag('fsm') 821 writer.newline() 822 writer.begintag('starts') 823 writer.write(" ".join(map(str, self.startStates))) 824 writer.endtag('starts') 825 writer.newline() 826 for i, s in enumerate(self.stateTrans): 827 writer.begintag('row', _i=i) 828 # no newlines here 829 writer.write(" ".join(map(str, s))) 830 writer.endtag('row') 831 writer.newline() 832 writer.endtag('fsm') 833 writer.newline() 834 835 def fromXML(self, name, attrs, content, ttFont, version=2.0): 836 if name == 'info': 837 getSimple(self, attrs, *pass_attrs_info) 838 elif name == 'fsminfo': 839 getSimple(self, attrs, *pass_attrs_fsm) 840 elif name == 'colmap': 841 e = content_string(content) 842 for w in e.split(): 843 x = w.split('=') 844 if len(x) != 2 or x[0] == '' or x[1] == '': continue 845 self.colMap[x[0]] = int(x[1]) 846 elif name == 'staterulemap': 847 for e in content: 848 if not isinstance(e, tuple): continue 849 tag, a, c = e 850 if tag == 'state': 851 self.rules.append([int(x) for x in a['rules'].split(" ")]) 852 elif name == 'rules': 853 for element in content: 854 if not isinstance(element, tuple): continue 855 tag, a, c = element 856 if tag != 'rule': continue 857 self.rulePreContexts.append(int(a['precontext'])) 858 self.ruleSortKeys.append(int(a['sortkey'])) 859 con = b"" 860 act = b"" 861 for e in c: 862 if not isinstance(e, tuple): continue 863 tag, a, subc = e 864 if tag == 'constraint': 865 con = readcode(subc) 866 elif tag == 'action': 867 act = readcode(subc) 868 self.actions.append(act) 869 self.ruleConstraints.append(con) 870 elif name == 'passConstraint': 871 self.passConstraints = readcode(content) 872 elif name == 'fsm': 873 for element in content: 874 if not isinstance(element, tuple): continue 875 tag, a, c = element 876 if tag == 'row': 877 s = array('H') 878 e = content_string(c) 879 s.extend(map(int, e.split())) 880 self.stateTrans.append(s) 881 elif tag == 'starts': 882 s = [] 883 e = content_string(c) 884 s.extend(map(int, e.split())) 885 self.startStates = s 886 887