| from __future__ import print_function, division, absolute_import |
| from fontTools.misc.py23 import * |
| from fontTools.misc import sstruct |
| from fontTools.misc.textTools import safeEval |
| from itertools import * |
| from . import DefaultTable |
| from . import grUtils |
| from array import array |
| 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_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("^\s*([^(]+)\s*(?:\(([^)]+)\))?") |
| def assemble(instrs): |
| res = [] |
| 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.append(struct.pack("B", opcode)) |
| if m.group(2): |
| if parmfmt == 0: |
| continue |
| parms = [int(x) for x in re.split(",\s*", m.group(2))] |
| if parmfmt == -1: |
| l = len(parms) |
| res.append(struct.pack(("%dB" % (l+1)), l, *parms)) |
| else: |
| res.append(struct.pack(parmfmt, *parms)) |
| return b"".join(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) |
| 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 = "" |
| 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) |
| _, 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] 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 = struct.unpack('>HH', 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 = "" |
| 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) for x in self.scriptTags] |
| data += "".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 = "" |
| datao = "" |
| 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 = "" |
| 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(map(ttFont.getGlyphName, |
| 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 = "" |
| 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 = map(ttFont.getGlyphID, 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 += "".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 = "" |
| 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]) |
| 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 "") 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 + [""], (0, []))[1] |
| oConstraints = reduce(lambda a, x: (a[0]+len(x), a[1]+[a[0]]), self.ruleConstraints + [""], (1, []))[1] |
| constraintCode = "\000" + "".join(self.ruleConstraints) |
| transes = [] |
| for t in self.stateTrans: |
| t.byteswap() |
| transes.append(t.tostring()) |
| 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 += "".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 + "".join(transes) + struct.pack("B", 0) + \ |
| self.passConstraints + constraintCode + "".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(map(int, 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 = "" |
| act = "" |
| 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 |
| |