blob: 9265ebce73e5113453a5abaac3d9e11d02120fdf [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
Behdad Esfahbode388db52013-11-28 14:26:58 -050024class CFFFontSet(object):
Just7842e561999-12-16 21:34:53 +000025
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:
Behdad Esfahbod024d1532013-11-28 07:10:53 -050084 xmlWriter.begintag("CFFFont", name=tostr(fontName))
Just7842e561999-12-16 21:34:53 +000085 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
Behdad Esfahbode388db52013-11-28 14:26:58 -0500129class CFFWriter(object):
jvrf2cf9c52002-05-23 21:50:36 +0000130
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
Behdad Esfahbode388db52013-11-28 14:26:58 -0500183class IndexCompiler(object):
jvrf2cf9c52002-05-23 21:50:36 +0000184
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:
Behdad Esfahbodd1ba7b52013-11-28 07:48:20 -0500229 file.write(tobytes(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
Behdad Esfahbode388db52013-11-28 14:26:58 -0500306class Index(object):
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
Behdad Esfahbode388db52013-11-28 14:26:58 -0500503class CharStrings(object):
jvr4756b3a2002-05-16 18:17:32 +0000504
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
Behdad Esfahbode388db52013-11-28 14:26:58 -0500665class SimpleConverter(object):
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
Behdad Esfahbod024d1532013-11-28 07:10:53 -0500676class ASCIIConverter(SimpleConverter):
677 def read(self, parent, value):
678 return tostr(value, encoding='ascii')
679 def write(self, parent, value):
680 return tobytes(value, encoding='ascii')
Behdad Esfahbod8c5c9662013-10-28 13:20:00 +0100681 def xmlWrite(self, xmlWriter, name, value, progress):
Behdad Esfahbod024d1532013-11-28 07:10:53 -0500682 xmlWriter.simpletag(name, value=tostr(value, encoding="ascii"))
Behdad Esfahbod8c5c9662013-10-28 13:20:00 +0100683 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500684 def xmlRead(self, name, attrs, content, parent):
Behdad Esfahbod024d1532013-11-28 07:10:53 -0500685 return tobytes(attrs["value"], encoding=("ascii"))
686
687class Latin1Converter(SimpleConverter):
688 def read(self, parent, value):
689 return tostr(value, encoding='latin1')
690 def write(self, parent, value):
691 return tobytes(value, encoding='latin1')
692 def xmlWrite(self, xmlWriter, name, value, progress):
693 xmlWriter.simpletag(name, value=tostr(value, encoding="latin1"))
694 xmlWriter.newline()
695 def xmlRead(self, name, attrs, content, parent):
696 return tobytes(attrs["value"], encoding=("latin1"))
jvre2ca9b52002-09-09 14:18:39 +0000697
698
jvr4e5af602002-05-24 09:58:04 +0000699def parseNum(s):
700 try:
701 value = int(s)
702 except:
703 value = float(s)
704 return value
705
706class NumberConverter(SimpleConverter):
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500707 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000708 return parseNum(attrs["value"])
709
710class ArrayConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000711 def xmlWrite(self, xmlWriter, name, value, progress):
Behdad Esfahbode5ca7962013-11-27 04:38:16 -0500712 value = " ".join(map(str, value))
713 xmlWriter.simpletag(name, value=value)
jvr4e5af602002-05-24 09:58:04 +0000714 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500715 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000716 values = attrs["value"].split()
Behdad Esfahbode5ca7962013-11-27 04:38:16 -0500717 return [parseNum(value) for value in values]
jvr4e5af602002-05-24 09:58:04 +0000718
719class TableConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000720 def xmlWrite(self, xmlWriter, name, value, progress):
jvra2ad5442002-05-17 18:36:07 +0000721 xmlWriter.begintag(name)
722 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000723 value.toXML(xmlWriter, progress)
jvra2ad5442002-05-17 18:36:07 +0000724 xmlWriter.endtag(name)
725 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500726 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000727 ob = self.getClass()()
728 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000729 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000730 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500731 name, attrs, content = element
732 ob.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000733 return ob
jvra2ad5442002-05-17 18:36:07 +0000734
jvr4e5af602002-05-24 09:58:04 +0000735class PrivateDictConverter(TableConverter):
736 def getClass(self):
737 return PrivateDict
jvr4756b3a2002-05-16 18:17:32 +0000738 def read(self, parent, value):
739 size, offset = value
740 file = parent.file
jvr4e5af602002-05-24 09:58:04 +0000741 priv = PrivateDict(parent.strings, file, offset)
jvr4756b3a2002-05-16 18:17:32 +0000742 file.seek(offset)
743 data = file.read(size)
744 len(data) == size
jvr4e5af602002-05-24 09:58:04 +0000745 priv.decompile(data)
746 return priv
jvrf2cf9c52002-05-23 21:50:36 +0000747 def write(self, parent, value):
748 return (0, 0) # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000749
jvr4e5af602002-05-24 09:58:04 +0000750class SubrsConverter(TableConverter):
751 def getClass(self):
752 return SubrsIndex
jvr4756b3a2002-05-16 18:17:32 +0000753 def read(self, parent, value):
754 file = parent.file
755 file.seek(parent.offset + value) # Offset(self)
jvr4e5af602002-05-24 09:58:04 +0000756 return SubrsIndex(file)
jvrf2cf9c52002-05-23 21:50:36 +0000757 def write(self, parent, value):
758 return 0 # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000759
jvr4e5af602002-05-24 09:58:04 +0000760class CharStringsConverter(TableConverter):
jvr4756b3a2002-05-16 18:17:32 +0000761 def read(self, parent, value):
762 file = parent.file
jvr767102e2002-05-17 07:06:32 +0000763 charset = parent.charset
jvra2ad5442002-05-17 18:36:07 +0000764 globalSubrs = parent.GlobalSubrs
765 if hasattr(parent, "ROS"):
766 fdSelect, fdArray = parent.FDSelect, parent.FDArray
767 private = None
768 else:
769 fdSelect, fdArray = None, None
770 private = parent.Private
jvr4756b3a2002-05-16 18:17:32 +0000771 file.seek(value) # Offset(0)
jvra2ad5442002-05-17 18:36:07 +0000772 return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
jvrf2cf9c52002-05-23 21:50:36 +0000773 def write(self, parent, value):
774 return 0 # dummy value
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500775 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +0000776 if hasattr(parent, "ROS"):
777 # if it is a CID-keyed font, then the private Dict is extracted from the parent.FDArray
778 private, fdSelect, fdArray = None, parent.FDSelect, parent.FDArray
779 else:
780 # if it is a name-keyed font, then the private dict is in the top dict, and there is no fdArray.
781 private, fdSelect, fdArray = parent.Private, None, None
782 charStrings = CharStrings(None, None, parent.GlobalSubrs, private, fdSelect, fdArray)
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500783 charStrings.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000784 return charStrings
jvr4756b3a2002-05-16 18:17:32 +0000785
Behdad Esfahbode388db52013-11-28 14:26:58 -0500786class CharsetConverter(object):
jvr4756b3a2002-05-16 18:17:32 +0000787 def read(self, parent, value):
788 isCID = hasattr(parent, "ROS")
789 if value > 2:
790 numGlyphs = parent.numGlyphs
791 file = parent.file
792 file.seek(value)
jvrf2cf9c52002-05-23 21:50:36 +0000793 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500794 print("loading charset at %s" % value)
jvra2ad5442002-05-17 18:36:07 +0000795 format = readCard8(file)
Just7842e561999-12-16 21:34:53 +0000796 if format == 0:
jvrc60a44f2006-10-21 13:41:18 +0000797 charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
jvr4756b3a2002-05-16 18:17:32 +0000798 elif format == 1 or format == 2:
799 charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
Just7842e561999-12-16 21:34:53 +0000800 else:
jvr1890b952002-05-15 07:41:30 +0000801 raise NotImplementedError
jvr4756b3a2002-05-16 18:17:32 +0000802 assert len(charset) == numGlyphs
jvrf2cf9c52002-05-23 21:50:36 +0000803 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500804 print(" charset end at %s" % file.tell())
jvrc60a44f2006-10-21 13:41:18 +0000805 else: # offset == 0 -> no charset data.
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500806 if isCID or "CharStrings" not in parent.rawDict:
jvrc60a44f2006-10-21 13:41:18 +0000807 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 +0000808 charset = None
809 elif value == 0:
jvrc60a44f2006-10-21 13:41:18 +0000810 charset = cffISOAdobeStrings
jvr4756b3a2002-05-16 18:17:32 +0000811 elif value == 1:
jvrc60a44f2006-10-21 13:41:18 +0000812 charset = cffIExpertStrings
jvr4756b3a2002-05-16 18:17:32 +0000813 elif value == 2:
jvrc60a44f2006-10-21 13:41:18 +0000814 charset = cffExpertSubsetStrings
jvr4756b3a2002-05-16 18:17:32 +0000815 return charset
jvrc60a44f2006-10-21 13:41:18 +0000816
jvrf2cf9c52002-05-23 21:50:36 +0000817 def write(self, parent, value):
818 return 0 # dummy value
jvr7ce0a132002-07-23 16:42:11 +0000819 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000820 # XXX only write charset when not in OT/TTX context, where we
821 # dump charset as a separate "GlyphOrder" table.
822 ##xmlWriter.simpletag("charset")
823 xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
jvr4756b3a2002-05-16 18:17:32 +0000824 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500825 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000826 if 0:
827 return safeEval(attrs["value"])
jvr4756b3a2002-05-16 18:17:32 +0000828
829
Behdad Esfahbode388db52013-11-28 14:26:58 -0500830class CharsetCompiler(object):
jvrf2cf9c52002-05-23 21:50:36 +0000831
832 def __init__(self, strings, charset, parent):
833 assert charset[0] == '.notdef'
jvred101512003-08-22 19:53:32 +0000834 isCID = hasattr(parent.dictObj, "ROS")
835 data0 = packCharset0(charset, isCID, strings)
836 data = packCharset(charset, isCID, strings)
jvr6004baf2002-05-24 10:35:13 +0000837 if len(data) < len(data0):
838 self.data = data
839 else:
840 self.data = data0
jvrf2cf9c52002-05-23 21:50:36 +0000841 self.parent = parent
842
jvr4e5af602002-05-24 09:58:04 +0000843 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000844 self.parent.rawDict["charset"] = pos
845
846 def getDataLength(self):
847 return len(self.data)
848
849 def toFile(self, file):
850 file.write(self.data)
851
852
jvred101512003-08-22 19:53:32 +0000853def getCIDfromName(name, strings):
854 return int(name[3:])
855
856def getSIDfromName(name, strings):
857 return strings.getSID(name)
858
859def packCharset0(charset, isCID, strings):
jvr6004baf2002-05-24 10:35:13 +0000860 format = 0
861 data = [packCard8(format)]
jvred101512003-08-22 19:53:32 +0000862 if isCID:
863 getNameID = getCIDfromName
864 else:
865 getNameID = getSIDfromName
866
jvr6004baf2002-05-24 10:35:13 +0000867 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000868 data.append(packCard16(getNameID(name,strings)))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -0500869 return bytesjoin(data)
jvr6004baf2002-05-24 10:35:13 +0000870
jvred101512003-08-22 19:53:32 +0000871
872def packCharset(charset, isCID, strings):
jvr6004baf2002-05-24 10:35:13 +0000873 format = 1
jvr6004baf2002-05-24 10:35:13 +0000874 ranges = []
875 first = None
876 end = 0
jvred101512003-08-22 19:53:32 +0000877 if isCID:
878 getNameID = getCIDfromName
879 else:
880 getNameID = getSIDfromName
881
jvr6004baf2002-05-24 10:35:13 +0000882 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000883 SID = getNameID(name, strings)
jvr6004baf2002-05-24 10:35:13 +0000884 if first is None:
885 first = SID
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500886 elif end + 1 != SID:
jvr6004baf2002-05-24 10:35:13 +0000887 nLeft = end - first
888 if nLeft > 255:
889 format = 2
890 ranges.append((first, nLeft))
891 first = SID
892 end = SID
893 nLeft = end - first
894 if nLeft > 255:
895 format = 2
896 ranges.append((first, nLeft))
897
jvr74cd1ef2002-05-24 10:38:04 +0000898 data = [packCard8(format)]
jvr6004baf2002-05-24 10:35:13 +0000899 if format == 1:
900 nLeftFunc = packCard8
901 else:
902 nLeftFunc = packCard16
903 for first, nLeft in ranges:
904 data.append(packCard16(first) + nLeftFunc(nLeft))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -0500905 return bytesjoin(data)
jvr6004baf2002-05-24 10:35:13 +0000906
jvrc60a44f2006-10-21 13:41:18 +0000907def parseCharset0(numGlyphs, file, strings, isCID):
jvrf2cf9c52002-05-23 21:50:36 +0000908 charset = [".notdef"]
jvrc60a44f2006-10-21 13:41:18 +0000909 if isCID:
910 for i in range(numGlyphs - 1):
911 CID = readCard16(file)
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500912 charset.append("cid" + str(CID).zfill(5))
jvrc60a44f2006-10-21 13:41:18 +0000913 else:
914 for i in range(numGlyphs - 1):
915 SID = readCard16(file)
916 charset.append(strings[SID])
jvrf2cf9c52002-05-23 21:50:36 +0000917 return charset
918
jvr4756b3a2002-05-16 18:17:32 +0000919def parseCharset(numGlyphs, file, strings, isCID, format):
920 charset = ['.notdef']
921 count = 1
922 if format == 1:
jvra2ad5442002-05-17 18:36:07 +0000923 nLeftFunc = readCard8
jvr4756b3a2002-05-16 18:17:32 +0000924 else:
jvra2ad5442002-05-17 18:36:07 +0000925 nLeftFunc = readCard16
jvr4756b3a2002-05-16 18:17:32 +0000926 while count < numGlyphs:
jvra2ad5442002-05-17 18:36:07 +0000927 first = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000928 nLeft = nLeftFunc(file)
929 if isCID:
930 for CID in range(first, first+nLeft+1):
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500931 charset.append("cid" + str(CID).zfill(5))
jvr1890b952002-05-15 07:41:30 +0000932 else:
jvr4756b3a2002-05-16 18:17:32 +0000933 for SID in range(first, first+nLeft+1):
934 charset.append(strings[SID])
935 count = count + nLeft + 1
936 return charset
937
938
Behdad Esfahbode388db52013-11-28 14:26:58 -0500939class EncodingCompiler(object):
jvrb9702ba2003-01-03 20:56:01 +0000940
941 def __init__(self, strings, encoding, parent):
jvr2a9bcde2008-03-07 19:56:17 +0000942 assert not isinstance(encoding, basestring)
jvrb9702ba2003-01-03 20:56:01 +0000943 data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
944 data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
945 if len(data0) < len(data1):
946 self.data = data0
947 else:
948 self.data = data1
949 self.parent = parent
950
951 def setPos(self, pos, endPos):
952 self.parent.rawDict["Encoding"] = pos
953
954 def getDataLength(self):
955 return len(self.data)
956
957 def toFile(self, file):
958 file.write(self.data)
959
960
961class EncodingConverter(SimpleConverter):
962
963 def read(self, parent, value):
964 if value == 0:
965 return "StandardEncoding"
966 elif value == 1:
967 return "ExpertEncoding"
968 else:
969 assert value > 1
970 file = parent.file
971 file.seek(value)
972 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500973 print("loading Encoding at %s" % value)
jvrb9702ba2003-01-03 20:56:01 +0000974 format = readCard8(file)
975 haveSupplement = format & 0x80
976 if haveSupplement:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -0500977 raise NotImplementedError("Encoding supplements are not yet supported")
jvrb9702ba2003-01-03 20:56:01 +0000978 format = format & 0x7f
979 if format == 0:
980 encoding = parseEncoding0(parent.charset, file, haveSupplement,
981 parent.strings)
982 elif format == 1:
983 encoding = parseEncoding1(parent.charset, file, haveSupplement,
984 parent.strings)
985 return encoding
986
987 def write(self, parent, value):
988 if value == "StandardEncoding":
989 return 0
990 elif value == "ExpertEncoding":
991 return 1
992 return 0 # dummy value
993
994 def xmlWrite(self, xmlWriter, name, value, progress):
995 if value in ("StandardEncoding", "ExpertEncoding"):
996 xmlWriter.simpletag(name, name=value)
997 xmlWriter.newline()
998 return
999 xmlWriter.begintag(name)
1000 xmlWriter.newline()
1001 for code in range(len(value)):
1002 glyphName = value[code]
1003 if glyphName != ".notdef":
1004 xmlWriter.simpletag("map", code=hex(code), name=glyphName)
1005 xmlWriter.newline()
1006 xmlWriter.endtag(name)
1007 xmlWriter.newline()
1008
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001009 def xmlRead(self, name, attrs, content, parent):
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -05001010 if "name" in attrs:
jvrb9702ba2003-01-03 20:56:01 +00001011 return attrs["name"]
1012 encoding = [".notdef"] * 256
1013 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +00001014 if isinstance(element, basestring):
jvrb9702ba2003-01-03 20:56:01 +00001015 continue
1016 name, attrs, content = element
1017 code = safeEval(attrs["code"])
1018 glyphName = attrs["name"]
1019 encoding[code] = glyphName
1020 return encoding
1021
1022
1023def parseEncoding0(charset, file, haveSupplement, strings):
1024 nCodes = readCard8(file)
1025 encoding = [".notdef"] * 256
1026 for glyphID in range(1, nCodes + 1):
1027 code = readCard8(file)
1028 if code != 0:
1029 encoding[code] = charset[glyphID]
1030 return encoding
1031
1032def parseEncoding1(charset, file, haveSupplement, strings):
1033 nRanges = readCard8(file)
1034 encoding = [".notdef"] * 256
1035 glyphID = 1
1036 for i in range(nRanges):
1037 code = readCard8(file)
1038 nLeft = readCard8(file)
1039 for glyphID in range(glyphID, glyphID + nLeft + 1):
1040 encoding[code] = charset[glyphID]
1041 code = code + 1
1042 glyphID = glyphID + 1
1043 return encoding
1044
1045def packEncoding0(charset, encoding, strings):
1046 format = 0
1047 m = {}
1048 for code in range(len(encoding)):
1049 name = encoding[code]
1050 if name != ".notdef":
1051 m[name] = code
1052 codes = []
1053 for name in charset[1:]:
1054 code = m.get(name)
1055 codes.append(code)
1056
1057 while codes and codes[-1] is None:
1058 codes.pop()
1059
1060 data = [packCard8(format), packCard8(len(codes))]
1061 for code in codes:
1062 if code is None:
1063 code = 0
1064 data.append(packCard8(code))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001065 return bytesjoin(data)
jvrb9702ba2003-01-03 20:56:01 +00001066
1067def packEncoding1(charset, encoding, strings):
1068 format = 1
1069 m = {}
1070 for code in range(len(encoding)):
1071 name = encoding[code]
1072 if name != ".notdef":
1073 m[name] = code
1074 ranges = []
1075 first = None
1076 end = 0
1077 for name in charset[1:]:
1078 code = m.get(name, -1)
1079 if first is None:
1080 first = code
Behdad Esfahbod180ace62013-11-27 02:40:30 -05001081 elif end + 1 != code:
jvrb9702ba2003-01-03 20:56:01 +00001082 nLeft = end - first
1083 ranges.append((first, nLeft))
1084 first = code
1085 end = code
1086 nLeft = end - first
1087 ranges.append((first, nLeft))
1088
1089 # remove unencoded glyphs at the end.
1090 while ranges and ranges[-1][0] == -1:
1091 ranges.pop()
1092
1093 data = [packCard8(format), packCard8(len(ranges))]
1094 for first, nLeft in ranges:
1095 if first == -1: # unencoded
1096 first = 0
1097 data.append(packCard8(first) + packCard8(nLeft))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001098 return bytesjoin(data)
jvrb9702ba2003-01-03 20:56:01 +00001099
1100
jvr4e5af602002-05-24 09:58:04 +00001101class FDArrayConverter(TableConverter):
jvred101512003-08-22 19:53:32 +00001102
jvra2ad5442002-05-17 18:36:07 +00001103 def read(self, parent, value):
1104 file = parent.file
1105 file.seek(value)
jvred101512003-08-22 19:53:32 +00001106 fdArray = FDArrayIndex(file)
jvra2ad5442002-05-17 18:36:07 +00001107 fdArray.strings = parent.strings
1108 fdArray.GlobalSubrs = parent.GlobalSubrs
1109 return fdArray
1110
jvred101512003-08-22 19:53:32 +00001111 def write(self, parent, value):
1112 return 0 # dummy value
1113
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001114 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001115 fdArray = FDArrayIndex()
1116 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +00001117 if isinstance(element, basestring):
jvred101512003-08-22 19:53:32 +00001118 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001119 name, attrs, content = element
1120 fdArray.fromXML(name, attrs, content)
jvred101512003-08-22 19:53:32 +00001121 return fdArray
1122
jvra2ad5442002-05-17 18:36:07 +00001123
Behdad Esfahbode388db52013-11-28 14:26:58 -05001124class FDSelectConverter(object):
jvred101512003-08-22 19:53:32 +00001125
jvra2ad5442002-05-17 18:36:07 +00001126 def read(self, parent, value):
1127 file = parent.file
1128 file.seek(value)
jvred101512003-08-22 19:53:32 +00001129 fdSelect = FDSelect(file, parent.numGlyphs)
1130 return fdSelect
1131
1132 def write(self, parent, value):
1133 return 0 # dummy value
1134
1135 # The FDSelect glyph data is written out to XML in the charstring keys,
1136 # so we write out only the format selector
jvr7ce0a132002-07-23 16:42:11 +00001137 def xmlWrite(self, xmlWriter, name, value, progress):
jvred101512003-08-22 19:53:32 +00001138 xmlWriter.simpletag(name, [('format', value.format)])
1139 xmlWriter.newline()
1140
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001141 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001142 format = safeEval(attrs["format"])
1143 file = None
1144 numGlyphs = None
1145 fdSelect = FDSelect(file, numGlyphs, format)
1146 return fdSelect
1147
1148
1149def packFDSelect0(fdSelectArray):
1150 format = 0
1151 data = [packCard8(format)]
1152 for index in fdSelectArray:
1153 data.append(packCard8(index))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001154 return bytesjoin(data)
jvred101512003-08-22 19:53:32 +00001155
1156
1157def packFDSelect3(fdSelectArray):
1158 format = 3
1159 fdRanges = []
1160 first = None
1161 end = 0
1162 lenArray = len(fdSelectArray)
1163 lastFDIndex = -1
1164 for i in range(lenArray):
1165 fdIndex = fdSelectArray[i]
1166 if lastFDIndex != fdIndex:
1167 fdRanges.append([i, fdIndex])
1168 lastFDIndex = fdIndex
1169 sentinelGID = i + 1
1170
1171 data = [packCard8(format)]
1172 data.append(packCard16( len(fdRanges) ))
1173 for fdRange in fdRanges:
1174 data.append(packCard16(fdRange[0]))
1175 data.append(packCard8(fdRange[1]))
1176 data.append(packCard16(sentinelGID))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001177 return bytesjoin(data)
jvred101512003-08-22 19:53:32 +00001178
1179
Behdad Esfahbode388db52013-11-28 14:26:58 -05001180class FDSelectCompiler(object):
jvred101512003-08-22 19:53:32 +00001181
1182 def __init__(self, fdSelect, parent):
1183 format = fdSelect.format
1184 fdSelectArray = fdSelect.gidArray
1185 if format == 0:
1186 self.data = packFDSelect0(fdSelectArray)
1187 elif format == 3:
1188 self.data = packFDSelect3(fdSelectArray)
1189 else:
1190 # choose smaller of the two formats
1191 data0 = packFDSelect0(fdSelectArray)
1192 data3 = packFDSelect3(fdSelectArray)
1193 if len(data0) < len(data3):
1194 self.data = data0
1195 fdSelect.format = 0
1196 else:
1197 self.data = data3
1198 fdSelect.format = 3
1199
1200 self.parent = parent
1201
1202 def setPos(self, pos, endPos):
1203 self.parent.rawDict["FDSelect"] = pos
1204
1205 def getDataLength(self):
1206 return len(self.data)
1207
1208 def toFile(self, file):
1209 file.write(self.data)
jvra2ad5442002-05-17 18:36:07 +00001210
1211
jvr4e5af602002-05-24 09:58:04 +00001212class ROSConverter(SimpleConverter):
jvred101512003-08-22 19:53:32 +00001213
jvr7ce0a132002-07-23 16:42:11 +00001214 def xmlWrite(self, xmlWriter, name, value, progress):
jvr155aa752002-05-17 19:58:49 +00001215 registry, order, supplement = value
Behdad Esfahbod024d1532013-11-28 07:10:53 -05001216 xmlWriter.simpletag(name, [('Registry', tostr(registry)), ('Order', tostr(order)),
jvrf2cf9c52002-05-23 21:50:36 +00001217 ('Supplement', supplement)])
jvr4afb2572002-05-18 20:07:01 +00001218 xmlWriter.newline()
jvr155aa752002-05-17 19:58:49 +00001219
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001220 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001221 return (attrs['Registry'], attrs['Order'], safeEval(attrs['Supplement']))
1222
1223
jvr155aa752002-05-17 19:58:49 +00001224
jvr4756b3a2002-05-16 18:17:32 +00001225topDictOperators = [
1226# opcode name argument type default converter
jvr155aa752002-05-17 19:58:49 +00001227 ((12, 30), 'ROS', ('SID','SID','number'), None, ROSConverter()),
jvrf2cf9c52002-05-23 21:50:36 +00001228 ((12, 20), 'SyntheticBase', 'number', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001229 (0, 'version', 'SID', None, None),
jvre2ca9b52002-09-09 14:18:39 +00001230 (1, 'Notice', 'SID', None, Latin1Converter()),
1231 ((12, 0), 'Copyright', 'SID', None, Latin1Converter()),
jvr4756b3a2002-05-16 18:17:32 +00001232 (2, 'FullName', 'SID', None, None),
jvr155aa752002-05-17 19:58:49 +00001233 ((12, 38), 'FontName', 'SID', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001234 (3, 'FamilyName', 'SID', None, None),
1235 (4, 'Weight', 'SID', None, None),
1236 ((12, 1), 'isFixedPitch', 'number', 0, None),
1237 ((12, 2), 'ItalicAngle', 'number', 0, None),
1238 ((12, 3), 'UnderlinePosition', 'number', None, None),
1239 ((12, 4), 'UnderlineThickness', 'number', 50, None),
1240 ((12, 5), 'PaintType', 'number', 0, None),
1241 ((12, 6), 'CharstringType', 'number', 2, None),
1242 ((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0], None),
1243 (13, 'UniqueID', 'number', None, None),
1244 (5, 'FontBBox', 'array', [0,0,0,0], None),
1245 ((12, 8), 'StrokeWidth', 'number', 0, None),
1246 (14, 'XUID', 'array', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001247 ((12, 21), 'PostScript', 'SID', None, None),
1248 ((12, 22), 'BaseFontName', 'SID', None, None),
1249 ((12, 23), 'BaseFontBlend', 'delta', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001250 ((12, 31), 'CIDFontVersion', 'number', 0, None),
1251 ((12, 32), 'CIDFontRevision', 'number', 0, None),
1252 ((12, 33), 'CIDFontType', 'number', 0, None),
1253 ((12, 34), 'CIDCount', 'number', 8720, None),
jvred101512003-08-22 19:53:32 +00001254 (15, 'charset', 'number', 0, CharsetConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001255 ((12, 35), 'UIDBase', 'number', None, None),
jvrb9702ba2003-01-03 20:56:01 +00001256 (16, 'Encoding', 'number', 0, EncodingConverter()),
jvr155aa752002-05-17 19:58:49 +00001257 (18, 'Private', ('number','number'), None, PrivateDictConverter()),
jvred101512003-08-22 19:53:32 +00001258 ((12, 37), 'FDSelect', 'number', None, FDSelectConverter()),
1259 ((12, 36), 'FDArray', 'number', None, FDArrayConverter()),
jvr155aa752002-05-17 19:58:49 +00001260 (17, 'CharStrings', 'number', None, CharStringsConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001261]
1262
jvred101512003-08-22 19:53:32 +00001263# Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order,
1264# in order for the font to compile back from xml.
1265
1266
jvr4756b3a2002-05-16 18:17:32 +00001267privateDictOperators = [
1268# opcode name argument type default converter
1269 (6, 'BlueValues', 'delta', None, None),
1270 (7, 'OtherBlues', 'delta', None, None),
1271 (8, 'FamilyBlues', 'delta', None, None),
1272 (9, 'FamilyOtherBlues', 'delta', None, None),
1273 ((12, 9), 'BlueScale', 'number', 0.039625, None),
1274 ((12, 10), 'BlueShift', 'number', 7, None),
1275 ((12, 11), 'BlueFuzz', 'number', 1, None),
1276 (10, 'StdHW', 'number', None, None),
1277 (11, 'StdVW', 'number', None, None),
1278 ((12, 12), 'StemSnapH', 'delta', None, None),
1279 ((12, 13), 'StemSnapV', 'delta', None, None),
1280 ((12, 14), 'ForceBold', 'number', 0, None),
jvrf2cf9c52002-05-23 21:50:36 +00001281 ((12, 15), 'ForceBoldThreshold', 'number', None, None), # deprecated
1282 ((12, 16), 'lenIV', 'number', None, None), # deprecated
jvr4756b3a2002-05-16 18:17:32 +00001283 ((12, 17), 'LanguageGroup', 'number', 0, None),
1284 ((12, 18), 'ExpansionFactor', 'number', 0.06, None),
1285 ((12, 19), 'initialRandomSeed', 'number', 0, None),
1286 (20, 'defaultWidthX', 'number', 0, None),
1287 (21, 'nominalWidthX', 'number', 0, None),
1288 (19, 'Subrs', 'number', None, SubrsConverter()),
1289]
1290
jvr4e5af602002-05-24 09:58:04 +00001291def addConverters(table):
1292 for i in range(len(table)):
1293 op, name, arg, default, conv = table[i]
1294 if conv is not None:
1295 continue
1296 if arg in ("delta", "array"):
1297 conv = ArrayConverter()
1298 elif arg == "number":
1299 conv = NumberConverter()
1300 elif arg == "SID":
Behdad Esfahbod024d1532013-11-28 07:10:53 -05001301 conv = ASCIIConverter()
jvr4e5af602002-05-24 09:58:04 +00001302 else:
1303 assert 0
1304 table[i] = op, name, arg, default, conv
1305
1306addConverters(privateDictOperators)
1307addConverters(topDictOperators)
1308
jvr4756b3a2002-05-16 18:17:32 +00001309
1310class TopDictDecompiler(psCharStrings.DictDecompiler):
1311 operators = buildOperatorDict(topDictOperators)
1312
1313
1314class PrivateDictDecompiler(psCharStrings.DictDecompiler):
1315 operators = buildOperatorDict(privateDictOperators)
1316
1317
Behdad Esfahbode388db52013-11-28 14:26:58 -05001318class DictCompiler(object):
jvrf2cf9c52002-05-23 21:50:36 +00001319
1320 def __init__(self, dictObj, strings, parent):
1321 assert isinstance(strings, IndexedStrings)
1322 self.dictObj = dictObj
1323 self.strings = strings
1324 self.parent = parent
1325 rawDict = {}
1326 for name in dictObj.order:
1327 value = getattr(dictObj, name, None)
1328 if value is None:
1329 continue
1330 conv = dictObj.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001331 value = conv.write(dictObj, value)
jvrf2cf9c52002-05-23 21:50:36 +00001332 if value == dictObj.defaults.get(name):
1333 continue
1334 rawDict[name] = value
1335 self.rawDict = rawDict
1336
jvr4e5af602002-05-24 09:58:04 +00001337 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +00001338 pass
1339
1340 def getDataLength(self):
jvr4e5af602002-05-24 09:58:04 +00001341 return len(self.compile("getDataLength"))
jvrf2cf9c52002-05-23 21:50:36 +00001342
jvr4e5af602002-05-24 09:58:04 +00001343 def compile(self, reason):
1344 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001345 print("-- compiling %s for %s" % (self.__class__.__name__, reason))
1346 print("in baseDict: ", self)
jvrf2cf9c52002-05-23 21:50:36 +00001347 rawDict = self.rawDict
1348 data = []
1349 for name in self.dictObj.order:
1350 value = rawDict.get(name)
1351 if value is None:
1352 continue
1353 op, argType = self.opcodes[name]
jvr2a9bcde2008-03-07 19:56:17 +00001354 if isinstance(argType, tuple):
jvrf2cf9c52002-05-23 21:50:36 +00001355 l = len(argType)
1356 assert len(value) == l, "value doesn't match arg type"
1357 for i in range(l):
jvred101512003-08-22 19:53:32 +00001358 arg = argType[i]
jvrf2cf9c52002-05-23 21:50:36 +00001359 v = value[i]
1360 arghandler = getattr(self, "arg_" + arg)
1361 data.append(arghandler(v))
1362 else:
1363 arghandler = getattr(self, "arg_" + argType)
1364 data.append(arghandler(value))
1365 data.append(op)
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001366 return bytesjoin(data)
jvrf2cf9c52002-05-23 21:50:36 +00001367
1368 def toFile(self, file):
jvr4e5af602002-05-24 09:58:04 +00001369 file.write(self.compile("toFile"))
jvrf2cf9c52002-05-23 21:50:36 +00001370
1371 def arg_number(self, num):
1372 return encodeNumber(num)
1373 def arg_SID(self, s):
1374 return psCharStrings.encodeIntCFF(self.strings.getSID(s))
1375 def arg_array(self, value):
1376 data = []
1377 for num in value:
1378 data.append(encodeNumber(num))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001379 return bytesjoin(data)
jvrf2cf9c52002-05-23 21:50:36 +00001380 def arg_delta(self, value):
1381 out = []
1382 last = 0
1383 for v in value:
1384 out.append(v - last)
1385 last = v
1386 data = []
1387 for num in out:
1388 data.append(encodeNumber(num))
Behdad Esfahbod18316aa2013-11-27 21:17:35 -05001389 return bytesjoin(data)
jvrf2cf9c52002-05-23 21:50:36 +00001390
1391
1392def encodeNumber(num):
jvrf6ff48b2008-03-07 19:49:25 +00001393 if isinstance(num, float):
jvrf2cf9c52002-05-23 21:50:36 +00001394 return psCharStrings.encodeFloat(num)
1395 else:
1396 return psCharStrings.encodeIntCFF(num)
1397
1398
1399class TopDictCompiler(DictCompiler):
1400
1401 opcodes = buildOpcodeDict(topDictOperators)
1402
1403 def getChildren(self, strings):
1404 children = []
jvred101512003-08-22 19:53:32 +00001405 if hasattr(self.dictObj, "charset") and self.dictObj.charset:
jvrf2cf9c52002-05-23 21:50:36 +00001406 children.append(CharsetCompiler(strings, self.dictObj.charset, self))
jvrb9702ba2003-01-03 20:56:01 +00001407 if hasattr(self.dictObj, "Encoding"):
1408 encoding = self.dictObj.Encoding
jvr2a9bcde2008-03-07 19:56:17 +00001409 if not isinstance(encoding, basestring):
jvrb9702ba2003-01-03 20:56:01 +00001410 children.append(EncodingCompiler(strings, encoding, self))
jvrce522412003-08-25 07:37:25 +00001411 if hasattr(self.dictObj, "FDSelect"):
jvred101512003-08-22 19:53:32 +00001412 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1413 # issues about merging the FDArrays. Here I assume that
1414 # either the font was read from XML, and teh FDSelect indices are all
1415 # in the charstring data, or the FDSelect array is already fully defined.
1416 fdSelect = self.dictObj.FDSelect
1417 if len(fdSelect) == 0: # probably read in from XML; assume fdIndex in CharString data
1418 charStrings = self.dictObj.CharStrings
1419 for name in self.dictObj.charset:
1420 charstring = charStrings[name]
1421 fdSelect.append(charStrings[name].fdSelectIndex)
1422 fdSelectComp = FDSelectCompiler(fdSelect, self)
1423 children.append(fdSelectComp)
jvrf2cf9c52002-05-23 21:50:36 +00001424 if hasattr(self.dictObj, "CharStrings"):
1425 items = []
1426 charStrings = self.dictObj.CharStrings
1427 for name in self.dictObj.charset:
1428 items.append(charStrings[name])
1429 charStringsComp = CharStringsCompiler(items, strings, self)
1430 children.append(charStringsComp)
jvrce522412003-08-25 07:37:25 +00001431 if hasattr(self.dictObj, "FDArray"):
jvred101512003-08-22 19:53:32 +00001432 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1433 # issues about merging the FDArrays. Here I assume that the FDArray info is correct
1434 # and complete.
1435 fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self)
1436 children.append(fdArrayIndexComp)
1437 children.extend(fdArrayIndexComp.getChildren(strings))
1438 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 FontDictCompiler(DictCompiler):
1446
1447 opcodes = buildOpcodeDict(topDictOperators)
1448
1449 def getChildren(self, strings):
1450 children = []
jvrf2cf9c52002-05-23 21:50:36 +00001451 if hasattr(self.dictObj, "Private"):
1452 privComp = self.dictObj.Private.getCompiler(strings, self)
1453 children.append(privComp)
1454 children.extend(privComp.getChildren(strings))
1455 return children
1456
1457
1458class PrivateDictCompiler(DictCompiler):
1459
1460 opcodes = buildOpcodeDict(privateDictOperators)
1461
jvr4e5af602002-05-24 09:58:04 +00001462 def setPos(self, pos, endPos):
1463 size = endPos - pos
jvrf2cf9c52002-05-23 21:50:36 +00001464 self.parent.rawDict["Private"] = size, pos
1465 self.pos = pos
1466
1467 def getChildren(self, strings):
1468 children = []
1469 if hasattr(self.dictObj, "Subrs"):
1470 children.append(self.dictObj.Subrs.getCompiler(strings, self))
1471 return children
1472
jvr4756b3a2002-05-16 18:17:32 +00001473
Behdad Esfahbode388db52013-11-28 14:26:58 -05001474class BaseDict(object):
jvr4756b3a2002-05-16 18:17:32 +00001475
jvr4e5af602002-05-24 09:58:04 +00001476 def __init__(self, strings=None, file=None, offset=None):
jvr4756b3a2002-05-16 18:17:32 +00001477 self.rawDict = {}
jvr767102e2002-05-17 07:06:32 +00001478 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001479 print("loading %s at %s" % (self.__class__.__name__, offset))
jvr4756b3a2002-05-16 18:17:32 +00001480 self.file = file
1481 self.offset = offset
1482 self.strings = strings
jvr155aa752002-05-17 19:58:49 +00001483 self.skipNames = []
jvr4756b3a2002-05-16 18:17:32 +00001484
1485 def decompile(self, data):
jvrf2cf9c52002-05-23 21:50:36 +00001486 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001487 print(" length %s is %s" % (self.__class__.__name__, len(data)))
jvrf2cf9c52002-05-23 21:50:36 +00001488 dec = self.decompilerClass(self.strings)
jvr4756b3a2002-05-16 18:17:32 +00001489 dec.decompile(data)
1490 self.rawDict = dec.getDict()
1491 self.postDecompile()
1492
1493 def postDecompile(self):
1494 pass
1495
jvrf2cf9c52002-05-23 21:50:36 +00001496 def getCompiler(self, strings, parent):
1497 return self.compilerClass(self, strings, parent)
1498
jvr4756b3a2002-05-16 18:17:32 +00001499 def __getattr__(self, name):
1500 value = self.rawDict.get(name)
1501 if value is None:
1502 value = self.defaults.get(name)
1503 if value is None:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -05001504 raise AttributeError(name)
jvr4756b3a2002-05-16 18:17:32 +00001505 conv = self.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001506 value = conv.read(self, value)
jvr4756b3a2002-05-16 18:17:32 +00001507 setattr(self, name, value)
1508 return value
1509
1510 def toXML(self, xmlWriter, progress):
1511 for name in self.order:
jvr155aa752002-05-17 19:58:49 +00001512 if name in self.skipNames:
1513 continue
jvr4756b3a2002-05-16 18:17:32 +00001514 value = getattr(self, name, None)
1515 if value is None:
1516 continue
jvr4e5af602002-05-24 09:58:04 +00001517 conv = self.converters[name]
jvr7ce0a132002-07-23 16:42:11 +00001518 conv.xmlWrite(xmlWriter, name, value, progress)
jvr4e5af602002-05-24 09:58:04 +00001519
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001520 def fromXML(self, name, attrs, content):
jvr4e5af602002-05-24 09:58:04 +00001521 conv = self.converters[name]
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001522 value = conv.xmlRead(name, attrs, content, self)
jvr4e5af602002-05-24 09:58:04 +00001523 setattr(self, name, value)
jvr4756b3a2002-05-16 18:17:32 +00001524
1525
1526class TopDict(BaseDict):
1527
1528 defaults = buildDefaults(topDictOperators)
1529 converters = buildConverters(topDictOperators)
1530 order = buildOrder(topDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001531 decompilerClass = TopDictDecompiler
1532 compilerClass = TopDictCompiler
Just7842e561999-12-16 21:34:53 +00001533
jvr4e5af602002-05-24 09:58:04 +00001534 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
jvr016ca762002-05-16 18:38:03 +00001535 BaseDict.__init__(self, strings, file, offset)
1536 self.GlobalSubrs = GlobalSubrs
1537
Just7842e561999-12-16 21:34:53 +00001538 def getGlyphOrder(self):
1539 return self.charset
1540
jvr4756b3a2002-05-16 18:17:32 +00001541 def postDecompile(self):
1542 offset = self.rawDict.get("CharStrings")
1543 if offset is None:
1544 return
1545 # get the number of glyphs beforehand.
1546 self.file.seek(offset)
jvra2ad5442002-05-17 18:36:07 +00001547 self.numGlyphs = readCard16(self.file)
Just7842e561999-12-16 21:34:53 +00001548
jvr016ca762002-05-16 18:38:03 +00001549 def toXML(self, xmlWriter, progress):
jvr155aa752002-05-17 19:58:49 +00001550 if hasattr(self, "CharStrings"):
jvr7ce0a132002-07-23 16:42:11 +00001551 self.decompileAllCharStrings(progress)
jvred101512003-08-22 19:53:32 +00001552 if hasattr(self, "ROS"):
1553 self.skipNames = ['Encoding']
jvr155aa752002-05-17 19:58:49 +00001554 if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
1555 # these values have default values, but I only want them to show up
1556 # in CID fonts.
1557 self.skipNames = ['CIDFontVersion', 'CIDFontRevision', 'CIDFontType',
1558 'CIDCount']
jvr016ca762002-05-16 18:38:03 +00001559 BaseDict.toXML(self, xmlWriter, progress)
jvr767102e2002-05-17 07:06:32 +00001560
jvr7ce0a132002-07-23 16:42:11 +00001561 def decompileAllCharStrings(self, progress):
jvr4e5af602002-05-24 09:58:04 +00001562 # XXX only when doing ttdump -i?
jvr7ce0a132002-07-23 16:42:11 +00001563 i = 0
jvra2ad5442002-05-17 18:36:07 +00001564 for charString in self.CharStrings.values():
jvred101512003-08-22 19:53:32 +00001565 try:
1566 charString.decompile()
1567 except:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001568 print("Error in charstring ", i)
jvred101512003-08-22 19:53:32 +00001569 import sys
1570 type, value = sys. exc_info()[0:2]
1571 raise type(value)
jvr7ce0a132002-07-23 16:42:11 +00001572 if not i % 30 and progress:
1573 progress.increment(0) # update
1574 i = i + 1
Just7842e561999-12-16 21:34:53 +00001575
1576
jvred101512003-08-22 19:53:32 +00001577class FontDict(BaseDict):
1578
1579 defaults = buildDefaults(topDictOperators)
1580 converters = buildConverters(topDictOperators)
1581 order = buildOrder(topDictOperators)
1582 decompilerClass = None
1583 compilerClass = FontDictCompiler
1584
1585 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
1586 BaseDict.__init__(self, strings, file, offset)
1587 self.GlobalSubrs = GlobalSubrs
1588
1589 def getGlyphOrder(self):
1590 return self.charset
1591
1592 def toXML(self, xmlWriter, progress):
1593 self.skipNames = ['Encoding']
1594 BaseDict.toXML(self, xmlWriter, progress)
1595
1596
1597
jvr4756b3a2002-05-16 18:17:32 +00001598class PrivateDict(BaseDict):
1599 defaults = buildDefaults(privateDictOperators)
1600 converters = buildConverters(privateDictOperators)
1601 order = buildOrder(privateDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001602 decompilerClass = PrivateDictDecompiler
1603 compilerClass = PrivateDictCompiler
Just7842e561999-12-16 21:34:53 +00001604
1605
Behdad Esfahbode388db52013-11-28 14:26:58 -05001606class IndexedStrings(object):
jvre3275582002-05-14 12:22:03 +00001607
jvr767102e2002-05-17 07:06:32 +00001608 """SID -> string mapping."""
1609
1610 def __init__(self, file=None):
1611 if file is None:
jvre3275582002-05-14 12:22:03 +00001612 strings = []
jvr767102e2002-05-17 07:06:32 +00001613 else:
Behdad Esfahbodd1ba7b52013-11-28 07:48:20 -05001614 strings = [tostr(s) for s in Index(file)]
jvre3275582002-05-14 12:22:03 +00001615 self.strings = strings
1616
jvrf2cf9c52002-05-23 21:50:36 +00001617 def getCompiler(self):
1618 return IndexedStringsCompiler(self, None, None)
1619
1620 def __len__(self):
1621 return len(self.strings)
1622
jvre3275582002-05-14 12:22:03 +00001623 def __getitem__(self, SID):
1624 if SID < cffStandardStringCount:
1625 return cffStandardStrings[SID]
1626 else:
1627 return self.strings[SID - cffStandardStringCount]
1628
1629 def getSID(self, s):
1630 if not hasattr(self, "stringMapping"):
1631 self.buildStringMapping()
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -05001632 if s in cffStandardStringMapping:
jvre3275582002-05-14 12:22:03 +00001633 SID = cffStandardStringMapping[s]
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -05001634 elif s in self.stringMapping:
jvre3275582002-05-14 12:22:03 +00001635 SID = self.stringMapping[s]
1636 else:
1637 SID = len(self.strings) + cffStandardStringCount
1638 self.strings.append(s)
1639 self.stringMapping[s] = SID
1640 return SID
1641
1642 def getStrings(self):
1643 return self.strings
1644
1645 def buildStringMapping(self):
1646 self.stringMapping = {}
1647 for index in range(len(self.strings)):
1648 self.stringMapping[self.strings[index]] = index + cffStandardStringCount
1649
1650
Just7842e561999-12-16 21:34:53 +00001651# The 391 Standard Strings as used in the CFF format.
1652# from Adobe Technical None #5176, version 1.0, 18 March 1998
1653
1654cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
1655 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
1656 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
1657 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
1658 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C',
1659 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
1660 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
1661 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
1662 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
1663 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
1664 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
1665 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
1666 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
1667 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
1668 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
1669 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
1670 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
1671 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
1672 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
1673 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
1674 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
1675 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
1676 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
1677 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
1678 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
1679 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
1680 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
1681 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
1682 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
1683 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
1684 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
1685 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
1686 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
1687 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
1688 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
1689 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
1690 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
1691 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
1692 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
1693 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
1694 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
1695 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
1696 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
1697 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
1698 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
1699 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
1700 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
1701 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
1702 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
1703 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
1704 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
1705 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
1706 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
1707 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
1708 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
1709 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
1710 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
1711 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
1712 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
1713 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
1714 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
1715 '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
1716 'Semibold'
1717]
1718
1719cffStandardStringCount = 391
1720assert len(cffStandardStrings) == cffStandardStringCount
1721# build reverse mapping
1722cffStandardStringMapping = {}
1723for _i in range(cffStandardStringCount):
1724 cffStandardStringMapping[cffStandardStrings[_i]] = _i
jvrc60a44f2006-10-21 13:41:18 +00001725
1726cffISOAdobeStrings = [".notdef", "space", "exclam", "quotedbl", "numbersign",
1727"dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright",
1728"asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two",
1729"three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon",
1730"less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G",
1731"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
1732"X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum",
1733"underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
1734"k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
1735"braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent",
1736"sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle",
1737"quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl",
1738"endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet",
1739"quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis",
1740"perthousand", "questiondown", "grave", "acute", "circumflex", "tilde",
1741"macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
1742"ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE",
1743"ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls",
1744"onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus",
1745"Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn",
1746"threequarters", "twosuperior", "registered", "minus", "eth", "multiply",
1747"threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
1748"Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave",
1749"Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute",
1750"Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute",
1751"Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute",
1752"acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute",
1753"ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis",
1754"igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde",
1755"scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis",
1756"zcaron"]
1757
1758cffISOAdobeStringCount = 229
1759assert len(cffISOAdobeStrings) == cffISOAdobeStringCount
1760
1761cffIExpertStrings = [".notdef", "space", "exclamsmall", "Hungarumlautsmall",
1762"dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall",
1763"parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader",
1764"comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle",
1765"twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle",
1766"sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon",
1767"commasuperior", "threequartersemdash", "periodsuperior", "questionsmall",
1768"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1769"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1770"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1771"parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
1772"Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall",
1773"Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
1774"Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall",
1775"Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall",
1776"exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall",
1777"Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall",
1778"figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall",
1779"onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth",
1780"threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds",
1781"zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior",
1782"fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior",
1783"zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior",
1784"fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior",
1785"centinferior", "dollarinferior", "periodinferior", "commainferior",
1786"Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall",
1787"Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall",
1788"Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall",
1789"Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall",
1790"Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall",
1791"Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall",
1792"Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall",
1793"Ydieresissmall"]
1794
1795cffExpertStringCount = 166
1796assert len(cffIExpertStrings) == cffExpertStringCount
1797
1798cffExpertSubsetStrings = [".notdef", "space", "dollaroldstyle",
1799"dollarsuperior", "parenleftsuperior", "parenrightsuperior", "twodotenleader",
1800"onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle",
1801"oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle",
1802"sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon",
1803"semicolon", "commasuperior", "threequartersemdash", "periodsuperior",
1804"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1805"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1806"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1807"parenrightinferior", "hyphensuperior", "colonmonetary", "onefitted", "rupiah",
1808"centoldstyle", "figuredash", "hypheninferior", "onequarter", "onehalf",
1809"threequarters", "oneeighth", "threeeighths", "fiveeighths", "seveneighths",
1810"onethird", "twothirds", "zerosuperior", "onesuperior", "twosuperior",
1811"threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior",
1812"eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior",
1813"threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior",
1814"eightinferior", "nineinferior", "centinferior", "dollarinferior",
1815"periodinferior", "commainferior"]
1816
1817cffExpertSubsetStringCount = 87
1818assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount