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