otTables.py 95 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700
  1. # coding: utf-8
  2. """fontTools.ttLib.tables.otTables -- A collection of classes representing the various
  3. OpenType subtables.
  4. Most are constructed upon import from data in otData.py, all are populated with
  5. converter objects from otConverters.py.
  6. """
  7. import copy
  8. from enum import IntEnum
  9. from functools import reduce
  10. from math import radians
  11. import itertools
  12. from collections import defaultdict, namedtuple
  13. from fontTools.ttLib.tables.TupleVariation import TupleVariation
  14. from fontTools.ttLib.tables.otTraverse import dfs_base_table
  15. from fontTools.misc.arrayTools import quantizeRect
  16. from fontTools.misc.roundTools import otRound
  17. from fontTools.misc.transform import Transform, Identity, DecomposedTransform
  18. from fontTools.misc.textTools import bytesjoin, pad, safeEval
  19. from fontTools.misc.vector import Vector
  20. from fontTools.pens.boundsPen import ControlBoundsPen
  21. from fontTools.pens.transformPen import TransformPen
  22. from .otBase import (
  23. BaseTable,
  24. FormatSwitchingBaseTable,
  25. ValueRecord,
  26. CountReference,
  27. getFormatSwitchingBaseTableClass,
  28. )
  29. from fontTools.misc.fixedTools import (
  30. fixedToFloat as fi2fl,
  31. floatToFixed as fl2fi,
  32. floatToFixedToStr as fl2str,
  33. strToFixedToFloat as str2fl,
  34. )
  35. from fontTools.feaLib.lookupDebugInfo import LookupDebugInfo, LOOKUP_DEBUG_INFO_KEY
  36. import logging
  37. import struct
  38. import array
  39. import sys
  40. from enum import IntFlag
  41. from typing import TYPE_CHECKING, Iterator, List, Optional, Set
  42. if TYPE_CHECKING:
  43. from fontTools.ttLib.ttGlyphSet import _TTGlyphSet
  44. log = logging.getLogger(__name__)
  45. class VarComponentFlags(IntFlag):
  46. RESET_UNSPECIFIED_AXES = 1 << 0
  47. HAVE_AXES = 1 << 1
  48. AXIS_VALUES_HAVE_VARIATION = 1 << 2
  49. TRANSFORM_HAS_VARIATION = 1 << 3
  50. HAVE_TRANSLATE_X = 1 << 4
  51. HAVE_TRANSLATE_Y = 1 << 5
  52. HAVE_ROTATION = 1 << 6
  53. HAVE_CONDITION = 1 << 7
  54. HAVE_SCALE_X = 1 << 8
  55. HAVE_SCALE_Y = 1 << 9
  56. HAVE_TCENTER_X = 1 << 10
  57. HAVE_TCENTER_Y = 1 << 11
  58. GID_IS_24BIT = 1 << 12
  59. HAVE_SKEW_X = 1 << 13
  60. HAVE_SKEW_Y = 1 << 14
  61. RESERVED_MASK = (1 << 32) - (1 << 15)
  62. VarTransformMappingValues = namedtuple(
  63. "VarTransformMappingValues",
  64. ["flag", "fractionalBits", "scale", "defaultValue"],
  65. )
  66. VAR_TRANSFORM_MAPPING = {
  67. "translateX": VarTransformMappingValues(
  68. VarComponentFlags.HAVE_TRANSLATE_X, 0, 1, 0
  69. ),
  70. "translateY": VarTransformMappingValues(
  71. VarComponentFlags.HAVE_TRANSLATE_Y, 0, 1, 0
  72. ),
  73. "rotation": VarTransformMappingValues(VarComponentFlags.HAVE_ROTATION, 12, 180, 0),
  74. "scaleX": VarTransformMappingValues(VarComponentFlags.HAVE_SCALE_X, 10, 1, 1),
  75. "scaleY": VarTransformMappingValues(VarComponentFlags.HAVE_SCALE_Y, 10, 1, 1),
  76. "skewX": VarTransformMappingValues(VarComponentFlags.HAVE_SKEW_X, 12, -180, 0),
  77. "skewY": VarTransformMappingValues(VarComponentFlags.HAVE_SKEW_Y, 12, 180, 0),
  78. "tCenterX": VarTransformMappingValues(VarComponentFlags.HAVE_TCENTER_X, 0, 1, 0),
  79. "tCenterY": VarTransformMappingValues(VarComponentFlags.HAVE_TCENTER_Y, 0, 1, 0),
  80. }
  81. # Probably should be somewhere in fontTools.misc
  82. _packer = {
  83. 1: lambda v: struct.pack(">B", v),
  84. 2: lambda v: struct.pack(">H", v),
  85. 3: lambda v: struct.pack(">L", v)[1:],
  86. 4: lambda v: struct.pack(">L", v),
  87. }
  88. _unpacker = {
  89. 1: lambda v: struct.unpack(">B", v)[0],
  90. 2: lambda v: struct.unpack(">H", v)[0],
  91. 3: lambda v: struct.unpack(">L", b"\0" + v)[0],
  92. 4: lambda v: struct.unpack(">L", v)[0],
  93. }
  94. def _read_uint32var(data, i):
  95. """Read a variable-length number from data starting at index i.
  96. Return the number and the next index.
  97. """
  98. b0 = data[i]
  99. if b0 < 0x80:
  100. return b0, i + 1
  101. elif b0 < 0xC0:
  102. return (b0 - 0x80) << 8 | data[i + 1], i + 2
  103. elif b0 < 0xE0:
  104. return (b0 - 0xC0) << 16 | data[i + 1] << 8 | data[i + 2], i + 3
  105. elif b0 < 0xF0:
  106. return (b0 - 0xE0) << 24 | data[i + 1] << 16 | data[i + 2] << 8 | data[
  107. i + 3
  108. ], i + 4
  109. else:
  110. return (b0 - 0xF0) << 32 | data[i + 1] << 24 | data[i + 2] << 16 | data[
  111. i + 3
  112. ] << 8 | data[i + 4], i + 5
  113. def _write_uint32var(v):
  114. """Write a variable-length number.
  115. Return the data.
  116. """
  117. if v < 0x80:
  118. return struct.pack(">B", v)
  119. elif v < 0x4000:
  120. return struct.pack(">H", (v | 0x8000))
  121. elif v < 0x200000:
  122. return struct.pack(">L", (v | 0xC00000))[1:]
  123. elif v < 0x10000000:
  124. return struct.pack(">L", (v | 0xE0000000))
  125. else:
  126. return struct.pack(">B", 0xF0) + struct.pack(">L", v)
  127. class VarComponent:
  128. def __init__(self):
  129. self.populateDefaults()
  130. def populateDefaults(self, propagator=None):
  131. self.flags = 0
  132. self.glyphName = None
  133. self.conditionIndex = None
  134. self.axisIndicesIndex = None
  135. self.axisValues = ()
  136. self.axisValuesVarIndex = NO_VARIATION_INDEX
  137. self.transformVarIndex = NO_VARIATION_INDEX
  138. self.transform = DecomposedTransform()
  139. def decompile(self, data, font, localState):
  140. i = 0
  141. self.flags, i = _read_uint32var(data, i)
  142. flags = self.flags
  143. gidSize = 3 if flags & VarComponentFlags.GID_IS_24BIT else 2
  144. glyphID = _unpacker[gidSize](data[i : i + gidSize])
  145. i += gidSize
  146. self.glyphName = font.glyphOrder[glyphID]
  147. if flags & VarComponentFlags.HAVE_CONDITION:
  148. self.conditionIndex, i = _read_uint32var(data, i)
  149. if flags & VarComponentFlags.HAVE_AXES:
  150. self.axisIndicesIndex, i = _read_uint32var(data, i)
  151. else:
  152. self.axisIndicesIndex = None
  153. if self.axisIndicesIndex is None:
  154. numAxes = 0
  155. else:
  156. axisIndices = localState["AxisIndicesList"].Item[self.axisIndicesIndex]
  157. numAxes = len(axisIndices)
  158. if flags & VarComponentFlags.HAVE_AXES:
  159. axisValues, i = TupleVariation.decompileDeltas_(numAxes, data, i)
  160. self.axisValues = tuple(fi2fl(v, 14) for v in axisValues)
  161. else:
  162. self.axisValues = ()
  163. assert len(self.axisValues) == numAxes
  164. if flags & VarComponentFlags.AXIS_VALUES_HAVE_VARIATION:
  165. self.axisValuesVarIndex, i = _read_uint32var(data, i)
  166. else:
  167. self.axisValuesVarIndex = NO_VARIATION_INDEX
  168. if flags & VarComponentFlags.TRANSFORM_HAS_VARIATION:
  169. self.transformVarIndex, i = _read_uint32var(data, i)
  170. else:
  171. self.transformVarIndex = NO_VARIATION_INDEX
  172. self.transform = DecomposedTransform()
  173. def read_transform_component(values):
  174. nonlocal i
  175. if flags & values.flag:
  176. v = (
  177. fi2fl(
  178. struct.unpack(">h", data[i : i + 2])[0], values.fractionalBits
  179. )
  180. * values.scale
  181. )
  182. i += 2
  183. return v
  184. else:
  185. return values.defaultValue
  186. for attr_name, mapping_values in VAR_TRANSFORM_MAPPING.items():
  187. value = read_transform_component(mapping_values)
  188. setattr(self.transform, attr_name, value)
  189. if not (flags & VarComponentFlags.HAVE_SCALE_Y):
  190. self.transform.scaleY = self.transform.scaleX
  191. n = flags & VarComponentFlags.RESERVED_MASK
  192. while n:
  193. _, i = _read_uint32var(data, i)
  194. n &= n - 1
  195. return data[i:]
  196. def compile(self, font):
  197. data = []
  198. flags = self.flags
  199. glyphID = font.getGlyphID(self.glyphName)
  200. if glyphID > 65535:
  201. flags |= VarComponentFlags.GID_IS_24BIT
  202. data.append(_packer[3](glyphID))
  203. else:
  204. flags &= ~VarComponentFlags.GID_IS_24BIT
  205. data.append(_packer[2](glyphID))
  206. if self.conditionIndex is not None:
  207. flags |= VarComponentFlags.HAVE_CONDITION
  208. data.append(_write_uint32var(self.conditionIndex))
  209. numAxes = len(self.axisValues)
  210. if numAxes:
  211. flags |= VarComponentFlags.HAVE_AXES
  212. data.append(_write_uint32var(self.axisIndicesIndex))
  213. data.append(
  214. TupleVariation.compileDeltaValues_(
  215. [fl2fi(v, 14) for v in self.axisValues]
  216. )
  217. )
  218. else:
  219. flags &= ~VarComponentFlags.HAVE_AXES
  220. if self.axisValuesVarIndex != NO_VARIATION_INDEX:
  221. flags |= VarComponentFlags.AXIS_VALUES_HAVE_VARIATION
  222. data.append(_write_uint32var(self.axisValuesVarIndex))
  223. else:
  224. flags &= ~VarComponentFlags.AXIS_VALUES_HAVE_VARIATION
  225. if self.transformVarIndex != NO_VARIATION_INDEX:
  226. flags |= VarComponentFlags.TRANSFORM_HAS_VARIATION
  227. data.append(_write_uint32var(self.transformVarIndex))
  228. else:
  229. flags &= ~VarComponentFlags.TRANSFORM_HAS_VARIATION
  230. def write_transform_component(value, values):
  231. if flags & values.flag:
  232. return struct.pack(
  233. ">h", fl2fi(value / values.scale, values.fractionalBits)
  234. )
  235. else:
  236. return b""
  237. for attr_name, mapping_values in VAR_TRANSFORM_MAPPING.items():
  238. value = getattr(self.transform, attr_name)
  239. data.append(write_transform_component(value, mapping_values))
  240. return _write_uint32var(flags) + bytesjoin(data)
  241. def toXML(self, writer, ttFont, attrs):
  242. writer.begintag("VarComponent", attrs)
  243. writer.newline()
  244. def write(name, value, attrs=()):
  245. if value is not None:
  246. writer.simpletag(name, (("value", value),) + attrs)
  247. writer.newline()
  248. write("glyphName", self.glyphName)
  249. if self.conditionIndex is not None:
  250. write("conditionIndex", self.conditionIndex)
  251. if self.axisIndicesIndex is not None:
  252. write("axisIndicesIndex", self.axisIndicesIndex)
  253. if (
  254. self.axisIndicesIndex is not None
  255. or self.flags & VarComponentFlags.RESET_UNSPECIFIED_AXES
  256. ):
  257. if self.flags & VarComponentFlags.RESET_UNSPECIFIED_AXES:
  258. attrs = (("resetUnspecifiedAxes", 1),)
  259. else:
  260. attrs = ()
  261. write("axisValues", [float(fl2str(v, 14)) for v in self.axisValues], attrs)
  262. if self.axisValuesVarIndex != NO_VARIATION_INDEX:
  263. write("axisValuesVarIndex", self.axisValuesVarIndex)
  264. if self.transformVarIndex != NO_VARIATION_INDEX:
  265. write("transformVarIndex", self.transformVarIndex)
  266. # Only write transform components that are specified in the
  267. # flags, even if they are the default value.
  268. for attr_name, mapping in VAR_TRANSFORM_MAPPING.items():
  269. if not (self.flags & mapping.flag):
  270. continue
  271. v = getattr(self.transform, attr_name)
  272. write(attr_name, fl2str(v, mapping.fractionalBits))
  273. writer.endtag("VarComponent")
  274. writer.newline()
  275. def fromXML(self, name, attrs, content, ttFont):
  276. content = [c for c in content if isinstance(c, tuple)]
  277. self.populateDefaults()
  278. for name, attrs, content in content:
  279. assert not content
  280. v = attrs["value"]
  281. if name == "glyphName":
  282. self.glyphName = v
  283. elif name == "conditionIndex":
  284. self.conditionIndex = safeEval(v)
  285. elif name == "axisIndicesIndex":
  286. self.axisIndicesIndex = safeEval(v)
  287. elif name == "axisValues":
  288. self.axisValues = tuple(str2fl(v, 14) for v in safeEval(v))
  289. if safeEval(attrs.get("resetUnspecifiedAxes", "0")):
  290. self.flags |= VarComponentFlags.RESET_UNSPECIFIED_AXES
  291. elif name == "axisValuesVarIndex":
  292. self.axisValuesVarIndex = safeEval(v)
  293. elif name == "transformVarIndex":
  294. self.transformVarIndex = safeEval(v)
  295. elif name in VAR_TRANSFORM_MAPPING:
  296. setattr(
  297. self.transform,
  298. name,
  299. safeEval(v),
  300. )
  301. self.flags |= VAR_TRANSFORM_MAPPING[name].flag
  302. else:
  303. assert False, name
  304. def applyTransformDeltas(self, deltas):
  305. i = 0
  306. def read_transform_component_delta(values):
  307. nonlocal i
  308. if self.flags & values.flag:
  309. v = fi2fl(deltas[i], values.fractionalBits) * values.scale
  310. i += 1
  311. return v
  312. else:
  313. return 0
  314. for attr_name, mapping_values in VAR_TRANSFORM_MAPPING.items():
  315. value = read_transform_component_delta(mapping_values)
  316. setattr(
  317. self.transform, attr_name, getattr(self.transform, attr_name) + value
  318. )
  319. if not (self.flags & VarComponentFlags.HAVE_SCALE_Y):
  320. self.transform.scaleY = self.transform.scaleX
  321. assert i == len(deltas), (i, len(deltas))
  322. def __eq__(self, other):
  323. if type(self) != type(other):
  324. return NotImplemented
  325. return self.__dict__ == other.__dict__
  326. def __ne__(self, other):
  327. result = self.__eq__(other)
  328. return result if result is NotImplemented else not result
  329. class VarCompositeGlyph:
  330. def __init__(self, components=None):
  331. self.components = components if components is not None else []
  332. def decompile(self, data, font, localState):
  333. self.components = []
  334. while data:
  335. component = VarComponent()
  336. data = component.decompile(data, font, localState)
  337. self.components.append(component)
  338. def compile(self, font):
  339. data = []
  340. for component in self.components:
  341. data.append(component.compile(font))
  342. return bytesjoin(data)
  343. def toXML(self, xmlWriter, font, attrs, name):
  344. xmlWriter.begintag("VarCompositeGlyph", attrs)
  345. xmlWriter.newline()
  346. for i, component in enumerate(self.components):
  347. component.toXML(xmlWriter, font, [("index", i)])
  348. xmlWriter.endtag("VarCompositeGlyph")
  349. xmlWriter.newline()
  350. def fromXML(self, name, attrs, content, font):
  351. content = [c for c in content if isinstance(c, tuple)]
  352. for name, attrs, content in content:
  353. assert name == "VarComponent"
  354. component = VarComponent()
  355. component.fromXML(name, attrs, content, font)
  356. self.components.append(component)
  357. class AATStateTable(object):
  358. def __init__(self):
  359. self.GlyphClasses = {} # GlyphID --> GlyphClass
  360. self.States = [] # List of AATState, indexed by state number
  361. self.PerGlyphLookups = [] # [{GlyphID:GlyphID}, ...]
  362. class AATState(object):
  363. def __init__(self):
  364. self.Transitions = {} # GlyphClass --> AATAction
  365. class AATAction(object):
  366. _FLAGS = None
  367. @staticmethod
  368. def compileActions(font, states):
  369. return (None, None)
  370. def _writeFlagsToXML(self, xmlWriter):
  371. flags = [f for f in self._FLAGS if self.__dict__[f]]
  372. if flags:
  373. xmlWriter.simpletag("Flags", value=",".join(flags))
  374. xmlWriter.newline()
  375. if self.ReservedFlags != 0:
  376. xmlWriter.simpletag("ReservedFlags", value="0x%04X" % self.ReservedFlags)
  377. xmlWriter.newline()
  378. def _setFlag(self, flag):
  379. assert flag in self._FLAGS, "unsupported flag %s" % flag
  380. self.__dict__[flag] = True
  381. class RearrangementMorphAction(AATAction):
  382. staticSize = 4
  383. actionHeaderSize = 0
  384. _FLAGS = ["MarkFirst", "DontAdvance", "MarkLast"]
  385. _VERBS = {
  386. 0: "no change",
  387. 1: "Ax ⇒ xA",
  388. 2: "xD ⇒ Dx",
  389. 3: "AxD ⇒ DxA",
  390. 4: "ABx ⇒ xAB",
  391. 5: "ABx ⇒ xBA",
  392. 6: "xCD ⇒ CDx",
  393. 7: "xCD ⇒ DCx",
  394. 8: "AxCD ⇒ CDxA",
  395. 9: "AxCD ⇒ DCxA",
  396. 10: "ABxD ⇒ DxAB",
  397. 11: "ABxD ⇒ DxBA",
  398. 12: "ABxCD ⇒ CDxAB",
  399. 13: "ABxCD ⇒ CDxBA",
  400. 14: "ABxCD ⇒ DCxAB",
  401. 15: "ABxCD ⇒ DCxBA",
  402. }
  403. def __init__(self):
  404. self.NewState = 0
  405. self.Verb = 0
  406. self.MarkFirst = False
  407. self.DontAdvance = False
  408. self.MarkLast = False
  409. self.ReservedFlags = 0
  410. def compile(self, writer, font, actionIndex):
  411. assert actionIndex is None
  412. writer.writeUShort(self.NewState)
  413. assert self.Verb >= 0 and self.Verb <= 15, self.Verb
  414. flags = self.Verb | self.ReservedFlags
  415. if self.MarkFirst:
  416. flags |= 0x8000
  417. if self.DontAdvance:
  418. flags |= 0x4000
  419. if self.MarkLast:
  420. flags |= 0x2000
  421. writer.writeUShort(flags)
  422. def decompile(self, reader, font, actionReader):
  423. assert actionReader is None
  424. self.NewState = reader.readUShort()
  425. flags = reader.readUShort()
  426. self.Verb = flags & 0xF
  427. self.MarkFirst = bool(flags & 0x8000)
  428. self.DontAdvance = bool(flags & 0x4000)
  429. self.MarkLast = bool(flags & 0x2000)
  430. self.ReservedFlags = flags & 0x1FF0
  431. def toXML(self, xmlWriter, font, attrs, name):
  432. xmlWriter.begintag(name, **attrs)
  433. xmlWriter.newline()
  434. xmlWriter.simpletag("NewState", value=self.NewState)
  435. xmlWriter.newline()
  436. self._writeFlagsToXML(xmlWriter)
  437. xmlWriter.simpletag("Verb", value=self.Verb)
  438. verbComment = self._VERBS.get(self.Verb)
  439. if verbComment is not None:
  440. xmlWriter.comment(verbComment)
  441. xmlWriter.newline()
  442. xmlWriter.endtag(name)
  443. xmlWriter.newline()
  444. def fromXML(self, name, attrs, content, font):
  445. self.NewState = self.Verb = self.ReservedFlags = 0
  446. self.MarkFirst = self.DontAdvance = self.MarkLast = False
  447. content = [t for t in content if isinstance(t, tuple)]
  448. for eltName, eltAttrs, eltContent in content:
  449. if eltName == "NewState":
  450. self.NewState = safeEval(eltAttrs["value"])
  451. elif eltName == "Verb":
  452. self.Verb = safeEval(eltAttrs["value"])
  453. elif eltName == "ReservedFlags":
  454. self.ReservedFlags = safeEval(eltAttrs["value"])
  455. elif eltName == "Flags":
  456. for flag in eltAttrs["value"].split(","):
  457. self._setFlag(flag.strip())
  458. class ContextualMorphAction(AATAction):
  459. staticSize = 8
  460. actionHeaderSize = 0
  461. _FLAGS = ["SetMark", "DontAdvance"]
  462. def __init__(self):
  463. self.NewState = 0
  464. self.SetMark, self.DontAdvance = False, False
  465. self.ReservedFlags = 0
  466. self.MarkIndex, self.CurrentIndex = 0xFFFF, 0xFFFF
  467. def compile(self, writer, font, actionIndex):
  468. assert actionIndex is None
  469. writer.writeUShort(self.NewState)
  470. flags = self.ReservedFlags
  471. if self.SetMark:
  472. flags |= 0x8000
  473. if self.DontAdvance:
  474. flags |= 0x4000
  475. writer.writeUShort(flags)
  476. writer.writeUShort(self.MarkIndex)
  477. writer.writeUShort(self.CurrentIndex)
  478. def decompile(self, reader, font, actionReader):
  479. assert actionReader is None
  480. self.NewState = reader.readUShort()
  481. flags = reader.readUShort()
  482. self.SetMark = bool(flags & 0x8000)
  483. self.DontAdvance = bool(flags & 0x4000)
  484. self.ReservedFlags = flags & 0x3FFF
  485. self.MarkIndex = reader.readUShort()
  486. self.CurrentIndex = reader.readUShort()
  487. def toXML(self, xmlWriter, font, attrs, name):
  488. xmlWriter.begintag(name, **attrs)
  489. xmlWriter.newline()
  490. xmlWriter.simpletag("NewState", value=self.NewState)
  491. xmlWriter.newline()
  492. self._writeFlagsToXML(xmlWriter)
  493. xmlWriter.simpletag("MarkIndex", value=self.MarkIndex)
  494. xmlWriter.newline()
  495. xmlWriter.simpletag("CurrentIndex", value=self.CurrentIndex)
  496. xmlWriter.newline()
  497. xmlWriter.endtag(name)
  498. xmlWriter.newline()
  499. def fromXML(self, name, attrs, content, font):
  500. self.NewState = self.ReservedFlags = 0
  501. self.SetMark = self.DontAdvance = False
  502. self.MarkIndex, self.CurrentIndex = 0xFFFF, 0xFFFF
  503. content = [t for t in content if isinstance(t, tuple)]
  504. for eltName, eltAttrs, eltContent in content:
  505. if eltName == "NewState":
  506. self.NewState = safeEval(eltAttrs["value"])
  507. elif eltName == "Flags":
  508. for flag in eltAttrs["value"].split(","):
  509. self._setFlag(flag.strip())
  510. elif eltName == "ReservedFlags":
  511. self.ReservedFlags = safeEval(eltAttrs["value"])
  512. elif eltName == "MarkIndex":
  513. self.MarkIndex = safeEval(eltAttrs["value"])
  514. elif eltName == "CurrentIndex":
  515. self.CurrentIndex = safeEval(eltAttrs["value"])
  516. class LigAction(object):
  517. def __init__(self):
  518. self.Store = False
  519. # GlyphIndexDelta is a (possibly negative) delta that gets
  520. # added to the glyph ID at the top of the AAT runtime
  521. # execution stack. It is *not* a byte offset into the
  522. # morx table. The result of the addition, which is performed
  523. # at run time by the shaping engine, is an index into
  524. # the ligature components table. See 'morx' specification.
  525. # In the AAT specification, this field is called Offset;
  526. # but its meaning is quite different from other offsets
  527. # in either AAT or OpenType, so we use a different name.
  528. self.GlyphIndexDelta = 0
  529. class LigatureMorphAction(AATAction):
  530. staticSize = 6
  531. # 4 bytes for each of {action,ligComponents,ligatures}Offset
  532. actionHeaderSize = 12
  533. _FLAGS = ["SetComponent", "DontAdvance"]
  534. def __init__(self):
  535. self.NewState = 0
  536. self.SetComponent, self.DontAdvance = False, False
  537. self.ReservedFlags = 0
  538. self.Actions = []
  539. def compile(self, writer, font, actionIndex):
  540. assert actionIndex is not None
  541. writer.writeUShort(self.NewState)
  542. flags = self.ReservedFlags
  543. if self.SetComponent:
  544. flags |= 0x8000
  545. if self.DontAdvance:
  546. flags |= 0x4000
  547. if len(self.Actions) > 0:
  548. flags |= 0x2000
  549. writer.writeUShort(flags)
  550. if len(self.Actions) > 0:
  551. actions = self.compileLigActions()
  552. writer.writeUShort(actionIndex[actions])
  553. else:
  554. writer.writeUShort(0)
  555. def decompile(self, reader, font, actionReader):
  556. assert actionReader is not None
  557. self.NewState = reader.readUShort()
  558. flags = reader.readUShort()
  559. self.SetComponent = bool(flags & 0x8000)
  560. self.DontAdvance = bool(flags & 0x4000)
  561. performAction = bool(flags & 0x2000)
  562. # As of 2017-09-12, the 'morx' specification says that
  563. # the reserved bitmask in ligature subtables is 0x3FFF.
  564. # However, the specification also defines a flag 0x2000,
  565. # so the reserved value should actually be 0x1FFF.
  566. # TODO: Report this specification bug to Apple.
  567. self.ReservedFlags = flags & 0x1FFF
  568. actionIndex = reader.readUShort()
  569. if performAction:
  570. self.Actions = self._decompileLigActions(actionReader, actionIndex)
  571. else:
  572. self.Actions = []
  573. @staticmethod
  574. def compileActions(font, states):
  575. result, actions, actionIndex = b"", set(), {}
  576. for state in states:
  577. for _glyphClass, trans in state.Transitions.items():
  578. actions.add(trans.compileLigActions())
  579. # Sort the compiled actions in decreasing order of
  580. # length, so that the longer sequence come before the
  581. # shorter ones. For each compiled action ABCD, its
  582. # suffixes BCD, CD, and D do not be encoded separately
  583. # (in case they occur); instead, we can just store an
  584. # index that points into the middle of the longer
  585. # sequence. Every compiled AAT ligature sequence is
  586. # terminated with an end-of-sequence flag, which can
  587. # only be set on the last element of the sequence.
  588. # Therefore, it is sufficient to consider just the
  589. # suffixes.
  590. for a in sorted(actions, key=lambda x: (-len(x), x)):
  591. if a not in actionIndex:
  592. for i in range(0, len(a), 4):
  593. suffix = a[i:]
  594. suffixIndex = (len(result) + i) // 4
  595. actionIndex.setdefault(suffix, suffixIndex)
  596. result += a
  597. result = pad(result, 4)
  598. return (result, actionIndex)
  599. def compileLigActions(self):
  600. result = []
  601. for i, action in enumerate(self.Actions):
  602. last = i == len(self.Actions) - 1
  603. value = action.GlyphIndexDelta & 0x3FFFFFFF
  604. value |= 0x80000000 if last else 0
  605. value |= 0x40000000 if action.Store else 0
  606. result.append(struct.pack(">L", value))
  607. return bytesjoin(result)
  608. def _decompileLigActions(self, actionReader, actionIndex):
  609. actions = []
  610. last = False
  611. reader = actionReader.getSubReader(actionReader.pos + actionIndex * 4)
  612. while not last:
  613. value = reader.readULong()
  614. last = bool(value & 0x80000000)
  615. action = LigAction()
  616. actions.append(action)
  617. action.Store = bool(value & 0x40000000)
  618. delta = value & 0x3FFFFFFF
  619. if delta >= 0x20000000: # sign-extend 30-bit value
  620. delta = -0x40000000 + delta
  621. action.GlyphIndexDelta = delta
  622. return actions
  623. def fromXML(self, name, attrs, content, font):
  624. self.NewState = self.ReservedFlags = 0
  625. self.SetComponent = self.DontAdvance = False
  626. self.ReservedFlags = 0
  627. self.Actions = []
  628. content = [t for t in content if isinstance(t, tuple)]
  629. for eltName, eltAttrs, eltContent in content:
  630. if eltName == "NewState":
  631. self.NewState = safeEval(eltAttrs["value"])
  632. elif eltName == "Flags":
  633. for flag in eltAttrs["value"].split(","):
  634. self._setFlag(flag.strip())
  635. elif eltName == "ReservedFlags":
  636. self.ReservedFlags = safeEval(eltAttrs["value"])
  637. elif eltName == "Action":
  638. action = LigAction()
  639. flags = eltAttrs.get("Flags", "").split(",")
  640. flags = [f.strip() for f in flags]
  641. action.Store = "Store" in flags
  642. action.GlyphIndexDelta = safeEval(eltAttrs["GlyphIndexDelta"])
  643. self.Actions.append(action)
  644. def toXML(self, xmlWriter, font, attrs, name):
  645. xmlWriter.begintag(name, **attrs)
  646. xmlWriter.newline()
  647. xmlWriter.simpletag("NewState", value=self.NewState)
  648. xmlWriter.newline()
  649. self._writeFlagsToXML(xmlWriter)
  650. for action in self.Actions:
  651. attribs = [("GlyphIndexDelta", action.GlyphIndexDelta)]
  652. if action.Store:
  653. attribs.append(("Flags", "Store"))
  654. xmlWriter.simpletag("Action", attribs)
  655. xmlWriter.newline()
  656. xmlWriter.endtag(name)
  657. xmlWriter.newline()
  658. class InsertionMorphAction(AATAction):
  659. staticSize = 8
  660. actionHeaderSize = 4 # 4 bytes for actionOffset
  661. _FLAGS = [
  662. "SetMark",
  663. "DontAdvance",
  664. "CurrentIsKashidaLike",
  665. "MarkedIsKashidaLike",
  666. "CurrentInsertBefore",
  667. "MarkedInsertBefore",
  668. ]
  669. def __init__(self):
  670. self.NewState = 0
  671. for flag in self._FLAGS:
  672. setattr(self, flag, False)
  673. self.ReservedFlags = 0
  674. self.CurrentInsertionAction, self.MarkedInsertionAction = [], []
  675. def compile(self, writer, font, actionIndex):
  676. assert actionIndex is not None
  677. writer.writeUShort(self.NewState)
  678. flags = self.ReservedFlags
  679. if self.SetMark:
  680. flags |= 0x8000
  681. if self.DontAdvance:
  682. flags |= 0x4000
  683. if self.CurrentIsKashidaLike:
  684. flags |= 0x2000
  685. if self.MarkedIsKashidaLike:
  686. flags |= 0x1000
  687. if self.CurrentInsertBefore:
  688. flags |= 0x0800
  689. if self.MarkedInsertBefore:
  690. flags |= 0x0400
  691. flags |= len(self.CurrentInsertionAction) << 5
  692. flags |= len(self.MarkedInsertionAction)
  693. writer.writeUShort(flags)
  694. if len(self.CurrentInsertionAction) > 0:
  695. currentIndex = actionIndex[tuple(self.CurrentInsertionAction)]
  696. else:
  697. currentIndex = 0xFFFF
  698. writer.writeUShort(currentIndex)
  699. if len(self.MarkedInsertionAction) > 0:
  700. markedIndex = actionIndex[tuple(self.MarkedInsertionAction)]
  701. else:
  702. markedIndex = 0xFFFF
  703. writer.writeUShort(markedIndex)
  704. def decompile(self, reader, font, actionReader):
  705. assert actionReader is not None
  706. self.NewState = reader.readUShort()
  707. flags = reader.readUShort()
  708. self.SetMark = bool(flags & 0x8000)
  709. self.DontAdvance = bool(flags & 0x4000)
  710. self.CurrentIsKashidaLike = bool(flags & 0x2000)
  711. self.MarkedIsKashidaLike = bool(flags & 0x1000)
  712. self.CurrentInsertBefore = bool(flags & 0x0800)
  713. self.MarkedInsertBefore = bool(flags & 0x0400)
  714. self.CurrentInsertionAction = self._decompileInsertionAction(
  715. actionReader, font, index=reader.readUShort(), count=((flags & 0x03E0) >> 5)
  716. )
  717. self.MarkedInsertionAction = self._decompileInsertionAction(
  718. actionReader, font, index=reader.readUShort(), count=(flags & 0x001F)
  719. )
  720. def _decompileInsertionAction(self, actionReader, font, index, count):
  721. if index == 0xFFFF or count == 0:
  722. return []
  723. reader = actionReader.getSubReader(actionReader.pos + index * 2)
  724. return font.getGlyphNameMany(reader.readUShortArray(count))
  725. def toXML(self, xmlWriter, font, attrs, name):
  726. xmlWriter.begintag(name, **attrs)
  727. xmlWriter.newline()
  728. xmlWriter.simpletag("NewState", value=self.NewState)
  729. xmlWriter.newline()
  730. self._writeFlagsToXML(xmlWriter)
  731. for g in self.CurrentInsertionAction:
  732. xmlWriter.simpletag("CurrentInsertionAction", glyph=g)
  733. xmlWriter.newline()
  734. for g in self.MarkedInsertionAction:
  735. xmlWriter.simpletag("MarkedInsertionAction", glyph=g)
  736. xmlWriter.newline()
  737. xmlWriter.endtag(name)
  738. xmlWriter.newline()
  739. def fromXML(self, name, attrs, content, font):
  740. self.__init__()
  741. content = [t for t in content if isinstance(t, tuple)]
  742. for eltName, eltAttrs, eltContent in content:
  743. if eltName == "NewState":
  744. self.NewState = safeEval(eltAttrs["value"])
  745. elif eltName == "Flags":
  746. for flag in eltAttrs["value"].split(","):
  747. self._setFlag(flag.strip())
  748. elif eltName == "CurrentInsertionAction":
  749. self.CurrentInsertionAction.append(eltAttrs["glyph"])
  750. elif eltName == "MarkedInsertionAction":
  751. self.MarkedInsertionAction.append(eltAttrs["glyph"])
  752. else:
  753. assert False, eltName
  754. @staticmethod
  755. def compileActions(font, states):
  756. actions, actionIndex, result = set(), {}, b""
  757. for state in states:
  758. for _glyphClass, trans in state.Transitions.items():
  759. if trans.CurrentInsertionAction is not None:
  760. actions.add(tuple(trans.CurrentInsertionAction))
  761. if trans.MarkedInsertionAction is not None:
  762. actions.add(tuple(trans.MarkedInsertionAction))
  763. # Sort the compiled actions in decreasing order of
  764. # length, so that the longer sequence come before the
  765. # shorter ones.
  766. for action in sorted(actions, key=lambda x: (-len(x), x)):
  767. # We insert all sub-sequences of the action glyph sequence
  768. # into actionIndex. For example, if one action triggers on
  769. # glyph sequence [A, B, C, D, E] and another action triggers
  770. # on [C, D], we return result=[A, B, C, D, E] (as list of
  771. # encoded glyph IDs), and actionIndex={('A','B','C','D','E'): 0,
  772. # ('C','D'): 2}.
  773. if action in actionIndex:
  774. continue
  775. for start in range(0, len(action)):
  776. startIndex = (len(result) // 2) + start
  777. for limit in range(start, len(action)):
  778. glyphs = action[start : limit + 1]
  779. actionIndex.setdefault(glyphs, startIndex)
  780. for glyph in action:
  781. glyphID = font.getGlyphID(glyph)
  782. result += struct.pack(">H", glyphID)
  783. return result, actionIndex
  784. class FeatureParams(BaseTable):
  785. def compile(self, writer, font):
  786. assert (
  787. featureParamTypes.get(writer["FeatureTag"]) == self.__class__
  788. ), "Wrong FeatureParams type for feature '%s': %s" % (
  789. writer["FeatureTag"],
  790. self.__class__.__name__,
  791. )
  792. BaseTable.compile(self, writer, font)
  793. def toXML(self, xmlWriter, font, attrs=None, name=None):
  794. BaseTable.toXML(self, xmlWriter, font, attrs, name=self.__class__.__name__)
  795. class FeatureParamsSize(FeatureParams):
  796. pass
  797. class FeatureParamsStylisticSet(FeatureParams):
  798. pass
  799. class FeatureParamsCharacterVariants(FeatureParams):
  800. pass
  801. class Coverage(FormatSwitchingBaseTable):
  802. # manual implementation to get rid of glyphID dependencies
  803. def populateDefaults(self, propagator=None):
  804. if not hasattr(self, "glyphs"):
  805. self.glyphs = []
  806. def postRead(self, rawTable, font):
  807. if self.Format == 1:
  808. self.glyphs = rawTable["GlyphArray"]
  809. elif self.Format == 2:
  810. glyphs = self.glyphs = []
  811. ranges = rawTable["RangeRecord"]
  812. # Some SIL fonts have coverage entries that don't have sorted
  813. # StartCoverageIndex. If it is so, fixup and warn. We undo
  814. # this when writing font out.
  815. sorted_ranges = sorted(ranges, key=lambda a: a.StartCoverageIndex)
  816. if ranges != sorted_ranges:
  817. log.warning("GSUB/GPOS Coverage is not sorted by glyph ids.")
  818. ranges = sorted_ranges
  819. del sorted_ranges
  820. for r in ranges:
  821. start = r.Start
  822. end = r.End
  823. startID = font.getGlyphID(start)
  824. endID = font.getGlyphID(end) + 1
  825. glyphs.extend(font.getGlyphNameMany(range(startID, endID)))
  826. else:
  827. self.glyphs = []
  828. log.warning("Unknown Coverage format: %s", self.Format)
  829. del self.Format # Don't need this anymore
  830. def preWrite(self, font):
  831. glyphs = getattr(self, "glyphs", None)
  832. if glyphs is None:
  833. glyphs = self.glyphs = []
  834. format = 1
  835. rawTable = {"GlyphArray": glyphs}
  836. if glyphs:
  837. # find out whether Format 2 is more compact or not
  838. glyphIDs = font.getGlyphIDMany(glyphs)
  839. brokenOrder = sorted(glyphIDs) != glyphIDs
  840. last = glyphIDs[0]
  841. ranges = [[last]]
  842. for glyphID in glyphIDs[1:]:
  843. if glyphID != last + 1:
  844. ranges[-1].append(last)
  845. ranges.append([glyphID])
  846. last = glyphID
  847. ranges[-1].append(last)
  848. if brokenOrder or len(ranges) * 3 < len(glyphs): # 3 words vs. 1 word
  849. # Format 2 is more compact
  850. index = 0
  851. for i in range(len(ranges)):
  852. start, end = ranges[i]
  853. r = RangeRecord()
  854. r.StartID = start
  855. r.Start = font.getGlyphName(start)
  856. r.End = font.getGlyphName(end)
  857. r.StartCoverageIndex = index
  858. ranges[i] = r
  859. index = index + end - start + 1
  860. if brokenOrder:
  861. log.warning("GSUB/GPOS Coverage is not sorted by glyph ids.")
  862. ranges.sort(key=lambda a: a.StartID)
  863. for r in ranges:
  864. del r.StartID
  865. format = 2
  866. rawTable = {"RangeRecord": ranges}
  867. # else:
  868. # fallthrough; Format 1 is more compact
  869. self.Format = format
  870. return rawTable
  871. def toXML2(self, xmlWriter, font):
  872. for glyphName in getattr(self, "glyphs", []):
  873. xmlWriter.simpletag("Glyph", value=glyphName)
  874. xmlWriter.newline()
  875. def fromXML(self, name, attrs, content, font):
  876. glyphs = getattr(self, "glyphs", None)
  877. if glyphs is None:
  878. glyphs = []
  879. self.glyphs = glyphs
  880. glyphs.append(attrs["value"])
  881. # The special 0xFFFFFFFF delta-set index is used to indicate that there
  882. # is no variation data in the ItemVariationStore for a given variable field
  883. NO_VARIATION_INDEX = 0xFFFFFFFF
  884. class DeltaSetIndexMap(getFormatSwitchingBaseTableClass("uint8")):
  885. def populateDefaults(self, propagator=None):
  886. if not hasattr(self, "mapping"):
  887. self.mapping = []
  888. def postRead(self, rawTable, font):
  889. assert (rawTable["EntryFormat"] & 0xFFC0) == 0
  890. self.mapping = rawTable["mapping"]
  891. @staticmethod
  892. def getEntryFormat(mapping):
  893. ored = 0
  894. for idx in mapping:
  895. ored |= idx
  896. inner = ored & 0xFFFF
  897. innerBits = 0
  898. while inner:
  899. innerBits += 1
  900. inner >>= 1
  901. innerBits = max(innerBits, 1)
  902. assert innerBits <= 16
  903. ored = (ored >> (16 - innerBits)) | (ored & ((1 << innerBits) - 1))
  904. if ored <= 0x000000FF:
  905. entrySize = 1
  906. elif ored <= 0x0000FFFF:
  907. entrySize = 2
  908. elif ored <= 0x00FFFFFF:
  909. entrySize = 3
  910. else:
  911. entrySize = 4
  912. return ((entrySize - 1) << 4) | (innerBits - 1)
  913. def preWrite(self, font):
  914. mapping = getattr(self, "mapping", None)
  915. if mapping is None:
  916. mapping = self.mapping = []
  917. self.Format = 1 if len(mapping) > 0xFFFF else 0
  918. rawTable = self.__dict__.copy()
  919. rawTable["MappingCount"] = len(mapping)
  920. rawTable["EntryFormat"] = self.getEntryFormat(mapping)
  921. return rawTable
  922. def toXML2(self, xmlWriter, font):
  923. # Make xml dump less verbose, by omitting no-op entries like:
  924. # <Map index="..." outer="65535" inner="65535"/>
  925. xmlWriter.comment("Omitted values default to 0xFFFF/0xFFFF (no variations)")
  926. xmlWriter.newline()
  927. for i, value in enumerate(getattr(self, "mapping", [])):
  928. attrs = [("index", i)]
  929. if value != NO_VARIATION_INDEX:
  930. attrs.extend(
  931. [
  932. ("outer", value >> 16),
  933. ("inner", value & 0xFFFF),
  934. ]
  935. )
  936. xmlWriter.simpletag("Map", attrs)
  937. xmlWriter.newline()
  938. def fromXML(self, name, attrs, content, font):
  939. mapping = getattr(self, "mapping", None)
  940. if mapping is None:
  941. self.mapping = mapping = []
  942. index = safeEval(attrs["index"])
  943. outer = safeEval(attrs.get("outer", "0xFFFF"))
  944. inner = safeEval(attrs.get("inner", "0xFFFF"))
  945. assert inner <= 0xFFFF
  946. mapping.insert(index, (outer << 16) | inner)
  947. def __getitem__(self, i):
  948. return self.mapping[i] if i < len(self.mapping) else NO_VARIATION_INDEX
  949. class VarIdxMap(BaseTable):
  950. def populateDefaults(self, propagator=None):
  951. if not hasattr(self, "mapping"):
  952. self.mapping = {}
  953. def postRead(self, rawTable, font):
  954. assert (rawTable["EntryFormat"] & 0xFFC0) == 0
  955. glyphOrder = font.getGlyphOrder()
  956. mapList = rawTable["mapping"]
  957. mapList.extend([mapList[-1]] * (len(glyphOrder) - len(mapList)))
  958. self.mapping = dict(zip(glyphOrder, mapList))
  959. def preWrite(self, font):
  960. mapping = getattr(self, "mapping", None)
  961. if mapping is None:
  962. mapping = self.mapping = {}
  963. glyphOrder = font.getGlyphOrder()
  964. mapping = [mapping[g] for g in glyphOrder]
  965. while len(mapping) > 1 and mapping[-2] == mapping[-1]:
  966. del mapping[-1]
  967. rawTable = {"mapping": mapping}
  968. rawTable["MappingCount"] = len(mapping)
  969. rawTable["EntryFormat"] = DeltaSetIndexMap.getEntryFormat(mapping)
  970. return rawTable
  971. def toXML2(self, xmlWriter, font):
  972. for glyph, value in sorted(getattr(self, "mapping", {}).items()):
  973. attrs = (
  974. ("glyph", glyph),
  975. ("outer", value >> 16),
  976. ("inner", value & 0xFFFF),
  977. )
  978. xmlWriter.simpletag("Map", attrs)
  979. xmlWriter.newline()
  980. def fromXML(self, name, attrs, content, font):
  981. mapping = getattr(self, "mapping", None)
  982. if mapping is None:
  983. mapping = {}
  984. self.mapping = mapping
  985. try:
  986. glyph = attrs["glyph"]
  987. except: # https://github.com/fonttools/fonttools/commit/21cbab8ce9ded3356fef3745122da64dcaf314e9#commitcomment-27649836
  988. glyph = font.getGlyphOrder()[attrs["index"]]
  989. outer = safeEval(attrs["outer"])
  990. inner = safeEval(attrs["inner"])
  991. assert inner <= 0xFFFF
  992. mapping[glyph] = (outer << 16) | inner
  993. def __getitem__(self, glyphName):
  994. return self.mapping.get(glyphName, NO_VARIATION_INDEX)
  995. class VarRegionList(BaseTable):
  996. def preWrite(self, font):
  997. # The OT spec says VarStore.VarRegionList.RegionAxisCount should always
  998. # be equal to the fvar.axisCount, and OTS < v8.0.0 enforces this rule
  999. # even when the VarRegionList is empty. We can't treat RegionAxisCount
  1000. # like a normal propagated count (== len(Region[i].VarRegionAxis)),
  1001. # otherwise it would default to 0 if VarRegionList is empty.
  1002. # Thus, we force it to always be equal to fvar.axisCount.
  1003. # https://github.com/khaledhosny/ots/pull/192
  1004. fvarTable = font.get("fvar")
  1005. if fvarTable:
  1006. self.RegionAxisCount = len(fvarTable.axes)
  1007. return {
  1008. **self.__dict__,
  1009. "RegionAxisCount": CountReference(self.__dict__, "RegionAxisCount"),
  1010. }
  1011. class SingleSubst(FormatSwitchingBaseTable):
  1012. def populateDefaults(self, propagator=None):
  1013. if not hasattr(self, "mapping"):
  1014. self.mapping = {}
  1015. def postRead(self, rawTable, font):
  1016. mapping = {}
  1017. input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
  1018. if self.Format == 1:
  1019. delta = rawTable["DeltaGlyphID"]
  1020. inputGIDS = font.getGlyphIDMany(input)
  1021. outGIDS = [(glyphID + delta) % 65536 for glyphID in inputGIDS]
  1022. outNames = font.getGlyphNameMany(outGIDS)
  1023. for inp, out in zip(input, outNames):
  1024. mapping[inp] = out
  1025. elif self.Format == 2:
  1026. assert (
  1027. len(input) == rawTable["GlyphCount"]
  1028. ), "invalid SingleSubstFormat2 table"
  1029. subst = rawTable["Substitute"]
  1030. for inp, sub in zip(input, subst):
  1031. mapping[inp] = sub
  1032. else:
  1033. assert 0, "unknown format: %s" % self.Format
  1034. self.mapping = mapping
  1035. del self.Format # Don't need this anymore
  1036. def preWrite(self, font):
  1037. mapping = getattr(self, "mapping", None)
  1038. if mapping is None:
  1039. mapping = self.mapping = {}
  1040. items = list(mapping.items())
  1041. getGlyphID = font.getGlyphID
  1042. gidItems = [(getGlyphID(a), getGlyphID(b)) for a, b in items]
  1043. sortableItems = sorted(zip(gidItems, items))
  1044. # figure out format
  1045. format = 2
  1046. delta = None
  1047. for inID, outID in gidItems:
  1048. if delta is None:
  1049. delta = (outID - inID) % 65536
  1050. if (inID + delta) % 65536 != outID:
  1051. break
  1052. else:
  1053. if delta is None:
  1054. # the mapping is empty, better use format 2
  1055. format = 2
  1056. else:
  1057. format = 1
  1058. rawTable = {}
  1059. self.Format = format
  1060. cov = Coverage()
  1061. input = [item[1][0] for item in sortableItems]
  1062. subst = [item[1][1] for item in sortableItems]
  1063. cov.glyphs = input
  1064. rawTable["Coverage"] = cov
  1065. if format == 1:
  1066. assert delta is not None
  1067. rawTable["DeltaGlyphID"] = delta
  1068. else:
  1069. rawTable["Substitute"] = subst
  1070. return rawTable
  1071. def toXML2(self, xmlWriter, font):
  1072. items = sorted(self.mapping.items())
  1073. for inGlyph, outGlyph in items:
  1074. xmlWriter.simpletag("Substitution", [("in", inGlyph), ("out", outGlyph)])
  1075. xmlWriter.newline()
  1076. def fromXML(self, name, attrs, content, font):
  1077. mapping = getattr(self, "mapping", None)
  1078. if mapping is None:
  1079. mapping = {}
  1080. self.mapping = mapping
  1081. mapping[attrs["in"]] = attrs["out"]
  1082. class MultipleSubst(FormatSwitchingBaseTable):
  1083. def populateDefaults(self, propagator=None):
  1084. if not hasattr(self, "mapping"):
  1085. self.mapping = {}
  1086. def postRead(self, rawTable, font):
  1087. mapping = {}
  1088. if self.Format == 1:
  1089. glyphs = _getGlyphsFromCoverageTable(rawTable["Coverage"])
  1090. subst = [s.Substitute for s in rawTable["Sequence"]]
  1091. mapping = dict(zip(glyphs, subst))
  1092. else:
  1093. assert 0, "unknown format: %s" % self.Format
  1094. self.mapping = mapping
  1095. del self.Format # Don't need this anymore
  1096. def preWrite(self, font):
  1097. mapping = getattr(self, "mapping", None)
  1098. if mapping is None:
  1099. mapping = self.mapping = {}
  1100. cov = Coverage()
  1101. cov.glyphs = sorted(list(mapping.keys()), key=font.getGlyphID)
  1102. self.Format = 1
  1103. rawTable = {
  1104. "Coverage": cov,
  1105. "Sequence": [self.makeSequence_(mapping[glyph]) for glyph in cov.glyphs],
  1106. }
  1107. return rawTable
  1108. def toXML2(self, xmlWriter, font):
  1109. items = sorted(self.mapping.items())
  1110. for inGlyph, outGlyphs in items:
  1111. out = ",".join(outGlyphs)
  1112. xmlWriter.simpletag("Substitution", [("in", inGlyph), ("out", out)])
  1113. xmlWriter.newline()
  1114. def fromXML(self, name, attrs, content, font):
  1115. mapping = getattr(self, "mapping", None)
  1116. if mapping is None:
  1117. mapping = {}
  1118. self.mapping = mapping
  1119. # TTX v3.0 and earlier.
  1120. if name == "Coverage":
  1121. self.old_coverage_ = []
  1122. for element in content:
  1123. if not isinstance(element, tuple):
  1124. continue
  1125. element_name, element_attrs, _ = element
  1126. if element_name == "Glyph":
  1127. self.old_coverage_.append(element_attrs["value"])
  1128. return
  1129. if name == "Sequence":
  1130. index = int(attrs.get("index", len(mapping)))
  1131. glyph = self.old_coverage_[index]
  1132. glyph_mapping = mapping[glyph] = []
  1133. for element in content:
  1134. if not isinstance(element, tuple):
  1135. continue
  1136. element_name, element_attrs, _ = element
  1137. if element_name == "Substitute":
  1138. glyph_mapping.append(element_attrs["value"])
  1139. return
  1140. # TTX v3.1 and later.
  1141. outGlyphs = attrs["out"].split(",") if attrs["out"] else []
  1142. mapping[attrs["in"]] = [g.strip() for g in outGlyphs]
  1143. @staticmethod
  1144. def makeSequence_(g):
  1145. seq = Sequence()
  1146. seq.Substitute = g
  1147. return seq
  1148. class ClassDef(FormatSwitchingBaseTable):
  1149. def populateDefaults(self, propagator=None):
  1150. if not hasattr(self, "classDefs"):
  1151. self.classDefs = {}
  1152. def postRead(self, rawTable, font):
  1153. classDefs = {}
  1154. if self.Format == 1:
  1155. start = rawTable["StartGlyph"]
  1156. classList = rawTable["ClassValueArray"]
  1157. startID = font.getGlyphID(start)
  1158. endID = startID + len(classList)
  1159. glyphNames = font.getGlyphNameMany(range(startID, endID))
  1160. for glyphName, cls in zip(glyphNames, classList):
  1161. if cls:
  1162. classDefs[glyphName] = cls
  1163. elif self.Format == 2:
  1164. records = rawTable["ClassRangeRecord"]
  1165. for rec in records:
  1166. cls = rec.Class
  1167. if not cls:
  1168. continue
  1169. start = rec.Start
  1170. end = rec.End
  1171. startID = font.getGlyphID(start)
  1172. endID = font.getGlyphID(end) + 1
  1173. glyphNames = font.getGlyphNameMany(range(startID, endID))
  1174. for glyphName in glyphNames:
  1175. classDefs[glyphName] = cls
  1176. else:
  1177. log.warning("Unknown ClassDef format: %s", self.Format)
  1178. self.classDefs = classDefs
  1179. del self.Format # Don't need this anymore
  1180. def _getClassRanges(self, font):
  1181. classDefs = getattr(self, "classDefs", None)
  1182. if classDefs is None:
  1183. self.classDefs = {}
  1184. return
  1185. getGlyphID = font.getGlyphID
  1186. items = []
  1187. for glyphName, cls in classDefs.items():
  1188. if not cls:
  1189. continue
  1190. items.append((getGlyphID(glyphName), glyphName, cls))
  1191. if items:
  1192. items.sort()
  1193. last, lastName, lastCls = items[0]
  1194. ranges = [[lastCls, last, lastName]]
  1195. for glyphID, glyphName, cls in items[1:]:
  1196. if glyphID != last + 1 or cls != lastCls:
  1197. ranges[-1].extend([last, lastName])
  1198. ranges.append([cls, glyphID, glyphName])
  1199. last = glyphID
  1200. lastName = glyphName
  1201. lastCls = cls
  1202. ranges[-1].extend([last, lastName])
  1203. return ranges
  1204. def preWrite(self, font):
  1205. format = 2
  1206. rawTable = {"ClassRangeRecord": []}
  1207. ranges = self._getClassRanges(font)
  1208. if ranges:
  1209. startGlyph = ranges[0][1]
  1210. endGlyph = ranges[-1][3]
  1211. glyphCount = endGlyph - startGlyph + 1
  1212. if len(ranges) * 3 < glyphCount + 1:
  1213. # Format 2 is more compact
  1214. for i in range(len(ranges)):
  1215. cls, start, startName, end, endName = ranges[i]
  1216. rec = ClassRangeRecord()
  1217. rec.Start = startName
  1218. rec.End = endName
  1219. rec.Class = cls
  1220. ranges[i] = rec
  1221. format = 2
  1222. rawTable = {"ClassRangeRecord": ranges}
  1223. else:
  1224. # Format 1 is more compact
  1225. startGlyphName = ranges[0][2]
  1226. classes = [0] * glyphCount
  1227. for cls, start, startName, end, endName in ranges:
  1228. for g in range(start - startGlyph, end - startGlyph + 1):
  1229. classes[g] = cls
  1230. format = 1
  1231. rawTable = {"StartGlyph": startGlyphName, "ClassValueArray": classes}
  1232. self.Format = format
  1233. return rawTable
  1234. def toXML2(self, xmlWriter, font):
  1235. items = sorted(self.classDefs.items())
  1236. for glyphName, cls in items:
  1237. xmlWriter.simpletag("ClassDef", [("glyph", glyphName), ("class", cls)])
  1238. xmlWriter.newline()
  1239. def fromXML(self, name, attrs, content, font):
  1240. classDefs = getattr(self, "classDefs", None)
  1241. if classDefs is None:
  1242. classDefs = {}
  1243. self.classDefs = classDefs
  1244. classDefs[attrs["glyph"]] = int(attrs["class"])
  1245. class AlternateSubst(FormatSwitchingBaseTable):
  1246. def populateDefaults(self, propagator=None):
  1247. if not hasattr(self, "alternates"):
  1248. self.alternates = {}
  1249. def postRead(self, rawTable, font):
  1250. alternates = {}
  1251. if self.Format == 1:
  1252. input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
  1253. alts = rawTable["AlternateSet"]
  1254. assert len(input) == len(alts)
  1255. for inp, alt in zip(input, alts):
  1256. alternates[inp] = alt.Alternate
  1257. else:
  1258. assert 0, "unknown format: %s" % self.Format
  1259. self.alternates = alternates
  1260. del self.Format # Don't need this anymore
  1261. def preWrite(self, font):
  1262. self.Format = 1
  1263. alternates = getattr(self, "alternates", None)
  1264. if alternates is None:
  1265. alternates = self.alternates = {}
  1266. items = list(alternates.items())
  1267. for i in range(len(items)):
  1268. glyphName, set = items[i]
  1269. items[i] = font.getGlyphID(glyphName), glyphName, set
  1270. items.sort()
  1271. cov = Coverage()
  1272. cov.glyphs = [item[1] for item in items]
  1273. alternates = []
  1274. setList = [item[-1] for item in items]
  1275. for set in setList:
  1276. alts = AlternateSet()
  1277. alts.Alternate = set
  1278. alternates.append(alts)
  1279. # a special case to deal with the fact that several hundred Adobe Japan1-5
  1280. # CJK fonts will overflow an offset if the coverage table isn't pushed to the end.
  1281. # Also useful in that when splitting a sub-table because of an offset overflow
  1282. # I don't need to calculate the change in the subtable offset due to the change in the coverage table size.
  1283. # Allows packing more rules in subtable.
  1284. self.sortCoverageLast = 1
  1285. return {"Coverage": cov, "AlternateSet": alternates}
  1286. def toXML2(self, xmlWriter, font):
  1287. items = sorted(self.alternates.items())
  1288. for glyphName, alternates in items:
  1289. xmlWriter.begintag("AlternateSet", glyph=glyphName)
  1290. xmlWriter.newline()
  1291. for alt in alternates:
  1292. xmlWriter.simpletag("Alternate", glyph=alt)
  1293. xmlWriter.newline()
  1294. xmlWriter.endtag("AlternateSet")
  1295. xmlWriter.newline()
  1296. def fromXML(self, name, attrs, content, font):
  1297. alternates = getattr(self, "alternates", None)
  1298. if alternates is None:
  1299. alternates = {}
  1300. self.alternates = alternates
  1301. glyphName = attrs["glyph"]
  1302. set = []
  1303. alternates[glyphName] = set
  1304. for element in content:
  1305. if not isinstance(element, tuple):
  1306. continue
  1307. name, attrs, content = element
  1308. set.append(attrs["glyph"])
  1309. class LigatureSubst(FormatSwitchingBaseTable):
  1310. def populateDefaults(self, propagator=None):
  1311. if not hasattr(self, "ligatures"):
  1312. self.ligatures = {}
  1313. def postRead(self, rawTable, font):
  1314. ligatures = {}
  1315. if self.Format == 1:
  1316. input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
  1317. ligSets = rawTable["LigatureSet"]
  1318. assert len(input) == len(ligSets)
  1319. for i in range(len(input)):
  1320. ligatures[input[i]] = ligSets[i].Ligature
  1321. else:
  1322. assert 0, "unknown format: %s" % self.Format
  1323. self.ligatures = ligatures
  1324. del self.Format # Don't need this anymore
  1325. @staticmethod
  1326. def _getLigatureSortKey(components):
  1327. # Computes a key for ordering ligatures in a GSUB Type-4 lookup.
  1328. # When building the OpenType lookup, we need to make sure that
  1329. # the longest sequence of components is listed first, so we
  1330. # use the negative length as the key for sorting.
  1331. # Note, we no longer need to worry about deterministic order because the
  1332. # ligature mapping `dict` remembers the insertion order, and this in
  1333. # turn depends on the order in which the ligatures are written in the FEA.
  1334. # Since python sort algorithm is stable, the ligatures of equal length
  1335. # will keep the relative order in which they appear in the feature file.
  1336. # For example, given the following ligatures (all starting with 'f' and
  1337. # thus belonging to the same LigatureSet):
  1338. #
  1339. # feature liga {
  1340. # sub f i by f_i;
  1341. # sub f f f by f_f_f;
  1342. # sub f f by f_f;
  1343. # sub f f i by f_f_i;
  1344. # } liga;
  1345. #
  1346. # this should sort to: f_f_f, f_f_i, f_i, f_f
  1347. # This is also what fea-rs does, see:
  1348. # https://github.com/adobe-type-tools/afdko/issues/1727
  1349. # https://github.com/fonttools/fonttools/issues/3428
  1350. # https://github.com/googlefonts/fontc/pull/680
  1351. return -len(components)
  1352. def preWrite(self, font):
  1353. self.Format = 1
  1354. ligatures = getattr(self, "ligatures", None)
  1355. if ligatures is None:
  1356. ligatures = self.ligatures = {}
  1357. if ligatures and isinstance(next(iter(ligatures)), tuple):
  1358. # New high-level API in v3.1 and later. Note that we just support compiling this
  1359. # for now. We don't load to this API, and don't do XML with it.
  1360. # ligatures is map from components-sequence to lig-glyph
  1361. newLigatures = dict()
  1362. for comps in sorted(ligatures.keys(), key=self._getLigatureSortKey):
  1363. ligature = Ligature()
  1364. ligature.Component = comps[1:]
  1365. ligature.CompCount = len(comps)
  1366. ligature.LigGlyph = ligatures[comps]
  1367. newLigatures.setdefault(comps[0], []).append(ligature)
  1368. ligatures = newLigatures
  1369. items = list(ligatures.items())
  1370. for i in range(len(items)):
  1371. glyphName, set = items[i]
  1372. items[i] = font.getGlyphID(glyphName), glyphName, set
  1373. items.sort()
  1374. cov = Coverage()
  1375. cov.glyphs = [item[1] for item in items]
  1376. ligSets = []
  1377. setList = [item[-1] for item in items]
  1378. for set in setList:
  1379. ligSet = LigatureSet()
  1380. ligs = ligSet.Ligature = []
  1381. for lig in set:
  1382. ligs.append(lig)
  1383. ligSets.append(ligSet)
  1384. # Useful in that when splitting a sub-table because of an offset overflow
  1385. # I don't need to calculate the change in subtabl offset due to the coverage table size.
  1386. # Allows packing more rules in subtable.
  1387. self.sortCoverageLast = 1
  1388. return {"Coverage": cov, "LigatureSet": ligSets}
  1389. def toXML2(self, xmlWriter, font):
  1390. items = sorted(self.ligatures.items())
  1391. for glyphName, ligSets in items:
  1392. xmlWriter.begintag("LigatureSet", glyph=glyphName)
  1393. xmlWriter.newline()
  1394. for lig in ligSets:
  1395. xmlWriter.simpletag(
  1396. "Ligature", glyph=lig.LigGlyph, components=",".join(lig.Component)
  1397. )
  1398. xmlWriter.newline()
  1399. xmlWriter.endtag("LigatureSet")
  1400. xmlWriter.newline()
  1401. def fromXML(self, name, attrs, content, font):
  1402. ligatures = getattr(self, "ligatures", None)
  1403. if ligatures is None:
  1404. ligatures = {}
  1405. self.ligatures = ligatures
  1406. glyphName = attrs["glyph"]
  1407. ligs = []
  1408. ligatures[glyphName] = ligs
  1409. for element in content:
  1410. if not isinstance(element, tuple):
  1411. continue
  1412. name, attrs, content = element
  1413. lig = Ligature()
  1414. lig.LigGlyph = attrs["glyph"]
  1415. components = attrs["components"]
  1416. lig.Component = components.split(",") if components else []
  1417. lig.CompCount = len(lig.Component)
  1418. ligs.append(lig)
  1419. class COLR(BaseTable):
  1420. def decompile(self, reader, font):
  1421. # COLRv0 is exceptional in that LayerRecordCount appears *after* the
  1422. # LayerRecordArray it counts, but the parser logic expects Count fields
  1423. # to always precede the arrays. Here we work around this by parsing the
  1424. # LayerRecordCount before the rest of the table, and storing it in
  1425. # the reader's local state.
  1426. subReader = reader.getSubReader(offset=0)
  1427. for conv in self.getConverters():
  1428. if conv.name != "LayerRecordCount":
  1429. subReader.advance(conv.staticSize)
  1430. continue
  1431. reader[conv.name] = conv.read(subReader, font, tableDict={})
  1432. break
  1433. else:
  1434. raise AssertionError("LayerRecordCount converter not found")
  1435. return BaseTable.decompile(self, reader, font)
  1436. def preWrite(self, font):
  1437. # The writer similarly assumes Count values precede the things counted,
  1438. # thus here we pre-initialize a CountReference; the actual count value
  1439. # will be set to the lenght of the array by the time this is assembled.
  1440. self.LayerRecordCount = None
  1441. return {
  1442. **self.__dict__,
  1443. "LayerRecordCount": CountReference(self.__dict__, "LayerRecordCount"),
  1444. }
  1445. def computeClipBoxes(self, glyphSet: "_TTGlyphSet", quantization: int = 1):
  1446. if self.Version == 0:
  1447. return
  1448. clips = {}
  1449. for rec in self.BaseGlyphList.BaseGlyphPaintRecord:
  1450. try:
  1451. clipBox = rec.Paint.computeClipBox(self, glyphSet, quantization)
  1452. except Exception as e:
  1453. from fontTools.ttLib import TTLibError
  1454. raise TTLibError(
  1455. f"Failed to compute COLR ClipBox for {rec.BaseGlyph!r}"
  1456. ) from e
  1457. if clipBox is not None:
  1458. clips[rec.BaseGlyph] = clipBox
  1459. hasClipList = hasattr(self, "ClipList") and self.ClipList is not None
  1460. if not clips:
  1461. if hasClipList:
  1462. self.ClipList = None
  1463. else:
  1464. if not hasClipList:
  1465. self.ClipList = ClipList()
  1466. self.ClipList.Format = 1
  1467. self.ClipList.clips = clips
  1468. class LookupList(BaseTable):
  1469. @property
  1470. def table(self):
  1471. for l in self.Lookup:
  1472. for st in l.SubTable:
  1473. if type(st).__name__.endswith("Subst"):
  1474. return "GSUB"
  1475. if type(st).__name__.endswith("Pos"):
  1476. return "GPOS"
  1477. raise ValueError
  1478. def toXML2(self, xmlWriter, font):
  1479. if (
  1480. not font
  1481. or "Debg" not in font
  1482. or LOOKUP_DEBUG_INFO_KEY not in font["Debg"].data
  1483. ):
  1484. return super().toXML2(xmlWriter, font)
  1485. debugData = font["Debg"].data[LOOKUP_DEBUG_INFO_KEY][self.table]
  1486. for conv in self.getConverters():
  1487. if conv.repeat:
  1488. value = getattr(self, conv.name, [])
  1489. for lookupIndex, item in enumerate(value):
  1490. if str(lookupIndex) in debugData:
  1491. info = LookupDebugInfo(*debugData[str(lookupIndex)])
  1492. tag = info.location
  1493. if info.name:
  1494. tag = f"{info.name}: {tag}"
  1495. if info.feature:
  1496. script, language, feature = info.feature
  1497. tag = f"{tag} in {feature} ({script}/{language})"
  1498. xmlWriter.comment(tag)
  1499. xmlWriter.newline()
  1500. conv.xmlWrite(
  1501. xmlWriter, font, item, conv.name, [("index", lookupIndex)]
  1502. )
  1503. else:
  1504. if conv.aux and not eval(conv.aux, None, vars(self)):
  1505. continue
  1506. value = getattr(
  1507. self, conv.name, None
  1508. ) # TODO Handle defaults instead of defaulting to None!
  1509. conv.xmlWrite(xmlWriter, font, value, conv.name, [])
  1510. class BaseGlyphRecordArray(BaseTable):
  1511. def preWrite(self, font):
  1512. self.BaseGlyphRecord = sorted(
  1513. self.BaseGlyphRecord, key=lambda rec: font.getGlyphID(rec.BaseGlyph)
  1514. )
  1515. return self.__dict__.copy()
  1516. class BaseGlyphList(BaseTable):
  1517. def preWrite(self, font):
  1518. self.BaseGlyphPaintRecord = sorted(
  1519. self.BaseGlyphPaintRecord, key=lambda rec: font.getGlyphID(rec.BaseGlyph)
  1520. )
  1521. return self.__dict__.copy()
  1522. class ClipBoxFormat(IntEnum):
  1523. Static = 1
  1524. Variable = 2
  1525. def is_variable(self):
  1526. return self is self.Variable
  1527. def as_variable(self):
  1528. return self.Variable
  1529. class ClipBox(getFormatSwitchingBaseTableClass("uint8")):
  1530. formatEnum = ClipBoxFormat
  1531. def as_tuple(self):
  1532. return tuple(getattr(self, conv.name) for conv in self.getConverters())
  1533. def __repr__(self):
  1534. return f"{self.__class__.__name__}{self.as_tuple()}"
  1535. class ClipList(getFormatSwitchingBaseTableClass("uint8")):
  1536. def populateDefaults(self, propagator=None):
  1537. if not hasattr(self, "clips"):
  1538. self.clips = {}
  1539. def postRead(self, rawTable, font):
  1540. clips = {}
  1541. glyphOrder = font.getGlyphOrder()
  1542. for i, rec in enumerate(rawTable["ClipRecord"]):
  1543. if rec.StartGlyphID > rec.EndGlyphID:
  1544. log.warning(
  1545. "invalid ClipRecord[%i].StartGlyphID (%i) > "
  1546. "EndGlyphID (%i); skipped",
  1547. i,
  1548. rec.StartGlyphID,
  1549. rec.EndGlyphID,
  1550. )
  1551. continue
  1552. redefinedGlyphs = []
  1553. missingGlyphs = []
  1554. for glyphID in range(rec.StartGlyphID, rec.EndGlyphID + 1):
  1555. try:
  1556. glyph = glyphOrder[glyphID]
  1557. except IndexError:
  1558. missingGlyphs.append(glyphID)
  1559. continue
  1560. if glyph not in clips:
  1561. clips[glyph] = copy.copy(rec.ClipBox)
  1562. else:
  1563. redefinedGlyphs.append(glyphID)
  1564. if redefinedGlyphs:
  1565. log.warning(
  1566. "ClipRecord[%i] overlaps previous records; "
  1567. "ignoring redefined clip boxes for the "
  1568. "following glyph ID range: [%i-%i]",
  1569. i,
  1570. min(redefinedGlyphs),
  1571. max(redefinedGlyphs),
  1572. )
  1573. if missingGlyphs:
  1574. log.warning(
  1575. "ClipRecord[%i] range references missing " "glyph IDs: [%i-%i]",
  1576. i,
  1577. min(missingGlyphs),
  1578. max(missingGlyphs),
  1579. )
  1580. self.clips = clips
  1581. def groups(self):
  1582. glyphsByClip = defaultdict(list)
  1583. uniqueClips = {}
  1584. for glyphName, clipBox in self.clips.items():
  1585. key = clipBox.as_tuple()
  1586. glyphsByClip[key].append(glyphName)
  1587. if key not in uniqueClips:
  1588. uniqueClips[key] = clipBox
  1589. return {
  1590. frozenset(glyphs): uniqueClips[key] for key, glyphs in glyphsByClip.items()
  1591. }
  1592. def preWrite(self, font):
  1593. if not hasattr(self, "clips"):
  1594. self.clips = {}
  1595. clipBoxRanges = {}
  1596. glyphMap = font.getReverseGlyphMap()
  1597. for glyphs, clipBox in self.groups().items():
  1598. glyphIDs = sorted(
  1599. glyphMap[glyphName] for glyphName in glyphs if glyphName in glyphMap
  1600. )
  1601. if not glyphIDs:
  1602. continue
  1603. last = glyphIDs[0]
  1604. ranges = [[last]]
  1605. for glyphID in glyphIDs[1:]:
  1606. if glyphID != last + 1:
  1607. ranges[-1].append(last)
  1608. ranges.append([glyphID])
  1609. last = glyphID
  1610. ranges[-1].append(last)
  1611. for start, end in ranges:
  1612. assert (start, end) not in clipBoxRanges
  1613. clipBoxRanges[(start, end)] = clipBox
  1614. clipRecords = []
  1615. for (start, end), clipBox in sorted(clipBoxRanges.items()):
  1616. record = ClipRecord()
  1617. record.StartGlyphID = start
  1618. record.EndGlyphID = end
  1619. record.ClipBox = clipBox
  1620. clipRecords.append(record)
  1621. rawTable = {
  1622. "ClipCount": len(clipRecords),
  1623. "ClipRecord": clipRecords,
  1624. }
  1625. return rawTable
  1626. def toXML(self, xmlWriter, font, attrs=None, name=None):
  1627. tableName = name if name else self.__class__.__name__
  1628. if attrs is None:
  1629. attrs = []
  1630. if hasattr(self, "Format"):
  1631. attrs.append(("Format", self.Format))
  1632. xmlWriter.begintag(tableName, attrs)
  1633. xmlWriter.newline()
  1634. # sort clips alphabetically to ensure deterministic XML dump
  1635. for glyphs, clipBox in sorted(
  1636. self.groups().items(), key=lambda item: min(item[0])
  1637. ):
  1638. xmlWriter.begintag("Clip")
  1639. xmlWriter.newline()
  1640. for glyphName in sorted(glyphs):
  1641. xmlWriter.simpletag("Glyph", value=glyphName)
  1642. xmlWriter.newline()
  1643. xmlWriter.begintag("ClipBox", [("Format", clipBox.Format)])
  1644. xmlWriter.newline()
  1645. clipBox.toXML2(xmlWriter, font)
  1646. xmlWriter.endtag("ClipBox")
  1647. xmlWriter.newline()
  1648. xmlWriter.endtag("Clip")
  1649. xmlWriter.newline()
  1650. xmlWriter.endtag(tableName)
  1651. xmlWriter.newline()
  1652. def fromXML(self, name, attrs, content, font):
  1653. clips = getattr(self, "clips", None)
  1654. if clips is None:
  1655. self.clips = clips = {}
  1656. assert name == "Clip"
  1657. glyphs = []
  1658. clipBox = None
  1659. for elem in content:
  1660. if not isinstance(elem, tuple):
  1661. continue
  1662. name, attrs, content = elem
  1663. if name == "Glyph":
  1664. glyphs.append(attrs["value"])
  1665. elif name == "ClipBox":
  1666. clipBox = ClipBox()
  1667. clipBox.Format = safeEval(attrs["Format"])
  1668. for elem in content:
  1669. if not isinstance(elem, tuple):
  1670. continue
  1671. name, attrs, content = elem
  1672. clipBox.fromXML(name, attrs, content, font)
  1673. if clipBox:
  1674. for glyphName in glyphs:
  1675. clips[glyphName] = clipBox
  1676. class ExtendMode(IntEnum):
  1677. PAD = 0
  1678. REPEAT = 1
  1679. REFLECT = 2
  1680. # Porter-Duff modes for COLRv1 PaintComposite:
  1681. # https://github.com/googlefonts/colr-gradients-spec/tree/off_sub_1#compositemode-enumeration
  1682. class CompositeMode(IntEnum):
  1683. CLEAR = 0
  1684. SRC = 1
  1685. DEST = 2
  1686. SRC_OVER = 3
  1687. DEST_OVER = 4
  1688. SRC_IN = 5
  1689. DEST_IN = 6
  1690. SRC_OUT = 7
  1691. DEST_OUT = 8
  1692. SRC_ATOP = 9
  1693. DEST_ATOP = 10
  1694. XOR = 11
  1695. PLUS = 12
  1696. SCREEN = 13
  1697. OVERLAY = 14
  1698. DARKEN = 15
  1699. LIGHTEN = 16
  1700. COLOR_DODGE = 17
  1701. COLOR_BURN = 18
  1702. HARD_LIGHT = 19
  1703. SOFT_LIGHT = 20
  1704. DIFFERENCE = 21
  1705. EXCLUSION = 22
  1706. MULTIPLY = 23
  1707. HSL_HUE = 24
  1708. HSL_SATURATION = 25
  1709. HSL_COLOR = 26
  1710. HSL_LUMINOSITY = 27
  1711. class PaintFormat(IntEnum):
  1712. PaintColrLayers = 1
  1713. PaintSolid = 2
  1714. PaintVarSolid = 3
  1715. PaintLinearGradient = 4
  1716. PaintVarLinearGradient = 5
  1717. PaintRadialGradient = 6
  1718. PaintVarRadialGradient = 7
  1719. PaintSweepGradient = 8
  1720. PaintVarSweepGradient = 9
  1721. PaintGlyph = 10
  1722. PaintColrGlyph = 11
  1723. PaintTransform = 12
  1724. PaintVarTransform = 13
  1725. PaintTranslate = 14
  1726. PaintVarTranslate = 15
  1727. PaintScale = 16
  1728. PaintVarScale = 17
  1729. PaintScaleAroundCenter = 18
  1730. PaintVarScaleAroundCenter = 19
  1731. PaintScaleUniform = 20
  1732. PaintVarScaleUniform = 21
  1733. PaintScaleUniformAroundCenter = 22
  1734. PaintVarScaleUniformAroundCenter = 23
  1735. PaintRotate = 24
  1736. PaintVarRotate = 25
  1737. PaintRotateAroundCenter = 26
  1738. PaintVarRotateAroundCenter = 27
  1739. PaintSkew = 28
  1740. PaintVarSkew = 29
  1741. PaintSkewAroundCenter = 30
  1742. PaintVarSkewAroundCenter = 31
  1743. PaintComposite = 32
  1744. def is_variable(self):
  1745. return self.name.startswith("PaintVar")
  1746. def as_variable(self):
  1747. if self.is_variable():
  1748. return self
  1749. try:
  1750. return PaintFormat.__members__[f"PaintVar{self.name[5:]}"]
  1751. except KeyError:
  1752. return None
  1753. class Paint(getFormatSwitchingBaseTableClass("uint8")):
  1754. formatEnum = PaintFormat
  1755. def getFormatName(self):
  1756. try:
  1757. return self.formatEnum(self.Format).name
  1758. except ValueError:
  1759. raise NotImplementedError(f"Unknown Paint format: {self.Format}")
  1760. def toXML(self, xmlWriter, font, attrs=None, name=None):
  1761. tableName = name if name else self.__class__.__name__
  1762. if attrs is None:
  1763. attrs = []
  1764. attrs.append(("Format", self.Format))
  1765. xmlWriter.begintag(tableName, attrs)
  1766. xmlWriter.comment(self.getFormatName())
  1767. xmlWriter.newline()
  1768. self.toXML2(xmlWriter, font)
  1769. xmlWriter.endtag(tableName)
  1770. xmlWriter.newline()
  1771. def iterPaintSubTables(self, colr: COLR) -> Iterator[BaseTable.SubTableEntry]:
  1772. if self.Format == PaintFormat.PaintColrLayers:
  1773. # https://github.com/fonttools/fonttools/issues/2438: don't die when no LayerList exists
  1774. layers = []
  1775. if colr.LayerList is not None:
  1776. layers = colr.LayerList.Paint
  1777. yield from (
  1778. BaseTable.SubTableEntry(name="Layers", value=v, index=i)
  1779. for i, v in enumerate(
  1780. layers[self.FirstLayerIndex : self.FirstLayerIndex + self.NumLayers]
  1781. )
  1782. )
  1783. return
  1784. if self.Format == PaintFormat.PaintColrGlyph:
  1785. for record in colr.BaseGlyphList.BaseGlyphPaintRecord:
  1786. if record.BaseGlyph == self.Glyph:
  1787. yield BaseTable.SubTableEntry(name="BaseGlyph", value=record.Paint)
  1788. return
  1789. else:
  1790. raise KeyError(f"{self.Glyph!r} not in colr.BaseGlyphList")
  1791. for conv in self.getConverters():
  1792. if conv.tableClass is not None and issubclass(conv.tableClass, type(self)):
  1793. value = getattr(self, conv.name)
  1794. yield BaseTable.SubTableEntry(name=conv.name, value=value)
  1795. def getChildren(self, colr) -> List["Paint"]:
  1796. # this is kept for backward compatibility (e.g. it's used by the subsetter)
  1797. return [p.value for p in self.iterPaintSubTables(colr)]
  1798. def traverse(self, colr: COLR, callback):
  1799. """Depth-first traversal of graph rooted at self, callback on each node."""
  1800. if not callable(callback):
  1801. raise TypeError("callback must be callable")
  1802. for path in dfs_base_table(
  1803. self, iter_subtables_fn=lambda paint: paint.iterPaintSubTables(colr)
  1804. ):
  1805. paint = path[-1].value
  1806. callback(paint)
  1807. def getTransform(self) -> Transform:
  1808. if self.Format == PaintFormat.PaintTransform:
  1809. t = self.Transform
  1810. return Transform(t.xx, t.yx, t.xy, t.yy, t.dx, t.dy)
  1811. elif self.Format == PaintFormat.PaintTranslate:
  1812. return Identity.translate(self.dx, self.dy)
  1813. elif self.Format == PaintFormat.PaintScale:
  1814. return Identity.scale(self.scaleX, self.scaleY)
  1815. elif self.Format == PaintFormat.PaintScaleAroundCenter:
  1816. return (
  1817. Identity.translate(self.centerX, self.centerY)
  1818. .scale(self.scaleX, self.scaleY)
  1819. .translate(-self.centerX, -self.centerY)
  1820. )
  1821. elif self.Format == PaintFormat.PaintScaleUniform:
  1822. return Identity.scale(self.scale)
  1823. elif self.Format == PaintFormat.PaintScaleUniformAroundCenter:
  1824. return (
  1825. Identity.translate(self.centerX, self.centerY)
  1826. .scale(self.scale)
  1827. .translate(-self.centerX, -self.centerY)
  1828. )
  1829. elif self.Format == PaintFormat.PaintRotate:
  1830. return Identity.rotate(radians(self.angle))
  1831. elif self.Format == PaintFormat.PaintRotateAroundCenter:
  1832. return (
  1833. Identity.translate(self.centerX, self.centerY)
  1834. .rotate(radians(self.angle))
  1835. .translate(-self.centerX, -self.centerY)
  1836. )
  1837. elif self.Format == PaintFormat.PaintSkew:
  1838. return Identity.skew(radians(-self.xSkewAngle), radians(self.ySkewAngle))
  1839. elif self.Format == PaintFormat.PaintSkewAroundCenter:
  1840. return (
  1841. Identity.translate(self.centerX, self.centerY)
  1842. .skew(radians(-self.xSkewAngle), radians(self.ySkewAngle))
  1843. .translate(-self.centerX, -self.centerY)
  1844. )
  1845. if PaintFormat(self.Format).is_variable():
  1846. raise NotImplementedError(f"Variable Paints not supported: {self.Format}")
  1847. return Identity
  1848. def computeClipBox(
  1849. self, colr: COLR, glyphSet: "_TTGlyphSet", quantization: int = 1
  1850. ) -> Optional[ClipBox]:
  1851. pen = ControlBoundsPen(glyphSet)
  1852. for path in dfs_base_table(
  1853. self, iter_subtables_fn=lambda paint: paint.iterPaintSubTables(colr)
  1854. ):
  1855. paint = path[-1].value
  1856. if paint.Format == PaintFormat.PaintGlyph:
  1857. transformation = reduce(
  1858. Transform.transform,
  1859. (st.value.getTransform() for st in path),
  1860. Identity,
  1861. )
  1862. glyphSet[paint.Glyph].draw(TransformPen(pen, transformation))
  1863. if pen.bounds is None:
  1864. return None
  1865. cb = ClipBox()
  1866. cb.Format = int(ClipBoxFormat.Static)
  1867. cb.xMin, cb.yMin, cb.xMax, cb.yMax = quantizeRect(pen.bounds, quantization)
  1868. return cb
  1869. # For each subtable format there is a class. However, we don't really distinguish
  1870. # between "field name" and "format name": often these are the same. Yet there's
  1871. # a whole bunch of fields with different names. The following dict is a mapping
  1872. # from "format name" to "field name". _buildClasses() uses this to create a
  1873. # subclass for each alternate field name.
  1874. #
  1875. _equivalents = {
  1876. "MarkArray": ("Mark1Array",),
  1877. "LangSys": ("DefaultLangSys",),
  1878. "Coverage": (
  1879. "MarkCoverage",
  1880. "BaseCoverage",
  1881. "LigatureCoverage",
  1882. "Mark1Coverage",
  1883. "Mark2Coverage",
  1884. "BacktrackCoverage",
  1885. "InputCoverage",
  1886. "LookAheadCoverage",
  1887. "VertGlyphCoverage",
  1888. "HorizGlyphCoverage",
  1889. "TopAccentCoverage",
  1890. "ExtendedShapeCoverage",
  1891. "MathKernCoverage",
  1892. ),
  1893. "ClassDef": (
  1894. "ClassDef1",
  1895. "ClassDef2",
  1896. "BacktrackClassDef",
  1897. "InputClassDef",
  1898. "LookAheadClassDef",
  1899. "GlyphClassDef",
  1900. "MarkAttachClassDef",
  1901. ),
  1902. "Anchor": (
  1903. "EntryAnchor",
  1904. "ExitAnchor",
  1905. "BaseAnchor",
  1906. "LigatureAnchor",
  1907. "Mark2Anchor",
  1908. "MarkAnchor",
  1909. ),
  1910. "Device": (
  1911. "XPlaDevice",
  1912. "YPlaDevice",
  1913. "XAdvDevice",
  1914. "YAdvDevice",
  1915. "XDeviceTable",
  1916. "YDeviceTable",
  1917. "DeviceTable",
  1918. ),
  1919. "Axis": (
  1920. "HorizAxis",
  1921. "VertAxis",
  1922. ),
  1923. "MinMax": ("DefaultMinMax",),
  1924. "BaseCoord": (
  1925. "MinCoord",
  1926. "MaxCoord",
  1927. ),
  1928. "JstfLangSys": ("DefJstfLangSys",),
  1929. "JstfGSUBModList": (
  1930. "ShrinkageEnableGSUB",
  1931. "ShrinkageDisableGSUB",
  1932. "ExtensionEnableGSUB",
  1933. "ExtensionDisableGSUB",
  1934. ),
  1935. "JstfGPOSModList": (
  1936. "ShrinkageEnableGPOS",
  1937. "ShrinkageDisableGPOS",
  1938. "ExtensionEnableGPOS",
  1939. "ExtensionDisableGPOS",
  1940. ),
  1941. "JstfMax": (
  1942. "ShrinkageJstfMax",
  1943. "ExtensionJstfMax",
  1944. ),
  1945. "MathKern": (
  1946. "TopRightMathKern",
  1947. "TopLeftMathKern",
  1948. "BottomRightMathKern",
  1949. "BottomLeftMathKern",
  1950. ),
  1951. "MathGlyphConstruction": ("VertGlyphConstruction", "HorizGlyphConstruction"),
  1952. }
  1953. #
  1954. # OverFlow logic, to automatically create ExtensionLookups
  1955. # XXX This should probably move to otBase.py
  1956. #
  1957. def fixLookupOverFlows(ttf, overflowRecord):
  1958. """Either the offset from the LookupList to a lookup overflowed, or
  1959. an offset from a lookup to a subtable overflowed.
  1960. The table layout is:
  1961. GPSO/GUSB
  1962. Script List
  1963. Feature List
  1964. LookUpList
  1965. Lookup[0] and contents
  1966. SubTable offset list
  1967. SubTable[0] and contents
  1968. ...
  1969. SubTable[n] and contents
  1970. ...
  1971. Lookup[n] and contents
  1972. SubTable offset list
  1973. SubTable[0] and contents
  1974. ...
  1975. SubTable[n] and contents
  1976. If the offset to a lookup overflowed (SubTableIndex is None)
  1977. we must promote the *previous* lookup to an Extension type.
  1978. If the offset from a lookup to subtable overflowed, then we must promote it
  1979. to an Extension Lookup type.
  1980. """
  1981. ok = 0
  1982. lookupIndex = overflowRecord.LookupListIndex
  1983. if overflowRecord.SubTableIndex is None:
  1984. lookupIndex = lookupIndex - 1
  1985. if lookupIndex < 0:
  1986. return ok
  1987. if overflowRecord.tableType == "GSUB":
  1988. extType = 7
  1989. elif overflowRecord.tableType == "GPOS":
  1990. extType = 9
  1991. lookups = ttf[overflowRecord.tableType].table.LookupList.Lookup
  1992. lookup = lookups[lookupIndex]
  1993. # If the previous lookup is an extType, look further back. Very unlikely, but possible.
  1994. while lookup.SubTable[0].__class__.LookupType == extType:
  1995. lookupIndex = lookupIndex - 1
  1996. if lookupIndex < 0:
  1997. return ok
  1998. lookup = lookups[lookupIndex]
  1999. for lookupIndex in range(lookupIndex, len(lookups)):
  2000. lookup = lookups[lookupIndex]
  2001. if lookup.LookupType != extType:
  2002. lookup.LookupType = extType
  2003. for si in range(len(lookup.SubTable)):
  2004. subTable = lookup.SubTable[si]
  2005. extSubTableClass = lookupTypes[overflowRecord.tableType][extType]
  2006. extSubTable = extSubTableClass()
  2007. extSubTable.Format = 1
  2008. extSubTable.ExtSubTable = subTable
  2009. lookup.SubTable[si] = extSubTable
  2010. ok = 1
  2011. return ok
  2012. def splitMultipleSubst(oldSubTable, newSubTable, overflowRecord):
  2013. ok = 1
  2014. oldMapping = sorted(oldSubTable.mapping.items())
  2015. oldLen = len(oldMapping)
  2016. if overflowRecord.itemName in ["Coverage", "RangeRecord"]:
  2017. # Coverage table is written last. Overflow is to or within the
  2018. # the coverage table. We will just cut the subtable in half.
  2019. newLen = oldLen // 2
  2020. elif overflowRecord.itemName == "Sequence":
  2021. # We just need to back up by two items from the overflowed
  2022. # Sequence index to make sure the offset to the Coverage table
  2023. # doesn't overflow.
  2024. newLen = overflowRecord.itemIndex - 1
  2025. newSubTable.mapping = {}
  2026. for i in range(newLen, oldLen):
  2027. item = oldMapping[i]
  2028. key = item[0]
  2029. newSubTable.mapping[key] = item[1]
  2030. del oldSubTable.mapping[key]
  2031. return ok
  2032. def splitAlternateSubst(oldSubTable, newSubTable, overflowRecord):
  2033. ok = 1
  2034. if hasattr(oldSubTable, "sortCoverageLast"):
  2035. newSubTable.sortCoverageLast = oldSubTable.sortCoverageLast
  2036. oldAlts = sorted(oldSubTable.alternates.items())
  2037. oldLen = len(oldAlts)
  2038. if overflowRecord.itemName in ["Coverage", "RangeRecord"]:
  2039. # Coverage table is written last. overflow is to or within the
  2040. # the coverage table. We will just cut the subtable in half.
  2041. newLen = oldLen // 2
  2042. elif overflowRecord.itemName == "AlternateSet":
  2043. # We just need to back up by two items
  2044. # from the overflowed AlternateSet index to make sure the offset
  2045. # to the Coverage table doesn't overflow.
  2046. newLen = overflowRecord.itemIndex - 1
  2047. newSubTable.alternates = {}
  2048. for i in range(newLen, oldLen):
  2049. item = oldAlts[i]
  2050. key = item[0]
  2051. newSubTable.alternates[key] = item[1]
  2052. del oldSubTable.alternates[key]
  2053. return ok
  2054. def splitLigatureSubst(oldSubTable, newSubTable, overflowRecord):
  2055. ok = 1
  2056. oldLigs = sorted(oldSubTable.ligatures.items())
  2057. oldLen = len(oldLigs)
  2058. if overflowRecord.itemName in ["Coverage", "RangeRecord"]:
  2059. # Coverage table is written last. overflow is to or within the
  2060. # the coverage table. We will just cut the subtable in half.
  2061. newLen = oldLen // 2
  2062. elif overflowRecord.itemName == "LigatureSet":
  2063. # We just need to back up by two items
  2064. # from the overflowed AlternateSet index to make sure the offset
  2065. # to the Coverage table doesn't overflow.
  2066. newLen = overflowRecord.itemIndex - 1
  2067. newSubTable.ligatures = {}
  2068. for i in range(newLen, oldLen):
  2069. item = oldLigs[i]
  2070. key = item[0]
  2071. newSubTable.ligatures[key] = item[1]
  2072. del oldSubTable.ligatures[key]
  2073. return ok
  2074. def splitPairPos(oldSubTable, newSubTable, overflowRecord):
  2075. st = oldSubTable
  2076. ok = False
  2077. newSubTable.Format = oldSubTable.Format
  2078. if oldSubTable.Format == 1 and len(oldSubTable.PairSet) > 1:
  2079. for name in "ValueFormat1", "ValueFormat2":
  2080. setattr(newSubTable, name, getattr(oldSubTable, name))
  2081. # Move top half of coverage to new subtable
  2082. newSubTable.Coverage = oldSubTable.Coverage.__class__()
  2083. coverage = oldSubTable.Coverage.glyphs
  2084. records = oldSubTable.PairSet
  2085. oldCount = len(oldSubTable.PairSet) // 2
  2086. oldSubTable.Coverage.glyphs = coverage[:oldCount]
  2087. oldSubTable.PairSet = records[:oldCount]
  2088. newSubTable.Coverage.glyphs = coverage[oldCount:]
  2089. newSubTable.PairSet = records[oldCount:]
  2090. oldSubTable.PairSetCount = len(oldSubTable.PairSet)
  2091. newSubTable.PairSetCount = len(newSubTable.PairSet)
  2092. ok = True
  2093. elif oldSubTable.Format == 2 and len(oldSubTable.Class1Record) > 1:
  2094. if not hasattr(oldSubTable, "Class2Count"):
  2095. oldSubTable.Class2Count = len(oldSubTable.Class1Record[0].Class2Record)
  2096. for name in "Class2Count", "ClassDef2", "ValueFormat1", "ValueFormat2":
  2097. setattr(newSubTable, name, getattr(oldSubTable, name))
  2098. # The two subtables will still have the same ClassDef2 and the table
  2099. # sharing will still cause the sharing to overflow. As such, disable
  2100. # sharing on the one that is serialized second (that's oldSubTable).
  2101. oldSubTable.DontShare = True
  2102. # Move top half of class numbers to new subtable
  2103. newSubTable.Coverage = oldSubTable.Coverage.__class__()
  2104. newSubTable.ClassDef1 = oldSubTable.ClassDef1.__class__()
  2105. coverage = oldSubTable.Coverage.glyphs
  2106. classDefs = oldSubTable.ClassDef1.classDefs
  2107. records = oldSubTable.Class1Record
  2108. oldCount = len(oldSubTable.Class1Record) // 2
  2109. newGlyphs = set(k for k, v in classDefs.items() if v >= oldCount)
  2110. oldSubTable.Coverage.glyphs = [g for g in coverage if g not in newGlyphs]
  2111. oldSubTable.ClassDef1.classDefs = {
  2112. k: v for k, v in classDefs.items() if v < oldCount
  2113. }
  2114. oldSubTable.Class1Record = records[:oldCount]
  2115. newSubTable.Coverage.glyphs = [g for g in coverage if g in newGlyphs]
  2116. newSubTable.ClassDef1.classDefs = {
  2117. k: (v - oldCount) for k, v in classDefs.items() if v > oldCount
  2118. }
  2119. newSubTable.Class1Record = records[oldCount:]
  2120. oldSubTable.Class1Count = len(oldSubTable.Class1Record)
  2121. newSubTable.Class1Count = len(newSubTable.Class1Record)
  2122. ok = True
  2123. return ok
  2124. def splitMarkBasePos(oldSubTable, newSubTable, overflowRecord):
  2125. # split half of the mark classes to the new subtable
  2126. classCount = oldSubTable.ClassCount
  2127. if classCount < 2:
  2128. # oh well, not much left to split...
  2129. return False
  2130. oldClassCount = classCount // 2
  2131. newClassCount = classCount - oldClassCount
  2132. oldMarkCoverage, oldMarkRecords = [], []
  2133. newMarkCoverage, newMarkRecords = [], []
  2134. for glyphName, markRecord in zip(
  2135. oldSubTable.MarkCoverage.glyphs, oldSubTable.MarkArray.MarkRecord
  2136. ):
  2137. if markRecord.Class < oldClassCount:
  2138. oldMarkCoverage.append(glyphName)
  2139. oldMarkRecords.append(markRecord)
  2140. else:
  2141. markRecord.Class -= oldClassCount
  2142. newMarkCoverage.append(glyphName)
  2143. newMarkRecords.append(markRecord)
  2144. oldBaseRecords, newBaseRecords = [], []
  2145. for rec in oldSubTable.BaseArray.BaseRecord:
  2146. oldBaseRecord, newBaseRecord = rec.__class__(), rec.__class__()
  2147. oldBaseRecord.BaseAnchor = rec.BaseAnchor[:oldClassCount]
  2148. newBaseRecord.BaseAnchor = rec.BaseAnchor[oldClassCount:]
  2149. oldBaseRecords.append(oldBaseRecord)
  2150. newBaseRecords.append(newBaseRecord)
  2151. newSubTable.Format = oldSubTable.Format
  2152. oldSubTable.MarkCoverage.glyphs = oldMarkCoverage
  2153. newSubTable.MarkCoverage = oldSubTable.MarkCoverage.__class__()
  2154. newSubTable.MarkCoverage.glyphs = newMarkCoverage
  2155. # share the same BaseCoverage in both halves
  2156. newSubTable.BaseCoverage = oldSubTable.BaseCoverage
  2157. oldSubTable.ClassCount = oldClassCount
  2158. newSubTable.ClassCount = newClassCount
  2159. oldSubTable.MarkArray.MarkRecord = oldMarkRecords
  2160. newSubTable.MarkArray = oldSubTable.MarkArray.__class__()
  2161. newSubTable.MarkArray.MarkRecord = newMarkRecords
  2162. oldSubTable.MarkArray.MarkCount = len(oldMarkRecords)
  2163. newSubTable.MarkArray.MarkCount = len(newMarkRecords)
  2164. oldSubTable.BaseArray.BaseRecord = oldBaseRecords
  2165. newSubTable.BaseArray = oldSubTable.BaseArray.__class__()
  2166. newSubTable.BaseArray.BaseRecord = newBaseRecords
  2167. oldSubTable.BaseArray.BaseCount = len(oldBaseRecords)
  2168. newSubTable.BaseArray.BaseCount = len(newBaseRecords)
  2169. return True
  2170. splitTable = {
  2171. "GSUB": {
  2172. # 1: splitSingleSubst,
  2173. 2: splitMultipleSubst,
  2174. 3: splitAlternateSubst,
  2175. 4: splitLigatureSubst,
  2176. # 5: splitContextSubst,
  2177. # 6: splitChainContextSubst,
  2178. # 7: splitExtensionSubst,
  2179. # 8: splitReverseChainSingleSubst,
  2180. },
  2181. "GPOS": {
  2182. # 1: splitSinglePos,
  2183. 2: splitPairPos,
  2184. # 3: splitCursivePos,
  2185. 4: splitMarkBasePos,
  2186. # 5: splitMarkLigPos,
  2187. # 6: splitMarkMarkPos,
  2188. # 7: splitContextPos,
  2189. # 8: splitChainContextPos,
  2190. # 9: splitExtensionPos,
  2191. },
  2192. }
  2193. def fixSubTableOverFlows(ttf, overflowRecord):
  2194. """
  2195. An offset has overflowed within a sub-table. We need to divide this subtable into smaller parts.
  2196. """
  2197. table = ttf[overflowRecord.tableType].table
  2198. lookup = table.LookupList.Lookup[overflowRecord.LookupListIndex]
  2199. subIndex = overflowRecord.SubTableIndex
  2200. subtable = lookup.SubTable[subIndex]
  2201. # First, try not sharing anything for this subtable...
  2202. if not hasattr(subtable, "DontShare"):
  2203. subtable.DontShare = True
  2204. return True
  2205. if hasattr(subtable, "ExtSubTable"):
  2206. # We split the subtable of the Extension table, and add a new Extension table
  2207. # to contain the new subtable.
  2208. subTableType = subtable.ExtSubTable.__class__.LookupType
  2209. extSubTable = subtable
  2210. subtable = extSubTable.ExtSubTable
  2211. newExtSubTableClass = lookupTypes[overflowRecord.tableType][
  2212. extSubTable.__class__.LookupType
  2213. ]
  2214. newExtSubTable = newExtSubTableClass()
  2215. newExtSubTable.Format = extSubTable.Format
  2216. toInsert = newExtSubTable
  2217. newSubTableClass = lookupTypes[overflowRecord.tableType][subTableType]
  2218. newSubTable = newSubTableClass()
  2219. newExtSubTable.ExtSubTable = newSubTable
  2220. else:
  2221. subTableType = subtable.__class__.LookupType
  2222. newSubTableClass = lookupTypes[overflowRecord.tableType][subTableType]
  2223. newSubTable = newSubTableClass()
  2224. toInsert = newSubTable
  2225. if hasattr(lookup, "SubTableCount"): # may not be defined yet.
  2226. lookup.SubTableCount = lookup.SubTableCount + 1
  2227. try:
  2228. splitFunc = splitTable[overflowRecord.tableType][subTableType]
  2229. except KeyError:
  2230. log.error(
  2231. "Don't know how to split %s lookup type %s",
  2232. overflowRecord.tableType,
  2233. subTableType,
  2234. )
  2235. return False
  2236. ok = splitFunc(subtable, newSubTable, overflowRecord)
  2237. if ok:
  2238. lookup.SubTable.insert(subIndex + 1, toInsert)
  2239. return ok
  2240. # End of OverFlow logic
  2241. def _buildClasses():
  2242. import re
  2243. from .otData import otData
  2244. formatPat = re.compile(r"([A-Za-z0-9]+)Format(\d+)$")
  2245. namespace = globals()
  2246. # populate module with classes
  2247. for name, table in otData:
  2248. baseClass = BaseTable
  2249. m = formatPat.match(name)
  2250. if m:
  2251. # XxxFormatN subtable, we only add the "base" table
  2252. name = m.group(1)
  2253. # the first row of a format-switching otData table describes the Format;
  2254. # the first column defines the type of the Format field.
  2255. # Currently this can be either 'uint16' or 'uint8'.
  2256. formatType = table[0][0]
  2257. baseClass = getFormatSwitchingBaseTableClass(formatType)
  2258. if name not in namespace:
  2259. # the class doesn't exist yet, so the base implementation is used.
  2260. cls = type(name, (baseClass,), {})
  2261. if name in ("GSUB", "GPOS"):
  2262. cls.DontShare = True
  2263. namespace[name] = cls
  2264. # link Var{Table} <-> {Table} (e.g. ColorStop <-> VarColorStop, etc.)
  2265. for name, _ in otData:
  2266. if name.startswith("Var") and len(name) > 3 and name[3:] in namespace:
  2267. varType = namespace[name]
  2268. noVarType = namespace[name[3:]]
  2269. varType.NoVarType = noVarType
  2270. noVarType.VarType = varType
  2271. for base, alts in _equivalents.items():
  2272. base = namespace[base]
  2273. for alt in alts:
  2274. namespace[alt] = base
  2275. global lookupTypes
  2276. lookupTypes = {
  2277. "GSUB": {
  2278. 1: SingleSubst,
  2279. 2: MultipleSubst,
  2280. 3: AlternateSubst,
  2281. 4: LigatureSubst,
  2282. 5: ContextSubst,
  2283. 6: ChainContextSubst,
  2284. 7: ExtensionSubst,
  2285. 8: ReverseChainSingleSubst,
  2286. },
  2287. "GPOS": {
  2288. 1: SinglePos,
  2289. 2: PairPos,
  2290. 3: CursivePos,
  2291. 4: MarkBasePos,
  2292. 5: MarkLigPos,
  2293. 6: MarkMarkPos,
  2294. 7: ContextPos,
  2295. 8: ChainContextPos,
  2296. 9: ExtensionPos,
  2297. },
  2298. "mort": {
  2299. 4: NoncontextualMorph,
  2300. },
  2301. "morx": {
  2302. 0: RearrangementMorph,
  2303. 1: ContextualMorph,
  2304. 2: LigatureMorph,
  2305. # 3: Reserved,
  2306. 4: NoncontextualMorph,
  2307. 5: InsertionMorph,
  2308. },
  2309. }
  2310. lookupTypes["JSTF"] = lookupTypes["GPOS"] # JSTF contains GPOS
  2311. for lookupEnum in lookupTypes.values():
  2312. for enum, cls in lookupEnum.items():
  2313. cls.LookupType = enum
  2314. global featureParamTypes
  2315. featureParamTypes = {
  2316. "size": FeatureParamsSize,
  2317. }
  2318. for i in range(1, 20 + 1):
  2319. featureParamTypes["ss%02d" % i] = FeatureParamsStylisticSet
  2320. for i in range(1, 99 + 1):
  2321. featureParamTypes["cv%02d" % i] = FeatureParamsCharacterVariants
  2322. # add converters to classes
  2323. from .otConverters import buildConverters
  2324. for name, table in otData:
  2325. m = formatPat.match(name)
  2326. if m:
  2327. # XxxFormatN subtable, add converter to "base" table
  2328. name, format = m.groups()
  2329. format = int(format)
  2330. cls = namespace[name]
  2331. if not hasattr(cls, "converters"):
  2332. cls.converters = {}
  2333. cls.convertersByName = {}
  2334. converters, convertersByName = buildConverters(table[1:], namespace)
  2335. cls.converters[format] = converters
  2336. cls.convertersByName[format] = convertersByName
  2337. # XXX Add staticSize?
  2338. else:
  2339. cls = namespace[name]
  2340. cls.converters, cls.convertersByName = buildConverters(table, namespace)
  2341. # XXX Add staticSize?
  2342. _buildClasses()
  2343. def _getGlyphsFromCoverageTable(coverage):
  2344. if coverage is None:
  2345. # empty coverage table
  2346. return []
  2347. else:
  2348. return coverage.glyphs