otTables.py 95 KB

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