__init__.py 105 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694
  1. """cffLib: read/write Adobe CFF fonts
  2. OpenType fonts with PostScript outlines embed a completely independent
  3. font file in 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. # https://github.com/fonttools/fonttools/issues/3673
  735. if length == 65535:
  736. self.data = self.file.read()
  737. else:
  738. self.data = self.file.read(length)
  739. globalState = {}
  740. reader = OTTableReader(self.data, globalState)
  741. self.otVarStore = ot.VarStore()
  742. self.otVarStore.decompile(reader, self.font)
  743. self.data = None
  744. return self
  745. def compile(self):
  746. writer = OTTableWriter()
  747. self.otVarStore.compile(writer, self.font)
  748. # Note that this omits the initial Card16 length from the CFF2
  749. # VarStore data block
  750. self.data = writer.getAllData()
  751. def writeXML(self, xmlWriter, name):
  752. self.otVarStore.toXML(xmlWriter, self.font)
  753. def xmlRead(self, name, attrs, content, parent):
  754. self.otVarStore = ot.VarStore()
  755. for element in content:
  756. if isinstance(element, tuple):
  757. name, attrs, content = element
  758. self.otVarStore.fromXML(name, attrs, content, self.font)
  759. else:
  760. pass
  761. return None
  762. def __len__(self):
  763. return len(self.data)
  764. def getNumRegions(self, vsIndex):
  765. if vsIndex is None:
  766. vsIndex = 0
  767. varData = self.otVarStore.VarData[vsIndex]
  768. numRegions = varData.VarRegionCount
  769. return numRegions
  770. class FDSelect(object):
  771. def __init__(self, file=None, numGlyphs=None, format=None):
  772. if file:
  773. # read data in from file
  774. self.format = readCard8(file)
  775. if self.format == 0:
  776. from array import array
  777. self.gidArray = array("B", file.read(numGlyphs)).tolist()
  778. elif self.format == 3:
  779. gidArray = [None] * numGlyphs
  780. nRanges = readCard16(file)
  781. fd = None
  782. prev = None
  783. for i in range(nRanges):
  784. first = readCard16(file)
  785. if prev is not None:
  786. for glyphID in range(prev, first):
  787. gidArray[glyphID] = fd
  788. prev = first
  789. fd = readCard8(file)
  790. if prev is not None:
  791. first = readCard16(file)
  792. for glyphID in range(prev, first):
  793. gidArray[glyphID] = fd
  794. self.gidArray = gidArray
  795. elif self.format == 4:
  796. gidArray = [None] * numGlyphs
  797. nRanges = readCard32(file)
  798. fd = None
  799. prev = None
  800. for i in range(nRanges):
  801. first = readCard32(file)
  802. if prev is not None:
  803. for glyphID in range(prev, first):
  804. gidArray[glyphID] = fd
  805. prev = first
  806. fd = readCard16(file)
  807. if prev is not None:
  808. first = readCard32(file)
  809. for glyphID in range(prev, first):
  810. gidArray[glyphID] = fd
  811. self.gidArray = gidArray
  812. else:
  813. assert False, "unsupported FDSelect format: %s" % format
  814. else:
  815. # reading from XML. Make empty gidArray, and leave format as passed in.
  816. # format is None will result in the smallest representation being used.
  817. self.format = format
  818. self.gidArray = []
  819. def __len__(self):
  820. return len(self.gidArray)
  821. def __getitem__(self, index):
  822. return self.gidArray[index]
  823. def __setitem__(self, index, fdSelectValue):
  824. self.gidArray[index] = fdSelectValue
  825. def append(self, fdSelectValue):
  826. self.gidArray.append(fdSelectValue)
  827. class CharStrings(object):
  828. """The ``CharStrings`` in the font represent the instructions for drawing
  829. each glyph. This object presents a dictionary interface to the font's
  830. CharStrings, indexed by glyph name:
  831. .. code:: python
  832. tt["CFF "].cff[0].CharStrings["a"]
  833. # <T2CharString (bytecode) at 103451e90>
  834. See :class:`fontTools.misc.psCharStrings.T1CharString` and
  835. :class:`fontTools.misc.psCharStrings.T2CharString` for how to decompile,
  836. compile and interpret the glyph drawing instructions in the returned objects.
  837. """
  838. def __init__(
  839. self,
  840. file,
  841. charset,
  842. globalSubrs,
  843. private,
  844. fdSelect,
  845. fdArray,
  846. isCFF2=None,
  847. varStore=None,
  848. ):
  849. self.globalSubrs = globalSubrs
  850. self.varStore = varStore
  851. if file is not None:
  852. self.charStringsIndex = SubrsIndex(
  853. file, globalSubrs, private, fdSelect, fdArray, isCFF2=isCFF2
  854. )
  855. self.charStrings = charStrings = {}
  856. for i in range(len(charset)):
  857. charStrings[charset[i]] = i
  858. # read from OTF file: charStrings.values() are indices into
  859. # charStringsIndex.
  860. self.charStringsAreIndexed = 1
  861. else:
  862. self.charStrings = {}
  863. # read from ttx file: charStrings.values() are actual charstrings
  864. self.charStringsAreIndexed = 0
  865. self.private = private
  866. if fdSelect is not None:
  867. self.fdSelect = fdSelect
  868. if fdArray is not None:
  869. self.fdArray = fdArray
  870. def keys(self):
  871. return list(self.charStrings.keys())
  872. def values(self):
  873. if self.charStringsAreIndexed:
  874. return self.charStringsIndex
  875. else:
  876. return list(self.charStrings.values())
  877. def has_key(self, name):
  878. return name in self.charStrings
  879. __contains__ = has_key
  880. def __len__(self):
  881. return len(self.charStrings)
  882. def __getitem__(self, name):
  883. charString = self.charStrings[name]
  884. if self.charStringsAreIndexed:
  885. charString = self.charStringsIndex[charString]
  886. return charString
  887. def __setitem__(self, name, charString):
  888. if self.charStringsAreIndexed:
  889. index = self.charStrings[name]
  890. self.charStringsIndex[index] = charString
  891. else:
  892. self.charStrings[name] = charString
  893. def getItemAndSelector(self, name):
  894. if self.charStringsAreIndexed:
  895. index = self.charStrings[name]
  896. return self.charStringsIndex.getItemAndSelector(index)
  897. else:
  898. if hasattr(self, "fdArray"):
  899. if hasattr(self, "fdSelect"):
  900. sel = self.charStrings[name].fdSelectIndex
  901. else:
  902. sel = 0
  903. else:
  904. sel = None
  905. return self.charStrings[name], sel
  906. def toXML(self, xmlWriter):
  907. names = sorted(self.keys())
  908. for name in names:
  909. charStr, fdSelectIndex = self.getItemAndSelector(name)
  910. if charStr.needsDecompilation():
  911. raw = [("raw", 1)]
  912. else:
  913. raw = []
  914. if fdSelectIndex is None:
  915. xmlWriter.begintag("CharString", [("name", name)] + raw)
  916. else:
  917. xmlWriter.begintag(
  918. "CharString",
  919. [("name", name), ("fdSelectIndex", fdSelectIndex)] + raw,
  920. )
  921. xmlWriter.newline()
  922. charStr.toXML(xmlWriter)
  923. xmlWriter.endtag("CharString")
  924. xmlWriter.newline()
  925. def fromXML(self, name, attrs, content):
  926. for element in content:
  927. if isinstance(element, str):
  928. continue
  929. name, attrs, content = element
  930. if name != "CharString":
  931. continue
  932. fdID = -1
  933. if hasattr(self, "fdArray"):
  934. try:
  935. fdID = safeEval(attrs["fdSelectIndex"])
  936. except KeyError:
  937. fdID = 0
  938. private = self.fdArray[fdID].Private
  939. else:
  940. private = self.private
  941. glyphName = attrs["name"]
  942. charStringClass = psCharStrings.T2CharString
  943. charString = charStringClass(private=private, globalSubrs=self.globalSubrs)
  944. charString.fromXML(name, attrs, content)
  945. if fdID >= 0:
  946. charString.fdSelectIndex = fdID
  947. self[glyphName] = charString
  948. def readCard8(file):
  949. return byteord(file.read(1))
  950. def readCard16(file):
  951. (value,) = struct.unpack(">H", file.read(2))
  952. return value
  953. def readCard32(file):
  954. (value,) = struct.unpack(">L", file.read(4))
  955. return value
  956. def writeCard8(file, value):
  957. file.write(bytechr(value))
  958. def writeCard16(file, value):
  959. file.write(struct.pack(">H", value))
  960. def writeCard32(file, value):
  961. file.write(struct.pack(">L", value))
  962. def packCard8(value):
  963. return bytechr(value)
  964. def packCard16(value):
  965. return struct.pack(">H", value)
  966. def packCard32(value):
  967. return struct.pack(">L", value)
  968. def buildOperatorDict(table):
  969. d = {}
  970. for op, name, arg, default, conv in table:
  971. d[op] = (name, arg)
  972. return d
  973. def buildOpcodeDict(table):
  974. d = {}
  975. for op, name, arg, default, conv in table:
  976. if isinstance(op, tuple):
  977. op = bytechr(op[0]) + bytechr(op[1])
  978. else:
  979. op = bytechr(op)
  980. d[name] = (op, arg)
  981. return d
  982. def buildOrder(table):
  983. l = []
  984. for op, name, arg, default, conv in table:
  985. l.append(name)
  986. return l
  987. def buildDefaults(table):
  988. d = {}
  989. for op, name, arg, default, conv in table:
  990. if default is not None:
  991. d[name] = default
  992. return d
  993. def buildConverters(table):
  994. d = {}
  995. for op, name, arg, default, conv in table:
  996. d[name] = conv
  997. return d
  998. class SimpleConverter(object):
  999. def read(self, parent, value):
  1000. if not hasattr(parent, "file"):
  1001. return self._read(parent, value)
  1002. file = parent.file
  1003. pos = file.tell()
  1004. try:
  1005. return self._read(parent, value)
  1006. finally:
  1007. file.seek(pos)
  1008. def _read(self, parent, value):
  1009. return value
  1010. def write(self, parent, value):
  1011. return value
  1012. def xmlWrite(self, xmlWriter, name, value):
  1013. xmlWriter.simpletag(name, value=value)
  1014. xmlWriter.newline()
  1015. def xmlRead(self, name, attrs, content, parent):
  1016. return attrs["value"]
  1017. class ASCIIConverter(SimpleConverter):
  1018. def _read(self, parent, value):
  1019. return tostr(value, encoding="ascii")
  1020. def write(self, parent, value):
  1021. return tobytes(value, encoding="ascii")
  1022. def xmlWrite(self, xmlWriter, name, value):
  1023. xmlWriter.simpletag(name, value=tostr(value, encoding="ascii"))
  1024. xmlWriter.newline()
  1025. def xmlRead(self, name, attrs, content, parent):
  1026. return tobytes(attrs["value"], encoding=("ascii"))
  1027. class Latin1Converter(SimpleConverter):
  1028. def _read(self, parent, value):
  1029. return tostr(value, encoding="latin1")
  1030. def write(self, parent, value):
  1031. return tobytes(value, encoding="latin1")
  1032. def xmlWrite(self, xmlWriter, name, value):
  1033. value = tostr(value, encoding="latin1")
  1034. if name in ["Notice", "Copyright"]:
  1035. value = re.sub(r"[\r\n]\s+", " ", value)
  1036. xmlWriter.simpletag(name, value=value)
  1037. xmlWriter.newline()
  1038. def xmlRead(self, name, attrs, content, parent):
  1039. return tobytes(attrs["value"], encoding=("latin1"))
  1040. def parseNum(s):
  1041. try:
  1042. value = int(s)
  1043. except:
  1044. value = float(s)
  1045. return value
  1046. def parseBlendList(s):
  1047. valueList = []
  1048. for element in s:
  1049. if isinstance(element, str):
  1050. continue
  1051. name, attrs, content = element
  1052. blendList = attrs["value"].split()
  1053. blendList = [safeEval(val) for val in blendList]
  1054. valueList.append(blendList)
  1055. if len(valueList) == 1:
  1056. valueList = valueList[0]
  1057. return valueList
  1058. class NumberConverter(SimpleConverter):
  1059. def xmlWrite(self, xmlWriter, name, value):
  1060. if isinstance(value, list):
  1061. xmlWriter.begintag(name)
  1062. xmlWriter.newline()
  1063. xmlWriter.indent()
  1064. blendValue = " ".join([str(val) for val in value])
  1065. xmlWriter.simpletag(kBlendDictOpName, value=blendValue)
  1066. xmlWriter.newline()
  1067. xmlWriter.dedent()
  1068. xmlWriter.endtag(name)
  1069. xmlWriter.newline()
  1070. else:
  1071. xmlWriter.simpletag(name, value=value)
  1072. xmlWriter.newline()
  1073. def xmlRead(self, name, attrs, content, parent):
  1074. valueString = attrs.get("value", None)
  1075. if valueString is None:
  1076. value = parseBlendList(content)
  1077. else:
  1078. value = parseNum(attrs["value"])
  1079. return value
  1080. class ArrayConverter(SimpleConverter):
  1081. def xmlWrite(self, xmlWriter, name, value):
  1082. if value and isinstance(value[0], list):
  1083. xmlWriter.begintag(name)
  1084. xmlWriter.newline()
  1085. xmlWriter.indent()
  1086. for valueList in value:
  1087. blendValue = " ".join([str(val) for val in valueList])
  1088. xmlWriter.simpletag(kBlendDictOpName, value=blendValue)
  1089. xmlWriter.newline()
  1090. xmlWriter.dedent()
  1091. xmlWriter.endtag(name)
  1092. xmlWriter.newline()
  1093. else:
  1094. value = " ".join([str(val) for val in value])
  1095. xmlWriter.simpletag(name, value=value)
  1096. xmlWriter.newline()
  1097. def xmlRead(self, name, attrs, content, parent):
  1098. valueString = attrs.get("value", None)
  1099. if valueString is None:
  1100. valueList = parseBlendList(content)
  1101. else:
  1102. values = valueString.split()
  1103. valueList = [parseNum(value) for value in values]
  1104. return valueList
  1105. class TableConverter(SimpleConverter):
  1106. def xmlWrite(self, xmlWriter, name, value):
  1107. xmlWriter.begintag(name)
  1108. xmlWriter.newline()
  1109. value.toXML(xmlWriter)
  1110. xmlWriter.endtag(name)
  1111. xmlWriter.newline()
  1112. def xmlRead(self, name, attrs, content, parent):
  1113. ob = self.getClass()()
  1114. for element in content:
  1115. if isinstance(element, str):
  1116. continue
  1117. name, attrs, content = element
  1118. ob.fromXML(name, attrs, content)
  1119. return ob
  1120. class PrivateDictConverter(TableConverter):
  1121. def getClass(self):
  1122. return PrivateDict
  1123. def _read(self, parent, value):
  1124. size, offset = value
  1125. file = parent.file
  1126. isCFF2 = parent._isCFF2
  1127. try:
  1128. vstore = parent.vstore
  1129. except AttributeError:
  1130. vstore = None
  1131. priv = PrivateDict(parent.strings, file, offset, isCFF2=isCFF2, vstore=vstore)
  1132. file.seek(offset)
  1133. data = file.read(size)
  1134. assert len(data) == size
  1135. priv.decompile(data)
  1136. return priv
  1137. def write(self, parent, value):
  1138. return (0, 0) # dummy value
  1139. class SubrsConverter(TableConverter):
  1140. def getClass(self):
  1141. return SubrsIndex
  1142. def _read(self, parent, value):
  1143. file = parent.file
  1144. isCFF2 = parent._isCFF2
  1145. file.seek(parent.offset + value) # Offset(self)
  1146. return SubrsIndex(file, isCFF2=isCFF2)
  1147. def write(self, parent, value):
  1148. return 0 # dummy value
  1149. class CharStringsConverter(TableConverter):
  1150. def _read(self, parent, value):
  1151. file = parent.file
  1152. isCFF2 = parent._isCFF2
  1153. charset = parent.charset
  1154. varStore = getattr(parent, "VarStore", None)
  1155. globalSubrs = parent.GlobalSubrs
  1156. if hasattr(parent, "FDArray"):
  1157. fdArray = parent.FDArray
  1158. if hasattr(parent, "FDSelect"):
  1159. fdSelect = parent.FDSelect
  1160. else:
  1161. fdSelect = None
  1162. private = None
  1163. else:
  1164. fdSelect, fdArray = None, None
  1165. private = parent.Private
  1166. file.seek(value) # Offset(0)
  1167. charStrings = CharStrings(
  1168. file,
  1169. charset,
  1170. globalSubrs,
  1171. private,
  1172. fdSelect,
  1173. fdArray,
  1174. isCFF2=isCFF2,
  1175. varStore=varStore,
  1176. )
  1177. return charStrings
  1178. def write(self, parent, value):
  1179. return 0 # dummy value
  1180. def xmlRead(self, name, attrs, content, parent):
  1181. if hasattr(parent, "FDArray"):
  1182. # if it is a CID-keyed font, then the private Dict is extracted from the
  1183. # parent.FDArray
  1184. fdArray = parent.FDArray
  1185. if hasattr(parent, "FDSelect"):
  1186. fdSelect = parent.FDSelect
  1187. else:
  1188. fdSelect = None
  1189. private = None
  1190. else:
  1191. # if it is a name-keyed font, then the private dict is in the top dict,
  1192. # and
  1193. # there is no fdArray.
  1194. private, fdSelect, fdArray = parent.Private, None, None
  1195. charStrings = CharStrings(
  1196. None,
  1197. None,
  1198. parent.GlobalSubrs,
  1199. private,
  1200. fdSelect,
  1201. fdArray,
  1202. varStore=getattr(parent, "VarStore", None),
  1203. )
  1204. charStrings.fromXML(name, attrs, content)
  1205. return charStrings
  1206. class CharsetConverter(SimpleConverter):
  1207. def _read(self, parent, value):
  1208. isCID = hasattr(parent, "ROS")
  1209. if value > 2:
  1210. numGlyphs = parent.numGlyphs
  1211. file = parent.file
  1212. file.seek(value)
  1213. log.log(DEBUG, "loading charset at %s", value)
  1214. format = readCard8(file)
  1215. if format == 0:
  1216. charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
  1217. elif format == 1 or format == 2:
  1218. charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
  1219. else:
  1220. raise NotImplementedError
  1221. assert len(charset) == numGlyphs
  1222. log.log(DEBUG, " charset end at %s", file.tell())
  1223. # make sure glyph names are unique
  1224. allNames = {}
  1225. newCharset = []
  1226. for glyphName in charset:
  1227. if glyphName in allNames:
  1228. # make up a new glyphName that's unique
  1229. n = allNames[glyphName]
  1230. names = set(allNames) | set(charset)
  1231. while (glyphName + "." + str(n)) in names:
  1232. n += 1
  1233. allNames[glyphName] = n + 1
  1234. glyphName = glyphName + "." + str(n)
  1235. allNames[glyphName] = 1
  1236. newCharset.append(glyphName)
  1237. charset = newCharset
  1238. else: # offset == 0 -> no charset data.
  1239. if isCID or "CharStrings" not in parent.rawDict:
  1240. # We get here only when processing fontDicts from the FDArray of
  1241. # CFF-CID fonts. Only the real topDict references the charset.
  1242. assert value == 0
  1243. charset = None
  1244. elif value == 0:
  1245. charset = cffISOAdobeStrings
  1246. elif value == 1:
  1247. charset = cffIExpertStrings
  1248. elif value == 2:
  1249. charset = cffExpertSubsetStrings
  1250. if charset and (len(charset) != parent.numGlyphs):
  1251. charset = charset[: parent.numGlyphs]
  1252. return charset
  1253. def write(self, parent, value):
  1254. return 0 # dummy value
  1255. def xmlWrite(self, xmlWriter, name, value):
  1256. # XXX only write charset when not in OT/TTX context, where we
  1257. # dump charset as a separate "GlyphOrder" table.
  1258. # # xmlWriter.simpletag("charset")
  1259. xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
  1260. xmlWriter.newline()
  1261. def xmlRead(self, name, attrs, content, parent):
  1262. pass
  1263. class CharsetCompiler(object):
  1264. def __init__(self, strings, charset, parent):
  1265. assert charset[0] == ".notdef"
  1266. isCID = hasattr(parent.dictObj, "ROS")
  1267. data0 = packCharset0(charset, isCID, strings)
  1268. data = packCharset(charset, isCID, strings)
  1269. if len(data) < len(data0):
  1270. self.data = data
  1271. else:
  1272. self.data = data0
  1273. self.parent = parent
  1274. def setPos(self, pos, endPos):
  1275. self.parent.rawDict["charset"] = pos
  1276. def getDataLength(self):
  1277. return len(self.data)
  1278. def toFile(self, file):
  1279. file.write(self.data)
  1280. def getStdCharSet(charset):
  1281. # check to see if we can use a predefined charset value.
  1282. predefinedCharSetVal = None
  1283. predefinedCharSets = [
  1284. (cffISOAdobeStringCount, cffISOAdobeStrings, 0),
  1285. (cffExpertStringCount, cffIExpertStrings, 1),
  1286. (cffExpertSubsetStringCount, cffExpertSubsetStrings, 2),
  1287. ]
  1288. lcs = len(charset)
  1289. for cnt, pcs, csv in predefinedCharSets:
  1290. if predefinedCharSetVal is not None:
  1291. break
  1292. if lcs > cnt:
  1293. continue
  1294. predefinedCharSetVal = csv
  1295. for i in range(lcs):
  1296. if charset[i] != pcs[i]:
  1297. predefinedCharSetVal = None
  1298. break
  1299. return predefinedCharSetVal
  1300. def getCIDfromName(name, strings):
  1301. return int(name[3:])
  1302. def getSIDfromName(name, strings):
  1303. return strings.getSID(name)
  1304. def packCharset0(charset, isCID, strings):
  1305. fmt = 0
  1306. data = [packCard8(fmt)]
  1307. if isCID:
  1308. getNameID = getCIDfromName
  1309. else:
  1310. getNameID = getSIDfromName
  1311. for name in charset[1:]:
  1312. data.append(packCard16(getNameID(name, strings)))
  1313. return bytesjoin(data)
  1314. def packCharset(charset, isCID, strings):
  1315. fmt = 1
  1316. ranges = []
  1317. first = None
  1318. end = 0
  1319. if isCID:
  1320. getNameID = getCIDfromName
  1321. else:
  1322. getNameID = getSIDfromName
  1323. for name in charset[1:]:
  1324. SID = getNameID(name, strings)
  1325. if first is None:
  1326. first = SID
  1327. elif end + 1 != SID:
  1328. nLeft = end - first
  1329. if nLeft > 255:
  1330. fmt = 2
  1331. ranges.append((first, nLeft))
  1332. first = SID
  1333. end = SID
  1334. if end:
  1335. nLeft = end - first
  1336. if nLeft > 255:
  1337. fmt = 2
  1338. ranges.append((first, nLeft))
  1339. data = [packCard8(fmt)]
  1340. if fmt == 1:
  1341. nLeftFunc = packCard8
  1342. else:
  1343. nLeftFunc = packCard16
  1344. for first, nLeft in ranges:
  1345. data.append(packCard16(first) + nLeftFunc(nLeft))
  1346. return bytesjoin(data)
  1347. def parseCharset0(numGlyphs, file, strings, isCID):
  1348. charset = [".notdef"]
  1349. if isCID:
  1350. for i in range(numGlyphs - 1):
  1351. CID = readCard16(file)
  1352. charset.append("cid" + str(CID).zfill(5))
  1353. else:
  1354. for i in range(numGlyphs - 1):
  1355. SID = readCard16(file)
  1356. charset.append(strings[SID])
  1357. return charset
  1358. def parseCharset(numGlyphs, file, strings, isCID, fmt):
  1359. charset = [".notdef"]
  1360. count = 1
  1361. if fmt == 1:
  1362. nLeftFunc = readCard8
  1363. else:
  1364. nLeftFunc = readCard16
  1365. while count < numGlyphs:
  1366. first = readCard16(file)
  1367. nLeft = nLeftFunc(file)
  1368. if isCID:
  1369. for CID in range(first, first + nLeft + 1):
  1370. charset.append("cid" + str(CID).zfill(5))
  1371. else:
  1372. for SID in range(first, first + nLeft + 1):
  1373. charset.append(strings[SID])
  1374. count = count + nLeft + 1
  1375. return charset
  1376. class EncodingCompiler(object):
  1377. def __init__(self, strings, encoding, parent):
  1378. assert not isinstance(encoding, str)
  1379. data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
  1380. data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
  1381. if len(data0) < len(data1):
  1382. self.data = data0
  1383. else:
  1384. self.data = data1
  1385. self.parent = parent
  1386. def setPos(self, pos, endPos):
  1387. self.parent.rawDict["Encoding"] = pos
  1388. def getDataLength(self):
  1389. return len(self.data)
  1390. def toFile(self, file):
  1391. file.write(self.data)
  1392. class EncodingConverter(SimpleConverter):
  1393. def _read(self, parent, value):
  1394. if value == 0:
  1395. return "StandardEncoding"
  1396. elif value == 1:
  1397. return "ExpertEncoding"
  1398. # custom encoding at offset `value`
  1399. assert value > 1
  1400. file = parent.file
  1401. file.seek(value)
  1402. log.log(DEBUG, "loading Encoding at %s", value)
  1403. fmt = readCard8(file)
  1404. haveSupplement = bool(fmt & 0x80)
  1405. fmt = fmt & 0x7F
  1406. if fmt == 0:
  1407. encoding = parseEncoding0(parent.charset, file)
  1408. elif fmt == 1:
  1409. encoding = parseEncoding1(parent.charset, file)
  1410. else:
  1411. raise ValueError(f"Unknown Encoding format: {fmt}")
  1412. if haveSupplement:
  1413. parseEncodingSupplement(file, encoding, parent.strings)
  1414. return encoding
  1415. def write(self, parent, value):
  1416. if value == "StandardEncoding":
  1417. return 0
  1418. elif value == "ExpertEncoding":
  1419. return 1
  1420. return 0 # dummy value
  1421. def xmlWrite(self, xmlWriter, name, value):
  1422. if value in ("StandardEncoding", "ExpertEncoding"):
  1423. xmlWriter.simpletag(name, name=value)
  1424. xmlWriter.newline()
  1425. return
  1426. xmlWriter.begintag(name)
  1427. xmlWriter.newline()
  1428. for code in range(len(value)):
  1429. glyphName = value[code]
  1430. if glyphName != ".notdef":
  1431. xmlWriter.simpletag("map", code=hex(code), name=glyphName)
  1432. xmlWriter.newline()
  1433. xmlWriter.endtag(name)
  1434. xmlWriter.newline()
  1435. def xmlRead(self, name, attrs, content, parent):
  1436. if "name" in attrs:
  1437. return attrs["name"]
  1438. encoding = [".notdef"] * 256
  1439. for element in content:
  1440. if isinstance(element, str):
  1441. continue
  1442. name, attrs, content = element
  1443. code = safeEval(attrs["code"])
  1444. glyphName = attrs["name"]
  1445. encoding[code] = glyphName
  1446. return encoding
  1447. def readSID(file):
  1448. """Read a String ID (SID) — 2-byte unsigned integer."""
  1449. data = file.read(2)
  1450. if len(data) != 2:
  1451. raise EOFError("Unexpected end of file while reading SID")
  1452. return struct.unpack(">H", data)[0] # big-endian uint16
  1453. def parseEncodingSupplement(file, encoding, strings):
  1454. """
  1455. Parse the CFF Encoding supplement data:
  1456. - nSups: number of supplementary mappings
  1457. - each mapping: (code, SID) pair
  1458. and apply them to the `encoding` list in place.
  1459. """
  1460. nSups = readCard8(file)
  1461. for _ in range(nSups):
  1462. code = readCard8(file)
  1463. sid = readSID(file)
  1464. name = strings[sid]
  1465. encoding[code] = name
  1466. def parseEncoding0(charset, file):
  1467. """
  1468. Format 0: simple list of codes.
  1469. After reading the base table, optionally parse the supplement.
  1470. """
  1471. nCodes = readCard8(file)
  1472. encoding = [".notdef"] * 256
  1473. for glyphID in range(1, nCodes + 1):
  1474. code = readCard8(file)
  1475. if code != 0:
  1476. encoding[code] = charset[glyphID]
  1477. return encoding
  1478. def parseEncoding1(charset, file):
  1479. """
  1480. Format 1: range-based encoding.
  1481. After reading the base ranges, optionally parse the supplement.
  1482. """
  1483. nRanges = readCard8(file)
  1484. encoding = [".notdef"] * 256
  1485. glyphID = 1
  1486. for _ in range(nRanges):
  1487. code = readCard8(file)
  1488. nLeft = readCard8(file)
  1489. for _ in range(nLeft + 1):
  1490. encoding[code] = charset[glyphID]
  1491. code += 1
  1492. glyphID += 1
  1493. return encoding
  1494. def packEncoding0(charset, encoding, strings):
  1495. fmt = 0
  1496. m = {}
  1497. for code in range(len(encoding)):
  1498. name = encoding[code]
  1499. if name != ".notdef":
  1500. m[name] = code
  1501. codes = []
  1502. for name in charset[1:]:
  1503. code = m.get(name)
  1504. codes.append(code)
  1505. while codes and codes[-1] is None:
  1506. codes.pop()
  1507. data = [packCard8(fmt), packCard8(len(codes))]
  1508. for code in codes:
  1509. if code is None:
  1510. code = 0
  1511. data.append(packCard8(code))
  1512. return bytesjoin(data)
  1513. def packEncoding1(charset, encoding, strings):
  1514. fmt = 1
  1515. m = {}
  1516. for code in range(len(encoding)):
  1517. name = encoding[code]
  1518. if name != ".notdef":
  1519. m[name] = code
  1520. ranges = []
  1521. first = None
  1522. end = 0
  1523. for name in charset[1:]:
  1524. code = m.get(name, -1)
  1525. if first is None:
  1526. first = code
  1527. elif end + 1 != code:
  1528. nLeft = end - first
  1529. ranges.append((first, nLeft))
  1530. first = code
  1531. end = code
  1532. nLeft = end - first
  1533. ranges.append((first, nLeft))
  1534. # remove unencoded glyphs at the end.
  1535. while ranges and ranges[-1][0] == -1:
  1536. ranges.pop()
  1537. data = [packCard8(fmt), packCard8(len(ranges))]
  1538. for first, nLeft in ranges:
  1539. if first == -1: # unencoded
  1540. first = 0
  1541. data.append(packCard8(first) + packCard8(nLeft))
  1542. return bytesjoin(data)
  1543. class FDArrayConverter(TableConverter):
  1544. def _read(self, parent, value):
  1545. try:
  1546. vstore = parent.VarStore
  1547. except AttributeError:
  1548. vstore = None
  1549. file = parent.file
  1550. isCFF2 = parent._isCFF2
  1551. file.seek(value)
  1552. fdArray = FDArrayIndex(file, isCFF2=isCFF2)
  1553. fdArray.vstore = vstore
  1554. fdArray.strings = parent.strings
  1555. fdArray.GlobalSubrs = parent.GlobalSubrs
  1556. return fdArray
  1557. def write(self, parent, value):
  1558. return 0 # dummy value
  1559. def xmlRead(self, name, attrs, content, parent):
  1560. fdArray = FDArrayIndex()
  1561. for element in content:
  1562. if isinstance(element, str):
  1563. continue
  1564. name, attrs, content = element
  1565. fdArray.fromXML(name, attrs, content)
  1566. return fdArray
  1567. class FDSelectConverter(SimpleConverter):
  1568. def _read(self, parent, value):
  1569. file = parent.file
  1570. file.seek(value)
  1571. fdSelect = FDSelect(file, parent.numGlyphs)
  1572. return fdSelect
  1573. def write(self, parent, value):
  1574. return 0 # dummy value
  1575. # The FDSelect glyph data is written out to XML in the charstring keys,
  1576. # so we write out only the format selector
  1577. def xmlWrite(self, xmlWriter, name, value):
  1578. xmlWriter.simpletag(name, [("format", value.format)])
  1579. xmlWriter.newline()
  1580. def xmlRead(self, name, attrs, content, parent):
  1581. fmt = safeEval(attrs["format"])
  1582. file = None
  1583. numGlyphs = None
  1584. fdSelect = FDSelect(file, numGlyphs, fmt)
  1585. return fdSelect
  1586. class VarStoreConverter(SimpleConverter):
  1587. def _read(self, parent, value):
  1588. file = parent.file
  1589. file.seek(value)
  1590. varStore = VarStoreData(file)
  1591. varStore.decompile()
  1592. return varStore
  1593. def write(self, parent, value):
  1594. return 0 # dummy value
  1595. def xmlWrite(self, xmlWriter, name, value):
  1596. value.writeXML(xmlWriter, name)
  1597. def xmlRead(self, name, attrs, content, parent):
  1598. varStore = VarStoreData()
  1599. varStore.xmlRead(name, attrs, content, parent)
  1600. return varStore
  1601. def packFDSelect0(fdSelectArray):
  1602. fmt = 0
  1603. data = [packCard8(fmt)]
  1604. for index in fdSelectArray:
  1605. data.append(packCard8(index))
  1606. return bytesjoin(data)
  1607. def packFDSelect3(fdSelectArray):
  1608. fmt = 3
  1609. fdRanges = []
  1610. lenArray = len(fdSelectArray)
  1611. lastFDIndex = -1
  1612. for i in range(lenArray):
  1613. fdIndex = fdSelectArray[i]
  1614. if lastFDIndex != fdIndex:
  1615. fdRanges.append([i, fdIndex])
  1616. lastFDIndex = fdIndex
  1617. sentinelGID = i + 1
  1618. data = [packCard8(fmt)]
  1619. data.append(packCard16(len(fdRanges)))
  1620. for fdRange in fdRanges:
  1621. data.append(packCard16(fdRange[0]))
  1622. data.append(packCard8(fdRange[1]))
  1623. data.append(packCard16(sentinelGID))
  1624. return bytesjoin(data)
  1625. def packFDSelect4(fdSelectArray):
  1626. fmt = 4
  1627. fdRanges = []
  1628. lenArray = len(fdSelectArray)
  1629. lastFDIndex = -1
  1630. for i in range(lenArray):
  1631. fdIndex = fdSelectArray[i]
  1632. if lastFDIndex != fdIndex:
  1633. fdRanges.append([i, fdIndex])
  1634. lastFDIndex = fdIndex
  1635. sentinelGID = i + 1
  1636. data = [packCard8(fmt)]
  1637. data.append(packCard32(len(fdRanges)))
  1638. for fdRange in fdRanges:
  1639. data.append(packCard32(fdRange[0]))
  1640. data.append(packCard16(fdRange[1]))
  1641. data.append(packCard32(sentinelGID))
  1642. return bytesjoin(data)
  1643. class FDSelectCompiler(object):
  1644. def __init__(self, fdSelect, parent):
  1645. fmt = fdSelect.format
  1646. fdSelectArray = fdSelect.gidArray
  1647. if fmt == 0:
  1648. self.data = packFDSelect0(fdSelectArray)
  1649. elif fmt == 3:
  1650. self.data = packFDSelect3(fdSelectArray)
  1651. elif fmt == 4:
  1652. self.data = packFDSelect4(fdSelectArray)
  1653. else:
  1654. # choose smaller of the two formats
  1655. data0 = packFDSelect0(fdSelectArray)
  1656. data3 = packFDSelect3(fdSelectArray)
  1657. if len(data0) < len(data3):
  1658. self.data = data0
  1659. fdSelect.format = 0
  1660. else:
  1661. self.data = data3
  1662. fdSelect.format = 3
  1663. self.parent = parent
  1664. def setPos(self, pos, endPos):
  1665. self.parent.rawDict["FDSelect"] = pos
  1666. def getDataLength(self):
  1667. return len(self.data)
  1668. def toFile(self, file):
  1669. file.write(self.data)
  1670. class VarStoreCompiler(object):
  1671. def __init__(self, varStoreData, parent):
  1672. self.parent = parent
  1673. if not varStoreData.data:
  1674. varStoreData.compile()
  1675. varStoreDataLen = min(0xFFFF, len(varStoreData.data))
  1676. data = [packCard16(varStoreDataLen), varStoreData.data]
  1677. self.data = bytesjoin(data)
  1678. def setPos(self, pos, endPos):
  1679. self.parent.rawDict["VarStore"] = pos
  1680. def getDataLength(self):
  1681. return len(self.data)
  1682. def toFile(self, file):
  1683. file.write(self.data)
  1684. class ROSConverter(SimpleConverter):
  1685. def xmlWrite(self, xmlWriter, name, value):
  1686. registry, order, supplement = value
  1687. xmlWriter.simpletag(
  1688. name,
  1689. [
  1690. ("Registry", tostr(registry)),
  1691. ("Order", tostr(order)),
  1692. ("Supplement", supplement),
  1693. ],
  1694. )
  1695. xmlWriter.newline()
  1696. def xmlRead(self, name, attrs, content, parent):
  1697. return (attrs["Registry"], attrs["Order"], safeEval(attrs["Supplement"]))
  1698. topDictOperators = [
  1699. # opcode name argument type default converter
  1700. (25, "maxstack", "number", None, None),
  1701. ((12, 30), "ROS", ("SID", "SID", "number"), None, ROSConverter()),
  1702. ((12, 20), "SyntheticBase", "number", None, None),
  1703. (0, "version", "SID", None, None),
  1704. (1, "Notice", "SID", None, Latin1Converter()),
  1705. ((12, 0), "Copyright", "SID", None, Latin1Converter()),
  1706. (2, "FullName", "SID", None, Latin1Converter()),
  1707. ((12, 38), "FontName", "SID", None, Latin1Converter()),
  1708. (3, "FamilyName", "SID", None, Latin1Converter()),
  1709. (4, "Weight", "SID", None, None),
  1710. ((12, 1), "isFixedPitch", "number", 0, None),
  1711. ((12, 2), "ItalicAngle", "number", 0, None),
  1712. ((12, 3), "UnderlinePosition", "number", -100, None),
  1713. ((12, 4), "UnderlineThickness", "number", 50, None),
  1714. ((12, 5), "PaintType", "number", 0, None),
  1715. ((12, 6), "CharstringType", "number", 2, None),
  1716. ((12, 7), "FontMatrix", "array", [0.001, 0, 0, 0.001, 0, 0], None),
  1717. (13, "UniqueID", "number", None, None),
  1718. (5, "FontBBox", "array", [0, 0, 0, 0], None),
  1719. ((12, 8), "StrokeWidth", "number", 0, None),
  1720. (14, "XUID", "array", None, None),
  1721. ((12, 21), "PostScript", "SID", None, None),
  1722. ((12, 22), "BaseFontName", "SID", None, None),
  1723. ((12, 23), "BaseFontBlend", "delta", None, None),
  1724. ((12, 31), "CIDFontVersion", "number", 0, None),
  1725. ((12, 32), "CIDFontRevision", "number", 0, None),
  1726. ((12, 33), "CIDFontType", "number", 0, None),
  1727. ((12, 34), "CIDCount", "number", 8720, None),
  1728. (15, "charset", "number", None, CharsetConverter()),
  1729. ((12, 35), "UIDBase", "number", None, None),
  1730. (16, "Encoding", "number", 0, EncodingConverter()),
  1731. (18, "Private", ("number", "number"), None, PrivateDictConverter()),
  1732. ((12, 37), "FDSelect", "number", None, FDSelectConverter()),
  1733. ((12, 36), "FDArray", "number", None, FDArrayConverter()),
  1734. (17, "CharStrings", "number", None, CharStringsConverter()),
  1735. (24, "VarStore", "number", None, VarStoreConverter()),
  1736. ]
  1737. topDictOperators2 = [
  1738. # opcode name argument type default converter
  1739. (25, "maxstack", "number", None, None),
  1740. ((12, 7), "FontMatrix", "array", [0.001, 0, 0, 0.001, 0, 0], None),
  1741. ((12, 37), "FDSelect", "number", None, FDSelectConverter()),
  1742. ((12, 36), "FDArray", "number", None, FDArrayConverter()),
  1743. (17, "CharStrings", "number", None, CharStringsConverter()),
  1744. (24, "VarStore", "number", None, VarStoreConverter()),
  1745. ]
  1746. # Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order,
  1747. # in order for the font to compile back from xml.
  1748. kBlendDictOpName = "blend"
  1749. blendOp = 23
  1750. privateDictOperators = [
  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, 14), "ForceBold", "number", 0, None),
  1772. ((12, 15), "ForceBoldThreshold", "number", None, None), # deprecated
  1773. ((12, 16), "lenIV", "number", None, None), # deprecated
  1774. ((12, 17), "LanguageGroup", "number", 0, None),
  1775. ((12, 18), "ExpansionFactor", "number", 0.06, None),
  1776. ((12, 19), "initialRandomSeed", "number", 0, None),
  1777. (20, "defaultWidthX", "number", 0, None),
  1778. (21, "nominalWidthX", "number", 0, None),
  1779. (19, "Subrs", "number", None, SubrsConverter()),
  1780. ]
  1781. privateDictOperators2 = [
  1782. # opcode name argument type default converter
  1783. (22, "vsindex", "number", None, None),
  1784. (
  1785. blendOp,
  1786. kBlendDictOpName,
  1787. "blendList",
  1788. None,
  1789. None,
  1790. ), # This is for reading to/from XML: it not written to CFF.
  1791. (6, "BlueValues", "delta", None, None),
  1792. (7, "OtherBlues", "delta", None, None),
  1793. (8, "FamilyBlues", "delta", None, None),
  1794. (9, "FamilyOtherBlues", "delta", None, None),
  1795. ((12, 9), "BlueScale", "number", 0.039625, None),
  1796. ((12, 10), "BlueShift", "number", 7, None),
  1797. ((12, 11), "BlueFuzz", "number", 1, None),
  1798. (10, "StdHW", "number", None, None),
  1799. (11, "StdVW", "number", None, None),
  1800. ((12, 12), "StemSnapH", "delta", None, None),
  1801. ((12, 13), "StemSnapV", "delta", None, None),
  1802. ((12, 17), "LanguageGroup", "number", 0, None),
  1803. ((12, 18), "ExpansionFactor", "number", 0.06, None),
  1804. (19, "Subrs", "number", None, SubrsConverter()),
  1805. ]
  1806. def addConverters(table):
  1807. for i in range(len(table)):
  1808. op, name, arg, default, conv = table[i]
  1809. if conv is not None:
  1810. continue
  1811. if arg in ("delta", "array"):
  1812. conv = ArrayConverter()
  1813. elif arg == "number":
  1814. conv = NumberConverter()
  1815. elif arg == "SID":
  1816. conv = ASCIIConverter()
  1817. elif arg == "blendList":
  1818. conv = None
  1819. else:
  1820. assert False
  1821. table[i] = op, name, arg, default, conv
  1822. addConverters(privateDictOperators)
  1823. addConverters(topDictOperators)
  1824. class TopDictDecompiler(psCharStrings.DictDecompiler):
  1825. operators = buildOperatorDict(topDictOperators)
  1826. class PrivateDictDecompiler(psCharStrings.DictDecompiler):
  1827. operators = buildOperatorDict(privateDictOperators)
  1828. class DictCompiler(object):
  1829. maxBlendStack = 0
  1830. def __init__(self, dictObj, strings, parent, isCFF2=None):
  1831. if strings:
  1832. assert isinstance(strings, IndexedStrings)
  1833. if isCFF2 is None and hasattr(parent, "isCFF2"):
  1834. isCFF2 = parent.isCFF2
  1835. assert isCFF2 is not None
  1836. self.isCFF2 = isCFF2
  1837. self.dictObj = dictObj
  1838. self.strings = strings
  1839. self.parent = parent
  1840. rawDict = {}
  1841. for name in dictObj.order:
  1842. value = getattr(dictObj, name, None)
  1843. if value is None:
  1844. continue
  1845. conv = dictObj.converters[name]
  1846. value = conv.write(dictObj, value)
  1847. if value == dictObj.defaults.get(name):
  1848. continue
  1849. rawDict[name] = value
  1850. self.rawDict = rawDict
  1851. def setPos(self, pos, endPos):
  1852. pass
  1853. def getDataLength(self):
  1854. return len(self.compile("getDataLength"))
  1855. def compile(self, reason):
  1856. log.log(DEBUG, "-- compiling %s for %s", self.__class__.__name__, reason)
  1857. rawDict = self.rawDict
  1858. data = []
  1859. for name in self.dictObj.order:
  1860. value = rawDict.get(name)
  1861. if value is None:
  1862. continue
  1863. op, argType = self.opcodes[name]
  1864. if isinstance(argType, tuple):
  1865. l = len(argType)
  1866. assert len(value) == l, "value doesn't match arg type"
  1867. for i in range(l):
  1868. arg = argType[i]
  1869. v = value[i]
  1870. arghandler = getattr(self, "arg_" + arg)
  1871. data.append(arghandler(v))
  1872. else:
  1873. arghandler = getattr(self, "arg_" + argType)
  1874. data.append(arghandler(value))
  1875. data.append(op)
  1876. data = bytesjoin(data)
  1877. return data
  1878. def toFile(self, file):
  1879. data = self.compile("toFile")
  1880. file.write(data)
  1881. def arg_number(self, num):
  1882. if isinstance(num, list):
  1883. data = [encodeNumber(val) for val in num]
  1884. data.append(encodeNumber(1))
  1885. data.append(bytechr(blendOp))
  1886. datum = bytesjoin(data)
  1887. else:
  1888. datum = encodeNumber(num)
  1889. return datum
  1890. def arg_SID(self, s):
  1891. return psCharStrings.encodeIntCFF(self.strings.getSID(s))
  1892. def arg_array(self, value):
  1893. data = []
  1894. for num in value:
  1895. data.append(self.arg_number(num))
  1896. return bytesjoin(data)
  1897. def arg_delta(self, value):
  1898. if not value:
  1899. return b""
  1900. val0 = value[0]
  1901. if isinstance(val0, list):
  1902. data = self.arg_delta_blend(value)
  1903. else:
  1904. out = []
  1905. last = 0
  1906. for v in value:
  1907. out.append(v - last)
  1908. last = v
  1909. data = []
  1910. for num in out:
  1911. data.append(encodeNumber(num))
  1912. return bytesjoin(data)
  1913. def arg_delta_blend(self, value):
  1914. """A delta list with blend lists has to be *all* blend lists.
  1915. The value is a list is arranged as follows::
  1916. [
  1917. [V0, d0..dn]
  1918. [V1, d0..dn]
  1919. ...
  1920. [Vm, d0..dn]
  1921. ]
  1922. ``V`` is the absolute coordinate value from the default font, and ``d0-dn``
  1923. are the delta values from the *n* regions. Each ``V`` is an absolute
  1924. coordinate from the default font.
  1925. We want to return a list::
  1926. [
  1927. [v0, v1..vm]
  1928. [d0..dn]
  1929. ...
  1930. [d0..dn]
  1931. numBlends
  1932. blendOp
  1933. ]
  1934. where each ``v`` is relative to the previous default font value.
  1935. """
  1936. numMasters = len(value[0])
  1937. numBlends = len(value)
  1938. numStack = (numBlends * numMasters) + 1
  1939. if numStack > self.maxBlendStack:
  1940. # Figure out the max number of value we can blend
  1941. # and divide this list up into chunks of that size.
  1942. numBlendValues = int((self.maxBlendStack - 1) / numMasters)
  1943. out = []
  1944. while True:
  1945. numVal = min(len(value), numBlendValues)
  1946. if numVal == 0:
  1947. break
  1948. valList = value[0:numVal]
  1949. out1 = self.arg_delta_blend(valList)
  1950. out.extend(out1)
  1951. value = value[numVal:]
  1952. else:
  1953. firstList = [0] * numBlends
  1954. deltaList = [None] * numBlends
  1955. i = 0
  1956. prevVal = 0
  1957. while i < numBlends:
  1958. # For PrivateDict BlueValues, the default font
  1959. # values are absolute, not relative.
  1960. # Must convert these back to relative coordinates
  1961. # before writing to CFF2.
  1962. defaultValue = value[i][0]
  1963. firstList[i] = defaultValue - prevVal
  1964. prevVal = defaultValue
  1965. deltaList[i] = value[i][1:]
  1966. i += 1
  1967. relValueList = firstList
  1968. for blendList in deltaList:
  1969. relValueList.extend(blendList)
  1970. out = [encodeNumber(val) for val in relValueList]
  1971. out.append(encodeNumber(numBlends))
  1972. out.append(bytechr(blendOp))
  1973. return out
  1974. def encodeNumber(num):
  1975. if isinstance(num, float):
  1976. return psCharStrings.encodeFloat(num)
  1977. else:
  1978. return psCharStrings.encodeIntCFF(num)
  1979. class TopDictCompiler(DictCompiler):
  1980. opcodes = buildOpcodeDict(topDictOperators)
  1981. def getChildren(self, strings):
  1982. isCFF2 = self.isCFF2
  1983. children = []
  1984. if self.dictObj.cff2GetGlyphOrder is None:
  1985. if hasattr(self.dictObj, "charset") and self.dictObj.charset:
  1986. if hasattr(self.dictObj, "ROS"): # aka isCID
  1987. charsetCode = None
  1988. else:
  1989. charsetCode = getStdCharSet(self.dictObj.charset)
  1990. if charsetCode is None:
  1991. children.append(
  1992. CharsetCompiler(strings, self.dictObj.charset, self)
  1993. )
  1994. else:
  1995. self.rawDict["charset"] = charsetCode
  1996. if hasattr(self.dictObj, "Encoding") and self.dictObj.Encoding:
  1997. encoding = self.dictObj.Encoding
  1998. if not isinstance(encoding, str):
  1999. children.append(EncodingCompiler(strings, encoding, self))
  2000. else:
  2001. if hasattr(self.dictObj, "VarStore"):
  2002. varStoreData = self.dictObj.VarStore
  2003. varStoreComp = VarStoreCompiler(varStoreData, self)
  2004. children.append(varStoreComp)
  2005. if hasattr(self.dictObj, "FDSelect"):
  2006. # I have not yet supported merging a ttx CFF-CID font, as there are
  2007. # interesting issues about merging the FDArrays. Here I assume that
  2008. # either the font was read from XML, and the FDSelect indices are all
  2009. # in the charstring data, or the FDSelect array is already fully defined.
  2010. fdSelect = self.dictObj.FDSelect
  2011. # probably read in from XML; assume fdIndex in CharString data
  2012. if len(fdSelect) == 0:
  2013. charStrings = self.dictObj.CharStrings
  2014. for name in self.dictObj.charset:
  2015. fdSelect.append(charStrings[name].fdSelectIndex)
  2016. fdSelectComp = FDSelectCompiler(fdSelect, self)
  2017. children.append(fdSelectComp)
  2018. if hasattr(self.dictObj, "CharStrings"):
  2019. items = []
  2020. charStrings = self.dictObj.CharStrings
  2021. for name in self.dictObj.charset:
  2022. items.append(charStrings[name])
  2023. charStringsComp = CharStringsCompiler(items, strings, self, isCFF2=isCFF2)
  2024. children.append(charStringsComp)
  2025. if hasattr(self.dictObj, "FDArray"):
  2026. # I have not yet supported merging a ttx CFF-CID font, as there are
  2027. # interesting issues about merging the FDArrays. Here I assume that the
  2028. # FDArray info is correct and complete.
  2029. fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self)
  2030. children.append(fdArrayIndexComp)
  2031. children.extend(fdArrayIndexComp.getChildren(strings))
  2032. if hasattr(self.dictObj, "Private"):
  2033. privComp = self.dictObj.Private.getCompiler(strings, self)
  2034. children.append(privComp)
  2035. children.extend(privComp.getChildren(strings))
  2036. return children
  2037. class FontDictCompiler(DictCompiler):
  2038. opcodes = buildOpcodeDict(topDictOperators)
  2039. def __init__(self, dictObj, strings, parent, isCFF2=None):
  2040. super(FontDictCompiler, self).__init__(dictObj, strings, parent, isCFF2=isCFF2)
  2041. #
  2042. # We now take some effort to detect if there were any key/value pairs
  2043. # supplied that were ignored in the FontDict context, and issue a warning
  2044. # for those cases.
  2045. #
  2046. ignoredNames = []
  2047. dictObj = self.dictObj
  2048. for name in sorted(set(dictObj.converters) - set(dictObj.order)):
  2049. if name in dictObj.rawDict:
  2050. # The font was directly read from binary. In this
  2051. # case, we want to report *all* "useless" key/value
  2052. # pairs that are in the font, not just the ones that
  2053. # are different from the default.
  2054. ignoredNames.append(name)
  2055. else:
  2056. # The font was probably read from a TTX file. We only
  2057. # warn about keys whos value is not the default. The
  2058. # ones that have the default value will not be written
  2059. # to binary anyway.
  2060. default = dictObj.defaults.get(name)
  2061. if default is not None:
  2062. conv = dictObj.converters[name]
  2063. default = conv.read(dictObj, default)
  2064. if getattr(dictObj, name, None) != default:
  2065. ignoredNames.append(name)
  2066. if ignoredNames:
  2067. log.warning(
  2068. "Some CFF FDArray/FontDict keys were ignored upon compile: "
  2069. + " ".join(sorted(ignoredNames))
  2070. )
  2071. def getChildren(self, strings):
  2072. children = []
  2073. if hasattr(self.dictObj, "Private"):
  2074. privComp = self.dictObj.Private.getCompiler(strings, self)
  2075. children.append(privComp)
  2076. children.extend(privComp.getChildren(strings))
  2077. return children
  2078. class PrivateDictCompiler(DictCompiler):
  2079. maxBlendStack = maxStackLimit
  2080. opcodes = buildOpcodeDict(privateDictOperators)
  2081. def setPos(self, pos, endPos):
  2082. size = endPos - pos
  2083. self.parent.rawDict["Private"] = size, pos
  2084. self.pos = pos
  2085. def getChildren(self, strings):
  2086. children = []
  2087. if hasattr(self.dictObj, "Subrs"):
  2088. children.append(self.dictObj.Subrs.getCompiler(strings, self))
  2089. return children
  2090. class BaseDict(object):
  2091. def __init__(self, strings=None, file=None, offset=None, isCFF2=None):
  2092. assert (isCFF2 is None) == (file is None)
  2093. self.rawDict = {}
  2094. self.skipNames = []
  2095. self.strings = strings
  2096. if file is None:
  2097. return
  2098. self._isCFF2 = isCFF2
  2099. self.file = file
  2100. if offset is not None:
  2101. log.log(DEBUG, "loading %s at %s", self.__class__.__name__, offset)
  2102. self.offset = offset
  2103. def decompile(self, data):
  2104. log.log(DEBUG, " length %s is %d", self.__class__.__name__, len(data))
  2105. dec = self.decompilerClass(self.strings, self)
  2106. dec.decompile(data)
  2107. self.rawDict = dec.getDict()
  2108. self.postDecompile()
  2109. def postDecompile(self):
  2110. pass
  2111. def getCompiler(self, strings, parent, isCFF2=None):
  2112. return self.compilerClass(self, strings, parent, isCFF2=isCFF2)
  2113. def __getattr__(self, name):
  2114. if name[:2] == name[-2:] == "__":
  2115. # to make deepcopy() and pickle.load() work, we need to signal with
  2116. # AttributeError that dunder methods like '__deepcopy__' or '__getstate__'
  2117. # aren't implemented. For more details, see:
  2118. # https://github.com/fonttools/fonttools/pull/1488
  2119. raise AttributeError(name)
  2120. value = self.rawDict.get(name, None)
  2121. if value is None:
  2122. value = self.defaults.get(name)
  2123. if value is None:
  2124. raise AttributeError(name)
  2125. conv = self.converters[name]
  2126. value = conv.read(self, value)
  2127. setattr(self, name, value)
  2128. return value
  2129. def toXML(self, xmlWriter):
  2130. for name in self.order:
  2131. if name in self.skipNames:
  2132. continue
  2133. value = getattr(self, name, None)
  2134. # XXX For "charset" we never skip calling xmlWrite even if the
  2135. # value is None, so we always write the following XML comment:
  2136. #
  2137. # <!-- charset is dumped separately as the 'GlyphOrder' element -->
  2138. #
  2139. # Charset is None when 'CFF ' table is imported from XML into an
  2140. # empty TTFont(). By writing this comment all the time, we obtain
  2141. # the same XML output whether roundtripping XML-to-XML or
  2142. # dumping binary-to-XML
  2143. if value is None and name != "charset":
  2144. continue
  2145. conv = self.converters[name]
  2146. conv.xmlWrite(xmlWriter, name, value)
  2147. ignoredNames = set(self.rawDict) - set(self.order)
  2148. if ignoredNames:
  2149. xmlWriter.comment(
  2150. "some keys were ignored: %s" % " ".join(sorted(ignoredNames))
  2151. )
  2152. xmlWriter.newline()
  2153. def fromXML(self, name, attrs, content):
  2154. conv = self.converters[name]
  2155. value = conv.xmlRead(name, attrs, content, self)
  2156. setattr(self, name, value)
  2157. class TopDict(BaseDict):
  2158. """The ``TopDict`` represents the top-level dictionary holding font
  2159. information. CFF2 tables contain a restricted set of top-level entries
  2160. as described `here <https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#7-top-dict-data>`_,
  2161. but CFF tables may contain a wider range of information. This information
  2162. can be accessed through attributes or through the dictionary returned
  2163. through the ``rawDict`` property:
  2164. .. code:: python
  2165. font = tt["CFF "].cff[0]
  2166. font.FamilyName
  2167. # 'Linux Libertine O'
  2168. font.rawDict["FamilyName"]
  2169. # 'Linux Libertine O'
  2170. More information is available in the CFF file's private dictionary, accessed
  2171. via the ``Private`` property:
  2172. .. code:: python
  2173. tt["CFF "].cff[0].Private.BlueValues
  2174. # [-15, 0, 515, 515, 666, 666]
  2175. """
  2176. defaults = buildDefaults(topDictOperators)
  2177. converters = buildConverters(topDictOperators)
  2178. compilerClass = TopDictCompiler
  2179. order = buildOrder(topDictOperators)
  2180. decompilerClass = TopDictDecompiler
  2181. def __init__(
  2182. self,
  2183. strings=None,
  2184. file=None,
  2185. offset=None,
  2186. GlobalSubrs=None,
  2187. cff2GetGlyphOrder=None,
  2188. isCFF2=None,
  2189. ):
  2190. super(TopDict, self).__init__(strings, file, offset, isCFF2=isCFF2)
  2191. self.cff2GetGlyphOrder = cff2GetGlyphOrder
  2192. self.GlobalSubrs = GlobalSubrs
  2193. if isCFF2:
  2194. self.defaults = buildDefaults(topDictOperators2)
  2195. self.charset = cff2GetGlyphOrder()
  2196. self.order = buildOrder(topDictOperators2)
  2197. else:
  2198. self.defaults = buildDefaults(topDictOperators)
  2199. self.order = buildOrder(topDictOperators)
  2200. def getGlyphOrder(self):
  2201. """Returns a list of glyph names in the CFF font."""
  2202. return self.charset
  2203. def postDecompile(self):
  2204. offset = self.rawDict.get("CharStrings")
  2205. if offset is None:
  2206. return
  2207. # get the number of glyphs beforehand.
  2208. self.file.seek(offset)
  2209. if self._isCFF2:
  2210. self.numGlyphs = readCard32(self.file)
  2211. else:
  2212. self.numGlyphs = readCard16(self.file)
  2213. def toXML(self, xmlWriter):
  2214. if hasattr(self, "CharStrings"):
  2215. self.decompileAllCharStrings()
  2216. if hasattr(self, "ROS"):
  2217. self.skipNames = ["Encoding"]
  2218. if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
  2219. # these values have default values, but I only want them to show up
  2220. # in CID fonts.
  2221. self.skipNames = [
  2222. "CIDFontVersion",
  2223. "CIDFontRevision",
  2224. "CIDFontType",
  2225. "CIDCount",
  2226. ]
  2227. BaseDict.toXML(self, xmlWriter)
  2228. def decompileAllCharStrings(self):
  2229. # Make sure that all the Private Dicts have been instantiated.
  2230. for i, charString in enumerate(self.CharStrings.values()):
  2231. try:
  2232. charString.decompile()
  2233. except:
  2234. log.error("Error in charstring %s", i)
  2235. raise
  2236. def recalcFontBBox(self):
  2237. fontBBox = None
  2238. for charString in self.CharStrings.values():
  2239. bounds = charString.calcBounds(self.CharStrings)
  2240. if bounds is not None:
  2241. if fontBBox is not None:
  2242. fontBBox = unionRect(fontBBox, bounds)
  2243. else:
  2244. fontBBox = bounds
  2245. if fontBBox is None:
  2246. self.FontBBox = self.defaults["FontBBox"][:]
  2247. else:
  2248. self.FontBBox = list(intRect(fontBBox))
  2249. class FontDict(BaseDict):
  2250. #
  2251. # Since fonttools used to pass a lot of fields that are not relevant in the FDArray
  2252. # FontDict, there are 'ttx' files in the wild that contain all these. These got in
  2253. # the ttx files because fonttools writes explicit values for all the TopDict default
  2254. # values. These are not actually illegal in the context of an FDArray FontDict - you
  2255. # can legally, per spec, put any arbitrary key/value pair in a FontDict - but are
  2256. # useless since current major company CFF interpreters ignore anything but the set
  2257. # listed in this file. So, we just silently skip them. An exception is Weight: this
  2258. # is not used by any interpreter, but some foundries have asked that this be
  2259. # supported in FDArray FontDicts just to preserve information about the design when
  2260. # the font is being inspected.
  2261. #
  2262. # On top of that, there are fonts out there that contain such useless FontDict values.
  2263. #
  2264. # By subclassing TopDict, we *allow* all key/values from TopDict, both when reading
  2265. # from binary or when reading from XML, but by overriding `order` with a limited
  2266. # list of names, we ensure that only the useful names ever get exported to XML and
  2267. # ever get compiled into the binary font.
  2268. #
  2269. # We override compilerClass so we can warn about "useless" key/value pairs, either
  2270. # from the original binary font or from TTX input.
  2271. #
  2272. # See:
  2273. # - https://github.com/fonttools/fonttools/issues/740
  2274. # - https://github.com/fonttools/fonttools/issues/601
  2275. # - https://github.com/adobe-type-tools/afdko/issues/137
  2276. #
  2277. defaults = {}
  2278. converters = buildConverters(topDictOperators)
  2279. compilerClass = FontDictCompiler
  2280. orderCFF = ["FontName", "FontMatrix", "Weight", "Private"]
  2281. orderCFF2 = ["Private"]
  2282. decompilerClass = TopDictDecompiler
  2283. def __init__(
  2284. self,
  2285. strings=None,
  2286. file=None,
  2287. offset=None,
  2288. GlobalSubrs=None,
  2289. isCFF2=None,
  2290. vstore=None,
  2291. ):
  2292. super(FontDict, self).__init__(strings, file, offset, isCFF2=isCFF2)
  2293. self.vstore = vstore
  2294. self.setCFF2(isCFF2)
  2295. def setCFF2(self, isCFF2):
  2296. # isCFF2 may be None.
  2297. if isCFF2:
  2298. self.order = self.orderCFF2
  2299. self._isCFF2 = True
  2300. else:
  2301. self.order = self.orderCFF
  2302. self._isCFF2 = False
  2303. class PrivateDict(BaseDict):
  2304. defaults = buildDefaults(privateDictOperators)
  2305. converters = buildConverters(privateDictOperators)
  2306. order = buildOrder(privateDictOperators)
  2307. decompilerClass = PrivateDictDecompiler
  2308. compilerClass = PrivateDictCompiler
  2309. def __init__(self, strings=None, file=None, offset=None, isCFF2=None, vstore=None):
  2310. super(PrivateDict, self).__init__(strings, file, offset, isCFF2=isCFF2)
  2311. self.vstore = vstore
  2312. if isCFF2:
  2313. self.defaults = buildDefaults(privateDictOperators2)
  2314. self.order = buildOrder(privateDictOperators2)
  2315. # Provide dummy values. This avoids needing to provide
  2316. # an isCFF2 state in a lot of places.
  2317. self.nominalWidthX = self.defaultWidthX = None
  2318. self._isCFF2 = True
  2319. else:
  2320. self.defaults = buildDefaults(privateDictOperators)
  2321. self.order = buildOrder(privateDictOperators)
  2322. self._isCFF2 = False
  2323. @property
  2324. def in_cff2(self):
  2325. return self._isCFF2
  2326. def getNumRegions(self, vi=None): # called from misc/psCharStrings.py
  2327. # if getNumRegions is being called, we can assume that VarStore exists.
  2328. if vi is None:
  2329. if hasattr(self, "vsindex"):
  2330. vi = self.vsindex
  2331. else:
  2332. vi = 0
  2333. numRegions = self.vstore.getNumRegions(vi)
  2334. return numRegions
  2335. class IndexedStrings(object):
  2336. """SID -> string mapping."""
  2337. def __init__(self, file=None):
  2338. if file is None:
  2339. strings = []
  2340. else:
  2341. strings = [tostr(s, encoding="latin1") for s in Index(file, isCFF2=False)]
  2342. self.strings = strings
  2343. def getCompiler(self):
  2344. return IndexedStringsCompiler(self, None, self, isCFF2=False)
  2345. def __len__(self):
  2346. return len(self.strings)
  2347. def __getitem__(self, SID):
  2348. if SID < cffStandardStringCount:
  2349. return cffStandardStrings[SID]
  2350. else:
  2351. return self.strings[SID - cffStandardStringCount]
  2352. def getSID(self, s):
  2353. if not hasattr(self, "stringMapping"):
  2354. self.buildStringMapping()
  2355. s = tostr(s, encoding="latin1")
  2356. if s in cffStandardStringMapping:
  2357. SID = cffStandardStringMapping[s]
  2358. elif s in self.stringMapping:
  2359. SID = self.stringMapping[s]
  2360. else:
  2361. SID = len(self.strings) + cffStandardStringCount
  2362. self.strings.append(s)
  2363. self.stringMapping[s] = SID
  2364. return SID
  2365. def getStrings(self):
  2366. return self.strings
  2367. def buildStringMapping(self):
  2368. self.stringMapping = {}
  2369. for index in range(len(self.strings)):
  2370. self.stringMapping[self.strings[index]] = index + cffStandardStringCount
  2371. # The 391 Standard Strings as used in the CFF format.
  2372. # from Adobe Technical None #5176, version 1.0, 18 March 1998
  2373. cffStandardStrings = [
  2374. ".notdef",
  2375. "space",
  2376. "exclam",
  2377. "quotedbl",
  2378. "numbersign",
  2379. "dollar",
  2380. "percent",
  2381. "ampersand",
  2382. "quoteright",
  2383. "parenleft",
  2384. "parenright",
  2385. "asterisk",
  2386. "plus",
  2387. "comma",
  2388. "hyphen",
  2389. "period",
  2390. "slash",
  2391. "zero",
  2392. "one",
  2393. "two",
  2394. "three",
  2395. "four",
  2396. "five",
  2397. "six",
  2398. "seven",
  2399. "eight",
  2400. "nine",
  2401. "colon",
  2402. "semicolon",
  2403. "less",
  2404. "equal",
  2405. "greater",
  2406. "question",
  2407. "at",
  2408. "A",
  2409. "B",
  2410. "C",
  2411. "D",
  2412. "E",
  2413. "F",
  2414. "G",
  2415. "H",
  2416. "I",
  2417. "J",
  2418. "K",
  2419. "L",
  2420. "M",
  2421. "N",
  2422. "O",
  2423. "P",
  2424. "Q",
  2425. "R",
  2426. "S",
  2427. "T",
  2428. "U",
  2429. "V",
  2430. "W",
  2431. "X",
  2432. "Y",
  2433. "Z",
  2434. "bracketleft",
  2435. "backslash",
  2436. "bracketright",
  2437. "asciicircum",
  2438. "underscore",
  2439. "quoteleft",
  2440. "a",
  2441. "b",
  2442. "c",
  2443. "d",
  2444. "e",
  2445. "f",
  2446. "g",
  2447. "h",
  2448. "i",
  2449. "j",
  2450. "k",
  2451. "l",
  2452. "m",
  2453. "n",
  2454. "o",
  2455. "p",
  2456. "q",
  2457. "r",
  2458. "s",
  2459. "t",
  2460. "u",
  2461. "v",
  2462. "w",
  2463. "x",
  2464. "y",
  2465. "z",
  2466. "braceleft",
  2467. "bar",
  2468. "braceright",
  2469. "asciitilde",
  2470. "exclamdown",
  2471. "cent",
  2472. "sterling",
  2473. "fraction",
  2474. "yen",
  2475. "florin",
  2476. "section",
  2477. "currency",
  2478. "quotesingle",
  2479. "quotedblleft",
  2480. "guillemotleft",
  2481. "guilsinglleft",
  2482. "guilsinglright",
  2483. "fi",
  2484. "fl",
  2485. "endash",
  2486. "dagger",
  2487. "daggerdbl",
  2488. "periodcentered",
  2489. "paragraph",
  2490. "bullet",
  2491. "quotesinglbase",
  2492. "quotedblbase",
  2493. "quotedblright",
  2494. "guillemotright",
  2495. "ellipsis",
  2496. "perthousand",
  2497. "questiondown",
  2498. "grave",
  2499. "acute",
  2500. "circumflex",
  2501. "tilde",
  2502. "macron",
  2503. "breve",
  2504. "dotaccent",
  2505. "dieresis",
  2506. "ring",
  2507. "cedilla",
  2508. "hungarumlaut",
  2509. "ogonek",
  2510. "caron",
  2511. "emdash",
  2512. "AE",
  2513. "ordfeminine",
  2514. "Lslash",
  2515. "Oslash",
  2516. "OE",
  2517. "ordmasculine",
  2518. "ae",
  2519. "dotlessi",
  2520. "lslash",
  2521. "oslash",
  2522. "oe",
  2523. "germandbls",
  2524. "onesuperior",
  2525. "logicalnot",
  2526. "mu",
  2527. "trademark",
  2528. "Eth",
  2529. "onehalf",
  2530. "plusminus",
  2531. "Thorn",
  2532. "onequarter",
  2533. "divide",
  2534. "brokenbar",
  2535. "degree",
  2536. "thorn",
  2537. "threequarters",
  2538. "twosuperior",
  2539. "registered",
  2540. "minus",
  2541. "eth",
  2542. "multiply",
  2543. "threesuperior",
  2544. "copyright",
  2545. "Aacute",
  2546. "Acircumflex",
  2547. "Adieresis",
  2548. "Agrave",
  2549. "Aring",
  2550. "Atilde",
  2551. "Ccedilla",
  2552. "Eacute",
  2553. "Ecircumflex",
  2554. "Edieresis",
  2555. "Egrave",
  2556. "Iacute",
  2557. "Icircumflex",
  2558. "Idieresis",
  2559. "Igrave",
  2560. "Ntilde",
  2561. "Oacute",
  2562. "Ocircumflex",
  2563. "Odieresis",
  2564. "Ograve",
  2565. "Otilde",
  2566. "Scaron",
  2567. "Uacute",
  2568. "Ucircumflex",
  2569. "Udieresis",
  2570. "Ugrave",
  2571. "Yacute",
  2572. "Ydieresis",
  2573. "Zcaron",
  2574. "aacute",
  2575. "acircumflex",
  2576. "adieresis",
  2577. "agrave",
  2578. "aring",
  2579. "atilde",
  2580. "ccedilla",
  2581. "eacute",
  2582. "ecircumflex",
  2583. "edieresis",
  2584. "egrave",
  2585. "iacute",
  2586. "icircumflex",
  2587. "idieresis",
  2588. "igrave",
  2589. "ntilde",
  2590. "oacute",
  2591. "ocircumflex",
  2592. "odieresis",
  2593. "ograve",
  2594. "otilde",
  2595. "scaron",
  2596. "uacute",
  2597. "ucircumflex",
  2598. "udieresis",
  2599. "ugrave",
  2600. "yacute",
  2601. "ydieresis",
  2602. "zcaron",
  2603. "exclamsmall",
  2604. "Hungarumlautsmall",
  2605. "dollaroldstyle",
  2606. "dollarsuperior",
  2607. "ampersandsmall",
  2608. "Acutesmall",
  2609. "parenleftsuperior",
  2610. "parenrightsuperior",
  2611. "twodotenleader",
  2612. "onedotenleader",
  2613. "zerooldstyle",
  2614. "oneoldstyle",
  2615. "twooldstyle",
  2616. "threeoldstyle",
  2617. "fouroldstyle",
  2618. "fiveoldstyle",
  2619. "sixoldstyle",
  2620. "sevenoldstyle",
  2621. "eightoldstyle",
  2622. "nineoldstyle",
  2623. "commasuperior",
  2624. "threequartersemdash",
  2625. "periodsuperior",
  2626. "questionsmall",
  2627. "asuperior",
  2628. "bsuperior",
  2629. "centsuperior",
  2630. "dsuperior",
  2631. "esuperior",
  2632. "isuperior",
  2633. "lsuperior",
  2634. "msuperior",
  2635. "nsuperior",
  2636. "osuperior",
  2637. "rsuperior",
  2638. "ssuperior",
  2639. "tsuperior",
  2640. "ff",
  2641. "ffi",
  2642. "ffl",
  2643. "parenleftinferior",
  2644. "parenrightinferior",
  2645. "Circumflexsmall",
  2646. "hyphensuperior",
  2647. "Gravesmall",
  2648. "Asmall",
  2649. "Bsmall",
  2650. "Csmall",
  2651. "Dsmall",
  2652. "Esmall",
  2653. "Fsmall",
  2654. "Gsmall",
  2655. "Hsmall",
  2656. "Ismall",
  2657. "Jsmall",
  2658. "Ksmall",
  2659. "Lsmall",
  2660. "Msmall",
  2661. "Nsmall",
  2662. "Osmall",
  2663. "Psmall",
  2664. "Qsmall",
  2665. "Rsmall",
  2666. "Ssmall",
  2667. "Tsmall",
  2668. "Usmall",
  2669. "Vsmall",
  2670. "Wsmall",
  2671. "Xsmall",
  2672. "Ysmall",
  2673. "Zsmall",
  2674. "colonmonetary",
  2675. "onefitted",
  2676. "rupiah",
  2677. "Tildesmall",
  2678. "exclamdownsmall",
  2679. "centoldstyle",
  2680. "Lslashsmall",
  2681. "Scaronsmall",
  2682. "Zcaronsmall",
  2683. "Dieresissmall",
  2684. "Brevesmall",
  2685. "Caronsmall",
  2686. "Dotaccentsmall",
  2687. "Macronsmall",
  2688. "figuredash",
  2689. "hypheninferior",
  2690. "Ogoneksmall",
  2691. "Ringsmall",
  2692. "Cedillasmall",
  2693. "questiondownsmall",
  2694. "oneeighth",
  2695. "threeeighths",
  2696. "fiveeighths",
  2697. "seveneighths",
  2698. "onethird",
  2699. "twothirds",
  2700. "zerosuperior",
  2701. "foursuperior",
  2702. "fivesuperior",
  2703. "sixsuperior",
  2704. "sevensuperior",
  2705. "eightsuperior",
  2706. "ninesuperior",
  2707. "zeroinferior",
  2708. "oneinferior",
  2709. "twoinferior",
  2710. "threeinferior",
  2711. "fourinferior",
  2712. "fiveinferior",
  2713. "sixinferior",
  2714. "seveninferior",
  2715. "eightinferior",
  2716. "nineinferior",
  2717. "centinferior",
  2718. "dollarinferior",
  2719. "periodinferior",
  2720. "commainferior",
  2721. "Agravesmall",
  2722. "Aacutesmall",
  2723. "Acircumflexsmall",
  2724. "Atildesmall",
  2725. "Adieresissmall",
  2726. "Aringsmall",
  2727. "AEsmall",
  2728. "Ccedillasmall",
  2729. "Egravesmall",
  2730. "Eacutesmall",
  2731. "Ecircumflexsmall",
  2732. "Edieresissmall",
  2733. "Igravesmall",
  2734. "Iacutesmall",
  2735. "Icircumflexsmall",
  2736. "Idieresissmall",
  2737. "Ethsmall",
  2738. "Ntildesmall",
  2739. "Ogravesmall",
  2740. "Oacutesmall",
  2741. "Ocircumflexsmall",
  2742. "Otildesmall",
  2743. "Odieresissmall",
  2744. "OEsmall",
  2745. "Oslashsmall",
  2746. "Ugravesmall",
  2747. "Uacutesmall",
  2748. "Ucircumflexsmall",
  2749. "Udieresissmall",
  2750. "Yacutesmall",
  2751. "Thornsmall",
  2752. "Ydieresissmall",
  2753. "001.000",
  2754. "001.001",
  2755. "001.002",
  2756. "001.003",
  2757. "Black",
  2758. "Bold",
  2759. "Book",
  2760. "Light",
  2761. "Medium",
  2762. "Regular",
  2763. "Roman",
  2764. "Semibold",
  2765. ]
  2766. cffStandardStringCount = 391
  2767. assert len(cffStandardStrings) == cffStandardStringCount
  2768. # build reverse mapping
  2769. cffStandardStringMapping = {}
  2770. for _i in range(cffStandardStringCount):
  2771. cffStandardStringMapping[cffStandardStrings[_i]] = _i
  2772. cffISOAdobeStrings = [
  2773. ".notdef",
  2774. "space",
  2775. "exclam",
  2776. "quotedbl",
  2777. "numbersign",
  2778. "dollar",
  2779. "percent",
  2780. "ampersand",
  2781. "quoteright",
  2782. "parenleft",
  2783. "parenright",
  2784. "asterisk",
  2785. "plus",
  2786. "comma",
  2787. "hyphen",
  2788. "period",
  2789. "slash",
  2790. "zero",
  2791. "one",
  2792. "two",
  2793. "three",
  2794. "four",
  2795. "five",
  2796. "six",
  2797. "seven",
  2798. "eight",
  2799. "nine",
  2800. "colon",
  2801. "semicolon",
  2802. "less",
  2803. "equal",
  2804. "greater",
  2805. "question",
  2806. "at",
  2807. "A",
  2808. "B",
  2809. "C",
  2810. "D",
  2811. "E",
  2812. "F",
  2813. "G",
  2814. "H",
  2815. "I",
  2816. "J",
  2817. "K",
  2818. "L",
  2819. "M",
  2820. "N",
  2821. "O",
  2822. "P",
  2823. "Q",
  2824. "R",
  2825. "S",
  2826. "T",
  2827. "U",
  2828. "V",
  2829. "W",
  2830. "X",
  2831. "Y",
  2832. "Z",
  2833. "bracketleft",
  2834. "backslash",
  2835. "bracketright",
  2836. "asciicircum",
  2837. "underscore",
  2838. "quoteleft",
  2839. "a",
  2840. "b",
  2841. "c",
  2842. "d",
  2843. "e",
  2844. "f",
  2845. "g",
  2846. "h",
  2847. "i",
  2848. "j",
  2849. "k",
  2850. "l",
  2851. "m",
  2852. "n",
  2853. "o",
  2854. "p",
  2855. "q",
  2856. "r",
  2857. "s",
  2858. "t",
  2859. "u",
  2860. "v",
  2861. "w",
  2862. "x",
  2863. "y",
  2864. "z",
  2865. "braceleft",
  2866. "bar",
  2867. "braceright",
  2868. "asciitilde",
  2869. "exclamdown",
  2870. "cent",
  2871. "sterling",
  2872. "fraction",
  2873. "yen",
  2874. "florin",
  2875. "section",
  2876. "currency",
  2877. "quotesingle",
  2878. "quotedblleft",
  2879. "guillemotleft",
  2880. "guilsinglleft",
  2881. "guilsinglright",
  2882. "fi",
  2883. "fl",
  2884. "endash",
  2885. "dagger",
  2886. "daggerdbl",
  2887. "periodcentered",
  2888. "paragraph",
  2889. "bullet",
  2890. "quotesinglbase",
  2891. "quotedblbase",
  2892. "quotedblright",
  2893. "guillemotright",
  2894. "ellipsis",
  2895. "perthousand",
  2896. "questiondown",
  2897. "grave",
  2898. "acute",
  2899. "circumflex",
  2900. "tilde",
  2901. "macron",
  2902. "breve",
  2903. "dotaccent",
  2904. "dieresis",
  2905. "ring",
  2906. "cedilla",
  2907. "hungarumlaut",
  2908. "ogonek",
  2909. "caron",
  2910. "emdash",
  2911. "AE",
  2912. "ordfeminine",
  2913. "Lslash",
  2914. "Oslash",
  2915. "OE",
  2916. "ordmasculine",
  2917. "ae",
  2918. "dotlessi",
  2919. "lslash",
  2920. "oslash",
  2921. "oe",
  2922. "germandbls",
  2923. "onesuperior",
  2924. "logicalnot",
  2925. "mu",
  2926. "trademark",
  2927. "Eth",
  2928. "onehalf",
  2929. "plusminus",
  2930. "Thorn",
  2931. "onequarter",
  2932. "divide",
  2933. "brokenbar",
  2934. "degree",
  2935. "thorn",
  2936. "threequarters",
  2937. "twosuperior",
  2938. "registered",
  2939. "minus",
  2940. "eth",
  2941. "multiply",
  2942. "threesuperior",
  2943. "copyright",
  2944. "Aacute",
  2945. "Acircumflex",
  2946. "Adieresis",
  2947. "Agrave",
  2948. "Aring",
  2949. "Atilde",
  2950. "Ccedilla",
  2951. "Eacute",
  2952. "Ecircumflex",
  2953. "Edieresis",
  2954. "Egrave",
  2955. "Iacute",
  2956. "Icircumflex",
  2957. "Idieresis",
  2958. "Igrave",
  2959. "Ntilde",
  2960. "Oacute",
  2961. "Ocircumflex",
  2962. "Odieresis",
  2963. "Ograve",
  2964. "Otilde",
  2965. "Scaron",
  2966. "Uacute",
  2967. "Ucircumflex",
  2968. "Udieresis",
  2969. "Ugrave",
  2970. "Yacute",
  2971. "Ydieresis",
  2972. "Zcaron",
  2973. "aacute",
  2974. "acircumflex",
  2975. "adieresis",
  2976. "agrave",
  2977. "aring",
  2978. "atilde",
  2979. "ccedilla",
  2980. "eacute",
  2981. "ecircumflex",
  2982. "edieresis",
  2983. "egrave",
  2984. "iacute",
  2985. "icircumflex",
  2986. "idieresis",
  2987. "igrave",
  2988. "ntilde",
  2989. "oacute",
  2990. "ocircumflex",
  2991. "odieresis",
  2992. "ograve",
  2993. "otilde",
  2994. "scaron",
  2995. "uacute",
  2996. "ucircumflex",
  2997. "udieresis",
  2998. "ugrave",
  2999. "yacute",
  3000. "ydieresis",
  3001. "zcaron",
  3002. ]
  3003. cffISOAdobeStringCount = 229
  3004. assert len(cffISOAdobeStrings) == cffISOAdobeStringCount
  3005. cffIExpertStrings = [
  3006. ".notdef",
  3007. "space",
  3008. "exclamsmall",
  3009. "Hungarumlautsmall",
  3010. "dollaroldstyle",
  3011. "dollarsuperior",
  3012. "ampersandsmall",
  3013. "Acutesmall",
  3014. "parenleftsuperior",
  3015. "parenrightsuperior",
  3016. "twodotenleader",
  3017. "onedotenleader",
  3018. "comma",
  3019. "hyphen",
  3020. "period",
  3021. "fraction",
  3022. "zerooldstyle",
  3023. "oneoldstyle",
  3024. "twooldstyle",
  3025. "threeoldstyle",
  3026. "fouroldstyle",
  3027. "fiveoldstyle",
  3028. "sixoldstyle",
  3029. "sevenoldstyle",
  3030. "eightoldstyle",
  3031. "nineoldstyle",
  3032. "colon",
  3033. "semicolon",
  3034. "commasuperior",
  3035. "threequartersemdash",
  3036. "periodsuperior",
  3037. "questionsmall",
  3038. "asuperior",
  3039. "bsuperior",
  3040. "centsuperior",
  3041. "dsuperior",
  3042. "esuperior",
  3043. "isuperior",
  3044. "lsuperior",
  3045. "msuperior",
  3046. "nsuperior",
  3047. "osuperior",
  3048. "rsuperior",
  3049. "ssuperior",
  3050. "tsuperior",
  3051. "ff",
  3052. "fi",
  3053. "fl",
  3054. "ffi",
  3055. "ffl",
  3056. "parenleftinferior",
  3057. "parenrightinferior",
  3058. "Circumflexsmall",
  3059. "hyphensuperior",
  3060. "Gravesmall",
  3061. "Asmall",
  3062. "Bsmall",
  3063. "Csmall",
  3064. "Dsmall",
  3065. "Esmall",
  3066. "Fsmall",
  3067. "Gsmall",
  3068. "Hsmall",
  3069. "Ismall",
  3070. "Jsmall",
  3071. "Ksmall",
  3072. "Lsmall",
  3073. "Msmall",
  3074. "Nsmall",
  3075. "Osmall",
  3076. "Psmall",
  3077. "Qsmall",
  3078. "Rsmall",
  3079. "Ssmall",
  3080. "Tsmall",
  3081. "Usmall",
  3082. "Vsmall",
  3083. "Wsmall",
  3084. "Xsmall",
  3085. "Ysmall",
  3086. "Zsmall",
  3087. "colonmonetary",
  3088. "onefitted",
  3089. "rupiah",
  3090. "Tildesmall",
  3091. "exclamdownsmall",
  3092. "centoldstyle",
  3093. "Lslashsmall",
  3094. "Scaronsmall",
  3095. "Zcaronsmall",
  3096. "Dieresissmall",
  3097. "Brevesmall",
  3098. "Caronsmall",
  3099. "Dotaccentsmall",
  3100. "Macronsmall",
  3101. "figuredash",
  3102. "hypheninferior",
  3103. "Ogoneksmall",
  3104. "Ringsmall",
  3105. "Cedillasmall",
  3106. "onequarter",
  3107. "onehalf",
  3108. "threequarters",
  3109. "questiondownsmall",
  3110. "oneeighth",
  3111. "threeeighths",
  3112. "fiveeighths",
  3113. "seveneighths",
  3114. "onethird",
  3115. "twothirds",
  3116. "zerosuperior",
  3117. "onesuperior",
  3118. "twosuperior",
  3119. "threesuperior",
  3120. "foursuperior",
  3121. "fivesuperior",
  3122. "sixsuperior",
  3123. "sevensuperior",
  3124. "eightsuperior",
  3125. "ninesuperior",
  3126. "zeroinferior",
  3127. "oneinferior",
  3128. "twoinferior",
  3129. "threeinferior",
  3130. "fourinferior",
  3131. "fiveinferior",
  3132. "sixinferior",
  3133. "seveninferior",
  3134. "eightinferior",
  3135. "nineinferior",
  3136. "centinferior",
  3137. "dollarinferior",
  3138. "periodinferior",
  3139. "commainferior",
  3140. "Agravesmall",
  3141. "Aacutesmall",
  3142. "Acircumflexsmall",
  3143. "Atildesmall",
  3144. "Adieresissmall",
  3145. "Aringsmall",
  3146. "AEsmall",
  3147. "Ccedillasmall",
  3148. "Egravesmall",
  3149. "Eacutesmall",
  3150. "Ecircumflexsmall",
  3151. "Edieresissmall",
  3152. "Igravesmall",
  3153. "Iacutesmall",
  3154. "Icircumflexsmall",
  3155. "Idieresissmall",
  3156. "Ethsmall",
  3157. "Ntildesmall",
  3158. "Ogravesmall",
  3159. "Oacutesmall",
  3160. "Ocircumflexsmall",
  3161. "Otildesmall",
  3162. "Odieresissmall",
  3163. "OEsmall",
  3164. "Oslashsmall",
  3165. "Ugravesmall",
  3166. "Uacutesmall",
  3167. "Ucircumflexsmall",
  3168. "Udieresissmall",
  3169. "Yacutesmall",
  3170. "Thornsmall",
  3171. "Ydieresissmall",
  3172. ]
  3173. cffExpertStringCount = 166
  3174. assert len(cffIExpertStrings) == cffExpertStringCount
  3175. cffExpertSubsetStrings = [
  3176. ".notdef",
  3177. "space",
  3178. "dollaroldstyle",
  3179. "dollarsuperior",
  3180. "parenleftsuperior",
  3181. "parenrightsuperior",
  3182. "twodotenleader",
  3183. "onedotenleader",
  3184. "comma",
  3185. "hyphen",
  3186. "period",
  3187. "fraction",
  3188. "zerooldstyle",
  3189. "oneoldstyle",
  3190. "twooldstyle",
  3191. "threeoldstyle",
  3192. "fouroldstyle",
  3193. "fiveoldstyle",
  3194. "sixoldstyle",
  3195. "sevenoldstyle",
  3196. "eightoldstyle",
  3197. "nineoldstyle",
  3198. "colon",
  3199. "semicolon",
  3200. "commasuperior",
  3201. "threequartersemdash",
  3202. "periodsuperior",
  3203. "asuperior",
  3204. "bsuperior",
  3205. "centsuperior",
  3206. "dsuperior",
  3207. "esuperior",
  3208. "isuperior",
  3209. "lsuperior",
  3210. "msuperior",
  3211. "nsuperior",
  3212. "osuperior",
  3213. "rsuperior",
  3214. "ssuperior",
  3215. "tsuperior",
  3216. "ff",
  3217. "fi",
  3218. "fl",
  3219. "ffi",
  3220. "ffl",
  3221. "parenleftinferior",
  3222. "parenrightinferior",
  3223. "hyphensuperior",
  3224. "colonmonetary",
  3225. "onefitted",
  3226. "rupiah",
  3227. "centoldstyle",
  3228. "figuredash",
  3229. "hypheninferior",
  3230. "onequarter",
  3231. "onehalf",
  3232. "threequarters",
  3233. "oneeighth",
  3234. "threeeighths",
  3235. "fiveeighths",
  3236. "seveneighths",
  3237. "onethird",
  3238. "twothirds",
  3239. "zerosuperior",
  3240. "onesuperior",
  3241. "twosuperior",
  3242. "threesuperior",
  3243. "foursuperior",
  3244. "fivesuperior",
  3245. "sixsuperior",
  3246. "sevensuperior",
  3247. "eightsuperior",
  3248. "ninesuperior",
  3249. "zeroinferior",
  3250. "oneinferior",
  3251. "twoinferior",
  3252. "threeinferior",
  3253. "fourinferior",
  3254. "fiveinferior",
  3255. "sixinferior",
  3256. "seveninferior",
  3257. "eightinferior",
  3258. "nineinferior",
  3259. "centinferior",
  3260. "dollarinferior",
  3261. "periodinferior",
  3262. "commainferior",
  3263. ]
  3264. cffExpertSubsetStringCount = 87
  3265. assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount