blob: e611f8bb6b499d9d7be208bd1e5e12e4b7f6eacc [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#
jvrb9702ba2003-01-03 20:56:01 +00004# $Id: cffLib.py,v 1.28 2003-01-03 20:56:01 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:
jvrb9702ba2003-01-03 20:56:01 +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
jvrb9702ba2003-01-03 20:56:01 +0000796class EncodingCompiler:
797
798 def __init__(self, strings, encoding, parent):
799 assert not isinstance(encoding, StringType)
800 data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
801 data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
802 if len(data0) < len(data1):
803 self.data = data0
804 else:
805 self.data = data1
806 self.parent = parent
807
808 def setPos(self, pos, endPos):
809 self.parent.rawDict["Encoding"] = pos
810
811 def getDataLength(self):
812 return len(self.data)
813
814 def toFile(self, file):
815 file.write(self.data)
816
817
818class EncodingConverter(SimpleConverter):
819
820 def read(self, parent, value):
821 if value == 0:
822 return "StandardEncoding"
823 elif value == 1:
824 return "ExpertEncoding"
825 else:
826 assert value > 1
827 file = parent.file
828 file.seek(value)
829 if DEBUG:
830 print "loading Encoding at %s" % value
831 format = readCard8(file)
832 haveSupplement = format & 0x80
833 if haveSupplement:
834 raise NotImplementedError, "Encoding supplements are not yet supported"
835 format = format & 0x7f
836 if format == 0:
837 encoding = parseEncoding0(parent.charset, file, haveSupplement,
838 parent.strings)
839 elif format == 1:
840 encoding = parseEncoding1(parent.charset, file, haveSupplement,
841 parent.strings)
842 return encoding
843
844 def write(self, parent, value):
845 if value == "StandardEncoding":
846 return 0
847 elif value == "ExpertEncoding":
848 return 1
849 return 0 # dummy value
850
851 def xmlWrite(self, xmlWriter, name, value, progress):
852 if value in ("StandardEncoding", "ExpertEncoding"):
853 xmlWriter.simpletag(name, name=value)
854 xmlWriter.newline()
855 return
856 xmlWriter.begintag(name)
857 xmlWriter.newline()
858 for code in range(len(value)):
859 glyphName = value[code]
860 if glyphName != ".notdef":
861 xmlWriter.simpletag("map", code=hex(code), name=glyphName)
862 xmlWriter.newline()
863 xmlWriter.endtag(name)
864 xmlWriter.newline()
865
866 def xmlRead(self, (name, attrs, content), parent):
867 if attrs.has_key("name"):
868 return attrs["name"]
869 encoding = [".notdef"] * 256
870 for element in content:
871 if isinstance(element, StringType):
872 continue
873 name, attrs, content = element
874 code = safeEval(attrs["code"])
875 glyphName = attrs["name"]
876 encoding[code] = glyphName
877 return encoding
878
879
880def parseEncoding0(charset, file, haveSupplement, strings):
881 nCodes = readCard8(file)
882 encoding = [".notdef"] * 256
883 for glyphID in range(1, nCodes + 1):
884 code = readCard8(file)
885 if code != 0:
886 encoding[code] = charset[glyphID]
887 return encoding
888
889def parseEncoding1(charset, file, haveSupplement, strings):
890 nRanges = readCard8(file)
891 encoding = [".notdef"] * 256
892 glyphID = 1
893 for i in range(nRanges):
894 code = readCard8(file)
895 nLeft = readCard8(file)
896 for glyphID in range(glyphID, glyphID + nLeft + 1):
897 encoding[code] = charset[glyphID]
898 code = code + 1
899 glyphID = glyphID + 1
900 return encoding
901
902def packEncoding0(charset, encoding, strings):
903 format = 0
904 m = {}
905 for code in range(len(encoding)):
906 name = encoding[code]
907 if name != ".notdef":
908 m[name] = code
909 codes = []
910 for name in charset[1:]:
911 code = m.get(name)
912 codes.append(code)
913
914 while codes and codes[-1] is None:
915 codes.pop()
916
917 data = [packCard8(format), packCard8(len(codes))]
918 for code in codes:
919 if code is None:
920 code = 0
921 data.append(packCard8(code))
922 return "".join(data)
923
924def packEncoding1(charset, encoding, strings):
925 format = 1
926 m = {}
927 for code in range(len(encoding)):
928 name = encoding[code]
929 if name != ".notdef":
930 m[name] = code
931 ranges = []
932 first = None
933 end = 0
934 for name in charset[1:]:
935 code = m.get(name, -1)
936 if first is None:
937 first = code
938 elif end + 1 <> code:
939 nLeft = end - first
940 ranges.append((first, nLeft))
941 first = code
942 end = code
943 nLeft = end - first
944 ranges.append((first, nLeft))
945
946 # remove unencoded glyphs at the end.
947 while ranges and ranges[-1][0] == -1:
948 ranges.pop()
949
950 data = [packCard8(format), packCard8(len(ranges))]
951 for first, nLeft in ranges:
952 if first == -1: # unencoded
953 first = 0
954 data.append(packCard8(first) + packCard8(nLeft))
955 return "".join(data)
956
957
jvr4e5af602002-05-24 09:58:04 +0000958class FDArrayConverter(TableConverter):
jvra2ad5442002-05-17 18:36:07 +0000959 def read(self, parent, value):
960 file = parent.file
961 file.seek(value)
962 fdArray = TopDictIndex(file)
963 fdArray.strings = parent.strings
964 fdArray.GlobalSubrs = parent.GlobalSubrs
965 return fdArray
966
967
968class FDSelectConverter:
969 def read(self, parent, value):
970 file = parent.file
971 file.seek(value)
972 format = readCard8(file)
973 numGlyphs = parent.numGlyphs
974 if format == 0:
975 from array import array
976 fdSelect = array("B", file.read(numGlyphs)).tolist()
977 elif format == 3:
978 fdSelect = [None] * numGlyphs
979 nRanges = readCard16(file)
980 prev = None
981 for i in range(nRanges):
982 first = readCard16(file)
983 if prev is not None:
984 for glyphID in range(prev, first):
985 fdSelect[glyphID] = fd
986 prev = first
987 fd = readCard8(file)
988 if prev is not None:
989 first = readCard16(file)
990 for glyphID in range(prev, first):
991 fdSelect[glyphID] = fd
992 else:
993 assert 0, "unsupported FDSelect format: %s" % format
994 return fdSelect
jvr7ce0a132002-07-23 16:42:11 +0000995 def xmlWrite(self, xmlWriter, name, value, progress):
jvra2ad5442002-05-17 18:36:07 +0000996 pass
997
998
jvr4e5af602002-05-24 09:58:04 +0000999class ROSConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +00001000 def xmlWrite(self, xmlWriter, name, value, progress):
jvr155aa752002-05-17 19:58:49 +00001001 registry, order, supplement = value
jvrf2cf9c52002-05-23 21:50:36 +00001002 xmlWriter.simpletag(name, [('Registry', registry), ('Order', order),
1003 ('Supplement', supplement)])
jvr4afb2572002-05-18 20:07:01 +00001004 xmlWriter.newline()
jvr155aa752002-05-17 19:58:49 +00001005
1006
jvr4756b3a2002-05-16 18:17:32 +00001007topDictOperators = [
1008# opcode name argument type default converter
jvr155aa752002-05-17 19:58:49 +00001009 ((12, 30), 'ROS', ('SID','SID','number'), None, ROSConverter()),
jvrf2cf9c52002-05-23 21:50:36 +00001010 ((12, 20), 'SyntheticBase', 'number', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001011 (0, 'version', 'SID', None, None),
jvre2ca9b52002-09-09 14:18:39 +00001012 (1, 'Notice', 'SID', None, Latin1Converter()),
1013 ((12, 0), 'Copyright', 'SID', None, Latin1Converter()),
jvr4756b3a2002-05-16 18:17:32 +00001014 (2, 'FullName', 'SID', None, None),
jvr155aa752002-05-17 19:58:49 +00001015 ((12, 38), 'FontName', 'SID', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001016 (3, 'FamilyName', 'SID', None, None),
1017 (4, 'Weight', 'SID', None, None),
1018 ((12, 1), 'isFixedPitch', 'number', 0, None),
1019 ((12, 2), 'ItalicAngle', 'number', 0, None),
1020 ((12, 3), 'UnderlinePosition', 'number', None, None),
1021 ((12, 4), 'UnderlineThickness', 'number', 50, None),
1022 ((12, 5), 'PaintType', 'number', 0, None),
1023 ((12, 6), 'CharstringType', 'number', 2, None),
1024 ((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0], None),
1025 (13, 'UniqueID', 'number', None, None),
1026 (5, 'FontBBox', 'array', [0,0,0,0], None),
1027 ((12, 8), 'StrokeWidth', 'number', 0, None),
1028 (14, 'XUID', 'array', None, None),
1029 (15, 'charset', 'number', 0, CharsetConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001030 ((12, 21), 'PostScript', 'SID', None, None),
1031 ((12, 22), 'BaseFontName', 'SID', None, None),
1032 ((12, 23), 'BaseFontBlend', 'delta', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001033 ((12, 31), 'CIDFontVersion', 'number', 0, None),
1034 ((12, 32), 'CIDFontRevision', 'number', 0, None),
1035 ((12, 33), 'CIDFontType', 'number', 0, None),
1036 ((12, 34), 'CIDCount', 'number', 8720, None),
1037 ((12, 35), 'UIDBase', 'number', None, None),
jvrb9702ba2003-01-03 20:56:01 +00001038 (16, 'Encoding', 'number', 0, EncodingConverter()),
jvra2ad5442002-05-17 18:36:07 +00001039 ((12, 36), 'FDArray', 'number', None, FDArrayConverter()),
1040 ((12, 37), 'FDSelect', 'number', None, FDSelectConverter()),
jvr155aa752002-05-17 19:58:49 +00001041 (18, 'Private', ('number','number'), None, PrivateDictConverter()),
1042 (17, 'CharStrings', 'number', None, CharStringsConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001043]
1044
1045privateDictOperators = [
1046# opcode name argument type default converter
1047 (6, 'BlueValues', 'delta', None, None),
1048 (7, 'OtherBlues', 'delta', None, None),
1049 (8, 'FamilyBlues', 'delta', None, None),
1050 (9, 'FamilyOtherBlues', 'delta', None, None),
1051 ((12, 9), 'BlueScale', 'number', 0.039625, None),
1052 ((12, 10), 'BlueShift', 'number', 7, None),
1053 ((12, 11), 'BlueFuzz', 'number', 1, None),
1054 (10, 'StdHW', 'number', None, None),
1055 (11, 'StdVW', 'number', None, None),
1056 ((12, 12), 'StemSnapH', 'delta', None, None),
1057 ((12, 13), 'StemSnapV', 'delta', None, None),
1058 ((12, 14), 'ForceBold', 'number', 0, None),
jvrf2cf9c52002-05-23 21:50:36 +00001059 ((12, 15), 'ForceBoldThreshold', 'number', None, None), # deprecated
1060 ((12, 16), 'lenIV', 'number', None, None), # deprecated
jvr4756b3a2002-05-16 18:17:32 +00001061 ((12, 17), 'LanguageGroup', 'number', 0, None),
1062 ((12, 18), 'ExpansionFactor', 'number', 0.06, None),
1063 ((12, 19), 'initialRandomSeed', 'number', 0, None),
1064 (20, 'defaultWidthX', 'number', 0, None),
1065 (21, 'nominalWidthX', 'number', 0, None),
1066 (19, 'Subrs', 'number', None, SubrsConverter()),
1067]
1068
jvr4e5af602002-05-24 09:58:04 +00001069def addConverters(table):
1070 for i in range(len(table)):
1071 op, name, arg, default, conv = table[i]
1072 if conv is not None:
1073 continue
1074 if arg in ("delta", "array"):
1075 conv = ArrayConverter()
1076 elif arg == "number":
1077 conv = NumberConverter()
1078 elif arg == "SID":
1079 conv = SimpleConverter()
1080 else:
1081 assert 0
1082 table[i] = op, name, arg, default, conv
1083
1084addConverters(privateDictOperators)
1085addConverters(topDictOperators)
1086
jvr4756b3a2002-05-16 18:17:32 +00001087
1088class TopDictDecompiler(psCharStrings.DictDecompiler):
1089 operators = buildOperatorDict(topDictOperators)
1090
1091
1092class PrivateDictDecompiler(psCharStrings.DictDecompiler):
1093 operators = buildOperatorDict(privateDictOperators)
1094
1095
jvrf2cf9c52002-05-23 21:50:36 +00001096class DictCompiler:
1097
1098 def __init__(self, dictObj, strings, parent):
1099 assert isinstance(strings, IndexedStrings)
1100 self.dictObj = dictObj
1101 self.strings = strings
1102 self.parent = parent
1103 rawDict = {}
1104 for name in dictObj.order:
1105 value = getattr(dictObj, name, None)
1106 if value is None:
1107 continue
1108 conv = dictObj.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001109 value = conv.write(dictObj, value)
jvrf2cf9c52002-05-23 21:50:36 +00001110 if value == dictObj.defaults.get(name):
1111 continue
1112 rawDict[name] = value
1113 self.rawDict = rawDict
1114
jvr4e5af602002-05-24 09:58:04 +00001115 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +00001116 pass
1117
1118 def getDataLength(self):
jvr4e5af602002-05-24 09:58:04 +00001119 return len(self.compile("getDataLength"))
jvrf2cf9c52002-05-23 21:50:36 +00001120
jvr4e5af602002-05-24 09:58:04 +00001121 def compile(self, reason):
1122 if DEBUG:
1123 print "-- compiling %s for %s" % (self.__class__.__name__, reason)
jvrf2cf9c52002-05-23 21:50:36 +00001124 rawDict = self.rawDict
1125 data = []
1126 for name in self.dictObj.order:
1127 value = rawDict.get(name)
1128 if value is None:
1129 continue
1130 op, argType = self.opcodes[name]
1131 if type(argType) == TupleType:
1132 l = len(argType)
1133 assert len(value) == l, "value doesn't match arg type"
1134 for i in range(l):
1135 arg = argType[l - i - 1]
1136 v = value[i]
1137 arghandler = getattr(self, "arg_" + arg)
1138 data.append(arghandler(v))
1139 else:
1140 arghandler = getattr(self, "arg_" + argType)
1141 data.append(arghandler(value))
1142 data.append(op)
1143 return "".join(data)
1144
1145 def toFile(self, file):
jvr4e5af602002-05-24 09:58:04 +00001146 file.write(self.compile("toFile"))
jvrf2cf9c52002-05-23 21:50:36 +00001147
1148 def arg_number(self, num):
1149 return encodeNumber(num)
1150 def arg_SID(self, s):
1151 return psCharStrings.encodeIntCFF(self.strings.getSID(s))
1152 def arg_array(self, value):
1153 data = []
1154 for num in value:
1155 data.append(encodeNumber(num))
1156 return "".join(data)
1157 def arg_delta(self, value):
1158 out = []
1159 last = 0
1160 for v in value:
1161 out.append(v - last)
1162 last = v
1163 data = []
1164 for num in out:
1165 data.append(encodeNumber(num))
1166 return "".join(data)
1167
1168
1169def encodeNumber(num):
1170 if type(num) == FloatType:
1171 return psCharStrings.encodeFloat(num)
1172 else:
1173 return psCharStrings.encodeIntCFF(num)
1174
1175
1176class TopDictCompiler(DictCompiler):
1177
1178 opcodes = buildOpcodeDict(topDictOperators)
1179
1180 def getChildren(self, strings):
1181 children = []
1182 if hasattr(self.dictObj, "charset"):
1183 children.append(CharsetCompiler(strings, self.dictObj.charset, self))
jvrb9702ba2003-01-03 20:56:01 +00001184 if hasattr(self.dictObj, "Encoding"):
1185 encoding = self.dictObj.Encoding
1186 if not isinstance(encoding, StringType):
1187 children.append(EncodingCompiler(strings, encoding, self))
jvrf2cf9c52002-05-23 21:50:36 +00001188 if hasattr(self.dictObj, "CharStrings"):
1189 items = []
1190 charStrings = self.dictObj.CharStrings
1191 for name in self.dictObj.charset:
1192 items.append(charStrings[name])
1193 charStringsComp = CharStringsCompiler(items, strings, self)
1194 children.append(charStringsComp)
1195 if hasattr(self.dictObj, "Private"):
1196 privComp = self.dictObj.Private.getCompiler(strings, self)
1197 children.append(privComp)
1198 children.extend(privComp.getChildren(strings))
1199 return children
1200
1201
1202class PrivateDictCompiler(DictCompiler):
1203
1204 opcodes = buildOpcodeDict(privateDictOperators)
1205
jvr4e5af602002-05-24 09:58:04 +00001206 def setPos(self, pos, endPos):
1207 size = endPos - pos
jvrf2cf9c52002-05-23 21:50:36 +00001208 self.parent.rawDict["Private"] = size, pos
1209 self.pos = pos
1210
1211 def getChildren(self, strings):
1212 children = []
1213 if hasattr(self.dictObj, "Subrs"):
1214 children.append(self.dictObj.Subrs.getCompiler(strings, self))
1215 return children
1216
jvr4756b3a2002-05-16 18:17:32 +00001217
1218class BaseDict:
1219
jvr4e5af602002-05-24 09:58:04 +00001220 def __init__(self, strings=None, file=None, offset=None):
jvr4756b3a2002-05-16 18:17:32 +00001221 self.rawDict = {}
jvr767102e2002-05-17 07:06:32 +00001222 if DEBUG:
jvrf2cf9c52002-05-23 21:50:36 +00001223 print "loading %s at %s" % (self.__class__.__name__, offset)
jvr4756b3a2002-05-16 18:17:32 +00001224 self.file = file
1225 self.offset = offset
1226 self.strings = strings
jvr155aa752002-05-17 19:58:49 +00001227 self.skipNames = []
jvr4756b3a2002-05-16 18:17:32 +00001228
1229 def decompile(self, data):
jvrf2cf9c52002-05-23 21:50:36 +00001230 if DEBUG:
1231 print " length %s is %s" % (self.__class__.__name__, len(data))
1232 dec = self.decompilerClass(self.strings)
jvr4756b3a2002-05-16 18:17:32 +00001233 dec.decompile(data)
1234 self.rawDict = dec.getDict()
1235 self.postDecompile()
1236
1237 def postDecompile(self):
1238 pass
1239
jvrf2cf9c52002-05-23 21:50:36 +00001240 def getCompiler(self, strings, parent):
1241 return self.compilerClass(self, strings, parent)
1242
jvr4756b3a2002-05-16 18:17:32 +00001243 def __getattr__(self, name):
1244 value = self.rawDict.get(name)
1245 if value is None:
1246 value = self.defaults.get(name)
1247 if value is None:
1248 raise AttributeError, name
1249 conv = self.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001250 value = conv.read(self, value)
jvr4756b3a2002-05-16 18:17:32 +00001251 setattr(self, name, value)
1252 return value
1253
1254 def toXML(self, xmlWriter, progress):
1255 for name in self.order:
jvr155aa752002-05-17 19:58:49 +00001256 if name in self.skipNames:
1257 continue
jvr4756b3a2002-05-16 18:17:32 +00001258 value = getattr(self, name, None)
1259 if value is None:
1260 continue
jvr4e5af602002-05-24 09:58:04 +00001261 conv = self.converters[name]
jvr7ce0a132002-07-23 16:42:11 +00001262 conv.xmlWrite(xmlWriter, name, value, progress)
jvr4e5af602002-05-24 09:58:04 +00001263
1264 def fromXML(self, (name, attrs, content)):
1265 conv = self.converters[name]
1266 value = conv.xmlRead((name, attrs, content), self)
1267 setattr(self, name, value)
jvr4756b3a2002-05-16 18:17:32 +00001268
1269
1270class TopDict(BaseDict):
1271
1272 defaults = buildDefaults(topDictOperators)
1273 converters = buildConverters(topDictOperators)
1274 order = buildOrder(topDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001275 decompilerClass = TopDictDecompiler
1276 compilerClass = TopDictCompiler
Just7842e561999-12-16 21:34:53 +00001277
jvr4e5af602002-05-24 09:58:04 +00001278 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
jvr016ca762002-05-16 18:38:03 +00001279 BaseDict.__init__(self, strings, file, offset)
1280 self.GlobalSubrs = GlobalSubrs
1281
Just7842e561999-12-16 21:34:53 +00001282 def getGlyphOrder(self):
1283 return self.charset
1284
jvr4756b3a2002-05-16 18:17:32 +00001285 def postDecompile(self):
1286 offset = self.rawDict.get("CharStrings")
1287 if offset is None:
1288 return
1289 # get the number of glyphs beforehand.
1290 self.file.seek(offset)
jvra2ad5442002-05-17 18:36:07 +00001291 self.numGlyphs = readCard16(self.file)
Just7842e561999-12-16 21:34:53 +00001292
jvr016ca762002-05-16 18:38:03 +00001293 def toXML(self, xmlWriter, progress):
jvr155aa752002-05-17 19:58:49 +00001294 if hasattr(self, "CharStrings"):
jvr7ce0a132002-07-23 16:42:11 +00001295 self.decompileAllCharStrings(progress)
jvr155aa752002-05-17 19:58:49 +00001296 if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
1297 # these values have default values, but I only want them to show up
1298 # in CID fonts.
1299 self.skipNames = ['CIDFontVersion', 'CIDFontRevision', 'CIDFontType',
1300 'CIDCount']
jvr016ca762002-05-16 18:38:03 +00001301 BaseDict.toXML(self, xmlWriter, progress)
jvr767102e2002-05-17 07:06:32 +00001302
jvr7ce0a132002-07-23 16:42:11 +00001303 def decompileAllCharStrings(self, progress):
jvr4e5af602002-05-24 09:58:04 +00001304 # XXX only when doing ttdump -i?
jvr7ce0a132002-07-23 16:42:11 +00001305 i = 0
jvra2ad5442002-05-17 18:36:07 +00001306 for charString in self.CharStrings.values():
1307 charString.decompile()
jvr7ce0a132002-07-23 16:42:11 +00001308 if not i % 30 and progress:
1309 progress.increment(0) # update
1310 i = i + 1
Just7842e561999-12-16 21:34:53 +00001311
1312
jvr4756b3a2002-05-16 18:17:32 +00001313class PrivateDict(BaseDict):
1314 defaults = buildDefaults(privateDictOperators)
1315 converters = buildConverters(privateDictOperators)
1316 order = buildOrder(privateDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001317 decompilerClass = PrivateDictDecompiler
1318 compilerClass = PrivateDictCompiler
Just7842e561999-12-16 21:34:53 +00001319
1320
jvre3275582002-05-14 12:22:03 +00001321class IndexedStrings:
1322
jvr767102e2002-05-17 07:06:32 +00001323 """SID -> string mapping."""
1324
1325 def __init__(self, file=None):
1326 if file is None:
jvre3275582002-05-14 12:22:03 +00001327 strings = []
jvr767102e2002-05-17 07:06:32 +00001328 else:
jvr4e5af602002-05-24 09:58:04 +00001329 strings = list(Index(file))
jvre3275582002-05-14 12:22:03 +00001330 self.strings = strings
1331
jvrf2cf9c52002-05-23 21:50:36 +00001332 def getCompiler(self):
1333 return IndexedStringsCompiler(self, None, None)
1334
1335 def __len__(self):
1336 return len(self.strings)
1337
jvre3275582002-05-14 12:22:03 +00001338 def __getitem__(self, SID):
1339 if SID < cffStandardStringCount:
1340 return cffStandardStrings[SID]
1341 else:
1342 return self.strings[SID - cffStandardStringCount]
1343
1344 def getSID(self, s):
1345 if not hasattr(self, "stringMapping"):
1346 self.buildStringMapping()
1347 if cffStandardStringMapping.has_key(s):
1348 SID = cffStandardStringMapping[s]
jvrf2cf9c52002-05-23 21:50:36 +00001349 elif self.stringMapping.has_key(s):
jvre3275582002-05-14 12:22:03 +00001350 SID = self.stringMapping[s]
1351 else:
1352 SID = len(self.strings) + cffStandardStringCount
1353 self.strings.append(s)
1354 self.stringMapping[s] = SID
1355 return SID
1356
1357 def getStrings(self):
1358 return self.strings
1359
1360 def buildStringMapping(self):
1361 self.stringMapping = {}
1362 for index in range(len(self.strings)):
1363 self.stringMapping[self.strings[index]] = index + cffStandardStringCount
1364
1365
Just7842e561999-12-16 21:34:53 +00001366# The 391 Standard Strings as used in the CFF format.
1367# from Adobe Technical None #5176, version 1.0, 18 March 1998
1368
1369cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
1370 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
1371 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
1372 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
1373 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C',
1374 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
1375 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
1376 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
1377 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
1378 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
1379 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
1380 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
1381 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
1382 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
1383 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
1384 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
1385 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
1386 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
1387 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
1388 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
1389 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
1390 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
1391 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
1392 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
1393 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
1394 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
1395 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
1396 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
1397 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
1398 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
1399 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
1400 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
1401 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
1402 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
1403 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
1404 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
1405 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
1406 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
1407 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
1408 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
1409 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
1410 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
1411 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
1412 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
1413 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
1414 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
1415 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
1416 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
1417 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
1418 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
1419 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
1420 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
1421 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
1422 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
1423 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
1424 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
1425 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
1426 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
1427 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
1428 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
1429 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
1430 '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
1431 'Semibold'
1432]
1433
1434cffStandardStringCount = 391
1435assert len(cffStandardStrings) == cffStandardStringCount
1436# build reverse mapping
1437cffStandardStringMapping = {}
1438for _i in range(cffStandardStringCount):
1439 cffStandardStringMapping[cffStandardStrings[_i]] = _i
1440