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