blob: fe4c376012d83f4d22e5eb7c31f2c1aafcb95799 [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#
jvr4e5af602002-05-24 09:58:04 +00004# $Id: cffLib.py,v 1.22 2002-05-24 09:58:03 jvr Exp $
Justec46d161999-12-20 22:02:10 +00005#
Just7842e561999-12-16 21:34:53 +00006
7import struct, sstruct
8import string
jvr4e5af602002-05-24 09:58:04 +00009from types import FloatType, ListType, StringType, TupleType
Just528614e2000-01-16 22:14:02 +000010from fontTools.misc import psCharStrings
jvr4e5af602002-05-24 09:58:04 +000011from fontTools.misc.textTools import safeEval
Just7842e561999-12-16 21:34:53 +000012
13
jvr767102e2002-05-17 07:06:32 +000014DEBUG = 0
15
16
Just7842e561999-12-16 21:34:53 +000017cffHeaderFormat = """
18 major: B
19 minor: B
20 hdrSize: B
21 offSize: B
22"""
23
24class CFFFontSet:
25
26 def __init__(self):
jvr4756b3a2002-05-16 18:17:32 +000027 pass
Just7842e561999-12-16 21:34:53 +000028
jvr4e5af602002-05-24 09:58:04 +000029 def decompile(self, file, otFont):
jvra2a75b32002-05-13 11:25:17 +000030 sstruct.unpack(cffHeaderFormat, file.read(4), self)
Just7842e561999-12-16 21:34:53 +000031 assert self.major == 1 and self.minor == 0, \
32 "unknown CFF format: %d.%d" % (self.major, self.minor)
Just7842e561999-12-16 21:34:53 +000033
jvrf2cf9c52002-05-23 21:50:36 +000034 file.seek(self.hdrSize)
jvr4e5af602002-05-24 09:58:04 +000035 self.fontNames = list(Index(file))
jvr4756b3a2002-05-16 18:17:32 +000036 self.topDictIndex = TopDictIndex(file)
jvr767102e2002-05-17 07:06:32 +000037 self.strings = IndexedStrings(file)
jvr4e5af602002-05-24 09:58:04 +000038 self.GlobalSubrs = GlobalSubrsIndex(file)
jvr4756b3a2002-05-16 18:17:32 +000039 self.topDictIndex.strings = self.strings
jvr016ca762002-05-16 18:38:03 +000040 self.topDictIndex.GlobalSubrs = self.GlobalSubrs
jvr4756b3a2002-05-16 18:17:32 +000041
42 def __len__(self):
43 return len(self.fontNames)
44
45 def keys(self):
46 return self.fontNames[:]
47
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:
55 raise KeyError, name
jvr016ca762002-05-16 18:38:03 +000056 return self.topDictIndex[index]
Just7842e561999-12-16 21:34:53 +000057
jvr4e5af602002-05-24 09:58:04 +000058 def compile(self, file, otFont):
Just7842e561999-12-16 21:34:53 +000059 strings = IndexedStrings()
jvrf2cf9c52002-05-23 21:50:36 +000060 writer = CFFWriter()
61 writer.add(sstruct.pack(cffHeaderFormat, self))
62 fontNames = Index()
63 for name in self.fontNames:
64 fontNames.append(name)
65 writer.add(fontNames.getCompiler(strings, None))
66 topCompiler = self.topDictIndex.getCompiler(strings, None)
67 writer.add(topCompiler)
68 writer.add(strings.getCompiler())
69 writer.add(self.GlobalSubrs.getCompiler(strings, None))
70
jvr4e5af602002-05-24 09:58:04 +000071 for topDict in self.topDictIndex:
72 if not hasattr(topDict, "charset") or topDict.charset is None:
73 charset = otFont.getGlyphOrder()
74 topDict.charset = charset
75
jvrf2cf9c52002-05-23 21:50:36 +000076 for child in topCompiler.getChildren(strings):
77 writer.add(child)
78
jvrf2cf9c52002-05-23 21:50:36 +000079 writer.toFile(file)
Just7842e561999-12-16 21:34:53 +000080
81 def toXML(self, xmlWriter, progress=None):
82 xmlWriter.newline()
83 for fontName in self.fontNames:
84 xmlWriter.begintag("CFFFont", name=fontName)
85 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +000086 font = self[fontName]
Just7842e561999-12-16 21:34:53 +000087 font.toXML(xmlWriter, progress)
88 xmlWriter.endtag("CFFFont")
89 xmlWriter.newline()
90 xmlWriter.newline()
91 xmlWriter.begintag("GlobalSubrs")
92 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +000093 self.GlobalSubrs.toXML(xmlWriter, progress)
Just7842e561999-12-16 21:34:53 +000094 xmlWriter.endtag("GlobalSubrs")
95 xmlWriter.newline()
96 xmlWriter.newline()
97
98 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:
115 if isinstance(element, StringType):
116 continue
117 topDict.fromXML(element)
118 elif name == "GlobalSubrs":
119 for element in content:
120 if isinstance(element, StringType):
121 continue
122 name, attrs, content = element
123 subr = psCharStrings.T2CharString(None, subrs=None, globalSubrs=None)
124 subr.fromXML((name, attrs, content))
125 self.GlobalSubrs.append(subr)
Just7842e561999-12-16 21:34:53 +0000126
127
jvrf2cf9c52002-05-23 21:50:36 +0000128class CFFWriter:
129
130 def __init__(self):
131 self.data = []
132
133 def add(self, table):
134 self.data.append(table)
135
136 def toFile(self, file):
137 lastPosList = None
138 count = 1
139 while 1:
jvr4e5af602002-05-24 09:58:04 +0000140 if DEBUG:
141 print "CFFWriter.toFile() iteration:", count
142 count = count + 1
jvrf2cf9c52002-05-23 21:50:36 +0000143 pos = 0
144 posList = [pos]
145 for item in self.data:
jvrf2cf9c52002-05-23 21:50:36 +0000146 if hasattr(item, "getDataLength"):
jvr4e5af602002-05-24 09:58:04 +0000147 endPos = pos + item.getDataLength()
jvrf2cf9c52002-05-23 21:50:36 +0000148 else:
jvr4e5af602002-05-24 09:58:04 +0000149 endPos = pos + len(item)
150 if hasattr(item, "setPos"):
151 item.setPos(pos, endPos)
152 pos = endPos
jvrf2cf9c52002-05-23 21:50:36 +0000153 posList.append(pos)
154 if posList == lastPosList:
155 break
156 lastPosList = posList
jvr4e5af602002-05-24 09:58:04 +0000157 if DEBUG:
158 print "CFFWriter.toFile() writing to file."
jvrf2cf9c52002-05-23 21:50:36 +0000159 begin = file.tell()
160 posList = [0]
161 for item in self.data:
162 if hasattr(item, "toFile"):
163 item.toFile(file)
164 else:
165 file.write(item)
166 posList.append(file.tell() - begin)
jvrf2cf9c52002-05-23 21:50:36 +0000167 assert posList == lastPosList
168
169
170def calcOffSize(largestOffset):
171 if largestOffset < 0x100:
172 offSize = 1
173 elif largestOffset < 0x10000:
174 offSize = 2
175 elif largestOffset < 0x1000000:
176 offSize = 3
177 else:
178 offSize = 4
179 return offSize
180
181
182class IndexCompiler:
183
184 def __init__(self, items, strings, parent):
185 self.items = self.getItems(items, strings)
186 self.parent = parent
187
188 def getItems(self, items, strings):
189 return items
190
191 def getOffsets(self):
192 pos = 1
193 offsets = [pos]
194 for item in self.items:
195 if hasattr(item, "getDataLength"):
196 pos = pos + item.getDataLength()
197 else:
198 pos = pos + len(item)
199 offsets.append(pos)
200 return offsets
201
202 def getDataLength(self):
203 lastOffset = self.getOffsets()[-1]
204 offSize = calcOffSize(lastOffset)
205 dataLength = (
206 2 + # count
207 1 + # offSize
208 (len(self.items) + 1) * offSize + # the offsets
209 lastOffset - 1 # size of object data
210 )
211 return dataLength
212
213 def toFile(self, file):
jvrf2cf9c52002-05-23 21:50:36 +0000214 offsets = self.getOffsets()
215 writeCard16(file, len(self.items))
216 offSize = calcOffSize(offsets[-1])
217 writeCard8(file, offSize)
218 offSize = -offSize
219 pack = struct.pack
220 for offset in offsets:
221 binOffset = pack(">l", offset)[offSize:]
222 assert len(binOffset) == -offSize
223 file.write(binOffset)
224 for item in self.items:
225 if hasattr(item, "toFile"):
226 item.toFile(file)
227 else:
228 file.write(item)
jvrf2cf9c52002-05-23 21:50:36 +0000229
230
231class IndexedStringsCompiler(IndexCompiler):
232
233 def getItems(self, items, strings):
234 return items.strings
235
236
237class TopDictIndexCompiler(IndexCompiler):
238
239 def getItems(self, items, strings):
240 out = []
241 for item in items:
242 out.append(item.getCompiler(strings, self))
243 return out
244
245 def getChildren(self, strings):
246 children = []
247 for topDict in self.items:
248 children.extend(topDict.getChildren(strings))
249 return children
250
251
252class GlobalSubrsCompiler(IndexCompiler):
253 def getItems(self, items, strings):
254 out = []
255 for cs in items:
256 cs.compile()
257 out.append(cs.bytecode)
258 return out
259
260class SubrsCompiler(GlobalSubrsCompiler):
jvr4e5af602002-05-24 09:58:04 +0000261 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000262 offset = pos - self.parent.pos
263 self.parent.rawDict["Subrs"] = offset
264
265class CharStringsCompiler(GlobalSubrsCompiler):
jvr4e5af602002-05-24 09:58:04 +0000266 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000267 self.parent.rawDict["CharStrings"] = pos
268
269
jvr4756b3a2002-05-16 18:17:32 +0000270class Index:
Just7842e561999-12-16 21:34:53 +0000271
jvr4756b3a2002-05-16 18:17:32 +0000272 """This class represents what the CFF spec calls an INDEX."""
Just7842e561999-12-16 21:34:53 +0000273
jvrf2cf9c52002-05-23 21:50:36 +0000274 compilerClass = IndexCompiler
275
jvr4e5af602002-05-24 09:58:04 +0000276 def __init__(self, file=None):
277 name = self.__class__.__name__
jvrf2cf9c52002-05-23 21:50:36 +0000278 if file is None:
279 self.items = []
280 return
jvr767102e2002-05-17 07:06:32 +0000281 if DEBUG:
282 print "loading %s at %s" % (name, file.tell())
jvr4756b3a2002-05-16 18:17:32 +0000283 self.file = file
jvra2ad5442002-05-17 18:36:07 +0000284 count = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000285 self.count = count
286 self.items = [None] * count
287 if count == 0:
jvrf2cf9c52002-05-23 21:50:36 +0000288 self.items = []
jvr4756b3a2002-05-16 18:17:32 +0000289 return
jvra2ad5442002-05-17 18:36:07 +0000290 offSize = readCard8(file)
jvr767102e2002-05-17 07:06:32 +0000291 if DEBUG:
jvrf2cf9c52002-05-23 21:50:36 +0000292 print " index count: %s offSize: %s" % (count, offSize)
jvr767102e2002-05-17 07:06:32 +0000293 assert offSize <= 4, "offSize too large: %s" % offSize
jvr4756b3a2002-05-16 18:17:32 +0000294 self.offsets = offsets = []
295 pad = '\0' * (4 - offSize)
296 for index in range(count+1):
297 chunk = file.read(offSize)
298 chunk = pad + chunk
299 offset, = struct.unpack(">L", chunk)
300 offsets.append(int(offset))
301 self.offsetBase = file.tell() - 1
302 file.seek(self.offsetBase + offsets[-1]) # pretend we've read the whole lot
jvrf2cf9c52002-05-23 21:50:36 +0000303 if DEBUG:
304 print " end of %s at %s" % (name, file.tell())
Just7842e561999-12-16 21:34:53 +0000305
jvr4756b3a2002-05-16 18:17:32 +0000306 def __len__(self):
jvrf2cf9c52002-05-23 21:50:36 +0000307 return len(self.items)
jvra2a75b32002-05-13 11:25:17 +0000308
jvr4756b3a2002-05-16 18:17:32 +0000309 def __getitem__(self, index):
310 item = self.items[index]
311 if item is not None:
312 return item
313 offset = self.offsets[index] + self.offsetBase
314 size = self.offsets[index+1] - self.offsets[index]
315 file = self.file
316 file.seek(offset)
317 data = file.read(size)
318 assert len(data) == size
jvra2ad5442002-05-17 18:36:07 +0000319 item = self.produceItem(index, data, file, offset, size)
jvr4756b3a2002-05-16 18:17:32 +0000320 self.items[index] = item
321 return item
322
jvra2ad5442002-05-17 18:36:07 +0000323 def produceItem(self, index, data, file, offset, size):
jvr4756b3a2002-05-16 18:17:32 +0000324 return data
jvr4756b3a2002-05-16 18:17:32 +0000325
jvrf2cf9c52002-05-23 21:50:36 +0000326 def append(self, item):
327 self.items.append(item)
328
329 def getCompiler(self, strings, parent):
330 return self.compilerClass(self, strings, parent)
331
332
333class GlobalSubrsIndex(Index):
334
335 compilerClass = GlobalSubrsCompiler
336
jvr4e5af602002-05-24 09:58:04 +0000337 def __init__(self, file=None, globalSubrs=None, private=None, fdSelect=None, fdArray=None):
338 Index.__init__(self, file)
jvra2ad5442002-05-17 18:36:07 +0000339 self.globalSubrs = globalSubrs
340 self.private = private
341 self.fdSelect = fdSelect
342 self.fdArray = fdArray
343
344 def produceItem(self, index, data, file, offset, size):
345 if self.private is not None:
346 private = self.private
347 elif self.fdArray is not None:
348 private = self.fdArray[self.fdSelect[index]].Private
349 else:
350 private = None
351 if hasattr(private, "Subrs"):
352 subrs = private.Subrs
353 else:
354 subrs = []
355 return psCharStrings.T2CharString(data, subrs=subrs, globalSubrs=self.globalSubrs)
jvr4756b3a2002-05-16 18:17:32 +0000356
357 def toXML(self, xmlWriter, progress):
jvra2ad5442002-05-17 18:36:07 +0000358 fdSelect = self.fdSelect
jvr4e5af602002-05-24 09:58:04 +0000359 xmlWriter.comment("The 'index' attribute is only for humans; "
360 "it is ignored when parsed.")
361 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +0000362 for i in range(len(self)):
363 xmlWriter.begintag("CharString", index=i)
364 xmlWriter.newline()
365 self[i].toXML(xmlWriter)
366 xmlWriter.endtag("CharString")
367 xmlWriter.newline()
jvra2ad5442002-05-17 18:36:07 +0000368
jvr4e5af602002-05-24 09:58:04 +0000369 def fromXML(self, (name, attrs, content)):
370 if name <> "CharString":
371 return
372 subr = psCharStrings.T2CharString(None, subrs=None, globalSubrs=None)
373 subr.fromXML((name, attrs, content))
374 self.append(subr)
375
jvra2ad5442002-05-17 18:36:07 +0000376 def getItemAndSelector(self, index):
377 fdSelect = self.fdSelect
378 if fdSelect is None:
379 sel = None
380 else:
381 sel = fdSelect[index]
382 return self[index], sel
jvrf2cf9c52002-05-23 21:50:36 +0000383
384class SubrsIndex(GlobalSubrsIndex):
385 compilerClass = SubrsCompiler
386
jvr4756b3a2002-05-16 18:17:32 +0000387
jvr767102e2002-05-17 07:06:32 +0000388class TopDictIndex(Index):
jvra2ad5442002-05-17 18:36:07 +0000389
jvrf2cf9c52002-05-23 21:50:36 +0000390 compilerClass = TopDictIndexCompiler
391
jvra2ad5442002-05-17 18:36:07 +0000392 def produceItem(self, index, data, file, offset, size):
jvr767102e2002-05-17 07:06:32 +0000393 top = TopDict(self.strings, file, offset, self.GlobalSubrs)
394 top.decompile(data)
395 return top
jvra2ad5442002-05-17 18:36:07 +0000396
397 def toXML(self, xmlWriter, progress):
398 for i in range(len(self)):
399 xmlWriter.begintag("FontDict", index=i)
400 xmlWriter.newline()
401 self[i].toXML(xmlWriter, progress)
402 xmlWriter.endtag("FontDict")
403 xmlWriter.newline()
jvr767102e2002-05-17 07:06:32 +0000404
405
jvr4756b3a2002-05-16 18:17:32 +0000406class CharStrings:
407
jvra2ad5442002-05-17 18:36:07 +0000408 def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray):
jvr4e5af602002-05-24 09:58:04 +0000409 if file is not None:
410 self.charStringsIndex = SubrsIndex(file, globalSubrs, private, fdSelect, fdArray)
411 self.charStrings = charStrings = {}
412 for i in range(len(charset)):
413 charStrings[charset[i]] = i
414 self.charStringsAreIndexed = 1
415 else:
416 self.charStrings = {}
417 self.charStringsAreIndexed = 0
418 self.globalSubrs = globalSubrs
419 self.private = private
420 self.fdSelect = fdSelect
421 self.fdArray = fdArray
jvr4756b3a2002-05-16 18:17:32 +0000422
423 def keys(self):
jvr4e5af602002-05-24 09:58:04 +0000424 return self.charStrings.keys()
jvr4756b3a2002-05-16 18:17:32 +0000425
jvr016ca762002-05-16 18:38:03 +0000426 def values(self):
jvr4e5af602002-05-24 09:58:04 +0000427 if self.charStringsAreIndexed:
428 return self.charStringsIndex
429 else:
430 return self.charStrings.values()
jvr016ca762002-05-16 18:38:03 +0000431
jvr4756b3a2002-05-16 18:17:32 +0000432 def has_key(self, name):
jvr4e5af602002-05-24 09:58:04 +0000433 return self.charStrings.has_key(name)
jvr4756b3a2002-05-16 18:17:32 +0000434
jvr767102e2002-05-17 07:06:32 +0000435 def __len__(self):
jvr4e5af602002-05-24 09:58:04 +0000436 return len(self.charStrings)
jvr767102e2002-05-17 07:06:32 +0000437
jvr4756b3a2002-05-16 18:17:32 +0000438 def __getitem__(self, name):
jvr4e5af602002-05-24 09:58:04 +0000439 charString = self.charStrings[name]
440 if self.charStringsAreIndexed:
441 charString = self.charStringsIndex[charString]
442 return charString
443
444 def __setitem__(self, name, charString):
445 if self.charStringsAreIndexed:
446 index = self.charStrings[name]
447 self.charStringsIndex[index] = charString
448 else:
449 self.charStrings[name] = charString
jvr4756b3a2002-05-16 18:17:32 +0000450
jvra2ad5442002-05-17 18:36:07 +0000451 def getItemAndSelector(self, name):
jvr4e5af602002-05-24 09:58:04 +0000452 if self.charStringsAreIndexed:
453 index = self.charStrings[name]
454 return self.charStringsIndex.getItemAndSelector(index)
455 else:
456 # XXX needs work for CID fonts
457 return self.charStrings[name], None
jvra2ad5442002-05-17 18:36:07 +0000458
jvr4756b3a2002-05-16 18:17:32 +0000459 def toXML(self, xmlWriter, progress):
460 names = self.keys()
461 names.sort()
462 for name in names:
jvra2ad5442002-05-17 18:36:07 +0000463 charStr, fdSelect = self.getItemAndSelector(name)
464 if fdSelect is None:
465 xmlWriter.begintag("CharString", name=name)
466 else:
467 xmlWriter.begintag("CharString",
468 [('name', name), ('fdSelect', fdSelect)])
jvr4756b3a2002-05-16 18:17:32 +0000469 xmlWriter.newline()
470 self[name].toXML(xmlWriter)
471 xmlWriter.endtag("CharString")
472 xmlWriter.newline()
jvr4e5af602002-05-24 09:58:04 +0000473
474 def fromXML(self, (name, attrs, content)):
475 for element in content:
476 if isinstance(element, StringType):
477 continue
478 name, attrs, content = element
479 if name <> "CharString":
480 continue
481 glyphName = attrs["name"]
482 if hasattr(self.private, "Subrs"):
483 subrs = self.private.Subrs
484 else:
485 subrs = []
486 globalSubrs = self.globalSubrs
487 charString = psCharStrings.T2CharString(None, subrs=subrs, globalSubrs=globalSubrs)
488 charString.fromXML((name, attrs, content))
489 self[glyphName] = charString
jvr4756b3a2002-05-16 18:17:32 +0000490
491
jvra2ad5442002-05-17 18:36:07 +0000492def readCard8(file):
493 return ord(file.read(1))
494
495def readCard16(file):
496 value, = struct.unpack(">H", file.read(2))
497 return value
498
jvrf2cf9c52002-05-23 21:50:36 +0000499def writeCard8(file, value):
500 file.write(chr(value))
501
502def writeCard16(file, value):
503 file.write(struct.pack(">H", value))
504
505def packCard8(value):
506 return chr(value)
507
508def packCard16(value):
509 return struct.pack(">H", value)
510
jvr4756b3a2002-05-16 18:17:32 +0000511def buildOperatorDict(table):
512 d = {}
513 for op, name, arg, default, conv in table:
514 d[op] = (name, arg)
515 return d
516
jvrf2cf9c52002-05-23 21:50:36 +0000517def buildOpcodeDict(table):
518 d = {}
519 for op, name, arg, default, conv in table:
520 if type(op) == TupleType:
521 op = chr(op[0]) + chr(op[1])
522 else:
523 op = chr(op)
524 d[name] = (op, arg)
525 return d
526
jvr4756b3a2002-05-16 18:17:32 +0000527def buildOrder(table):
528 l = []
529 for op, name, arg, default, conv in table:
530 l.append(name)
531 return l
532
533def buildDefaults(table):
534 d = {}
535 for op, name, arg, default, conv in table:
536 if default is not None:
537 d[name] = default
538 return d
539
540def buildConverters(table):
541 d = {}
542 for op, name, arg, default, conv in table:
543 d[name] = conv
544 return d
545
546
jvr4e5af602002-05-24 09:58:04 +0000547class SimpleConverter:
jvr7ce02ea2002-05-17 20:04:05 +0000548 def read(self, parent, value):
549 return value
jvrf2cf9c52002-05-23 21:50:36 +0000550 def write(self, parent, value):
551 return value
jvra2ad5442002-05-17 18:36:07 +0000552 def xmlWrite(self, xmlWriter, name, value):
jvr4e5af602002-05-24 09:58:04 +0000553 xmlWriter.simpletag(name, value=value)
554 xmlWriter.newline()
555 def xmlRead(self, (name, attrs, content), parent):
556 return attrs["value"]
557
558def parseNum(s):
559 try:
560 value = int(s)
561 except:
562 value = float(s)
563 return value
564
565class NumberConverter(SimpleConverter):
566 def xmlRead(self, (name, attrs, content), parent):
567 return parseNum(attrs["value"])
568
569class ArrayConverter(SimpleConverter):
570 def xmlWrite(self, xmlWriter, name, value):
571 value = map(str, value)
572 xmlWriter.simpletag(name, value=" ".join(value))
573 xmlWriter.newline()
574 def xmlRead(self, (name, attrs, content), parent):
575 values = attrs["value"].split()
576 return map(parseNum, values)
577
578class TableConverter(SimpleConverter):
579 def xmlWrite(self, xmlWriter, name, value):
jvra2ad5442002-05-17 18:36:07 +0000580 xmlWriter.begintag(name)
581 xmlWriter.newline()
582 value.toXML(xmlWriter, None)
583 xmlWriter.endtag(name)
584 xmlWriter.newline()
jvr4e5af602002-05-24 09:58:04 +0000585 def xmlRead(self, (name, attrs, content), parent):
586 ob = self.getClass()()
587 for element in content:
588 if isinstance(element, StringType):
589 continue
590 ob.fromXML(element)
591 return ob
jvra2ad5442002-05-17 18:36:07 +0000592
jvr4e5af602002-05-24 09:58:04 +0000593class PrivateDictConverter(TableConverter):
594 def getClass(self):
595 return PrivateDict
jvr4756b3a2002-05-16 18:17:32 +0000596 def read(self, parent, value):
597 size, offset = value
598 file = parent.file
jvr4e5af602002-05-24 09:58:04 +0000599 priv = PrivateDict(parent.strings, file, offset)
jvr4756b3a2002-05-16 18:17:32 +0000600 file.seek(offset)
601 data = file.read(size)
602 len(data) == size
jvr4e5af602002-05-24 09:58:04 +0000603 priv.decompile(data)
604 return priv
jvrf2cf9c52002-05-23 21:50:36 +0000605 def write(self, parent, value):
606 return (0, 0) # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000607
jvr4e5af602002-05-24 09:58:04 +0000608class SubrsConverter(TableConverter):
609 def getClass(self):
610 return SubrsIndex
jvr4756b3a2002-05-16 18:17:32 +0000611 def read(self, parent, value):
612 file = parent.file
613 file.seek(parent.offset + value) # Offset(self)
jvr4e5af602002-05-24 09:58:04 +0000614 return SubrsIndex(file)
jvrf2cf9c52002-05-23 21:50:36 +0000615 def write(self, parent, value):
616 return 0 # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000617
jvr4e5af602002-05-24 09:58:04 +0000618class CharStringsConverter(TableConverter):
jvr4756b3a2002-05-16 18:17:32 +0000619 def read(self, parent, value):
620 file = parent.file
jvr767102e2002-05-17 07:06:32 +0000621 charset = parent.charset
jvra2ad5442002-05-17 18:36:07 +0000622 globalSubrs = parent.GlobalSubrs
623 if hasattr(parent, "ROS"):
624 fdSelect, fdArray = parent.FDSelect, parent.FDArray
625 private = None
626 else:
627 fdSelect, fdArray = None, None
628 private = parent.Private
jvr4756b3a2002-05-16 18:17:32 +0000629 file.seek(value) # Offset(0)
jvra2ad5442002-05-17 18:36:07 +0000630 return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
jvrf2cf9c52002-05-23 21:50:36 +0000631 def write(self, parent, value):
632 return 0 # dummy value
jvr4e5af602002-05-24 09:58:04 +0000633 def xmlRead(self, (name, attrs, content), parent):
634 # XXX needs work for CID fonts
635 fdSelect, fdArray = None, None
636 charStrings = CharStrings(None, None, parent.GlobalSubrs, parent.Private, fdSelect, fdArray)
637 charStrings.fromXML((name, attrs, content))
638 return charStrings
jvr4756b3a2002-05-16 18:17:32 +0000639
640class CharsetConverter:
641 def read(self, parent, value):
642 isCID = hasattr(parent, "ROS")
643 if value > 2:
644 numGlyphs = parent.numGlyphs
645 file = parent.file
646 file.seek(value)
jvrf2cf9c52002-05-23 21:50:36 +0000647 if DEBUG:
648 print "loading charset at %s" % value
jvra2ad5442002-05-17 18:36:07 +0000649 format = readCard8(file)
Just7842e561999-12-16 21:34:53 +0000650 if format == 0:
jvrf2cf9c52002-05-23 21:50:36 +0000651 charset =parseCharset0(numGlyphs, file, parent.strings)
jvr4756b3a2002-05-16 18:17:32 +0000652 elif format == 1 or format == 2:
653 charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
Just7842e561999-12-16 21:34:53 +0000654 else:
jvr1890b952002-05-15 07:41:30 +0000655 raise NotImplementedError
jvr4756b3a2002-05-16 18:17:32 +0000656 assert len(charset) == numGlyphs
jvrf2cf9c52002-05-23 21:50:36 +0000657 if DEBUG:
658 print " charset end at %s" % file.tell()
jvr1890b952002-05-15 07:41:30 +0000659 else:
jvra2ad5442002-05-17 18:36:07 +0000660 if isCID or not hasattr(parent, "CharStrings"):
jvr4756b3a2002-05-16 18:17:32 +0000661 assert value == 0
662 charset = None
663 elif value == 0:
664 charset = ISOAdobe
665 elif value == 1:
666 charset = Expert
667 elif value == 2:
668 charset = ExpertSubset
jvr1890b952002-05-15 07:41:30 +0000669 # self.charset:
670 # 0: ISOAdobe (or CID font!)
671 # 1: Expert
672 # 2: ExpertSubset
jvr4756b3a2002-05-16 18:17:32 +0000673 charset = None #
674 return charset
jvrf2cf9c52002-05-23 21:50:36 +0000675 def write(self, parent, value):
676 return 0 # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000677 def xmlWrite(self, xmlWriter, name, value):
jvr4e5af602002-05-24 09:58:04 +0000678 # XXX only write charset when not in OT/TTX context, where we
679 # dump charset as a separate "GlyphOrder" table.
680 ##xmlWriter.simpletag("charset")
681 xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
jvr4756b3a2002-05-16 18:17:32 +0000682 xmlWriter.newline()
jvr4e5af602002-05-24 09:58:04 +0000683 def xmlRead(self, (name, attrs, content), parent):
684 if 0:
685 return safeEval(attrs["value"])
jvr4756b3a2002-05-16 18:17:32 +0000686
687
jvrf2cf9c52002-05-23 21:50:36 +0000688class CharsetCompiler:
689
690 def __init__(self, strings, charset, parent):
691 assert charset[0] == '.notdef'
692 format = 0 # XXX!
693 data = [packCard8(format)]
694 for name in charset[1:]:
695 data.append(packCard16(strings.getSID(name)))
696 self.data = "".join(data)
697 self.parent = parent
698
jvr4e5af602002-05-24 09:58:04 +0000699 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000700 self.parent.rawDict["charset"] = pos
701
702 def getDataLength(self):
703 return len(self.data)
704
705 def toFile(self, file):
706 file.write(self.data)
707
708
709def parseCharset0(numGlyphs, file, strings):
710 charset = [".notdef"]
711 for i in range(numGlyphs - 1):
712 SID = readCard16(file)
713 charset.append(strings[SID])
714 return charset
715
jvr4756b3a2002-05-16 18:17:32 +0000716def parseCharset(numGlyphs, file, strings, isCID, format):
717 charset = ['.notdef']
718 count = 1
719 if format == 1:
jvra2ad5442002-05-17 18:36:07 +0000720 nLeftFunc = readCard8
jvr4756b3a2002-05-16 18:17:32 +0000721 else:
jvra2ad5442002-05-17 18:36:07 +0000722 nLeftFunc = readCard16
jvr4756b3a2002-05-16 18:17:32 +0000723 while count < numGlyphs:
jvra2ad5442002-05-17 18:36:07 +0000724 first = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000725 nLeft = nLeftFunc(file)
726 if isCID:
727 for CID in range(first, first+nLeft+1):
728 charset.append(CID)
jvr1890b952002-05-15 07:41:30 +0000729 else:
jvr4756b3a2002-05-16 18:17:32 +0000730 for SID in range(first, first+nLeft+1):
731 charset.append(strings[SID])
732 count = count + nLeft + 1
733 return charset
734
735
jvr4e5af602002-05-24 09:58:04 +0000736class FDArrayConverter(TableConverter):
jvra2ad5442002-05-17 18:36:07 +0000737 def read(self, parent, value):
738 file = parent.file
739 file.seek(value)
740 fdArray = TopDictIndex(file)
741 fdArray.strings = parent.strings
742 fdArray.GlobalSubrs = parent.GlobalSubrs
743 return fdArray
744
745
746class FDSelectConverter:
747 def read(self, parent, value):
748 file = parent.file
749 file.seek(value)
750 format = readCard8(file)
751 numGlyphs = parent.numGlyphs
752 if format == 0:
753 from array import array
754 fdSelect = array("B", file.read(numGlyphs)).tolist()
755 elif format == 3:
756 fdSelect = [None] * numGlyphs
757 nRanges = readCard16(file)
758 prev = None
759 for i in range(nRanges):
760 first = readCard16(file)
761 if prev is not None:
762 for glyphID in range(prev, first):
763 fdSelect[glyphID] = fd
764 prev = first
765 fd = readCard8(file)
766 if prev is not None:
767 first = readCard16(file)
768 for glyphID in range(prev, first):
769 fdSelect[glyphID] = fd
770 else:
771 assert 0, "unsupported FDSelect format: %s" % format
772 return fdSelect
773 def xmlWrite(self, xmlWriter, name, value):
774 pass
775
776
jvr4e5af602002-05-24 09:58:04 +0000777class ROSConverter(SimpleConverter):
jvr155aa752002-05-17 19:58:49 +0000778 def xmlWrite(self, xmlWriter, name, value):
779 registry, order, supplement = value
jvrf2cf9c52002-05-23 21:50:36 +0000780 xmlWriter.simpletag(name, [('Registry', registry), ('Order', order),
781 ('Supplement', supplement)])
jvr4afb2572002-05-18 20:07:01 +0000782 xmlWriter.newline()
jvr155aa752002-05-17 19:58:49 +0000783
784
jvr4756b3a2002-05-16 18:17:32 +0000785topDictOperators = [
786# opcode name argument type default converter
jvr155aa752002-05-17 19:58:49 +0000787 ((12, 30), 'ROS', ('SID','SID','number'), None, ROSConverter()),
jvrf2cf9c52002-05-23 21:50:36 +0000788 ((12, 20), 'SyntheticBase', 'number', None, None),
jvr4756b3a2002-05-16 18:17:32 +0000789 (0, 'version', 'SID', None, None),
790 (1, 'Notice', 'SID', None, None),
791 ((12, 0), 'Copyright', 'SID', None, None),
792 (2, 'FullName', 'SID', None, None),
jvr155aa752002-05-17 19:58:49 +0000793 ((12, 38), 'FontName', 'SID', None, None),
jvr4756b3a2002-05-16 18:17:32 +0000794 (3, 'FamilyName', 'SID', None, None),
795 (4, 'Weight', 'SID', None, None),
796 ((12, 1), 'isFixedPitch', 'number', 0, None),
797 ((12, 2), 'ItalicAngle', 'number', 0, None),
798 ((12, 3), 'UnderlinePosition', 'number', None, None),
799 ((12, 4), 'UnderlineThickness', 'number', 50, None),
800 ((12, 5), 'PaintType', 'number', 0, None),
801 ((12, 6), 'CharstringType', 'number', 2, None),
802 ((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0], None),
803 (13, 'UniqueID', 'number', None, None),
804 (5, 'FontBBox', 'array', [0,0,0,0], None),
805 ((12, 8), 'StrokeWidth', 'number', 0, None),
806 (14, 'XUID', 'array', None, None),
807 (15, 'charset', 'number', 0, CharsetConverter()),
jvr4756b3a2002-05-16 18:17:32 +0000808 ((12, 21), 'PostScript', 'SID', None, None),
809 ((12, 22), 'BaseFontName', 'SID', None, None),
810 ((12, 23), 'BaseFontBlend', 'delta', None, None),
jvr4756b3a2002-05-16 18:17:32 +0000811 ((12, 31), 'CIDFontVersion', 'number', 0, None),
812 ((12, 32), 'CIDFontRevision', 'number', 0, None),
813 ((12, 33), 'CIDFontType', 'number', 0, None),
814 ((12, 34), 'CIDCount', 'number', 8720, None),
815 ((12, 35), 'UIDBase', 'number', None, None),
jvr155aa752002-05-17 19:58:49 +0000816 (16, 'Encoding', 'number', 0, None), # XXX
jvra2ad5442002-05-17 18:36:07 +0000817 ((12, 36), 'FDArray', 'number', None, FDArrayConverter()),
818 ((12, 37), 'FDSelect', 'number', None, FDSelectConverter()),
jvr155aa752002-05-17 19:58:49 +0000819 (18, 'Private', ('number','number'), None, PrivateDictConverter()),
820 (17, 'CharStrings', 'number', None, CharStringsConverter()),
jvr4756b3a2002-05-16 18:17:32 +0000821]
822
823privateDictOperators = [
824# opcode name argument type default converter
825 (6, 'BlueValues', 'delta', None, None),
826 (7, 'OtherBlues', 'delta', None, None),
827 (8, 'FamilyBlues', 'delta', None, None),
828 (9, 'FamilyOtherBlues', 'delta', None, None),
829 ((12, 9), 'BlueScale', 'number', 0.039625, None),
830 ((12, 10), 'BlueShift', 'number', 7, None),
831 ((12, 11), 'BlueFuzz', 'number', 1, None),
832 (10, 'StdHW', 'number', None, None),
833 (11, 'StdVW', 'number', None, None),
834 ((12, 12), 'StemSnapH', 'delta', None, None),
835 ((12, 13), 'StemSnapV', 'delta', None, None),
836 ((12, 14), 'ForceBold', 'number', 0, None),
jvrf2cf9c52002-05-23 21:50:36 +0000837 ((12, 15), 'ForceBoldThreshold', 'number', None, None), # deprecated
838 ((12, 16), 'lenIV', 'number', None, None), # deprecated
jvr4756b3a2002-05-16 18:17:32 +0000839 ((12, 17), 'LanguageGroup', 'number', 0, None),
840 ((12, 18), 'ExpansionFactor', 'number', 0.06, None),
841 ((12, 19), 'initialRandomSeed', 'number', 0, None),
842 (20, 'defaultWidthX', 'number', 0, None),
843 (21, 'nominalWidthX', 'number', 0, None),
844 (19, 'Subrs', 'number', None, SubrsConverter()),
845]
846
jvr4e5af602002-05-24 09:58:04 +0000847def addConverters(table):
848 for i in range(len(table)):
849 op, name, arg, default, conv = table[i]
850 if conv is not None:
851 continue
852 if arg in ("delta", "array"):
853 conv = ArrayConverter()
854 elif arg == "number":
855 conv = NumberConverter()
856 elif arg == "SID":
857 conv = SimpleConverter()
858 else:
859 assert 0
860 table[i] = op, name, arg, default, conv
861
862addConverters(privateDictOperators)
863addConverters(topDictOperators)
864
jvr4756b3a2002-05-16 18:17:32 +0000865
866class TopDictDecompiler(psCharStrings.DictDecompiler):
867 operators = buildOperatorDict(topDictOperators)
868
869
870class PrivateDictDecompiler(psCharStrings.DictDecompiler):
871 operators = buildOperatorDict(privateDictOperators)
872
873
jvrf2cf9c52002-05-23 21:50:36 +0000874class DictCompiler:
875
876 def __init__(self, dictObj, strings, parent):
877 assert isinstance(strings, IndexedStrings)
878 self.dictObj = dictObj
879 self.strings = strings
880 self.parent = parent
881 rawDict = {}
882 for name in dictObj.order:
883 value = getattr(dictObj, name, None)
884 if value is None:
885 continue
886 conv = dictObj.converters[name]
jvr4e5af602002-05-24 09:58:04 +0000887 value = conv.write(dictObj, value)
jvrf2cf9c52002-05-23 21:50:36 +0000888 if value == dictObj.defaults.get(name):
889 continue
890 rawDict[name] = value
891 self.rawDict = rawDict
892
jvr4e5af602002-05-24 09:58:04 +0000893 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000894 pass
895
896 def getDataLength(self):
jvr4e5af602002-05-24 09:58:04 +0000897 return len(self.compile("getDataLength"))
jvrf2cf9c52002-05-23 21:50:36 +0000898
jvr4e5af602002-05-24 09:58:04 +0000899 def compile(self, reason):
900 if DEBUG:
901 print "-- compiling %s for %s" % (self.__class__.__name__, reason)
jvrf2cf9c52002-05-23 21:50:36 +0000902 rawDict = self.rawDict
903 data = []
904 for name in self.dictObj.order:
905 value = rawDict.get(name)
906 if value is None:
907 continue
908 op, argType = self.opcodes[name]
909 if type(argType) == TupleType:
910 l = len(argType)
911 assert len(value) == l, "value doesn't match arg type"
912 for i in range(l):
913 arg = argType[l - i - 1]
914 v = value[i]
915 arghandler = getattr(self, "arg_" + arg)
916 data.append(arghandler(v))
917 else:
918 arghandler = getattr(self, "arg_" + argType)
919 data.append(arghandler(value))
920 data.append(op)
921 return "".join(data)
922
923 def toFile(self, file):
jvr4e5af602002-05-24 09:58:04 +0000924 file.write(self.compile("toFile"))
jvrf2cf9c52002-05-23 21:50:36 +0000925
926 def arg_number(self, num):
927 return encodeNumber(num)
928 def arg_SID(self, s):
929 return psCharStrings.encodeIntCFF(self.strings.getSID(s))
930 def arg_array(self, value):
931 data = []
932 for num in value:
933 data.append(encodeNumber(num))
934 return "".join(data)
935 def arg_delta(self, value):
936 out = []
937 last = 0
938 for v in value:
939 out.append(v - last)
940 last = v
941 data = []
942 for num in out:
943 data.append(encodeNumber(num))
944 return "".join(data)
945
946
947def encodeNumber(num):
948 if type(num) == FloatType:
949 return psCharStrings.encodeFloat(num)
950 else:
951 return psCharStrings.encodeIntCFF(num)
952
953
954class TopDictCompiler(DictCompiler):
955
956 opcodes = buildOpcodeDict(topDictOperators)
957
958 def getChildren(self, strings):
959 children = []
960 if hasattr(self.dictObj, "charset"):
961 children.append(CharsetCompiler(strings, self.dictObj.charset, self))
962 if hasattr(self.dictObj, "CharStrings"):
963 items = []
964 charStrings = self.dictObj.CharStrings
965 for name in self.dictObj.charset:
966 items.append(charStrings[name])
967 charStringsComp = CharStringsCompiler(items, strings, self)
968 children.append(charStringsComp)
969 if hasattr(self.dictObj, "Private"):
970 privComp = self.dictObj.Private.getCompiler(strings, self)
971 children.append(privComp)
972 children.extend(privComp.getChildren(strings))
973 return children
974
975
976class PrivateDictCompiler(DictCompiler):
977
978 opcodes = buildOpcodeDict(privateDictOperators)
979
jvr4e5af602002-05-24 09:58:04 +0000980 def setPos(self, pos, endPos):
981 size = endPos - pos
jvrf2cf9c52002-05-23 21:50:36 +0000982 self.parent.rawDict["Private"] = size, pos
983 self.pos = pos
984
985 def getChildren(self, strings):
986 children = []
987 if hasattr(self.dictObj, "Subrs"):
988 children.append(self.dictObj.Subrs.getCompiler(strings, self))
989 return children
990
jvr4756b3a2002-05-16 18:17:32 +0000991
992class BaseDict:
993
jvr4e5af602002-05-24 09:58:04 +0000994 def __init__(self, strings=None, file=None, offset=None):
jvr4756b3a2002-05-16 18:17:32 +0000995 self.rawDict = {}
jvr767102e2002-05-17 07:06:32 +0000996 if DEBUG:
jvrf2cf9c52002-05-23 21:50:36 +0000997 print "loading %s at %s" % (self.__class__.__name__, offset)
jvr4756b3a2002-05-16 18:17:32 +0000998 self.file = file
999 self.offset = offset
1000 self.strings = strings
jvr155aa752002-05-17 19:58:49 +00001001 self.skipNames = []
jvr4756b3a2002-05-16 18:17:32 +00001002
1003 def decompile(self, data):
jvrf2cf9c52002-05-23 21:50:36 +00001004 if DEBUG:
1005 print " length %s is %s" % (self.__class__.__name__, len(data))
1006 dec = self.decompilerClass(self.strings)
jvr4756b3a2002-05-16 18:17:32 +00001007 dec.decompile(data)
1008 self.rawDict = dec.getDict()
1009 self.postDecompile()
1010
1011 def postDecompile(self):
1012 pass
1013
jvrf2cf9c52002-05-23 21:50:36 +00001014 def getCompiler(self, strings, parent):
1015 return self.compilerClass(self, strings, parent)
1016
jvr4756b3a2002-05-16 18:17:32 +00001017 def __getattr__(self, name):
1018 value = self.rawDict.get(name)
1019 if value is None:
1020 value = self.defaults.get(name)
1021 if value is None:
1022 raise AttributeError, name
1023 conv = self.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001024 value = conv.read(self, value)
jvr4756b3a2002-05-16 18:17:32 +00001025 setattr(self, name, value)
1026 return value
1027
1028 def toXML(self, xmlWriter, progress):
1029 for name in self.order:
jvr155aa752002-05-17 19:58:49 +00001030 if name in self.skipNames:
1031 continue
jvr4756b3a2002-05-16 18:17:32 +00001032 value = getattr(self, name, None)
1033 if value is None:
1034 continue
jvr4e5af602002-05-24 09:58:04 +00001035 conv = self.converters[name]
1036 conv.xmlWrite(xmlWriter, name, value)
1037
1038 def fromXML(self, (name, attrs, content)):
1039 conv = self.converters[name]
1040 value = conv.xmlRead((name, attrs, content), self)
1041 setattr(self, name, value)
jvr4756b3a2002-05-16 18:17:32 +00001042
1043
1044class TopDict(BaseDict):
1045
1046 defaults = buildDefaults(topDictOperators)
1047 converters = buildConverters(topDictOperators)
1048 order = buildOrder(topDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001049 decompilerClass = TopDictDecompiler
1050 compilerClass = TopDictCompiler
Just7842e561999-12-16 21:34:53 +00001051
jvr4e5af602002-05-24 09:58:04 +00001052 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
jvr016ca762002-05-16 18:38:03 +00001053 BaseDict.__init__(self, strings, file, offset)
1054 self.GlobalSubrs = GlobalSubrs
1055
Just7842e561999-12-16 21:34:53 +00001056 def getGlyphOrder(self):
1057 return self.charset
1058
jvr4756b3a2002-05-16 18:17:32 +00001059 def postDecompile(self):
1060 offset = self.rawDict.get("CharStrings")
1061 if offset is None:
1062 return
1063 # get the number of glyphs beforehand.
1064 self.file.seek(offset)
jvra2ad5442002-05-17 18:36:07 +00001065 self.numGlyphs = readCard16(self.file)
Just7842e561999-12-16 21:34:53 +00001066
jvr016ca762002-05-16 18:38:03 +00001067 def toXML(self, xmlWriter, progress):
jvr155aa752002-05-17 19:58:49 +00001068 if hasattr(self, "CharStrings"):
1069 self.decompileAllCharStrings()
1070 if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
1071 # these values have default values, but I only want them to show up
1072 # in CID fonts.
1073 self.skipNames = ['CIDFontVersion', 'CIDFontRevision', 'CIDFontType',
1074 'CIDCount']
jvr016ca762002-05-16 18:38:03 +00001075 BaseDict.toXML(self, xmlWriter, progress)
jvr767102e2002-05-17 07:06:32 +00001076
Just7842e561999-12-16 21:34:53 +00001077 def decompileAllCharStrings(self):
jvr4e5af602002-05-24 09:58:04 +00001078 # XXX only when doing ttdump -i?
jvra2ad5442002-05-17 18:36:07 +00001079 for charString in self.CharStrings.values():
1080 charString.decompile()
Just7842e561999-12-16 21:34:53 +00001081
1082
jvr4756b3a2002-05-16 18:17:32 +00001083class PrivateDict(BaseDict):
1084 defaults = buildDefaults(privateDictOperators)
1085 converters = buildConverters(privateDictOperators)
1086 order = buildOrder(privateDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001087 decompilerClass = PrivateDictDecompiler
1088 compilerClass = PrivateDictCompiler
Just7842e561999-12-16 21:34:53 +00001089
1090
jvre3275582002-05-14 12:22:03 +00001091class IndexedStrings:
1092
jvr767102e2002-05-17 07:06:32 +00001093 """SID -> string mapping."""
1094
1095 def __init__(self, file=None):
1096 if file is None:
jvre3275582002-05-14 12:22:03 +00001097 strings = []
jvr767102e2002-05-17 07:06:32 +00001098 else:
jvr4e5af602002-05-24 09:58:04 +00001099 strings = list(Index(file))
jvre3275582002-05-14 12:22:03 +00001100 self.strings = strings
1101
jvrf2cf9c52002-05-23 21:50:36 +00001102 def getCompiler(self):
1103 return IndexedStringsCompiler(self, None, None)
1104
1105 def __len__(self):
1106 return len(self.strings)
1107
jvre3275582002-05-14 12:22:03 +00001108 def __getitem__(self, SID):
1109 if SID < cffStandardStringCount:
1110 return cffStandardStrings[SID]
1111 else:
1112 return self.strings[SID - cffStandardStringCount]
1113
1114 def getSID(self, s):
1115 if not hasattr(self, "stringMapping"):
1116 self.buildStringMapping()
1117 if cffStandardStringMapping.has_key(s):
1118 SID = cffStandardStringMapping[s]
jvrf2cf9c52002-05-23 21:50:36 +00001119 elif self.stringMapping.has_key(s):
jvre3275582002-05-14 12:22:03 +00001120 SID = self.stringMapping[s]
1121 else:
1122 SID = len(self.strings) + cffStandardStringCount
1123 self.strings.append(s)
1124 self.stringMapping[s] = SID
1125 return SID
1126
1127 def getStrings(self):
1128 return self.strings
1129
1130 def buildStringMapping(self):
1131 self.stringMapping = {}
1132 for index in range(len(self.strings)):
1133 self.stringMapping[self.strings[index]] = index + cffStandardStringCount
1134
1135
Just7842e561999-12-16 21:34:53 +00001136# The 391 Standard Strings as used in the CFF format.
1137# from Adobe Technical None #5176, version 1.0, 18 March 1998
1138
1139cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
1140 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
1141 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
1142 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
1143 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C',
1144 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
1145 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
1146 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
1147 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
1148 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
1149 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
1150 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
1151 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
1152 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
1153 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
1154 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
1155 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
1156 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
1157 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
1158 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
1159 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
1160 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
1161 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
1162 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
1163 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
1164 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
1165 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
1166 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
1167 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
1168 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
1169 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
1170 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
1171 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
1172 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
1173 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
1174 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
1175 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
1176 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
1177 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
1178 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
1179 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
1180 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
1181 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
1182 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
1183 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
1184 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
1185 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
1186 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
1187 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
1188 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
1189 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
1190 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
1191 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
1192 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
1193 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
1194 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
1195 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
1196 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
1197 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
1198 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
1199 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
1200 '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
1201 'Semibold'
1202]
1203
1204cffStandardStringCount = 391
1205assert len(cffStandardStrings) == cffStandardStringCount
1206# build reverse mapping
1207cffStandardStringMapping = {}
1208for _i in range(cffStandardStringCount):
1209 cffStandardStringMapping[cffStandardStrings[_i]] = _i
1210