S__i_l_f.py 34 KB

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