__init__.py 105 KB

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