blob: 2d84494717ff713839ce14611b9b3e40c09e0623 [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#
jvre2ca9b52002-09-09 14:18:39 +00004# $Id: cffLib.py,v 1.27 2002-09-09 14:18:39 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)):
jvrb58176e2002-05-24 11:55:37 +0000363 subr = self[i]
364 if subr.needsDecompilation():
365 xmlWriter.begintag("CharString", index=i, raw=1)
366 else:
367 xmlWriter.begintag("CharString", index=i)
jvr4756b3a2002-05-16 18:17:32 +0000368 xmlWriter.newline()
jvrb58176e2002-05-24 11:55:37 +0000369 subr.toXML(xmlWriter)
jvr4756b3a2002-05-16 18:17:32 +0000370 xmlWriter.endtag("CharString")
371 xmlWriter.newline()
jvra2ad5442002-05-17 18:36:07 +0000372
jvr4e5af602002-05-24 09:58:04 +0000373 def fromXML(self, (name, attrs, content)):
374 if name <> "CharString":
375 return
376 subr = psCharStrings.T2CharString(None, subrs=None, globalSubrs=None)
377 subr.fromXML((name, attrs, content))
378 self.append(subr)
379
jvra2ad5442002-05-17 18:36:07 +0000380 def getItemAndSelector(self, index):
381 fdSelect = self.fdSelect
382 if fdSelect is None:
383 sel = None
384 else:
385 sel = fdSelect[index]
386 return self[index], sel
jvrf2cf9c52002-05-23 21:50:36 +0000387
jvre2ca9b52002-09-09 14:18:39 +0000388
jvrf2cf9c52002-05-23 21:50:36 +0000389class SubrsIndex(GlobalSubrsIndex):
390 compilerClass = SubrsCompiler
391
jvr4756b3a2002-05-16 18:17:32 +0000392
jvr767102e2002-05-17 07:06:32 +0000393class TopDictIndex(Index):
jvra2ad5442002-05-17 18:36:07 +0000394
jvrf2cf9c52002-05-23 21:50:36 +0000395 compilerClass = TopDictIndexCompiler
396
jvra2ad5442002-05-17 18:36:07 +0000397 def produceItem(self, index, data, file, offset, size):
jvr767102e2002-05-17 07:06:32 +0000398 top = TopDict(self.strings, file, offset, self.GlobalSubrs)
399 top.decompile(data)
400 return top
jvra2ad5442002-05-17 18:36:07 +0000401
402 def toXML(self, xmlWriter, progress):
403 for i in range(len(self)):
404 xmlWriter.begintag("FontDict", index=i)
405 xmlWriter.newline()
406 self[i].toXML(xmlWriter, progress)
407 xmlWriter.endtag("FontDict")
408 xmlWriter.newline()
jvr767102e2002-05-17 07:06:32 +0000409
410
jvr4756b3a2002-05-16 18:17:32 +0000411class CharStrings:
412
jvra2ad5442002-05-17 18:36:07 +0000413 def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray):
jvr4e5af602002-05-24 09:58:04 +0000414 if file is not None:
415 self.charStringsIndex = SubrsIndex(file, globalSubrs, private, fdSelect, fdArray)
416 self.charStrings = charStrings = {}
417 for i in range(len(charset)):
418 charStrings[charset[i]] = i
419 self.charStringsAreIndexed = 1
420 else:
421 self.charStrings = {}
422 self.charStringsAreIndexed = 0
423 self.globalSubrs = globalSubrs
424 self.private = private
425 self.fdSelect = fdSelect
426 self.fdArray = fdArray
jvr4756b3a2002-05-16 18:17:32 +0000427
428 def keys(self):
jvr4e5af602002-05-24 09:58:04 +0000429 return self.charStrings.keys()
jvr4756b3a2002-05-16 18:17:32 +0000430
jvr016ca762002-05-16 18:38:03 +0000431 def values(self):
jvr4e5af602002-05-24 09:58:04 +0000432 if self.charStringsAreIndexed:
433 return self.charStringsIndex
434 else:
435 return self.charStrings.values()
jvr016ca762002-05-16 18:38:03 +0000436
jvr4756b3a2002-05-16 18:17:32 +0000437 def has_key(self, name):
jvr4e5af602002-05-24 09:58:04 +0000438 return self.charStrings.has_key(name)
jvr4756b3a2002-05-16 18:17:32 +0000439
jvr767102e2002-05-17 07:06:32 +0000440 def __len__(self):
jvr4e5af602002-05-24 09:58:04 +0000441 return len(self.charStrings)
jvr767102e2002-05-17 07:06:32 +0000442
jvr4756b3a2002-05-16 18:17:32 +0000443 def __getitem__(self, name):
jvr4e5af602002-05-24 09:58:04 +0000444 charString = self.charStrings[name]
445 if self.charStringsAreIndexed:
446 charString = self.charStringsIndex[charString]
447 return charString
448
449 def __setitem__(self, name, charString):
450 if self.charStringsAreIndexed:
451 index = self.charStrings[name]
452 self.charStringsIndex[index] = charString
453 else:
454 self.charStrings[name] = charString
jvr4756b3a2002-05-16 18:17:32 +0000455
jvra2ad5442002-05-17 18:36:07 +0000456 def getItemAndSelector(self, name):
jvr4e5af602002-05-24 09:58:04 +0000457 if self.charStringsAreIndexed:
458 index = self.charStrings[name]
459 return self.charStringsIndex.getItemAndSelector(index)
460 else:
461 # XXX needs work for CID fonts
462 return self.charStrings[name], None
jvra2ad5442002-05-17 18:36:07 +0000463
jvr4756b3a2002-05-16 18:17:32 +0000464 def toXML(self, xmlWriter, progress):
465 names = self.keys()
466 names.sort()
jvr7ce0a132002-07-23 16:42:11 +0000467 i = 0
468 step = 10
469 numGlyphs = len(names)
jvr4756b3a2002-05-16 18:17:32 +0000470 for name in names:
jvra2ad5442002-05-17 18:36:07 +0000471 charStr, fdSelect = self.getItemAndSelector(name)
jvrb58176e2002-05-24 11:55:37 +0000472 if charStr.needsDecompilation():
473 raw = [("raw", 1)]
474 else:
475 raw = []
jvra2ad5442002-05-17 18:36:07 +0000476 if fdSelect is None:
jvrb58176e2002-05-24 11:55:37 +0000477 xmlWriter.begintag("CharString", [('name', name)] + raw)
jvra2ad5442002-05-17 18:36:07 +0000478 else:
479 xmlWriter.begintag("CharString",
jvrb58176e2002-05-24 11:55:37 +0000480 [('name', name), ('fdSelect', fdSelect)] + raw)
jvr4756b3a2002-05-16 18:17:32 +0000481 xmlWriter.newline()
jvrb58176e2002-05-24 11:55:37 +0000482 charStr.toXML(xmlWriter)
jvr4756b3a2002-05-16 18:17:32 +0000483 xmlWriter.endtag("CharString")
484 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000485 if not i % step and progress is not None:
486 progress.setLabel("Dumping 'CFF ' table... (%s)" % name)
487 progress.increment(step / float(numGlyphs))
488 i = i + 1
jvr4e5af602002-05-24 09:58:04 +0000489
490 def fromXML(self, (name, attrs, content)):
491 for element in content:
492 if isinstance(element, StringType):
493 continue
494 name, attrs, content = element
495 if name <> "CharString":
496 continue
497 glyphName = attrs["name"]
498 if hasattr(self.private, "Subrs"):
499 subrs = self.private.Subrs
500 else:
501 subrs = []
502 globalSubrs = self.globalSubrs
503 charString = psCharStrings.T2CharString(None, subrs=subrs, globalSubrs=globalSubrs)
504 charString.fromXML((name, attrs, content))
505 self[glyphName] = charString
jvr4756b3a2002-05-16 18:17:32 +0000506
507
jvra2ad5442002-05-17 18:36:07 +0000508def readCard8(file):
509 return ord(file.read(1))
510
511def readCard16(file):
512 value, = struct.unpack(">H", file.read(2))
513 return value
514
jvrf2cf9c52002-05-23 21:50:36 +0000515def writeCard8(file, value):
516 file.write(chr(value))
517
518def writeCard16(file, value):
519 file.write(struct.pack(">H", value))
520
521def packCard8(value):
522 return chr(value)
523
524def packCard16(value):
525 return struct.pack(">H", value)
526
jvr4756b3a2002-05-16 18:17:32 +0000527def buildOperatorDict(table):
528 d = {}
529 for op, name, arg, default, conv in table:
530 d[op] = (name, arg)
531 return d
532
jvrf2cf9c52002-05-23 21:50:36 +0000533def buildOpcodeDict(table):
534 d = {}
535 for op, name, arg, default, conv in table:
536 if type(op) == TupleType:
537 op = chr(op[0]) + chr(op[1])
538 else:
539 op = chr(op)
540 d[name] = (op, arg)
541 return d
542
jvr4756b3a2002-05-16 18:17:32 +0000543def buildOrder(table):
544 l = []
545 for op, name, arg, default, conv in table:
546 l.append(name)
547 return l
548
549def buildDefaults(table):
550 d = {}
551 for op, name, arg, default, conv in table:
552 if default is not None:
553 d[name] = default
554 return d
555
556def buildConverters(table):
557 d = {}
558 for op, name, arg, default, conv in table:
559 d[name] = conv
560 return d
561
562
jvr4e5af602002-05-24 09:58:04 +0000563class SimpleConverter:
jvr7ce02ea2002-05-17 20:04:05 +0000564 def read(self, parent, value):
565 return value
jvrf2cf9c52002-05-23 21:50:36 +0000566 def write(self, parent, value):
567 return value
jvr7ce0a132002-07-23 16:42:11 +0000568 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000569 xmlWriter.simpletag(name, value=value)
570 xmlWriter.newline()
571 def xmlRead(self, (name, attrs, content), parent):
572 return attrs["value"]
573
jvre2ca9b52002-09-09 14:18:39 +0000574class Latin1Converter(SimpleConverter):
575 def xmlRead(self, (name, attrs, content), parent):
576 s = unicode(attrs["value"], "utf-8")
577 return s.encode("latin-1")
578
579
jvr4e5af602002-05-24 09:58:04 +0000580def parseNum(s):
581 try:
582 value = int(s)
583 except:
584 value = float(s)
585 return value
586
587class NumberConverter(SimpleConverter):
588 def xmlRead(self, (name, attrs, content), parent):
589 return parseNum(attrs["value"])
590
591class ArrayConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000592 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000593 value = map(str, value)
594 xmlWriter.simpletag(name, value=" ".join(value))
595 xmlWriter.newline()
596 def xmlRead(self, (name, attrs, content), parent):
597 values = attrs["value"].split()
598 return map(parseNum, values)
599
600class TableConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000601 def xmlWrite(self, xmlWriter, name, value, progress):
jvra2ad5442002-05-17 18:36:07 +0000602 xmlWriter.begintag(name)
603 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000604 value.toXML(xmlWriter, progress)
jvra2ad5442002-05-17 18:36:07 +0000605 xmlWriter.endtag(name)
606 xmlWriter.newline()
jvr4e5af602002-05-24 09:58:04 +0000607 def xmlRead(self, (name, attrs, content), parent):
608 ob = self.getClass()()
609 for element in content:
610 if isinstance(element, StringType):
611 continue
612 ob.fromXML(element)
613 return ob
jvra2ad5442002-05-17 18:36:07 +0000614
jvr4e5af602002-05-24 09:58:04 +0000615class PrivateDictConverter(TableConverter):
616 def getClass(self):
617 return PrivateDict
jvr4756b3a2002-05-16 18:17:32 +0000618 def read(self, parent, value):
619 size, offset = value
620 file = parent.file
jvr4e5af602002-05-24 09:58:04 +0000621 priv = PrivateDict(parent.strings, file, offset)
jvr4756b3a2002-05-16 18:17:32 +0000622 file.seek(offset)
623 data = file.read(size)
624 len(data) == size
jvr4e5af602002-05-24 09:58:04 +0000625 priv.decompile(data)
626 return priv
jvrf2cf9c52002-05-23 21:50:36 +0000627 def write(self, parent, value):
628 return (0, 0) # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000629
jvr4e5af602002-05-24 09:58:04 +0000630class SubrsConverter(TableConverter):
631 def getClass(self):
632 return SubrsIndex
jvr4756b3a2002-05-16 18:17:32 +0000633 def read(self, parent, value):
634 file = parent.file
635 file.seek(parent.offset + value) # Offset(self)
jvr4e5af602002-05-24 09:58:04 +0000636 return SubrsIndex(file)
jvrf2cf9c52002-05-23 21:50:36 +0000637 def write(self, parent, value):
638 return 0 # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000639
jvr4e5af602002-05-24 09:58:04 +0000640class CharStringsConverter(TableConverter):
jvr4756b3a2002-05-16 18:17:32 +0000641 def read(self, parent, value):
642 file = parent.file
jvr767102e2002-05-17 07:06:32 +0000643 charset = parent.charset
jvra2ad5442002-05-17 18:36:07 +0000644 globalSubrs = parent.GlobalSubrs
645 if hasattr(parent, "ROS"):
646 fdSelect, fdArray = parent.FDSelect, parent.FDArray
647 private = None
648 else:
649 fdSelect, fdArray = None, None
650 private = parent.Private
jvr4756b3a2002-05-16 18:17:32 +0000651 file.seek(value) # Offset(0)
jvra2ad5442002-05-17 18:36:07 +0000652 return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
jvrf2cf9c52002-05-23 21:50:36 +0000653 def write(self, parent, value):
654 return 0 # dummy value
jvr4e5af602002-05-24 09:58:04 +0000655 def xmlRead(self, (name, attrs, content), parent):
656 # XXX needs work for CID fonts
657 fdSelect, fdArray = None, None
658 charStrings = CharStrings(None, None, parent.GlobalSubrs, parent.Private, fdSelect, fdArray)
659 charStrings.fromXML((name, attrs, content))
660 return charStrings
jvr4756b3a2002-05-16 18:17:32 +0000661
662class CharsetConverter:
663 def read(self, parent, value):
664 isCID = hasattr(parent, "ROS")
665 if value > 2:
666 numGlyphs = parent.numGlyphs
667 file = parent.file
668 file.seek(value)
jvrf2cf9c52002-05-23 21:50:36 +0000669 if DEBUG:
670 print "loading charset at %s" % value
jvra2ad5442002-05-17 18:36:07 +0000671 format = readCard8(file)
Just7842e561999-12-16 21:34:53 +0000672 if format == 0:
jvrf2cf9c52002-05-23 21:50:36 +0000673 charset =parseCharset0(numGlyphs, file, parent.strings)
jvr4756b3a2002-05-16 18:17:32 +0000674 elif format == 1 or format == 2:
675 charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
Just7842e561999-12-16 21:34:53 +0000676 else:
jvr1890b952002-05-15 07:41:30 +0000677 raise NotImplementedError
jvr4756b3a2002-05-16 18:17:32 +0000678 assert len(charset) == numGlyphs
jvrf2cf9c52002-05-23 21:50:36 +0000679 if DEBUG:
680 print " charset end at %s" % file.tell()
jvr1890b952002-05-15 07:41:30 +0000681 else:
jvra2ad5442002-05-17 18:36:07 +0000682 if isCID or not hasattr(parent, "CharStrings"):
jvr4756b3a2002-05-16 18:17:32 +0000683 assert value == 0
684 charset = None
685 elif value == 0:
686 charset = ISOAdobe
687 elif value == 1:
688 charset = Expert
689 elif value == 2:
690 charset = ExpertSubset
jvr1890b952002-05-15 07:41:30 +0000691 # self.charset:
692 # 0: ISOAdobe (or CID font!)
693 # 1: Expert
694 # 2: ExpertSubset
jvr4756b3a2002-05-16 18:17:32 +0000695 charset = None #
696 return charset
jvrf2cf9c52002-05-23 21:50:36 +0000697 def write(self, parent, value):
698 return 0 # dummy value
jvr7ce0a132002-07-23 16:42:11 +0000699 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000700 # XXX only write charset when not in OT/TTX context, where we
701 # dump charset as a separate "GlyphOrder" table.
702 ##xmlWriter.simpletag("charset")
703 xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
jvr4756b3a2002-05-16 18:17:32 +0000704 xmlWriter.newline()
jvr4e5af602002-05-24 09:58:04 +0000705 def xmlRead(self, (name, attrs, content), parent):
706 if 0:
707 return safeEval(attrs["value"])
jvr4756b3a2002-05-16 18:17:32 +0000708
709
jvrf2cf9c52002-05-23 21:50:36 +0000710class CharsetCompiler:
711
712 def __init__(self, strings, charset, parent):
713 assert charset[0] == '.notdef'
jvr6004baf2002-05-24 10:35:13 +0000714 data0 = packCharset0(charset, strings)
715 data = packCharset(charset, strings)
716 if len(data) < len(data0):
717 self.data = data
718 else:
719 self.data = data0
jvrf2cf9c52002-05-23 21:50:36 +0000720 self.parent = parent
721
jvr4e5af602002-05-24 09:58:04 +0000722 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000723 self.parent.rawDict["charset"] = pos
724
725 def getDataLength(self):
726 return len(self.data)
727
728 def toFile(self, file):
729 file.write(self.data)
730
731
jvr6004baf2002-05-24 10:35:13 +0000732def packCharset0(charset, strings):
733 format = 0
734 data = [packCard8(format)]
735 for name in charset[1:]:
736 data.append(packCard16(strings.getSID(name)))
737 return "".join(data)
738
739def packCharset(charset, strings):
740 format = 1
jvr6004baf2002-05-24 10:35:13 +0000741 ranges = []
742 first = None
743 end = 0
744 for name in charset[1:]:
745 SID = strings.getSID(name)
746 if first is None:
747 first = SID
748 elif end + 1 <> SID:
749 nLeft = end - first
750 if nLeft > 255:
751 format = 2
752 ranges.append((first, nLeft))
753 first = SID
754 end = SID
755 nLeft = end - first
756 if nLeft > 255:
757 format = 2
758 ranges.append((first, nLeft))
759
jvr74cd1ef2002-05-24 10:38:04 +0000760 data = [packCard8(format)]
jvr6004baf2002-05-24 10:35:13 +0000761 if format == 1:
762 nLeftFunc = packCard8
763 else:
764 nLeftFunc = packCard16
765 for first, nLeft in ranges:
766 data.append(packCard16(first) + nLeftFunc(nLeft))
jvrb58176e2002-05-24 11:55:37 +0000767 return "".join(data)
jvr6004baf2002-05-24 10:35:13 +0000768
jvrf2cf9c52002-05-23 21:50:36 +0000769def parseCharset0(numGlyphs, file, strings):
770 charset = [".notdef"]
771 for i in range(numGlyphs - 1):
772 SID = readCard16(file)
773 charset.append(strings[SID])
774 return charset
775
jvr4756b3a2002-05-16 18:17:32 +0000776def parseCharset(numGlyphs, file, strings, isCID, format):
777 charset = ['.notdef']
778 count = 1
779 if format == 1:
jvra2ad5442002-05-17 18:36:07 +0000780 nLeftFunc = readCard8
jvr4756b3a2002-05-16 18:17:32 +0000781 else:
jvra2ad5442002-05-17 18:36:07 +0000782 nLeftFunc = readCard16
jvr4756b3a2002-05-16 18:17:32 +0000783 while count < numGlyphs:
jvra2ad5442002-05-17 18:36:07 +0000784 first = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000785 nLeft = nLeftFunc(file)
786 if isCID:
787 for CID in range(first, first+nLeft+1):
788 charset.append(CID)
jvr1890b952002-05-15 07:41:30 +0000789 else:
jvr4756b3a2002-05-16 18:17:32 +0000790 for SID in range(first, first+nLeft+1):
791 charset.append(strings[SID])
792 count = count + nLeft + 1
793 return charset
794
795
jvr4e5af602002-05-24 09:58:04 +0000796class FDArrayConverter(TableConverter):
jvra2ad5442002-05-17 18:36:07 +0000797 def read(self, parent, value):
798 file = parent.file
799 file.seek(value)
800 fdArray = TopDictIndex(file)
801 fdArray.strings = parent.strings
802 fdArray.GlobalSubrs = parent.GlobalSubrs
803 return fdArray
804
805
806class FDSelectConverter:
807 def read(self, parent, value):
808 file = parent.file
809 file.seek(value)
810 format = readCard8(file)
811 numGlyphs = parent.numGlyphs
812 if format == 0:
813 from array import array
814 fdSelect = array("B", file.read(numGlyphs)).tolist()
815 elif format == 3:
816 fdSelect = [None] * numGlyphs
817 nRanges = readCard16(file)
818 prev = None
819 for i in range(nRanges):
820 first = readCard16(file)
821 if prev is not None:
822 for glyphID in range(prev, first):
823 fdSelect[glyphID] = fd
824 prev = first
825 fd = readCard8(file)
826 if prev is not None:
827 first = readCard16(file)
828 for glyphID in range(prev, first):
829 fdSelect[glyphID] = fd
830 else:
831 assert 0, "unsupported FDSelect format: %s" % format
832 return fdSelect
jvr7ce0a132002-07-23 16:42:11 +0000833 def xmlWrite(self, xmlWriter, name, value, progress):
jvra2ad5442002-05-17 18:36:07 +0000834 pass
835
836
jvr4e5af602002-05-24 09:58:04 +0000837class ROSConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000838 def xmlWrite(self, xmlWriter, name, value, progress):
jvr155aa752002-05-17 19:58:49 +0000839 registry, order, supplement = value
jvrf2cf9c52002-05-23 21:50:36 +0000840 xmlWriter.simpletag(name, [('Registry', registry), ('Order', order),
841 ('Supplement', supplement)])
jvr4afb2572002-05-18 20:07:01 +0000842 xmlWriter.newline()
jvr155aa752002-05-17 19:58:49 +0000843
844
jvr4756b3a2002-05-16 18:17:32 +0000845topDictOperators = [
846# opcode name argument type default converter
jvr155aa752002-05-17 19:58:49 +0000847 ((12, 30), 'ROS', ('SID','SID','number'), None, ROSConverter()),
jvrf2cf9c52002-05-23 21:50:36 +0000848 ((12, 20), 'SyntheticBase', 'number', None, None),
jvr4756b3a2002-05-16 18:17:32 +0000849 (0, 'version', 'SID', None, None),
jvre2ca9b52002-09-09 14:18:39 +0000850 (1, 'Notice', 'SID', None, Latin1Converter()),
851 ((12, 0), 'Copyright', 'SID', None, Latin1Converter()),
jvr4756b3a2002-05-16 18:17:32 +0000852 (2, 'FullName', 'SID', None, None),
jvr155aa752002-05-17 19:58:49 +0000853 ((12, 38), 'FontName', 'SID', None, None),
jvr4756b3a2002-05-16 18:17:32 +0000854 (3, 'FamilyName', 'SID', None, None),
855 (4, 'Weight', 'SID', None, None),
856 ((12, 1), 'isFixedPitch', 'number', 0, None),
857 ((12, 2), 'ItalicAngle', 'number', 0, None),
858 ((12, 3), 'UnderlinePosition', 'number', None, None),
859 ((12, 4), 'UnderlineThickness', 'number', 50, None),
860 ((12, 5), 'PaintType', 'number', 0, None),
861 ((12, 6), 'CharstringType', 'number', 2, None),
862 ((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0], None),
863 (13, 'UniqueID', 'number', None, None),
864 (5, 'FontBBox', 'array', [0,0,0,0], None),
865 ((12, 8), 'StrokeWidth', 'number', 0, None),
866 (14, 'XUID', 'array', None, None),
867 (15, 'charset', 'number', 0, CharsetConverter()),
jvr4756b3a2002-05-16 18:17:32 +0000868 ((12, 21), 'PostScript', 'SID', None, None),
869 ((12, 22), 'BaseFontName', 'SID', None, None),
870 ((12, 23), 'BaseFontBlend', 'delta', None, None),
jvr4756b3a2002-05-16 18:17:32 +0000871 ((12, 31), 'CIDFontVersion', 'number', 0, None),
872 ((12, 32), 'CIDFontRevision', 'number', 0, None),
873 ((12, 33), 'CIDFontType', 'number', 0, None),
874 ((12, 34), 'CIDCount', 'number', 8720, None),
875 ((12, 35), 'UIDBase', 'number', None, None),
jvr155aa752002-05-17 19:58:49 +0000876 (16, 'Encoding', 'number', 0, None), # XXX
jvra2ad5442002-05-17 18:36:07 +0000877 ((12, 36), 'FDArray', 'number', None, FDArrayConverter()),
878 ((12, 37), 'FDSelect', 'number', None, FDSelectConverter()),
jvr155aa752002-05-17 19:58:49 +0000879 (18, 'Private', ('number','number'), None, PrivateDictConverter()),
880 (17, 'CharStrings', 'number', None, CharStringsConverter()),
jvr4756b3a2002-05-16 18:17:32 +0000881]
882
883privateDictOperators = [
884# opcode name argument type default converter
885 (6, 'BlueValues', 'delta', None, None),
886 (7, 'OtherBlues', 'delta', None, None),
887 (8, 'FamilyBlues', 'delta', None, None),
888 (9, 'FamilyOtherBlues', 'delta', None, None),
889 ((12, 9), 'BlueScale', 'number', 0.039625, None),
890 ((12, 10), 'BlueShift', 'number', 7, None),
891 ((12, 11), 'BlueFuzz', 'number', 1, None),
892 (10, 'StdHW', 'number', None, None),
893 (11, 'StdVW', 'number', None, None),
894 ((12, 12), 'StemSnapH', 'delta', None, None),
895 ((12, 13), 'StemSnapV', 'delta', None, None),
896 ((12, 14), 'ForceBold', 'number', 0, None),
jvrf2cf9c52002-05-23 21:50:36 +0000897 ((12, 15), 'ForceBoldThreshold', 'number', None, None), # deprecated
898 ((12, 16), 'lenIV', 'number', None, None), # deprecated
jvr4756b3a2002-05-16 18:17:32 +0000899 ((12, 17), 'LanguageGroup', 'number', 0, None),
900 ((12, 18), 'ExpansionFactor', 'number', 0.06, None),
901 ((12, 19), 'initialRandomSeed', 'number', 0, None),
902 (20, 'defaultWidthX', 'number', 0, None),
903 (21, 'nominalWidthX', 'number', 0, None),
904 (19, 'Subrs', 'number', None, SubrsConverter()),
905]
906
jvr4e5af602002-05-24 09:58:04 +0000907def addConverters(table):
908 for i in range(len(table)):
909 op, name, arg, default, conv = table[i]
910 if conv is not None:
911 continue
912 if arg in ("delta", "array"):
913 conv = ArrayConverter()
914 elif arg == "number":
915 conv = NumberConverter()
916 elif arg == "SID":
917 conv = SimpleConverter()
918 else:
919 assert 0
920 table[i] = op, name, arg, default, conv
921
922addConverters(privateDictOperators)
923addConverters(topDictOperators)
924
jvr4756b3a2002-05-16 18:17:32 +0000925
926class TopDictDecompiler(psCharStrings.DictDecompiler):
927 operators = buildOperatorDict(topDictOperators)
928
929
930class PrivateDictDecompiler(psCharStrings.DictDecompiler):
931 operators = buildOperatorDict(privateDictOperators)
932
933
jvrf2cf9c52002-05-23 21:50:36 +0000934class DictCompiler:
935
936 def __init__(self, dictObj, strings, parent):
937 assert isinstance(strings, IndexedStrings)
938 self.dictObj = dictObj
939 self.strings = strings
940 self.parent = parent
941 rawDict = {}
942 for name in dictObj.order:
943 value = getattr(dictObj, name, None)
944 if value is None:
945 continue
946 conv = dictObj.converters[name]
jvr4e5af602002-05-24 09:58:04 +0000947 value = conv.write(dictObj, value)
jvrf2cf9c52002-05-23 21:50:36 +0000948 if value == dictObj.defaults.get(name):
949 continue
950 rawDict[name] = value
951 self.rawDict = rawDict
952
jvr4e5af602002-05-24 09:58:04 +0000953 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000954 pass
955
956 def getDataLength(self):
jvr4e5af602002-05-24 09:58:04 +0000957 return len(self.compile("getDataLength"))
jvrf2cf9c52002-05-23 21:50:36 +0000958
jvr4e5af602002-05-24 09:58:04 +0000959 def compile(self, reason):
960 if DEBUG:
961 print "-- compiling %s for %s" % (self.__class__.__name__, reason)
jvrf2cf9c52002-05-23 21:50:36 +0000962 rawDict = self.rawDict
963 data = []
964 for name in self.dictObj.order:
965 value = rawDict.get(name)
966 if value is None:
967 continue
968 op, argType = self.opcodes[name]
969 if type(argType) == TupleType:
970 l = len(argType)
971 assert len(value) == l, "value doesn't match arg type"
972 for i in range(l):
973 arg = argType[l - i - 1]
974 v = value[i]
975 arghandler = getattr(self, "arg_" + arg)
976 data.append(arghandler(v))
977 else:
978 arghandler = getattr(self, "arg_" + argType)
979 data.append(arghandler(value))
980 data.append(op)
981 return "".join(data)
982
983 def toFile(self, file):
jvr4e5af602002-05-24 09:58:04 +0000984 file.write(self.compile("toFile"))
jvrf2cf9c52002-05-23 21:50:36 +0000985
986 def arg_number(self, num):
987 return encodeNumber(num)
988 def arg_SID(self, s):
989 return psCharStrings.encodeIntCFF(self.strings.getSID(s))
990 def arg_array(self, value):
991 data = []
992 for num in value:
993 data.append(encodeNumber(num))
994 return "".join(data)
995 def arg_delta(self, value):
996 out = []
997 last = 0
998 for v in value:
999 out.append(v - last)
1000 last = v
1001 data = []
1002 for num in out:
1003 data.append(encodeNumber(num))
1004 return "".join(data)
1005
1006
1007def encodeNumber(num):
1008 if type(num) == FloatType:
1009 return psCharStrings.encodeFloat(num)
1010 else:
1011 return psCharStrings.encodeIntCFF(num)
1012
1013
1014class TopDictCompiler(DictCompiler):
1015
1016 opcodes = buildOpcodeDict(topDictOperators)
1017
1018 def getChildren(self, strings):
1019 children = []
1020 if hasattr(self.dictObj, "charset"):
1021 children.append(CharsetCompiler(strings, self.dictObj.charset, self))
1022 if hasattr(self.dictObj, "CharStrings"):
1023 items = []
1024 charStrings = self.dictObj.CharStrings
1025 for name in self.dictObj.charset:
1026 items.append(charStrings[name])
1027 charStringsComp = CharStringsCompiler(items, strings, self)
1028 children.append(charStringsComp)
1029 if hasattr(self.dictObj, "Private"):
1030 privComp = self.dictObj.Private.getCompiler(strings, self)
1031 children.append(privComp)
1032 children.extend(privComp.getChildren(strings))
1033 return children
1034
1035
1036class PrivateDictCompiler(DictCompiler):
1037
1038 opcodes = buildOpcodeDict(privateDictOperators)
1039
jvr4e5af602002-05-24 09:58:04 +00001040 def setPos(self, pos, endPos):
1041 size = endPos - pos
jvrf2cf9c52002-05-23 21:50:36 +00001042 self.parent.rawDict["Private"] = size, pos
1043 self.pos = pos
1044
1045 def getChildren(self, strings):
1046 children = []
1047 if hasattr(self.dictObj, "Subrs"):
1048 children.append(self.dictObj.Subrs.getCompiler(strings, self))
1049 return children
1050
jvr4756b3a2002-05-16 18:17:32 +00001051
1052class BaseDict:
1053
jvr4e5af602002-05-24 09:58:04 +00001054 def __init__(self, strings=None, file=None, offset=None):
jvr4756b3a2002-05-16 18:17:32 +00001055 self.rawDict = {}
jvr767102e2002-05-17 07:06:32 +00001056 if DEBUG:
jvrf2cf9c52002-05-23 21:50:36 +00001057 print "loading %s at %s" % (self.__class__.__name__, offset)
jvr4756b3a2002-05-16 18:17:32 +00001058 self.file = file
1059 self.offset = offset
1060 self.strings = strings
jvr155aa752002-05-17 19:58:49 +00001061 self.skipNames = []
jvr4756b3a2002-05-16 18:17:32 +00001062
1063 def decompile(self, data):
jvrf2cf9c52002-05-23 21:50:36 +00001064 if DEBUG:
1065 print " length %s is %s" % (self.__class__.__name__, len(data))
1066 dec = self.decompilerClass(self.strings)
jvr4756b3a2002-05-16 18:17:32 +00001067 dec.decompile(data)
1068 self.rawDict = dec.getDict()
1069 self.postDecompile()
1070
1071 def postDecompile(self):
1072 pass
1073
jvrf2cf9c52002-05-23 21:50:36 +00001074 def getCompiler(self, strings, parent):
1075 return self.compilerClass(self, strings, parent)
1076
jvr4756b3a2002-05-16 18:17:32 +00001077 def __getattr__(self, name):
1078 value = self.rawDict.get(name)
1079 if value is None:
1080 value = self.defaults.get(name)
1081 if value is None:
1082 raise AttributeError, name
1083 conv = self.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001084 value = conv.read(self, value)
jvr4756b3a2002-05-16 18:17:32 +00001085 setattr(self, name, value)
1086 return value
1087
1088 def toXML(self, xmlWriter, progress):
1089 for name in self.order:
jvr155aa752002-05-17 19:58:49 +00001090 if name in self.skipNames:
1091 continue
jvr4756b3a2002-05-16 18:17:32 +00001092 value = getattr(self, name, None)
1093 if value is None:
1094 continue
jvr4e5af602002-05-24 09:58:04 +00001095 conv = self.converters[name]
jvr7ce0a132002-07-23 16:42:11 +00001096 conv.xmlWrite(xmlWriter, name, value, progress)
jvr4e5af602002-05-24 09:58:04 +00001097
1098 def fromXML(self, (name, attrs, content)):
1099 conv = self.converters[name]
1100 value = conv.xmlRead((name, attrs, content), self)
1101 setattr(self, name, value)
jvr4756b3a2002-05-16 18:17:32 +00001102
1103
1104class TopDict(BaseDict):
1105
1106 defaults = buildDefaults(topDictOperators)
1107 converters = buildConverters(topDictOperators)
1108 order = buildOrder(topDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001109 decompilerClass = TopDictDecompiler
1110 compilerClass = TopDictCompiler
Just7842e561999-12-16 21:34:53 +00001111
jvr4e5af602002-05-24 09:58:04 +00001112 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
jvr016ca762002-05-16 18:38:03 +00001113 BaseDict.__init__(self, strings, file, offset)
1114 self.GlobalSubrs = GlobalSubrs
1115
Just7842e561999-12-16 21:34:53 +00001116 def getGlyphOrder(self):
1117 return self.charset
1118
jvr4756b3a2002-05-16 18:17:32 +00001119 def postDecompile(self):
1120 offset = self.rawDict.get("CharStrings")
1121 if offset is None:
1122 return
1123 # get the number of glyphs beforehand.
1124 self.file.seek(offset)
jvra2ad5442002-05-17 18:36:07 +00001125 self.numGlyphs = readCard16(self.file)
Just7842e561999-12-16 21:34:53 +00001126
jvr016ca762002-05-16 18:38:03 +00001127 def toXML(self, xmlWriter, progress):
jvr155aa752002-05-17 19:58:49 +00001128 if hasattr(self, "CharStrings"):
jvr7ce0a132002-07-23 16:42:11 +00001129 self.decompileAllCharStrings(progress)
jvr155aa752002-05-17 19:58:49 +00001130 if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
1131 # these values have default values, but I only want them to show up
1132 # in CID fonts.
1133 self.skipNames = ['CIDFontVersion', 'CIDFontRevision', 'CIDFontType',
1134 'CIDCount']
jvr016ca762002-05-16 18:38:03 +00001135 BaseDict.toXML(self, xmlWriter, progress)
jvr767102e2002-05-17 07:06:32 +00001136
jvr7ce0a132002-07-23 16:42:11 +00001137 def decompileAllCharStrings(self, progress):
jvr4e5af602002-05-24 09:58:04 +00001138 # XXX only when doing ttdump -i?
jvr7ce0a132002-07-23 16:42:11 +00001139 i = 0
jvra2ad5442002-05-17 18:36:07 +00001140 for charString in self.CharStrings.values():
1141 charString.decompile()
jvr7ce0a132002-07-23 16:42:11 +00001142 if not i % 30 and progress:
1143 progress.increment(0) # update
1144 i = i + 1
Just7842e561999-12-16 21:34:53 +00001145
1146
jvr4756b3a2002-05-16 18:17:32 +00001147class PrivateDict(BaseDict):
1148 defaults = buildDefaults(privateDictOperators)
1149 converters = buildConverters(privateDictOperators)
1150 order = buildOrder(privateDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001151 decompilerClass = PrivateDictDecompiler
1152 compilerClass = PrivateDictCompiler
Just7842e561999-12-16 21:34:53 +00001153
1154
jvre3275582002-05-14 12:22:03 +00001155class IndexedStrings:
1156
jvr767102e2002-05-17 07:06:32 +00001157 """SID -> string mapping."""
1158
1159 def __init__(self, file=None):
1160 if file is None:
jvre3275582002-05-14 12:22:03 +00001161 strings = []
jvr767102e2002-05-17 07:06:32 +00001162 else:
jvr4e5af602002-05-24 09:58:04 +00001163 strings = list(Index(file))
jvre3275582002-05-14 12:22:03 +00001164 self.strings = strings
1165
jvrf2cf9c52002-05-23 21:50:36 +00001166 def getCompiler(self):
1167 return IndexedStringsCompiler(self, None, None)
1168
1169 def __len__(self):
1170 return len(self.strings)
1171
jvre3275582002-05-14 12:22:03 +00001172 def __getitem__(self, SID):
1173 if SID < cffStandardStringCount:
1174 return cffStandardStrings[SID]
1175 else:
1176 return self.strings[SID - cffStandardStringCount]
1177
1178 def getSID(self, s):
1179 if not hasattr(self, "stringMapping"):
1180 self.buildStringMapping()
1181 if cffStandardStringMapping.has_key(s):
1182 SID = cffStandardStringMapping[s]
jvrf2cf9c52002-05-23 21:50:36 +00001183 elif self.stringMapping.has_key(s):
jvre3275582002-05-14 12:22:03 +00001184 SID = self.stringMapping[s]
1185 else:
1186 SID = len(self.strings) + cffStandardStringCount
1187 self.strings.append(s)
1188 self.stringMapping[s] = SID
1189 return SID
1190
1191 def getStrings(self):
1192 return self.strings
1193
1194 def buildStringMapping(self):
1195 self.stringMapping = {}
1196 for index in range(len(self.strings)):
1197 self.stringMapping[self.strings[index]] = index + cffStandardStringCount
1198
1199
Just7842e561999-12-16 21:34:53 +00001200# The 391 Standard Strings as used in the CFF format.
1201# from Adobe Technical None #5176, version 1.0, 18 March 1998
1202
1203cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
1204 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
1205 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
1206 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
1207 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C',
1208 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
1209 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
1210 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
1211 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
1212 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
1213 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
1214 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
1215 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
1216 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
1217 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
1218 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
1219 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
1220 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
1221 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
1222 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
1223 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
1224 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
1225 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
1226 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
1227 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
1228 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
1229 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
1230 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
1231 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
1232 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
1233 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
1234 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
1235 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
1236 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
1237 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
1238 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
1239 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
1240 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
1241 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
1242 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
1243 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
1244 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
1245 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
1246 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
1247 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
1248 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
1249 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
1250 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
1251 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
1252 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
1253 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
1254 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
1255 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
1256 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
1257 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
1258 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
1259 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
1260 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
1261 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
1262 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
1263 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
1264 '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
1265 'Semibold'
1266]
1267
1268cffStandardStringCount = 391
1269assert len(cffStandardStrings) == cffStandardStringCount
1270# build reverse mapping
1271cffStandardStringMapping = {}
1272for _i in range(cffStandardStringCount):
1273 cffStandardStringMapping[cffStandardStrings[_i]] = _i
1274