12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040 |
- from fontTools.misc import sstruct
- from fontTools.misc.fixedTools import floatToFixedToStr
- from fontTools.misc.textTools import byteord, safeEval
- # from itertools import *
- from . import DefaultTable
- from . import grUtils
- from array import array
- from functools import reduce
- import struct, 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):
- """Graphite Rules table
- See also https://graphite.sil.org/graphite_techAbout#graphite-font-tables
- """
- 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
|