blob: cf5755e4e0e3ac2659c5e84bee8dfb1e61b7e0e0 [file] [log] [blame]
Just7842e561999-12-16 21:34:53 +00001"""cffLib.py -- read/write tools for Adobe CFF fonts."""
2
Justec46d161999-12-20 22:02:10 +00003#
jvr2a9bcde2008-03-07 19:56:17 +00004# $Id: cffLib.py,v 1.34 2008-03-07 19:56:17 jvr Exp $
Justec46d161999-12-20 22:02:10 +00005#
Just7842e561999-12-16 21:34:53 +00006
Behdad Esfahbod32c10ee2013-11-27 17:46:17 -05007from __future__ import print_function, division
Behdad Esfahbod30e691e2013-11-27 17:27:45 -05008from fontTools.misc.py23 import *
Behdad Esfahbod8413c102013-09-17 16:59:39 -04009from fontTools.misc import sstruct
Just528614e2000-01-16 22:14:02 +000010from fontTools.misc import psCharStrings
jvr4e5af602002-05-24 09:58:04 +000011from fontTools.misc.textTools import safeEval
Behdad Esfahbod30e691e2013-11-27 17:27:45 -050012import struct
Behdad Esfahbod2a9b8682013-11-27 05:52:33 -050013
jvr767102e2002-05-17 07:06:32 +000014DEBUG = 0
15
16
Just7842e561999-12-16 21:34:53 +000017cffHeaderFormat = """
18 major: B
19 minor: B
20 hdrSize: B
21 offSize: B
22"""
23
24class CFFFontSet:
25
26 def __init__(self):
jvr4756b3a2002-05-16 18:17:32 +000027 pass
Just7842e561999-12-16 21:34:53 +000028
jvr4e5af602002-05-24 09:58:04 +000029 def decompile(self, file, otFont):
jvra2a75b32002-05-13 11:25:17 +000030 sstruct.unpack(cffHeaderFormat, file.read(4), self)
Just7842e561999-12-16 21:34:53 +000031 assert self.major == 1 and self.minor == 0, \
32 "unknown CFF format: %d.%d" % (self.major, self.minor)
Just7842e561999-12-16 21:34:53 +000033
jvrf2cf9c52002-05-23 21:50:36 +000034 file.seek(self.hdrSize)
jvr4e5af602002-05-24 09:58:04 +000035 self.fontNames = list(Index(file))
jvr4756b3a2002-05-16 18:17:32 +000036 self.topDictIndex = TopDictIndex(file)
jvr767102e2002-05-17 07:06:32 +000037 self.strings = IndexedStrings(file)
jvr4e5af602002-05-24 09:58:04 +000038 self.GlobalSubrs = GlobalSubrsIndex(file)
jvr4756b3a2002-05-16 18:17:32 +000039 self.topDictIndex.strings = self.strings
jvr016ca762002-05-16 18:38:03 +000040 self.topDictIndex.GlobalSubrs = self.GlobalSubrs
jvr4756b3a2002-05-16 18:17:32 +000041
42 def __len__(self):
43 return len(self.fontNames)
44
45 def keys(self):
jvrce522412003-08-25 07:37:25 +000046 return list(self.fontNames)
jvr4756b3a2002-05-16 18:17:32 +000047
jvr767102e2002-05-17 07:06:32 +000048 def values(self):
49 return self.topDictIndex
50
jvr4756b3a2002-05-16 18:17:32 +000051 def __getitem__(self, name):
52 try:
53 index = self.fontNames.index(name)
54 except ValueError:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -050055 raise KeyError(name)
jvr016ca762002-05-16 18:38:03 +000056 return self.topDictIndex[index]
Just7842e561999-12-16 21:34:53 +000057
jvr4e5af602002-05-24 09:58:04 +000058 def compile(self, file, otFont):
Just7842e561999-12-16 21:34:53 +000059 strings = IndexedStrings()
jvrf2cf9c52002-05-23 21:50:36 +000060 writer = CFFWriter()
61 writer.add(sstruct.pack(cffHeaderFormat, self))
62 fontNames = Index()
63 for name in self.fontNames:
64 fontNames.append(name)
65 writer.add(fontNames.getCompiler(strings, None))
66 topCompiler = self.topDictIndex.getCompiler(strings, None)
67 writer.add(topCompiler)
68 writer.add(strings.getCompiler())
69 writer.add(self.GlobalSubrs.getCompiler(strings, None))
70
jvr4e5af602002-05-24 09:58:04 +000071 for topDict in self.topDictIndex:
72 if not hasattr(topDict, "charset") or topDict.charset is None:
73 charset = otFont.getGlyphOrder()
74 topDict.charset = charset
75
jvrf2cf9c52002-05-23 21:50:36 +000076 for child in topCompiler.getChildren(strings):
77 writer.add(child)
78
jvrf2cf9c52002-05-23 21:50:36 +000079 writer.toFile(file)
Just7842e561999-12-16 21:34:53 +000080
81 def toXML(self, xmlWriter, progress=None):
82 xmlWriter.newline()
83 for fontName in self.fontNames:
84 xmlWriter.begintag("CFFFont", name=fontName)
85 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +000086 font = self[fontName]
Just7842e561999-12-16 21:34:53 +000087 font.toXML(xmlWriter, progress)
88 xmlWriter.endtag("CFFFont")
89 xmlWriter.newline()
90 xmlWriter.newline()
91 xmlWriter.begintag("GlobalSubrs")
92 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +000093 self.GlobalSubrs.toXML(xmlWriter, progress)
Just7842e561999-12-16 21:34:53 +000094 xmlWriter.endtag("GlobalSubrs")
95 xmlWriter.newline()
96 xmlWriter.newline()
97
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050098 def fromXML(self, name, attrs, content):
jvr4e5af602002-05-24 09:58:04 +000099 if not hasattr(self, "GlobalSubrs"):
100 self.GlobalSubrs = GlobalSubrsIndex()
101 self.major = 1
102 self.minor = 0
103 self.hdrSize = 4
104 self.offSize = 4 # XXX ??
105 if name == "CFFFont":
106 if not hasattr(self, "fontNames"):
107 self.fontNames = []
108 self.topDictIndex = TopDictIndex()
109 fontName = attrs["name"]
110 topDict = TopDict(GlobalSubrs=self.GlobalSubrs)
111 topDict.charset = None # gets filled in later
112 self.fontNames.append(fontName)
113 self.topDictIndex.append(topDict)
114 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000115 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000116 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500117 name, attrs, content = element
118 topDict.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000119 elif name == "GlobalSubrs":
120 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000121 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000122 continue
123 name, attrs, content = element
jvr489d76a2003-08-24 19:56:16 +0000124 subr = psCharStrings.T2CharString()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500125 subr.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000126 self.GlobalSubrs.append(subr)
Just7842e561999-12-16 21:34:53 +0000127
128
jvrf2cf9c52002-05-23 21:50:36 +0000129class CFFWriter:
130
131 def __init__(self):
132 self.data = []
133
134 def add(self, table):
135 self.data.append(table)
136
137 def toFile(self, file):
138 lastPosList = None
139 count = 1
Behdad Esfahbodac1b4352013-11-27 04:15:34 -0500140 while True:
jvr4e5af602002-05-24 09:58:04 +0000141 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500142 print("CFFWriter.toFile() iteration:", count)
jvr4e5af602002-05-24 09:58:04 +0000143 count = count + 1
jvrf2cf9c52002-05-23 21:50:36 +0000144 pos = 0
145 posList = [pos]
146 for item in self.data:
jvrf2cf9c52002-05-23 21:50:36 +0000147 if hasattr(item, "getDataLength"):
jvr4e5af602002-05-24 09:58:04 +0000148 endPos = pos + item.getDataLength()
jvrf2cf9c52002-05-23 21:50:36 +0000149 else:
jvr4e5af602002-05-24 09:58:04 +0000150 endPos = pos + len(item)
151 if hasattr(item, "setPos"):
152 item.setPos(pos, endPos)
153 pos = endPos
jvrf2cf9c52002-05-23 21:50:36 +0000154 posList.append(pos)
155 if posList == lastPosList:
156 break
157 lastPosList = posList
jvr4e5af602002-05-24 09:58:04 +0000158 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500159 print("CFFWriter.toFile() writing to file.")
jvrf2cf9c52002-05-23 21:50:36 +0000160 begin = file.tell()
161 posList = [0]
162 for item in self.data:
163 if hasattr(item, "toFile"):
164 item.toFile(file)
165 else:
166 file.write(item)
167 posList.append(file.tell() - begin)
jvrf2cf9c52002-05-23 21:50:36 +0000168 assert posList == lastPosList
169
170
171def calcOffSize(largestOffset):
172 if largestOffset < 0x100:
173 offSize = 1
174 elif largestOffset < 0x10000:
175 offSize = 2
176 elif largestOffset < 0x1000000:
177 offSize = 3
178 else:
179 offSize = 4
180 return offSize
181
182
183class IndexCompiler:
184
185 def __init__(self, items, strings, parent):
186 self.items = self.getItems(items, strings)
187 self.parent = parent
188
189 def getItems(self, items, strings):
190 return items
191
192 def getOffsets(self):
193 pos = 1
194 offsets = [pos]
195 for item in self.items:
196 if hasattr(item, "getDataLength"):
197 pos = pos + item.getDataLength()
198 else:
199 pos = pos + len(item)
200 offsets.append(pos)
201 return offsets
202
203 def getDataLength(self):
204 lastOffset = self.getOffsets()[-1]
205 offSize = calcOffSize(lastOffset)
206 dataLength = (
207 2 + # count
208 1 + # offSize
209 (len(self.items) + 1) * offSize + # the offsets
210 lastOffset - 1 # size of object data
211 )
212 return dataLength
213
214 def toFile(self, file):
jvrf2cf9c52002-05-23 21:50:36 +0000215 offsets = self.getOffsets()
216 writeCard16(file, len(self.items))
217 offSize = calcOffSize(offsets[-1])
218 writeCard8(file, offSize)
219 offSize = -offSize
220 pack = struct.pack
221 for offset in offsets:
222 binOffset = pack(">l", offset)[offSize:]
223 assert len(binOffset) == -offSize
224 file.write(binOffset)
225 for item in self.items:
226 if hasattr(item, "toFile"):
227 item.toFile(file)
228 else:
229 file.write(item)
jvrf2cf9c52002-05-23 21:50:36 +0000230
231
232class IndexedStringsCompiler(IndexCompiler):
233
234 def getItems(self, items, strings):
235 return items.strings
236
237
238class TopDictIndexCompiler(IndexCompiler):
239
240 def getItems(self, items, strings):
241 out = []
242 for item in items:
243 out.append(item.getCompiler(strings, self))
244 return out
245
246 def getChildren(self, strings):
247 children = []
248 for topDict in self.items:
249 children.extend(topDict.getChildren(strings))
250 return children
251
252
jvred101512003-08-22 19:53:32 +0000253class FDArrayIndexCompiler(IndexCompiler):
254
255 def getItems(self, items, strings):
256 out = []
257 for item in items:
258 out.append(item.getCompiler(strings, self))
259 return out
260
261 def getChildren(self, strings):
262 children = []
263 for fontDict in self.items:
264 children.extend(fontDict.getChildren(strings))
265 return children
266
jvred101512003-08-22 19:53:32 +0000267 def toFile(self, file):
268 offsets = self.getOffsets()
269 writeCard16(file, len(self.items))
270 offSize = calcOffSize(offsets[-1])
271 writeCard8(file, offSize)
272 offSize = -offSize
273 pack = struct.pack
274 for offset in offsets:
275 binOffset = pack(">l", offset)[offSize:]
276 assert len(binOffset) == -offSize
277 file.write(binOffset)
278 for item in self.items:
279 if hasattr(item, "toFile"):
280 item.toFile(file)
281 else:
282 file.write(item)
283
284 def setPos(self, pos, endPos):
285 self.parent.rawDict["FDArray"] = pos
286
287
jvrf2cf9c52002-05-23 21:50:36 +0000288class GlobalSubrsCompiler(IndexCompiler):
289 def getItems(self, items, strings):
290 out = []
291 for cs in items:
292 cs.compile()
293 out.append(cs.bytecode)
294 return out
295
296class SubrsCompiler(GlobalSubrsCompiler):
jvr4e5af602002-05-24 09:58:04 +0000297 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000298 offset = pos - self.parent.pos
299 self.parent.rawDict["Subrs"] = offset
300
301class CharStringsCompiler(GlobalSubrsCompiler):
jvr4e5af602002-05-24 09:58:04 +0000302 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000303 self.parent.rawDict["CharStrings"] = pos
304
305
jvr4756b3a2002-05-16 18:17:32 +0000306class Index:
Just7842e561999-12-16 21:34:53 +0000307
jvr4756b3a2002-05-16 18:17:32 +0000308 """This class represents what the CFF spec calls an INDEX."""
Just7842e561999-12-16 21:34:53 +0000309
jvrf2cf9c52002-05-23 21:50:36 +0000310 compilerClass = IndexCompiler
311
jvr4e5af602002-05-24 09:58:04 +0000312 def __init__(self, file=None):
313 name = self.__class__.__name__
jvrf2cf9c52002-05-23 21:50:36 +0000314 if file is None:
315 self.items = []
316 return
jvr767102e2002-05-17 07:06:32 +0000317 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500318 print("loading %s at %s" % (name, file.tell()))
jvr4756b3a2002-05-16 18:17:32 +0000319 self.file = file
jvra2ad5442002-05-17 18:36:07 +0000320 count = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000321 self.count = count
322 self.items = [None] * count
323 if count == 0:
jvrf2cf9c52002-05-23 21:50:36 +0000324 self.items = []
jvr4756b3a2002-05-16 18:17:32 +0000325 return
jvra2ad5442002-05-17 18:36:07 +0000326 offSize = readCard8(file)
jvr767102e2002-05-17 07:06:32 +0000327 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500328 print(" index count: %s offSize: %s" % (count, offSize))
jvr767102e2002-05-17 07:06:32 +0000329 assert offSize <= 4, "offSize too large: %s" % offSize
jvr4756b3a2002-05-16 18:17:32 +0000330 self.offsets = offsets = []
Behdad Esfahbod18316aa2013-11-27 21:17:35 -0500331 pad = b'\0' * (4 - offSize)
jvr4756b3a2002-05-16 18:17:32 +0000332 for index in range(count+1):
333 chunk = file.read(offSize)
334 chunk = pad + chunk
335 offset, = struct.unpack(">L", chunk)
336 offsets.append(int(offset))
337 self.offsetBase = file.tell() - 1
338 file.seek(self.offsetBase + offsets[-1]) # pretend we've read the whole lot
jvrf2cf9c52002-05-23 21:50:36 +0000339 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500340 print(" end of %s at %s" % (name, file.tell()))
Just7842e561999-12-16 21:34:53 +0000341
jvr4756b3a2002-05-16 18:17:32 +0000342 def __len__(self):
jvrf2cf9c52002-05-23 21:50:36 +0000343 return len(self.items)
jvra2a75b32002-05-13 11:25:17 +0000344
jvr4756b3a2002-05-16 18:17:32 +0000345 def __getitem__(self, index):
346 item = self.items[index]
347 if item is not None:
348 return item
349 offset = self.offsets[index] + self.offsetBase
350 size = self.offsets[index+1] - self.offsets[index]
351 file = self.file
352 file.seek(offset)
353 data = file.read(size)
354 assert len(data) == size
jvra2ad5442002-05-17 18:36:07 +0000355 item = self.produceItem(index, data, file, offset, size)
jvr4756b3a2002-05-16 18:17:32 +0000356 self.items[index] = item
357 return item
358
jvra2ad5442002-05-17 18:36:07 +0000359 def produceItem(self, index, data, file, offset, size):
jvr4756b3a2002-05-16 18:17:32 +0000360 return data
jvr4756b3a2002-05-16 18:17:32 +0000361
jvrf2cf9c52002-05-23 21:50:36 +0000362 def append(self, item):
363 self.items.append(item)
364
365 def getCompiler(self, strings, parent):
366 return self.compilerClass(self, strings, parent)
367
368
369class GlobalSubrsIndex(Index):
370
371 compilerClass = GlobalSubrsCompiler
372
jvr4e5af602002-05-24 09:58:04 +0000373 def __init__(self, file=None, globalSubrs=None, private=None, fdSelect=None, fdArray=None):
374 Index.__init__(self, file)
jvra2ad5442002-05-17 18:36:07 +0000375 self.globalSubrs = globalSubrs
376 self.private = private
jvred101512003-08-22 19:53:32 +0000377 if fdSelect:
378 self.fdSelect = fdSelect
379 if fdArray:
380 self.fdArray = fdArray
jvra2ad5442002-05-17 18:36:07 +0000381
382 def produceItem(self, index, data, file, offset, size):
383 if self.private is not None:
384 private = self.private
jvred101512003-08-22 19:53:32 +0000385 elif hasattr(self, 'fdArray') and self.fdArray is not None:
jvra2ad5442002-05-17 18:36:07 +0000386 private = self.fdArray[self.fdSelect[index]].Private
387 else:
388 private = None
jvr489d76a2003-08-24 19:56:16 +0000389 return psCharStrings.T2CharString(data, private=private, globalSubrs=self.globalSubrs)
jvr4756b3a2002-05-16 18:17:32 +0000390
391 def toXML(self, xmlWriter, progress):
jvred101512003-08-22 19:53:32 +0000392 xmlWriter.comment("The 'index' attribute is only for humans; it is ignored when parsed.")
jvr4e5af602002-05-24 09:58:04 +0000393 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +0000394 for i in range(len(self)):
jvrb58176e2002-05-24 11:55:37 +0000395 subr = self[i]
396 if subr.needsDecompilation():
397 xmlWriter.begintag("CharString", index=i, raw=1)
398 else:
399 xmlWriter.begintag("CharString", index=i)
jvr4756b3a2002-05-16 18:17:32 +0000400 xmlWriter.newline()
jvrb58176e2002-05-24 11:55:37 +0000401 subr.toXML(xmlWriter)
jvr4756b3a2002-05-16 18:17:32 +0000402 xmlWriter.endtag("CharString")
403 xmlWriter.newline()
jvra2ad5442002-05-17 18:36:07 +0000404
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500405 def fromXML(self, name, attrs, content):
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500406 if name != "CharString":
jvr4e5af602002-05-24 09:58:04 +0000407 return
jvr489d76a2003-08-24 19:56:16 +0000408 subr = psCharStrings.T2CharString()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500409 subr.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000410 self.append(subr)
411
jvra2ad5442002-05-17 18:36:07 +0000412 def getItemAndSelector(self, index):
jvred101512003-08-22 19:53:32 +0000413 sel = None
414 if hasattr(self, 'fdSelect'):
415 sel = self.fdSelect[index]
jvra2ad5442002-05-17 18:36:07 +0000416 return self[index], sel
jvrf2cf9c52002-05-23 21:50:36 +0000417
jvre2ca9b52002-09-09 14:18:39 +0000418
jvrf2cf9c52002-05-23 21:50:36 +0000419class SubrsIndex(GlobalSubrsIndex):
420 compilerClass = SubrsCompiler
421
jvr4756b3a2002-05-16 18:17:32 +0000422
jvr767102e2002-05-17 07:06:32 +0000423class TopDictIndex(Index):
jvra2ad5442002-05-17 18:36:07 +0000424
jvrf2cf9c52002-05-23 21:50:36 +0000425 compilerClass = TopDictIndexCompiler
426
jvra2ad5442002-05-17 18:36:07 +0000427 def produceItem(self, index, data, file, offset, size):
jvr767102e2002-05-17 07:06:32 +0000428 top = TopDict(self.strings, file, offset, self.GlobalSubrs)
429 top.decompile(data)
430 return top
jvra2ad5442002-05-17 18:36:07 +0000431
432 def toXML(self, xmlWriter, progress):
433 for i in range(len(self)):
434 xmlWriter.begintag("FontDict", index=i)
435 xmlWriter.newline()
436 self[i].toXML(xmlWriter, progress)
437 xmlWriter.endtag("FontDict")
438 xmlWriter.newline()
jvr767102e2002-05-17 07:06:32 +0000439
440
jvred101512003-08-22 19:53:32 +0000441class FDArrayIndex(TopDictIndex):
442
443 compilerClass = FDArrayIndexCompiler
444
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500445 def fromXML(self, name, attrs, content):
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500446 if name != "FontDict":
jvred101512003-08-22 19:53:32 +0000447 return
448 fontDict = FontDict()
449 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000450 if isinstance(element, basestring):
jvred101512003-08-22 19:53:32 +0000451 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500452 name, attrs, content = element
453 fontDict.fromXML(name, attrs, content)
jvred101512003-08-22 19:53:32 +0000454 self.append(fontDict)
455
456
457class FDSelect:
458 def __init__(self, file = None, numGlyphs = None, format=None):
459 if file:
460 # read data in from file
461 self.format = readCard8(file)
462 if self.format == 0:
463 from array import array
464 self.gidArray = array("B", file.read(numGlyphs)).tolist()
465 elif self.format == 3:
466 gidArray = [None] * numGlyphs
467 nRanges = readCard16(file)
468 prev = None
469 for i in range(nRanges):
470 first = readCard16(file)
471 if prev is not None:
472 for glyphID in range(prev, first):
473 gidArray[glyphID] = fd
474 prev = first
475 fd = readCard8(file)
476 if prev is not None:
477 first = readCard16(file)
478 for glyphID in range(prev, first):
479 gidArray[glyphID] = fd
480 self.gidArray = gidArray
481 else:
482 assert 0, "unsupported FDSelect format: %s" % format
483 else:
484 # reading from XML. Make empty gidArray,, and leave format as passed in.
485 # format == None will result in the smallest representation being used.
486 self.format = format
487 self.gidArray = []
488
489
490 def __len__(self):
491 return len(self.gidArray)
492
493 def __getitem__(self, index):
494 return self.gidArray[index]
495
496 def __setitem__(self, index, fdSelectValue):
497 self.gidArray[index] = fdSelectValue
498
499 def append(self, fdSelectValue):
500 self.gidArray.append(fdSelectValue)
501
502
jvr4756b3a2002-05-16 18:17:32 +0000503class CharStrings:
504
jvra2ad5442002-05-17 18:36:07 +0000505 def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray):
jvr4e5af602002-05-24 09:58:04 +0000506 if file is not None:
507 self.charStringsIndex = SubrsIndex(file, globalSubrs, private, fdSelect, fdArray)
508 self.charStrings = charStrings = {}
509 for i in range(len(charset)):
510 charStrings[charset[i]] = i
511 self.charStringsAreIndexed = 1
512 else:
513 self.charStrings = {}
514 self.charStringsAreIndexed = 0
515 self.globalSubrs = globalSubrs
516 self.private = private
jvred101512003-08-22 19:53:32 +0000517 if fdSelect != None:
518 self.fdSelect = fdSelect
519 if fdArray!= None:
520 self.fdArray = fdArray
jvr4756b3a2002-05-16 18:17:32 +0000521
522 def keys(self):
Behdad Esfahbodc2297cd2013-11-27 06:26:55 -0500523 return list(self.charStrings.keys())
jvr4756b3a2002-05-16 18:17:32 +0000524
jvr016ca762002-05-16 18:38:03 +0000525 def values(self):
jvr4e5af602002-05-24 09:58:04 +0000526 if self.charStringsAreIndexed:
527 return self.charStringsIndex
528 else:
Behdad Esfahbodc2297cd2013-11-27 06:26:55 -0500529 return list(self.charStrings.values())
jvr016ca762002-05-16 18:38:03 +0000530
jvr4756b3a2002-05-16 18:17:32 +0000531 def has_key(self, name):
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500532 return name in self.charStrings
jvr4756b3a2002-05-16 18:17:32 +0000533
jvr767102e2002-05-17 07:06:32 +0000534 def __len__(self):
jvr4e5af602002-05-24 09:58:04 +0000535 return len(self.charStrings)
jvr767102e2002-05-17 07:06:32 +0000536
jvr4756b3a2002-05-16 18:17:32 +0000537 def __getitem__(self, name):
jvr4e5af602002-05-24 09:58:04 +0000538 charString = self.charStrings[name]
539 if self.charStringsAreIndexed:
540 charString = self.charStringsIndex[charString]
541 return charString
542
543 def __setitem__(self, name, charString):
544 if self.charStringsAreIndexed:
545 index = self.charStrings[name]
546 self.charStringsIndex[index] = charString
547 else:
548 self.charStrings[name] = charString
jvr4756b3a2002-05-16 18:17:32 +0000549
jvra2ad5442002-05-17 18:36:07 +0000550 def getItemAndSelector(self, name):
jvr4e5af602002-05-24 09:58:04 +0000551 if self.charStringsAreIndexed:
552 index = self.charStrings[name]
553 return self.charStringsIndex.getItemAndSelector(index)
554 else:
jvred101512003-08-22 19:53:32 +0000555 if hasattr(self, 'fdSelect'):
jvr91bca422012-10-18 12:49:22 +0000556 sel = self.fdSelect[index] # index is not defined at this point. Read R. ?
jvred101512003-08-22 19:53:32 +0000557 else:
558 raise KeyError("fdSelect array not yet defined.")
559 return self.charStrings[name], sel
jvra2ad5442002-05-17 18:36:07 +0000560
jvr4756b3a2002-05-16 18:17:32 +0000561 def toXML(self, xmlWriter, progress):
Behdad Esfahbodac1b4352013-11-27 04:15:34 -0500562 names = sorted(self.keys())
jvr7ce0a132002-07-23 16:42:11 +0000563 i = 0
564 step = 10
565 numGlyphs = len(names)
jvr4756b3a2002-05-16 18:17:32 +0000566 for name in names:
jvred101512003-08-22 19:53:32 +0000567 charStr, fdSelectIndex = self.getItemAndSelector(name)
jvrb58176e2002-05-24 11:55:37 +0000568 if charStr.needsDecompilation():
569 raw = [("raw", 1)]
570 else:
571 raw = []
jvred101512003-08-22 19:53:32 +0000572 if fdSelectIndex is None:
jvrb58176e2002-05-24 11:55:37 +0000573 xmlWriter.begintag("CharString", [('name', name)] + raw)
jvra2ad5442002-05-17 18:36:07 +0000574 else:
575 xmlWriter.begintag("CharString",
jvred101512003-08-22 19:53:32 +0000576 [('name', name), ('fdSelectIndex', fdSelectIndex)] + raw)
jvr4756b3a2002-05-16 18:17:32 +0000577 xmlWriter.newline()
jvrb58176e2002-05-24 11:55:37 +0000578 charStr.toXML(xmlWriter)
jvr4756b3a2002-05-16 18:17:32 +0000579 xmlWriter.endtag("CharString")
580 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000581 if not i % step and progress is not None:
582 progress.setLabel("Dumping 'CFF ' table... (%s)" % name)
Behdad Esfahbod32c10ee2013-11-27 17:46:17 -0500583 progress.increment(step / numGlyphs)
jvr7ce0a132002-07-23 16:42:11 +0000584 i = i + 1
jvr4e5af602002-05-24 09:58:04 +0000585
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500586 def fromXML(self, name, attrs, content):
jvr4e5af602002-05-24 09:58:04 +0000587 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000588 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000589 continue
590 name, attrs, content = element
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500591 if name != "CharString":
jvr4e5af602002-05-24 09:58:04 +0000592 continue
jvred101512003-08-22 19:53:32 +0000593 fdID = -1
594 if hasattr(self, "fdArray"):
595 fdID = safeEval(attrs["fdSelectIndex"])
596 private = self.fdArray[fdID].Private
597 else:
598 private = self.private
599
jvr4e5af602002-05-24 09:58:04 +0000600 glyphName = attrs["name"]
jvr489d76a2003-08-24 19:56:16 +0000601 charString = psCharStrings.T2CharString(
602 private=private,
603 globalSubrs=self.globalSubrs)
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500604 charString.fromXML(name, attrs, content)
jvred101512003-08-22 19:53:32 +0000605 if fdID >= 0:
606 charString.fdSelectIndex = fdID
jvr4e5af602002-05-24 09:58:04 +0000607 self[glyphName] = charString
jvr4756b3a2002-05-16 18:17:32 +0000608
609
jvra2ad5442002-05-17 18:36:07 +0000610def readCard8(file):
Behdad Esfahbod319c5fd2013-11-27 18:13:48 -0500611 return byteord(file.read(1))
jvra2ad5442002-05-17 18:36:07 +0000612
613def readCard16(file):
614 value, = struct.unpack(">H", file.read(2))
615 return value
616
jvrf2cf9c52002-05-23 21:50:36 +0000617def writeCard8(file, value):
Behdad Esfahbodb7a2d792013-11-27 15:19:40 -0500618 file.write(bytechr(value))
jvrf2cf9c52002-05-23 21:50:36 +0000619
620def writeCard16(file, value):
621 file.write(struct.pack(">H", value))
622
623def packCard8(value):
Behdad Esfahbodb7a2d792013-11-27 15:19:40 -0500624 return bytechr(value)
jvrf2cf9c52002-05-23 21:50:36 +0000625
626def packCard16(value):
627 return struct.pack(">H", value)
628
jvr4756b3a2002-05-16 18:17:32 +0000629def buildOperatorDict(table):
630 d = {}
631 for op, name, arg, default, conv in table:
632 d[op] = (name, arg)
633 return d
634
jvrf2cf9c52002-05-23 21:50:36 +0000635def buildOpcodeDict(table):
636 d = {}
637 for op, name, arg, default, conv in table:
jvr2a9bcde2008-03-07 19:56:17 +0000638 if isinstance(op, tuple):
Behdad Esfahbodb7a2d792013-11-27 15:19:40 -0500639 op = bytechr(op[0]) + bytechr(op[1])
jvrf2cf9c52002-05-23 21:50:36 +0000640 else:
Behdad Esfahbodb7a2d792013-11-27 15:19:40 -0500641 op = bytechr(op)
jvrf2cf9c52002-05-23 21:50:36 +0000642 d[name] = (op, arg)
643 return d
644
jvr4756b3a2002-05-16 18:17:32 +0000645def buildOrder(table):
646 l = []
647 for op, name, arg, default, conv in table:
648 l.append(name)
649 return l
650
651def buildDefaults(table):
652 d = {}
653 for op, name, arg, default, conv in table:
654 if default is not None:
655 d[name] = default
656 return d
657
658def buildConverters(table):
659 d = {}
660 for op, name, arg, default, conv in table:
661 d[name] = conv
662 return d
663
664
jvr4e5af602002-05-24 09:58:04 +0000665class SimpleConverter:
jvr7ce02ea2002-05-17 20:04:05 +0000666 def read(self, parent, value):
667 return value
jvrf2cf9c52002-05-23 21:50:36 +0000668 def write(self, parent, value):
669 return value
jvr7ce0a132002-07-23 16:42:11 +0000670 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000671 xmlWriter.simpletag(name, value=value)
672 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500673 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000674 return attrs["value"]
675
jvre2ca9b52002-09-09 14:18:39 +0000676class Latin1Converter(SimpleConverter):
Behdad Esfahbod8c5c9662013-10-28 13:20:00 +0100677 def xmlWrite(self, xmlWriter, name, value, progress):
678 # Store as UTF-8
Behdad Esfahbod90057742013-11-27 13:58:09 -0500679 value = value.decode("latin-1").encode("utf-8")
Behdad Esfahbod8c5c9662013-10-28 13:20:00 +0100680 xmlWriter.simpletag(name, value=value)
681 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500682 def xmlRead(self, name, attrs, content, parent):
Behdad Esfahbod90057742013-11-27 13:58:09 -0500683 return attrs["value"].decode("utf-8").encode("latin-1")
jvre2ca9b52002-09-09 14:18:39 +0000684
685
jvr4e5af602002-05-24 09:58:04 +0000686def parseNum(s):
687 try:
688 value = int(s)
689 except:
690 value = float(s)
691 return value
692
693class NumberConverter(SimpleConverter):
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500694 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000695 return parseNum(attrs["value"])
696
697class ArrayConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000698 def xmlWrite(self, xmlWriter, name, value, progress):
Behdad Esfahbode5ca7962013-11-27 04:38:16 -0500699 value = " ".join(map(str, value))
700 xmlWriter.simpletag(name, value=value)
jvr4e5af602002-05-24 09:58:04 +0000701 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500702 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000703 values = attrs["value"].split()
Behdad Esfahbode5ca7962013-11-27 04:38:16 -0500704 return [parseNum(value) for value in values]
jvr4e5af602002-05-24 09:58:04 +0000705
706class TableConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000707 def xmlWrite(self, xmlWriter, name, value, progress):
jvra2ad5442002-05-17 18:36:07 +0000708 xmlWriter.begintag(name)
709 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000710 value.toXML(xmlWriter, progress)
jvra2ad5442002-05-17 18:36:07 +0000711 xmlWriter.endtag(name)
712 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500713 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000714 ob = self.getClass()()
715 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000716 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000717 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500718 name, attrs, content = element
719 ob.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000720 return ob
jvra2ad5442002-05-17 18:36:07 +0000721
jvr4e5af602002-05-24 09:58:04 +0000722class PrivateDictConverter(TableConverter):
723 def getClass(self):
724 return PrivateDict
jvr4756b3a2002-05-16 18:17:32 +0000725 def read(self, parent, value):
726 size, offset = value
727 file = parent.file
jvr4e5af602002-05-24 09:58:04 +0000728 priv = PrivateDict(parent.strings, file, offset)
jvr4756b3a2002-05-16 18:17:32 +0000729 file.seek(offset)
730 data = file.read(size)
731 len(data) == size
jvr4e5af602002-05-24 09:58:04 +0000732 priv.decompile(data)
733 return priv
jvrf2cf9c52002-05-23 21:50:36 +0000734 def write(self, parent, value):
735 return (0, 0) # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000736
jvr4e5af602002-05-24 09:58:04 +0000737class SubrsConverter(TableConverter):
738 def getClass(self):
739 return SubrsIndex
jvr4756b3a2002-05-16 18:17:32 +0000740 def read(self, parent, value):
741 file = parent.file
742 file.seek(parent.offset + value) # Offset(self)
jvr4e5af602002-05-24 09:58:04 +0000743 return SubrsIndex(file)
jvrf2cf9c52002-05-23 21:50:36 +0000744 def write(self, parent, value):
745 return 0 # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000746
jvr4e5af602002-05-24 09:58:04 +0000747class CharStringsConverter(TableConverter):
jvr4756b3a2002-05-16 18:17:32 +0000748 def read(self, parent, value):
749 file = parent.file
jvr767102e2002-05-17 07:06:32 +0000750 charset = parent.charset
jvra2ad5442002-05-17 18:36:07 +0000751 globalSubrs = parent.GlobalSubrs
752 if hasattr(parent, "ROS"):
753 fdSelect, fdArray = parent.FDSelect, parent.FDArray
754 private = None
755 else:
756 fdSelect, fdArray = None, None
757 private = parent.Private
jvr4756b3a2002-05-16 18:17:32 +0000758 file.seek(value) # Offset(0)
jvra2ad5442002-05-17 18:36:07 +0000759 return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
jvrf2cf9c52002-05-23 21:50:36 +0000760 def write(self, parent, value):
761 return 0 # dummy value
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500762 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +0000763 if hasattr(parent, "ROS"):
764 # if it is a CID-keyed font, then the private Dict is extracted from the parent.FDArray
765 private, fdSelect, fdArray = None, parent.FDSelect, parent.FDArray
766 else:
767 # if it is a name-keyed font, then the private dict is in the top dict, and there is no fdArray.
768 private, fdSelect, fdArray = parent.Private, None, None
769 charStrings = CharStrings(None, None, parent.GlobalSubrs, private, fdSelect, fdArray)
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500770 charStrings.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000771 return charStrings
jvr4756b3a2002-05-16 18:17:32 +0000772
773class CharsetConverter:
774 def read(self, parent, value):
775 isCID = hasattr(parent, "ROS")
776 if value > 2:
777 numGlyphs = parent.numGlyphs
778 file = parent.file
779 file.seek(value)
jvrf2cf9c52002-05-23 21:50:36 +0000780 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500781 print("loading charset at %s" % value)
jvra2ad5442002-05-17 18:36:07 +0000782 format = readCard8(file)
Just7842e561999-12-16 21:34:53 +0000783 if format == 0:
jvrc60a44f2006-10-21 13:41:18 +0000784 charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
jvr4756b3a2002-05-16 18:17:32 +0000785 elif format == 1 or format == 2:
786 charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
Just7842e561999-12-16 21:34:53 +0000787 else:
jvr1890b952002-05-15 07:41:30 +0000788 raise NotImplementedError
jvr4756b3a2002-05-16 18:17:32 +0000789 assert len(charset) == numGlyphs
jvrf2cf9c52002-05-23 21:50:36 +0000790 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500791 print(" charset end at %s" % file.tell())
jvrc60a44f2006-10-21 13:41:18 +0000792 else: # offset == 0 -> no charset data.
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500793 if isCID or "CharStrings" not in parent.rawDict:
jvrc60a44f2006-10-21 13:41:18 +0000794 assert value == 0 # We get here only when processing fontDicts from the FDArray of CFF-CID fonts. Only the real topDict references the chrset.
jvr4756b3a2002-05-16 18:17:32 +0000795 charset = None
796 elif value == 0:
jvrc60a44f2006-10-21 13:41:18 +0000797 charset = cffISOAdobeStrings
jvr4756b3a2002-05-16 18:17:32 +0000798 elif value == 1:
jvrc60a44f2006-10-21 13:41:18 +0000799 charset = cffIExpertStrings
jvr4756b3a2002-05-16 18:17:32 +0000800 elif value == 2:
jvrc60a44f2006-10-21 13:41:18 +0000801 charset = cffExpertSubsetStrings
jvr4756b3a2002-05-16 18:17:32 +0000802 return charset
jvrc60a44f2006-10-21 13:41:18 +0000803
jvrf2cf9c52002-05-23 21:50:36 +0000804 def write(self, parent, value):
805 return 0 # dummy value
jvr7ce0a132002-07-23 16:42:11 +0000806 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000807 # XXX only write charset when not in OT/TTX context, where we
808 # dump charset as a separate "GlyphOrder" table.
809 ##xmlWriter.simpletag("charset")
810 xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
jvr4756b3a2002-05-16 18:17:32 +0000811 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500812 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000813 if 0:
814 return safeEval(attrs["value"])
jvr4756b3a2002-05-16 18:17:32 +0000815
816
jvrf2cf9c52002-05-23 21:50:36 +0000817class CharsetCompiler:
818
819 def __init__(self, strings, charset, parent):
820 assert charset[0] == '.notdef'
jvred101512003-08-22 19:53:32 +0000821 isCID = hasattr(parent.dictObj, "ROS")
822 data0 = packCharset0(charset, isCID, strings)
823 data = packCharset(charset, isCID, strings)
jvr6004baf2002-05-24 10:35:13 +0000824 if len(data) < len(data0):
825 self.data = data
826 else:
827 self.data = data0
jvrf2cf9c52002-05-23 21:50:36 +0000828 self.parent = parent
829
jvr4e5af602002-05-24 09:58:04 +0000830 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000831 self.parent.rawDict["charset"] = pos
832
833 def getDataLength(self):
834 return len(self.data)
835
836 def toFile(self, file):
837 file.write(self.data)
838
839
jvred101512003-08-22 19:53:32 +0000840def getCIDfromName(name, strings):
841 return int(name[3:])
842
843def getSIDfromName(name, strings):
844 return strings.getSID(name)
845
846def packCharset0(charset, isCID, strings):
jvr6004baf2002-05-24 10:35:13 +0000847 format = 0
848 data = [packCard8(format)]
jvred101512003-08-22 19:53:32 +0000849 if isCID:
850 getNameID = getCIDfromName
851 else:
852 getNameID = getSIDfromName
853
jvr6004baf2002-05-24 10:35:13 +0000854 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000855 data.append(packCard16(getNameID(name,strings)))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -0500856 return bytesjoin(data)
jvr6004baf2002-05-24 10:35:13 +0000857
jvred101512003-08-22 19:53:32 +0000858
859def packCharset(charset, isCID, strings):
jvr6004baf2002-05-24 10:35:13 +0000860 format = 1
jvr6004baf2002-05-24 10:35:13 +0000861 ranges = []
862 first = None
863 end = 0
jvred101512003-08-22 19:53:32 +0000864 if isCID:
865 getNameID = getCIDfromName
866 else:
867 getNameID = getSIDfromName
868
jvr6004baf2002-05-24 10:35:13 +0000869 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000870 SID = getNameID(name, strings)
jvr6004baf2002-05-24 10:35:13 +0000871 if first is None:
872 first = SID
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500873 elif end + 1 != SID:
jvr6004baf2002-05-24 10:35:13 +0000874 nLeft = end - first
875 if nLeft > 255:
876 format = 2
877 ranges.append((first, nLeft))
878 first = SID
879 end = SID
880 nLeft = end - first
881 if nLeft > 255:
882 format = 2
883 ranges.append((first, nLeft))
884
jvr74cd1ef2002-05-24 10:38:04 +0000885 data = [packCard8(format)]
jvr6004baf2002-05-24 10:35:13 +0000886 if format == 1:
887 nLeftFunc = packCard8
888 else:
889 nLeftFunc = packCard16
890 for first, nLeft in ranges:
891 data.append(packCard16(first) + nLeftFunc(nLeft))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -0500892 return bytesjoin(data)
jvr6004baf2002-05-24 10:35:13 +0000893
jvrc60a44f2006-10-21 13:41:18 +0000894def parseCharset0(numGlyphs, file, strings, isCID):
jvrf2cf9c52002-05-23 21:50:36 +0000895 charset = [".notdef"]
jvrc60a44f2006-10-21 13:41:18 +0000896 if isCID:
897 for i in range(numGlyphs - 1):
898 CID = readCard16(file)
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500899 charset.append("cid" + str(CID).zfill(5))
jvrc60a44f2006-10-21 13:41:18 +0000900 else:
901 for i in range(numGlyphs - 1):
902 SID = readCard16(file)
903 charset.append(strings[SID])
jvrf2cf9c52002-05-23 21:50:36 +0000904 return charset
905
jvr4756b3a2002-05-16 18:17:32 +0000906def parseCharset(numGlyphs, file, strings, isCID, format):
907 charset = ['.notdef']
908 count = 1
909 if format == 1:
jvra2ad5442002-05-17 18:36:07 +0000910 nLeftFunc = readCard8
jvr4756b3a2002-05-16 18:17:32 +0000911 else:
jvra2ad5442002-05-17 18:36:07 +0000912 nLeftFunc = readCard16
jvr4756b3a2002-05-16 18:17:32 +0000913 while count < numGlyphs:
jvra2ad5442002-05-17 18:36:07 +0000914 first = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000915 nLeft = nLeftFunc(file)
916 if isCID:
917 for CID in range(first, first+nLeft+1):
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500918 charset.append("cid" + str(CID).zfill(5))
jvr1890b952002-05-15 07:41:30 +0000919 else:
jvr4756b3a2002-05-16 18:17:32 +0000920 for SID in range(first, first+nLeft+1):
921 charset.append(strings[SID])
922 count = count + nLeft + 1
923 return charset
924
925
jvrb9702ba2003-01-03 20:56:01 +0000926class EncodingCompiler:
927
928 def __init__(self, strings, encoding, parent):
jvr2a9bcde2008-03-07 19:56:17 +0000929 assert not isinstance(encoding, basestring)
jvrb9702ba2003-01-03 20:56:01 +0000930 data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
931 data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
932 if len(data0) < len(data1):
933 self.data = data0
934 else:
935 self.data = data1
936 self.parent = parent
937
938 def setPos(self, pos, endPos):
939 self.parent.rawDict["Encoding"] = pos
940
941 def getDataLength(self):
942 return len(self.data)
943
944 def toFile(self, file):
945 file.write(self.data)
946
947
948class EncodingConverter(SimpleConverter):
949
950 def read(self, parent, value):
951 if value == 0:
952 return "StandardEncoding"
953 elif value == 1:
954 return "ExpertEncoding"
955 else:
956 assert value > 1
957 file = parent.file
958 file.seek(value)
959 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500960 print("loading Encoding at %s" % value)
jvrb9702ba2003-01-03 20:56:01 +0000961 format = readCard8(file)
962 haveSupplement = format & 0x80
963 if haveSupplement:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -0500964 raise NotImplementedError("Encoding supplements are not yet supported")
jvrb9702ba2003-01-03 20:56:01 +0000965 format = format & 0x7f
966 if format == 0:
967 encoding = parseEncoding0(parent.charset, file, haveSupplement,
968 parent.strings)
969 elif format == 1:
970 encoding = parseEncoding1(parent.charset, file, haveSupplement,
971 parent.strings)
972 return encoding
973
974 def write(self, parent, value):
975 if value == "StandardEncoding":
976 return 0
977 elif value == "ExpertEncoding":
978 return 1
979 return 0 # dummy value
980
981 def xmlWrite(self, xmlWriter, name, value, progress):
982 if value in ("StandardEncoding", "ExpertEncoding"):
983 xmlWriter.simpletag(name, name=value)
984 xmlWriter.newline()
985 return
986 xmlWriter.begintag(name)
987 xmlWriter.newline()
988 for code in range(len(value)):
989 glyphName = value[code]
990 if glyphName != ".notdef":
991 xmlWriter.simpletag("map", code=hex(code), name=glyphName)
992 xmlWriter.newline()
993 xmlWriter.endtag(name)
994 xmlWriter.newline()
995
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500996 def xmlRead(self, name, attrs, content, parent):
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500997 if "name" in attrs:
jvrb9702ba2003-01-03 20:56:01 +0000998 return attrs["name"]
999 encoding = [".notdef"] * 256
1000 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +00001001 if isinstance(element, basestring):
jvrb9702ba2003-01-03 20:56:01 +00001002 continue
1003 name, attrs, content = element
1004 code = safeEval(attrs["code"])
1005 glyphName = attrs["name"]
1006 encoding[code] = glyphName
1007 return encoding
1008
1009
1010def parseEncoding0(charset, file, haveSupplement, strings):
1011 nCodes = readCard8(file)
1012 encoding = [".notdef"] * 256
1013 for glyphID in range(1, nCodes + 1):
1014 code = readCard8(file)
1015 if code != 0:
1016 encoding[code] = charset[glyphID]
1017 return encoding
1018
1019def parseEncoding1(charset, file, haveSupplement, strings):
1020 nRanges = readCard8(file)
1021 encoding = [".notdef"] * 256
1022 glyphID = 1
1023 for i in range(nRanges):
1024 code = readCard8(file)
1025 nLeft = readCard8(file)
1026 for glyphID in range(glyphID, glyphID + nLeft + 1):
1027 encoding[code] = charset[glyphID]
1028 code = code + 1
1029 glyphID = glyphID + 1
1030 return encoding
1031
1032def packEncoding0(charset, encoding, strings):
1033 format = 0
1034 m = {}
1035 for code in range(len(encoding)):
1036 name = encoding[code]
1037 if name != ".notdef":
1038 m[name] = code
1039 codes = []
1040 for name in charset[1:]:
1041 code = m.get(name)
1042 codes.append(code)
1043
1044 while codes and codes[-1] is None:
1045 codes.pop()
1046
1047 data = [packCard8(format), packCard8(len(codes))]
1048 for code in codes:
1049 if code is None:
1050 code = 0
1051 data.append(packCard8(code))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001052 return bytesjoin(data)
jvrb9702ba2003-01-03 20:56:01 +00001053
1054def packEncoding1(charset, encoding, strings):
1055 format = 1
1056 m = {}
1057 for code in range(len(encoding)):
1058 name = encoding[code]
1059 if name != ".notdef":
1060 m[name] = code
1061 ranges = []
1062 first = None
1063 end = 0
1064 for name in charset[1:]:
1065 code = m.get(name, -1)
1066 if first is None:
1067 first = code
Behdad Esfahbod180ace62013-11-27 02:40:30 -05001068 elif end + 1 != code:
jvrb9702ba2003-01-03 20:56:01 +00001069 nLeft = end - first
1070 ranges.append((first, nLeft))
1071 first = code
1072 end = code
1073 nLeft = end - first
1074 ranges.append((first, nLeft))
1075
1076 # remove unencoded glyphs at the end.
1077 while ranges and ranges[-1][0] == -1:
1078 ranges.pop()
1079
1080 data = [packCard8(format), packCard8(len(ranges))]
1081 for first, nLeft in ranges:
1082 if first == -1: # unencoded
1083 first = 0
1084 data.append(packCard8(first) + packCard8(nLeft))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001085 return bytesjoin(data)
jvrb9702ba2003-01-03 20:56:01 +00001086
1087
jvr4e5af602002-05-24 09:58:04 +00001088class FDArrayConverter(TableConverter):
jvred101512003-08-22 19:53:32 +00001089
jvra2ad5442002-05-17 18:36:07 +00001090 def read(self, parent, value):
1091 file = parent.file
1092 file.seek(value)
jvred101512003-08-22 19:53:32 +00001093 fdArray = FDArrayIndex(file)
jvra2ad5442002-05-17 18:36:07 +00001094 fdArray.strings = parent.strings
1095 fdArray.GlobalSubrs = parent.GlobalSubrs
1096 return fdArray
1097
jvred101512003-08-22 19:53:32 +00001098 def write(self, parent, value):
1099 return 0 # dummy value
1100
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001101 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001102 fdArray = FDArrayIndex()
1103 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +00001104 if isinstance(element, basestring):
jvred101512003-08-22 19:53:32 +00001105 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001106 name, attrs, content = element
1107 fdArray.fromXML(name, attrs, content)
jvred101512003-08-22 19:53:32 +00001108 return fdArray
1109
jvra2ad5442002-05-17 18:36:07 +00001110
1111class FDSelectConverter:
jvred101512003-08-22 19:53:32 +00001112
jvra2ad5442002-05-17 18:36:07 +00001113 def read(self, parent, value):
1114 file = parent.file
1115 file.seek(value)
jvred101512003-08-22 19:53:32 +00001116 fdSelect = FDSelect(file, parent.numGlyphs)
1117 return fdSelect
1118
1119 def write(self, parent, value):
1120 return 0 # dummy value
1121
1122 # The FDSelect glyph data is written out to XML in the charstring keys,
1123 # so we write out only the format selector
jvr7ce0a132002-07-23 16:42:11 +00001124 def xmlWrite(self, xmlWriter, name, value, progress):
jvred101512003-08-22 19:53:32 +00001125 xmlWriter.simpletag(name, [('format', value.format)])
1126 xmlWriter.newline()
1127
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001128 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001129 format = safeEval(attrs["format"])
1130 file = None
1131 numGlyphs = None
1132 fdSelect = FDSelect(file, numGlyphs, format)
1133 return fdSelect
1134
1135
1136def packFDSelect0(fdSelectArray):
1137 format = 0
1138 data = [packCard8(format)]
1139 for index in fdSelectArray:
1140 data.append(packCard8(index))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001141 return bytesjoin(data)
jvred101512003-08-22 19:53:32 +00001142
1143
1144def packFDSelect3(fdSelectArray):
1145 format = 3
1146 fdRanges = []
1147 first = None
1148 end = 0
1149 lenArray = len(fdSelectArray)
1150 lastFDIndex = -1
1151 for i in range(lenArray):
1152 fdIndex = fdSelectArray[i]
1153 if lastFDIndex != fdIndex:
1154 fdRanges.append([i, fdIndex])
1155 lastFDIndex = fdIndex
1156 sentinelGID = i + 1
1157
1158 data = [packCard8(format)]
1159 data.append(packCard16( len(fdRanges) ))
1160 for fdRange in fdRanges:
1161 data.append(packCard16(fdRange[0]))
1162 data.append(packCard8(fdRange[1]))
1163 data.append(packCard16(sentinelGID))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001164 return bytesjoin(data)
jvred101512003-08-22 19:53:32 +00001165
1166
1167class FDSelectCompiler:
1168
1169 def __init__(self, fdSelect, parent):
1170 format = fdSelect.format
1171 fdSelectArray = fdSelect.gidArray
1172 if format == 0:
1173 self.data = packFDSelect0(fdSelectArray)
1174 elif format == 3:
1175 self.data = packFDSelect3(fdSelectArray)
1176 else:
1177 # choose smaller of the two formats
1178 data0 = packFDSelect0(fdSelectArray)
1179 data3 = packFDSelect3(fdSelectArray)
1180 if len(data0) < len(data3):
1181 self.data = data0
1182 fdSelect.format = 0
1183 else:
1184 self.data = data3
1185 fdSelect.format = 3
1186
1187 self.parent = parent
1188
1189 def setPos(self, pos, endPos):
1190 self.parent.rawDict["FDSelect"] = pos
1191
1192 def getDataLength(self):
1193 return len(self.data)
1194
1195 def toFile(self, file):
1196 file.write(self.data)
jvra2ad5442002-05-17 18:36:07 +00001197
1198
jvr4e5af602002-05-24 09:58:04 +00001199class ROSConverter(SimpleConverter):
jvred101512003-08-22 19:53:32 +00001200
jvr7ce0a132002-07-23 16:42:11 +00001201 def xmlWrite(self, xmlWriter, name, value, progress):
jvr155aa752002-05-17 19:58:49 +00001202 registry, order, supplement = value
jvrf2cf9c52002-05-23 21:50:36 +00001203 xmlWriter.simpletag(name, [('Registry', registry), ('Order', order),
1204 ('Supplement', supplement)])
jvr4afb2572002-05-18 20:07:01 +00001205 xmlWriter.newline()
jvr155aa752002-05-17 19:58:49 +00001206
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001207 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001208 return (attrs['Registry'], attrs['Order'], safeEval(attrs['Supplement']))
1209
1210
jvr155aa752002-05-17 19:58:49 +00001211
jvr4756b3a2002-05-16 18:17:32 +00001212topDictOperators = [
1213# opcode name argument type default converter
jvr155aa752002-05-17 19:58:49 +00001214 ((12, 30), 'ROS', ('SID','SID','number'), None, ROSConverter()),
jvrf2cf9c52002-05-23 21:50:36 +00001215 ((12, 20), 'SyntheticBase', 'number', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001216 (0, 'version', 'SID', None, None),
jvre2ca9b52002-09-09 14:18:39 +00001217 (1, 'Notice', 'SID', None, Latin1Converter()),
1218 ((12, 0), 'Copyright', 'SID', None, Latin1Converter()),
jvr4756b3a2002-05-16 18:17:32 +00001219 (2, 'FullName', 'SID', None, None),
jvr155aa752002-05-17 19:58:49 +00001220 ((12, 38), 'FontName', 'SID', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001221 (3, 'FamilyName', 'SID', None, None),
1222 (4, 'Weight', 'SID', None, None),
1223 ((12, 1), 'isFixedPitch', 'number', 0, None),
1224 ((12, 2), 'ItalicAngle', 'number', 0, None),
1225 ((12, 3), 'UnderlinePosition', 'number', None, None),
1226 ((12, 4), 'UnderlineThickness', 'number', 50, None),
1227 ((12, 5), 'PaintType', 'number', 0, None),
1228 ((12, 6), 'CharstringType', 'number', 2, None),
1229 ((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0], None),
1230 (13, 'UniqueID', 'number', None, None),
1231 (5, 'FontBBox', 'array', [0,0,0,0], None),
1232 ((12, 8), 'StrokeWidth', 'number', 0, None),
1233 (14, 'XUID', 'array', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001234 ((12, 21), 'PostScript', 'SID', None, None),
1235 ((12, 22), 'BaseFontName', 'SID', None, None),
1236 ((12, 23), 'BaseFontBlend', 'delta', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001237 ((12, 31), 'CIDFontVersion', 'number', 0, None),
1238 ((12, 32), 'CIDFontRevision', 'number', 0, None),
1239 ((12, 33), 'CIDFontType', 'number', 0, None),
1240 ((12, 34), 'CIDCount', 'number', 8720, None),
jvred101512003-08-22 19:53:32 +00001241 (15, 'charset', 'number', 0, CharsetConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001242 ((12, 35), 'UIDBase', 'number', None, None),
jvrb9702ba2003-01-03 20:56:01 +00001243 (16, 'Encoding', 'number', 0, EncodingConverter()),
jvr155aa752002-05-17 19:58:49 +00001244 (18, 'Private', ('number','number'), None, PrivateDictConverter()),
jvred101512003-08-22 19:53:32 +00001245 ((12, 37), 'FDSelect', 'number', None, FDSelectConverter()),
1246 ((12, 36), 'FDArray', 'number', None, FDArrayConverter()),
jvr155aa752002-05-17 19:58:49 +00001247 (17, 'CharStrings', 'number', None, CharStringsConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001248]
1249
jvred101512003-08-22 19:53:32 +00001250# Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order,
1251# in order for the font to compile back from xml.
1252
1253
jvr4756b3a2002-05-16 18:17:32 +00001254privateDictOperators = [
1255# opcode name argument type default converter
1256 (6, 'BlueValues', 'delta', None, None),
1257 (7, 'OtherBlues', 'delta', None, None),
1258 (8, 'FamilyBlues', 'delta', None, None),
1259 (9, 'FamilyOtherBlues', 'delta', None, None),
1260 ((12, 9), 'BlueScale', 'number', 0.039625, None),
1261 ((12, 10), 'BlueShift', 'number', 7, None),
1262 ((12, 11), 'BlueFuzz', 'number', 1, None),
1263 (10, 'StdHW', 'number', None, None),
1264 (11, 'StdVW', 'number', None, None),
1265 ((12, 12), 'StemSnapH', 'delta', None, None),
1266 ((12, 13), 'StemSnapV', 'delta', None, None),
1267 ((12, 14), 'ForceBold', 'number', 0, None),
jvrf2cf9c52002-05-23 21:50:36 +00001268 ((12, 15), 'ForceBoldThreshold', 'number', None, None), # deprecated
1269 ((12, 16), 'lenIV', 'number', None, None), # deprecated
jvr4756b3a2002-05-16 18:17:32 +00001270 ((12, 17), 'LanguageGroup', 'number', 0, None),
1271 ((12, 18), 'ExpansionFactor', 'number', 0.06, None),
1272 ((12, 19), 'initialRandomSeed', 'number', 0, None),
1273 (20, 'defaultWidthX', 'number', 0, None),
1274 (21, 'nominalWidthX', 'number', 0, None),
1275 (19, 'Subrs', 'number', None, SubrsConverter()),
1276]
1277
jvr4e5af602002-05-24 09:58:04 +00001278def addConverters(table):
1279 for i in range(len(table)):
1280 op, name, arg, default, conv = table[i]
1281 if conv is not None:
1282 continue
1283 if arg in ("delta", "array"):
1284 conv = ArrayConverter()
1285 elif arg == "number":
1286 conv = NumberConverter()
1287 elif arg == "SID":
1288 conv = SimpleConverter()
1289 else:
1290 assert 0
1291 table[i] = op, name, arg, default, conv
1292
1293addConverters(privateDictOperators)
1294addConverters(topDictOperators)
1295
jvr4756b3a2002-05-16 18:17:32 +00001296
1297class TopDictDecompiler(psCharStrings.DictDecompiler):
1298 operators = buildOperatorDict(topDictOperators)
1299
1300
1301class PrivateDictDecompiler(psCharStrings.DictDecompiler):
1302 operators = buildOperatorDict(privateDictOperators)
1303
1304
jvrf2cf9c52002-05-23 21:50:36 +00001305class DictCompiler:
1306
1307 def __init__(self, dictObj, strings, parent):
1308 assert isinstance(strings, IndexedStrings)
1309 self.dictObj = dictObj
1310 self.strings = strings
1311 self.parent = parent
1312 rawDict = {}
1313 for name in dictObj.order:
1314 value = getattr(dictObj, name, None)
1315 if value is None:
1316 continue
1317 conv = dictObj.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001318 value = conv.write(dictObj, value)
jvrf2cf9c52002-05-23 21:50:36 +00001319 if value == dictObj.defaults.get(name):
1320 continue
1321 rawDict[name] = value
1322 self.rawDict = rawDict
1323
jvr4e5af602002-05-24 09:58:04 +00001324 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +00001325 pass
1326
1327 def getDataLength(self):
jvr4e5af602002-05-24 09:58:04 +00001328 return len(self.compile("getDataLength"))
jvrf2cf9c52002-05-23 21:50:36 +00001329
jvr4e5af602002-05-24 09:58:04 +00001330 def compile(self, reason):
1331 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001332 print("-- compiling %s for %s" % (self.__class__.__name__, reason))
1333 print("in baseDict: ", self)
jvrf2cf9c52002-05-23 21:50:36 +00001334 rawDict = self.rawDict
1335 data = []
1336 for name in self.dictObj.order:
1337 value = rawDict.get(name)
1338 if value is None:
1339 continue
1340 op, argType = self.opcodes[name]
jvr2a9bcde2008-03-07 19:56:17 +00001341 if isinstance(argType, tuple):
jvrf2cf9c52002-05-23 21:50:36 +00001342 l = len(argType)
1343 assert len(value) == l, "value doesn't match arg type"
1344 for i in range(l):
jvred101512003-08-22 19:53:32 +00001345 arg = argType[i]
jvrf2cf9c52002-05-23 21:50:36 +00001346 v = value[i]
1347 arghandler = getattr(self, "arg_" + arg)
1348 data.append(arghandler(v))
1349 else:
1350 arghandler = getattr(self, "arg_" + argType)
1351 data.append(arghandler(value))
1352 data.append(op)
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001353 return bytesjoin(data)
jvrf2cf9c52002-05-23 21:50:36 +00001354
1355 def toFile(self, file):
jvr4e5af602002-05-24 09:58:04 +00001356 file.write(self.compile("toFile"))
jvrf2cf9c52002-05-23 21:50:36 +00001357
1358 def arg_number(self, num):
1359 return encodeNumber(num)
1360 def arg_SID(self, s):
1361 return psCharStrings.encodeIntCFF(self.strings.getSID(s))
1362 def arg_array(self, value):
1363 data = []
1364 for num in value:
1365 data.append(encodeNumber(num))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001366 return bytesjoin(data)
jvrf2cf9c52002-05-23 21:50:36 +00001367 def arg_delta(self, value):
1368 out = []
1369 last = 0
1370 for v in value:
1371 out.append(v - last)
1372 last = v
1373 data = []
1374 for num in out:
1375 data.append(encodeNumber(num))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001376 return bytesjoin(data)
jvrf2cf9c52002-05-23 21:50:36 +00001377
1378
1379def encodeNumber(num):
jvrf6ff48b2008-03-07 19:49:25 +00001380 if isinstance(num, float):
jvrf2cf9c52002-05-23 21:50:36 +00001381 return psCharStrings.encodeFloat(num)
1382 else:
1383 return psCharStrings.encodeIntCFF(num)
1384
1385
1386class TopDictCompiler(DictCompiler):
1387
1388 opcodes = buildOpcodeDict(topDictOperators)
1389
1390 def getChildren(self, strings):
1391 children = []
jvred101512003-08-22 19:53:32 +00001392 if hasattr(self.dictObj, "charset") and self.dictObj.charset:
jvrf2cf9c52002-05-23 21:50:36 +00001393 children.append(CharsetCompiler(strings, self.dictObj.charset, self))
jvrb9702ba2003-01-03 20:56:01 +00001394 if hasattr(self.dictObj, "Encoding"):
1395 encoding = self.dictObj.Encoding
jvr2a9bcde2008-03-07 19:56:17 +00001396 if not isinstance(encoding, basestring):
jvrb9702ba2003-01-03 20:56:01 +00001397 children.append(EncodingCompiler(strings, encoding, self))
jvrce522412003-08-25 07:37:25 +00001398 if hasattr(self.dictObj, "FDSelect"):
jvred101512003-08-22 19:53:32 +00001399 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1400 # issues about merging the FDArrays. Here I assume that
1401 # either the font was read from XML, and teh FDSelect indices are all
1402 # in the charstring data, or the FDSelect array is already fully defined.
1403 fdSelect = self.dictObj.FDSelect
1404 if len(fdSelect) == 0: # probably read in from XML; assume fdIndex in CharString data
1405 charStrings = self.dictObj.CharStrings
1406 for name in self.dictObj.charset:
1407 charstring = charStrings[name]
1408 fdSelect.append(charStrings[name].fdSelectIndex)
1409 fdSelectComp = FDSelectCompiler(fdSelect, self)
1410 children.append(fdSelectComp)
jvrf2cf9c52002-05-23 21:50:36 +00001411 if hasattr(self.dictObj, "CharStrings"):
1412 items = []
1413 charStrings = self.dictObj.CharStrings
1414 for name in self.dictObj.charset:
1415 items.append(charStrings[name])
1416 charStringsComp = CharStringsCompiler(items, strings, self)
1417 children.append(charStringsComp)
jvrce522412003-08-25 07:37:25 +00001418 if hasattr(self.dictObj, "FDArray"):
jvred101512003-08-22 19:53:32 +00001419 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1420 # issues about merging the FDArrays. Here I assume that the FDArray info is correct
1421 # and complete.
1422 fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self)
1423 children.append(fdArrayIndexComp)
1424 children.extend(fdArrayIndexComp.getChildren(strings))
1425 if hasattr(self.dictObj, "Private"):
1426 privComp = self.dictObj.Private.getCompiler(strings, self)
1427 children.append(privComp)
1428 children.extend(privComp.getChildren(strings))
1429 return children
1430
1431
1432class FontDictCompiler(DictCompiler):
1433
1434 opcodes = buildOpcodeDict(topDictOperators)
1435
1436 def getChildren(self, strings):
1437 children = []
jvrf2cf9c52002-05-23 21:50:36 +00001438 if hasattr(self.dictObj, "Private"):
1439 privComp = self.dictObj.Private.getCompiler(strings, self)
1440 children.append(privComp)
1441 children.extend(privComp.getChildren(strings))
1442 return children
1443
1444
1445class PrivateDictCompiler(DictCompiler):
1446
1447 opcodes = buildOpcodeDict(privateDictOperators)
1448
jvr4e5af602002-05-24 09:58:04 +00001449 def setPos(self, pos, endPos):
1450 size = endPos - pos
jvrf2cf9c52002-05-23 21:50:36 +00001451 self.parent.rawDict["Private"] = size, pos
1452 self.pos = pos
1453
1454 def getChildren(self, strings):
1455 children = []
1456 if hasattr(self.dictObj, "Subrs"):
1457 children.append(self.dictObj.Subrs.getCompiler(strings, self))
1458 return children
1459
jvr4756b3a2002-05-16 18:17:32 +00001460
1461class BaseDict:
1462
jvr4e5af602002-05-24 09:58:04 +00001463 def __init__(self, strings=None, file=None, offset=None):
jvr4756b3a2002-05-16 18:17:32 +00001464 self.rawDict = {}
jvr767102e2002-05-17 07:06:32 +00001465 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001466 print("loading %s at %s" % (self.__class__.__name__, offset))
jvr4756b3a2002-05-16 18:17:32 +00001467 self.file = file
1468 self.offset = offset
1469 self.strings = strings
jvr155aa752002-05-17 19:58:49 +00001470 self.skipNames = []
jvr4756b3a2002-05-16 18:17:32 +00001471
1472 def decompile(self, data):
jvrf2cf9c52002-05-23 21:50:36 +00001473 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001474 print(" length %s is %s" % (self.__class__.__name__, len(data)))
jvrf2cf9c52002-05-23 21:50:36 +00001475 dec = self.decompilerClass(self.strings)
jvr4756b3a2002-05-16 18:17:32 +00001476 dec.decompile(data)
1477 self.rawDict = dec.getDict()
1478 self.postDecompile()
1479
1480 def postDecompile(self):
1481 pass
1482
jvrf2cf9c52002-05-23 21:50:36 +00001483 def getCompiler(self, strings, parent):
1484 return self.compilerClass(self, strings, parent)
1485
jvr4756b3a2002-05-16 18:17:32 +00001486 def __getattr__(self, name):
1487 value = self.rawDict.get(name)
1488 if value is None:
1489 value = self.defaults.get(name)
1490 if value is None:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -05001491 raise AttributeError(name)
jvr4756b3a2002-05-16 18:17:32 +00001492 conv = self.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001493 value = conv.read(self, value)
jvr4756b3a2002-05-16 18:17:32 +00001494 setattr(self, name, value)
1495 return value
1496
1497 def toXML(self, xmlWriter, progress):
1498 for name in self.order:
jvr155aa752002-05-17 19:58:49 +00001499 if name in self.skipNames:
1500 continue
jvr4756b3a2002-05-16 18:17:32 +00001501 value = getattr(self, name, None)
1502 if value is None:
1503 continue
jvr4e5af602002-05-24 09:58:04 +00001504 conv = self.converters[name]
jvr7ce0a132002-07-23 16:42:11 +00001505 conv.xmlWrite(xmlWriter, name, value, progress)
jvr4e5af602002-05-24 09:58:04 +00001506
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001507 def fromXML(self, name, attrs, content):
jvr4e5af602002-05-24 09:58:04 +00001508 conv = self.converters[name]
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001509 value = conv.xmlRead(name, attrs, content, self)
jvr4e5af602002-05-24 09:58:04 +00001510 setattr(self, name, value)
jvr4756b3a2002-05-16 18:17:32 +00001511
1512
1513class TopDict(BaseDict):
1514
1515 defaults = buildDefaults(topDictOperators)
1516 converters = buildConverters(topDictOperators)
1517 order = buildOrder(topDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001518 decompilerClass = TopDictDecompiler
1519 compilerClass = TopDictCompiler
Just7842e561999-12-16 21:34:53 +00001520
jvr4e5af602002-05-24 09:58:04 +00001521 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
jvr016ca762002-05-16 18:38:03 +00001522 BaseDict.__init__(self, strings, file, offset)
1523 self.GlobalSubrs = GlobalSubrs
1524
Just7842e561999-12-16 21:34:53 +00001525 def getGlyphOrder(self):
1526 return self.charset
1527
jvr4756b3a2002-05-16 18:17:32 +00001528 def postDecompile(self):
1529 offset = self.rawDict.get("CharStrings")
1530 if offset is None:
1531 return
1532 # get the number of glyphs beforehand.
1533 self.file.seek(offset)
jvra2ad5442002-05-17 18:36:07 +00001534 self.numGlyphs = readCard16(self.file)
Just7842e561999-12-16 21:34:53 +00001535
jvr016ca762002-05-16 18:38:03 +00001536 def toXML(self, xmlWriter, progress):
jvr155aa752002-05-17 19:58:49 +00001537 if hasattr(self, "CharStrings"):
jvr7ce0a132002-07-23 16:42:11 +00001538 self.decompileAllCharStrings(progress)
jvred101512003-08-22 19:53:32 +00001539 if hasattr(self, "ROS"):
1540 self.skipNames = ['Encoding']
jvr155aa752002-05-17 19:58:49 +00001541 if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
1542 # these values have default values, but I only want them to show up
1543 # in CID fonts.
1544 self.skipNames = ['CIDFontVersion', 'CIDFontRevision', 'CIDFontType',
1545 'CIDCount']
jvr016ca762002-05-16 18:38:03 +00001546 BaseDict.toXML(self, xmlWriter, progress)
jvr767102e2002-05-17 07:06:32 +00001547
jvr7ce0a132002-07-23 16:42:11 +00001548 def decompileAllCharStrings(self, progress):
jvr4e5af602002-05-24 09:58:04 +00001549 # XXX only when doing ttdump -i?
jvr7ce0a132002-07-23 16:42:11 +00001550 i = 0
jvra2ad5442002-05-17 18:36:07 +00001551 for charString in self.CharStrings.values():
jvred101512003-08-22 19:53:32 +00001552 try:
1553 charString.decompile()
1554 except:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001555 print("Error in charstring ", i)
jvred101512003-08-22 19:53:32 +00001556 import sys
1557 type, value = sys. exc_info()[0:2]
1558 raise type(value)
jvr7ce0a132002-07-23 16:42:11 +00001559 if not i % 30 and progress:
1560 progress.increment(0) # update
1561 i = i + 1
Just7842e561999-12-16 21:34:53 +00001562
1563
jvred101512003-08-22 19:53:32 +00001564class FontDict(BaseDict):
1565
1566 defaults = buildDefaults(topDictOperators)
1567 converters = buildConverters(topDictOperators)
1568 order = buildOrder(topDictOperators)
1569 decompilerClass = None
1570 compilerClass = FontDictCompiler
1571
1572 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
1573 BaseDict.__init__(self, strings, file, offset)
1574 self.GlobalSubrs = GlobalSubrs
1575
1576 def getGlyphOrder(self):
1577 return self.charset
1578
1579 def toXML(self, xmlWriter, progress):
1580 self.skipNames = ['Encoding']
1581 BaseDict.toXML(self, xmlWriter, progress)
1582
1583
1584
jvr4756b3a2002-05-16 18:17:32 +00001585class PrivateDict(BaseDict):
1586 defaults = buildDefaults(privateDictOperators)
1587 converters = buildConverters(privateDictOperators)
1588 order = buildOrder(privateDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001589 decompilerClass = PrivateDictDecompiler
1590 compilerClass = PrivateDictCompiler
Just7842e561999-12-16 21:34:53 +00001591
1592
jvre3275582002-05-14 12:22:03 +00001593class IndexedStrings:
1594
jvr767102e2002-05-17 07:06:32 +00001595 """SID -> string mapping."""
1596
1597 def __init__(self, file=None):
1598 if file is None:
jvre3275582002-05-14 12:22:03 +00001599 strings = []
jvr767102e2002-05-17 07:06:32 +00001600 else:
jvr4e5af602002-05-24 09:58:04 +00001601 strings = list(Index(file))
jvre3275582002-05-14 12:22:03 +00001602 self.strings = strings
1603
jvrf2cf9c52002-05-23 21:50:36 +00001604 def getCompiler(self):
1605 return IndexedStringsCompiler(self, None, None)
1606
1607 def __len__(self):
1608 return len(self.strings)
1609
jvre3275582002-05-14 12:22:03 +00001610 def __getitem__(self, SID):
1611 if SID < cffStandardStringCount:
1612 return cffStandardStrings[SID]
1613 else:
1614 return self.strings[SID - cffStandardStringCount]
1615
1616 def getSID(self, s):
1617 if not hasattr(self, "stringMapping"):
1618 self.buildStringMapping()
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -05001619 if s in cffStandardStringMapping:
jvre3275582002-05-14 12:22:03 +00001620 SID = cffStandardStringMapping[s]
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -05001621 elif s in self.stringMapping:
jvre3275582002-05-14 12:22:03 +00001622 SID = self.stringMapping[s]
1623 else:
1624 SID = len(self.strings) + cffStandardStringCount
1625 self.strings.append(s)
1626 self.stringMapping[s] = SID
1627 return SID
1628
1629 def getStrings(self):
1630 return self.strings
1631
1632 def buildStringMapping(self):
1633 self.stringMapping = {}
1634 for index in range(len(self.strings)):
1635 self.stringMapping[self.strings[index]] = index + cffStandardStringCount
1636
1637
Just7842e561999-12-16 21:34:53 +00001638# The 391 Standard Strings as used in the CFF format.
1639# from Adobe Technical None #5176, version 1.0, 18 March 1998
1640
1641cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
1642 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
1643 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
1644 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
1645 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C',
1646 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
1647 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
1648 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
1649 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
1650 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
1651 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
1652 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
1653 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
1654 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
1655 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
1656 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
1657 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
1658 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
1659 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
1660 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
1661 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
1662 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
1663 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
1664 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
1665 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
1666 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
1667 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
1668 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
1669 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
1670 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
1671 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
1672 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
1673 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
1674 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
1675 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
1676 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
1677 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
1678 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
1679 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
1680 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
1681 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
1682 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
1683 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
1684 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
1685 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
1686 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
1687 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
1688 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
1689 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
1690 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
1691 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
1692 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
1693 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
1694 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
1695 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
1696 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
1697 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
1698 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
1699 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
1700 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
1701 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
1702 '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
1703 'Semibold'
1704]
1705
1706cffStandardStringCount = 391
1707assert len(cffStandardStrings) == cffStandardStringCount
1708# build reverse mapping
1709cffStandardStringMapping = {}
1710for _i in range(cffStandardStringCount):
1711 cffStandardStringMapping[cffStandardStrings[_i]] = _i
jvrc60a44f2006-10-21 13:41:18 +00001712
1713cffISOAdobeStrings = [".notdef", "space", "exclam", "quotedbl", "numbersign",
1714"dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright",
1715"asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two",
1716"three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon",
1717"less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G",
1718"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
1719"X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum",
1720"underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
1721"k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
1722"braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent",
1723"sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle",
1724"quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl",
1725"endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet",
1726"quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis",
1727"perthousand", "questiondown", "grave", "acute", "circumflex", "tilde",
1728"macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
1729"ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE",
1730"ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls",
1731"onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus",
1732"Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn",
1733"threequarters", "twosuperior", "registered", "minus", "eth", "multiply",
1734"threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
1735"Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave",
1736"Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute",
1737"Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute",
1738"Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute",
1739"acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute",
1740"ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis",
1741"igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde",
1742"scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis",
1743"zcaron"]
1744
1745cffISOAdobeStringCount = 229
1746assert len(cffISOAdobeStrings) == cffISOAdobeStringCount
1747
1748cffIExpertStrings = [".notdef", "space", "exclamsmall", "Hungarumlautsmall",
1749"dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall",
1750"parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader",
1751"comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle",
1752"twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle",
1753"sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon",
1754"commasuperior", "threequartersemdash", "periodsuperior", "questionsmall",
1755"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1756"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1757"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1758"parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
1759"Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall",
1760"Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
1761"Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall",
1762"Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall",
1763"exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall",
1764"Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall",
1765"figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall",
1766"onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth",
1767"threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds",
1768"zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior",
1769"fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior",
1770"zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior",
1771"fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior",
1772"centinferior", "dollarinferior", "periodinferior", "commainferior",
1773"Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall",
1774"Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall",
1775"Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall",
1776"Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall",
1777"Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall",
1778"Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall",
1779"Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall",
1780"Ydieresissmall"]
1781
1782cffExpertStringCount = 166
1783assert len(cffIExpertStrings) == cffExpertStringCount
1784
1785cffExpertSubsetStrings = [".notdef", "space", "dollaroldstyle",
1786"dollarsuperior", "parenleftsuperior", "parenrightsuperior", "twodotenleader",
1787"onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle",
1788"oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle",
1789"sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon",
1790"semicolon", "commasuperior", "threequartersemdash", "periodsuperior",
1791"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1792"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1793"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1794"parenrightinferior", "hyphensuperior", "colonmonetary", "onefitted", "rupiah",
1795"centoldstyle", "figuredash", "hypheninferior", "onequarter", "onehalf",
1796"threequarters", "oneeighth", "threeeighths", "fiveeighths", "seveneighths",
1797"onethird", "twothirds", "zerosuperior", "onesuperior", "twosuperior",
1798"threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior",
1799"eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior",
1800"threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior",
1801"eightinferior", "nineinferior", "centinferior", "dollarinferior",
1802"periodinferior", "commainferior"]
1803
1804cffExpertSubsetStringCount = 87
1805assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount