S__i_l_f.py 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037
  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. """Silf table support"""
  309. def __init__(self, tag=None):
  310. DefaultTable.DefaultTable.__init__(self, tag)
  311. self.silfs = []
  312. def decompile(self, data, ttFont):
  313. sstruct.unpack2(Silf_hdr_format, data, self)
  314. self.version = float(floatToFixedToStr(self.version, precisionBits=16))
  315. if self.version >= 5.0:
  316. (data, self.scheme) = grUtils.decompress(data)
  317. sstruct.unpack2(Silf_hdr_format_3, data, self)
  318. base = sstruct.calcsize(Silf_hdr_format_3)
  319. elif self.version < 3.0:
  320. self.numSilf = struct.unpack(">H", data[4:6])
  321. self.scheme = 0
  322. self.compilerVersion = 0
  323. base = 8
  324. else:
  325. self.scheme = 0
  326. sstruct.unpack2(Silf_hdr_format_3, data, self)
  327. base = sstruct.calcsize(Silf_hdr_format_3)
  328. silfoffsets = struct.unpack_from((">%dL" % self.numSilf), data[base:])
  329. for offset in silfoffsets:
  330. s = Silf()
  331. self.silfs.append(s)
  332. s.decompile(data[offset:], ttFont, self.version)
  333. def compile(self, ttFont):
  334. self.numSilf = len(self.silfs)
  335. if self.version < 3.0:
  336. hdr = sstruct.pack(Silf_hdr_format, self)
  337. hdr += struct.pack(">HH", self.numSilf, 0)
  338. else:
  339. hdr = sstruct.pack(Silf_hdr_format_3, self)
  340. offset = len(hdr) + 4 * self.numSilf
  341. data = b""
  342. for s in self.silfs:
  343. hdr += struct.pack(">L", offset)
  344. subdata = s.compile(ttFont, self.version)
  345. offset += len(subdata)
  346. data += subdata
  347. if self.version >= 5.0:
  348. return grUtils.compress(self.scheme, hdr + data)
  349. return hdr + data
  350. def toXML(self, writer, ttFont):
  351. writer.comment("Attributes starting with _ are informative only")
  352. writer.newline()
  353. writer.simpletag(
  354. "version",
  355. version=self.version,
  356. compilerVersion=self.compilerVersion,
  357. compressionScheme=self.scheme,
  358. )
  359. writer.newline()
  360. for s in self.silfs:
  361. writer.begintag("silf")
  362. writer.newline()
  363. s.toXML(writer, ttFont, self.version)
  364. writer.endtag("silf")
  365. writer.newline()
  366. def fromXML(self, name, attrs, content, ttFont):
  367. if name == "version":
  368. self.scheme = int(safeEval(attrs["compressionScheme"]))
  369. self.version = float(safeEval(attrs["version"]))
  370. self.compilerVersion = int(safeEval(attrs["compilerVersion"]))
  371. return
  372. if name == "silf":
  373. s = Silf()
  374. self.silfs.append(s)
  375. for element in content:
  376. if not isinstance(element, tuple):
  377. continue
  378. tag, attrs, subcontent = element
  379. s.fromXML(tag, attrs, subcontent, ttFont, self.version)
  380. class Silf(object):
  381. """A particular Silf subtable"""
  382. def __init__(self):
  383. self.passes = []
  384. self.scriptTags = []
  385. self.critFeatures = []
  386. self.jLevels = []
  387. self.pMap = {}
  388. def decompile(self, data, ttFont, version=2.0):
  389. if version >= 3.0:
  390. _, data = sstruct.unpack2(Silf_part1_format_v3, data, self)
  391. self.ruleVersion = float(
  392. floatToFixedToStr(self.ruleVersion, precisionBits=16)
  393. )
  394. _, data = sstruct.unpack2(Silf_part1_format, data, self)
  395. for jlevel in range(self.numJLevels):
  396. j, data = sstruct.unpack2(Silf_justify_format, data, _Object())
  397. self.jLevels.append(j)
  398. _, data = sstruct.unpack2(Silf_part2_format, data, self)
  399. if self.numCritFeatures:
  400. self.critFeatures = struct.unpack_from(
  401. (">%dH" % self.numCritFeatures), data
  402. )
  403. data = data[self.numCritFeatures * 2 + 1 :]
  404. (numScriptTag,) = struct.unpack_from("B", data)
  405. if numScriptTag:
  406. self.scriptTags = [
  407. struct.unpack("4s", data[x : x + 4])[0].decode("ascii")
  408. for x in range(1, 1 + 4 * numScriptTag, 4)
  409. ]
  410. data = data[1 + 4 * numScriptTag :]
  411. (self.lbGID,) = struct.unpack(">H", data[:2])
  412. if self.numPasses:
  413. self.oPasses = struct.unpack(
  414. (">%dL" % (self.numPasses + 1)), data[2 : 6 + 4 * self.numPasses]
  415. )
  416. data = data[6 + 4 * self.numPasses :]
  417. (numPseudo,) = struct.unpack(">H", data[:2])
  418. for i in range(numPseudo):
  419. if version >= 3.0:
  420. pseudo = sstruct.unpack(
  421. Silf_pseudomap_format, data[8 + 6 * i : 14 + 6 * i], _Object()
  422. )
  423. else:
  424. pseudo = sstruct.unpack(
  425. Silf_pseudomap_format_h, data[8 + 4 * i : 12 + 4 * i], _Object()
  426. )
  427. self.pMap[pseudo.unicode] = ttFont.getGlyphName(pseudo.nPseudo)
  428. data = data[8 + 6 * numPseudo :]
  429. currpos = (
  430. sstruct.calcsize(Silf_part1_format)
  431. + sstruct.calcsize(Silf_justify_format) * self.numJLevels
  432. + sstruct.calcsize(Silf_part2_format)
  433. + 2 * self.numCritFeatures
  434. + 1
  435. + 1
  436. + 4 * numScriptTag
  437. + 6
  438. + 4 * self.numPasses
  439. + 8
  440. + 6 * numPseudo
  441. )
  442. if version >= 3.0:
  443. currpos += sstruct.calcsize(Silf_part1_format_v3)
  444. self.classes = Classes()
  445. self.classes.decompile(data, ttFont, version)
  446. for i in range(self.numPasses):
  447. p = Pass()
  448. self.passes.append(p)
  449. p.decompile(
  450. data[self.oPasses[i] - currpos : self.oPasses[i + 1] - currpos],
  451. ttFont,
  452. version,
  453. )
  454. def compile(self, ttFont, version=2.0):
  455. self.numPasses = len(self.passes)
  456. self.numJLevels = len(self.jLevels)
  457. self.numCritFeatures = len(self.critFeatures)
  458. numPseudo = len(self.pMap)
  459. data = b""
  460. if version >= 3.0:
  461. hdroffset = sstruct.calcsize(Silf_part1_format_v3)
  462. else:
  463. hdroffset = 0
  464. data += sstruct.pack(Silf_part1_format, self)
  465. for j in self.jLevels:
  466. data += sstruct.pack(Silf_justify_format, j)
  467. data += sstruct.pack(Silf_part2_format, self)
  468. if self.numCritFeatures:
  469. data += struct.pack((">%dH" % self.numCritFeaturs), *self.critFeatures)
  470. data += struct.pack("BB", 0, len(self.scriptTags))
  471. if len(self.scriptTags):
  472. tdata = [struct.pack("4s", x.encode("ascii")) for x in self.scriptTags]
  473. data += b"".join(tdata)
  474. data += struct.pack(">H", self.lbGID)
  475. self.passOffset = len(data)
  476. data1 = grUtils.bininfo(numPseudo, 6)
  477. currpos = hdroffset + len(data) + 4 * (self.numPasses + 1)
  478. self.pseudosOffset = currpos + len(data1)
  479. for u, p in sorted(self.pMap.items()):
  480. data1 += struct.pack(
  481. (">LH" if version >= 3.0 else ">HH"), u, ttFont.getGlyphID(p)
  482. )
  483. data1 += self.classes.compile(ttFont, version)
  484. currpos += len(data1)
  485. data2 = b""
  486. datao = b""
  487. for i, p in enumerate(self.passes):
  488. base = currpos + len(data2)
  489. datao += struct.pack(">L", base)
  490. data2 += p.compile(ttFont, base, version)
  491. datao += struct.pack(">L", currpos + len(data2))
  492. if version >= 3.0:
  493. data3 = sstruct.pack(Silf_part1_format_v3, self)
  494. else:
  495. data3 = b""
  496. return data3 + data + datao + data1 + data2
  497. def toXML(self, writer, ttFont, version=2.0):
  498. if version >= 3.0:
  499. writer.simpletag("version", ruleVersion=self.ruleVersion)
  500. writer.newline()
  501. writesimple("info", self, writer, *attrs_info)
  502. writesimple("passindexes", self, writer, *attrs_passindexes)
  503. writesimple("contexts", self, writer, *attrs_contexts)
  504. writesimple("attributes", self, writer, *attrs_attributes)
  505. if len(self.jLevels):
  506. writer.begintag("justifications")
  507. writer.newline()
  508. jformat, jnames, jfixes = sstruct.getformat(Silf_justify_format)
  509. for i, j in enumerate(self.jLevels):
  510. attrs = dict([(k, getattr(j, k)) for k in jnames])
  511. writer.simpletag("justify", **attrs)
  512. writer.newline()
  513. writer.endtag("justifications")
  514. writer.newline()
  515. if len(self.critFeatures):
  516. writer.begintag("critFeatures")
  517. writer.newline()
  518. writer.write(" ".join(map(str, self.critFeatures)))
  519. writer.newline()
  520. writer.endtag("critFeatures")
  521. writer.newline()
  522. if len(self.scriptTags):
  523. writer.begintag("scriptTags")
  524. writer.newline()
  525. writer.write(" ".join(self.scriptTags))
  526. writer.newline()
  527. writer.endtag("scriptTags")
  528. writer.newline()
  529. if self.pMap:
  530. writer.begintag("pseudoMap")
  531. writer.newline()
  532. for k, v in sorted(self.pMap.items()):
  533. writer.simpletag("pseudo", unicode=hex(k), pseudo=v)
  534. writer.newline()
  535. writer.endtag("pseudoMap")
  536. writer.newline()
  537. self.classes.toXML(writer, ttFont, version)
  538. if len(self.passes):
  539. writer.begintag("passes")
  540. writer.newline()
  541. for i, p in enumerate(self.passes):
  542. writer.begintag("pass", _index=i)
  543. writer.newline()
  544. p.toXML(writer, ttFont, version)
  545. writer.endtag("pass")
  546. writer.newline()
  547. writer.endtag("passes")
  548. writer.newline()
  549. def fromXML(self, name, attrs, content, ttFont, version=2.0):
  550. if name == "version":
  551. self.ruleVersion = float(safeEval(attrs.get("ruleVersion", "0")))
  552. if name == "info":
  553. getSimple(self, attrs, *attrs_info)
  554. elif name == "passindexes":
  555. getSimple(self, attrs, *attrs_passindexes)
  556. elif name == "contexts":
  557. getSimple(self, attrs, *attrs_contexts)
  558. elif name == "attributes":
  559. getSimple(self, attrs, *attrs_attributes)
  560. elif name == "justifications":
  561. for element in content:
  562. if not isinstance(element, tuple):
  563. continue
  564. (tag, attrs, subcontent) = element
  565. if tag == "justify":
  566. j = _Object()
  567. for k, v in attrs.items():
  568. setattr(j, k, int(v))
  569. self.jLevels.append(j)
  570. elif name == "critFeatures":
  571. self.critFeatures = []
  572. element = content_string(content)
  573. self.critFeatures.extend(map(int, element.split()))
  574. elif name == "scriptTags":
  575. self.scriptTags = []
  576. element = content_string(content)
  577. for n in element.split():
  578. self.scriptTags.append(n)
  579. elif name == "pseudoMap":
  580. self.pMap = {}
  581. for element in content:
  582. if not isinstance(element, tuple):
  583. continue
  584. (tag, attrs, subcontent) = element
  585. if tag == "pseudo":
  586. k = int(attrs["unicode"], 16)
  587. v = attrs["pseudo"]
  588. self.pMap[k] = v
  589. elif name == "classes":
  590. self.classes = Classes()
  591. for element in content:
  592. if not isinstance(element, tuple):
  593. continue
  594. tag, attrs, subcontent = element
  595. self.classes.fromXML(tag, attrs, subcontent, ttFont, version)
  596. elif name == "passes":
  597. for element in content:
  598. if not isinstance(element, tuple):
  599. continue
  600. tag, attrs, subcontent = element
  601. if tag == "pass":
  602. p = Pass()
  603. for e in subcontent:
  604. if not isinstance(e, tuple):
  605. continue
  606. p.fromXML(e[0], e[1], e[2], ttFont, version)
  607. self.passes.append(p)
  608. class Classes(object):
  609. def __init__(self):
  610. self.linear = []
  611. self.nonLinear = []
  612. def decompile(self, data, ttFont, version=2.0):
  613. sstruct.unpack2(Silf_classmap_format, data, self)
  614. if version >= 4.0:
  615. oClasses = struct.unpack(
  616. (">%dL" % (self.numClass + 1)), data[4 : 8 + 4 * self.numClass]
  617. )
  618. else:
  619. oClasses = struct.unpack(
  620. (">%dH" % (self.numClass + 1)), data[4 : 6 + 2 * self.numClass]
  621. )
  622. for s, e in zip(oClasses[: self.numLinear], oClasses[1 : self.numLinear + 1]):
  623. self.linear.append(
  624. ttFont.getGlyphName(x)
  625. for x in struct.unpack((">%dH" % ((e - s) / 2)), data[s:e])
  626. )
  627. for s, e in zip(
  628. oClasses[self.numLinear : self.numClass],
  629. oClasses[self.numLinear + 1 : self.numClass + 1],
  630. ):
  631. nonLinids = [
  632. struct.unpack(">HH", data[x : x + 4]) for x in range(s + 8, e, 4)
  633. ]
  634. nonLin = dict([(ttFont.getGlyphName(x[0]), x[1]) for x in nonLinids])
  635. self.nonLinear.append(nonLin)
  636. def compile(self, ttFont, version=2.0):
  637. data = b""
  638. oClasses = []
  639. if version >= 4.0:
  640. offset = 8 + 4 * (len(self.linear) + len(self.nonLinear))
  641. else:
  642. offset = 6 + 2 * (len(self.linear) + len(self.nonLinear))
  643. for l in self.linear:
  644. oClasses.append(len(data) + offset)
  645. gs = [ttFont.getGlyphID(x) for x in l]
  646. data += struct.pack((">%dH" % len(l)), *gs)
  647. for l in self.nonLinear:
  648. oClasses.append(len(data) + offset)
  649. gs = [(ttFont.getGlyphID(x[0]), x[1]) for x in l.items()]
  650. data += grUtils.bininfo(len(gs))
  651. data += b"".join([struct.pack(">HH", *x) for x in sorted(gs)])
  652. oClasses.append(len(data) + offset)
  653. self.numClass = len(oClasses) - 1
  654. self.numLinear = len(self.linear)
  655. return (
  656. sstruct.pack(Silf_classmap_format, self)
  657. + struct.pack(
  658. ((">%dL" if version >= 4.0 else ">%dH") % len(oClasses)), *oClasses
  659. )
  660. + data
  661. )
  662. def toXML(self, writer, ttFont, version=2.0):
  663. writer.begintag("classes")
  664. writer.newline()
  665. writer.begintag("linearClasses")
  666. writer.newline()
  667. for i, l in enumerate(self.linear):
  668. writer.begintag("linear", _index=i)
  669. writer.newline()
  670. wrapline(writer, l)
  671. writer.endtag("linear")
  672. writer.newline()
  673. writer.endtag("linearClasses")
  674. writer.newline()
  675. writer.begintag("nonLinearClasses")
  676. writer.newline()
  677. for i, l in enumerate(self.nonLinear):
  678. writer.begintag("nonLinear", _index=i + self.numLinear)
  679. writer.newline()
  680. for inp, ind in l.items():
  681. writer.simpletag("map", glyph=inp, index=ind)
  682. writer.newline()
  683. writer.endtag("nonLinear")
  684. writer.newline()
  685. writer.endtag("nonLinearClasses")
  686. writer.newline()
  687. writer.endtag("classes")
  688. writer.newline()
  689. def fromXML(self, name, attrs, content, ttFont, version=2.0):
  690. if name == "linearClasses":
  691. for element in content:
  692. if not isinstance(element, tuple):
  693. continue
  694. tag, attrs, subcontent = element
  695. if tag == "linear":
  696. l = content_string(subcontent).split()
  697. self.linear.append(l)
  698. elif name == "nonLinearClasses":
  699. for element in content:
  700. if not isinstance(element, tuple):
  701. continue
  702. tag, attrs, subcontent = element
  703. if tag == "nonLinear":
  704. l = {}
  705. for e in subcontent:
  706. if not isinstance(e, tuple):
  707. continue
  708. tag, attrs, subsubcontent = e
  709. if tag == "map":
  710. l[attrs["glyph"]] = int(safeEval(attrs["index"]))
  711. self.nonLinear.append(l)
  712. class Pass(object):
  713. def __init__(self):
  714. self.colMap = {}
  715. self.rules = []
  716. self.rulePreContexts = []
  717. self.ruleSortKeys = []
  718. self.ruleConstraints = []
  719. self.passConstraints = b""
  720. self.actions = []
  721. self.stateTrans = []
  722. self.startStates = []
  723. def decompile(self, data, ttFont, version=2.0):
  724. _, data = sstruct.unpack2(Silf_pass_format, data, self)
  725. (numRange, _, _, _) = struct.unpack(">4H", data[:8])
  726. data = data[8:]
  727. for i in range(numRange):
  728. (first, last, col) = struct.unpack(">3H", data[6 * i : 6 * i + 6])
  729. for g in range(first, last + 1):
  730. self.colMap[ttFont.getGlyphName(g)] = col
  731. data = data[6 * numRange :]
  732. oRuleMap = struct.unpack_from((">%dH" % (self.numSuccess + 1)), data)
  733. data = data[2 + 2 * self.numSuccess :]
  734. rules = struct.unpack_from((">%dH" % oRuleMap[-1]), data)
  735. self.rules = [rules[s:e] for (s, e) in zip(oRuleMap, oRuleMap[1:])]
  736. data = data[2 * oRuleMap[-1] :]
  737. (self.minRulePreContext, self.maxRulePreContext) = struct.unpack("BB", data[:2])
  738. numStartStates = self.maxRulePreContext - self.minRulePreContext + 1
  739. self.startStates = struct.unpack(
  740. (">%dH" % numStartStates), data[2 : 2 + numStartStates * 2]
  741. )
  742. data = data[2 + numStartStates * 2 :]
  743. self.ruleSortKeys = struct.unpack(
  744. (">%dH" % self.numRules), data[: 2 * self.numRules]
  745. )
  746. data = data[2 * self.numRules :]
  747. self.rulePreContexts = struct.unpack(
  748. ("%dB" % self.numRules), data[: self.numRules]
  749. )
  750. data = data[self.numRules :]
  751. (self.collisionThreshold, pConstraint) = struct.unpack(">BH", data[:3])
  752. oConstraints = list(
  753. struct.unpack(
  754. (">%dH" % (self.numRules + 1)), data[3 : 5 + self.numRules * 2]
  755. )
  756. )
  757. data = data[5 + self.numRules * 2 :]
  758. oActions = list(
  759. struct.unpack((">%dH" % (self.numRules + 1)), data[: 2 + self.numRules * 2])
  760. )
  761. data = data[2 * self.numRules + 2 :]
  762. for i in range(self.numTransitional):
  763. a = array(
  764. "H", data[i * self.numColumns * 2 : (i + 1) * self.numColumns * 2]
  765. )
  766. if sys.byteorder != "big":
  767. a.byteswap()
  768. self.stateTrans.append(a)
  769. data = data[self.numTransitional * self.numColumns * 2 + 1 :]
  770. self.passConstraints = data[:pConstraint]
  771. data = data[pConstraint:]
  772. for i in range(len(oConstraints) - 2, -1, -1):
  773. if oConstraints[i] == 0:
  774. oConstraints[i] = oConstraints[i + 1]
  775. self.ruleConstraints = [
  776. (data[s:e] if (e - s > 1) else b"")
  777. for (s, e) in zip(oConstraints, oConstraints[1:])
  778. ]
  779. data = data[oConstraints[-1] :]
  780. self.actions = [
  781. (data[s:e] if (e - s > 1) else "") for (s, e) in zip(oActions, oActions[1:])
  782. ]
  783. data = data[oActions[-1] :]
  784. # not using debug
  785. def compile(self, ttFont, base, version=2.0):
  786. # build it all up backwards
  787. oActions = reduce(
  788. lambda a, x: (a[0] + len(x), a[1] + [a[0]]), self.actions + [b""], (0, [])
  789. )[1]
  790. oConstraints = reduce(
  791. lambda a, x: (a[0] + len(x), a[1] + [a[0]]),
  792. self.ruleConstraints + [b""],
  793. (1, []),
  794. )[1]
  795. constraintCode = b"\000" + b"".join(self.ruleConstraints)
  796. transes = []
  797. for t in self.stateTrans:
  798. if sys.byteorder != "big":
  799. t.byteswap()
  800. transes.append(t.tobytes())
  801. if sys.byteorder != "big":
  802. t.byteswap()
  803. if not len(transes):
  804. self.startStates = [0]
  805. oRuleMap = reduce(
  806. lambda a, x: (a[0] + len(x), a[1] + [a[0]]), self.rules + [[]], (0, [])
  807. )[1]
  808. passRanges = []
  809. gidcolmap = dict([(ttFont.getGlyphID(x[0]), x[1]) for x in self.colMap.items()])
  810. for e in grUtils.entries(gidcolmap, sameval=True):
  811. if e[1]:
  812. passRanges.append((e[0], e[0] + e[1] - 1, e[2][0]))
  813. self.numRules = len(self.actions)
  814. self.fsmOffset = (
  815. sstruct.calcsize(Silf_pass_format)
  816. + 8
  817. + len(passRanges) * 6
  818. + len(oRuleMap) * 2
  819. + 2 * oRuleMap[-1]
  820. + 2
  821. + 2 * len(self.startStates)
  822. + 3 * self.numRules
  823. + 3
  824. + 4 * self.numRules
  825. + 4
  826. )
  827. self.pcCode = (
  828. self.fsmOffset + 2 * self.numTransitional * self.numColumns + 1 + base
  829. )
  830. self.rcCode = self.pcCode + len(self.passConstraints)
  831. self.aCode = self.rcCode + len(constraintCode)
  832. self.oDebug = 0
  833. # now generate output
  834. data = sstruct.pack(Silf_pass_format, self)
  835. data += grUtils.bininfo(len(passRanges), 6)
  836. data += b"".join(struct.pack(">3H", *p) for p in passRanges)
  837. data += struct.pack((">%dH" % len(oRuleMap)), *oRuleMap)
  838. flatrules = reduce(lambda a, x: a + x, self.rules, [])
  839. data += struct.pack((">%dH" % oRuleMap[-1]), *flatrules)
  840. data += struct.pack("BB", self.minRulePreContext, self.maxRulePreContext)
  841. data += struct.pack((">%dH" % len(self.startStates)), *self.startStates)
  842. data += struct.pack((">%dH" % self.numRules), *self.ruleSortKeys)
  843. data += struct.pack(("%dB" % self.numRules), *self.rulePreContexts)
  844. data += struct.pack(">BH", self.collisionThreshold, len(self.passConstraints))
  845. data += struct.pack((">%dH" % (self.numRules + 1)), *oConstraints)
  846. data += struct.pack((">%dH" % (self.numRules + 1)), *oActions)
  847. return (
  848. data
  849. + b"".join(transes)
  850. + struct.pack("B", 0)
  851. + self.passConstraints
  852. + constraintCode
  853. + b"".join(self.actions)
  854. )
  855. def toXML(self, writer, ttFont, version=2.0):
  856. writesimple("info", self, writer, *pass_attrs_info)
  857. writesimple("fsminfo", self, writer, *pass_attrs_fsm)
  858. writer.begintag("colmap")
  859. writer.newline()
  860. wrapline(
  861. writer,
  862. [
  863. "{}={}".format(*x)
  864. for x in sorted(
  865. self.colMap.items(), key=lambda x: ttFont.getGlyphID(x[0])
  866. )
  867. ],
  868. )
  869. writer.endtag("colmap")
  870. writer.newline()
  871. writer.begintag("staterulemap")
  872. writer.newline()
  873. for i, r in enumerate(self.rules):
  874. writer.simpletag(
  875. "state",
  876. number=self.numRows - self.numSuccess + i,
  877. rules=" ".join(map(str, r)),
  878. )
  879. writer.newline()
  880. writer.endtag("staterulemap")
  881. writer.newline()
  882. writer.begintag("rules")
  883. writer.newline()
  884. for i in range(len(self.actions)):
  885. writer.begintag(
  886. "rule",
  887. index=i,
  888. precontext=self.rulePreContexts[i],
  889. sortkey=self.ruleSortKeys[i],
  890. )
  891. writer.newline()
  892. if len(self.ruleConstraints[i]):
  893. writecode("constraint", writer, self.ruleConstraints[i])
  894. writecode("action", writer, self.actions[i])
  895. writer.endtag("rule")
  896. writer.newline()
  897. writer.endtag("rules")
  898. writer.newline()
  899. if len(self.passConstraints):
  900. writecode("passConstraint", writer, self.passConstraints)
  901. if len(self.stateTrans):
  902. writer.begintag("fsm")
  903. writer.newline()
  904. writer.begintag("starts")
  905. writer.write(" ".join(map(str, self.startStates)))
  906. writer.endtag("starts")
  907. writer.newline()
  908. for i, s in enumerate(self.stateTrans):
  909. writer.begintag("row", _i=i)
  910. # no newlines here
  911. writer.write(" ".join(map(str, s)))
  912. writer.endtag("row")
  913. writer.newline()
  914. writer.endtag("fsm")
  915. writer.newline()
  916. def fromXML(self, name, attrs, content, ttFont, version=2.0):
  917. if name == "info":
  918. getSimple(self, attrs, *pass_attrs_info)
  919. elif name == "fsminfo":
  920. getSimple(self, attrs, *pass_attrs_fsm)
  921. elif name == "colmap":
  922. e = content_string(content)
  923. for w in e.split():
  924. x = w.split("=")
  925. if len(x) != 2 or x[0] == "" or x[1] == "":
  926. continue
  927. self.colMap[x[0]] = int(x[1])
  928. elif name == "staterulemap":
  929. for e in content:
  930. if not isinstance(e, tuple):
  931. continue
  932. tag, a, c = e
  933. if tag == "state":
  934. self.rules.append([int(x) for x in a["rules"].split(" ")])
  935. elif name == "rules":
  936. for element in content:
  937. if not isinstance(element, tuple):
  938. continue
  939. tag, a, c = element
  940. if tag != "rule":
  941. continue
  942. self.rulePreContexts.append(int(a["precontext"]))
  943. self.ruleSortKeys.append(int(a["sortkey"]))
  944. con = b""
  945. act = b""
  946. for e in c:
  947. if not isinstance(e, tuple):
  948. continue
  949. tag, a, subc = e
  950. if tag == "constraint":
  951. con = readcode(subc)
  952. elif tag == "action":
  953. act = readcode(subc)
  954. self.actions.append(act)
  955. self.ruleConstraints.append(con)
  956. elif name == "passConstraint":
  957. self.passConstraints = readcode(content)
  958. elif name == "fsm":
  959. for element in content:
  960. if not isinstance(element, tuple):
  961. continue
  962. tag, a, c = element
  963. if tag == "row":
  964. s = array("H")
  965. e = content_string(c)
  966. s.extend(map(int, e.split()))
  967. self.stateTrans.append(s)
  968. elif tag == "starts":
  969. s = []
  970. e = content_string(c)
  971. s.extend(map(int, e.split()))
  972. self.startStates = s