from fontTools.misc.py23 import * from fontTools.misc import sstruct from fontTools.misc.fixedTools import floatToFixedToStr from fontTools.misc.textTools import safeEval from itertools import * from . import DefaultTable from . import grUtils from array import array from functools import reduce import struct, operator, warnings, re, sys Silf_hdr_format = ''' > version: 16.16F ''' Silf_hdr_format_3 = ''' > version: 16.16F compilerVersion: L numSilf: H x x ''' Silf_part1_format_v3 = ''' > ruleVersion: 16.16F passOffset: H pseudosOffset: H ''' Silf_part1_format = ''' > maxGlyphID: H extraAscent: h extraDescent: h numPasses: B iSubst: B iPos: B iJust: B iBidi: B flags: B maxPreContext: B maxPostContext: B attrPseudo: B attrBreakWeight: B attrDirectionality: B attrMirroring: B attrSkipPasses: B numJLevels: B ''' Silf_justify_format = ''' > attrStretch: B attrShrink: B attrStep: B attrWeight: B runto: B x x x ''' Silf_part2_format = ''' > numLigComp: H numUserDefn: B maxCompPerLig: B direction: B attCollisions: B x x x numCritFeatures: B ''' Silf_pseudomap_format = ''' > unicode: L nPseudo: H ''' Silf_pseudomap_format_h = ''' > unicode: H nPseudo: H ''' Silf_classmap_format = ''' > numClass: H numLinear: H ''' Silf_lookupclass_format = ''' > numIDs: H searchRange: H entrySelector: H rangeShift: H ''' Silf_lookuppair_format = ''' > glyphId: H index: H ''' Silf_pass_format = ''' > flags: B maxRuleLoop: B maxRuleContext: B maxBackup: B numRules: H fsmOffset: H pcCode: L rcCode: L aCode: L oDebug: L numRows: H numTransitional: H numSuccess: H numColumns: H ''' aCode_info = ( ("NOP", 0), ("PUSH_BYTE", "b"), ("PUSH_BYTE_U", "B"), ("PUSH_SHORT", ">h"), ("PUSH_SHORT_U", ">H"), ("PUSH_LONG", ">L"), ("ADD", 0), ("SUB", 0), ("MUL", 0), ("DIV", 0), ("MIN", 0), ("MAX", 0), ("NEG", 0), ("TRUNC8", 0), ("TRUNC16", 0), ("COND", 0), ("AND", 0), # x10 ("OR", 0), ("NOT", 0), ("EQUAL", 0), ("NOT_EQ", 0), ("LESS", 0), ("GTR", 0), ("LESS_EQ", 0), ("GTR_EQ", 0), ("NEXT", 0), ("NEXT_N", "b"), ("COPY_NEXT", 0), ("PUT_GLYPH_8BIT_OBS", "B"), ("PUT_SUBS_8BIT_OBS", "bBB"), ("PUT_COPY", "b"), ("INSERT", 0), ("DELETE", 0), # x20 ("ASSOC", -1), ("CNTXT_ITEM", "bB"), ("ATTR_SET", "B"), ("ATTR_ADD", "B"), ("ATTR_SUB", "B"), ("ATTR_SET_SLOT", "B"), ("IATTR_SET_SLOT", "BB"), ("PUSH_SLOT_ATTR", "Bb"), ("PUSH_GLYPH_ATTR_OBS", "Bb"), ("PUSH_GLYPH_METRIC", "Bbb"), ("PUSH_FEAT", "Bb"), ("PUSH_ATT_TO_GATTR_OBS", "Bb"), ("PUSH_ATT_TO_GLYPH_METRIC", "Bbb"), ("PUSH_ISLOT_ATTR", "Bbb"), ("PUSH_IGLYPH_ATTR", "Bbb"), ("POP_RET", 0), # x30 ("RET_ZERO", 0), ("RET_TRUE", 0), ("IATTR_SET", "BB"), ("IATTR_ADD", "BB"), ("IATTR_SUB", "BB"), ("PUSH_PROC_STATE", "B"), ("PUSH_VERSION", 0), ("PUT_SUBS", ">bHH"), ("PUT_SUBS2", 0), ("PUT_SUBS3", 0), ("PUT_GLYPH", ">H"), ("PUSH_GLYPH_ATTR", ">Hb"), ("PUSH_ATT_TO_GLYPH_ATTR", ">Hb"), ("BITOR", 0), ("BITAND", 0), ("BITNOT", 0), # x40 ("BITSET", ">HH"), ("SET_FEAT", "Bb") ) aCode_map = dict([(x[0], (i, x[1])) for i,x in enumerate(aCode_info)]) def disassemble(aCode): codelen = len(aCode) pc = 0 res = [] while pc < codelen: opcode = byteord(aCode[pc:pc+1]) if opcode > len(aCode_info): instr = aCode_info[0] else: instr = aCode_info[opcode] pc += 1 if instr[1] != 0 and pc >= codelen : return res if instr[1] == -1: count = byteord(aCode[pc]) fmt = "%dB" % count pc += 1 elif instr[1] == 0: fmt = "" else : fmt = instr[1] if fmt == "": res.append(instr[0]) continue parms = struct.unpack_from(fmt, aCode[pc:]) res.append(instr[0] + "(" + ", ".join(map(str, parms)) + ")") pc += struct.calcsize(fmt) return res instre = re.compile(r"^\s*([^(]+)\s*(?:\(([^)]+)\))?") def assemble(instrs): res = b"" for inst in instrs: m = instre.match(inst) if not m or not m.group(1) in aCode_map: continue opcode, parmfmt = aCode_map[m.group(1)] res += struct.pack("B", opcode) if m.group(2): if parmfmt == 0: continue parms = [int(x) for x in re.split(r",\s*", m.group(2))] if parmfmt == -1: l = len(parms) res += struct.pack(("%dB" % (l+1)), l, *parms) else: res += struct.pack(parmfmt, *parms) return res def writecode(tag, writer, instrs): writer.begintag(tag) writer.newline() for l in disassemble(instrs): writer.write(l) writer.newline() writer.endtag(tag) writer.newline() def readcode(content): res = [] for e in content_string(content).split('\n'): e = e.strip() if not len(e): continue res.append(e) return assemble(res) attrs_info=('flags', 'extraAscent', 'extraDescent', 'maxGlyphID', 'numLigComp', 'numUserDefn', 'maxCompPerLig', 'direction', 'lbGID') attrs_passindexes = ('iSubst', 'iPos', 'iJust', 'iBidi') attrs_contexts = ('maxPreContext', 'maxPostContext') attrs_attributes = ('attrPseudo', 'attrBreakWeight', 'attrDirectionality', 'attrMirroring', 'attrSkipPasses', 'attCollisions') pass_attrs_info = ('flags', 'maxRuleLoop', 'maxRuleContext', 'maxBackup', 'minRulePreContext', 'maxRulePreContext', 'collisionThreshold') pass_attrs_fsm = ('numRows', 'numTransitional', 'numSuccess', 'numColumns') def writesimple(tag, self, writer, *attrkeys): attrs = dict([(k, getattr(self, k)) for k in attrkeys]) writer.simpletag(tag, **attrs) writer.newline() def getSimple(self, attrs, *attr_list): for k in attr_list: if k in attrs: setattr(self, k, int(safeEval(attrs[k]))) def content_string(contents): res = "" for element in contents: if isinstance(element, tuple): continue res += element return res.strip() def wrapline(writer, dat, length=80): currline = "" for d in dat: if len(currline) > length: writer.write(currline[:-1]) writer.newline() currline = "" currline += d + " " if len(currline): writer.write(currline[:-1]) writer.newline() class _Object() : pass class table_S__i_l_f(DefaultTable.DefaultTable): '''Silf table support''' def __init__(self, tag=None): DefaultTable.DefaultTable.__init__(self, tag) self.silfs = [] def decompile(self, data, ttFont): sstruct.unpack2(Silf_hdr_format, data, self) self.version = float(floatToFixedToStr(self.version, precisionBits=16)) if self.version >= 5.0: (data, self.scheme) = grUtils.decompress(data) sstruct.unpack2(Silf_hdr_format_3, data, self) base = sstruct.calcsize(Silf_hdr_format_3) elif self.version < 3.0: self.numSilf = struct.unpack('>H', data[4:6]) self.scheme = 0 self.compilerVersion = 0 base = 8 else: self.scheme = 0 sstruct.unpack2(Silf_hdr_format_3, data, self) base = sstruct.calcsize(Silf_hdr_format_3) silfoffsets = struct.unpack_from(('>%dL' % self.numSilf), data[base:]) for offset in silfoffsets: s = Silf() self.silfs.append(s) s.decompile(data[offset:], ttFont, self.version) def compile(self, ttFont): self.numSilf = len(self.silfs) if self.version < 3.0: hdr = sstruct.pack(Silf_hdr_format, self) hdr += struct.pack(">HH", self.numSilf, 0) else: hdr = sstruct.pack(Silf_hdr_format_3, self) offset = len(hdr) + 4 * self.numSilf data = b"" for s in self.silfs: hdr += struct.pack(">L", offset) subdata = s.compile(ttFont, self.version) offset += len(subdata) data += subdata if self.version >= 5.0: return grUtils.compress(self.scheme, hdr+data) return hdr+data def toXML(self, writer, ttFont): writer.comment('Attributes starting with _ are informative only') writer.newline() writer.simpletag('version', version=self.version, compilerVersion=self.compilerVersion, compressionScheme=self.scheme) writer.newline() for s in self.silfs: writer.begintag('silf') writer.newline() s.toXML(writer, ttFont, self.version) writer.endtag('silf') writer.newline() def fromXML(self, name, attrs, content, ttFont): if name == 'version': self.scheme=int(safeEval(attrs['compressionScheme'])) self.version = float(safeEval(attrs['version'])) self.compilerVersion = int(safeEval(attrs['compilerVersion'])) return if name == 'silf': s = Silf() self.silfs.append(s) for element in content: if not isinstance(element, tuple): continue tag, attrs, subcontent = element s.fromXML(tag, attrs, subcontent, ttFont, self.version) class Silf(object): '''A particular Silf subtable''' def __init__(self): self.passes = [] self.scriptTags = [] self.critFeatures = [] self.jLevels = [] self.pMap = {} def decompile(self, data, ttFont, version=2.0): if version >= 3.0 : _, data = sstruct.unpack2(Silf_part1_format_v3, data, self) self.ruleVersion = float(floatToFixedToStr(self.ruleVersion, precisionBits=16)) _, data = sstruct.unpack2(Silf_part1_format, data, self) for jlevel in range(self.numJLevels): j, data = sstruct.unpack2(Silf_justify_format, data, _Object()) self.jLevels.append(j) _, data = sstruct.unpack2(Silf_part2_format, data, self) if self.numCritFeatures: self.critFeatures = struct.unpack_from(('>%dH' % self.numCritFeatures), data) data = data[self.numCritFeatures * 2 + 1:] (numScriptTag,) = struct.unpack_from('B', data) if numScriptTag: self.scriptTags = [struct.unpack("4s", data[x:x+4])[0].decode("ascii") for x in range(1, 1 + 4 * numScriptTag, 4)] data = data[1 + 4 * numScriptTag:] (self.lbGID,) = struct.unpack('>H', data[:2]) if self.numPasses: self.oPasses = struct.unpack(('>%dL' % (self.numPasses+1)), data[2:6+4*self.numPasses]) data = data[6 + 4 * self.numPasses:] (numPseudo,) = struct.unpack(">H", data[:2]) for i in range(numPseudo): if version >= 3.0: pseudo = sstruct.unpack(Silf_pseudomap_format, data[8+6*i:14+6*i], _Object()) else: pseudo = sstruct.unpack(Silf_pseudomap_format_h, data[8+4*i:12+4*i], _Object()) self.pMap[pseudo.unicode] = ttFont.getGlyphName(pseudo.nPseudo) data = data[8 + 6 * numPseudo:] currpos = (sstruct.calcsize(Silf_part1_format) + sstruct.calcsize(Silf_justify_format) * self.numJLevels + sstruct.calcsize(Silf_part2_format) + 2 * self.numCritFeatures + 1 + 1 + 4 * numScriptTag + 6 + 4 * self.numPasses + 8 + 6 * numPseudo) if version >= 3.0: currpos += sstruct.calcsize(Silf_part1_format_v3) self.classes = Classes() self.classes.decompile(data, ttFont, version) for i in range(self.numPasses): p = Pass() self.passes.append(p) p.decompile(data[self.oPasses[i]-currpos:self.oPasses[i+1]-currpos], ttFont, version) def compile(self, ttFont, version=2.0): self.numPasses = len(self.passes) self.numJLevels = len(self.jLevels) self.numCritFeatures = len(self.critFeatures) numPseudo = len(self.pMap) data = b"" if version >= 3.0: hdroffset = sstruct.calcsize(Silf_part1_format_v3) else: hdroffset = 0 data += sstruct.pack(Silf_part1_format, self) for j in self.jLevels: data += sstruct.pack(Silf_justify_format, j) data += sstruct.pack(Silf_part2_format, self) if self.numCritFeatures: data += struct.pack((">%dH" % self.numCritFeaturs), *self.critFeatures) data += struct.pack("BB", 0, len(self.scriptTags)) if len(self.scriptTags): tdata = [struct.pack("4s", x.encode("ascii")) for x in self.scriptTags] data += b"".join(tdata) data += struct.pack(">H", self.lbGID) self.passOffset = len(data) data1 = grUtils.bininfo(numPseudo, 6) currpos = hdroffset + len(data) + 4 * (self.numPasses + 1) self.pseudosOffset = currpos + len(data1) for u, p in sorted(self.pMap.items()): data1 += struct.pack((">LH" if version >= 3.0 else ">HH"), u, ttFont.getGlyphID(p)) data1 += self.classes.compile(ttFont, version) currpos += len(data1) data2 = b"" datao = b"" for i, p in enumerate(self.passes): base = currpos + len(data2) datao += struct.pack(">L", base) data2 += p.compile(ttFont, base, version) datao += struct.pack(">L", currpos + len(data2)) if version >= 3.0: data3 = sstruct.pack(Silf_part1_format_v3, self) else: data3 = b"" return data3 + data + datao + data1 + data2 def toXML(self, writer, ttFont, version=2.0): if version >= 3.0: writer.simpletag('version', ruleVersion=self.ruleVersion) writer.newline() writesimple('info', self, writer, *attrs_info) writesimple('passindexes', self, writer, *attrs_passindexes) writesimple('contexts', self, writer, *attrs_contexts) writesimple('attributes', self, writer, *attrs_attributes) if len(self.jLevels): writer.begintag('justifications') writer.newline() jformat, jnames, jfixes = sstruct.getformat(Silf_justify_format) for i, j in enumerate(self.jLevels): attrs = dict([(k, getattr(j, k)) for k in jnames]) writer.simpletag('justify', **attrs) writer.newline() writer.endtag('justifications') writer.newline() if len(self.critFeatures): writer.begintag('critFeatures') writer.newline() writer.write(" ".join(map(str, self.critFeatures))) writer.newline() writer.endtag('critFeatures') writer.newline() if len(self.scriptTags): writer.begintag('scriptTags') writer.newline() writer.write(" ".join(self.scriptTags)) writer.newline() writer.endtag('scriptTags') writer.newline() if self.pMap: writer.begintag('pseudoMap') writer.newline() for k, v in sorted(self.pMap.items()): writer.simpletag('pseudo', unicode=hex(k), pseudo=v) writer.newline() writer.endtag('pseudoMap') writer.newline() self.classes.toXML(writer, ttFont, version) if len(self.passes): writer.begintag('passes') writer.newline() for i, p in enumerate(self.passes): writer.begintag('pass', _index=i) writer.newline() p.toXML(writer, ttFont, version) writer.endtag('pass') writer.newline() writer.endtag('passes') writer.newline() def fromXML(self, name, attrs, content, ttFont, version=2.0): if name == 'version': self.ruleVersion = float(safeEval(attrs.get('ruleVersion', "0"))) if name == 'info': getSimple(self, attrs, *attrs_info) elif name == 'passindexes': getSimple(self, attrs, *attrs_passindexes) elif name == 'contexts': getSimple(self, attrs, *attrs_contexts) elif name == 'attributes': getSimple(self, attrs, *attrs_attributes) elif name == 'justifications': for element in content: if not isinstance(element, tuple): continue (tag, attrs, subcontent) = element if tag == 'justify': j = _Object() for k, v in attrs.items(): setattr(j, k, int(v)) self.jLevels.append(j) elif name == 'critFeatures': self.critFeatures = [] element = content_string(content) self.critFeatures.extend(map(int, element.split())) elif name == 'scriptTags': self.scriptTags = [] element = content_string(content) for n in element.split(): self.scriptTags.append(n) elif name == 'pseudoMap': self.pMap = {} for element in content: if not isinstance(element, tuple): continue (tag, attrs, subcontent) = element if tag == 'pseudo': k = int(attrs['unicode'], 16) v = attrs['pseudo'] self.pMap[k] = v elif name == 'classes': self.classes = Classes() for element in content: if not isinstance(element, tuple): continue tag, attrs, subcontent = element self.classes.fromXML(tag, attrs, subcontent, ttFont, version) elif name == 'passes': for element in content: if not isinstance(element, tuple): continue tag, attrs, subcontent = element if tag == 'pass': p = Pass() for e in subcontent: if not isinstance(e, tuple): continue p.fromXML(e[0], e[1], e[2], ttFont, version) self.passes.append(p) class Classes(object): def __init__(self): self.linear = [] self.nonLinear = [] def decompile(self, data, ttFont, version=2.0): sstruct.unpack2(Silf_classmap_format, data, self) if version >= 4.0 : oClasses = struct.unpack((">%dL" % (self.numClass+1)), data[4:8+4*self.numClass]) else: oClasses = struct.unpack((">%dH" % (self.numClass+1)), data[4:6+2*self.numClass]) for s,e in zip(oClasses[:self.numLinear], oClasses[1:self.numLinear+1]): self.linear.append(ttFont.getGlyphName(x) for x in struct.unpack((">%dH" % ((e-s)/2)), data[s:e])) for s,e in zip(oClasses[self.numLinear:self.numClass], oClasses[self.numLinear+1:self.numClass+1]): nonLinids = [struct.unpack(">HH", data[x:x+4]) for x in range(s+8, e, 4)] nonLin = dict([(ttFont.getGlyphName(x[0]), x[1]) for x in nonLinids]) self.nonLinear.append(nonLin) def compile(self, ttFont, version=2.0): data = b"" oClasses = [] if version >= 4.0: offset = 8 + 4 * (len(self.linear) + len(self.nonLinear)) else: offset = 6 + 2 * (len(self.linear) + len(self.nonLinear)) for l in self.linear: oClasses.append(len(data) + offset) gs = [ttFont.getGlyphID(x) for x in l] data += struct.pack((">%dH" % len(l)), *gs) for l in self.nonLinear: oClasses.append(len(data) + offset) gs = [(ttFont.getGlyphID(x[0]), x[1]) for x in l.items()] data += grUtils.bininfo(len(gs)) data += b"".join([struct.pack(">HH", *x) for x in sorted(gs)]) oClasses.append(len(data) + offset) self.numClass = len(oClasses) - 1 self.numLinear = len(self.linear) return sstruct.pack(Silf_classmap_format, self) + \ struct.pack(((">%dL" if version >= 4.0 else ">%dH") % len(oClasses)), *oClasses) + data def toXML(self, writer, ttFont, version=2.0): writer.begintag('classes') writer.newline() writer.begintag('linearClasses') writer.newline() for i,l in enumerate(self.linear): writer.begintag('linear', _index=i) writer.newline() wrapline(writer, l) writer.endtag('linear') writer.newline() writer.endtag('linearClasses') writer.newline() writer.begintag('nonLinearClasses') writer.newline() for i, l in enumerate(self.nonLinear): writer.begintag('nonLinear', _index=i + self.numLinear) writer.newline() for inp, ind in l.items(): writer.simpletag('map', glyph=inp, index=ind) writer.newline() writer.endtag('nonLinear') writer.newline() writer.endtag('nonLinearClasses') writer.newline() writer.endtag('classes') writer.newline() def fromXML(self, name, attrs, content, ttFont, version=2.0): if name == 'linearClasses': for element in content: if not isinstance(element, tuple): continue tag, attrs, subcontent = element if tag == 'linear': l = content_string(subcontent).split() self.linear.append(l) elif name == 'nonLinearClasses': for element in content: if not isinstance(element, tuple): continue tag, attrs, subcontent = element if tag =='nonLinear': l = {} for e in subcontent: if not isinstance(e, tuple): continue tag, attrs, subsubcontent = e if tag == 'map': l[attrs['glyph']] = int(safeEval(attrs['index'])) self.nonLinear.append(l) class Pass(object): def __init__(self): self.colMap = {} self.rules = [] self.rulePreContexts = [] self.ruleSortKeys = [] self.ruleConstraints = [] self.passConstraints = b"" self.actions = [] self.stateTrans = [] self.startStates = [] def decompile(self, data, ttFont, version=2.0): _, data = sstruct.unpack2(Silf_pass_format, data, self) (numRange, _, _, _) = struct.unpack(">4H", data[:8]) data = data[8:] for i in range(numRange): (first, last, col) = struct.unpack(">3H", data[6*i:6*i+6]) for g in range(first, last+1): self.colMap[ttFont.getGlyphName(g)] = col data = data[6*numRange:] oRuleMap = struct.unpack_from((">%dH" % (self.numSuccess + 1)), data) data = data[2+2*self.numSuccess:] rules = struct.unpack_from((">%dH" % oRuleMap[-1]), data) self.rules = [rules[s:e] for (s,e) in zip(oRuleMap, oRuleMap[1:])] data = data[2*oRuleMap[-1]:] (self.minRulePreContext, self.maxRulePreContext) = struct.unpack('BB', data[:2]) numStartStates = self.maxRulePreContext - self.minRulePreContext + 1 self.startStates = struct.unpack((">%dH" % numStartStates), data[2:2 + numStartStates * 2]) data = data[2+numStartStates*2:] self.ruleSortKeys = struct.unpack((">%dH" % self.numRules), data[:2 * self.numRules]) data = data[2*self.numRules:] self.rulePreContexts = struct.unpack(("%dB" % self.numRules), data[:self.numRules]) data = data[self.numRules:] (self.collisionThreshold, pConstraint) = struct.unpack(">BH", data[:3]) oConstraints = list(struct.unpack((">%dH" % (self.numRules + 1)), data[3:5 + self.numRules * 2])) data = data[5 + self.numRules * 2:] oActions = list(struct.unpack((">%dH" % (self.numRules + 1)), data[:2 + self.numRules * 2])) data = data[2 * self.numRules + 2:] for i in range(self.numTransitional): a = array("H", data[i*self.numColumns*2:(i+1)*self.numColumns*2]) if sys.byteorder != "big": a.byteswap() self.stateTrans.append(a) data = data[self.numTransitional * self.numColumns * 2 + 1:] self.passConstraints = data[:pConstraint] data = data[pConstraint:] for i in range(len(oConstraints)-2,-1,-1): if oConstraints[i] == 0 : oConstraints[i] = oConstraints[i+1] self.ruleConstraints = [(data[s:e] if (e-s > 1) else b"") for (s,e) in zip(oConstraints, oConstraints[1:])] data = data[oConstraints[-1]:] self.actions = [(data[s:e] if (e-s > 1) else "") for (s,e) in zip(oActions, oActions[1:])] data = data[oActions[-1]:] # not using debug def compile(self, ttFont, base, version=2.0): # build it all up backwards oActions = reduce(lambda a, x: (a[0]+len(x), a[1]+[a[0]]), self.actions + [b""], (0, []))[1] oConstraints = reduce(lambda a, x: (a[0]+len(x), a[1]+[a[0]]), self.ruleConstraints + [b""], (1, []))[1] constraintCode = b"\000" + b"".join(self.ruleConstraints) transes = [] for t in self.stateTrans: if sys.byteorder != "big": t.byteswap() transes.append(t.tobytes()) if sys.byteorder != "big": t.byteswap() if not len(transes): self.startStates = [0] oRuleMap = reduce(lambda a, x: (a[0]+len(x), a[1]+[a[0]]), self.rules+[[]], (0, []))[1] passRanges = [] gidcolmap = dict([(ttFont.getGlyphID(x[0]), x[1]) for x in self.colMap.items()]) for e in grUtils.entries(gidcolmap, sameval = True): if e[1]: passRanges.append((e[0], e[0]+e[1]-1, e[2][0])) self.numRules = len(self.actions) self.fsmOffset = (sstruct.calcsize(Silf_pass_format) + 8 + len(passRanges) * 6 + len(oRuleMap) * 2 + 2 * oRuleMap[-1] + 2 + 2 * len(self.startStates) + 3 * self.numRules + 3 + 4 * self.numRules + 4) self.pcCode = self.fsmOffset + 2*self.numTransitional*self.numColumns + 1 + base self.rcCode = self.pcCode + len(self.passConstraints) self.aCode = self.rcCode + len(constraintCode) self.oDebug = 0 # now generate output data = sstruct.pack(Silf_pass_format, self) data += grUtils.bininfo(len(passRanges), 6) data += b"".join(struct.pack(">3H", *p) for p in passRanges) data += struct.pack((">%dH" % len(oRuleMap)), *oRuleMap) flatrules = reduce(lambda a,x: a+x, self.rules, []) data += struct.pack((">%dH" % oRuleMap[-1]), *flatrules) data += struct.pack("BB", self.minRulePreContext, self.maxRulePreContext) data += struct.pack((">%dH" % len(self.startStates)), *self.startStates) data += struct.pack((">%dH" % self.numRules), *self.ruleSortKeys) data += struct.pack(("%dB" % self.numRules), *self.rulePreContexts) data += struct.pack(">BH", self.collisionThreshold, len(self.passConstraints)) data += struct.pack((">%dH" % (self.numRules+1)), *oConstraints) data += struct.pack((">%dH" % (self.numRules+1)), *oActions) return data + b"".join(transes) + struct.pack("B", 0) + \ self.passConstraints + constraintCode + b"".join(self.actions) def toXML(self, writer, ttFont, version=2.0): writesimple('info', self, writer, *pass_attrs_info) writesimple('fsminfo', self, writer, *pass_attrs_fsm) writer.begintag('colmap') writer.newline() wrapline(writer, ["{}={}".format(*x) for x in sorted(self.colMap.items(), key=lambda x:ttFont.getGlyphID(x[0]))]) writer.endtag('colmap') writer.newline() writer.begintag('staterulemap') writer.newline() for i, r in enumerate(self.rules): writer.simpletag('state', number = self.numRows - self.numSuccess + i, rules = " ".join(map(str, r))) writer.newline() writer.endtag('staterulemap') writer.newline() writer.begintag('rules') writer.newline() for i in range(len(self.actions)): writer.begintag('rule', index=i, precontext=self.rulePreContexts[i], sortkey=self.ruleSortKeys[i]) writer.newline() if len(self.ruleConstraints[i]): writecode('constraint', writer, self.ruleConstraints[i]) writecode('action', writer, self.actions[i]) writer.endtag('rule') writer.newline() writer.endtag('rules') writer.newline() if len(self.passConstraints): writecode('passConstraint', writer, self.passConstraints) if len(self.stateTrans): writer.begintag('fsm') writer.newline() writer.begintag('starts') writer.write(" ".join(map(str, self.startStates))) writer.endtag('starts') writer.newline() for i, s in enumerate(self.stateTrans): writer.begintag('row', _i=i) # no newlines here writer.write(" ".join(map(str, s))) writer.endtag('row') writer.newline() writer.endtag('fsm') writer.newline() def fromXML(self, name, attrs, content, ttFont, version=2.0): if name == 'info': getSimple(self, attrs, *pass_attrs_info) elif name == 'fsminfo': getSimple(self, attrs, *pass_attrs_fsm) elif name == 'colmap': e = content_string(content) for w in e.split(): x = w.split('=') if len(x) != 2 or x[0] == '' or x[1] == '': continue self.colMap[x[0]] = int(x[1]) elif name == 'staterulemap': for e in content: if not isinstance(e, tuple): continue tag, a, c = e if tag == 'state': self.rules.append([int(x) for x in a['rules'].split(" ")]) elif name == 'rules': for element in content: if not isinstance(element, tuple): continue tag, a, c = element if tag != 'rule': continue self.rulePreContexts.append(int(a['precontext'])) self.ruleSortKeys.append(int(a['sortkey'])) con = b"" act = b"" for e in c: if not isinstance(e, tuple): continue tag, a, subc = e if tag == 'constraint': con = readcode(subc) elif tag == 'action': act = readcode(subc) self.actions.append(act) self.ruleConstraints.append(con) elif name == 'passConstraint': self.passConstraints = readcode(content) elif name == 'fsm': for element in content: if not isinstance(element, tuple): continue tag, a, c = element if tag == 'row': s = array('H') e = content_string(c) s.extend(map(int, e.split())) self.stateTrans.append(s) elif tag == 'starts': s = [] e = content_string(c) s.extend(map(int, e.split())) self.startStates = s