blob: cd5a92f4f5f2732a2558b05341aa45d9a4e6502f [file] [log] [blame]
Just7842e561999-12-16 21:34:53 +00001"""cffLib.py -- read/write tools for Adobe CFF fonts."""
2
Behdad Esfahbod1ae29592014-01-14 15:07:50 +08003from __future__ import print_function, division, absolute_import
Behdad Esfahbod30e691e2013-11-27 17:27:45 -05004from fontTools.misc.py23 import *
Behdad Esfahbod8413c102013-09-17 16:59:39 -04005from fontTools.misc import sstruct
Just528614e2000-01-16 22:14:02 +00006from fontTools.misc import psCharStrings
jvr4e5af602002-05-24 09:58:04 +00007from fontTools.misc.textTools import safeEval
Behdad Esfahbod30e691e2013-11-27 17:27:45 -05008import struct
Behdad Esfahbod2a9b8682013-11-27 05:52:33 -05009
jvr767102e2002-05-17 07:06:32 +000010DEBUG = 0
11
12
Just7842e561999-12-16 21:34:53 +000013cffHeaderFormat = """
14 major: B
15 minor: B
16 hdrSize: B
17 offSize: B
18"""
19
Behdad Esfahbode388db52013-11-28 14:26:58 -050020class CFFFontSet(object):
Just7842e561999-12-16 21:34:53 +000021
22 def __init__(self):
jvr4756b3a2002-05-16 18:17:32 +000023 pass
Just7842e561999-12-16 21:34:53 +000024
jvr4e5af602002-05-24 09:58:04 +000025 def decompile(self, file, otFont):
jvra2a75b32002-05-13 11:25:17 +000026 sstruct.unpack(cffHeaderFormat, file.read(4), self)
Just7842e561999-12-16 21:34:53 +000027 assert self.major == 1 and self.minor == 0, \
28 "unknown CFF format: %d.%d" % (self.major, self.minor)
Just7842e561999-12-16 21:34:53 +000029
jvrf2cf9c52002-05-23 21:50:36 +000030 file.seek(self.hdrSize)
jvr4e5af602002-05-24 09:58:04 +000031 self.fontNames = list(Index(file))
jvr4756b3a2002-05-16 18:17:32 +000032 self.topDictIndex = TopDictIndex(file)
jvr767102e2002-05-17 07:06:32 +000033 self.strings = IndexedStrings(file)
jvr4e5af602002-05-24 09:58:04 +000034 self.GlobalSubrs = GlobalSubrsIndex(file)
jvr4756b3a2002-05-16 18:17:32 +000035 self.topDictIndex.strings = self.strings
jvr016ca762002-05-16 18:38:03 +000036 self.topDictIndex.GlobalSubrs = self.GlobalSubrs
jvr4756b3a2002-05-16 18:17:32 +000037
38 def __len__(self):
39 return len(self.fontNames)
40
41 def keys(self):
jvrce522412003-08-25 07:37:25 +000042 return list(self.fontNames)
jvr4756b3a2002-05-16 18:17:32 +000043
jvr767102e2002-05-17 07:06:32 +000044 def values(self):
45 return self.topDictIndex
46
jvr4756b3a2002-05-16 18:17:32 +000047 def __getitem__(self, name):
48 try:
49 index = self.fontNames.index(name)
50 except ValueError:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -050051 raise KeyError(name)
jvr016ca762002-05-16 18:38:03 +000052 return self.topDictIndex[index]
Just7842e561999-12-16 21:34:53 +000053
jvr4e5af602002-05-24 09:58:04 +000054 def compile(self, file, otFont):
Just7842e561999-12-16 21:34:53 +000055 strings = IndexedStrings()
jvrf2cf9c52002-05-23 21:50:36 +000056 writer = CFFWriter()
57 writer.add(sstruct.pack(cffHeaderFormat, self))
58 fontNames = Index()
59 for name in self.fontNames:
60 fontNames.append(name)
61 writer.add(fontNames.getCompiler(strings, None))
62 topCompiler = self.topDictIndex.getCompiler(strings, None)
63 writer.add(topCompiler)
64 writer.add(strings.getCompiler())
65 writer.add(self.GlobalSubrs.getCompiler(strings, None))
66
jvr4e5af602002-05-24 09:58:04 +000067 for topDict in self.topDictIndex:
68 if not hasattr(topDict, "charset") or topDict.charset is None:
69 charset = otFont.getGlyphOrder()
70 topDict.charset = charset
71
jvrf2cf9c52002-05-23 21:50:36 +000072 for child in topCompiler.getChildren(strings):
73 writer.add(child)
74
jvrf2cf9c52002-05-23 21:50:36 +000075 writer.toFile(file)
Just7842e561999-12-16 21:34:53 +000076
77 def toXML(self, xmlWriter, progress=None):
Just7842e561999-12-16 21:34:53 +000078 for fontName in self.fontNames:
Behdad Esfahbod024d1532013-11-28 07:10:53 -050079 xmlWriter.begintag("CFFFont", name=tostr(fontName))
Just7842e561999-12-16 21:34:53 +000080 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +000081 font = self[fontName]
Just7842e561999-12-16 21:34:53 +000082 font.toXML(xmlWriter, progress)
83 xmlWriter.endtag("CFFFont")
84 xmlWriter.newline()
85 xmlWriter.newline()
86 xmlWriter.begintag("GlobalSubrs")
87 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +000088 self.GlobalSubrs.toXML(xmlWriter, progress)
Just7842e561999-12-16 21:34:53 +000089 xmlWriter.endtag("GlobalSubrs")
90 xmlWriter.newline()
Just7842e561999-12-16 21:34:53 +000091
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050092 def fromXML(self, name, attrs, content):
jvr4e5af602002-05-24 09:58:04 +000093 if not hasattr(self, "GlobalSubrs"):
94 self.GlobalSubrs = GlobalSubrsIndex()
95 self.major = 1
96 self.minor = 0
97 self.hdrSize = 4
98 self.offSize = 4 # XXX ??
99 if name == "CFFFont":
100 if not hasattr(self, "fontNames"):
101 self.fontNames = []
102 self.topDictIndex = TopDictIndex()
103 fontName = attrs["name"]
104 topDict = TopDict(GlobalSubrs=self.GlobalSubrs)
105 topDict.charset = None # gets filled in later
106 self.fontNames.append(fontName)
107 self.topDictIndex.append(topDict)
108 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000109 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000110 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500111 name, attrs, content = element
112 topDict.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000113 elif name == "GlobalSubrs":
114 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000115 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000116 continue
117 name, attrs, content = element
jvr489d76a2003-08-24 19:56:16 +0000118 subr = psCharStrings.T2CharString()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500119 subr.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000120 self.GlobalSubrs.append(subr)
Just7842e561999-12-16 21:34:53 +0000121
122
Behdad Esfahbode388db52013-11-28 14:26:58 -0500123class CFFWriter(object):
jvrf2cf9c52002-05-23 21:50:36 +0000124
125 def __init__(self):
126 self.data = []
127
128 def add(self, table):
129 self.data.append(table)
130
131 def toFile(self, file):
132 lastPosList = None
133 count = 1
Behdad Esfahbodac1b4352013-11-27 04:15:34 -0500134 while True:
jvr4e5af602002-05-24 09:58:04 +0000135 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500136 print("CFFWriter.toFile() iteration:", count)
jvr4e5af602002-05-24 09:58:04 +0000137 count = count + 1
jvrf2cf9c52002-05-23 21:50:36 +0000138 pos = 0
139 posList = [pos]
140 for item in self.data:
jvrf2cf9c52002-05-23 21:50:36 +0000141 if hasattr(item, "getDataLength"):
jvr4e5af602002-05-24 09:58:04 +0000142 endPos = pos + item.getDataLength()
jvrf2cf9c52002-05-23 21:50:36 +0000143 else:
jvr4e5af602002-05-24 09:58:04 +0000144 endPos = pos + len(item)
145 if hasattr(item, "setPos"):
146 item.setPos(pos, endPos)
147 pos = endPos
jvrf2cf9c52002-05-23 21:50:36 +0000148 posList.append(pos)
149 if posList == lastPosList:
150 break
151 lastPosList = posList
jvr4e5af602002-05-24 09:58:04 +0000152 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500153 print("CFFWriter.toFile() writing to file.")
jvrf2cf9c52002-05-23 21:50:36 +0000154 begin = file.tell()
155 posList = [0]
156 for item in self.data:
157 if hasattr(item, "toFile"):
158 item.toFile(file)
159 else:
160 file.write(item)
161 posList.append(file.tell() - begin)
jvrf2cf9c52002-05-23 21:50:36 +0000162 assert posList == lastPosList
163
164
165def calcOffSize(largestOffset):
166 if largestOffset < 0x100:
167 offSize = 1
168 elif largestOffset < 0x10000:
169 offSize = 2
170 elif largestOffset < 0x1000000:
171 offSize = 3
172 else:
173 offSize = 4
174 return offSize
175
176
Behdad Esfahbode388db52013-11-28 14:26:58 -0500177class IndexCompiler(object):
jvrf2cf9c52002-05-23 21:50:36 +0000178
179 def __init__(self, items, strings, parent):
180 self.items = self.getItems(items, strings)
181 self.parent = parent
182
183 def getItems(self, items, strings):
184 return items
185
186 def getOffsets(self):
187 pos = 1
188 offsets = [pos]
189 for item in self.items:
190 if hasattr(item, "getDataLength"):
191 pos = pos + item.getDataLength()
192 else:
193 pos = pos + len(item)
194 offsets.append(pos)
195 return offsets
196
197 def getDataLength(self):
198 lastOffset = self.getOffsets()[-1]
199 offSize = calcOffSize(lastOffset)
200 dataLength = (
201 2 + # count
202 1 + # offSize
203 (len(self.items) + 1) * offSize + # the offsets
204 lastOffset - 1 # size of object data
205 )
206 return dataLength
207
208 def toFile(self, file):
jvrf2cf9c52002-05-23 21:50:36 +0000209 offsets = self.getOffsets()
210 writeCard16(file, len(self.items))
211 offSize = calcOffSize(offsets[-1])
212 writeCard8(file, offSize)
213 offSize = -offSize
214 pack = struct.pack
215 for offset in offsets:
216 binOffset = pack(">l", offset)[offSize:]
217 assert len(binOffset) == -offSize
218 file.write(binOffset)
219 for item in self.items:
220 if hasattr(item, "toFile"):
221 item.toFile(file)
222 else:
Behdad Esfahbod85a64db2013-12-16 00:03:15 -0500223 file.write(tobytes(item, encoding="latin1"))
jvrf2cf9c52002-05-23 21:50:36 +0000224
225
226class IndexedStringsCompiler(IndexCompiler):
227
228 def getItems(self, items, strings):
229 return items.strings
230
231
232class TopDictIndexCompiler(IndexCompiler):
233
234 def getItems(self, items, strings):
235 out = []
236 for item in items:
237 out.append(item.getCompiler(strings, self))
238 return out
239
240 def getChildren(self, strings):
241 children = []
242 for topDict in self.items:
243 children.extend(topDict.getChildren(strings))
244 return children
245
246
jvred101512003-08-22 19:53:32 +0000247class FDArrayIndexCompiler(IndexCompiler):
248
249 def getItems(self, items, strings):
250 out = []
251 for item in items:
252 out.append(item.getCompiler(strings, self))
253 return out
254
255 def getChildren(self, strings):
256 children = []
257 for fontDict in self.items:
258 children.extend(fontDict.getChildren(strings))
259 return children
260
jvred101512003-08-22 19:53:32 +0000261 def toFile(self, file):
262 offsets = self.getOffsets()
263 writeCard16(file, len(self.items))
264 offSize = calcOffSize(offsets[-1])
265 writeCard8(file, offSize)
266 offSize = -offSize
267 pack = struct.pack
268 for offset in offsets:
269 binOffset = pack(">l", offset)[offSize:]
270 assert len(binOffset) == -offSize
271 file.write(binOffset)
272 for item in self.items:
273 if hasattr(item, "toFile"):
274 item.toFile(file)
275 else:
276 file.write(item)
277
278 def setPos(self, pos, endPos):
279 self.parent.rawDict["FDArray"] = pos
280
281
jvrf2cf9c52002-05-23 21:50:36 +0000282class GlobalSubrsCompiler(IndexCompiler):
283 def getItems(self, items, strings):
284 out = []
285 for cs in items:
286 cs.compile()
287 out.append(cs.bytecode)
288 return out
289
290class SubrsCompiler(GlobalSubrsCompiler):
jvr4e5af602002-05-24 09:58:04 +0000291 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000292 offset = pos - self.parent.pos
293 self.parent.rawDict["Subrs"] = offset
294
295class CharStringsCompiler(GlobalSubrsCompiler):
jvr4e5af602002-05-24 09:58:04 +0000296 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000297 self.parent.rawDict["CharStrings"] = pos
298
299
Behdad Esfahbode388db52013-11-28 14:26:58 -0500300class Index(object):
Just7842e561999-12-16 21:34:53 +0000301
jvr4756b3a2002-05-16 18:17:32 +0000302 """This class represents what the CFF spec calls an INDEX."""
Just7842e561999-12-16 21:34:53 +0000303
jvrf2cf9c52002-05-23 21:50:36 +0000304 compilerClass = IndexCompiler
305
jvr4e5af602002-05-24 09:58:04 +0000306 def __init__(self, file=None):
307 name = self.__class__.__name__
jvrf2cf9c52002-05-23 21:50:36 +0000308 if file is None:
309 self.items = []
310 return
jvr767102e2002-05-17 07:06:32 +0000311 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500312 print("loading %s at %s" % (name, file.tell()))
jvr4756b3a2002-05-16 18:17:32 +0000313 self.file = file
jvra2ad5442002-05-17 18:36:07 +0000314 count = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000315 self.count = count
316 self.items = [None] * count
317 if count == 0:
jvrf2cf9c52002-05-23 21:50:36 +0000318 self.items = []
jvr4756b3a2002-05-16 18:17:32 +0000319 return
jvra2ad5442002-05-17 18:36:07 +0000320 offSize = readCard8(file)
jvr767102e2002-05-17 07:06:32 +0000321 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500322 print(" index count: %s offSize: %s" % (count, offSize))
jvr767102e2002-05-17 07:06:32 +0000323 assert offSize <= 4, "offSize too large: %s" % offSize
jvr4756b3a2002-05-16 18:17:32 +0000324 self.offsets = offsets = []
Behdad Esfahbod18316aa2013-11-27 21:17:35 -0500325 pad = b'\0' * (4 - offSize)
jvr4756b3a2002-05-16 18:17:32 +0000326 for index in range(count+1):
327 chunk = file.read(offSize)
328 chunk = pad + chunk
329 offset, = struct.unpack(">L", chunk)
330 offsets.append(int(offset))
331 self.offsetBase = file.tell() - 1
332 file.seek(self.offsetBase + offsets[-1]) # pretend we've read the whole lot
jvrf2cf9c52002-05-23 21:50:36 +0000333 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500334 print(" end of %s at %s" % (name, file.tell()))
Just7842e561999-12-16 21:34:53 +0000335
jvr4756b3a2002-05-16 18:17:32 +0000336 def __len__(self):
jvrf2cf9c52002-05-23 21:50:36 +0000337 return len(self.items)
jvra2a75b32002-05-13 11:25:17 +0000338
jvr4756b3a2002-05-16 18:17:32 +0000339 def __getitem__(self, index):
340 item = self.items[index]
341 if item is not None:
342 return item
343 offset = self.offsets[index] + self.offsetBase
344 size = self.offsets[index+1] - self.offsets[index]
345 file = self.file
346 file.seek(offset)
347 data = file.read(size)
348 assert len(data) == size
jvra2ad5442002-05-17 18:36:07 +0000349 item = self.produceItem(index, data, file, offset, size)
jvr4756b3a2002-05-16 18:17:32 +0000350 self.items[index] = item
351 return item
352
jvra2ad5442002-05-17 18:36:07 +0000353 def produceItem(self, index, data, file, offset, size):
jvr4756b3a2002-05-16 18:17:32 +0000354 return data
jvr4756b3a2002-05-16 18:17:32 +0000355
jvrf2cf9c52002-05-23 21:50:36 +0000356 def append(self, item):
357 self.items.append(item)
358
359 def getCompiler(self, strings, parent):
360 return self.compilerClass(self, strings, parent)
361
362
363class GlobalSubrsIndex(Index):
364
365 compilerClass = GlobalSubrsCompiler
366
jvr4e5af602002-05-24 09:58:04 +0000367 def __init__(self, file=None, globalSubrs=None, private=None, fdSelect=None, fdArray=None):
368 Index.__init__(self, file)
jvra2ad5442002-05-17 18:36:07 +0000369 self.globalSubrs = globalSubrs
370 self.private = private
jvred101512003-08-22 19:53:32 +0000371 if fdSelect:
372 self.fdSelect = fdSelect
373 if fdArray:
374 self.fdArray = fdArray
jvra2ad5442002-05-17 18:36:07 +0000375
376 def produceItem(self, index, data, file, offset, size):
377 if self.private is not None:
378 private = self.private
jvred101512003-08-22 19:53:32 +0000379 elif hasattr(self, 'fdArray') and self.fdArray is not None:
jvra2ad5442002-05-17 18:36:07 +0000380 private = self.fdArray[self.fdSelect[index]].Private
381 else:
382 private = None
jvr489d76a2003-08-24 19:56:16 +0000383 return psCharStrings.T2CharString(data, private=private, globalSubrs=self.globalSubrs)
jvr4756b3a2002-05-16 18:17:32 +0000384
385 def toXML(self, xmlWriter, progress):
jvred101512003-08-22 19:53:32 +0000386 xmlWriter.comment("The 'index' attribute is only for humans; it is ignored when parsed.")
jvr4e5af602002-05-24 09:58:04 +0000387 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +0000388 for i in range(len(self)):
jvrb58176e2002-05-24 11:55:37 +0000389 subr = self[i]
390 if subr.needsDecompilation():
391 xmlWriter.begintag("CharString", index=i, raw=1)
392 else:
393 xmlWriter.begintag("CharString", index=i)
jvr4756b3a2002-05-16 18:17:32 +0000394 xmlWriter.newline()
jvrb58176e2002-05-24 11:55:37 +0000395 subr.toXML(xmlWriter)
jvr4756b3a2002-05-16 18:17:32 +0000396 xmlWriter.endtag("CharString")
397 xmlWriter.newline()
jvra2ad5442002-05-17 18:36:07 +0000398
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500399 def fromXML(self, name, attrs, content):
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500400 if name != "CharString":
jvr4e5af602002-05-24 09:58:04 +0000401 return
jvr489d76a2003-08-24 19:56:16 +0000402 subr = psCharStrings.T2CharString()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500403 subr.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000404 self.append(subr)
405
jvra2ad5442002-05-17 18:36:07 +0000406 def getItemAndSelector(self, index):
jvred101512003-08-22 19:53:32 +0000407 sel = None
408 if hasattr(self, 'fdSelect'):
409 sel = self.fdSelect[index]
jvra2ad5442002-05-17 18:36:07 +0000410 return self[index], sel
jvrf2cf9c52002-05-23 21:50:36 +0000411
jvre2ca9b52002-09-09 14:18:39 +0000412
jvrf2cf9c52002-05-23 21:50:36 +0000413class SubrsIndex(GlobalSubrsIndex):
414 compilerClass = SubrsCompiler
415
jvr4756b3a2002-05-16 18:17:32 +0000416
jvr767102e2002-05-17 07:06:32 +0000417class TopDictIndex(Index):
jvra2ad5442002-05-17 18:36:07 +0000418
jvrf2cf9c52002-05-23 21:50:36 +0000419 compilerClass = TopDictIndexCompiler
420
jvra2ad5442002-05-17 18:36:07 +0000421 def produceItem(self, index, data, file, offset, size):
jvr767102e2002-05-17 07:06:32 +0000422 top = TopDict(self.strings, file, offset, self.GlobalSubrs)
423 top.decompile(data)
424 return top
jvra2ad5442002-05-17 18:36:07 +0000425
426 def toXML(self, xmlWriter, progress):
427 for i in range(len(self)):
428 xmlWriter.begintag("FontDict", index=i)
429 xmlWriter.newline()
430 self[i].toXML(xmlWriter, progress)
431 xmlWriter.endtag("FontDict")
432 xmlWriter.newline()
jvr767102e2002-05-17 07:06:32 +0000433
434
jvred101512003-08-22 19:53:32 +0000435class FDArrayIndex(TopDictIndex):
436
437 compilerClass = FDArrayIndexCompiler
438
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500439 def fromXML(self, name, attrs, content):
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500440 if name != "FontDict":
jvred101512003-08-22 19:53:32 +0000441 return
442 fontDict = FontDict()
443 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000444 if isinstance(element, basestring):
jvred101512003-08-22 19:53:32 +0000445 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500446 name, attrs, content = element
447 fontDict.fromXML(name, attrs, content)
jvred101512003-08-22 19:53:32 +0000448 self.append(fontDict)
449
450
451class FDSelect:
452 def __init__(self, file = None, numGlyphs = None, format=None):
453 if file:
454 # read data in from file
455 self.format = readCard8(file)
456 if self.format == 0:
457 from array import array
458 self.gidArray = array("B", file.read(numGlyphs)).tolist()
459 elif self.format == 3:
460 gidArray = [None] * numGlyphs
461 nRanges = readCard16(file)
462 prev = None
463 for i in range(nRanges):
464 first = readCard16(file)
465 if prev is not None:
466 for glyphID in range(prev, first):
467 gidArray[glyphID] = fd
468 prev = first
469 fd = readCard8(file)
470 if prev is not None:
471 first = readCard16(file)
472 for glyphID in range(prev, first):
473 gidArray[glyphID] = fd
474 self.gidArray = gidArray
475 else:
Behdad Esfahbod153ec402013-12-04 01:15:46 -0500476 assert False, "unsupported FDSelect format: %s" % format
jvred101512003-08-22 19:53:32 +0000477 else:
478 # reading from XML. Make empty gidArray,, and leave format as passed in.
Behdad Esfahbod9e6ef942013-12-04 16:31:44 -0500479 # format is None will result in the smallest representation being used.
jvred101512003-08-22 19:53:32 +0000480 self.format = format
481 self.gidArray = []
482
483
484 def __len__(self):
485 return len(self.gidArray)
486
487 def __getitem__(self, index):
488 return self.gidArray[index]
489
490 def __setitem__(self, index, fdSelectValue):
491 self.gidArray[index] = fdSelectValue
492
493 def append(self, fdSelectValue):
494 self.gidArray.append(fdSelectValue)
495
496
Behdad Esfahbode388db52013-11-28 14:26:58 -0500497class CharStrings(object):
jvr4756b3a2002-05-16 18:17:32 +0000498
jvra2ad5442002-05-17 18:36:07 +0000499 def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray):
jvr4e5af602002-05-24 09:58:04 +0000500 if file is not None:
501 self.charStringsIndex = SubrsIndex(file, globalSubrs, private, fdSelect, fdArray)
502 self.charStrings = charStrings = {}
503 for i in range(len(charset)):
504 charStrings[charset[i]] = i
505 self.charStringsAreIndexed = 1
506 else:
507 self.charStrings = {}
508 self.charStringsAreIndexed = 0
509 self.globalSubrs = globalSubrs
510 self.private = private
Behdad Esfahbod9e6ef942013-12-04 16:31:44 -0500511 if fdSelect is not None:
jvred101512003-08-22 19:53:32 +0000512 self.fdSelect = fdSelect
Behdad Esfahbod9e6ef942013-12-04 16:31:44 -0500513 if fdArray is not None:
jvred101512003-08-22 19:53:32 +0000514 self.fdArray = fdArray
jvr4756b3a2002-05-16 18:17:32 +0000515
516 def keys(self):
Behdad Esfahbodc2297cd2013-11-27 06:26:55 -0500517 return list(self.charStrings.keys())
jvr4756b3a2002-05-16 18:17:32 +0000518
jvr016ca762002-05-16 18:38:03 +0000519 def values(self):
jvr4e5af602002-05-24 09:58:04 +0000520 if self.charStringsAreIndexed:
521 return self.charStringsIndex
522 else:
Behdad Esfahbodc2297cd2013-11-27 06:26:55 -0500523 return list(self.charStrings.values())
jvr016ca762002-05-16 18:38:03 +0000524
jvr4756b3a2002-05-16 18:17:32 +0000525 def has_key(self, name):
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500526 return name in self.charStrings
jvr4756b3a2002-05-16 18:17:32 +0000527
jvr767102e2002-05-17 07:06:32 +0000528 def __len__(self):
jvr4e5af602002-05-24 09:58:04 +0000529 return len(self.charStrings)
jvr767102e2002-05-17 07:06:32 +0000530
jvr4756b3a2002-05-16 18:17:32 +0000531 def __getitem__(self, name):
jvr4e5af602002-05-24 09:58:04 +0000532 charString = self.charStrings[name]
533 if self.charStringsAreIndexed:
534 charString = self.charStringsIndex[charString]
535 return charString
536
537 def __setitem__(self, name, charString):
538 if self.charStringsAreIndexed:
539 index = self.charStrings[name]
540 self.charStringsIndex[index] = charString
541 else:
542 self.charStrings[name] = charString
jvr4756b3a2002-05-16 18:17:32 +0000543
jvra2ad5442002-05-17 18:36:07 +0000544 def getItemAndSelector(self, name):
jvr4e5af602002-05-24 09:58:04 +0000545 if self.charStringsAreIndexed:
546 index = self.charStrings[name]
547 return self.charStringsIndex.getItemAndSelector(index)
548 else:
jvred101512003-08-22 19:53:32 +0000549 if hasattr(self, 'fdSelect'):
jvr91bca422012-10-18 12:49:22 +0000550 sel = self.fdSelect[index] # index is not defined at this point. Read R. ?
jvred101512003-08-22 19:53:32 +0000551 else:
552 raise KeyError("fdSelect array not yet defined.")
553 return self.charStrings[name], sel
jvra2ad5442002-05-17 18:36:07 +0000554
jvr4756b3a2002-05-16 18:17:32 +0000555 def toXML(self, xmlWriter, progress):
Behdad Esfahbodac1b4352013-11-27 04:15:34 -0500556 names = sorted(self.keys())
jvr7ce0a132002-07-23 16:42:11 +0000557 i = 0
558 step = 10
559 numGlyphs = len(names)
jvr4756b3a2002-05-16 18:17:32 +0000560 for name in names:
jvred101512003-08-22 19:53:32 +0000561 charStr, fdSelectIndex = self.getItemAndSelector(name)
jvrb58176e2002-05-24 11:55:37 +0000562 if charStr.needsDecompilation():
563 raw = [("raw", 1)]
564 else:
565 raw = []
jvred101512003-08-22 19:53:32 +0000566 if fdSelectIndex is None:
jvrb58176e2002-05-24 11:55:37 +0000567 xmlWriter.begintag("CharString", [('name', name)] + raw)
jvra2ad5442002-05-17 18:36:07 +0000568 else:
569 xmlWriter.begintag("CharString",
jvred101512003-08-22 19:53:32 +0000570 [('name', name), ('fdSelectIndex', fdSelectIndex)] + raw)
jvr4756b3a2002-05-16 18:17:32 +0000571 xmlWriter.newline()
jvrb58176e2002-05-24 11:55:37 +0000572 charStr.toXML(xmlWriter)
jvr4756b3a2002-05-16 18:17:32 +0000573 xmlWriter.endtag("CharString")
574 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000575 if not i % step and progress is not None:
576 progress.setLabel("Dumping 'CFF ' table... (%s)" % name)
Behdad Esfahbod32c10ee2013-11-27 17:46:17 -0500577 progress.increment(step / numGlyphs)
jvr7ce0a132002-07-23 16:42:11 +0000578 i = i + 1
jvr4e5af602002-05-24 09:58:04 +0000579
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500580 def fromXML(self, name, attrs, content):
jvr4e5af602002-05-24 09:58:04 +0000581 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000582 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000583 continue
584 name, attrs, content = element
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500585 if name != "CharString":
jvr4e5af602002-05-24 09:58:04 +0000586 continue
jvred101512003-08-22 19:53:32 +0000587 fdID = -1
588 if hasattr(self, "fdArray"):
589 fdID = safeEval(attrs["fdSelectIndex"])
590 private = self.fdArray[fdID].Private
591 else:
592 private = self.private
593
jvr4e5af602002-05-24 09:58:04 +0000594 glyphName = attrs["name"]
jvr489d76a2003-08-24 19:56:16 +0000595 charString = psCharStrings.T2CharString(
596 private=private,
597 globalSubrs=self.globalSubrs)
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500598 charString.fromXML(name, attrs, content)
jvred101512003-08-22 19:53:32 +0000599 if fdID >= 0:
600 charString.fdSelectIndex = fdID
jvr4e5af602002-05-24 09:58:04 +0000601 self[glyphName] = charString
jvr4756b3a2002-05-16 18:17:32 +0000602
603
jvra2ad5442002-05-17 18:36:07 +0000604def readCard8(file):
Behdad Esfahbod319c5fd2013-11-27 18:13:48 -0500605 return byteord(file.read(1))
jvra2ad5442002-05-17 18:36:07 +0000606
607def readCard16(file):
608 value, = struct.unpack(">H", file.read(2))
609 return value
610
jvrf2cf9c52002-05-23 21:50:36 +0000611def writeCard8(file, value):
Behdad Esfahbodb7a2d792013-11-27 15:19:40 -0500612 file.write(bytechr(value))
jvrf2cf9c52002-05-23 21:50:36 +0000613
614def writeCard16(file, value):
615 file.write(struct.pack(">H", value))
616
617def packCard8(value):
Behdad Esfahbodb7a2d792013-11-27 15:19:40 -0500618 return bytechr(value)
jvrf2cf9c52002-05-23 21:50:36 +0000619
620def packCard16(value):
621 return struct.pack(">H", value)
622
jvr4756b3a2002-05-16 18:17:32 +0000623def buildOperatorDict(table):
624 d = {}
625 for op, name, arg, default, conv in table:
626 d[op] = (name, arg)
627 return d
628
jvrf2cf9c52002-05-23 21:50:36 +0000629def buildOpcodeDict(table):
630 d = {}
631 for op, name, arg, default, conv in table:
jvr2a9bcde2008-03-07 19:56:17 +0000632 if isinstance(op, tuple):
Behdad Esfahbodb7a2d792013-11-27 15:19:40 -0500633 op = bytechr(op[0]) + bytechr(op[1])
jvrf2cf9c52002-05-23 21:50:36 +0000634 else:
Behdad Esfahbodb7a2d792013-11-27 15:19:40 -0500635 op = bytechr(op)
jvrf2cf9c52002-05-23 21:50:36 +0000636 d[name] = (op, arg)
637 return d
638
jvr4756b3a2002-05-16 18:17:32 +0000639def buildOrder(table):
640 l = []
641 for op, name, arg, default, conv in table:
642 l.append(name)
643 return l
644
645def buildDefaults(table):
646 d = {}
647 for op, name, arg, default, conv in table:
648 if default is not None:
649 d[name] = default
650 return d
651
652def buildConverters(table):
653 d = {}
654 for op, name, arg, default, conv in table:
655 d[name] = conv
656 return d
657
658
Behdad Esfahbode388db52013-11-28 14:26:58 -0500659class SimpleConverter(object):
jvr7ce02ea2002-05-17 20:04:05 +0000660 def read(self, parent, value):
661 return value
jvrf2cf9c52002-05-23 21:50:36 +0000662 def write(self, parent, value):
663 return value
jvr7ce0a132002-07-23 16:42:11 +0000664 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000665 xmlWriter.simpletag(name, value=value)
666 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500667 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000668 return attrs["value"]
669
Behdad Esfahbod024d1532013-11-28 07:10:53 -0500670class ASCIIConverter(SimpleConverter):
671 def read(self, parent, value):
672 return tostr(value, encoding='ascii')
673 def write(self, parent, value):
674 return tobytes(value, encoding='ascii')
Behdad Esfahbod8c5c9662013-10-28 13:20:00 +0100675 def xmlWrite(self, xmlWriter, name, value, progress):
Behdad Esfahbod024d1532013-11-28 07:10:53 -0500676 xmlWriter.simpletag(name, value=tostr(value, encoding="ascii"))
Behdad Esfahbod8c5c9662013-10-28 13:20:00 +0100677 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500678 def xmlRead(self, name, attrs, content, parent):
Behdad Esfahbod024d1532013-11-28 07:10:53 -0500679 return tobytes(attrs["value"], encoding=("ascii"))
680
681class Latin1Converter(SimpleConverter):
682 def read(self, parent, value):
683 return tostr(value, encoding='latin1')
684 def write(self, parent, value):
685 return tobytes(value, encoding='latin1')
686 def xmlWrite(self, xmlWriter, name, value, progress):
687 xmlWriter.simpletag(name, value=tostr(value, encoding="latin1"))
688 xmlWriter.newline()
689 def xmlRead(self, name, attrs, content, parent):
690 return tobytes(attrs["value"], encoding=("latin1"))
jvre2ca9b52002-09-09 14:18:39 +0000691
692
jvr4e5af602002-05-24 09:58:04 +0000693def parseNum(s):
694 try:
695 value = int(s)
696 except:
697 value = float(s)
698 return value
699
700class NumberConverter(SimpleConverter):
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500701 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000702 return parseNum(attrs["value"])
703
704class ArrayConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000705 def xmlWrite(self, xmlWriter, name, value, progress):
Behdad Esfahbode5ca7962013-11-27 04:38:16 -0500706 value = " ".join(map(str, value))
707 xmlWriter.simpletag(name, value=value)
jvr4e5af602002-05-24 09:58:04 +0000708 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500709 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000710 values = attrs["value"].split()
Behdad Esfahbode5ca7962013-11-27 04:38:16 -0500711 return [parseNum(value) for value in values]
jvr4e5af602002-05-24 09:58:04 +0000712
713class TableConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000714 def xmlWrite(self, xmlWriter, name, value, progress):
jvra2ad5442002-05-17 18:36:07 +0000715 xmlWriter.begintag(name)
716 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000717 value.toXML(xmlWriter, progress)
jvra2ad5442002-05-17 18:36:07 +0000718 xmlWriter.endtag(name)
719 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500720 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000721 ob = self.getClass()()
722 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000723 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000724 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500725 name, attrs, content = element
726 ob.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000727 return ob
jvra2ad5442002-05-17 18:36:07 +0000728
jvr4e5af602002-05-24 09:58:04 +0000729class PrivateDictConverter(TableConverter):
730 def getClass(self):
731 return PrivateDict
jvr4756b3a2002-05-16 18:17:32 +0000732 def read(self, parent, value):
733 size, offset = value
734 file = parent.file
jvr4e5af602002-05-24 09:58:04 +0000735 priv = PrivateDict(parent.strings, file, offset)
jvr4756b3a2002-05-16 18:17:32 +0000736 file.seek(offset)
737 data = file.read(size)
Behdad Esfahbod153ec402013-12-04 01:15:46 -0500738 assert len(data) == size
jvr4e5af602002-05-24 09:58:04 +0000739 priv.decompile(data)
740 return priv
jvrf2cf9c52002-05-23 21:50:36 +0000741 def write(self, parent, value):
742 return (0, 0) # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000743
jvr4e5af602002-05-24 09:58:04 +0000744class SubrsConverter(TableConverter):
745 def getClass(self):
746 return SubrsIndex
jvr4756b3a2002-05-16 18:17:32 +0000747 def read(self, parent, value):
748 file = parent.file
749 file.seek(parent.offset + value) # Offset(self)
jvr4e5af602002-05-24 09:58:04 +0000750 return SubrsIndex(file)
jvrf2cf9c52002-05-23 21:50:36 +0000751 def write(self, parent, value):
752 return 0 # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000753
jvr4e5af602002-05-24 09:58:04 +0000754class CharStringsConverter(TableConverter):
jvr4756b3a2002-05-16 18:17:32 +0000755 def read(self, parent, value):
756 file = parent.file
jvr767102e2002-05-17 07:06:32 +0000757 charset = parent.charset
jvra2ad5442002-05-17 18:36:07 +0000758 globalSubrs = parent.GlobalSubrs
759 if hasattr(parent, "ROS"):
760 fdSelect, fdArray = parent.FDSelect, parent.FDArray
761 private = None
762 else:
763 fdSelect, fdArray = None, None
764 private = parent.Private
jvr4756b3a2002-05-16 18:17:32 +0000765 file.seek(value) # Offset(0)
jvra2ad5442002-05-17 18:36:07 +0000766 return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
jvrf2cf9c52002-05-23 21:50:36 +0000767 def write(self, parent, value):
768 return 0 # dummy value
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500769 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +0000770 if hasattr(parent, "ROS"):
771 # if it is a CID-keyed font, then the private Dict is extracted from the parent.FDArray
772 private, fdSelect, fdArray = None, parent.FDSelect, parent.FDArray
773 else:
774 # if it is a name-keyed font, then the private dict is in the top dict, and there is no fdArray.
775 private, fdSelect, fdArray = parent.Private, None, None
776 charStrings = CharStrings(None, None, parent.GlobalSubrs, private, fdSelect, fdArray)
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500777 charStrings.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000778 return charStrings
jvr4756b3a2002-05-16 18:17:32 +0000779
Behdad Esfahbode388db52013-11-28 14:26:58 -0500780class CharsetConverter(object):
jvr4756b3a2002-05-16 18:17:32 +0000781 def read(self, parent, value):
782 isCID = hasattr(parent, "ROS")
783 if value > 2:
784 numGlyphs = parent.numGlyphs
785 file = parent.file
786 file.seek(value)
jvrf2cf9c52002-05-23 21:50:36 +0000787 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500788 print("loading charset at %s" % value)
jvra2ad5442002-05-17 18:36:07 +0000789 format = readCard8(file)
Just7842e561999-12-16 21:34:53 +0000790 if format == 0:
jvrc60a44f2006-10-21 13:41:18 +0000791 charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
jvr4756b3a2002-05-16 18:17:32 +0000792 elif format == 1 or format == 2:
793 charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
Just7842e561999-12-16 21:34:53 +0000794 else:
jvr1890b952002-05-15 07:41:30 +0000795 raise NotImplementedError
jvr4756b3a2002-05-16 18:17:32 +0000796 assert len(charset) == numGlyphs
jvrf2cf9c52002-05-23 21:50:36 +0000797 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500798 print(" charset end at %s" % file.tell())
jvrc60a44f2006-10-21 13:41:18 +0000799 else: # offset == 0 -> no charset data.
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500800 if isCID or "CharStrings" not in parent.rawDict:
jvrc60a44f2006-10-21 13:41:18 +0000801 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 +0000802 charset = None
803 elif value == 0:
jvrc60a44f2006-10-21 13:41:18 +0000804 charset = cffISOAdobeStrings
jvr4756b3a2002-05-16 18:17:32 +0000805 elif value == 1:
jvrc60a44f2006-10-21 13:41:18 +0000806 charset = cffIExpertStrings
jvr4756b3a2002-05-16 18:17:32 +0000807 elif value == 2:
jvrc60a44f2006-10-21 13:41:18 +0000808 charset = cffExpertSubsetStrings
jvr4756b3a2002-05-16 18:17:32 +0000809 return charset
jvrc60a44f2006-10-21 13:41:18 +0000810
jvrf2cf9c52002-05-23 21:50:36 +0000811 def write(self, parent, value):
812 return 0 # dummy value
jvr7ce0a132002-07-23 16:42:11 +0000813 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000814 # XXX only write charset when not in OT/TTX context, where we
815 # dump charset as a separate "GlyphOrder" table.
816 ##xmlWriter.simpletag("charset")
817 xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
jvr4756b3a2002-05-16 18:17:32 +0000818 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500819 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000820 if 0:
821 return safeEval(attrs["value"])
jvr4756b3a2002-05-16 18:17:32 +0000822
823
Behdad Esfahbode388db52013-11-28 14:26:58 -0500824class CharsetCompiler(object):
jvrf2cf9c52002-05-23 21:50:36 +0000825
826 def __init__(self, strings, charset, parent):
827 assert charset[0] == '.notdef'
jvred101512003-08-22 19:53:32 +0000828 isCID = hasattr(parent.dictObj, "ROS")
829 data0 = packCharset0(charset, isCID, strings)
830 data = packCharset(charset, isCID, strings)
jvr6004baf2002-05-24 10:35:13 +0000831 if len(data) < len(data0):
832 self.data = data
833 else:
834 self.data = data0
jvrf2cf9c52002-05-23 21:50:36 +0000835 self.parent = parent
836
jvr4e5af602002-05-24 09:58:04 +0000837 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000838 self.parent.rawDict["charset"] = pos
839
840 def getDataLength(self):
841 return len(self.data)
842
843 def toFile(self, file):
844 file.write(self.data)
845
846
jvred101512003-08-22 19:53:32 +0000847def getCIDfromName(name, strings):
848 return int(name[3:])
849
850def getSIDfromName(name, strings):
851 return strings.getSID(name)
852
853def packCharset0(charset, isCID, strings):
Behdad Esfahbod153ec402013-12-04 01:15:46 -0500854 fmt = 0
855 data = [packCard8(fmt)]
jvred101512003-08-22 19:53:32 +0000856 if isCID:
857 getNameID = getCIDfromName
858 else:
859 getNameID = getSIDfromName
860
jvr6004baf2002-05-24 10:35:13 +0000861 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000862 data.append(packCard16(getNameID(name,strings)))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -0500863 return bytesjoin(data)
jvr6004baf2002-05-24 10:35:13 +0000864
jvred101512003-08-22 19:53:32 +0000865
866def packCharset(charset, isCID, strings):
Behdad Esfahbod153ec402013-12-04 01:15:46 -0500867 fmt = 1
jvr6004baf2002-05-24 10:35:13 +0000868 ranges = []
869 first = None
870 end = 0
jvred101512003-08-22 19:53:32 +0000871 if isCID:
872 getNameID = getCIDfromName
873 else:
874 getNameID = getSIDfromName
875
jvr6004baf2002-05-24 10:35:13 +0000876 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000877 SID = getNameID(name, strings)
jvr6004baf2002-05-24 10:35:13 +0000878 if first is None:
879 first = SID
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500880 elif end + 1 != SID:
jvr6004baf2002-05-24 10:35:13 +0000881 nLeft = end - first
882 if nLeft > 255:
Behdad Esfahbod153ec402013-12-04 01:15:46 -0500883 fmt = 2
jvr6004baf2002-05-24 10:35:13 +0000884 ranges.append((first, nLeft))
885 first = SID
886 end = SID
887 nLeft = end - first
888 if nLeft > 255:
Behdad Esfahbod153ec402013-12-04 01:15:46 -0500889 fmt = 2
jvr6004baf2002-05-24 10:35:13 +0000890 ranges.append((first, nLeft))
891
Behdad Esfahbod153ec402013-12-04 01:15:46 -0500892 data = [packCard8(fmt)]
893 if fmt == 1:
jvr6004baf2002-05-24 10:35:13 +0000894 nLeftFunc = packCard8
895 else:
896 nLeftFunc = packCard16
897 for first, nLeft in ranges:
898 data.append(packCard16(first) + nLeftFunc(nLeft))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -0500899 return bytesjoin(data)
jvr6004baf2002-05-24 10:35:13 +0000900
jvrc60a44f2006-10-21 13:41:18 +0000901def parseCharset0(numGlyphs, file, strings, isCID):
jvrf2cf9c52002-05-23 21:50:36 +0000902 charset = [".notdef"]
jvrc60a44f2006-10-21 13:41:18 +0000903 if isCID:
904 for i in range(numGlyphs - 1):
905 CID = readCard16(file)
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500906 charset.append("cid" + str(CID).zfill(5))
jvrc60a44f2006-10-21 13:41:18 +0000907 else:
908 for i in range(numGlyphs - 1):
909 SID = readCard16(file)
910 charset.append(strings[SID])
jvrf2cf9c52002-05-23 21:50:36 +0000911 return charset
912
Behdad Esfahbod153ec402013-12-04 01:15:46 -0500913def parseCharset(numGlyphs, file, strings, isCID, fmt):
jvr4756b3a2002-05-16 18:17:32 +0000914 charset = ['.notdef']
915 count = 1
Behdad Esfahbod153ec402013-12-04 01:15:46 -0500916 if fmt == 1:
jvra2ad5442002-05-17 18:36:07 +0000917 nLeftFunc = readCard8
jvr4756b3a2002-05-16 18:17:32 +0000918 else:
jvra2ad5442002-05-17 18:36:07 +0000919 nLeftFunc = readCard16
jvr4756b3a2002-05-16 18:17:32 +0000920 while count < numGlyphs:
jvra2ad5442002-05-17 18:36:07 +0000921 first = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000922 nLeft = nLeftFunc(file)
923 if isCID:
924 for CID in range(first, first+nLeft+1):
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500925 charset.append("cid" + str(CID).zfill(5))
jvr1890b952002-05-15 07:41:30 +0000926 else:
jvr4756b3a2002-05-16 18:17:32 +0000927 for SID in range(first, first+nLeft+1):
928 charset.append(strings[SID])
929 count = count + nLeft + 1
930 return charset
931
932
Behdad Esfahbode388db52013-11-28 14:26:58 -0500933class EncodingCompiler(object):
jvrb9702ba2003-01-03 20:56:01 +0000934
935 def __init__(self, strings, encoding, parent):
jvr2a9bcde2008-03-07 19:56:17 +0000936 assert not isinstance(encoding, basestring)
jvrb9702ba2003-01-03 20:56:01 +0000937 data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
938 data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
939 if len(data0) < len(data1):
940 self.data = data0
941 else:
942 self.data = data1
943 self.parent = parent
944
945 def setPos(self, pos, endPos):
946 self.parent.rawDict["Encoding"] = pos
947
948 def getDataLength(self):
949 return len(self.data)
950
951 def toFile(self, file):
952 file.write(self.data)
953
954
955class EncodingConverter(SimpleConverter):
956
957 def read(self, parent, value):
958 if value == 0:
959 return "StandardEncoding"
960 elif value == 1:
961 return "ExpertEncoding"
962 else:
963 assert value > 1
964 file = parent.file
965 file.seek(value)
966 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500967 print("loading Encoding at %s" % value)
Behdad Esfahbod153ec402013-12-04 01:15:46 -0500968 fmt = readCard8(file)
969 haveSupplement = fmt & 0x80
jvrb9702ba2003-01-03 20:56:01 +0000970 if haveSupplement:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -0500971 raise NotImplementedError("Encoding supplements are not yet supported")
Behdad Esfahbod153ec402013-12-04 01:15:46 -0500972 fmt = fmt & 0x7f
973 if fmt == 0:
jvrb9702ba2003-01-03 20:56:01 +0000974 encoding = parseEncoding0(parent.charset, file, haveSupplement,
975 parent.strings)
Behdad Esfahbod153ec402013-12-04 01:15:46 -0500976 elif fmt == 1:
jvrb9702ba2003-01-03 20:56:01 +0000977 encoding = parseEncoding1(parent.charset, file, haveSupplement,
978 parent.strings)
979 return encoding
980
981 def write(self, parent, value):
982 if value == "StandardEncoding":
983 return 0
984 elif value == "ExpertEncoding":
985 return 1
986 return 0 # dummy value
987
988 def xmlWrite(self, xmlWriter, name, value, progress):
989 if value in ("StandardEncoding", "ExpertEncoding"):
990 xmlWriter.simpletag(name, name=value)
991 xmlWriter.newline()
992 return
993 xmlWriter.begintag(name)
994 xmlWriter.newline()
995 for code in range(len(value)):
996 glyphName = value[code]
997 if glyphName != ".notdef":
998 xmlWriter.simpletag("map", code=hex(code), name=glyphName)
999 xmlWriter.newline()
1000 xmlWriter.endtag(name)
1001 xmlWriter.newline()
1002
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001003 def xmlRead(self, name, attrs, content, parent):
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -05001004 if "name" in attrs:
jvrb9702ba2003-01-03 20:56:01 +00001005 return attrs["name"]
1006 encoding = [".notdef"] * 256
1007 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +00001008 if isinstance(element, basestring):
jvrb9702ba2003-01-03 20:56:01 +00001009 continue
1010 name, attrs, content = element
1011 code = safeEval(attrs["code"])
1012 glyphName = attrs["name"]
1013 encoding[code] = glyphName
1014 return encoding
1015
1016
1017def parseEncoding0(charset, file, haveSupplement, strings):
1018 nCodes = readCard8(file)
1019 encoding = [".notdef"] * 256
1020 for glyphID in range(1, nCodes + 1):
1021 code = readCard8(file)
1022 if code != 0:
1023 encoding[code] = charset[glyphID]
1024 return encoding
1025
1026def parseEncoding1(charset, file, haveSupplement, strings):
1027 nRanges = readCard8(file)
1028 encoding = [".notdef"] * 256
1029 glyphID = 1
1030 for i in range(nRanges):
1031 code = readCard8(file)
1032 nLeft = readCard8(file)
1033 for glyphID in range(glyphID, glyphID + nLeft + 1):
1034 encoding[code] = charset[glyphID]
1035 code = code + 1
1036 glyphID = glyphID + 1
1037 return encoding
1038
1039def packEncoding0(charset, encoding, strings):
Behdad Esfahbod153ec402013-12-04 01:15:46 -05001040 fmt = 0
jvrb9702ba2003-01-03 20:56:01 +00001041 m = {}
1042 for code in range(len(encoding)):
1043 name = encoding[code]
1044 if name != ".notdef":
1045 m[name] = code
1046 codes = []
1047 for name in charset[1:]:
1048 code = m.get(name)
1049 codes.append(code)
1050
1051 while codes and codes[-1] is None:
1052 codes.pop()
1053
Behdad Esfahbod153ec402013-12-04 01:15:46 -05001054 data = [packCard8(fmt), packCard8(len(codes))]
jvrb9702ba2003-01-03 20:56:01 +00001055 for code in codes:
1056 if code is None:
1057 code = 0
1058 data.append(packCard8(code))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001059 return bytesjoin(data)
jvrb9702ba2003-01-03 20:56:01 +00001060
1061def packEncoding1(charset, encoding, strings):
Behdad Esfahbod153ec402013-12-04 01:15:46 -05001062 fmt = 1
jvrb9702ba2003-01-03 20:56:01 +00001063 m = {}
1064 for code in range(len(encoding)):
1065 name = encoding[code]
1066 if name != ".notdef":
1067 m[name] = code
1068 ranges = []
1069 first = None
1070 end = 0
1071 for name in charset[1:]:
1072 code = m.get(name, -1)
1073 if first is None:
1074 first = code
Behdad Esfahbod180ace62013-11-27 02:40:30 -05001075 elif end + 1 != code:
jvrb9702ba2003-01-03 20:56:01 +00001076 nLeft = end - first
1077 ranges.append((first, nLeft))
1078 first = code
1079 end = code
1080 nLeft = end - first
1081 ranges.append((first, nLeft))
1082
1083 # remove unencoded glyphs at the end.
1084 while ranges and ranges[-1][0] == -1:
1085 ranges.pop()
1086
Behdad Esfahbod153ec402013-12-04 01:15:46 -05001087 data = [packCard8(fmt), packCard8(len(ranges))]
jvrb9702ba2003-01-03 20:56:01 +00001088 for first, nLeft in ranges:
1089 if first == -1: # unencoded
1090 first = 0
1091 data.append(packCard8(first) + packCard8(nLeft))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001092 return bytesjoin(data)
jvrb9702ba2003-01-03 20:56:01 +00001093
1094
jvr4e5af602002-05-24 09:58:04 +00001095class FDArrayConverter(TableConverter):
jvred101512003-08-22 19:53:32 +00001096
jvra2ad5442002-05-17 18:36:07 +00001097 def read(self, parent, value):
1098 file = parent.file
1099 file.seek(value)
jvred101512003-08-22 19:53:32 +00001100 fdArray = FDArrayIndex(file)
jvra2ad5442002-05-17 18:36:07 +00001101 fdArray.strings = parent.strings
1102 fdArray.GlobalSubrs = parent.GlobalSubrs
1103 return fdArray
1104
jvred101512003-08-22 19:53:32 +00001105 def write(self, parent, value):
1106 return 0 # dummy value
1107
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001108 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001109 fdArray = FDArrayIndex()
1110 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +00001111 if isinstance(element, basestring):
jvred101512003-08-22 19:53:32 +00001112 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001113 name, attrs, content = element
1114 fdArray.fromXML(name, attrs, content)
jvred101512003-08-22 19:53:32 +00001115 return fdArray
1116
jvra2ad5442002-05-17 18:36:07 +00001117
Behdad Esfahbode388db52013-11-28 14:26:58 -05001118class FDSelectConverter(object):
jvred101512003-08-22 19:53:32 +00001119
jvra2ad5442002-05-17 18:36:07 +00001120 def read(self, parent, value):
1121 file = parent.file
1122 file.seek(value)
jvred101512003-08-22 19:53:32 +00001123 fdSelect = FDSelect(file, parent.numGlyphs)
1124 return fdSelect
1125
1126 def write(self, parent, value):
1127 return 0 # dummy value
1128
1129 # The FDSelect glyph data is written out to XML in the charstring keys,
1130 # so we write out only the format selector
jvr7ce0a132002-07-23 16:42:11 +00001131 def xmlWrite(self, xmlWriter, name, value, progress):
jvred101512003-08-22 19:53:32 +00001132 xmlWriter.simpletag(name, [('format', value.format)])
1133 xmlWriter.newline()
1134
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001135 def xmlRead(self, name, attrs, content, parent):
Behdad Esfahbod153ec402013-12-04 01:15:46 -05001136 fmt = safeEval(attrs["format"])
jvred101512003-08-22 19:53:32 +00001137 file = None
1138 numGlyphs = None
Behdad Esfahbod153ec402013-12-04 01:15:46 -05001139 fdSelect = FDSelect(file, numGlyphs, fmt)
jvred101512003-08-22 19:53:32 +00001140 return fdSelect
1141
1142
1143def packFDSelect0(fdSelectArray):
Behdad Esfahbod153ec402013-12-04 01:15:46 -05001144 fmt = 0
1145 data = [packCard8(fmt)]
jvred101512003-08-22 19:53:32 +00001146 for index in fdSelectArray:
1147 data.append(packCard8(index))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001148 return bytesjoin(data)
jvred101512003-08-22 19:53:32 +00001149
1150
1151def packFDSelect3(fdSelectArray):
Behdad Esfahbod153ec402013-12-04 01:15:46 -05001152 fmt = 3
jvred101512003-08-22 19:53:32 +00001153 fdRanges = []
1154 first = None
1155 end = 0
1156 lenArray = len(fdSelectArray)
1157 lastFDIndex = -1
1158 for i in range(lenArray):
1159 fdIndex = fdSelectArray[i]
1160 if lastFDIndex != fdIndex:
1161 fdRanges.append([i, fdIndex])
1162 lastFDIndex = fdIndex
1163 sentinelGID = i + 1
1164
Behdad Esfahbod153ec402013-12-04 01:15:46 -05001165 data = [packCard8(fmt)]
jvred101512003-08-22 19:53:32 +00001166 data.append(packCard16( len(fdRanges) ))
1167 for fdRange in fdRanges:
1168 data.append(packCard16(fdRange[0]))
1169 data.append(packCard8(fdRange[1]))
1170 data.append(packCard16(sentinelGID))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001171 return bytesjoin(data)
jvred101512003-08-22 19:53:32 +00001172
1173
Behdad Esfahbode388db52013-11-28 14:26:58 -05001174class FDSelectCompiler(object):
jvred101512003-08-22 19:53:32 +00001175
1176 def __init__(self, fdSelect, parent):
Behdad Esfahbod153ec402013-12-04 01:15:46 -05001177 fmt = fdSelect.format
jvred101512003-08-22 19:53:32 +00001178 fdSelectArray = fdSelect.gidArray
Behdad Esfahbod153ec402013-12-04 01:15:46 -05001179 if fmt == 0:
jvred101512003-08-22 19:53:32 +00001180 self.data = packFDSelect0(fdSelectArray)
Behdad Esfahbod153ec402013-12-04 01:15:46 -05001181 elif fmt == 3:
jvred101512003-08-22 19:53:32 +00001182 self.data = packFDSelect3(fdSelectArray)
1183 else:
1184 # choose smaller of the two formats
1185 data0 = packFDSelect0(fdSelectArray)
1186 data3 = packFDSelect3(fdSelectArray)
1187 if len(data0) < len(data3):
1188 self.data = data0
1189 fdSelect.format = 0
1190 else:
1191 self.data = data3
1192 fdSelect.format = 3
1193
1194 self.parent = parent
1195
1196 def setPos(self, pos, endPos):
1197 self.parent.rawDict["FDSelect"] = pos
1198
1199 def getDataLength(self):
1200 return len(self.data)
1201
1202 def toFile(self, file):
1203 file.write(self.data)
jvra2ad5442002-05-17 18:36:07 +00001204
1205
jvr4e5af602002-05-24 09:58:04 +00001206class ROSConverter(SimpleConverter):
jvred101512003-08-22 19:53:32 +00001207
jvr7ce0a132002-07-23 16:42:11 +00001208 def xmlWrite(self, xmlWriter, name, value, progress):
jvr155aa752002-05-17 19:58:49 +00001209 registry, order, supplement = value
Behdad Esfahbod024d1532013-11-28 07:10:53 -05001210 xmlWriter.simpletag(name, [('Registry', tostr(registry)), ('Order', tostr(order)),
jvrf2cf9c52002-05-23 21:50:36 +00001211 ('Supplement', supplement)])
jvr4afb2572002-05-18 20:07:01 +00001212 xmlWriter.newline()
jvr155aa752002-05-17 19:58:49 +00001213
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001214 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001215 return (attrs['Registry'], attrs['Order'], safeEval(attrs['Supplement']))
1216
1217
jvr155aa752002-05-17 19:58:49 +00001218
jvr4756b3a2002-05-16 18:17:32 +00001219topDictOperators = [
1220# opcode name argument type default converter
jvr155aa752002-05-17 19:58:49 +00001221 ((12, 30), 'ROS', ('SID','SID','number'), None, ROSConverter()),
jvrf2cf9c52002-05-23 21:50:36 +00001222 ((12, 20), 'SyntheticBase', 'number', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001223 (0, 'version', 'SID', None, None),
jvre2ca9b52002-09-09 14:18:39 +00001224 (1, 'Notice', 'SID', None, Latin1Converter()),
1225 ((12, 0), 'Copyright', 'SID', None, Latin1Converter()),
jvr4756b3a2002-05-16 18:17:32 +00001226 (2, 'FullName', 'SID', None, None),
jvr155aa752002-05-17 19:58:49 +00001227 ((12, 38), 'FontName', 'SID', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001228 (3, 'FamilyName', 'SID', None, None),
1229 (4, 'Weight', 'SID', None, None),
1230 ((12, 1), 'isFixedPitch', 'number', 0, None),
1231 ((12, 2), 'ItalicAngle', 'number', 0, None),
1232 ((12, 3), 'UnderlinePosition', 'number', None, None),
1233 ((12, 4), 'UnderlineThickness', 'number', 50, None),
1234 ((12, 5), 'PaintType', 'number', 0, None),
1235 ((12, 6), 'CharstringType', 'number', 2, None),
1236 ((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0], None),
1237 (13, 'UniqueID', 'number', None, None),
1238 (5, 'FontBBox', 'array', [0,0,0,0], None),
1239 ((12, 8), 'StrokeWidth', 'number', 0, None),
1240 (14, 'XUID', 'array', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001241 ((12, 21), 'PostScript', 'SID', None, None),
1242 ((12, 22), 'BaseFontName', 'SID', None, None),
1243 ((12, 23), 'BaseFontBlend', 'delta', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001244 ((12, 31), 'CIDFontVersion', 'number', 0, None),
1245 ((12, 32), 'CIDFontRevision', 'number', 0, None),
1246 ((12, 33), 'CIDFontType', 'number', 0, None),
1247 ((12, 34), 'CIDCount', 'number', 8720, None),
jvred101512003-08-22 19:53:32 +00001248 (15, 'charset', 'number', 0, CharsetConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001249 ((12, 35), 'UIDBase', 'number', None, None),
jvrb9702ba2003-01-03 20:56:01 +00001250 (16, 'Encoding', 'number', 0, EncodingConverter()),
jvr155aa752002-05-17 19:58:49 +00001251 (18, 'Private', ('number','number'), None, PrivateDictConverter()),
jvred101512003-08-22 19:53:32 +00001252 ((12, 37), 'FDSelect', 'number', None, FDSelectConverter()),
1253 ((12, 36), 'FDArray', 'number', None, FDArrayConverter()),
jvr155aa752002-05-17 19:58:49 +00001254 (17, 'CharStrings', 'number', None, CharStringsConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001255]
1256
jvred101512003-08-22 19:53:32 +00001257# Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order,
1258# in order for the font to compile back from xml.
1259
1260
jvr4756b3a2002-05-16 18:17:32 +00001261privateDictOperators = [
1262# opcode name argument type default converter
1263 (6, 'BlueValues', 'delta', None, None),
1264 (7, 'OtherBlues', 'delta', None, None),
1265 (8, 'FamilyBlues', 'delta', None, None),
1266 (9, 'FamilyOtherBlues', 'delta', None, None),
1267 ((12, 9), 'BlueScale', 'number', 0.039625, None),
1268 ((12, 10), 'BlueShift', 'number', 7, None),
1269 ((12, 11), 'BlueFuzz', 'number', 1, None),
1270 (10, 'StdHW', 'number', None, None),
1271 (11, 'StdVW', 'number', None, None),
1272 ((12, 12), 'StemSnapH', 'delta', None, None),
1273 ((12, 13), 'StemSnapV', 'delta', None, None),
1274 ((12, 14), 'ForceBold', 'number', 0, None),
jvrf2cf9c52002-05-23 21:50:36 +00001275 ((12, 15), 'ForceBoldThreshold', 'number', None, None), # deprecated
1276 ((12, 16), 'lenIV', 'number', None, None), # deprecated
jvr4756b3a2002-05-16 18:17:32 +00001277 ((12, 17), 'LanguageGroup', 'number', 0, None),
1278 ((12, 18), 'ExpansionFactor', 'number', 0.06, None),
1279 ((12, 19), 'initialRandomSeed', 'number', 0, None),
1280 (20, 'defaultWidthX', 'number', 0, None),
1281 (21, 'nominalWidthX', 'number', 0, None),
1282 (19, 'Subrs', 'number', None, SubrsConverter()),
1283]
1284
jvr4e5af602002-05-24 09:58:04 +00001285def addConverters(table):
1286 for i in range(len(table)):
1287 op, name, arg, default, conv = table[i]
1288 if conv is not None:
1289 continue
1290 if arg in ("delta", "array"):
1291 conv = ArrayConverter()
1292 elif arg == "number":
1293 conv = NumberConverter()
1294 elif arg == "SID":
Behdad Esfahbod024d1532013-11-28 07:10:53 -05001295 conv = ASCIIConverter()
jvr4e5af602002-05-24 09:58:04 +00001296 else:
Behdad Esfahbod153ec402013-12-04 01:15:46 -05001297 assert False
jvr4e5af602002-05-24 09:58:04 +00001298 table[i] = op, name, arg, default, conv
1299
1300addConverters(privateDictOperators)
1301addConverters(topDictOperators)
1302
jvr4756b3a2002-05-16 18:17:32 +00001303
1304class TopDictDecompiler(psCharStrings.DictDecompiler):
1305 operators = buildOperatorDict(topDictOperators)
1306
1307
1308class PrivateDictDecompiler(psCharStrings.DictDecompiler):
1309 operators = buildOperatorDict(privateDictOperators)
1310
1311
Behdad Esfahbode388db52013-11-28 14:26:58 -05001312class DictCompiler(object):
jvrf2cf9c52002-05-23 21:50:36 +00001313
1314 def __init__(self, dictObj, strings, parent):
1315 assert isinstance(strings, IndexedStrings)
1316 self.dictObj = dictObj
1317 self.strings = strings
1318 self.parent = parent
1319 rawDict = {}
1320 for name in dictObj.order:
1321 value = getattr(dictObj, name, None)
1322 if value is None:
1323 continue
1324 conv = dictObj.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001325 value = conv.write(dictObj, value)
jvrf2cf9c52002-05-23 21:50:36 +00001326 if value == dictObj.defaults.get(name):
1327 continue
1328 rawDict[name] = value
1329 self.rawDict = rawDict
1330
jvr4e5af602002-05-24 09:58:04 +00001331 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +00001332 pass
1333
1334 def getDataLength(self):
jvr4e5af602002-05-24 09:58:04 +00001335 return len(self.compile("getDataLength"))
jvrf2cf9c52002-05-23 21:50:36 +00001336
jvr4e5af602002-05-24 09:58:04 +00001337 def compile(self, reason):
1338 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001339 print("-- compiling %s for %s" % (self.__class__.__name__, reason))
1340 print("in baseDict: ", self)
jvrf2cf9c52002-05-23 21:50:36 +00001341 rawDict = self.rawDict
1342 data = []
1343 for name in self.dictObj.order:
1344 value = rawDict.get(name)
1345 if value is None:
1346 continue
1347 op, argType = self.opcodes[name]
jvr2a9bcde2008-03-07 19:56:17 +00001348 if isinstance(argType, tuple):
jvrf2cf9c52002-05-23 21:50:36 +00001349 l = len(argType)
1350 assert len(value) == l, "value doesn't match arg type"
1351 for i in range(l):
jvred101512003-08-22 19:53:32 +00001352 arg = argType[i]
jvrf2cf9c52002-05-23 21:50:36 +00001353 v = value[i]
1354 arghandler = getattr(self, "arg_" + arg)
1355 data.append(arghandler(v))
1356 else:
1357 arghandler = getattr(self, "arg_" + argType)
1358 data.append(arghandler(value))
1359 data.append(op)
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001360 return bytesjoin(data)
jvrf2cf9c52002-05-23 21:50:36 +00001361
1362 def toFile(self, file):
jvr4e5af602002-05-24 09:58:04 +00001363 file.write(self.compile("toFile"))
jvrf2cf9c52002-05-23 21:50:36 +00001364
1365 def arg_number(self, num):
1366 return encodeNumber(num)
1367 def arg_SID(self, s):
1368 return psCharStrings.encodeIntCFF(self.strings.getSID(s))
1369 def arg_array(self, value):
1370 data = []
1371 for num in value:
1372 data.append(encodeNumber(num))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001373 return bytesjoin(data)
jvrf2cf9c52002-05-23 21:50:36 +00001374 def arg_delta(self, value):
1375 out = []
1376 last = 0
1377 for v in value:
1378 out.append(v - last)
1379 last = v
1380 data = []
1381 for num in out:
1382 data.append(encodeNumber(num))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001383 return bytesjoin(data)
jvrf2cf9c52002-05-23 21:50:36 +00001384
1385
1386def encodeNumber(num):
jvrf6ff48b2008-03-07 19:49:25 +00001387 if isinstance(num, float):
jvrf2cf9c52002-05-23 21:50:36 +00001388 return psCharStrings.encodeFloat(num)
1389 else:
1390 return psCharStrings.encodeIntCFF(num)
1391
1392
1393class TopDictCompiler(DictCompiler):
1394
1395 opcodes = buildOpcodeDict(topDictOperators)
1396
1397 def getChildren(self, strings):
1398 children = []
jvred101512003-08-22 19:53:32 +00001399 if hasattr(self.dictObj, "charset") and self.dictObj.charset:
jvrf2cf9c52002-05-23 21:50:36 +00001400 children.append(CharsetCompiler(strings, self.dictObj.charset, self))
jvrb9702ba2003-01-03 20:56:01 +00001401 if hasattr(self.dictObj, "Encoding"):
1402 encoding = self.dictObj.Encoding
jvr2a9bcde2008-03-07 19:56:17 +00001403 if not isinstance(encoding, basestring):
jvrb9702ba2003-01-03 20:56:01 +00001404 children.append(EncodingCompiler(strings, encoding, self))
jvrce522412003-08-25 07:37:25 +00001405 if hasattr(self.dictObj, "FDSelect"):
jvred101512003-08-22 19:53:32 +00001406 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1407 # issues about merging the FDArrays. Here I assume that
1408 # either the font was read from XML, and teh FDSelect indices are all
1409 # in the charstring data, or the FDSelect array is already fully defined.
1410 fdSelect = self.dictObj.FDSelect
1411 if len(fdSelect) == 0: # probably read in from XML; assume fdIndex in CharString data
1412 charStrings = self.dictObj.CharStrings
1413 for name in self.dictObj.charset:
jvred101512003-08-22 19:53:32 +00001414 fdSelect.append(charStrings[name].fdSelectIndex)
1415 fdSelectComp = FDSelectCompiler(fdSelect, self)
1416 children.append(fdSelectComp)
jvrf2cf9c52002-05-23 21:50:36 +00001417 if hasattr(self.dictObj, "CharStrings"):
1418 items = []
1419 charStrings = self.dictObj.CharStrings
1420 for name in self.dictObj.charset:
1421 items.append(charStrings[name])
1422 charStringsComp = CharStringsCompiler(items, strings, self)
1423 children.append(charStringsComp)
jvrce522412003-08-25 07:37:25 +00001424 if hasattr(self.dictObj, "FDArray"):
jvred101512003-08-22 19:53:32 +00001425 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1426 # issues about merging the FDArrays. Here I assume that the FDArray info is correct
1427 # and complete.
1428 fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self)
1429 children.append(fdArrayIndexComp)
1430 children.extend(fdArrayIndexComp.getChildren(strings))
1431 if hasattr(self.dictObj, "Private"):
1432 privComp = self.dictObj.Private.getCompiler(strings, self)
1433 children.append(privComp)
1434 children.extend(privComp.getChildren(strings))
1435 return children
1436
1437
1438class FontDictCompiler(DictCompiler):
1439
1440 opcodes = buildOpcodeDict(topDictOperators)
1441
1442 def getChildren(self, strings):
1443 children = []
jvrf2cf9c52002-05-23 21:50:36 +00001444 if hasattr(self.dictObj, "Private"):
1445 privComp = self.dictObj.Private.getCompiler(strings, self)
1446 children.append(privComp)
1447 children.extend(privComp.getChildren(strings))
1448 return children
1449
1450
1451class PrivateDictCompiler(DictCompiler):
1452
1453 opcodes = buildOpcodeDict(privateDictOperators)
1454
jvr4e5af602002-05-24 09:58:04 +00001455 def setPos(self, pos, endPos):
1456 size = endPos - pos
jvrf2cf9c52002-05-23 21:50:36 +00001457 self.parent.rawDict["Private"] = size, pos
1458 self.pos = pos
1459
1460 def getChildren(self, strings):
1461 children = []
1462 if hasattr(self.dictObj, "Subrs"):
1463 children.append(self.dictObj.Subrs.getCompiler(strings, self))
1464 return children
1465
jvr4756b3a2002-05-16 18:17:32 +00001466
Behdad Esfahbode388db52013-11-28 14:26:58 -05001467class BaseDict(object):
jvr4756b3a2002-05-16 18:17:32 +00001468
jvr4e5af602002-05-24 09:58:04 +00001469 def __init__(self, strings=None, file=None, offset=None):
jvr4756b3a2002-05-16 18:17:32 +00001470 self.rawDict = {}
jvr767102e2002-05-17 07:06:32 +00001471 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001472 print("loading %s at %s" % (self.__class__.__name__, offset))
jvr4756b3a2002-05-16 18:17:32 +00001473 self.file = file
1474 self.offset = offset
1475 self.strings = strings
jvr155aa752002-05-17 19:58:49 +00001476 self.skipNames = []
jvr4756b3a2002-05-16 18:17:32 +00001477
1478 def decompile(self, data):
jvrf2cf9c52002-05-23 21:50:36 +00001479 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001480 print(" length %s is %s" % (self.__class__.__name__, len(data)))
jvrf2cf9c52002-05-23 21:50:36 +00001481 dec = self.decompilerClass(self.strings)
jvr4756b3a2002-05-16 18:17:32 +00001482 dec.decompile(data)
1483 self.rawDict = dec.getDict()
1484 self.postDecompile()
1485
1486 def postDecompile(self):
1487 pass
1488
jvrf2cf9c52002-05-23 21:50:36 +00001489 def getCompiler(self, strings, parent):
1490 return self.compilerClass(self, strings, parent)
1491
jvr4756b3a2002-05-16 18:17:32 +00001492 def __getattr__(self, name):
1493 value = self.rawDict.get(name)
1494 if value is None:
1495 value = self.defaults.get(name)
1496 if value is None:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -05001497 raise AttributeError(name)
jvr4756b3a2002-05-16 18:17:32 +00001498 conv = self.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001499 value = conv.read(self, value)
jvr4756b3a2002-05-16 18:17:32 +00001500 setattr(self, name, value)
1501 return value
1502
1503 def toXML(self, xmlWriter, progress):
1504 for name in self.order:
jvr155aa752002-05-17 19:58:49 +00001505 if name in self.skipNames:
1506 continue
jvr4756b3a2002-05-16 18:17:32 +00001507 value = getattr(self, name, None)
1508 if value is None:
1509 continue
jvr4e5af602002-05-24 09:58:04 +00001510 conv = self.converters[name]
jvr7ce0a132002-07-23 16:42:11 +00001511 conv.xmlWrite(xmlWriter, name, value, progress)
jvr4e5af602002-05-24 09:58:04 +00001512
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001513 def fromXML(self, name, attrs, content):
jvr4e5af602002-05-24 09:58:04 +00001514 conv = self.converters[name]
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001515 value = conv.xmlRead(name, attrs, content, self)
jvr4e5af602002-05-24 09:58:04 +00001516 setattr(self, name, value)
jvr4756b3a2002-05-16 18:17:32 +00001517
1518
1519class TopDict(BaseDict):
1520
1521 defaults = buildDefaults(topDictOperators)
1522 converters = buildConverters(topDictOperators)
1523 order = buildOrder(topDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001524 decompilerClass = TopDictDecompiler
1525 compilerClass = TopDictCompiler
Just7842e561999-12-16 21:34:53 +00001526
jvr4e5af602002-05-24 09:58:04 +00001527 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
jvr016ca762002-05-16 18:38:03 +00001528 BaseDict.__init__(self, strings, file, offset)
1529 self.GlobalSubrs = GlobalSubrs
1530
Just7842e561999-12-16 21:34:53 +00001531 def getGlyphOrder(self):
1532 return self.charset
1533
jvr4756b3a2002-05-16 18:17:32 +00001534 def postDecompile(self):
1535 offset = self.rawDict.get("CharStrings")
1536 if offset is None:
1537 return
1538 # get the number of glyphs beforehand.
1539 self.file.seek(offset)
jvra2ad5442002-05-17 18:36:07 +00001540 self.numGlyphs = readCard16(self.file)
Just7842e561999-12-16 21:34:53 +00001541
jvr016ca762002-05-16 18:38:03 +00001542 def toXML(self, xmlWriter, progress):
jvr155aa752002-05-17 19:58:49 +00001543 if hasattr(self, "CharStrings"):
jvr7ce0a132002-07-23 16:42:11 +00001544 self.decompileAllCharStrings(progress)
jvred101512003-08-22 19:53:32 +00001545 if hasattr(self, "ROS"):
1546 self.skipNames = ['Encoding']
jvr155aa752002-05-17 19:58:49 +00001547 if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
1548 # these values have default values, but I only want them to show up
1549 # in CID fonts.
1550 self.skipNames = ['CIDFontVersion', 'CIDFontRevision', 'CIDFontType',
1551 'CIDCount']
jvr016ca762002-05-16 18:38:03 +00001552 BaseDict.toXML(self, xmlWriter, progress)
jvr767102e2002-05-17 07:06:32 +00001553
jvr7ce0a132002-07-23 16:42:11 +00001554 def decompileAllCharStrings(self, progress):
jvr4e5af602002-05-24 09:58:04 +00001555 # XXX only when doing ttdump -i?
jvr7ce0a132002-07-23 16:42:11 +00001556 i = 0
jvra2ad5442002-05-17 18:36:07 +00001557 for charString in self.CharStrings.values():
jvred101512003-08-22 19:53:32 +00001558 try:
1559 charString.decompile()
1560 except:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001561 print("Error in charstring ", i)
jvred101512003-08-22 19:53:32 +00001562 import sys
Behdad Esfahbod153ec402013-12-04 01:15:46 -05001563 typ, value = sys.exc_info()[0:2]
1564 raise typ(value)
jvr7ce0a132002-07-23 16:42:11 +00001565 if not i % 30 and progress:
1566 progress.increment(0) # update
1567 i = i + 1
Just7842e561999-12-16 21:34:53 +00001568
1569
jvred101512003-08-22 19:53:32 +00001570class FontDict(BaseDict):
1571
1572 defaults = buildDefaults(topDictOperators)
1573 converters = buildConverters(topDictOperators)
1574 order = buildOrder(topDictOperators)
1575 decompilerClass = None
1576 compilerClass = FontDictCompiler
1577
1578 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
1579 BaseDict.__init__(self, strings, file, offset)
1580 self.GlobalSubrs = GlobalSubrs
1581
1582 def getGlyphOrder(self):
1583 return self.charset
1584
1585 def toXML(self, xmlWriter, progress):
1586 self.skipNames = ['Encoding']
1587 BaseDict.toXML(self, xmlWriter, progress)
1588
1589
1590
jvr4756b3a2002-05-16 18:17:32 +00001591class PrivateDict(BaseDict):
1592 defaults = buildDefaults(privateDictOperators)
1593 converters = buildConverters(privateDictOperators)
1594 order = buildOrder(privateDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001595 decompilerClass = PrivateDictDecompiler
1596 compilerClass = PrivateDictCompiler
Just7842e561999-12-16 21:34:53 +00001597
1598
Behdad Esfahbode388db52013-11-28 14:26:58 -05001599class IndexedStrings(object):
jvre3275582002-05-14 12:22:03 +00001600
jvr767102e2002-05-17 07:06:32 +00001601 """SID -> string mapping."""
1602
1603 def __init__(self, file=None):
1604 if file is None:
jvre3275582002-05-14 12:22:03 +00001605 strings = []
jvr767102e2002-05-17 07:06:32 +00001606 else:
Behdad Esfahbod617ec412013-12-10 18:54:26 -05001607 strings = [tostr(s, encoding="latin1") for s in Index(file)]
jvre3275582002-05-14 12:22:03 +00001608 self.strings = strings
1609
jvrf2cf9c52002-05-23 21:50:36 +00001610 def getCompiler(self):
1611 return IndexedStringsCompiler(self, None, None)
1612
1613 def __len__(self):
1614 return len(self.strings)
1615
jvre3275582002-05-14 12:22:03 +00001616 def __getitem__(self, SID):
1617 if SID < cffStandardStringCount:
1618 return cffStandardStrings[SID]
1619 else:
1620 return self.strings[SID - cffStandardStringCount]
1621
1622 def getSID(self, s):
1623 if not hasattr(self, "stringMapping"):
1624 self.buildStringMapping()
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -05001625 if s in cffStandardStringMapping:
jvre3275582002-05-14 12:22:03 +00001626 SID = cffStandardStringMapping[s]
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -05001627 elif s in self.stringMapping:
jvre3275582002-05-14 12:22:03 +00001628 SID = self.stringMapping[s]
1629 else:
1630 SID = len(self.strings) + cffStandardStringCount
1631 self.strings.append(s)
1632 self.stringMapping[s] = SID
1633 return SID
1634
1635 def getStrings(self):
1636 return self.strings
1637
1638 def buildStringMapping(self):
1639 self.stringMapping = {}
1640 for index in range(len(self.strings)):
1641 self.stringMapping[self.strings[index]] = index + cffStandardStringCount
1642
1643
Just7842e561999-12-16 21:34:53 +00001644# The 391 Standard Strings as used in the CFF format.
1645# from Adobe Technical None #5176, version 1.0, 18 March 1998
1646
1647cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
1648 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
1649 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
1650 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
1651 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C',
1652 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
1653 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
1654 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
1655 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
1656 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
1657 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
1658 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
1659 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
1660 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
1661 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
1662 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
1663 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
1664 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
1665 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
1666 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
1667 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
1668 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
1669 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
1670 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
1671 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
1672 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
1673 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
1674 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
1675 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
1676 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
1677 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
1678 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
1679 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
1680 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
1681 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
1682 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
1683 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
1684 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
1685 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
1686 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
1687 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
1688 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
1689 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
1690 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
1691 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
1692 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
1693 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
1694 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
1695 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
1696 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
1697 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
1698 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
1699 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
1700 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
1701 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
1702 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
1703 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
1704 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
1705 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
1706 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
1707 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
1708 '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
1709 'Semibold'
1710]
1711
1712cffStandardStringCount = 391
1713assert len(cffStandardStrings) == cffStandardStringCount
1714# build reverse mapping
1715cffStandardStringMapping = {}
1716for _i in range(cffStandardStringCount):
1717 cffStandardStringMapping[cffStandardStrings[_i]] = _i
jvrc60a44f2006-10-21 13:41:18 +00001718
1719cffISOAdobeStrings = [".notdef", "space", "exclam", "quotedbl", "numbersign",
1720"dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright",
1721"asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two",
1722"three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon",
1723"less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G",
1724"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
1725"X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum",
1726"underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
1727"k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
1728"braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent",
1729"sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle",
1730"quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl",
1731"endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet",
1732"quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis",
1733"perthousand", "questiondown", "grave", "acute", "circumflex", "tilde",
1734"macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
1735"ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE",
1736"ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls",
1737"onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus",
1738"Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn",
1739"threequarters", "twosuperior", "registered", "minus", "eth", "multiply",
1740"threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
1741"Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave",
1742"Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute",
1743"Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute",
1744"Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute",
1745"acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute",
1746"ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis",
1747"igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde",
1748"scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis",
1749"zcaron"]
1750
1751cffISOAdobeStringCount = 229
1752assert len(cffISOAdobeStrings) == cffISOAdobeStringCount
1753
1754cffIExpertStrings = [".notdef", "space", "exclamsmall", "Hungarumlautsmall",
1755"dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall",
1756"parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader",
1757"comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle",
1758"twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle",
1759"sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon",
1760"commasuperior", "threequartersemdash", "periodsuperior", "questionsmall",
1761"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1762"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1763"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1764"parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
1765"Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall",
1766"Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
1767"Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall",
1768"Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall",
1769"exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall",
1770"Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall",
1771"figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall",
1772"onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth",
1773"threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds",
1774"zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior",
1775"fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior",
1776"zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior",
1777"fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior",
1778"centinferior", "dollarinferior", "periodinferior", "commainferior",
1779"Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall",
1780"Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall",
1781"Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall",
1782"Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall",
1783"Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall",
1784"Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall",
1785"Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall",
1786"Ydieresissmall"]
1787
1788cffExpertStringCount = 166
1789assert len(cffIExpertStrings) == cffExpertStringCount
1790
1791cffExpertSubsetStrings = [".notdef", "space", "dollaroldstyle",
1792"dollarsuperior", "parenleftsuperior", "parenrightsuperior", "twodotenleader",
1793"onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle",
1794"oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle",
1795"sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon",
1796"semicolon", "commasuperior", "threequartersemdash", "periodsuperior",
1797"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1798"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1799"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1800"parenrightinferior", "hyphensuperior", "colonmonetary", "onefitted", "rupiah",
1801"centoldstyle", "figuredash", "hypheninferior", "onequarter", "onehalf",
1802"threequarters", "oneeighth", "threeeighths", "fiveeighths", "seveneighths",
1803"onethird", "twothirds", "zerosuperior", "onesuperior", "twosuperior",
1804"threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior",
1805"eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior",
1806"threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior",
1807"eightinferior", "nineinferior", "centinferior", "dollarinferior",
1808"periodinferior", "commainferior"]
1809
1810cffExpertSubsetStringCount = 87
1811assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount