__init__.py 104 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654
  1. """cffLib: read/write Adobe CFF fonts
  2. OpenType fonts with PostScript outlines contain a completely independent
  3. font file, Adobe's *Compact Font Format*. So dealing with OpenType fonts
  4. requires also dealing with CFF. This module allows you to read and write
  5. fonts written in the CFF format.
  6. In 2016, OpenType 1.8 introduced the `CFF2 <https://docs.microsoft.com/en-us/typography/opentype/spec/cff2>`_
  7. format which, along with other changes, extended the CFF format to deal with
  8. the demands of variable fonts. This module parses both original CFF and CFF2.
  9. """
  10. from fontTools.misc import sstruct
  11. from fontTools.misc import psCharStrings
  12. from fontTools.misc.arrayTools import unionRect, intRect
  13. from fontTools.misc.textTools import (
  14. bytechr,
  15. byteord,
  16. bytesjoin,
  17. tobytes,
  18. tostr,
  19. safeEval,
  20. )
  21. from fontTools.ttLib import TTFont
  22. from fontTools.ttLib.tables.otBase import OTTableWriter
  23. from fontTools.ttLib.tables.otBase import OTTableReader
  24. from fontTools.ttLib.tables import otTables as ot
  25. from io import BytesIO
  26. import struct
  27. import logging
  28. import re
  29. # mute cffLib debug messages when running ttx in verbose mode
  30. DEBUG = logging.DEBUG - 1
  31. log = logging.getLogger(__name__)
  32. cffHeaderFormat = """
  33. major: B
  34. minor: B
  35. hdrSize: B
  36. """
  37. maxStackLimit = 513
  38. # maxstack operator has been deprecated. max stack is now always 513.
  39. class CFFFontSet(object):
  40. """A CFF font "file" can contain more than one font, although this is
  41. extremely rare (and not allowed within OpenType fonts).
  42. This class is the entry point for parsing a CFF table. To actually
  43. manipulate the data inside the CFF font, you will want to access the
  44. ``CFFFontSet``'s :class:`TopDict` object. To do this, a ``CFFFontSet``
  45. object can either be treated as a dictionary (with appropriate
  46. ``keys()`` and ``values()`` methods) mapping font names to :class:`TopDict`
  47. objects, or as a list.
  48. .. code:: python
  49. from fontTools import ttLib
  50. tt = ttLib.TTFont("Tests/cffLib/data/LinLibertine_RBI.otf")
  51. tt["CFF "].cff
  52. # <fontTools.cffLib.CFFFontSet object at 0x101e24c90>
  53. tt["CFF "].cff[0] # Here's your actual font data
  54. # <fontTools.cffLib.TopDict object at 0x1020f1fd0>
  55. """
  56. def decompile(self, file, otFont, isCFF2=None):
  57. """Parse a binary CFF file into an internal representation. ``file``
  58. should be a file handle object. ``otFont`` is the top-level
  59. :py:class:`fontTools.ttLib.ttFont.TTFont` object containing this CFF file.
  60. If ``isCFF2`` is passed and set to ``True`` or ``False``, then the
  61. library makes an assertion that the CFF header is of the appropriate
  62. version.
  63. """
  64. self.otFont = otFont
  65. sstruct.unpack(cffHeaderFormat, file.read(3), self)
  66. if isCFF2 is not None:
  67. # called from ttLib: assert 'major' as read from file matches the
  68. # expected version
  69. expected_major = 2 if isCFF2 else 1
  70. if self.major != expected_major:
  71. raise ValueError(
  72. "Invalid CFF 'major' version: expected %d, found %d"
  73. % (expected_major, self.major)
  74. )
  75. else:
  76. # use 'major' version from file to determine if isCFF2
  77. assert self.major in (1, 2), "Unknown CFF format"
  78. isCFF2 = self.major == 2
  79. if not isCFF2:
  80. self.offSize = struct.unpack("B", file.read(1))[0]
  81. file.seek(self.hdrSize)
  82. self.fontNames = list(tostr(s) for s in Index(file, isCFF2=isCFF2))
  83. self.topDictIndex = TopDictIndex(file, isCFF2=isCFF2)
  84. self.strings = IndexedStrings(file)
  85. else: # isCFF2
  86. self.topDictSize = struct.unpack(">H", file.read(2))[0]
  87. file.seek(self.hdrSize)
  88. self.fontNames = ["CFF2Font"]
  89. cff2GetGlyphOrder = otFont.getGlyphOrder
  90. # in CFF2, offsetSize is the size of the TopDict data.
  91. self.topDictIndex = TopDictIndex(
  92. file, cff2GetGlyphOrder, self.topDictSize, isCFF2=isCFF2
  93. )
  94. self.strings = None
  95. self.GlobalSubrs = GlobalSubrsIndex(file, isCFF2=isCFF2)
  96. self.topDictIndex.strings = self.strings
  97. self.topDictIndex.GlobalSubrs = self.GlobalSubrs
  98. def __len__(self):
  99. return len(self.fontNames)
  100. def keys(self):
  101. return list(self.fontNames)
  102. def values(self):
  103. return self.topDictIndex
  104. def __getitem__(self, nameOrIndex):
  105. """Return TopDict instance identified by name (str) or index (int
  106. or any object that implements `__index__`).
  107. """
  108. if hasattr(nameOrIndex, "__index__"):
  109. index = nameOrIndex.__index__()
  110. elif isinstance(nameOrIndex, str):
  111. name = nameOrIndex
  112. try:
  113. index = self.fontNames.index(name)
  114. except ValueError:
  115. raise KeyError(nameOrIndex)
  116. else:
  117. raise TypeError(nameOrIndex)
  118. return self.topDictIndex[index]
  119. def compile(self, file, otFont, isCFF2=None):
  120. """Write the object back into binary representation onto the given file.
  121. ``file`` should be a file handle object. ``otFont`` is the top-level
  122. :py:class:`fontTools.ttLib.ttFont.TTFont` object containing this CFF file.
  123. If ``isCFF2`` is passed and set to ``True`` or ``False``, then the
  124. library makes an assertion that the CFF header is of the appropriate
  125. version.
  126. """
  127. self.otFont = otFont
  128. if isCFF2 is not None:
  129. # called from ttLib: assert 'major' value matches expected version
  130. expected_major = 2 if isCFF2 else 1
  131. if self.major != expected_major:
  132. raise ValueError(
  133. "Invalid CFF 'major' version: expected %d, found %d"
  134. % (expected_major, self.major)
  135. )
  136. else:
  137. # use current 'major' value to determine output format
  138. assert self.major in (1, 2), "Unknown CFF format"
  139. isCFF2 = self.major == 2
  140. if otFont.recalcBBoxes and not isCFF2:
  141. for topDict in self.topDictIndex:
  142. topDict.recalcFontBBox()
  143. if not isCFF2:
  144. strings = IndexedStrings()
  145. else:
  146. strings = None
  147. writer = CFFWriter(isCFF2)
  148. topCompiler = self.topDictIndex.getCompiler(strings, self, isCFF2=isCFF2)
  149. if isCFF2:
  150. self.hdrSize = 5
  151. writer.add(sstruct.pack(cffHeaderFormat, self))
  152. # Note: topDictSize will most likely change in CFFWriter.toFile().
  153. self.topDictSize = topCompiler.getDataLength()
  154. writer.add(struct.pack(">H", self.topDictSize))
  155. else:
  156. self.hdrSize = 4
  157. self.offSize = 4 # will most likely change in CFFWriter.toFile().
  158. writer.add(sstruct.pack(cffHeaderFormat, self))
  159. writer.add(struct.pack("B", self.offSize))
  160. if not isCFF2:
  161. fontNames = Index()
  162. for name in self.fontNames:
  163. fontNames.append(name)
  164. writer.add(fontNames.getCompiler(strings, self, isCFF2=isCFF2))
  165. writer.add(topCompiler)
  166. if not isCFF2:
  167. writer.add(strings.getCompiler())
  168. writer.add(self.GlobalSubrs.getCompiler(strings, self, isCFF2=isCFF2))
  169. for topDict in self.topDictIndex:
  170. if not hasattr(topDict, "charset") or topDict.charset is None:
  171. charset = otFont.getGlyphOrder()
  172. topDict.charset = charset
  173. children = topCompiler.getChildren(strings)
  174. for child in children:
  175. writer.add(child)
  176. writer.toFile(file)
  177. def toXML(self, xmlWriter):
  178. """Write the object into XML representation onto the given
  179. :class:`fontTools.misc.xmlWriter.XMLWriter`.
  180. .. code:: python
  181. writer = xmlWriter.XMLWriter(sys.stdout)
  182. tt["CFF "].cff.toXML(writer)
  183. """
  184. xmlWriter.simpletag("major", value=self.major)
  185. xmlWriter.newline()
  186. xmlWriter.simpletag("minor", value=self.minor)
  187. xmlWriter.newline()
  188. for fontName in self.fontNames:
  189. xmlWriter.begintag("CFFFont", name=tostr(fontName))
  190. xmlWriter.newline()
  191. font = self[fontName]
  192. font.toXML(xmlWriter)
  193. xmlWriter.endtag("CFFFont")
  194. xmlWriter.newline()
  195. xmlWriter.newline()
  196. xmlWriter.begintag("GlobalSubrs")
  197. xmlWriter.newline()
  198. self.GlobalSubrs.toXML(xmlWriter)
  199. xmlWriter.endtag("GlobalSubrs")
  200. xmlWriter.newline()
  201. def fromXML(self, name, attrs, content, otFont=None):
  202. """Reads data from the XML element into the ``CFFFontSet`` object."""
  203. self.otFont = otFont
  204. # set defaults. These will be replaced if there are entries for them
  205. # in the XML file.
  206. if not hasattr(self, "major"):
  207. self.major = 1
  208. if not hasattr(self, "minor"):
  209. self.minor = 0
  210. if name == "CFFFont":
  211. if self.major == 1:
  212. if not hasattr(self, "offSize"):
  213. # this will be recalculated when the cff is compiled.
  214. self.offSize = 4
  215. if not hasattr(self, "hdrSize"):
  216. self.hdrSize = 4
  217. if not hasattr(self, "GlobalSubrs"):
  218. self.GlobalSubrs = GlobalSubrsIndex()
  219. if not hasattr(self, "fontNames"):
  220. self.fontNames = []
  221. self.topDictIndex = TopDictIndex()
  222. fontName = attrs["name"]
  223. self.fontNames.append(fontName)
  224. topDict = TopDict(GlobalSubrs=self.GlobalSubrs)
  225. topDict.charset = None # gets filled in later
  226. elif self.major == 2:
  227. if not hasattr(self, "hdrSize"):
  228. self.hdrSize = 5
  229. if not hasattr(self, "GlobalSubrs"):
  230. self.GlobalSubrs = GlobalSubrsIndex()
  231. if not hasattr(self, "fontNames"):
  232. self.fontNames = ["CFF2Font"]
  233. cff2GetGlyphOrder = self.otFont.getGlyphOrder
  234. topDict = TopDict(
  235. GlobalSubrs=self.GlobalSubrs, cff2GetGlyphOrder=cff2GetGlyphOrder
  236. )
  237. self.topDictIndex = TopDictIndex(None, cff2GetGlyphOrder)
  238. self.topDictIndex.append(topDict)
  239. for element in content:
  240. if isinstance(element, str):
  241. continue
  242. name, attrs, content = element
  243. topDict.fromXML(name, attrs, content)
  244. if hasattr(topDict, "VarStore") and topDict.FDArray[0].vstore is None:
  245. fdArray = topDict.FDArray
  246. for fontDict in fdArray:
  247. if hasattr(fontDict, "Private"):
  248. fontDict.Private.vstore = topDict.VarStore
  249. elif name == "GlobalSubrs":
  250. subrCharStringClass = psCharStrings.T2CharString
  251. if not hasattr(self, "GlobalSubrs"):
  252. self.GlobalSubrs = GlobalSubrsIndex()
  253. for element in content:
  254. if isinstance(element, str):
  255. continue
  256. name, attrs, content = element
  257. subr = subrCharStringClass()
  258. subr.fromXML(name, attrs, content)
  259. self.GlobalSubrs.append(subr)
  260. elif name == "major":
  261. self.major = int(attrs["value"])
  262. elif name == "minor":
  263. self.minor = int(attrs["value"])
  264. def convertCFFToCFF2(self, otFont):
  265. from .CFFToCFF2 import _convertCFFToCFF2
  266. _convertCFFToCFF2(self, otFont)
  267. def convertCFF2ToCFF(self, otFont):
  268. from .CFF2ToCFF import _convertCFF2ToCFF
  269. _convertCFF2ToCFF(self, otFont)
  270. def desubroutinize(self):
  271. from .transforms import desubroutinize
  272. desubroutinize(self)
  273. def remove_hints(self):
  274. from .transforms import remove_hints
  275. remove_hints(self)
  276. def remove_unused_subroutines(self):
  277. from .transforms import remove_unused_subroutines
  278. remove_unused_subroutines(self)
  279. class CFFWriter(object):
  280. """Helper class for serializing CFF data to binary. Used by
  281. :meth:`CFFFontSet.compile`."""
  282. def __init__(self, isCFF2):
  283. self.data = []
  284. self.isCFF2 = isCFF2
  285. def add(self, table):
  286. self.data.append(table)
  287. def toFile(self, file):
  288. lastPosList = None
  289. count = 1
  290. while True:
  291. log.log(DEBUG, "CFFWriter.toFile() iteration: %d", count)
  292. count = count + 1
  293. pos = 0
  294. posList = [pos]
  295. for item in self.data:
  296. if hasattr(item, "getDataLength"):
  297. endPos = pos + item.getDataLength()
  298. if isinstance(item, TopDictIndexCompiler) and item.isCFF2:
  299. self.topDictSize = item.getDataLength()
  300. else:
  301. endPos = pos + len(item)
  302. if hasattr(item, "setPos"):
  303. item.setPos(pos, endPos)
  304. pos = endPos
  305. posList.append(pos)
  306. if posList == lastPosList:
  307. break
  308. lastPosList = posList
  309. log.log(DEBUG, "CFFWriter.toFile() writing to file.")
  310. begin = file.tell()
  311. if self.isCFF2:
  312. self.data[1] = struct.pack(">H", self.topDictSize)
  313. else:
  314. self.offSize = calcOffSize(lastPosList[-1])
  315. self.data[1] = struct.pack("B", self.offSize)
  316. posList = [0]
  317. for item in self.data:
  318. if hasattr(item, "toFile"):
  319. item.toFile(file)
  320. else:
  321. file.write(item)
  322. posList.append(file.tell() - begin)
  323. assert posList == lastPosList
  324. def calcOffSize(largestOffset):
  325. if largestOffset < 0x100:
  326. offSize = 1
  327. elif largestOffset < 0x10000:
  328. offSize = 2
  329. elif largestOffset < 0x1000000:
  330. offSize = 3
  331. else:
  332. offSize = 4
  333. return offSize
  334. class IndexCompiler(object):
  335. """Base class for writing CFF `INDEX data <https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#5-index-data>`_
  336. to binary."""
  337. def __init__(self, items, strings, parent, isCFF2=None):
  338. if isCFF2 is None and hasattr(parent, "isCFF2"):
  339. isCFF2 = parent.isCFF2
  340. assert isCFF2 is not None
  341. self.isCFF2 = isCFF2
  342. self.items = self.getItems(items, strings)
  343. self.parent = parent
  344. def getItems(self, items, strings):
  345. return items
  346. def getOffsets(self):
  347. # An empty INDEX contains only the count field.
  348. if self.items:
  349. pos = 1
  350. offsets = [pos]
  351. for item in self.items:
  352. if hasattr(item, "getDataLength"):
  353. pos = pos + item.getDataLength()
  354. else:
  355. pos = pos + len(item)
  356. offsets.append(pos)
  357. else:
  358. offsets = []
  359. return offsets
  360. def getDataLength(self):
  361. if self.isCFF2:
  362. countSize = 4
  363. else:
  364. countSize = 2
  365. if self.items:
  366. lastOffset = self.getOffsets()[-1]
  367. offSize = calcOffSize(lastOffset)
  368. dataLength = (
  369. countSize
  370. + 1 # count
  371. + (len(self.items) + 1) * offSize # offSize
  372. + lastOffset # the offsets
  373. - 1 # size of object data
  374. )
  375. else:
  376. # count. For empty INDEX tables, this is the only entry.
  377. dataLength = countSize
  378. return dataLength
  379. def toFile(self, file):
  380. offsets = self.getOffsets()
  381. if self.isCFF2:
  382. writeCard32(file, len(self.items))
  383. else:
  384. writeCard16(file, len(self.items))
  385. # An empty INDEX contains only the count field.
  386. if self.items:
  387. offSize = calcOffSize(offsets[-1])
  388. writeCard8(file, offSize)
  389. offSize = -offSize
  390. pack = struct.pack
  391. for offset in offsets:
  392. binOffset = pack(">l", offset)[offSize:]
  393. assert len(binOffset) == -offSize
  394. file.write(binOffset)
  395. for item in self.items:
  396. if hasattr(item, "toFile"):
  397. item.toFile(file)
  398. else:
  399. data = tobytes(item, encoding="latin1")
  400. file.write(data)
  401. class IndexedStringsCompiler(IndexCompiler):
  402. def getItems(self, items, strings):
  403. return items.strings
  404. class TopDictIndexCompiler(IndexCompiler):
  405. """Helper class for writing the TopDict to binary."""
  406. def getItems(self, items, strings):
  407. out = []
  408. for item in items:
  409. out.append(item.getCompiler(strings, self))
  410. return out
  411. def getChildren(self, strings):
  412. children = []
  413. for topDict in self.items:
  414. children.extend(topDict.getChildren(strings))
  415. return children
  416. def getOffsets(self):
  417. if self.isCFF2:
  418. offsets = [0, self.items[0].getDataLength()]
  419. return offsets
  420. else:
  421. return super(TopDictIndexCompiler, self).getOffsets()
  422. def getDataLength(self):
  423. if self.isCFF2:
  424. dataLength = self.items[0].getDataLength()
  425. return dataLength
  426. else:
  427. return super(TopDictIndexCompiler, self).getDataLength()
  428. def toFile(self, file):
  429. if self.isCFF2:
  430. self.items[0].toFile(file)
  431. else:
  432. super(TopDictIndexCompiler, self).toFile(file)
  433. class FDArrayIndexCompiler(IndexCompiler):
  434. """Helper class for writing the
  435. `Font DICT INDEX <https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#10-font-dict-index-font-dicts-and-fdselect>`_
  436. to binary."""
  437. def getItems(self, items, strings):
  438. out = []
  439. for item in items:
  440. out.append(item.getCompiler(strings, self))
  441. return out
  442. def getChildren(self, strings):
  443. children = []
  444. for fontDict in self.items:
  445. children.extend(fontDict.getChildren(strings))
  446. return children
  447. def toFile(self, file):
  448. offsets = self.getOffsets()
  449. if self.isCFF2:
  450. writeCard32(file, len(self.items))
  451. else:
  452. writeCard16(file, len(self.items))
  453. offSize = calcOffSize(offsets[-1])
  454. writeCard8(file, offSize)
  455. offSize = -offSize
  456. pack = struct.pack
  457. for offset in offsets:
  458. binOffset = pack(">l", offset)[offSize:]
  459. assert len(binOffset) == -offSize
  460. file.write(binOffset)
  461. for item in self.items:
  462. if hasattr(item, "toFile"):
  463. item.toFile(file)
  464. else:
  465. file.write(item)
  466. def setPos(self, pos, endPos):
  467. self.parent.rawDict["FDArray"] = pos
  468. class GlobalSubrsCompiler(IndexCompiler):
  469. """Helper class for writing the `global subroutine INDEX <https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#9-local-and-global-subr-indexes>`_
  470. to binary."""
  471. def getItems(self, items, strings):
  472. out = []
  473. for cs in items:
  474. cs.compile(self.isCFF2)
  475. out.append(cs.bytecode)
  476. return out
  477. class SubrsCompiler(GlobalSubrsCompiler):
  478. """Helper class for writing the `local subroutine INDEX <https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#9-local-and-global-subr-indexes>`_
  479. to binary."""
  480. def setPos(self, pos, endPos):
  481. offset = pos - self.parent.pos
  482. self.parent.rawDict["Subrs"] = offset
  483. class CharStringsCompiler(GlobalSubrsCompiler):
  484. """Helper class for writing the `CharStrings INDEX <https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#9-local-and-global-subr-indexes>`_
  485. to binary."""
  486. def getItems(self, items, strings):
  487. out = []
  488. for cs in items:
  489. cs.compile(self.isCFF2)
  490. out.append(cs.bytecode)
  491. return out
  492. def setPos(self, pos, endPos):
  493. self.parent.rawDict["CharStrings"] = pos
  494. class Index(object):
  495. """This class represents what the CFF spec calls an INDEX (an array of
  496. variable-sized objects). `Index` items can be addressed and set using
  497. Python list indexing."""
  498. compilerClass = IndexCompiler
  499. def __init__(self, file=None, isCFF2=None):
  500. self.items = []
  501. self.offsets = offsets = []
  502. name = self.__class__.__name__
  503. if file is None:
  504. return
  505. self._isCFF2 = isCFF2
  506. log.log(DEBUG, "loading %s at %s", name, file.tell())
  507. self.file = file
  508. if isCFF2:
  509. count = readCard32(file)
  510. else:
  511. count = readCard16(file)
  512. if count == 0:
  513. return
  514. self.items = [None] * count
  515. offSize = readCard8(file)
  516. log.log(DEBUG, " index count: %s offSize: %s", count, offSize)
  517. assert offSize <= 4, "offSize too large: %s" % offSize
  518. pad = b"\0" * (4 - offSize)
  519. for index in range(count + 1):
  520. chunk = file.read(offSize)
  521. chunk = pad + chunk
  522. (offset,) = struct.unpack(">L", chunk)
  523. offsets.append(int(offset))
  524. self.offsetBase = file.tell() - 1
  525. file.seek(self.offsetBase + offsets[-1]) # pretend we've read the whole lot
  526. log.log(DEBUG, " end of %s at %s", name, file.tell())
  527. def __len__(self):
  528. return len(self.items)
  529. def __getitem__(self, index):
  530. item = self.items[index]
  531. if item is not None:
  532. return item
  533. offset = self.offsets[index] + self.offsetBase
  534. size = self.offsets[index + 1] - self.offsets[index]
  535. file = self.file
  536. file.seek(offset)
  537. data = file.read(size)
  538. assert len(data) == size
  539. item = self.produceItem(index, data, file, offset)
  540. self.items[index] = item
  541. return item
  542. def __setitem__(self, index, item):
  543. self.items[index] = item
  544. def produceItem(self, index, data, file, offset):
  545. return data
  546. def append(self, item):
  547. """Add an item to an INDEX."""
  548. self.items.append(item)
  549. def getCompiler(self, strings, parent, isCFF2=None):
  550. return self.compilerClass(self, strings, parent, isCFF2=isCFF2)
  551. def clear(self):
  552. """Empty the INDEX."""
  553. del self.items[:]
  554. class GlobalSubrsIndex(Index):
  555. """This index contains all the global subroutines in the font. A global
  556. subroutine is a set of ``CharString`` data which is accessible to any
  557. glyph in the font, and are used to store repeated instructions - for
  558. example, components may be encoded as global subroutines, but so could
  559. hinting instructions.
  560. Remember that when interpreting a ``callgsubr`` instruction (or indeed
  561. a ``callsubr`` instruction) that you will need to add the "subroutine
  562. number bias" to number given:
  563. .. code:: python
  564. tt = ttLib.TTFont("Almendra-Bold.otf")
  565. u = tt["CFF "].cff[0].CharStrings["udieresis"]
  566. u.decompile()
  567. u.toXML(XMLWriter(sys.stdout))
  568. # <some stuff>
  569. # -64 callgsubr <-- Subroutine which implements the dieresis mark
  570. # <other stuff>
  571. tt["CFF "].cff[0].GlobalSubrs[-64] # <-- WRONG
  572. # <T2CharString (bytecode) at 103451d10>
  573. tt["CFF "].cff[0].GlobalSubrs[-64 + 107] # <-- RIGHT
  574. # <T2CharString (source) at 103451390>
  575. ("The bias applied depends on the number of subrs (gsubrs). If the number of
  576. subrs (gsubrs) is less than 1240, the bias is 107. Otherwise if it is less
  577. than 33900, it is 1131; otherwise it is 32768.",
  578. `Subroutine Operators <https://docs.microsoft.com/en-us/typography/opentype/otspec180/cff2charstr#section4.4>`)
  579. """
  580. compilerClass = GlobalSubrsCompiler
  581. subrClass = psCharStrings.T2CharString
  582. charStringClass = psCharStrings.T2CharString
  583. def __init__(
  584. self,
  585. file=None,
  586. globalSubrs=None,
  587. private=None,
  588. fdSelect=None,
  589. fdArray=None,
  590. isCFF2=None,
  591. ):
  592. super(GlobalSubrsIndex, self).__init__(file, isCFF2=isCFF2)
  593. self.globalSubrs = globalSubrs
  594. self.private = private
  595. if fdSelect:
  596. self.fdSelect = fdSelect
  597. if fdArray:
  598. self.fdArray = fdArray
  599. def produceItem(self, index, data, file, offset):
  600. if self.private is not None:
  601. private = self.private
  602. elif hasattr(self, "fdArray") and self.fdArray is not None:
  603. if hasattr(self, "fdSelect") and self.fdSelect is not None:
  604. fdIndex = self.fdSelect[index]
  605. else:
  606. fdIndex = 0
  607. private = self.fdArray[fdIndex].Private
  608. else:
  609. private = None
  610. return self.subrClass(data, private=private, globalSubrs=self.globalSubrs)
  611. def toXML(self, xmlWriter):
  612. """Write the subroutines index into XML representation onto the given
  613. :class:`fontTools.misc.xmlWriter.XMLWriter`.
  614. .. code:: python
  615. writer = xmlWriter.XMLWriter(sys.stdout)
  616. tt["CFF "].cff[0].GlobalSubrs.toXML(writer)
  617. """
  618. xmlWriter.comment(
  619. "The 'index' attribute is only for humans; " "it is ignored when parsed."
  620. )
  621. xmlWriter.newline()
  622. for i in range(len(self)):
  623. subr = self[i]
  624. if subr.needsDecompilation():
  625. xmlWriter.begintag("CharString", index=i, raw=1)
  626. else:
  627. xmlWriter.begintag("CharString", index=i)
  628. xmlWriter.newline()
  629. subr.toXML(xmlWriter)
  630. xmlWriter.endtag("CharString")
  631. xmlWriter.newline()
  632. def fromXML(self, name, attrs, content):
  633. if name != "CharString":
  634. return
  635. subr = self.subrClass()
  636. subr.fromXML(name, attrs, content)
  637. self.append(subr)
  638. def getItemAndSelector(self, index):
  639. sel = None
  640. if hasattr(self, "fdSelect"):
  641. sel = self.fdSelect[index]
  642. return self[index], sel
  643. class SubrsIndex(GlobalSubrsIndex):
  644. """This index contains a glyph's local subroutines. A local subroutine is a
  645. private set of ``CharString`` data which is accessible only to the glyph to
  646. which the index is attached."""
  647. compilerClass = SubrsCompiler
  648. class TopDictIndex(Index):
  649. """This index represents the array of ``TopDict`` structures in the font
  650. (again, usually only one entry is present). Hence the following calls are
  651. equivalent:
  652. .. code:: python
  653. tt["CFF "].cff[0]
  654. # <fontTools.cffLib.TopDict object at 0x102ed6e50>
  655. tt["CFF "].cff.topDictIndex[0]
  656. # <fontTools.cffLib.TopDict object at 0x102ed6e50>
  657. """
  658. compilerClass = TopDictIndexCompiler
  659. def __init__(self, file=None, cff2GetGlyphOrder=None, topSize=0, isCFF2=None):
  660. self.cff2GetGlyphOrder = cff2GetGlyphOrder
  661. if file is not None and isCFF2:
  662. self._isCFF2 = isCFF2
  663. self.items = []
  664. name = self.__class__.__name__
  665. log.log(DEBUG, "loading %s at %s", name, file.tell())
  666. self.file = file
  667. count = 1
  668. self.items = [None] * count
  669. self.offsets = [0, topSize]
  670. self.offsetBase = file.tell()
  671. # pretend we've read the whole lot
  672. file.seek(self.offsetBase + topSize)
  673. log.log(DEBUG, " end of %s at %s", name, file.tell())
  674. else:
  675. super(TopDictIndex, self).__init__(file, isCFF2=isCFF2)
  676. def produceItem(self, index, data, file, offset):
  677. top = TopDict(
  678. self.strings,
  679. file,
  680. offset,
  681. self.GlobalSubrs,
  682. self.cff2GetGlyphOrder,
  683. isCFF2=self._isCFF2,
  684. )
  685. top.decompile(data)
  686. return top
  687. def toXML(self, xmlWriter):
  688. for i in range(len(self)):
  689. xmlWriter.begintag("FontDict", index=i)
  690. xmlWriter.newline()
  691. self[i].toXML(xmlWriter)
  692. xmlWriter.endtag("FontDict")
  693. xmlWriter.newline()
  694. class FDArrayIndex(Index):
  695. compilerClass = FDArrayIndexCompiler
  696. def toXML(self, xmlWriter):
  697. for i in range(len(self)):
  698. xmlWriter.begintag("FontDict", index=i)
  699. xmlWriter.newline()
  700. self[i].toXML(xmlWriter)
  701. xmlWriter.endtag("FontDict")
  702. xmlWriter.newline()
  703. def produceItem(self, index, data, file, offset):
  704. fontDict = FontDict(
  705. self.strings,
  706. file,
  707. offset,
  708. self.GlobalSubrs,
  709. isCFF2=self._isCFF2,
  710. vstore=self.vstore,
  711. )
  712. fontDict.decompile(data)
  713. return fontDict
  714. def fromXML(self, name, attrs, content):
  715. if name != "FontDict":
  716. return
  717. fontDict = FontDict()
  718. for element in content:
  719. if isinstance(element, str):
  720. continue
  721. name, attrs, content = element
  722. fontDict.fromXML(name, attrs, content)
  723. self.append(fontDict)
  724. class VarStoreData(object):
  725. def __init__(self, file=None, otVarStore=None):
  726. self.file = file
  727. self.data = None
  728. self.otVarStore = otVarStore
  729. self.font = TTFont() # dummy font for the decompile function.
  730. def decompile(self):
  731. if self.file:
  732. # read data in from file. Assume position is correct.
  733. length = readCard16(self.file)
  734. self.data = self.file.read(length)
  735. globalState = {}
  736. reader = OTTableReader(self.data, globalState)
  737. self.otVarStore = ot.VarStore()
  738. self.otVarStore.decompile(reader, self.font)
  739. self.data = None
  740. return self
  741. def compile(self):
  742. writer = OTTableWriter()
  743. self.otVarStore.compile(writer, self.font)
  744. # Note that this omits the initial Card16 length from the CFF2
  745. # VarStore data block
  746. self.data = writer.getAllData()
  747. def writeXML(self, xmlWriter, name):
  748. self.otVarStore.toXML(xmlWriter, self.font)
  749. def xmlRead(self, name, attrs, content, parent):
  750. self.otVarStore = ot.VarStore()
  751. for element in content:
  752. if isinstance(element, tuple):
  753. name, attrs, content = element
  754. self.otVarStore.fromXML(name, attrs, content, self.font)
  755. else:
  756. pass
  757. return None
  758. def __len__(self):
  759. return len(self.data)
  760. def getNumRegions(self, vsIndex):
  761. if vsIndex is None:
  762. vsIndex = 0
  763. varData = self.otVarStore.VarData[vsIndex]
  764. numRegions = varData.VarRegionCount
  765. return numRegions
  766. class FDSelect(object):
  767. def __init__(self, file=None, numGlyphs=None, format=None):
  768. if file:
  769. # read data in from file
  770. self.format = readCard8(file)
  771. if self.format == 0:
  772. from array import array
  773. self.gidArray = array("B", file.read(numGlyphs)).tolist()
  774. elif self.format == 3:
  775. gidArray = [None] * numGlyphs
  776. nRanges = readCard16(file)
  777. fd = None
  778. prev = None
  779. for i in range(nRanges):
  780. first = readCard16(file)
  781. if prev is not None:
  782. for glyphID in range(prev, first):
  783. gidArray[glyphID] = fd
  784. prev = first
  785. fd = readCard8(file)
  786. if prev is not None:
  787. first = readCard16(file)
  788. for glyphID in range(prev, first):
  789. gidArray[glyphID] = fd
  790. self.gidArray = gidArray
  791. elif self.format == 4:
  792. gidArray = [None] * numGlyphs
  793. nRanges = readCard32(file)
  794. fd = None
  795. prev = None
  796. for i in range(nRanges):
  797. first = readCard32(file)
  798. if prev is not None:
  799. for glyphID in range(prev, first):
  800. gidArray[glyphID] = fd
  801. prev = first
  802. fd = readCard16(file)
  803. if prev is not None:
  804. first = readCard32(file)
  805. for glyphID in range(prev, first):
  806. gidArray[glyphID] = fd
  807. self.gidArray = gidArray
  808. else:
  809. assert False, "unsupported FDSelect format: %s" % format
  810. else:
  811. # reading from XML. Make empty gidArray, and leave format as passed in.
  812. # format is None will result in the smallest representation being used.
  813. self.format = format
  814. self.gidArray = []
  815. def __len__(self):
  816. return len(self.gidArray)
  817. def __getitem__(self, index):
  818. return self.gidArray[index]
  819. def __setitem__(self, index, fdSelectValue):
  820. self.gidArray[index] = fdSelectValue
  821. def append(self, fdSelectValue):
  822. self.gidArray.append(fdSelectValue)
  823. class CharStrings(object):
  824. """The ``CharStrings`` in the font represent the instructions for drawing
  825. each glyph. This object presents a dictionary interface to the font's
  826. CharStrings, indexed by glyph name:
  827. .. code:: python
  828. tt["CFF "].cff[0].CharStrings["a"]
  829. # <T2CharString (bytecode) at 103451e90>
  830. See :class:`fontTools.misc.psCharStrings.T1CharString` and
  831. :class:`fontTools.misc.psCharStrings.T2CharString` for how to decompile,
  832. compile and interpret the glyph drawing instructions in the returned objects.
  833. """
  834. def __init__(
  835. self,
  836. file,
  837. charset,
  838. globalSubrs,
  839. private,
  840. fdSelect,
  841. fdArray,
  842. isCFF2=None,
  843. varStore=None,
  844. ):
  845. self.globalSubrs = globalSubrs
  846. self.varStore = varStore
  847. if file is not None:
  848. self.charStringsIndex = SubrsIndex(
  849. file, globalSubrs, private, fdSelect, fdArray, isCFF2=isCFF2
  850. )
  851. self.charStrings = charStrings = {}
  852. for i in range(len(charset)):
  853. charStrings[charset[i]] = i
  854. # read from OTF file: charStrings.values() are indices into
  855. # charStringsIndex.
  856. self.charStringsAreIndexed = 1
  857. else:
  858. self.charStrings = {}
  859. # read from ttx file: charStrings.values() are actual charstrings
  860. self.charStringsAreIndexed = 0
  861. self.private = private
  862. if fdSelect is not None:
  863. self.fdSelect = fdSelect
  864. if fdArray is not None:
  865. self.fdArray = fdArray
  866. def keys(self):
  867. return list(self.charStrings.keys())
  868. def values(self):
  869. if self.charStringsAreIndexed:
  870. return self.charStringsIndex
  871. else:
  872. return list(self.charStrings.values())
  873. def has_key(self, name):
  874. return name in self.charStrings
  875. __contains__ = has_key
  876. def __len__(self):
  877. return len(self.charStrings)
  878. def __getitem__(self, name):
  879. charString = self.charStrings[name]
  880. if self.charStringsAreIndexed:
  881. charString = self.charStringsIndex[charString]
  882. return charString
  883. def __setitem__(self, name, charString):
  884. if self.charStringsAreIndexed:
  885. index = self.charStrings[name]
  886. self.charStringsIndex[index] = charString
  887. else:
  888. self.charStrings[name] = charString
  889. def getItemAndSelector(self, name):
  890. if self.charStringsAreIndexed:
  891. index = self.charStrings[name]
  892. return self.charStringsIndex.getItemAndSelector(index)
  893. else:
  894. if hasattr(self, "fdArray"):
  895. if hasattr(self, "fdSelect"):
  896. sel = self.charStrings[name].fdSelectIndex
  897. else:
  898. sel = 0
  899. else:
  900. sel = None
  901. return self.charStrings[name], sel
  902. def toXML(self, xmlWriter):
  903. names = sorted(self.keys())
  904. for name in names:
  905. charStr, fdSelectIndex = self.getItemAndSelector(name)
  906. if charStr.needsDecompilation():
  907. raw = [("raw", 1)]
  908. else:
  909. raw = []
  910. if fdSelectIndex is None:
  911. xmlWriter.begintag("CharString", [("name", name)] + raw)
  912. else:
  913. xmlWriter.begintag(
  914. "CharString",
  915. [("name", name), ("fdSelectIndex", fdSelectIndex)] + raw,
  916. )
  917. xmlWriter.newline()
  918. charStr.toXML(xmlWriter)
  919. xmlWriter.endtag("CharString")
  920. xmlWriter.newline()
  921. def fromXML(self, name, attrs, content):
  922. for element in content:
  923. if isinstance(element, str):
  924. continue
  925. name, attrs, content = element
  926. if name != "CharString":
  927. continue
  928. fdID = -1
  929. if hasattr(self, "fdArray"):
  930. try:
  931. fdID = safeEval(attrs["fdSelectIndex"])
  932. except KeyError:
  933. fdID = 0
  934. private = self.fdArray[fdID].Private
  935. else:
  936. private = self.private
  937. glyphName = attrs["name"]
  938. charStringClass = psCharStrings.T2CharString
  939. charString = charStringClass(private=private, globalSubrs=self.globalSubrs)
  940. charString.fromXML(name, attrs, content)
  941. if fdID >= 0:
  942. charString.fdSelectIndex = fdID
  943. self[glyphName] = charString
  944. def readCard8(file):
  945. return byteord(file.read(1))
  946. def readCard16(file):
  947. (value,) = struct.unpack(">H", file.read(2))
  948. return value
  949. def readCard32(file):
  950. (value,) = struct.unpack(">L", file.read(4))
  951. return value
  952. def writeCard8(file, value):
  953. file.write(bytechr(value))
  954. def writeCard16(file, value):
  955. file.write(struct.pack(">H", value))
  956. def writeCard32(file, value):
  957. file.write(struct.pack(">L", value))
  958. def packCard8(value):
  959. return bytechr(value)
  960. def packCard16(value):
  961. return struct.pack(">H", value)
  962. def packCard32(value):
  963. return struct.pack(">L", value)
  964. def buildOperatorDict(table):
  965. d = {}
  966. for op, name, arg, default, conv in table:
  967. d[op] = (name, arg)
  968. return d
  969. def buildOpcodeDict(table):
  970. d = {}
  971. for op, name, arg, default, conv in table:
  972. if isinstance(op, tuple):
  973. op = bytechr(op[0]) + bytechr(op[1])
  974. else:
  975. op = bytechr(op)
  976. d[name] = (op, arg)
  977. return d
  978. def buildOrder(table):
  979. l = []
  980. for op, name, arg, default, conv in table:
  981. l.append(name)
  982. return l
  983. def buildDefaults(table):
  984. d = {}
  985. for op, name, arg, default, conv in table:
  986. if default is not None:
  987. d[name] = default
  988. return d
  989. def buildConverters(table):
  990. d = {}
  991. for op, name, arg, default, conv in table:
  992. d[name] = conv
  993. return d
  994. class SimpleConverter(object):
  995. def read(self, parent, value):
  996. if not hasattr(parent, "file"):
  997. return self._read(parent, value)
  998. file = parent.file
  999. pos = file.tell()
  1000. try:
  1001. return self._read(parent, value)
  1002. finally:
  1003. file.seek(pos)
  1004. def _read(self, parent, value):
  1005. return value
  1006. def write(self, parent, value):
  1007. return value
  1008. def xmlWrite(self, xmlWriter, name, value):
  1009. xmlWriter.simpletag(name, value=value)
  1010. xmlWriter.newline()
  1011. def xmlRead(self, name, attrs, content, parent):
  1012. return attrs["value"]
  1013. class ASCIIConverter(SimpleConverter):
  1014. def _read(self, parent, value):
  1015. return tostr(value, encoding="ascii")
  1016. def write(self, parent, value):
  1017. return tobytes(value, encoding="ascii")
  1018. def xmlWrite(self, xmlWriter, name, value):
  1019. xmlWriter.simpletag(name, value=tostr(value, encoding="ascii"))
  1020. xmlWriter.newline()
  1021. def xmlRead(self, name, attrs, content, parent):
  1022. return tobytes(attrs["value"], encoding=("ascii"))
  1023. class Latin1Converter(SimpleConverter):
  1024. def _read(self, parent, value):
  1025. return tostr(value, encoding="latin1")
  1026. def write(self, parent, value):
  1027. return tobytes(value, encoding="latin1")
  1028. def xmlWrite(self, xmlWriter, name, value):
  1029. value = tostr(value, encoding="latin1")
  1030. if name in ["Notice", "Copyright"]:
  1031. value = re.sub(r"[\r\n]\s+", " ", value)
  1032. xmlWriter.simpletag(name, value=value)
  1033. xmlWriter.newline()
  1034. def xmlRead(self, name, attrs, content, parent):
  1035. return tobytes(attrs["value"], encoding=("latin1"))
  1036. def parseNum(s):
  1037. try:
  1038. value = int(s)
  1039. except:
  1040. value = float(s)
  1041. return value
  1042. def parseBlendList(s):
  1043. valueList = []
  1044. for element in s:
  1045. if isinstance(element, str):
  1046. continue
  1047. name, attrs, content = element
  1048. blendList = attrs["value"].split()
  1049. blendList = [eval(val) for val in blendList]
  1050. valueList.append(blendList)
  1051. if len(valueList) == 1:
  1052. valueList = valueList[0]
  1053. return valueList
  1054. class NumberConverter(SimpleConverter):
  1055. def xmlWrite(self, xmlWriter, name, value):
  1056. if isinstance(value, list):
  1057. xmlWriter.begintag(name)
  1058. xmlWriter.newline()
  1059. xmlWriter.indent()
  1060. blendValue = " ".join([str(val) for val in value])
  1061. xmlWriter.simpletag(kBlendDictOpName, value=blendValue)
  1062. xmlWriter.newline()
  1063. xmlWriter.dedent()
  1064. xmlWriter.endtag(name)
  1065. xmlWriter.newline()
  1066. else:
  1067. xmlWriter.simpletag(name, value=value)
  1068. xmlWriter.newline()
  1069. def xmlRead(self, name, attrs, content, parent):
  1070. valueString = attrs.get("value", None)
  1071. if valueString is None:
  1072. value = parseBlendList(content)
  1073. else:
  1074. value = parseNum(attrs["value"])
  1075. return value
  1076. class ArrayConverter(SimpleConverter):
  1077. def xmlWrite(self, xmlWriter, name, value):
  1078. if value and isinstance(value[0], list):
  1079. xmlWriter.begintag(name)
  1080. xmlWriter.newline()
  1081. xmlWriter.indent()
  1082. for valueList in value:
  1083. blendValue = " ".join([str(val) for val in valueList])
  1084. xmlWriter.simpletag(kBlendDictOpName, value=blendValue)
  1085. xmlWriter.newline()
  1086. xmlWriter.dedent()
  1087. xmlWriter.endtag(name)
  1088. xmlWriter.newline()
  1089. else:
  1090. value = " ".join([str(val) for val in value])
  1091. xmlWriter.simpletag(name, value=value)
  1092. xmlWriter.newline()
  1093. def xmlRead(self, name, attrs, content, parent):
  1094. valueString = attrs.get("value", None)
  1095. if valueString is None:
  1096. valueList = parseBlendList(content)
  1097. else:
  1098. values = valueString.split()
  1099. valueList = [parseNum(value) for value in values]
  1100. return valueList
  1101. class TableConverter(SimpleConverter):
  1102. def xmlWrite(self, xmlWriter, name, value):
  1103. xmlWriter.begintag(name)
  1104. xmlWriter.newline()
  1105. value.toXML(xmlWriter)
  1106. xmlWriter.endtag(name)
  1107. xmlWriter.newline()
  1108. def xmlRead(self, name, attrs, content, parent):
  1109. ob = self.getClass()()
  1110. for element in content:
  1111. if isinstance(element, str):
  1112. continue
  1113. name, attrs, content = element
  1114. ob.fromXML(name, attrs, content)
  1115. return ob
  1116. class PrivateDictConverter(TableConverter):
  1117. def getClass(self):
  1118. return PrivateDict
  1119. def _read(self, parent, value):
  1120. size, offset = value
  1121. file = parent.file
  1122. isCFF2 = parent._isCFF2
  1123. try:
  1124. vstore = parent.vstore
  1125. except AttributeError:
  1126. vstore = None
  1127. priv = PrivateDict(parent.strings, file, offset, isCFF2=isCFF2, vstore=vstore)
  1128. file.seek(offset)
  1129. data = file.read(size)
  1130. assert len(data) == size
  1131. priv.decompile(data)
  1132. return priv
  1133. def write(self, parent, value):
  1134. return (0, 0) # dummy value
  1135. class SubrsConverter(TableConverter):
  1136. def getClass(self):
  1137. return SubrsIndex
  1138. def _read(self, parent, value):
  1139. file = parent.file
  1140. isCFF2 = parent._isCFF2
  1141. file.seek(parent.offset + value) # Offset(self)
  1142. return SubrsIndex(file, isCFF2=isCFF2)
  1143. def write(self, parent, value):
  1144. return 0 # dummy value
  1145. class CharStringsConverter(TableConverter):
  1146. def _read(self, parent, value):
  1147. file = parent.file
  1148. isCFF2 = parent._isCFF2
  1149. charset = parent.charset
  1150. varStore = getattr(parent, "VarStore", None)
  1151. globalSubrs = parent.GlobalSubrs
  1152. if hasattr(parent, "FDArray"):
  1153. fdArray = parent.FDArray
  1154. if hasattr(parent, "FDSelect"):
  1155. fdSelect = parent.FDSelect
  1156. else:
  1157. fdSelect = None
  1158. private = None
  1159. else:
  1160. fdSelect, fdArray = None, None
  1161. private = parent.Private
  1162. file.seek(value) # Offset(0)
  1163. charStrings = CharStrings(
  1164. file,
  1165. charset,
  1166. globalSubrs,
  1167. private,
  1168. fdSelect,
  1169. fdArray,
  1170. isCFF2=isCFF2,
  1171. varStore=varStore,
  1172. )
  1173. return charStrings
  1174. def write(self, parent, value):
  1175. return 0 # dummy value
  1176. def xmlRead(self, name, attrs, content, parent):
  1177. if hasattr(parent, "FDArray"):
  1178. # if it is a CID-keyed font, then the private Dict is extracted from the
  1179. # parent.FDArray
  1180. fdArray = parent.FDArray
  1181. if hasattr(parent, "FDSelect"):
  1182. fdSelect = parent.FDSelect
  1183. else:
  1184. fdSelect = None
  1185. private = None
  1186. else:
  1187. # if it is a name-keyed font, then the private dict is in the top dict,
  1188. # and
  1189. # there is no fdArray.
  1190. private, fdSelect, fdArray = parent.Private, None, None
  1191. charStrings = CharStrings(
  1192. None,
  1193. None,
  1194. parent.GlobalSubrs,
  1195. private,
  1196. fdSelect,
  1197. fdArray,
  1198. varStore=getattr(parent, "VarStore", None),
  1199. )
  1200. charStrings.fromXML(name, attrs, content)
  1201. return charStrings
  1202. class CharsetConverter(SimpleConverter):
  1203. def _read(self, parent, value):
  1204. isCID = hasattr(parent, "ROS")
  1205. if value > 2:
  1206. numGlyphs = parent.numGlyphs
  1207. file = parent.file
  1208. file.seek(value)
  1209. log.log(DEBUG, "loading charset at %s", value)
  1210. format = readCard8(file)
  1211. if format == 0:
  1212. charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
  1213. elif format == 1 or format == 2:
  1214. charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
  1215. else:
  1216. raise NotImplementedError
  1217. assert len(charset) == numGlyphs
  1218. log.log(DEBUG, " charset end at %s", file.tell())
  1219. # make sure glyph names are unique
  1220. allNames = {}
  1221. newCharset = []
  1222. for glyphName in charset:
  1223. if glyphName in allNames:
  1224. # make up a new glyphName that's unique
  1225. n = allNames[glyphName]
  1226. while (glyphName + "#" + str(n)) in allNames:
  1227. n += 1
  1228. allNames[glyphName] = n + 1
  1229. glyphName = glyphName + "#" + str(n)
  1230. allNames[glyphName] = 1
  1231. newCharset.append(glyphName)
  1232. charset = newCharset
  1233. else: # offset == 0 -> no charset data.
  1234. if isCID or "CharStrings" not in parent.rawDict:
  1235. # We get here only when processing fontDicts from the FDArray of
  1236. # CFF-CID fonts. Only the real topDict references the charset.
  1237. assert value == 0
  1238. charset = None
  1239. elif value == 0:
  1240. charset = cffISOAdobeStrings
  1241. elif value == 1:
  1242. charset = cffIExpertStrings
  1243. elif value == 2:
  1244. charset = cffExpertSubsetStrings
  1245. if charset and (len(charset) != parent.numGlyphs):
  1246. charset = charset[: parent.numGlyphs]
  1247. return charset
  1248. def write(self, parent, value):
  1249. return 0 # dummy value
  1250. def xmlWrite(self, xmlWriter, name, value):
  1251. # XXX only write charset when not in OT/TTX context, where we
  1252. # dump charset as a separate "GlyphOrder" table.
  1253. # # xmlWriter.simpletag("charset")
  1254. xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
  1255. xmlWriter.newline()
  1256. def xmlRead(self, name, attrs, content, parent):
  1257. pass
  1258. class CharsetCompiler(object):
  1259. def __init__(self, strings, charset, parent):
  1260. assert charset[0] == ".notdef"
  1261. isCID = hasattr(parent.dictObj, "ROS")
  1262. data0 = packCharset0(charset, isCID, strings)
  1263. data = packCharset(charset, isCID, strings)
  1264. if len(data) < len(data0):
  1265. self.data = data
  1266. else:
  1267. self.data = data0
  1268. self.parent = parent
  1269. def setPos(self, pos, endPos):
  1270. self.parent.rawDict["charset"] = pos
  1271. def getDataLength(self):
  1272. return len(self.data)
  1273. def toFile(self, file):
  1274. file.write(self.data)
  1275. def getStdCharSet(charset):
  1276. # check to see if we can use a predefined charset value.
  1277. predefinedCharSetVal = None
  1278. predefinedCharSets = [
  1279. (cffISOAdobeStringCount, cffISOAdobeStrings, 0),
  1280. (cffExpertStringCount, cffIExpertStrings, 1),
  1281. (cffExpertSubsetStringCount, cffExpertSubsetStrings, 2),
  1282. ]
  1283. lcs = len(charset)
  1284. for cnt, pcs, csv in predefinedCharSets:
  1285. if predefinedCharSetVal is not None:
  1286. break
  1287. if lcs > cnt:
  1288. continue
  1289. predefinedCharSetVal = csv
  1290. for i in range(lcs):
  1291. if charset[i] != pcs[i]:
  1292. predefinedCharSetVal = None
  1293. break
  1294. return predefinedCharSetVal
  1295. def getCIDfromName(name, strings):
  1296. return int(name[3:])
  1297. def getSIDfromName(name, strings):
  1298. return strings.getSID(name)
  1299. def packCharset0(charset, isCID, strings):
  1300. fmt = 0
  1301. data = [packCard8(fmt)]
  1302. if isCID:
  1303. getNameID = getCIDfromName
  1304. else:
  1305. getNameID = getSIDfromName
  1306. for name in charset[1:]:
  1307. data.append(packCard16(getNameID(name, strings)))
  1308. return bytesjoin(data)
  1309. def packCharset(charset, isCID, strings):
  1310. fmt = 1
  1311. ranges = []
  1312. first = None
  1313. end = 0
  1314. if isCID:
  1315. getNameID = getCIDfromName
  1316. else:
  1317. getNameID = getSIDfromName
  1318. for name in charset[1:]:
  1319. SID = getNameID(name, strings)
  1320. if first is None:
  1321. first = SID
  1322. elif end + 1 != SID:
  1323. nLeft = end - first
  1324. if nLeft > 255:
  1325. fmt = 2
  1326. ranges.append((first, nLeft))
  1327. first = SID
  1328. end = SID
  1329. if end:
  1330. nLeft = end - first
  1331. if nLeft > 255:
  1332. fmt = 2
  1333. ranges.append((first, nLeft))
  1334. data = [packCard8(fmt)]
  1335. if fmt == 1:
  1336. nLeftFunc = packCard8
  1337. else:
  1338. nLeftFunc = packCard16
  1339. for first, nLeft in ranges:
  1340. data.append(packCard16(first) + nLeftFunc(nLeft))
  1341. return bytesjoin(data)
  1342. def parseCharset0(numGlyphs, file, strings, isCID):
  1343. charset = [".notdef"]
  1344. if isCID:
  1345. for i in range(numGlyphs - 1):
  1346. CID = readCard16(file)
  1347. charset.append("cid" + str(CID).zfill(5))
  1348. else:
  1349. for i in range(numGlyphs - 1):
  1350. SID = readCard16(file)
  1351. charset.append(strings[SID])
  1352. return charset
  1353. def parseCharset(numGlyphs, file, strings, isCID, fmt):
  1354. charset = [".notdef"]
  1355. count = 1
  1356. if fmt == 1:
  1357. nLeftFunc = readCard8
  1358. else:
  1359. nLeftFunc = readCard16
  1360. while count < numGlyphs:
  1361. first = readCard16(file)
  1362. nLeft = nLeftFunc(file)
  1363. if isCID:
  1364. for CID in range(first, first + nLeft + 1):
  1365. charset.append("cid" + str(CID).zfill(5))
  1366. else:
  1367. for SID in range(first, first + nLeft + 1):
  1368. charset.append(strings[SID])
  1369. count = count + nLeft + 1
  1370. return charset
  1371. class EncodingCompiler(object):
  1372. def __init__(self, strings, encoding, parent):
  1373. assert not isinstance(encoding, str)
  1374. data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
  1375. data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
  1376. if len(data0) < len(data1):
  1377. self.data = data0
  1378. else:
  1379. self.data = data1
  1380. self.parent = parent
  1381. def setPos(self, pos, endPos):
  1382. self.parent.rawDict["Encoding"] = pos
  1383. def getDataLength(self):
  1384. return len(self.data)
  1385. def toFile(self, file):
  1386. file.write(self.data)
  1387. class EncodingConverter(SimpleConverter):
  1388. def _read(self, parent, value):
  1389. if value == 0:
  1390. return "StandardEncoding"
  1391. elif value == 1:
  1392. return "ExpertEncoding"
  1393. else:
  1394. assert value > 1
  1395. file = parent.file
  1396. file.seek(value)
  1397. log.log(DEBUG, "loading Encoding at %s", value)
  1398. fmt = readCard8(file)
  1399. haveSupplement = fmt & 0x80
  1400. if haveSupplement:
  1401. raise NotImplementedError("Encoding supplements are not yet supported")
  1402. fmt = fmt & 0x7F
  1403. if fmt == 0:
  1404. encoding = parseEncoding0(
  1405. parent.charset, file, haveSupplement, parent.strings
  1406. )
  1407. elif fmt == 1:
  1408. encoding = parseEncoding1(
  1409. parent.charset, file, haveSupplement, parent.strings
  1410. )
  1411. return encoding
  1412. def write(self, parent, value):
  1413. if value == "StandardEncoding":
  1414. return 0
  1415. elif value == "ExpertEncoding":
  1416. return 1
  1417. return 0 # dummy value
  1418. def xmlWrite(self, xmlWriter, name, value):
  1419. if value in ("StandardEncoding", "ExpertEncoding"):
  1420. xmlWriter.simpletag(name, name=value)
  1421. xmlWriter.newline()
  1422. return
  1423. xmlWriter.begintag(name)
  1424. xmlWriter.newline()
  1425. for code in range(len(value)):
  1426. glyphName = value[code]
  1427. if glyphName != ".notdef":
  1428. xmlWriter.simpletag("map", code=hex(code), name=glyphName)
  1429. xmlWriter.newline()
  1430. xmlWriter.endtag(name)
  1431. xmlWriter.newline()
  1432. def xmlRead(self, name, attrs, content, parent):
  1433. if "name" in attrs:
  1434. return attrs["name"]
  1435. encoding = [".notdef"] * 256
  1436. for element in content:
  1437. if isinstance(element, str):
  1438. continue
  1439. name, attrs, content = element
  1440. code = safeEval(attrs["code"])
  1441. glyphName = attrs["name"]
  1442. encoding[code] = glyphName
  1443. return encoding
  1444. def parseEncoding0(charset, file, haveSupplement, strings):
  1445. nCodes = readCard8(file)
  1446. encoding = [".notdef"] * 256
  1447. for glyphID in range(1, nCodes + 1):
  1448. code = readCard8(file)
  1449. if code != 0:
  1450. encoding[code] = charset[glyphID]
  1451. return encoding
  1452. def parseEncoding1(charset, file, haveSupplement, strings):
  1453. nRanges = readCard8(file)
  1454. encoding = [".notdef"] * 256
  1455. glyphID = 1
  1456. for i in range(nRanges):
  1457. code = readCard8(file)
  1458. nLeft = readCard8(file)
  1459. for glyphID in range(glyphID, glyphID + nLeft + 1):
  1460. encoding[code] = charset[glyphID]
  1461. code = code + 1
  1462. glyphID = glyphID + 1
  1463. return encoding
  1464. def packEncoding0(charset, encoding, strings):
  1465. fmt = 0
  1466. m = {}
  1467. for code in range(len(encoding)):
  1468. name = encoding[code]
  1469. if name != ".notdef":
  1470. m[name] = code
  1471. codes = []
  1472. for name in charset[1:]:
  1473. code = m.get(name)
  1474. codes.append(code)
  1475. while codes and codes[-1] is None:
  1476. codes.pop()
  1477. data = [packCard8(fmt), packCard8(len(codes))]
  1478. for code in codes:
  1479. if code is None:
  1480. code = 0
  1481. data.append(packCard8(code))
  1482. return bytesjoin(data)
  1483. def packEncoding1(charset, encoding, strings):
  1484. fmt = 1
  1485. m = {}
  1486. for code in range(len(encoding)):
  1487. name = encoding[code]
  1488. if name != ".notdef":
  1489. m[name] = code
  1490. ranges = []
  1491. first = None
  1492. end = 0
  1493. for name in charset[1:]:
  1494. code = m.get(name, -1)
  1495. if first is None:
  1496. first = code
  1497. elif end + 1 != code:
  1498. nLeft = end - first
  1499. ranges.append((first, nLeft))
  1500. first = code
  1501. end = code
  1502. nLeft = end - first
  1503. ranges.append((first, nLeft))
  1504. # remove unencoded glyphs at the end.
  1505. while ranges and ranges[-1][0] == -1:
  1506. ranges.pop()
  1507. data = [packCard8(fmt), packCard8(len(ranges))]
  1508. for first, nLeft in ranges:
  1509. if first == -1: # unencoded
  1510. first = 0
  1511. data.append(packCard8(first) + packCard8(nLeft))
  1512. return bytesjoin(data)
  1513. class FDArrayConverter(TableConverter):
  1514. def _read(self, parent, value):
  1515. try:
  1516. vstore = parent.VarStore
  1517. except AttributeError:
  1518. vstore = None
  1519. file = parent.file
  1520. isCFF2 = parent._isCFF2
  1521. file.seek(value)
  1522. fdArray = FDArrayIndex(file, isCFF2=isCFF2)
  1523. fdArray.vstore = vstore
  1524. fdArray.strings = parent.strings
  1525. fdArray.GlobalSubrs = parent.GlobalSubrs
  1526. return fdArray
  1527. def write(self, parent, value):
  1528. return 0 # dummy value
  1529. def xmlRead(self, name, attrs, content, parent):
  1530. fdArray = FDArrayIndex()
  1531. for element in content:
  1532. if isinstance(element, str):
  1533. continue
  1534. name, attrs, content = element
  1535. fdArray.fromXML(name, attrs, content)
  1536. return fdArray
  1537. class FDSelectConverter(SimpleConverter):
  1538. def _read(self, parent, value):
  1539. file = parent.file
  1540. file.seek(value)
  1541. fdSelect = FDSelect(file, parent.numGlyphs)
  1542. return fdSelect
  1543. def write(self, parent, value):
  1544. return 0 # dummy value
  1545. # The FDSelect glyph data is written out to XML in the charstring keys,
  1546. # so we write out only the format selector
  1547. def xmlWrite(self, xmlWriter, name, value):
  1548. xmlWriter.simpletag(name, [("format", value.format)])
  1549. xmlWriter.newline()
  1550. def xmlRead(self, name, attrs, content, parent):
  1551. fmt = safeEval(attrs["format"])
  1552. file = None
  1553. numGlyphs = None
  1554. fdSelect = FDSelect(file, numGlyphs, fmt)
  1555. return fdSelect
  1556. class VarStoreConverter(SimpleConverter):
  1557. def _read(self, parent, value):
  1558. file = parent.file
  1559. file.seek(value)
  1560. varStore = VarStoreData(file)
  1561. varStore.decompile()
  1562. return varStore
  1563. def write(self, parent, value):
  1564. return 0 # dummy value
  1565. def xmlWrite(self, xmlWriter, name, value):
  1566. value.writeXML(xmlWriter, name)
  1567. def xmlRead(self, name, attrs, content, parent):
  1568. varStore = VarStoreData()
  1569. varStore.xmlRead(name, attrs, content, parent)
  1570. return varStore
  1571. def packFDSelect0(fdSelectArray):
  1572. fmt = 0
  1573. data = [packCard8(fmt)]
  1574. for index in fdSelectArray:
  1575. data.append(packCard8(index))
  1576. return bytesjoin(data)
  1577. def packFDSelect3(fdSelectArray):
  1578. fmt = 3
  1579. fdRanges = []
  1580. lenArray = len(fdSelectArray)
  1581. lastFDIndex = -1
  1582. for i in range(lenArray):
  1583. fdIndex = fdSelectArray[i]
  1584. if lastFDIndex != fdIndex:
  1585. fdRanges.append([i, fdIndex])
  1586. lastFDIndex = fdIndex
  1587. sentinelGID = i + 1
  1588. data = [packCard8(fmt)]
  1589. data.append(packCard16(len(fdRanges)))
  1590. for fdRange in fdRanges:
  1591. data.append(packCard16(fdRange[0]))
  1592. data.append(packCard8(fdRange[1]))
  1593. data.append(packCard16(sentinelGID))
  1594. return bytesjoin(data)
  1595. def packFDSelect4(fdSelectArray):
  1596. fmt = 4
  1597. fdRanges = []
  1598. lenArray = len(fdSelectArray)
  1599. lastFDIndex = -1
  1600. for i in range(lenArray):
  1601. fdIndex = fdSelectArray[i]
  1602. if lastFDIndex != fdIndex:
  1603. fdRanges.append([i, fdIndex])
  1604. lastFDIndex = fdIndex
  1605. sentinelGID = i + 1
  1606. data = [packCard8(fmt)]
  1607. data.append(packCard32(len(fdRanges)))
  1608. for fdRange in fdRanges:
  1609. data.append(packCard32(fdRange[0]))
  1610. data.append(packCard16(fdRange[1]))
  1611. data.append(packCard32(sentinelGID))
  1612. return bytesjoin(data)
  1613. class FDSelectCompiler(object):
  1614. def __init__(self, fdSelect, parent):
  1615. fmt = fdSelect.format
  1616. fdSelectArray = fdSelect.gidArray
  1617. if fmt == 0:
  1618. self.data = packFDSelect0(fdSelectArray)
  1619. elif fmt == 3:
  1620. self.data = packFDSelect3(fdSelectArray)
  1621. elif fmt == 4:
  1622. self.data = packFDSelect4(fdSelectArray)
  1623. else:
  1624. # choose smaller of the two formats
  1625. data0 = packFDSelect0(fdSelectArray)
  1626. data3 = packFDSelect3(fdSelectArray)
  1627. if len(data0) < len(data3):
  1628. self.data = data0
  1629. fdSelect.format = 0
  1630. else:
  1631. self.data = data3
  1632. fdSelect.format = 3
  1633. self.parent = parent
  1634. def setPos(self, pos, endPos):
  1635. self.parent.rawDict["FDSelect"] = pos
  1636. def getDataLength(self):
  1637. return len(self.data)
  1638. def toFile(self, file):
  1639. file.write(self.data)
  1640. class VarStoreCompiler(object):
  1641. def __init__(self, varStoreData, parent):
  1642. self.parent = parent
  1643. if not varStoreData.data:
  1644. varStoreData.compile()
  1645. data = [packCard16(len(varStoreData.data)), varStoreData.data]
  1646. self.data = bytesjoin(data)
  1647. def setPos(self, pos, endPos):
  1648. self.parent.rawDict["VarStore"] = pos
  1649. def getDataLength(self):
  1650. return len(self.data)
  1651. def toFile(self, file):
  1652. file.write(self.data)
  1653. class ROSConverter(SimpleConverter):
  1654. def xmlWrite(self, xmlWriter, name, value):
  1655. registry, order, supplement = value
  1656. xmlWriter.simpletag(
  1657. name,
  1658. [
  1659. ("Registry", tostr(registry)),
  1660. ("Order", tostr(order)),
  1661. ("Supplement", supplement),
  1662. ],
  1663. )
  1664. xmlWriter.newline()
  1665. def xmlRead(self, name, attrs, content, parent):
  1666. return (attrs["Registry"], attrs["Order"], safeEval(attrs["Supplement"]))
  1667. topDictOperators = [
  1668. # opcode name argument type default converter
  1669. (25, "maxstack", "number", None, None),
  1670. ((12, 30), "ROS", ("SID", "SID", "number"), None, ROSConverter()),
  1671. ((12, 20), "SyntheticBase", "number", None, None),
  1672. (0, "version", "SID", None, None),
  1673. (1, "Notice", "SID", None, Latin1Converter()),
  1674. ((12, 0), "Copyright", "SID", None, Latin1Converter()),
  1675. (2, "FullName", "SID", None, Latin1Converter()),
  1676. ((12, 38), "FontName", "SID", None, Latin1Converter()),
  1677. (3, "FamilyName", "SID", None, Latin1Converter()),
  1678. (4, "Weight", "SID", None, None),
  1679. ((12, 1), "isFixedPitch", "number", 0, None),
  1680. ((12, 2), "ItalicAngle", "number", 0, None),
  1681. ((12, 3), "UnderlinePosition", "number", -100, None),
  1682. ((12, 4), "UnderlineThickness", "number", 50, None),
  1683. ((12, 5), "PaintType", "number", 0, None),
  1684. ((12, 6), "CharstringType", "number", 2, None),
  1685. ((12, 7), "FontMatrix", "array", [0.001, 0, 0, 0.001, 0, 0], None),
  1686. (13, "UniqueID", "number", None, None),
  1687. (5, "FontBBox", "array", [0, 0, 0, 0], None),
  1688. ((12, 8), "StrokeWidth", "number", 0, None),
  1689. (14, "XUID", "array", None, None),
  1690. ((12, 21), "PostScript", "SID", None, None),
  1691. ((12, 22), "BaseFontName", "SID", None, None),
  1692. ((12, 23), "BaseFontBlend", "delta", None, None),
  1693. ((12, 31), "CIDFontVersion", "number", 0, None),
  1694. ((12, 32), "CIDFontRevision", "number", 0, None),
  1695. ((12, 33), "CIDFontType", "number", 0, None),
  1696. ((12, 34), "CIDCount", "number", 8720, None),
  1697. (15, "charset", "number", None, CharsetConverter()),
  1698. ((12, 35), "UIDBase", "number", None, None),
  1699. (16, "Encoding", "number", 0, EncodingConverter()),
  1700. (18, "Private", ("number", "number"), None, PrivateDictConverter()),
  1701. ((12, 37), "FDSelect", "number", None, FDSelectConverter()),
  1702. ((12, 36), "FDArray", "number", None, FDArrayConverter()),
  1703. (17, "CharStrings", "number", None, CharStringsConverter()),
  1704. (24, "VarStore", "number", None, VarStoreConverter()),
  1705. ]
  1706. topDictOperators2 = [
  1707. # opcode name argument type default converter
  1708. (25, "maxstack", "number", None, None),
  1709. ((12, 7), "FontMatrix", "array", [0.001, 0, 0, 0.001, 0, 0], None),
  1710. ((12, 37), "FDSelect", "number", None, FDSelectConverter()),
  1711. ((12, 36), "FDArray", "number", None, FDArrayConverter()),
  1712. (17, "CharStrings", "number", None, CharStringsConverter()),
  1713. (24, "VarStore", "number", None, VarStoreConverter()),
  1714. ]
  1715. # Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order,
  1716. # in order for the font to compile back from xml.
  1717. kBlendDictOpName = "blend"
  1718. blendOp = 23
  1719. privateDictOperators = [
  1720. # opcode name argument type default converter
  1721. (22, "vsindex", "number", None, None),
  1722. (
  1723. blendOp,
  1724. kBlendDictOpName,
  1725. "blendList",
  1726. None,
  1727. None,
  1728. ), # This is for reading to/from XML: it not written to CFF.
  1729. (6, "BlueValues", "delta", None, None),
  1730. (7, "OtherBlues", "delta", None, None),
  1731. (8, "FamilyBlues", "delta", None, None),
  1732. (9, "FamilyOtherBlues", "delta", None, None),
  1733. ((12, 9), "BlueScale", "number", 0.039625, None),
  1734. ((12, 10), "BlueShift", "number", 7, None),
  1735. ((12, 11), "BlueFuzz", "number", 1, None),
  1736. (10, "StdHW", "number", None, None),
  1737. (11, "StdVW", "number", None, None),
  1738. ((12, 12), "StemSnapH", "delta", None, None),
  1739. ((12, 13), "StemSnapV", "delta", None, None),
  1740. ((12, 14), "ForceBold", "number", 0, None),
  1741. ((12, 15), "ForceBoldThreshold", "number", None, None), # deprecated
  1742. ((12, 16), "lenIV", "number", None, None), # deprecated
  1743. ((12, 17), "LanguageGroup", "number", 0, None),
  1744. ((12, 18), "ExpansionFactor", "number", 0.06, None),
  1745. ((12, 19), "initialRandomSeed", "number", 0, None),
  1746. (20, "defaultWidthX", "number", 0, None),
  1747. (21, "nominalWidthX", "number", 0, None),
  1748. (19, "Subrs", "number", None, SubrsConverter()),
  1749. ]
  1750. privateDictOperators2 = [
  1751. # opcode name argument type default converter
  1752. (22, "vsindex", "number", None, None),
  1753. (
  1754. blendOp,
  1755. kBlendDictOpName,
  1756. "blendList",
  1757. None,
  1758. None,
  1759. ), # This is for reading to/from XML: it not written to CFF.
  1760. (6, "BlueValues", "delta", None, None),
  1761. (7, "OtherBlues", "delta", None, None),
  1762. (8, "FamilyBlues", "delta", None, None),
  1763. (9, "FamilyOtherBlues", "delta", None, None),
  1764. ((12, 9), "BlueScale", "number", 0.039625, None),
  1765. ((12, 10), "BlueShift", "number", 7, None),
  1766. ((12, 11), "BlueFuzz", "number", 1, None),
  1767. (10, "StdHW", "number", None, None),
  1768. (11, "StdVW", "number", None, None),
  1769. ((12, 12), "StemSnapH", "delta", None, None),
  1770. ((12, 13), "StemSnapV", "delta", None, None),
  1771. ((12, 17), "LanguageGroup", "number", 0, None),
  1772. ((12, 18), "ExpansionFactor", "number", 0.06, None),
  1773. (19, "Subrs", "number", None, SubrsConverter()),
  1774. ]
  1775. def addConverters(table):
  1776. for i in range(len(table)):
  1777. op, name, arg, default, conv = table[i]
  1778. if conv is not None:
  1779. continue
  1780. if arg in ("delta", "array"):
  1781. conv = ArrayConverter()
  1782. elif arg == "number":
  1783. conv = NumberConverter()
  1784. elif arg == "SID":
  1785. conv = ASCIIConverter()
  1786. elif arg == "blendList":
  1787. conv = None
  1788. else:
  1789. assert False
  1790. table[i] = op, name, arg, default, conv
  1791. addConverters(privateDictOperators)
  1792. addConverters(topDictOperators)
  1793. class TopDictDecompiler(psCharStrings.DictDecompiler):
  1794. operators = buildOperatorDict(topDictOperators)
  1795. class PrivateDictDecompiler(psCharStrings.DictDecompiler):
  1796. operators = buildOperatorDict(privateDictOperators)
  1797. class DictCompiler(object):
  1798. maxBlendStack = 0
  1799. def __init__(self, dictObj, strings, parent, isCFF2=None):
  1800. if strings:
  1801. assert isinstance(strings, IndexedStrings)
  1802. if isCFF2 is None and hasattr(parent, "isCFF2"):
  1803. isCFF2 = parent.isCFF2
  1804. assert isCFF2 is not None
  1805. self.isCFF2 = isCFF2
  1806. self.dictObj = dictObj
  1807. self.strings = strings
  1808. self.parent = parent
  1809. rawDict = {}
  1810. for name in dictObj.order:
  1811. value = getattr(dictObj, name, None)
  1812. if value is None:
  1813. continue
  1814. conv = dictObj.converters[name]
  1815. value = conv.write(dictObj, value)
  1816. if value == dictObj.defaults.get(name):
  1817. continue
  1818. rawDict[name] = value
  1819. self.rawDict = rawDict
  1820. def setPos(self, pos, endPos):
  1821. pass
  1822. def getDataLength(self):
  1823. return len(self.compile("getDataLength"))
  1824. def compile(self, reason):
  1825. log.log(DEBUG, "-- compiling %s for %s", self.__class__.__name__, reason)
  1826. rawDict = self.rawDict
  1827. data = []
  1828. for name in self.dictObj.order:
  1829. value = rawDict.get(name)
  1830. if value is None:
  1831. continue
  1832. op, argType = self.opcodes[name]
  1833. if isinstance(argType, tuple):
  1834. l = len(argType)
  1835. assert len(value) == l, "value doesn't match arg type"
  1836. for i in range(l):
  1837. arg = argType[i]
  1838. v = value[i]
  1839. arghandler = getattr(self, "arg_" + arg)
  1840. data.append(arghandler(v))
  1841. else:
  1842. arghandler = getattr(self, "arg_" + argType)
  1843. data.append(arghandler(value))
  1844. data.append(op)
  1845. data = bytesjoin(data)
  1846. return data
  1847. def toFile(self, file):
  1848. data = self.compile("toFile")
  1849. file.write(data)
  1850. def arg_number(self, num):
  1851. if isinstance(num, list):
  1852. data = [encodeNumber(val) for val in num]
  1853. data.append(encodeNumber(1))
  1854. data.append(bytechr(blendOp))
  1855. datum = bytesjoin(data)
  1856. else:
  1857. datum = encodeNumber(num)
  1858. return datum
  1859. def arg_SID(self, s):
  1860. return psCharStrings.encodeIntCFF(self.strings.getSID(s))
  1861. def arg_array(self, value):
  1862. data = []
  1863. for num in value:
  1864. data.append(self.arg_number(num))
  1865. return bytesjoin(data)
  1866. def arg_delta(self, value):
  1867. if not value:
  1868. return b""
  1869. val0 = value[0]
  1870. if isinstance(val0, list):
  1871. data = self.arg_delta_blend(value)
  1872. else:
  1873. out = []
  1874. last = 0
  1875. for v in value:
  1876. out.append(v - last)
  1877. last = v
  1878. data = []
  1879. for num in out:
  1880. data.append(encodeNumber(num))
  1881. return bytesjoin(data)
  1882. def arg_delta_blend(self, value):
  1883. """A delta list with blend lists has to be *all* blend lists.
  1884. The value is a list is arranged as follows::
  1885. [
  1886. [V0, d0..dn]
  1887. [V1, d0..dn]
  1888. ...
  1889. [Vm, d0..dn]
  1890. ]
  1891. ``V`` is the absolute coordinate value from the default font, and ``d0-dn``
  1892. are the delta values from the *n* regions. Each ``V`` is an absolute
  1893. coordinate from the default font.
  1894. We want to return a list::
  1895. [
  1896. [v0, v1..vm]
  1897. [d0..dn]
  1898. ...
  1899. [d0..dn]
  1900. numBlends
  1901. blendOp
  1902. ]
  1903. where each ``v`` is relative to the previous default font value.
  1904. """
  1905. numMasters = len(value[0])
  1906. numBlends = len(value)
  1907. numStack = (numBlends * numMasters) + 1
  1908. if numStack > self.maxBlendStack:
  1909. # Figure out the max number of value we can blend
  1910. # and divide this list up into chunks of that size.
  1911. numBlendValues = int((self.maxBlendStack - 1) / numMasters)
  1912. out = []
  1913. while True:
  1914. numVal = min(len(value), numBlendValues)
  1915. if numVal == 0:
  1916. break
  1917. valList = value[0:numVal]
  1918. out1 = self.arg_delta_blend(valList)
  1919. out.extend(out1)
  1920. value = value[numVal:]
  1921. else:
  1922. firstList = [0] * numBlends
  1923. deltaList = [None] * numBlends
  1924. i = 0
  1925. prevVal = 0
  1926. while i < numBlends:
  1927. # For PrivateDict BlueValues, the default font
  1928. # values are absolute, not relative.
  1929. # Must convert these back to relative coordinates
  1930. # befor writing to CFF2.
  1931. defaultValue = value[i][0]
  1932. firstList[i] = defaultValue - prevVal
  1933. prevVal = defaultValue
  1934. deltaList[i] = value[i][1:]
  1935. i += 1
  1936. relValueList = firstList
  1937. for blendList in deltaList:
  1938. relValueList.extend(blendList)
  1939. out = [encodeNumber(val) for val in relValueList]
  1940. out.append(encodeNumber(numBlends))
  1941. out.append(bytechr(blendOp))
  1942. return out
  1943. def encodeNumber(num):
  1944. if isinstance(num, float):
  1945. return psCharStrings.encodeFloat(num)
  1946. else:
  1947. return psCharStrings.encodeIntCFF(num)
  1948. class TopDictCompiler(DictCompiler):
  1949. opcodes = buildOpcodeDict(topDictOperators)
  1950. def getChildren(self, strings):
  1951. isCFF2 = self.isCFF2
  1952. children = []
  1953. if self.dictObj.cff2GetGlyphOrder is None:
  1954. if hasattr(self.dictObj, "charset") and self.dictObj.charset:
  1955. if hasattr(self.dictObj, "ROS"): # aka isCID
  1956. charsetCode = None
  1957. else:
  1958. charsetCode = getStdCharSet(self.dictObj.charset)
  1959. if charsetCode is None:
  1960. children.append(
  1961. CharsetCompiler(strings, self.dictObj.charset, self)
  1962. )
  1963. else:
  1964. self.rawDict["charset"] = charsetCode
  1965. if hasattr(self.dictObj, "Encoding") and self.dictObj.Encoding:
  1966. encoding = self.dictObj.Encoding
  1967. if not isinstance(encoding, str):
  1968. children.append(EncodingCompiler(strings, encoding, self))
  1969. else:
  1970. if hasattr(self.dictObj, "VarStore"):
  1971. varStoreData = self.dictObj.VarStore
  1972. varStoreComp = VarStoreCompiler(varStoreData, self)
  1973. children.append(varStoreComp)
  1974. if hasattr(self.dictObj, "FDSelect"):
  1975. # I have not yet supported merging a ttx CFF-CID font, as there are
  1976. # interesting issues about merging the FDArrays. Here I assume that
  1977. # either the font was read from XML, and the FDSelect indices are all
  1978. # in the charstring data, or the FDSelect array is already fully defined.
  1979. fdSelect = self.dictObj.FDSelect
  1980. # probably read in from XML; assume fdIndex in CharString data
  1981. if len(fdSelect) == 0:
  1982. charStrings = self.dictObj.CharStrings
  1983. for name in self.dictObj.charset:
  1984. fdSelect.append(charStrings[name].fdSelectIndex)
  1985. fdSelectComp = FDSelectCompiler(fdSelect, self)
  1986. children.append(fdSelectComp)
  1987. if hasattr(self.dictObj, "CharStrings"):
  1988. items = []
  1989. charStrings = self.dictObj.CharStrings
  1990. for name in self.dictObj.charset:
  1991. items.append(charStrings[name])
  1992. charStringsComp = CharStringsCompiler(items, strings, self, isCFF2=isCFF2)
  1993. children.append(charStringsComp)
  1994. if hasattr(self.dictObj, "FDArray"):
  1995. # I have not yet supported merging a ttx CFF-CID font, as there are
  1996. # interesting issues about merging the FDArrays. Here I assume that the
  1997. # FDArray info is correct and complete.
  1998. fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self)
  1999. children.append(fdArrayIndexComp)
  2000. children.extend(fdArrayIndexComp.getChildren(strings))
  2001. if hasattr(self.dictObj, "Private"):
  2002. privComp = self.dictObj.Private.getCompiler(strings, self)
  2003. children.append(privComp)
  2004. children.extend(privComp.getChildren(strings))
  2005. return children
  2006. class FontDictCompiler(DictCompiler):
  2007. opcodes = buildOpcodeDict(topDictOperators)
  2008. def __init__(self, dictObj, strings, parent, isCFF2=None):
  2009. super(FontDictCompiler, self).__init__(dictObj, strings, parent, isCFF2=isCFF2)
  2010. #
  2011. # We now take some effort to detect if there were any key/value pairs
  2012. # supplied that were ignored in the FontDict context, and issue a warning
  2013. # for those cases.
  2014. #
  2015. ignoredNames = []
  2016. dictObj = self.dictObj
  2017. for name in sorted(set(dictObj.converters) - set(dictObj.order)):
  2018. if name in dictObj.rawDict:
  2019. # The font was directly read from binary. In this
  2020. # case, we want to report *all* "useless" key/value
  2021. # pairs that are in the font, not just the ones that
  2022. # are different from the default.
  2023. ignoredNames.append(name)
  2024. else:
  2025. # The font was probably read from a TTX file. We only
  2026. # warn about keys whos value is not the default. The
  2027. # ones that have the default value will not be written
  2028. # to binary anyway.
  2029. default = dictObj.defaults.get(name)
  2030. if default is not None:
  2031. conv = dictObj.converters[name]
  2032. default = conv.read(dictObj, default)
  2033. if getattr(dictObj, name, None) != default:
  2034. ignoredNames.append(name)
  2035. if ignoredNames:
  2036. log.warning(
  2037. "Some CFF FDArray/FontDict keys were ignored upon compile: "
  2038. + " ".join(sorted(ignoredNames))
  2039. )
  2040. def getChildren(self, strings):
  2041. children = []
  2042. if hasattr(self.dictObj, "Private"):
  2043. privComp = self.dictObj.Private.getCompiler(strings, self)
  2044. children.append(privComp)
  2045. children.extend(privComp.getChildren(strings))
  2046. return children
  2047. class PrivateDictCompiler(DictCompiler):
  2048. maxBlendStack = maxStackLimit
  2049. opcodes = buildOpcodeDict(privateDictOperators)
  2050. def setPos(self, pos, endPos):
  2051. size = endPos - pos
  2052. self.parent.rawDict["Private"] = size, pos
  2053. self.pos = pos
  2054. def getChildren(self, strings):
  2055. children = []
  2056. if hasattr(self.dictObj, "Subrs"):
  2057. children.append(self.dictObj.Subrs.getCompiler(strings, self))
  2058. return children
  2059. class BaseDict(object):
  2060. def __init__(self, strings=None, file=None, offset=None, isCFF2=None):
  2061. assert (isCFF2 is None) == (file is None)
  2062. self.rawDict = {}
  2063. self.skipNames = []
  2064. self.strings = strings
  2065. if file is None:
  2066. return
  2067. self._isCFF2 = isCFF2
  2068. self.file = file
  2069. if offset is not None:
  2070. log.log(DEBUG, "loading %s at %s", self.__class__.__name__, offset)
  2071. self.offset = offset
  2072. def decompile(self, data):
  2073. log.log(DEBUG, " length %s is %d", self.__class__.__name__, len(data))
  2074. dec = self.decompilerClass(self.strings, self)
  2075. dec.decompile(data)
  2076. self.rawDict = dec.getDict()
  2077. self.postDecompile()
  2078. def postDecompile(self):
  2079. pass
  2080. def getCompiler(self, strings, parent, isCFF2=None):
  2081. return self.compilerClass(self, strings, parent, isCFF2=isCFF2)
  2082. def __getattr__(self, name):
  2083. if name[:2] == name[-2:] == "__":
  2084. # to make deepcopy() and pickle.load() work, we need to signal with
  2085. # AttributeError that dunder methods like '__deepcopy__' or '__getstate__'
  2086. # aren't implemented. For more details, see:
  2087. # https://github.com/fonttools/fonttools/pull/1488
  2088. raise AttributeError(name)
  2089. value = self.rawDict.get(name, None)
  2090. if value is None:
  2091. value = self.defaults.get(name)
  2092. if value is None:
  2093. raise AttributeError(name)
  2094. conv = self.converters[name]
  2095. value = conv.read(self, value)
  2096. setattr(self, name, value)
  2097. return value
  2098. def toXML(self, xmlWriter):
  2099. for name in self.order:
  2100. if name in self.skipNames:
  2101. continue
  2102. value = getattr(self, name, None)
  2103. # XXX For "charset" we never skip calling xmlWrite even if the
  2104. # value is None, so we always write the following XML comment:
  2105. #
  2106. # <!-- charset is dumped separately as the 'GlyphOrder' element -->
  2107. #
  2108. # Charset is None when 'CFF ' table is imported from XML into an
  2109. # empty TTFont(). By writing this comment all the time, we obtain
  2110. # the same XML output whether roundtripping XML-to-XML or
  2111. # dumping binary-to-XML
  2112. if value is None and name != "charset":
  2113. continue
  2114. conv = self.converters[name]
  2115. conv.xmlWrite(xmlWriter, name, value)
  2116. ignoredNames = set(self.rawDict) - set(self.order)
  2117. if ignoredNames:
  2118. xmlWriter.comment(
  2119. "some keys were ignored: %s" % " ".join(sorted(ignoredNames))
  2120. )
  2121. xmlWriter.newline()
  2122. def fromXML(self, name, attrs, content):
  2123. conv = self.converters[name]
  2124. value = conv.xmlRead(name, attrs, content, self)
  2125. setattr(self, name, value)
  2126. class TopDict(BaseDict):
  2127. """The ``TopDict`` represents the top-level dictionary holding font
  2128. information. CFF2 tables contain a restricted set of top-level entries
  2129. as described `here <https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#7-top-dict-data>`_,
  2130. but CFF tables may contain a wider range of information. This information
  2131. can be accessed through attributes or through the dictionary returned
  2132. through the ``rawDict`` property:
  2133. .. code:: python
  2134. font = tt["CFF "].cff[0]
  2135. font.FamilyName
  2136. # 'Linux Libertine O'
  2137. font.rawDict["FamilyName"]
  2138. # 'Linux Libertine O'
  2139. More information is available in the CFF file's private dictionary, accessed
  2140. via the ``Private`` property:
  2141. .. code:: python
  2142. tt["CFF "].cff[0].Private.BlueValues
  2143. # [-15, 0, 515, 515, 666, 666]
  2144. """
  2145. defaults = buildDefaults(topDictOperators)
  2146. converters = buildConverters(topDictOperators)
  2147. compilerClass = TopDictCompiler
  2148. order = buildOrder(topDictOperators)
  2149. decompilerClass = TopDictDecompiler
  2150. def __init__(
  2151. self,
  2152. strings=None,
  2153. file=None,
  2154. offset=None,
  2155. GlobalSubrs=None,
  2156. cff2GetGlyphOrder=None,
  2157. isCFF2=None,
  2158. ):
  2159. super(TopDict, self).__init__(strings, file, offset, isCFF2=isCFF2)
  2160. self.cff2GetGlyphOrder = cff2GetGlyphOrder
  2161. self.GlobalSubrs = GlobalSubrs
  2162. if isCFF2:
  2163. self.defaults = buildDefaults(topDictOperators2)
  2164. self.charset = cff2GetGlyphOrder()
  2165. self.order = buildOrder(topDictOperators2)
  2166. else:
  2167. self.defaults = buildDefaults(topDictOperators)
  2168. self.order = buildOrder(topDictOperators)
  2169. def getGlyphOrder(self):
  2170. """Returns a list of glyph names in the CFF font."""
  2171. return self.charset
  2172. def postDecompile(self):
  2173. offset = self.rawDict.get("CharStrings")
  2174. if offset is None:
  2175. return
  2176. # get the number of glyphs beforehand.
  2177. self.file.seek(offset)
  2178. if self._isCFF2:
  2179. self.numGlyphs = readCard32(self.file)
  2180. else:
  2181. self.numGlyphs = readCard16(self.file)
  2182. def toXML(self, xmlWriter):
  2183. if hasattr(self, "CharStrings"):
  2184. self.decompileAllCharStrings()
  2185. if hasattr(self, "ROS"):
  2186. self.skipNames = ["Encoding"]
  2187. if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
  2188. # these values have default values, but I only want them to show up
  2189. # in CID fonts.
  2190. self.skipNames = [
  2191. "CIDFontVersion",
  2192. "CIDFontRevision",
  2193. "CIDFontType",
  2194. "CIDCount",
  2195. ]
  2196. BaseDict.toXML(self, xmlWriter)
  2197. def decompileAllCharStrings(self):
  2198. # Make sure that all the Private Dicts have been instantiated.
  2199. for i, charString in enumerate(self.CharStrings.values()):
  2200. try:
  2201. charString.decompile()
  2202. except:
  2203. log.error("Error in charstring %s", i)
  2204. raise
  2205. def recalcFontBBox(self):
  2206. fontBBox = None
  2207. for charString in self.CharStrings.values():
  2208. bounds = charString.calcBounds(self.CharStrings)
  2209. if bounds is not None:
  2210. if fontBBox is not None:
  2211. fontBBox = unionRect(fontBBox, bounds)
  2212. else:
  2213. fontBBox = bounds
  2214. if fontBBox is None:
  2215. self.FontBBox = self.defaults["FontBBox"][:]
  2216. else:
  2217. self.FontBBox = list(intRect(fontBBox))
  2218. class FontDict(BaseDict):
  2219. #
  2220. # Since fonttools used to pass a lot of fields that are not relevant in the FDArray
  2221. # FontDict, there are 'ttx' files in the wild that contain all these. These got in
  2222. # the ttx files because fonttools writes explicit values for all the TopDict default
  2223. # values. These are not actually illegal in the context of an FDArray FontDict - you
  2224. # can legally, per spec, put any arbitrary key/value pair in a FontDict - but are
  2225. # useless since current major company CFF interpreters ignore anything but the set
  2226. # listed in this file. So, we just silently skip them. An exception is Weight: this
  2227. # is not used by any interpreter, but some foundries have asked that this be
  2228. # supported in FDArray FontDicts just to preserve information about the design when
  2229. # the font is being inspected.
  2230. #
  2231. # On top of that, there are fonts out there that contain such useless FontDict values.
  2232. #
  2233. # By subclassing TopDict, we *allow* all key/values from TopDict, both when reading
  2234. # from binary or when reading from XML, but by overriding `order` with a limited
  2235. # list of names, we ensure that only the useful names ever get exported to XML and
  2236. # ever get compiled into the binary font.
  2237. #
  2238. # We override compilerClass so we can warn about "useless" key/value pairs, either
  2239. # from the original binary font or from TTX input.
  2240. #
  2241. # See:
  2242. # - https://github.com/fonttools/fonttools/issues/740
  2243. # - https://github.com/fonttools/fonttools/issues/601
  2244. # - https://github.com/adobe-type-tools/afdko/issues/137
  2245. #
  2246. defaults = {}
  2247. converters = buildConverters(topDictOperators)
  2248. compilerClass = FontDictCompiler
  2249. orderCFF = ["FontName", "FontMatrix", "Weight", "Private"]
  2250. orderCFF2 = ["Private"]
  2251. decompilerClass = TopDictDecompiler
  2252. def __init__(
  2253. self,
  2254. strings=None,
  2255. file=None,
  2256. offset=None,
  2257. GlobalSubrs=None,
  2258. isCFF2=None,
  2259. vstore=None,
  2260. ):
  2261. super(FontDict, self).__init__(strings, file, offset, isCFF2=isCFF2)
  2262. self.vstore = vstore
  2263. self.setCFF2(isCFF2)
  2264. def setCFF2(self, isCFF2):
  2265. # isCFF2 may be None.
  2266. if isCFF2:
  2267. self.order = self.orderCFF2
  2268. self._isCFF2 = True
  2269. else:
  2270. self.order = self.orderCFF
  2271. self._isCFF2 = False
  2272. class PrivateDict(BaseDict):
  2273. defaults = buildDefaults(privateDictOperators)
  2274. converters = buildConverters(privateDictOperators)
  2275. order = buildOrder(privateDictOperators)
  2276. decompilerClass = PrivateDictDecompiler
  2277. compilerClass = PrivateDictCompiler
  2278. def __init__(self, strings=None, file=None, offset=None, isCFF2=None, vstore=None):
  2279. super(PrivateDict, self).__init__(strings, file, offset, isCFF2=isCFF2)
  2280. self.vstore = vstore
  2281. if isCFF2:
  2282. self.defaults = buildDefaults(privateDictOperators2)
  2283. self.order = buildOrder(privateDictOperators2)
  2284. # Provide dummy values. This avoids needing to provide
  2285. # an isCFF2 state in a lot of places.
  2286. self.nominalWidthX = self.defaultWidthX = None
  2287. self._isCFF2 = True
  2288. else:
  2289. self.defaults = buildDefaults(privateDictOperators)
  2290. self.order = buildOrder(privateDictOperators)
  2291. self._isCFF2 = False
  2292. @property
  2293. def in_cff2(self):
  2294. return self._isCFF2
  2295. def getNumRegions(self, vi=None): # called from misc/psCharStrings.py
  2296. # if getNumRegions is being called, we can assume that VarStore exists.
  2297. if vi is None:
  2298. if hasattr(self, "vsindex"):
  2299. vi = self.vsindex
  2300. else:
  2301. vi = 0
  2302. numRegions = self.vstore.getNumRegions(vi)
  2303. return numRegions
  2304. class IndexedStrings(object):
  2305. """SID -> string mapping."""
  2306. def __init__(self, file=None):
  2307. if file is None:
  2308. strings = []
  2309. else:
  2310. strings = [tostr(s, encoding="latin1") for s in Index(file, isCFF2=False)]
  2311. self.strings = strings
  2312. def getCompiler(self):
  2313. return IndexedStringsCompiler(self, None, self, isCFF2=False)
  2314. def __len__(self):
  2315. return len(self.strings)
  2316. def __getitem__(self, SID):
  2317. if SID < cffStandardStringCount:
  2318. return cffStandardStrings[SID]
  2319. else:
  2320. return self.strings[SID - cffStandardStringCount]
  2321. def getSID(self, s):
  2322. if not hasattr(self, "stringMapping"):
  2323. self.buildStringMapping()
  2324. s = tostr(s, encoding="latin1")
  2325. if s in cffStandardStringMapping:
  2326. SID = cffStandardStringMapping[s]
  2327. elif s in self.stringMapping:
  2328. SID = self.stringMapping[s]
  2329. else:
  2330. SID = len(self.strings) + cffStandardStringCount
  2331. self.strings.append(s)
  2332. self.stringMapping[s] = SID
  2333. return SID
  2334. def getStrings(self):
  2335. return self.strings
  2336. def buildStringMapping(self):
  2337. self.stringMapping = {}
  2338. for index in range(len(self.strings)):
  2339. self.stringMapping[self.strings[index]] = index + cffStandardStringCount
  2340. # The 391 Standard Strings as used in the CFF format.
  2341. # from Adobe Technical None #5176, version 1.0, 18 March 1998
  2342. cffStandardStrings = [
  2343. ".notdef",
  2344. "space",
  2345. "exclam",
  2346. "quotedbl",
  2347. "numbersign",
  2348. "dollar",
  2349. "percent",
  2350. "ampersand",
  2351. "quoteright",
  2352. "parenleft",
  2353. "parenright",
  2354. "asterisk",
  2355. "plus",
  2356. "comma",
  2357. "hyphen",
  2358. "period",
  2359. "slash",
  2360. "zero",
  2361. "one",
  2362. "two",
  2363. "three",
  2364. "four",
  2365. "five",
  2366. "six",
  2367. "seven",
  2368. "eight",
  2369. "nine",
  2370. "colon",
  2371. "semicolon",
  2372. "less",
  2373. "equal",
  2374. "greater",
  2375. "question",
  2376. "at",
  2377. "A",
  2378. "B",
  2379. "C",
  2380. "D",
  2381. "E",
  2382. "F",
  2383. "G",
  2384. "H",
  2385. "I",
  2386. "J",
  2387. "K",
  2388. "L",
  2389. "M",
  2390. "N",
  2391. "O",
  2392. "P",
  2393. "Q",
  2394. "R",
  2395. "S",
  2396. "T",
  2397. "U",
  2398. "V",
  2399. "W",
  2400. "X",
  2401. "Y",
  2402. "Z",
  2403. "bracketleft",
  2404. "backslash",
  2405. "bracketright",
  2406. "asciicircum",
  2407. "underscore",
  2408. "quoteleft",
  2409. "a",
  2410. "b",
  2411. "c",
  2412. "d",
  2413. "e",
  2414. "f",
  2415. "g",
  2416. "h",
  2417. "i",
  2418. "j",
  2419. "k",
  2420. "l",
  2421. "m",
  2422. "n",
  2423. "o",
  2424. "p",
  2425. "q",
  2426. "r",
  2427. "s",
  2428. "t",
  2429. "u",
  2430. "v",
  2431. "w",
  2432. "x",
  2433. "y",
  2434. "z",
  2435. "braceleft",
  2436. "bar",
  2437. "braceright",
  2438. "asciitilde",
  2439. "exclamdown",
  2440. "cent",
  2441. "sterling",
  2442. "fraction",
  2443. "yen",
  2444. "florin",
  2445. "section",
  2446. "currency",
  2447. "quotesingle",
  2448. "quotedblleft",
  2449. "guillemotleft",
  2450. "guilsinglleft",
  2451. "guilsinglright",
  2452. "fi",
  2453. "fl",
  2454. "endash",
  2455. "dagger",
  2456. "daggerdbl",
  2457. "periodcentered",
  2458. "paragraph",
  2459. "bullet",
  2460. "quotesinglbase",
  2461. "quotedblbase",
  2462. "quotedblright",
  2463. "guillemotright",
  2464. "ellipsis",
  2465. "perthousand",
  2466. "questiondown",
  2467. "grave",
  2468. "acute",
  2469. "circumflex",
  2470. "tilde",
  2471. "macron",
  2472. "breve",
  2473. "dotaccent",
  2474. "dieresis",
  2475. "ring",
  2476. "cedilla",
  2477. "hungarumlaut",
  2478. "ogonek",
  2479. "caron",
  2480. "emdash",
  2481. "AE",
  2482. "ordfeminine",
  2483. "Lslash",
  2484. "Oslash",
  2485. "OE",
  2486. "ordmasculine",
  2487. "ae",
  2488. "dotlessi",
  2489. "lslash",
  2490. "oslash",
  2491. "oe",
  2492. "germandbls",
  2493. "onesuperior",
  2494. "logicalnot",
  2495. "mu",
  2496. "trademark",
  2497. "Eth",
  2498. "onehalf",
  2499. "plusminus",
  2500. "Thorn",
  2501. "onequarter",
  2502. "divide",
  2503. "brokenbar",
  2504. "degree",
  2505. "thorn",
  2506. "threequarters",
  2507. "twosuperior",
  2508. "registered",
  2509. "minus",
  2510. "eth",
  2511. "multiply",
  2512. "threesuperior",
  2513. "copyright",
  2514. "Aacute",
  2515. "Acircumflex",
  2516. "Adieresis",
  2517. "Agrave",
  2518. "Aring",
  2519. "Atilde",
  2520. "Ccedilla",
  2521. "Eacute",
  2522. "Ecircumflex",
  2523. "Edieresis",
  2524. "Egrave",
  2525. "Iacute",
  2526. "Icircumflex",
  2527. "Idieresis",
  2528. "Igrave",
  2529. "Ntilde",
  2530. "Oacute",
  2531. "Ocircumflex",
  2532. "Odieresis",
  2533. "Ograve",
  2534. "Otilde",
  2535. "Scaron",
  2536. "Uacute",
  2537. "Ucircumflex",
  2538. "Udieresis",
  2539. "Ugrave",
  2540. "Yacute",
  2541. "Ydieresis",
  2542. "Zcaron",
  2543. "aacute",
  2544. "acircumflex",
  2545. "adieresis",
  2546. "agrave",
  2547. "aring",
  2548. "atilde",
  2549. "ccedilla",
  2550. "eacute",
  2551. "ecircumflex",
  2552. "edieresis",
  2553. "egrave",
  2554. "iacute",
  2555. "icircumflex",
  2556. "idieresis",
  2557. "igrave",
  2558. "ntilde",
  2559. "oacute",
  2560. "ocircumflex",
  2561. "odieresis",
  2562. "ograve",
  2563. "otilde",
  2564. "scaron",
  2565. "uacute",
  2566. "ucircumflex",
  2567. "udieresis",
  2568. "ugrave",
  2569. "yacute",
  2570. "ydieresis",
  2571. "zcaron",
  2572. "exclamsmall",
  2573. "Hungarumlautsmall",
  2574. "dollaroldstyle",
  2575. "dollarsuperior",
  2576. "ampersandsmall",
  2577. "Acutesmall",
  2578. "parenleftsuperior",
  2579. "parenrightsuperior",
  2580. "twodotenleader",
  2581. "onedotenleader",
  2582. "zerooldstyle",
  2583. "oneoldstyle",
  2584. "twooldstyle",
  2585. "threeoldstyle",
  2586. "fouroldstyle",
  2587. "fiveoldstyle",
  2588. "sixoldstyle",
  2589. "sevenoldstyle",
  2590. "eightoldstyle",
  2591. "nineoldstyle",
  2592. "commasuperior",
  2593. "threequartersemdash",
  2594. "periodsuperior",
  2595. "questionsmall",
  2596. "asuperior",
  2597. "bsuperior",
  2598. "centsuperior",
  2599. "dsuperior",
  2600. "esuperior",
  2601. "isuperior",
  2602. "lsuperior",
  2603. "msuperior",
  2604. "nsuperior",
  2605. "osuperior",
  2606. "rsuperior",
  2607. "ssuperior",
  2608. "tsuperior",
  2609. "ff",
  2610. "ffi",
  2611. "ffl",
  2612. "parenleftinferior",
  2613. "parenrightinferior",
  2614. "Circumflexsmall",
  2615. "hyphensuperior",
  2616. "Gravesmall",
  2617. "Asmall",
  2618. "Bsmall",
  2619. "Csmall",
  2620. "Dsmall",
  2621. "Esmall",
  2622. "Fsmall",
  2623. "Gsmall",
  2624. "Hsmall",
  2625. "Ismall",
  2626. "Jsmall",
  2627. "Ksmall",
  2628. "Lsmall",
  2629. "Msmall",
  2630. "Nsmall",
  2631. "Osmall",
  2632. "Psmall",
  2633. "Qsmall",
  2634. "Rsmall",
  2635. "Ssmall",
  2636. "Tsmall",
  2637. "Usmall",
  2638. "Vsmall",
  2639. "Wsmall",
  2640. "Xsmall",
  2641. "Ysmall",
  2642. "Zsmall",
  2643. "colonmonetary",
  2644. "onefitted",
  2645. "rupiah",
  2646. "Tildesmall",
  2647. "exclamdownsmall",
  2648. "centoldstyle",
  2649. "Lslashsmall",
  2650. "Scaronsmall",
  2651. "Zcaronsmall",
  2652. "Dieresissmall",
  2653. "Brevesmall",
  2654. "Caronsmall",
  2655. "Dotaccentsmall",
  2656. "Macronsmall",
  2657. "figuredash",
  2658. "hypheninferior",
  2659. "Ogoneksmall",
  2660. "Ringsmall",
  2661. "Cedillasmall",
  2662. "questiondownsmall",
  2663. "oneeighth",
  2664. "threeeighths",
  2665. "fiveeighths",
  2666. "seveneighths",
  2667. "onethird",
  2668. "twothirds",
  2669. "zerosuperior",
  2670. "foursuperior",
  2671. "fivesuperior",
  2672. "sixsuperior",
  2673. "sevensuperior",
  2674. "eightsuperior",
  2675. "ninesuperior",
  2676. "zeroinferior",
  2677. "oneinferior",
  2678. "twoinferior",
  2679. "threeinferior",
  2680. "fourinferior",
  2681. "fiveinferior",
  2682. "sixinferior",
  2683. "seveninferior",
  2684. "eightinferior",
  2685. "nineinferior",
  2686. "centinferior",
  2687. "dollarinferior",
  2688. "periodinferior",
  2689. "commainferior",
  2690. "Agravesmall",
  2691. "Aacutesmall",
  2692. "Acircumflexsmall",
  2693. "Atildesmall",
  2694. "Adieresissmall",
  2695. "Aringsmall",
  2696. "AEsmall",
  2697. "Ccedillasmall",
  2698. "Egravesmall",
  2699. "Eacutesmall",
  2700. "Ecircumflexsmall",
  2701. "Edieresissmall",
  2702. "Igravesmall",
  2703. "Iacutesmall",
  2704. "Icircumflexsmall",
  2705. "Idieresissmall",
  2706. "Ethsmall",
  2707. "Ntildesmall",
  2708. "Ogravesmall",
  2709. "Oacutesmall",
  2710. "Ocircumflexsmall",
  2711. "Otildesmall",
  2712. "Odieresissmall",
  2713. "OEsmall",
  2714. "Oslashsmall",
  2715. "Ugravesmall",
  2716. "Uacutesmall",
  2717. "Ucircumflexsmall",
  2718. "Udieresissmall",
  2719. "Yacutesmall",
  2720. "Thornsmall",
  2721. "Ydieresissmall",
  2722. "001.000",
  2723. "001.001",
  2724. "001.002",
  2725. "001.003",
  2726. "Black",
  2727. "Bold",
  2728. "Book",
  2729. "Light",
  2730. "Medium",
  2731. "Regular",
  2732. "Roman",
  2733. "Semibold",
  2734. ]
  2735. cffStandardStringCount = 391
  2736. assert len(cffStandardStrings) == cffStandardStringCount
  2737. # build reverse mapping
  2738. cffStandardStringMapping = {}
  2739. for _i in range(cffStandardStringCount):
  2740. cffStandardStringMapping[cffStandardStrings[_i]] = _i
  2741. cffISOAdobeStrings = [
  2742. ".notdef",
  2743. "space",
  2744. "exclam",
  2745. "quotedbl",
  2746. "numbersign",
  2747. "dollar",
  2748. "percent",
  2749. "ampersand",
  2750. "quoteright",
  2751. "parenleft",
  2752. "parenright",
  2753. "asterisk",
  2754. "plus",
  2755. "comma",
  2756. "hyphen",
  2757. "period",
  2758. "slash",
  2759. "zero",
  2760. "one",
  2761. "two",
  2762. "three",
  2763. "four",
  2764. "five",
  2765. "six",
  2766. "seven",
  2767. "eight",
  2768. "nine",
  2769. "colon",
  2770. "semicolon",
  2771. "less",
  2772. "equal",
  2773. "greater",
  2774. "question",
  2775. "at",
  2776. "A",
  2777. "B",
  2778. "C",
  2779. "D",
  2780. "E",
  2781. "F",
  2782. "G",
  2783. "H",
  2784. "I",
  2785. "J",
  2786. "K",
  2787. "L",
  2788. "M",
  2789. "N",
  2790. "O",
  2791. "P",
  2792. "Q",
  2793. "R",
  2794. "S",
  2795. "T",
  2796. "U",
  2797. "V",
  2798. "W",
  2799. "X",
  2800. "Y",
  2801. "Z",
  2802. "bracketleft",
  2803. "backslash",
  2804. "bracketright",
  2805. "asciicircum",
  2806. "underscore",
  2807. "quoteleft",
  2808. "a",
  2809. "b",
  2810. "c",
  2811. "d",
  2812. "e",
  2813. "f",
  2814. "g",
  2815. "h",
  2816. "i",
  2817. "j",
  2818. "k",
  2819. "l",
  2820. "m",
  2821. "n",
  2822. "o",
  2823. "p",
  2824. "q",
  2825. "r",
  2826. "s",
  2827. "t",
  2828. "u",
  2829. "v",
  2830. "w",
  2831. "x",
  2832. "y",
  2833. "z",
  2834. "braceleft",
  2835. "bar",
  2836. "braceright",
  2837. "asciitilde",
  2838. "exclamdown",
  2839. "cent",
  2840. "sterling",
  2841. "fraction",
  2842. "yen",
  2843. "florin",
  2844. "section",
  2845. "currency",
  2846. "quotesingle",
  2847. "quotedblleft",
  2848. "guillemotleft",
  2849. "guilsinglleft",
  2850. "guilsinglright",
  2851. "fi",
  2852. "fl",
  2853. "endash",
  2854. "dagger",
  2855. "daggerdbl",
  2856. "periodcentered",
  2857. "paragraph",
  2858. "bullet",
  2859. "quotesinglbase",
  2860. "quotedblbase",
  2861. "quotedblright",
  2862. "guillemotright",
  2863. "ellipsis",
  2864. "perthousand",
  2865. "questiondown",
  2866. "grave",
  2867. "acute",
  2868. "circumflex",
  2869. "tilde",
  2870. "macron",
  2871. "breve",
  2872. "dotaccent",
  2873. "dieresis",
  2874. "ring",
  2875. "cedilla",
  2876. "hungarumlaut",
  2877. "ogonek",
  2878. "caron",
  2879. "emdash",
  2880. "AE",
  2881. "ordfeminine",
  2882. "Lslash",
  2883. "Oslash",
  2884. "OE",
  2885. "ordmasculine",
  2886. "ae",
  2887. "dotlessi",
  2888. "lslash",
  2889. "oslash",
  2890. "oe",
  2891. "germandbls",
  2892. "onesuperior",
  2893. "logicalnot",
  2894. "mu",
  2895. "trademark",
  2896. "Eth",
  2897. "onehalf",
  2898. "plusminus",
  2899. "Thorn",
  2900. "onequarter",
  2901. "divide",
  2902. "brokenbar",
  2903. "degree",
  2904. "thorn",
  2905. "threequarters",
  2906. "twosuperior",
  2907. "registered",
  2908. "minus",
  2909. "eth",
  2910. "multiply",
  2911. "threesuperior",
  2912. "copyright",
  2913. "Aacute",
  2914. "Acircumflex",
  2915. "Adieresis",
  2916. "Agrave",
  2917. "Aring",
  2918. "Atilde",
  2919. "Ccedilla",
  2920. "Eacute",
  2921. "Ecircumflex",
  2922. "Edieresis",
  2923. "Egrave",
  2924. "Iacute",
  2925. "Icircumflex",
  2926. "Idieresis",
  2927. "Igrave",
  2928. "Ntilde",
  2929. "Oacute",
  2930. "Ocircumflex",
  2931. "Odieresis",
  2932. "Ograve",
  2933. "Otilde",
  2934. "Scaron",
  2935. "Uacute",
  2936. "Ucircumflex",
  2937. "Udieresis",
  2938. "Ugrave",
  2939. "Yacute",
  2940. "Ydieresis",
  2941. "Zcaron",
  2942. "aacute",
  2943. "acircumflex",
  2944. "adieresis",
  2945. "agrave",
  2946. "aring",
  2947. "atilde",
  2948. "ccedilla",
  2949. "eacute",
  2950. "ecircumflex",
  2951. "edieresis",
  2952. "egrave",
  2953. "iacute",
  2954. "icircumflex",
  2955. "idieresis",
  2956. "igrave",
  2957. "ntilde",
  2958. "oacute",
  2959. "ocircumflex",
  2960. "odieresis",
  2961. "ograve",
  2962. "otilde",
  2963. "scaron",
  2964. "uacute",
  2965. "ucircumflex",
  2966. "udieresis",
  2967. "ugrave",
  2968. "yacute",
  2969. "ydieresis",
  2970. "zcaron",
  2971. ]
  2972. cffISOAdobeStringCount = 229
  2973. assert len(cffISOAdobeStrings) == cffISOAdobeStringCount
  2974. cffIExpertStrings = [
  2975. ".notdef",
  2976. "space",
  2977. "exclamsmall",
  2978. "Hungarumlautsmall",
  2979. "dollaroldstyle",
  2980. "dollarsuperior",
  2981. "ampersandsmall",
  2982. "Acutesmall",
  2983. "parenleftsuperior",
  2984. "parenrightsuperior",
  2985. "twodotenleader",
  2986. "onedotenleader",
  2987. "comma",
  2988. "hyphen",
  2989. "period",
  2990. "fraction",
  2991. "zerooldstyle",
  2992. "oneoldstyle",
  2993. "twooldstyle",
  2994. "threeoldstyle",
  2995. "fouroldstyle",
  2996. "fiveoldstyle",
  2997. "sixoldstyle",
  2998. "sevenoldstyle",
  2999. "eightoldstyle",
  3000. "nineoldstyle",
  3001. "colon",
  3002. "semicolon",
  3003. "commasuperior",
  3004. "threequartersemdash",
  3005. "periodsuperior",
  3006. "questionsmall",
  3007. "asuperior",
  3008. "bsuperior",
  3009. "centsuperior",
  3010. "dsuperior",
  3011. "esuperior",
  3012. "isuperior",
  3013. "lsuperior",
  3014. "msuperior",
  3015. "nsuperior",
  3016. "osuperior",
  3017. "rsuperior",
  3018. "ssuperior",
  3019. "tsuperior",
  3020. "ff",
  3021. "fi",
  3022. "fl",
  3023. "ffi",
  3024. "ffl",
  3025. "parenleftinferior",
  3026. "parenrightinferior",
  3027. "Circumflexsmall",
  3028. "hyphensuperior",
  3029. "Gravesmall",
  3030. "Asmall",
  3031. "Bsmall",
  3032. "Csmall",
  3033. "Dsmall",
  3034. "Esmall",
  3035. "Fsmall",
  3036. "Gsmall",
  3037. "Hsmall",
  3038. "Ismall",
  3039. "Jsmall",
  3040. "Ksmall",
  3041. "Lsmall",
  3042. "Msmall",
  3043. "Nsmall",
  3044. "Osmall",
  3045. "Psmall",
  3046. "Qsmall",
  3047. "Rsmall",
  3048. "Ssmall",
  3049. "Tsmall",
  3050. "Usmall",
  3051. "Vsmall",
  3052. "Wsmall",
  3053. "Xsmall",
  3054. "Ysmall",
  3055. "Zsmall",
  3056. "colonmonetary",
  3057. "onefitted",
  3058. "rupiah",
  3059. "Tildesmall",
  3060. "exclamdownsmall",
  3061. "centoldstyle",
  3062. "Lslashsmall",
  3063. "Scaronsmall",
  3064. "Zcaronsmall",
  3065. "Dieresissmall",
  3066. "Brevesmall",
  3067. "Caronsmall",
  3068. "Dotaccentsmall",
  3069. "Macronsmall",
  3070. "figuredash",
  3071. "hypheninferior",
  3072. "Ogoneksmall",
  3073. "Ringsmall",
  3074. "Cedillasmall",
  3075. "onequarter",
  3076. "onehalf",
  3077. "threequarters",
  3078. "questiondownsmall",
  3079. "oneeighth",
  3080. "threeeighths",
  3081. "fiveeighths",
  3082. "seveneighths",
  3083. "onethird",
  3084. "twothirds",
  3085. "zerosuperior",
  3086. "onesuperior",
  3087. "twosuperior",
  3088. "threesuperior",
  3089. "foursuperior",
  3090. "fivesuperior",
  3091. "sixsuperior",
  3092. "sevensuperior",
  3093. "eightsuperior",
  3094. "ninesuperior",
  3095. "zeroinferior",
  3096. "oneinferior",
  3097. "twoinferior",
  3098. "threeinferior",
  3099. "fourinferior",
  3100. "fiveinferior",
  3101. "sixinferior",
  3102. "seveninferior",
  3103. "eightinferior",
  3104. "nineinferior",
  3105. "centinferior",
  3106. "dollarinferior",
  3107. "periodinferior",
  3108. "commainferior",
  3109. "Agravesmall",
  3110. "Aacutesmall",
  3111. "Acircumflexsmall",
  3112. "Atildesmall",
  3113. "Adieresissmall",
  3114. "Aringsmall",
  3115. "AEsmall",
  3116. "Ccedillasmall",
  3117. "Egravesmall",
  3118. "Eacutesmall",
  3119. "Ecircumflexsmall",
  3120. "Edieresissmall",
  3121. "Igravesmall",
  3122. "Iacutesmall",
  3123. "Icircumflexsmall",
  3124. "Idieresissmall",
  3125. "Ethsmall",
  3126. "Ntildesmall",
  3127. "Ogravesmall",
  3128. "Oacutesmall",
  3129. "Ocircumflexsmall",
  3130. "Otildesmall",
  3131. "Odieresissmall",
  3132. "OEsmall",
  3133. "Oslashsmall",
  3134. "Ugravesmall",
  3135. "Uacutesmall",
  3136. "Ucircumflexsmall",
  3137. "Udieresissmall",
  3138. "Yacutesmall",
  3139. "Thornsmall",
  3140. "Ydieresissmall",
  3141. ]
  3142. cffExpertStringCount = 166
  3143. assert len(cffIExpertStrings) == cffExpertStringCount
  3144. cffExpertSubsetStrings = [
  3145. ".notdef",
  3146. "space",
  3147. "dollaroldstyle",
  3148. "dollarsuperior",
  3149. "parenleftsuperior",
  3150. "parenrightsuperior",
  3151. "twodotenleader",
  3152. "onedotenleader",
  3153. "comma",
  3154. "hyphen",
  3155. "period",
  3156. "fraction",
  3157. "zerooldstyle",
  3158. "oneoldstyle",
  3159. "twooldstyle",
  3160. "threeoldstyle",
  3161. "fouroldstyle",
  3162. "fiveoldstyle",
  3163. "sixoldstyle",
  3164. "sevenoldstyle",
  3165. "eightoldstyle",
  3166. "nineoldstyle",
  3167. "colon",
  3168. "semicolon",
  3169. "commasuperior",
  3170. "threequartersemdash",
  3171. "periodsuperior",
  3172. "asuperior",
  3173. "bsuperior",
  3174. "centsuperior",
  3175. "dsuperior",
  3176. "esuperior",
  3177. "isuperior",
  3178. "lsuperior",
  3179. "msuperior",
  3180. "nsuperior",
  3181. "osuperior",
  3182. "rsuperior",
  3183. "ssuperior",
  3184. "tsuperior",
  3185. "ff",
  3186. "fi",
  3187. "fl",
  3188. "ffi",
  3189. "ffl",
  3190. "parenleftinferior",
  3191. "parenrightinferior",
  3192. "hyphensuperior",
  3193. "colonmonetary",
  3194. "onefitted",
  3195. "rupiah",
  3196. "centoldstyle",
  3197. "figuredash",
  3198. "hypheninferior",
  3199. "onequarter",
  3200. "onehalf",
  3201. "threequarters",
  3202. "oneeighth",
  3203. "threeeighths",
  3204. "fiveeighths",
  3205. "seveneighths",
  3206. "onethird",
  3207. "twothirds",
  3208. "zerosuperior",
  3209. "onesuperior",
  3210. "twosuperior",
  3211. "threesuperior",
  3212. "foursuperior",
  3213. "fivesuperior",
  3214. "sixsuperior",
  3215. "sevensuperior",
  3216. "eightsuperior",
  3217. "ninesuperior",
  3218. "zeroinferior",
  3219. "oneinferior",
  3220. "twoinferior",
  3221. "threeinferior",
  3222. "fourinferior",
  3223. "fiveinferior",
  3224. "sixinferior",
  3225. "seveninferior",
  3226. "eightinferior",
  3227. "nineinferior",
  3228. "centinferior",
  3229. "dollarinferior",
  3230. "periodinferior",
  3231. "commainferior",
  3232. ]
  3233. cffExpertSubsetStringCount = 87
  3234. assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount