blob: 719a88bcf56303b5d3df39f356bbef9bd87c5243 [file] [log] [blame]
Just7842e561999-12-16 21:34:53 +00001"""cffLib.py -- read/write tools for Adobe CFF fonts."""
2
Justec46d161999-12-20 22:02:10 +00003#
jvr2a9bcde2008-03-07 19:56:17 +00004# $Id: cffLib.py,v 1.34 2008-03-07 19:56:17 jvr Exp $
Justec46d161999-12-20 22:02:10 +00005#
Just7842e561999-12-16 21:34:53 +00006
7import struct, sstruct
8import string
Just528614e2000-01-16 22:14:02 +00009from fontTools.misc import psCharStrings
jvr4e5af602002-05-24 09:58:04 +000010from fontTools.misc.textTools import safeEval
Just7842e561999-12-16 21:34:53 +000011
jvr767102e2002-05-17 07:06:32 +000012DEBUG = 0
13
14
Just7842e561999-12-16 21:34:53 +000015cffHeaderFormat = """
16 major: B
17 minor: B
18 hdrSize: B
19 offSize: B
20"""
21
22class CFFFontSet:
23
24 def __init__(self):
jvr4756b3a2002-05-16 18:17:32 +000025 pass
Just7842e561999-12-16 21:34:53 +000026
jvr4e5af602002-05-24 09:58:04 +000027 def decompile(self, file, otFont):
jvra2a75b32002-05-13 11:25:17 +000028 sstruct.unpack(cffHeaderFormat, file.read(4), self)
Just7842e561999-12-16 21:34:53 +000029 assert self.major == 1 and self.minor == 0, \
30 "unknown CFF format: %d.%d" % (self.major, self.minor)
Just7842e561999-12-16 21:34:53 +000031
jvrf2cf9c52002-05-23 21:50:36 +000032 file.seek(self.hdrSize)
jvr4e5af602002-05-24 09:58:04 +000033 self.fontNames = list(Index(file))
jvr4756b3a2002-05-16 18:17:32 +000034 self.topDictIndex = TopDictIndex(file)
jvr767102e2002-05-17 07:06:32 +000035 self.strings = IndexedStrings(file)
jvr4e5af602002-05-24 09:58:04 +000036 self.GlobalSubrs = GlobalSubrsIndex(file)
jvr4756b3a2002-05-16 18:17:32 +000037 self.topDictIndex.strings = self.strings
jvr016ca762002-05-16 18:38:03 +000038 self.topDictIndex.GlobalSubrs = self.GlobalSubrs
jvr4756b3a2002-05-16 18:17:32 +000039
40 def __len__(self):
41 return len(self.fontNames)
42
43 def keys(self):
jvrce522412003-08-25 07:37:25 +000044 return list(self.fontNames)
jvr4756b3a2002-05-16 18:17:32 +000045
jvr767102e2002-05-17 07:06:32 +000046 def values(self):
47 return self.topDictIndex
48
jvr4756b3a2002-05-16 18:17:32 +000049 def __getitem__(self, name):
50 try:
51 index = self.fontNames.index(name)
52 except ValueError:
53 raise KeyError, name
jvr016ca762002-05-16 18:38:03 +000054 return self.topDictIndex[index]
Just7842e561999-12-16 21:34:53 +000055
jvr4e5af602002-05-24 09:58:04 +000056 def compile(self, file, otFont):
Just7842e561999-12-16 21:34:53 +000057 strings = IndexedStrings()
jvrf2cf9c52002-05-23 21:50:36 +000058 writer = CFFWriter()
59 writer.add(sstruct.pack(cffHeaderFormat, self))
60 fontNames = Index()
61 for name in self.fontNames:
62 fontNames.append(name)
63 writer.add(fontNames.getCompiler(strings, None))
64 topCompiler = self.topDictIndex.getCompiler(strings, None)
65 writer.add(topCompiler)
66 writer.add(strings.getCompiler())
67 writer.add(self.GlobalSubrs.getCompiler(strings, None))
68
jvr4e5af602002-05-24 09:58:04 +000069 for topDict in self.topDictIndex:
70 if not hasattr(topDict, "charset") or topDict.charset is None:
71 charset = otFont.getGlyphOrder()
72 topDict.charset = charset
73
jvrf2cf9c52002-05-23 21:50:36 +000074 for child in topCompiler.getChildren(strings):
75 writer.add(child)
76
jvrf2cf9c52002-05-23 21:50:36 +000077 writer.toFile(file)
Just7842e561999-12-16 21:34:53 +000078
79 def toXML(self, xmlWriter, progress=None):
80 xmlWriter.newline()
81 for fontName in self.fontNames:
82 xmlWriter.begintag("CFFFont", name=fontName)
83 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +000084 font = self[fontName]
Just7842e561999-12-16 21:34:53 +000085 font.toXML(xmlWriter, progress)
86 xmlWriter.endtag("CFFFont")
87 xmlWriter.newline()
88 xmlWriter.newline()
89 xmlWriter.begintag("GlobalSubrs")
90 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +000091 self.GlobalSubrs.toXML(xmlWriter, progress)
Just7842e561999-12-16 21:34:53 +000092 xmlWriter.endtag("GlobalSubrs")
93 xmlWriter.newline()
94 xmlWriter.newline()
95
96 def fromXML(self, (name, attrs, content)):
jvr4e5af602002-05-24 09:58:04 +000097 if not hasattr(self, "GlobalSubrs"):
98 self.GlobalSubrs = GlobalSubrsIndex()
99 self.major = 1
100 self.minor = 0
101 self.hdrSize = 4
102 self.offSize = 4 # XXX ??
103 if name == "CFFFont":
104 if not hasattr(self, "fontNames"):
105 self.fontNames = []
106 self.topDictIndex = TopDictIndex()
107 fontName = attrs["name"]
108 topDict = TopDict(GlobalSubrs=self.GlobalSubrs)
109 topDict.charset = None # gets filled in later
110 self.fontNames.append(fontName)
111 self.topDictIndex.append(topDict)
112 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000113 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000114 continue
115 topDict.fromXML(element)
116 elif name == "GlobalSubrs":
117 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000118 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000119 continue
120 name, attrs, content = element
jvr489d76a2003-08-24 19:56:16 +0000121 subr = psCharStrings.T2CharString()
jvr4e5af602002-05-24 09:58:04 +0000122 subr.fromXML((name, attrs, content))
123 self.GlobalSubrs.append(subr)
Just7842e561999-12-16 21:34:53 +0000124
125
jvrf2cf9c52002-05-23 21:50:36 +0000126class CFFWriter:
127
128 def __init__(self):
129 self.data = []
130
131 def add(self, table):
132 self.data.append(table)
133
134 def toFile(self, file):
135 lastPosList = None
136 count = 1
137 while 1:
jvr4e5af602002-05-24 09:58:04 +0000138 if DEBUG:
139 print "CFFWriter.toFile() iteration:", count
140 count = count + 1
jvrf2cf9c52002-05-23 21:50:36 +0000141 pos = 0
142 posList = [pos]
143 for item in self.data:
jvrf2cf9c52002-05-23 21:50:36 +0000144 if hasattr(item, "getDataLength"):
jvr4e5af602002-05-24 09:58:04 +0000145 endPos = pos + item.getDataLength()
jvrf2cf9c52002-05-23 21:50:36 +0000146 else:
jvr4e5af602002-05-24 09:58:04 +0000147 endPos = pos + len(item)
148 if hasattr(item, "setPos"):
149 item.setPos(pos, endPos)
150 pos = endPos
jvrf2cf9c52002-05-23 21:50:36 +0000151 posList.append(pos)
152 if posList == lastPosList:
153 break
154 lastPosList = posList
jvr4e5af602002-05-24 09:58:04 +0000155 if DEBUG:
156 print "CFFWriter.toFile() writing to file."
jvrf2cf9c52002-05-23 21:50:36 +0000157 begin = file.tell()
158 posList = [0]
159 for item in self.data:
160 if hasattr(item, "toFile"):
161 item.toFile(file)
162 else:
163 file.write(item)
164 posList.append(file.tell() - begin)
jvrf2cf9c52002-05-23 21:50:36 +0000165 assert posList == lastPosList
166
167
168def calcOffSize(largestOffset):
169 if largestOffset < 0x100:
170 offSize = 1
171 elif largestOffset < 0x10000:
172 offSize = 2
173 elif largestOffset < 0x1000000:
174 offSize = 3
175 else:
176 offSize = 4
177 return offSize
178
179
180class IndexCompiler:
181
182 def __init__(self, items, strings, parent):
183 self.items = self.getItems(items, strings)
184 self.parent = parent
185
186 def getItems(self, items, strings):
187 return items
188
189 def getOffsets(self):
190 pos = 1
191 offsets = [pos]
192 for item in self.items:
193 if hasattr(item, "getDataLength"):
194 pos = pos + item.getDataLength()
195 else:
196 pos = pos + len(item)
197 offsets.append(pos)
198 return offsets
199
200 def getDataLength(self):
201 lastOffset = self.getOffsets()[-1]
202 offSize = calcOffSize(lastOffset)
203 dataLength = (
204 2 + # count
205 1 + # offSize
206 (len(self.items) + 1) * offSize + # the offsets
207 lastOffset - 1 # size of object data
208 )
209 return dataLength
210
211 def toFile(self, file):
jvrf2cf9c52002-05-23 21:50:36 +0000212 offsets = self.getOffsets()
213 writeCard16(file, len(self.items))
214 offSize = calcOffSize(offsets[-1])
215 writeCard8(file, offSize)
216 offSize = -offSize
217 pack = struct.pack
218 for offset in offsets:
219 binOffset = pack(">l", offset)[offSize:]
220 assert len(binOffset) == -offSize
221 file.write(binOffset)
222 for item in self.items:
223 if hasattr(item, "toFile"):
224 item.toFile(file)
225 else:
226 file.write(item)
jvrf2cf9c52002-05-23 21:50:36 +0000227
228
229class IndexedStringsCompiler(IndexCompiler):
230
231 def getItems(self, items, strings):
232 return items.strings
233
234
235class TopDictIndexCompiler(IndexCompiler):
236
237 def getItems(self, items, strings):
238 out = []
239 for item in items:
240 out.append(item.getCompiler(strings, self))
241 return out
242
243 def getChildren(self, strings):
244 children = []
245 for topDict in self.items:
246 children.extend(topDict.getChildren(strings))
247 return children
248
249
jvred101512003-08-22 19:53:32 +0000250class FDArrayIndexCompiler(IndexCompiler):
251
252 def getItems(self, items, strings):
253 out = []
254 for item in items:
255 out.append(item.getCompiler(strings, self))
256 return out
257
258 def getChildren(self, strings):
259 children = []
260 for fontDict in self.items:
261 children.extend(fontDict.getChildren(strings))
262 return children
263
jvred101512003-08-22 19:53:32 +0000264 def toFile(self, file):
265 offsets = self.getOffsets()
266 writeCard16(file, len(self.items))
267 offSize = calcOffSize(offsets[-1])
268 writeCard8(file, offSize)
269 offSize = -offSize
270 pack = struct.pack
271 for offset in offsets:
272 binOffset = pack(">l", offset)[offSize:]
273 assert len(binOffset) == -offSize
274 file.write(binOffset)
275 for item in self.items:
276 if hasattr(item, "toFile"):
277 item.toFile(file)
278 else:
279 file.write(item)
280
281 def setPos(self, pos, endPos):
282 self.parent.rawDict["FDArray"] = pos
283
284
jvrf2cf9c52002-05-23 21:50:36 +0000285class GlobalSubrsCompiler(IndexCompiler):
286 def getItems(self, items, strings):
287 out = []
288 for cs in items:
289 cs.compile()
290 out.append(cs.bytecode)
291 return out
292
293class SubrsCompiler(GlobalSubrsCompiler):
jvr4e5af602002-05-24 09:58:04 +0000294 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000295 offset = pos - self.parent.pos
296 self.parent.rawDict["Subrs"] = offset
297
298class CharStringsCompiler(GlobalSubrsCompiler):
jvr4e5af602002-05-24 09:58:04 +0000299 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000300 self.parent.rawDict["CharStrings"] = pos
301
302
jvr4756b3a2002-05-16 18:17:32 +0000303class Index:
Just7842e561999-12-16 21:34:53 +0000304
jvr4756b3a2002-05-16 18:17:32 +0000305 """This class represents what the CFF spec calls an INDEX."""
Just7842e561999-12-16 21:34:53 +0000306
jvrf2cf9c52002-05-23 21:50:36 +0000307 compilerClass = IndexCompiler
308
jvr4e5af602002-05-24 09:58:04 +0000309 def __init__(self, file=None):
310 name = self.__class__.__name__
jvrf2cf9c52002-05-23 21:50:36 +0000311 if file is None:
312 self.items = []
313 return
jvr767102e2002-05-17 07:06:32 +0000314 if DEBUG:
315 print "loading %s at %s" % (name, file.tell())
jvr4756b3a2002-05-16 18:17:32 +0000316 self.file = file
jvra2ad5442002-05-17 18:36:07 +0000317 count = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000318 self.count = count
319 self.items = [None] * count
320 if count == 0:
jvrf2cf9c52002-05-23 21:50:36 +0000321 self.items = []
jvr4756b3a2002-05-16 18:17:32 +0000322 return
jvra2ad5442002-05-17 18:36:07 +0000323 offSize = readCard8(file)
jvr767102e2002-05-17 07:06:32 +0000324 if DEBUG:
jvrf2cf9c52002-05-23 21:50:36 +0000325 print " index count: %s offSize: %s" % (count, offSize)
jvr767102e2002-05-17 07:06:32 +0000326 assert offSize <= 4, "offSize too large: %s" % offSize
jvr4756b3a2002-05-16 18:17:32 +0000327 self.offsets = offsets = []
328 pad = '\0' * (4 - offSize)
329 for index in range(count+1):
330 chunk = file.read(offSize)
331 chunk = pad + chunk
332 offset, = struct.unpack(">L", chunk)
333 offsets.append(int(offset))
334 self.offsetBase = file.tell() - 1
335 file.seek(self.offsetBase + offsets[-1]) # pretend we've read the whole lot
jvrf2cf9c52002-05-23 21:50:36 +0000336 if DEBUG:
337 print " end of %s at %s" % (name, file.tell())
Just7842e561999-12-16 21:34:53 +0000338
jvr4756b3a2002-05-16 18:17:32 +0000339 def __len__(self):
jvrf2cf9c52002-05-23 21:50:36 +0000340 return len(self.items)
jvra2a75b32002-05-13 11:25:17 +0000341
jvr4756b3a2002-05-16 18:17:32 +0000342 def __getitem__(self, index):
343 item = self.items[index]
344 if item is not None:
345 return item
346 offset = self.offsets[index] + self.offsetBase
347 size = self.offsets[index+1] - self.offsets[index]
348 file = self.file
349 file.seek(offset)
350 data = file.read(size)
351 assert len(data) == size
jvra2ad5442002-05-17 18:36:07 +0000352 item = self.produceItem(index, data, file, offset, size)
jvr4756b3a2002-05-16 18:17:32 +0000353 self.items[index] = item
354 return item
355
jvra2ad5442002-05-17 18:36:07 +0000356 def produceItem(self, index, data, file, offset, size):
jvr4756b3a2002-05-16 18:17:32 +0000357 return data
jvr4756b3a2002-05-16 18:17:32 +0000358
jvrf2cf9c52002-05-23 21:50:36 +0000359 def append(self, item):
360 self.items.append(item)
361
362 def getCompiler(self, strings, parent):
363 return self.compilerClass(self, strings, parent)
364
365
366class GlobalSubrsIndex(Index):
367
368 compilerClass = GlobalSubrsCompiler
369
jvr4e5af602002-05-24 09:58:04 +0000370 def __init__(self, file=None, globalSubrs=None, private=None, fdSelect=None, fdArray=None):
371 Index.__init__(self, file)
jvra2ad5442002-05-17 18:36:07 +0000372 self.globalSubrs = globalSubrs
373 self.private = private
jvred101512003-08-22 19:53:32 +0000374 if fdSelect:
375 self.fdSelect = fdSelect
376 if fdArray:
377 self.fdArray = fdArray
jvra2ad5442002-05-17 18:36:07 +0000378
379 def produceItem(self, index, data, file, offset, size):
380 if self.private is not None:
381 private = self.private
jvred101512003-08-22 19:53:32 +0000382 elif hasattr(self, 'fdArray') and self.fdArray is not None:
jvra2ad5442002-05-17 18:36:07 +0000383 private = self.fdArray[self.fdSelect[index]].Private
384 else:
385 private = None
jvr489d76a2003-08-24 19:56:16 +0000386 return psCharStrings.T2CharString(data, private=private, globalSubrs=self.globalSubrs)
jvr4756b3a2002-05-16 18:17:32 +0000387
388 def toXML(self, xmlWriter, progress):
jvred101512003-08-22 19:53:32 +0000389 xmlWriter.comment("The 'index' attribute is only for humans; it is ignored when parsed.")
jvr4e5af602002-05-24 09:58:04 +0000390 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +0000391 for i in range(len(self)):
jvrb58176e2002-05-24 11:55:37 +0000392 subr = self[i]
393 if subr.needsDecompilation():
394 xmlWriter.begintag("CharString", index=i, raw=1)
395 else:
396 xmlWriter.begintag("CharString", index=i)
jvr4756b3a2002-05-16 18:17:32 +0000397 xmlWriter.newline()
jvrb58176e2002-05-24 11:55:37 +0000398 subr.toXML(xmlWriter)
jvr4756b3a2002-05-16 18:17:32 +0000399 xmlWriter.endtag("CharString")
400 xmlWriter.newline()
jvra2ad5442002-05-17 18:36:07 +0000401
jvr4e5af602002-05-24 09:58:04 +0000402 def fromXML(self, (name, attrs, content)):
403 if name <> "CharString":
404 return
jvr489d76a2003-08-24 19:56:16 +0000405 subr = psCharStrings.T2CharString()
jvr4e5af602002-05-24 09:58:04 +0000406 subr.fromXML((name, attrs, content))
407 self.append(subr)
408
jvra2ad5442002-05-17 18:36:07 +0000409 def getItemAndSelector(self, index):
jvred101512003-08-22 19:53:32 +0000410 sel = None
411 if hasattr(self, 'fdSelect'):
412 sel = self.fdSelect[index]
jvra2ad5442002-05-17 18:36:07 +0000413 return self[index], sel
jvrf2cf9c52002-05-23 21:50:36 +0000414
jvre2ca9b52002-09-09 14:18:39 +0000415
jvrf2cf9c52002-05-23 21:50:36 +0000416class SubrsIndex(GlobalSubrsIndex):
417 compilerClass = SubrsCompiler
418
jvr4756b3a2002-05-16 18:17:32 +0000419
jvr767102e2002-05-17 07:06:32 +0000420class TopDictIndex(Index):
jvra2ad5442002-05-17 18:36:07 +0000421
jvrf2cf9c52002-05-23 21:50:36 +0000422 compilerClass = TopDictIndexCompiler
423
jvra2ad5442002-05-17 18:36:07 +0000424 def produceItem(self, index, data, file, offset, size):
jvr767102e2002-05-17 07:06:32 +0000425 top = TopDict(self.strings, file, offset, self.GlobalSubrs)
426 top.decompile(data)
427 return top
jvra2ad5442002-05-17 18:36:07 +0000428
429 def toXML(self, xmlWriter, progress):
430 for i in range(len(self)):
431 xmlWriter.begintag("FontDict", index=i)
432 xmlWriter.newline()
433 self[i].toXML(xmlWriter, progress)
434 xmlWriter.endtag("FontDict")
435 xmlWriter.newline()
jvr767102e2002-05-17 07:06:32 +0000436
437
jvred101512003-08-22 19:53:32 +0000438class FDArrayIndex(TopDictIndex):
439
440 compilerClass = FDArrayIndexCompiler
441
442 def fromXML(self, (name, attrs, content)):
443 if name <> "FontDict":
444 return
445 fontDict = FontDict()
446 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000447 if isinstance(element, basestring):
jvred101512003-08-22 19:53:32 +0000448 continue
449 fontDict.fromXML(element)
450 self.append(fontDict)
451
452
453class FDSelect:
454 def __init__(self, file = None, numGlyphs = None, format=None):
455 if file:
456 # read data in from file
457 self.format = readCard8(file)
458 if self.format == 0:
459 from array import array
460 self.gidArray = array("B", file.read(numGlyphs)).tolist()
461 elif self.format == 3:
462 gidArray = [None] * numGlyphs
463 nRanges = readCard16(file)
464 prev = None
465 for i in range(nRanges):
466 first = readCard16(file)
467 if prev is not None:
468 for glyphID in range(prev, first):
469 gidArray[glyphID] = fd
470 prev = first
471 fd = readCard8(file)
472 if prev is not None:
473 first = readCard16(file)
474 for glyphID in range(prev, first):
475 gidArray[glyphID] = fd
476 self.gidArray = gidArray
477 else:
478 assert 0, "unsupported FDSelect format: %s" % format
479 else:
480 # reading from XML. Make empty gidArray,, and leave format as passed in.
481 # format == None will result in the smallest representation being used.
482 self.format = format
483 self.gidArray = []
484
485
486 def __len__(self):
487 return len(self.gidArray)
488
489 def __getitem__(self, index):
490 return self.gidArray[index]
491
492 def __setitem__(self, index, fdSelectValue):
493 self.gidArray[index] = fdSelectValue
494
495 def append(self, fdSelectValue):
496 self.gidArray.append(fdSelectValue)
497
498
jvr4756b3a2002-05-16 18:17:32 +0000499class CharStrings:
500
jvra2ad5442002-05-17 18:36:07 +0000501 def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray):
jvr4e5af602002-05-24 09:58:04 +0000502 if file is not None:
503 self.charStringsIndex = SubrsIndex(file, globalSubrs, private, fdSelect, fdArray)
504 self.charStrings = charStrings = {}
505 for i in range(len(charset)):
506 charStrings[charset[i]] = i
507 self.charStringsAreIndexed = 1
508 else:
509 self.charStrings = {}
510 self.charStringsAreIndexed = 0
511 self.globalSubrs = globalSubrs
512 self.private = private
jvred101512003-08-22 19:53:32 +0000513 if fdSelect != None:
514 self.fdSelect = fdSelect
515 if fdArray!= None:
516 self.fdArray = fdArray
jvr4756b3a2002-05-16 18:17:32 +0000517
518 def keys(self):
jvr4e5af602002-05-24 09:58:04 +0000519 return self.charStrings.keys()
jvr4756b3a2002-05-16 18:17:32 +0000520
jvr016ca762002-05-16 18:38:03 +0000521 def values(self):
jvr4e5af602002-05-24 09:58:04 +0000522 if self.charStringsAreIndexed:
523 return self.charStringsIndex
524 else:
525 return self.charStrings.values()
jvr016ca762002-05-16 18:38:03 +0000526
jvr4756b3a2002-05-16 18:17:32 +0000527 def has_key(self, name):
jvr4e5af602002-05-24 09:58:04 +0000528 return self.charStrings.has_key(name)
jvr4756b3a2002-05-16 18:17:32 +0000529
jvr767102e2002-05-17 07:06:32 +0000530 def __len__(self):
jvr4e5af602002-05-24 09:58:04 +0000531 return len(self.charStrings)
jvr767102e2002-05-17 07:06:32 +0000532
jvr4756b3a2002-05-16 18:17:32 +0000533 def __getitem__(self, name):
jvr4e5af602002-05-24 09:58:04 +0000534 charString = self.charStrings[name]
535 if self.charStringsAreIndexed:
536 charString = self.charStringsIndex[charString]
537 return charString
538
539 def __setitem__(self, name, charString):
540 if self.charStringsAreIndexed:
541 index = self.charStrings[name]
542 self.charStringsIndex[index] = charString
543 else:
544 self.charStrings[name] = charString
jvr4756b3a2002-05-16 18:17:32 +0000545
jvra2ad5442002-05-17 18:36:07 +0000546 def getItemAndSelector(self, name):
jvr4e5af602002-05-24 09:58:04 +0000547 if self.charStringsAreIndexed:
548 index = self.charStrings[name]
549 return self.charStringsIndex.getItemAndSelector(index)
550 else:
jvred101512003-08-22 19:53:32 +0000551 if hasattr(self, 'fdSelect'):
jvr91bca422012-10-18 12:49:22 +0000552 sel = self.fdSelect[index] # index is not defined at this point. Read R. ?
jvred101512003-08-22 19:53:32 +0000553 else:
554 raise KeyError("fdSelect array not yet defined.")
555 return self.charStrings[name], sel
jvra2ad5442002-05-17 18:36:07 +0000556
jvr4756b3a2002-05-16 18:17:32 +0000557 def toXML(self, xmlWriter, progress):
558 names = self.keys()
559 names.sort()
jvr7ce0a132002-07-23 16:42:11 +0000560 i = 0
561 step = 10
562 numGlyphs = len(names)
jvr4756b3a2002-05-16 18:17:32 +0000563 for name in names:
jvred101512003-08-22 19:53:32 +0000564 charStr, fdSelectIndex = self.getItemAndSelector(name)
jvrb58176e2002-05-24 11:55:37 +0000565 if charStr.needsDecompilation():
566 raw = [("raw", 1)]
567 else:
568 raw = []
jvred101512003-08-22 19:53:32 +0000569 if fdSelectIndex is None:
jvrb58176e2002-05-24 11:55:37 +0000570 xmlWriter.begintag("CharString", [('name', name)] + raw)
jvra2ad5442002-05-17 18:36:07 +0000571 else:
572 xmlWriter.begintag("CharString",
jvred101512003-08-22 19:53:32 +0000573 [('name', name), ('fdSelectIndex', fdSelectIndex)] + raw)
jvr4756b3a2002-05-16 18:17:32 +0000574 xmlWriter.newline()
jvrb58176e2002-05-24 11:55:37 +0000575 charStr.toXML(xmlWriter)
jvr4756b3a2002-05-16 18:17:32 +0000576 xmlWriter.endtag("CharString")
577 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000578 if not i % step and progress is not None:
579 progress.setLabel("Dumping 'CFF ' table... (%s)" % name)
580 progress.increment(step / float(numGlyphs))
581 i = i + 1
jvr4e5af602002-05-24 09:58:04 +0000582
583 def fromXML(self, (name, attrs, content)):
584 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000585 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000586 continue
587 name, attrs, content = element
588 if name <> "CharString":
589 continue
jvred101512003-08-22 19:53:32 +0000590 fdID = -1
591 if hasattr(self, "fdArray"):
592 fdID = safeEval(attrs["fdSelectIndex"])
593 private = self.fdArray[fdID].Private
594 else:
595 private = self.private
596
jvr4e5af602002-05-24 09:58:04 +0000597 glyphName = attrs["name"]
jvr489d76a2003-08-24 19:56:16 +0000598 charString = psCharStrings.T2CharString(
599 private=private,
600 globalSubrs=self.globalSubrs)
jvr4e5af602002-05-24 09:58:04 +0000601 charString.fromXML((name, attrs, content))
jvred101512003-08-22 19:53:32 +0000602 if fdID >= 0:
603 charString.fdSelectIndex = fdID
jvr4e5af602002-05-24 09:58:04 +0000604 self[glyphName] = charString
jvr4756b3a2002-05-16 18:17:32 +0000605
606
jvra2ad5442002-05-17 18:36:07 +0000607def readCard8(file):
608 return ord(file.read(1))
609
610def readCard16(file):
611 value, = struct.unpack(">H", file.read(2))
612 return value
613
jvrf2cf9c52002-05-23 21:50:36 +0000614def writeCard8(file, value):
615 file.write(chr(value))
616
617def writeCard16(file, value):
618 file.write(struct.pack(">H", value))
619
620def packCard8(value):
621 return chr(value)
622
623def packCard16(value):
624 return struct.pack(">H", value)
625
jvr4756b3a2002-05-16 18:17:32 +0000626def buildOperatorDict(table):
627 d = {}
628 for op, name, arg, default, conv in table:
629 d[op] = (name, arg)
630 return d
631
jvrf2cf9c52002-05-23 21:50:36 +0000632def buildOpcodeDict(table):
633 d = {}
634 for op, name, arg, default, conv in table:
jvr2a9bcde2008-03-07 19:56:17 +0000635 if isinstance(op, tuple):
jvrf2cf9c52002-05-23 21:50:36 +0000636 op = chr(op[0]) + chr(op[1])
637 else:
638 op = chr(op)
639 d[name] = (op, arg)
640 return d
641
jvr4756b3a2002-05-16 18:17:32 +0000642def buildOrder(table):
643 l = []
644 for op, name, arg, default, conv in table:
645 l.append(name)
646 return l
647
648def buildDefaults(table):
649 d = {}
650 for op, name, arg, default, conv in table:
651 if default is not None:
652 d[name] = default
653 return d
654
655def buildConverters(table):
656 d = {}
657 for op, name, arg, default, conv in table:
658 d[name] = conv
659 return d
660
661
jvr4e5af602002-05-24 09:58:04 +0000662class SimpleConverter:
jvr7ce02ea2002-05-17 20:04:05 +0000663 def read(self, parent, value):
664 return value
jvrf2cf9c52002-05-23 21:50:36 +0000665 def write(self, parent, value):
666 return value
jvr7ce0a132002-07-23 16:42:11 +0000667 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000668 xmlWriter.simpletag(name, value=value)
669 xmlWriter.newline()
670 def xmlRead(self, (name, attrs, content), parent):
671 return attrs["value"]
672
jvre2ca9b52002-09-09 14:18:39 +0000673class Latin1Converter(SimpleConverter):
674 def xmlRead(self, (name, attrs, content), parent):
675 s = unicode(attrs["value"], "utf-8")
676 return s.encode("latin-1")
677
678
jvr4e5af602002-05-24 09:58:04 +0000679def parseNum(s):
680 try:
681 value = int(s)
682 except:
683 value = float(s)
684 return value
685
686class NumberConverter(SimpleConverter):
687 def xmlRead(self, (name, attrs, content), parent):
688 return parseNum(attrs["value"])
689
690class ArrayConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000691 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000692 value = map(str, value)
693 xmlWriter.simpletag(name, value=" ".join(value))
694 xmlWriter.newline()
695 def xmlRead(self, (name, attrs, content), parent):
696 values = attrs["value"].split()
697 return map(parseNum, values)
698
699class TableConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000700 def xmlWrite(self, xmlWriter, name, value, progress):
jvra2ad5442002-05-17 18:36:07 +0000701 xmlWriter.begintag(name)
702 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000703 value.toXML(xmlWriter, progress)
jvra2ad5442002-05-17 18:36:07 +0000704 xmlWriter.endtag(name)
705 xmlWriter.newline()
jvr4e5af602002-05-24 09:58:04 +0000706 def xmlRead(self, (name, attrs, content), parent):
707 ob = self.getClass()()
708 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000709 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000710 continue
711 ob.fromXML(element)
712 return ob
jvra2ad5442002-05-17 18:36:07 +0000713
jvr4e5af602002-05-24 09:58:04 +0000714class PrivateDictConverter(TableConverter):
715 def getClass(self):
716 return PrivateDict
jvr4756b3a2002-05-16 18:17:32 +0000717 def read(self, parent, value):
718 size, offset = value
719 file = parent.file
jvr4e5af602002-05-24 09:58:04 +0000720 priv = PrivateDict(parent.strings, file, offset)
jvr4756b3a2002-05-16 18:17:32 +0000721 file.seek(offset)
722 data = file.read(size)
723 len(data) == size
jvr4e5af602002-05-24 09:58:04 +0000724 priv.decompile(data)
725 return priv
jvrf2cf9c52002-05-23 21:50:36 +0000726 def write(self, parent, value):
727 return (0, 0) # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000728
jvr4e5af602002-05-24 09:58:04 +0000729class SubrsConverter(TableConverter):
730 def getClass(self):
731 return SubrsIndex
jvr4756b3a2002-05-16 18:17:32 +0000732 def read(self, parent, value):
733 file = parent.file
734 file.seek(parent.offset + value) # Offset(self)
jvr4e5af602002-05-24 09:58:04 +0000735 return SubrsIndex(file)
jvrf2cf9c52002-05-23 21:50:36 +0000736 def write(self, parent, value):
737 return 0 # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000738
jvr4e5af602002-05-24 09:58:04 +0000739class CharStringsConverter(TableConverter):
jvr4756b3a2002-05-16 18:17:32 +0000740 def read(self, parent, value):
741 file = parent.file
jvr767102e2002-05-17 07:06:32 +0000742 charset = parent.charset
jvra2ad5442002-05-17 18:36:07 +0000743 globalSubrs = parent.GlobalSubrs
744 if hasattr(parent, "ROS"):
745 fdSelect, fdArray = parent.FDSelect, parent.FDArray
746 private = None
747 else:
748 fdSelect, fdArray = None, None
749 private = parent.Private
jvr4756b3a2002-05-16 18:17:32 +0000750 file.seek(value) # Offset(0)
jvra2ad5442002-05-17 18:36:07 +0000751 return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
jvrf2cf9c52002-05-23 21:50:36 +0000752 def write(self, parent, value):
753 return 0 # dummy value
jvr4e5af602002-05-24 09:58:04 +0000754 def xmlRead(self, (name, attrs, content), parent):
jvred101512003-08-22 19:53:32 +0000755 if hasattr(parent, "ROS"):
756 # if it is a CID-keyed font, then the private Dict is extracted from the parent.FDArray
757 private, fdSelect, fdArray = None, parent.FDSelect, parent.FDArray
758 else:
759 # if it is a name-keyed font, then the private dict is in the top dict, and there is no fdArray.
760 private, fdSelect, fdArray = parent.Private, None, None
761 charStrings = CharStrings(None, None, parent.GlobalSubrs, private, fdSelect, fdArray)
jvr4e5af602002-05-24 09:58:04 +0000762 charStrings.fromXML((name, attrs, content))
763 return charStrings
jvr4756b3a2002-05-16 18:17:32 +0000764
765class CharsetConverter:
766 def read(self, parent, value):
767 isCID = hasattr(parent, "ROS")
768 if value > 2:
769 numGlyphs = parent.numGlyphs
770 file = parent.file
771 file.seek(value)
jvrf2cf9c52002-05-23 21:50:36 +0000772 if DEBUG:
773 print "loading charset at %s" % value
jvra2ad5442002-05-17 18:36:07 +0000774 format = readCard8(file)
Just7842e561999-12-16 21:34:53 +0000775 if format == 0:
jvrc60a44f2006-10-21 13:41:18 +0000776 charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
jvr4756b3a2002-05-16 18:17:32 +0000777 elif format == 1 or format == 2:
778 charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
Just7842e561999-12-16 21:34:53 +0000779 else:
jvr1890b952002-05-15 07:41:30 +0000780 raise NotImplementedError
jvr4756b3a2002-05-16 18:17:32 +0000781 assert len(charset) == numGlyphs
jvrf2cf9c52002-05-23 21:50:36 +0000782 if DEBUG:
783 print " charset end at %s" % file.tell()
jvrc60a44f2006-10-21 13:41:18 +0000784 else: # offset == 0 -> no charset data.
785 if isCID or not parent.rawDict.has_key("CharStrings"):
786 assert value == 0 # We get here only when processing fontDicts from the FDArray of CFF-CID fonts. Only the real topDict references the chrset.
jvr4756b3a2002-05-16 18:17:32 +0000787 charset = None
788 elif value == 0:
jvrc60a44f2006-10-21 13:41:18 +0000789 charset = cffISOAdobeStrings
jvr4756b3a2002-05-16 18:17:32 +0000790 elif value == 1:
jvrc60a44f2006-10-21 13:41:18 +0000791 charset = cffIExpertStrings
jvr4756b3a2002-05-16 18:17:32 +0000792 elif value == 2:
jvrc60a44f2006-10-21 13:41:18 +0000793 charset = cffExpertSubsetStrings
jvr4756b3a2002-05-16 18:17:32 +0000794 return charset
jvrc60a44f2006-10-21 13:41:18 +0000795
jvrf2cf9c52002-05-23 21:50:36 +0000796 def write(self, parent, value):
797 return 0 # dummy value
jvr7ce0a132002-07-23 16:42:11 +0000798 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000799 # XXX only write charset when not in OT/TTX context, where we
800 # dump charset as a separate "GlyphOrder" table.
801 ##xmlWriter.simpletag("charset")
802 xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
jvr4756b3a2002-05-16 18:17:32 +0000803 xmlWriter.newline()
jvr4e5af602002-05-24 09:58:04 +0000804 def xmlRead(self, (name, attrs, content), parent):
805 if 0:
806 return safeEval(attrs["value"])
jvr4756b3a2002-05-16 18:17:32 +0000807
808
jvrf2cf9c52002-05-23 21:50:36 +0000809class CharsetCompiler:
810
811 def __init__(self, strings, charset, parent):
812 assert charset[0] == '.notdef'
jvred101512003-08-22 19:53:32 +0000813 isCID = hasattr(parent.dictObj, "ROS")
814 data0 = packCharset0(charset, isCID, strings)
815 data = packCharset(charset, isCID, strings)
jvr6004baf2002-05-24 10:35:13 +0000816 if len(data) < len(data0):
817 self.data = data
818 else:
819 self.data = data0
jvrf2cf9c52002-05-23 21:50:36 +0000820 self.parent = parent
821
jvr4e5af602002-05-24 09:58:04 +0000822 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000823 self.parent.rawDict["charset"] = pos
824
825 def getDataLength(self):
826 return len(self.data)
827
828 def toFile(self, file):
829 file.write(self.data)
830
831
jvred101512003-08-22 19:53:32 +0000832def getCIDfromName(name, strings):
833 return int(name[3:])
834
835def getSIDfromName(name, strings):
836 return strings.getSID(name)
837
838def packCharset0(charset, isCID, strings):
jvr6004baf2002-05-24 10:35:13 +0000839 format = 0
840 data = [packCard8(format)]
jvred101512003-08-22 19:53:32 +0000841 if isCID:
842 getNameID = getCIDfromName
843 else:
844 getNameID = getSIDfromName
845
jvr6004baf2002-05-24 10:35:13 +0000846 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000847 data.append(packCard16(getNameID(name,strings)))
jvr6004baf2002-05-24 10:35:13 +0000848 return "".join(data)
849
jvred101512003-08-22 19:53:32 +0000850
851def packCharset(charset, isCID, strings):
jvr6004baf2002-05-24 10:35:13 +0000852 format = 1
jvr6004baf2002-05-24 10:35:13 +0000853 ranges = []
854 first = None
855 end = 0
jvred101512003-08-22 19:53:32 +0000856 if isCID:
857 getNameID = getCIDfromName
858 else:
859 getNameID = getSIDfromName
860
jvr6004baf2002-05-24 10:35:13 +0000861 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000862 SID = getNameID(name, strings)
jvr6004baf2002-05-24 10:35:13 +0000863 if first is None:
864 first = SID
865 elif end + 1 <> SID:
866 nLeft = end - first
867 if nLeft > 255:
868 format = 2
869 ranges.append((first, nLeft))
870 first = SID
871 end = SID
872 nLeft = end - first
873 if nLeft > 255:
874 format = 2
875 ranges.append((first, nLeft))
876
jvr74cd1ef2002-05-24 10:38:04 +0000877 data = [packCard8(format)]
jvr6004baf2002-05-24 10:35:13 +0000878 if format == 1:
879 nLeftFunc = packCard8
880 else:
881 nLeftFunc = packCard16
882 for first, nLeft in ranges:
883 data.append(packCard16(first) + nLeftFunc(nLeft))
jvrb58176e2002-05-24 11:55:37 +0000884 return "".join(data)
jvr6004baf2002-05-24 10:35:13 +0000885
jvrc60a44f2006-10-21 13:41:18 +0000886def parseCharset0(numGlyphs, file, strings, isCID):
jvrf2cf9c52002-05-23 21:50:36 +0000887 charset = [".notdef"]
jvrc60a44f2006-10-21 13:41:18 +0000888 if isCID:
889 for i in range(numGlyphs - 1):
890 CID = readCard16(file)
891 charset.append("cid" + string.zfill(str(CID), 5) )
892 else:
893 for i in range(numGlyphs - 1):
894 SID = readCard16(file)
895 charset.append(strings[SID])
jvrf2cf9c52002-05-23 21:50:36 +0000896 return charset
897
jvr4756b3a2002-05-16 18:17:32 +0000898def parseCharset(numGlyphs, file, strings, isCID, format):
899 charset = ['.notdef']
900 count = 1
901 if format == 1:
jvra2ad5442002-05-17 18:36:07 +0000902 nLeftFunc = readCard8
jvr4756b3a2002-05-16 18:17:32 +0000903 else:
jvra2ad5442002-05-17 18:36:07 +0000904 nLeftFunc = readCard16
jvr4756b3a2002-05-16 18:17:32 +0000905 while count < numGlyphs:
jvra2ad5442002-05-17 18:36:07 +0000906 first = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000907 nLeft = nLeftFunc(file)
908 if isCID:
909 for CID in range(first, first+nLeft+1):
jvrc60a44f2006-10-21 13:41:18 +0000910 charset.append("cid" + string.zfill(str(CID), 5) )
jvr1890b952002-05-15 07:41:30 +0000911 else:
jvr4756b3a2002-05-16 18:17:32 +0000912 for SID in range(first, first+nLeft+1):
913 charset.append(strings[SID])
914 count = count + nLeft + 1
915 return charset
916
917
jvrb9702ba2003-01-03 20:56:01 +0000918class EncodingCompiler:
919
920 def __init__(self, strings, encoding, parent):
jvr2a9bcde2008-03-07 19:56:17 +0000921 assert not isinstance(encoding, basestring)
jvrb9702ba2003-01-03 20:56:01 +0000922 data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
923 data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
924 if len(data0) < len(data1):
925 self.data = data0
926 else:
927 self.data = data1
928 self.parent = parent
929
930 def setPos(self, pos, endPos):
931 self.parent.rawDict["Encoding"] = pos
932
933 def getDataLength(self):
934 return len(self.data)
935
936 def toFile(self, file):
937 file.write(self.data)
938
939
940class EncodingConverter(SimpleConverter):
941
942 def read(self, parent, value):
943 if value == 0:
944 return "StandardEncoding"
945 elif value == 1:
946 return "ExpertEncoding"
947 else:
948 assert value > 1
949 file = parent.file
950 file.seek(value)
951 if DEBUG:
952 print "loading Encoding at %s" % value
953 format = readCard8(file)
954 haveSupplement = format & 0x80
955 if haveSupplement:
956 raise NotImplementedError, "Encoding supplements are not yet supported"
957 format = format & 0x7f
958 if format == 0:
959 encoding = parseEncoding0(parent.charset, file, haveSupplement,
960 parent.strings)
961 elif format == 1:
962 encoding = parseEncoding1(parent.charset, file, haveSupplement,
963 parent.strings)
964 return encoding
965
966 def write(self, parent, value):
967 if value == "StandardEncoding":
968 return 0
969 elif value == "ExpertEncoding":
970 return 1
971 return 0 # dummy value
972
973 def xmlWrite(self, xmlWriter, name, value, progress):
974 if value in ("StandardEncoding", "ExpertEncoding"):
975 xmlWriter.simpletag(name, name=value)
976 xmlWriter.newline()
977 return
978 xmlWriter.begintag(name)
979 xmlWriter.newline()
980 for code in range(len(value)):
981 glyphName = value[code]
982 if glyphName != ".notdef":
983 xmlWriter.simpletag("map", code=hex(code), name=glyphName)
984 xmlWriter.newline()
985 xmlWriter.endtag(name)
986 xmlWriter.newline()
987
988 def xmlRead(self, (name, attrs, content), parent):
989 if attrs.has_key("name"):
990 return attrs["name"]
991 encoding = [".notdef"] * 256
992 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000993 if isinstance(element, basestring):
jvrb9702ba2003-01-03 20:56:01 +0000994 continue
995 name, attrs, content = element
996 code = safeEval(attrs["code"])
997 glyphName = attrs["name"]
998 encoding[code] = glyphName
999 return encoding
1000
1001
1002def parseEncoding0(charset, file, haveSupplement, strings):
1003 nCodes = readCard8(file)
1004 encoding = [".notdef"] * 256
1005 for glyphID in range(1, nCodes + 1):
1006 code = readCard8(file)
1007 if code != 0:
1008 encoding[code] = charset[glyphID]
1009 return encoding
1010
1011def parseEncoding1(charset, file, haveSupplement, strings):
1012 nRanges = readCard8(file)
1013 encoding = [".notdef"] * 256
1014 glyphID = 1
1015 for i in range(nRanges):
1016 code = readCard8(file)
1017 nLeft = readCard8(file)
1018 for glyphID in range(glyphID, glyphID + nLeft + 1):
1019 encoding[code] = charset[glyphID]
1020 code = code + 1
1021 glyphID = glyphID + 1
1022 return encoding
1023
1024def packEncoding0(charset, encoding, strings):
1025 format = 0
1026 m = {}
1027 for code in range(len(encoding)):
1028 name = encoding[code]
1029 if name != ".notdef":
1030 m[name] = code
1031 codes = []
1032 for name in charset[1:]:
1033 code = m.get(name)
1034 codes.append(code)
1035
1036 while codes and codes[-1] is None:
1037 codes.pop()
1038
1039 data = [packCard8(format), packCard8(len(codes))]
1040 for code in codes:
1041 if code is None:
1042 code = 0
1043 data.append(packCard8(code))
1044 return "".join(data)
1045
1046def packEncoding1(charset, encoding, strings):
1047 format = 1
1048 m = {}
1049 for code in range(len(encoding)):
1050 name = encoding[code]
1051 if name != ".notdef":
1052 m[name] = code
1053 ranges = []
1054 first = None
1055 end = 0
1056 for name in charset[1:]:
1057 code = m.get(name, -1)
1058 if first is None:
1059 first = code
1060 elif end + 1 <> code:
1061 nLeft = end - first
1062 ranges.append((first, nLeft))
1063 first = code
1064 end = code
1065 nLeft = end - first
1066 ranges.append((first, nLeft))
1067
1068 # remove unencoded glyphs at the end.
1069 while ranges and ranges[-1][0] == -1:
1070 ranges.pop()
1071
1072 data = [packCard8(format), packCard8(len(ranges))]
1073 for first, nLeft in ranges:
1074 if first == -1: # unencoded
1075 first = 0
1076 data.append(packCard8(first) + packCard8(nLeft))
1077 return "".join(data)
1078
1079
jvr4e5af602002-05-24 09:58:04 +00001080class FDArrayConverter(TableConverter):
jvred101512003-08-22 19:53:32 +00001081
jvra2ad5442002-05-17 18:36:07 +00001082 def read(self, parent, value):
1083 file = parent.file
1084 file.seek(value)
jvred101512003-08-22 19:53:32 +00001085 fdArray = FDArrayIndex(file)
jvra2ad5442002-05-17 18:36:07 +00001086 fdArray.strings = parent.strings
1087 fdArray.GlobalSubrs = parent.GlobalSubrs
1088 return fdArray
1089
jvred101512003-08-22 19:53:32 +00001090 def write(self, parent, value):
1091 return 0 # dummy value
1092
1093 def xmlRead(self, (name, attrs, content), parent):
1094 fdArray = FDArrayIndex()
1095 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +00001096 if isinstance(element, basestring):
jvred101512003-08-22 19:53:32 +00001097 continue
1098 fdArray.fromXML(element)
1099 return fdArray
1100
jvra2ad5442002-05-17 18:36:07 +00001101
1102class FDSelectConverter:
jvred101512003-08-22 19:53:32 +00001103
jvra2ad5442002-05-17 18:36:07 +00001104 def read(self, parent, value):
1105 file = parent.file
1106 file.seek(value)
jvred101512003-08-22 19:53:32 +00001107 fdSelect = FDSelect(file, parent.numGlyphs)
1108 return fdSelect
1109
1110 def write(self, parent, value):
1111 return 0 # dummy value
1112
1113 # The FDSelect glyph data is written out to XML in the charstring keys,
1114 # so we write out only the format selector
jvr7ce0a132002-07-23 16:42:11 +00001115 def xmlWrite(self, xmlWriter, name, value, progress):
jvred101512003-08-22 19:53:32 +00001116 xmlWriter.simpletag(name, [('format', value.format)])
1117 xmlWriter.newline()
1118
1119 def xmlRead(self, (name, attrs, content), parent):
1120 format = safeEval(attrs["format"])
1121 file = None
1122 numGlyphs = None
1123 fdSelect = FDSelect(file, numGlyphs, format)
1124 return fdSelect
1125
1126
1127def packFDSelect0(fdSelectArray):
1128 format = 0
1129 data = [packCard8(format)]
1130 for index in fdSelectArray:
1131 data.append(packCard8(index))
1132 return "".join(data)
1133
1134
1135def packFDSelect3(fdSelectArray):
1136 format = 3
1137 fdRanges = []
1138 first = None
1139 end = 0
1140 lenArray = len(fdSelectArray)
1141 lastFDIndex = -1
1142 for i in range(lenArray):
1143 fdIndex = fdSelectArray[i]
1144 if lastFDIndex != fdIndex:
1145 fdRanges.append([i, fdIndex])
1146 lastFDIndex = fdIndex
1147 sentinelGID = i + 1
1148
1149 data = [packCard8(format)]
1150 data.append(packCard16( len(fdRanges) ))
1151 for fdRange in fdRanges:
1152 data.append(packCard16(fdRange[0]))
1153 data.append(packCard8(fdRange[1]))
1154 data.append(packCard16(sentinelGID))
1155 return "".join(data)
1156
1157
1158class FDSelectCompiler:
1159
1160 def __init__(self, fdSelect, parent):
1161 format = fdSelect.format
1162 fdSelectArray = fdSelect.gidArray
1163 if format == 0:
1164 self.data = packFDSelect0(fdSelectArray)
1165 elif format == 3:
1166 self.data = packFDSelect3(fdSelectArray)
1167 else:
1168 # choose smaller of the two formats
1169 data0 = packFDSelect0(fdSelectArray)
1170 data3 = packFDSelect3(fdSelectArray)
1171 if len(data0) < len(data3):
1172 self.data = data0
1173 fdSelect.format = 0
1174 else:
1175 self.data = data3
1176 fdSelect.format = 3
1177
1178 self.parent = parent
1179
1180 def setPos(self, pos, endPos):
1181 self.parent.rawDict["FDSelect"] = pos
1182
1183 def getDataLength(self):
1184 return len(self.data)
1185
1186 def toFile(self, file):
1187 file.write(self.data)
jvra2ad5442002-05-17 18:36:07 +00001188
1189
jvr4e5af602002-05-24 09:58:04 +00001190class ROSConverter(SimpleConverter):
jvred101512003-08-22 19:53:32 +00001191
jvr7ce0a132002-07-23 16:42:11 +00001192 def xmlWrite(self, xmlWriter, name, value, progress):
jvr155aa752002-05-17 19:58:49 +00001193 registry, order, supplement = value
jvrf2cf9c52002-05-23 21:50:36 +00001194 xmlWriter.simpletag(name, [('Registry', registry), ('Order', order),
1195 ('Supplement', supplement)])
jvr4afb2572002-05-18 20:07:01 +00001196 xmlWriter.newline()
jvr155aa752002-05-17 19:58:49 +00001197
jvred101512003-08-22 19:53:32 +00001198 def xmlRead(self, (name, attrs, content), parent):
1199 return (attrs['Registry'], attrs['Order'], safeEval(attrs['Supplement']))
1200
1201
jvr155aa752002-05-17 19:58:49 +00001202
jvr4756b3a2002-05-16 18:17:32 +00001203topDictOperators = [
1204# opcode name argument type default converter
jvr155aa752002-05-17 19:58:49 +00001205 ((12, 30), 'ROS', ('SID','SID','number'), None, ROSConverter()),
jvrf2cf9c52002-05-23 21:50:36 +00001206 ((12, 20), 'SyntheticBase', 'number', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001207 (0, 'version', 'SID', None, None),
jvre2ca9b52002-09-09 14:18:39 +00001208 (1, 'Notice', 'SID', None, Latin1Converter()),
1209 ((12, 0), 'Copyright', 'SID', None, Latin1Converter()),
jvr4756b3a2002-05-16 18:17:32 +00001210 (2, 'FullName', 'SID', None, None),
jvr155aa752002-05-17 19:58:49 +00001211 ((12, 38), 'FontName', 'SID', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001212 (3, 'FamilyName', 'SID', None, None),
1213 (4, 'Weight', 'SID', None, None),
1214 ((12, 1), 'isFixedPitch', 'number', 0, None),
1215 ((12, 2), 'ItalicAngle', 'number', 0, None),
1216 ((12, 3), 'UnderlinePosition', 'number', None, None),
1217 ((12, 4), 'UnderlineThickness', 'number', 50, None),
1218 ((12, 5), 'PaintType', 'number', 0, None),
1219 ((12, 6), 'CharstringType', 'number', 2, None),
1220 ((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0], None),
1221 (13, 'UniqueID', 'number', None, None),
1222 (5, 'FontBBox', 'array', [0,0,0,0], None),
1223 ((12, 8), 'StrokeWidth', 'number', 0, None),
1224 (14, 'XUID', 'array', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001225 ((12, 21), 'PostScript', 'SID', None, None),
1226 ((12, 22), 'BaseFontName', 'SID', None, None),
1227 ((12, 23), 'BaseFontBlend', 'delta', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001228 ((12, 31), 'CIDFontVersion', 'number', 0, None),
1229 ((12, 32), 'CIDFontRevision', 'number', 0, None),
1230 ((12, 33), 'CIDFontType', 'number', 0, None),
1231 ((12, 34), 'CIDCount', 'number', 8720, None),
jvred101512003-08-22 19:53:32 +00001232 (15, 'charset', 'number', 0, CharsetConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001233 ((12, 35), 'UIDBase', 'number', None, None),
jvrb9702ba2003-01-03 20:56:01 +00001234 (16, 'Encoding', 'number', 0, EncodingConverter()),
jvr155aa752002-05-17 19:58:49 +00001235 (18, 'Private', ('number','number'), None, PrivateDictConverter()),
jvred101512003-08-22 19:53:32 +00001236 ((12, 37), 'FDSelect', 'number', None, FDSelectConverter()),
1237 ((12, 36), 'FDArray', 'number', None, FDArrayConverter()),
jvr155aa752002-05-17 19:58:49 +00001238 (17, 'CharStrings', 'number', None, CharStringsConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001239]
1240
jvred101512003-08-22 19:53:32 +00001241# Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order,
1242# in order for the font to compile back from xml.
1243
1244
jvr4756b3a2002-05-16 18:17:32 +00001245privateDictOperators = [
1246# opcode name argument type default converter
1247 (6, 'BlueValues', 'delta', None, None),
1248 (7, 'OtherBlues', 'delta', None, None),
1249 (8, 'FamilyBlues', 'delta', None, None),
1250 (9, 'FamilyOtherBlues', 'delta', None, None),
1251 ((12, 9), 'BlueScale', 'number', 0.039625, None),
1252 ((12, 10), 'BlueShift', 'number', 7, None),
1253 ((12, 11), 'BlueFuzz', 'number', 1, None),
1254 (10, 'StdHW', 'number', None, None),
1255 (11, 'StdVW', 'number', None, None),
1256 ((12, 12), 'StemSnapH', 'delta', None, None),
1257 ((12, 13), 'StemSnapV', 'delta', None, None),
1258 ((12, 14), 'ForceBold', 'number', 0, None),
jvrf2cf9c52002-05-23 21:50:36 +00001259 ((12, 15), 'ForceBoldThreshold', 'number', None, None), # deprecated
1260 ((12, 16), 'lenIV', 'number', None, None), # deprecated
jvr4756b3a2002-05-16 18:17:32 +00001261 ((12, 17), 'LanguageGroup', 'number', 0, None),
1262 ((12, 18), 'ExpansionFactor', 'number', 0.06, None),
1263 ((12, 19), 'initialRandomSeed', 'number', 0, None),
1264 (20, 'defaultWidthX', 'number', 0, None),
1265 (21, 'nominalWidthX', 'number', 0, None),
1266 (19, 'Subrs', 'number', None, SubrsConverter()),
1267]
1268
jvr4e5af602002-05-24 09:58:04 +00001269def addConverters(table):
1270 for i in range(len(table)):
1271 op, name, arg, default, conv = table[i]
1272 if conv is not None:
1273 continue
1274 if arg in ("delta", "array"):
1275 conv = ArrayConverter()
1276 elif arg == "number":
1277 conv = NumberConverter()
1278 elif arg == "SID":
1279 conv = SimpleConverter()
1280 else:
1281 assert 0
1282 table[i] = op, name, arg, default, conv
1283
1284addConverters(privateDictOperators)
1285addConverters(topDictOperators)
1286
jvr4756b3a2002-05-16 18:17:32 +00001287
1288class TopDictDecompiler(psCharStrings.DictDecompiler):
1289 operators = buildOperatorDict(topDictOperators)
1290
1291
1292class PrivateDictDecompiler(psCharStrings.DictDecompiler):
1293 operators = buildOperatorDict(privateDictOperators)
1294
1295
jvrf2cf9c52002-05-23 21:50:36 +00001296class DictCompiler:
1297
1298 def __init__(self, dictObj, strings, parent):
1299 assert isinstance(strings, IndexedStrings)
1300 self.dictObj = dictObj
1301 self.strings = strings
1302 self.parent = parent
1303 rawDict = {}
1304 for name in dictObj.order:
1305 value = getattr(dictObj, name, None)
1306 if value is None:
1307 continue
1308 conv = dictObj.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001309 value = conv.write(dictObj, value)
jvrf2cf9c52002-05-23 21:50:36 +00001310 if value == dictObj.defaults.get(name):
1311 continue
1312 rawDict[name] = value
1313 self.rawDict = rawDict
1314
jvr4e5af602002-05-24 09:58:04 +00001315 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +00001316 pass
1317
1318 def getDataLength(self):
jvr4e5af602002-05-24 09:58:04 +00001319 return len(self.compile("getDataLength"))
jvrf2cf9c52002-05-23 21:50:36 +00001320
jvr4e5af602002-05-24 09:58:04 +00001321 def compile(self, reason):
1322 if DEBUG:
1323 print "-- compiling %s for %s" % (self.__class__.__name__, reason)
jvred101512003-08-22 19:53:32 +00001324 print "in baseDict: ", self
jvrf2cf9c52002-05-23 21:50:36 +00001325 rawDict = self.rawDict
1326 data = []
1327 for name in self.dictObj.order:
1328 value = rawDict.get(name)
1329 if value is None:
1330 continue
1331 op, argType = self.opcodes[name]
jvr2a9bcde2008-03-07 19:56:17 +00001332 if isinstance(argType, tuple):
jvrf2cf9c52002-05-23 21:50:36 +00001333 l = len(argType)
1334 assert len(value) == l, "value doesn't match arg type"
1335 for i in range(l):
jvred101512003-08-22 19:53:32 +00001336 arg = argType[i]
jvrf2cf9c52002-05-23 21:50:36 +00001337 v = value[i]
1338 arghandler = getattr(self, "arg_" + arg)
1339 data.append(arghandler(v))
1340 else:
1341 arghandler = getattr(self, "arg_" + argType)
1342 data.append(arghandler(value))
1343 data.append(op)
1344 return "".join(data)
1345
1346 def toFile(self, file):
jvr4e5af602002-05-24 09:58:04 +00001347 file.write(self.compile("toFile"))
jvrf2cf9c52002-05-23 21:50:36 +00001348
1349 def arg_number(self, num):
1350 return encodeNumber(num)
1351 def arg_SID(self, s):
1352 return psCharStrings.encodeIntCFF(self.strings.getSID(s))
1353 def arg_array(self, value):
1354 data = []
1355 for num in value:
1356 data.append(encodeNumber(num))
1357 return "".join(data)
1358 def arg_delta(self, value):
1359 out = []
1360 last = 0
1361 for v in value:
1362 out.append(v - last)
1363 last = v
1364 data = []
1365 for num in out:
1366 data.append(encodeNumber(num))
1367 return "".join(data)
1368
1369
1370def encodeNumber(num):
jvrf6ff48b2008-03-07 19:49:25 +00001371 if isinstance(num, float):
jvrf2cf9c52002-05-23 21:50:36 +00001372 return psCharStrings.encodeFloat(num)
1373 else:
1374 return psCharStrings.encodeIntCFF(num)
1375
1376
1377class TopDictCompiler(DictCompiler):
1378
1379 opcodes = buildOpcodeDict(topDictOperators)
1380
1381 def getChildren(self, strings):
1382 children = []
jvred101512003-08-22 19:53:32 +00001383 if hasattr(self.dictObj, "charset") and self.dictObj.charset:
jvrf2cf9c52002-05-23 21:50:36 +00001384 children.append(CharsetCompiler(strings, self.dictObj.charset, self))
jvrb9702ba2003-01-03 20:56:01 +00001385 if hasattr(self.dictObj, "Encoding"):
1386 encoding = self.dictObj.Encoding
jvr2a9bcde2008-03-07 19:56:17 +00001387 if not isinstance(encoding, basestring):
jvrb9702ba2003-01-03 20:56:01 +00001388 children.append(EncodingCompiler(strings, encoding, self))
jvrce522412003-08-25 07:37:25 +00001389 if hasattr(self.dictObj, "FDSelect"):
jvred101512003-08-22 19:53:32 +00001390 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1391 # issues about merging the FDArrays. Here I assume that
1392 # either the font was read from XML, and teh FDSelect indices are all
1393 # in the charstring data, or the FDSelect array is already fully defined.
1394 fdSelect = self.dictObj.FDSelect
1395 if len(fdSelect) == 0: # probably read in from XML; assume fdIndex in CharString data
1396 charStrings = self.dictObj.CharStrings
1397 for name in self.dictObj.charset:
1398 charstring = charStrings[name]
1399 fdSelect.append(charStrings[name].fdSelectIndex)
1400 fdSelectComp = FDSelectCompiler(fdSelect, self)
1401 children.append(fdSelectComp)
jvrf2cf9c52002-05-23 21:50:36 +00001402 if hasattr(self.dictObj, "CharStrings"):
1403 items = []
1404 charStrings = self.dictObj.CharStrings
1405 for name in self.dictObj.charset:
1406 items.append(charStrings[name])
1407 charStringsComp = CharStringsCompiler(items, strings, self)
1408 children.append(charStringsComp)
jvrce522412003-08-25 07:37:25 +00001409 if hasattr(self.dictObj, "FDArray"):
jvred101512003-08-22 19:53:32 +00001410 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1411 # issues about merging the FDArrays. Here I assume that the FDArray info is correct
1412 # and complete.
1413 fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self)
1414 children.append(fdArrayIndexComp)
1415 children.extend(fdArrayIndexComp.getChildren(strings))
1416 if hasattr(self.dictObj, "Private"):
1417 privComp = self.dictObj.Private.getCompiler(strings, self)
1418 children.append(privComp)
1419 children.extend(privComp.getChildren(strings))
1420 return children
1421
1422
1423class FontDictCompiler(DictCompiler):
1424
1425 opcodes = buildOpcodeDict(topDictOperators)
1426
1427 def getChildren(self, strings):
1428 children = []
jvrf2cf9c52002-05-23 21:50:36 +00001429 if hasattr(self.dictObj, "Private"):
1430 privComp = self.dictObj.Private.getCompiler(strings, self)
1431 children.append(privComp)
1432 children.extend(privComp.getChildren(strings))
1433 return children
1434
1435
1436class PrivateDictCompiler(DictCompiler):
1437
1438 opcodes = buildOpcodeDict(privateDictOperators)
1439
jvr4e5af602002-05-24 09:58:04 +00001440 def setPos(self, pos, endPos):
1441 size = endPos - pos
jvrf2cf9c52002-05-23 21:50:36 +00001442 self.parent.rawDict["Private"] = size, pos
1443 self.pos = pos
1444
1445 def getChildren(self, strings):
1446 children = []
1447 if hasattr(self.dictObj, "Subrs"):
1448 children.append(self.dictObj.Subrs.getCompiler(strings, self))
1449 return children
1450
jvr4756b3a2002-05-16 18:17:32 +00001451
1452class BaseDict:
1453
jvr4e5af602002-05-24 09:58:04 +00001454 def __init__(self, strings=None, file=None, offset=None):
jvr4756b3a2002-05-16 18:17:32 +00001455 self.rawDict = {}
jvr767102e2002-05-17 07:06:32 +00001456 if DEBUG:
jvrf2cf9c52002-05-23 21:50:36 +00001457 print "loading %s at %s" % (self.__class__.__name__, offset)
jvr4756b3a2002-05-16 18:17:32 +00001458 self.file = file
1459 self.offset = offset
1460 self.strings = strings
jvr155aa752002-05-17 19:58:49 +00001461 self.skipNames = []
jvr4756b3a2002-05-16 18:17:32 +00001462
1463 def decompile(self, data):
jvrf2cf9c52002-05-23 21:50:36 +00001464 if DEBUG:
1465 print " length %s is %s" % (self.__class__.__name__, len(data))
1466 dec = self.decompilerClass(self.strings)
jvr4756b3a2002-05-16 18:17:32 +00001467 dec.decompile(data)
1468 self.rawDict = dec.getDict()
1469 self.postDecompile()
1470
1471 def postDecompile(self):
1472 pass
1473
jvrf2cf9c52002-05-23 21:50:36 +00001474 def getCompiler(self, strings, parent):
1475 return self.compilerClass(self, strings, parent)
1476
jvr4756b3a2002-05-16 18:17:32 +00001477 def __getattr__(self, name):
1478 value = self.rawDict.get(name)
1479 if value is None:
1480 value = self.defaults.get(name)
1481 if value is None:
1482 raise AttributeError, name
1483 conv = self.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001484 value = conv.read(self, value)
jvr4756b3a2002-05-16 18:17:32 +00001485 setattr(self, name, value)
1486 return value
1487
1488 def toXML(self, xmlWriter, progress):
1489 for name in self.order:
jvr155aa752002-05-17 19:58:49 +00001490 if name in self.skipNames:
1491 continue
jvr4756b3a2002-05-16 18:17:32 +00001492 value = getattr(self, name, None)
1493 if value is None:
1494 continue
jvr4e5af602002-05-24 09:58:04 +00001495 conv = self.converters[name]
jvr7ce0a132002-07-23 16:42:11 +00001496 conv.xmlWrite(xmlWriter, name, value, progress)
jvr4e5af602002-05-24 09:58:04 +00001497
1498 def fromXML(self, (name, attrs, content)):
1499 conv = self.converters[name]
1500 value = conv.xmlRead((name, attrs, content), self)
1501 setattr(self, name, value)
jvr4756b3a2002-05-16 18:17:32 +00001502
1503
1504class TopDict(BaseDict):
1505
1506 defaults = buildDefaults(topDictOperators)
1507 converters = buildConverters(topDictOperators)
1508 order = buildOrder(topDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001509 decompilerClass = TopDictDecompiler
1510 compilerClass = TopDictCompiler
Just7842e561999-12-16 21:34:53 +00001511
jvr4e5af602002-05-24 09:58:04 +00001512 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
jvr016ca762002-05-16 18:38:03 +00001513 BaseDict.__init__(self, strings, file, offset)
1514 self.GlobalSubrs = GlobalSubrs
1515
Just7842e561999-12-16 21:34:53 +00001516 def getGlyphOrder(self):
1517 return self.charset
1518
jvr4756b3a2002-05-16 18:17:32 +00001519 def postDecompile(self):
1520 offset = self.rawDict.get("CharStrings")
1521 if offset is None:
1522 return
1523 # get the number of glyphs beforehand.
1524 self.file.seek(offset)
jvra2ad5442002-05-17 18:36:07 +00001525 self.numGlyphs = readCard16(self.file)
Just7842e561999-12-16 21:34:53 +00001526
jvr016ca762002-05-16 18:38:03 +00001527 def toXML(self, xmlWriter, progress):
jvr155aa752002-05-17 19:58:49 +00001528 if hasattr(self, "CharStrings"):
jvr7ce0a132002-07-23 16:42:11 +00001529 self.decompileAllCharStrings(progress)
jvred101512003-08-22 19:53:32 +00001530 if hasattr(self, "ROS"):
1531 self.skipNames = ['Encoding']
jvr155aa752002-05-17 19:58:49 +00001532 if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
1533 # these values have default values, but I only want them to show up
1534 # in CID fonts.
1535 self.skipNames = ['CIDFontVersion', 'CIDFontRevision', 'CIDFontType',
1536 'CIDCount']
jvr016ca762002-05-16 18:38:03 +00001537 BaseDict.toXML(self, xmlWriter, progress)
jvr767102e2002-05-17 07:06:32 +00001538
jvr7ce0a132002-07-23 16:42:11 +00001539 def decompileAllCharStrings(self, progress):
jvr4e5af602002-05-24 09:58:04 +00001540 # XXX only when doing ttdump -i?
jvr7ce0a132002-07-23 16:42:11 +00001541 i = 0
jvra2ad5442002-05-17 18:36:07 +00001542 for charString in self.CharStrings.values():
jvred101512003-08-22 19:53:32 +00001543 try:
1544 charString.decompile()
1545 except:
1546 print "Error in charstring ", i
1547 import sys
1548 type, value = sys. exc_info()[0:2]
1549 raise type(value)
jvr7ce0a132002-07-23 16:42:11 +00001550 if not i % 30 and progress:
1551 progress.increment(0) # update
1552 i = i + 1
Just7842e561999-12-16 21:34:53 +00001553
1554
jvred101512003-08-22 19:53:32 +00001555class FontDict(BaseDict):
1556
1557 defaults = buildDefaults(topDictOperators)
1558 converters = buildConverters(topDictOperators)
1559 order = buildOrder(topDictOperators)
1560 decompilerClass = None
1561 compilerClass = FontDictCompiler
1562
1563 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
1564 BaseDict.__init__(self, strings, file, offset)
1565 self.GlobalSubrs = GlobalSubrs
1566
1567 def getGlyphOrder(self):
1568 return self.charset
1569
1570 def toXML(self, xmlWriter, progress):
1571 self.skipNames = ['Encoding']
1572 BaseDict.toXML(self, xmlWriter, progress)
1573
1574
1575
jvr4756b3a2002-05-16 18:17:32 +00001576class PrivateDict(BaseDict):
1577 defaults = buildDefaults(privateDictOperators)
1578 converters = buildConverters(privateDictOperators)
1579 order = buildOrder(privateDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001580 decompilerClass = PrivateDictDecompiler
1581 compilerClass = PrivateDictCompiler
Just7842e561999-12-16 21:34:53 +00001582
1583
jvre3275582002-05-14 12:22:03 +00001584class IndexedStrings:
1585
jvr767102e2002-05-17 07:06:32 +00001586 """SID -> string mapping."""
1587
1588 def __init__(self, file=None):
1589 if file is None:
jvre3275582002-05-14 12:22:03 +00001590 strings = []
jvr767102e2002-05-17 07:06:32 +00001591 else:
jvr4e5af602002-05-24 09:58:04 +00001592 strings = list(Index(file))
jvre3275582002-05-14 12:22:03 +00001593 self.strings = strings
1594
jvrf2cf9c52002-05-23 21:50:36 +00001595 def getCompiler(self):
1596 return IndexedStringsCompiler(self, None, None)
1597
1598 def __len__(self):
1599 return len(self.strings)
1600
jvre3275582002-05-14 12:22:03 +00001601 def __getitem__(self, SID):
1602 if SID < cffStandardStringCount:
1603 return cffStandardStrings[SID]
1604 else:
1605 return self.strings[SID - cffStandardStringCount]
1606
1607 def getSID(self, s):
1608 if not hasattr(self, "stringMapping"):
1609 self.buildStringMapping()
1610 if cffStandardStringMapping.has_key(s):
1611 SID = cffStandardStringMapping[s]
jvrf2cf9c52002-05-23 21:50:36 +00001612 elif self.stringMapping.has_key(s):
jvre3275582002-05-14 12:22:03 +00001613 SID = self.stringMapping[s]
1614 else:
1615 SID = len(self.strings) + cffStandardStringCount
1616 self.strings.append(s)
1617 self.stringMapping[s] = SID
1618 return SID
1619
1620 def getStrings(self):
1621 return self.strings
1622
1623 def buildStringMapping(self):
1624 self.stringMapping = {}
1625 for index in range(len(self.strings)):
1626 self.stringMapping[self.strings[index]] = index + cffStandardStringCount
1627
1628
Just7842e561999-12-16 21:34:53 +00001629# The 391 Standard Strings as used in the CFF format.
1630# from Adobe Technical None #5176, version 1.0, 18 March 1998
1631
1632cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
1633 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
1634 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
1635 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
1636 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C',
1637 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
1638 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
1639 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
1640 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
1641 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
1642 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
1643 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
1644 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
1645 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
1646 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
1647 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
1648 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
1649 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
1650 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
1651 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
1652 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
1653 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
1654 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
1655 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
1656 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
1657 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
1658 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
1659 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
1660 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
1661 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
1662 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
1663 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
1664 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
1665 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
1666 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
1667 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
1668 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
1669 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
1670 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
1671 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
1672 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
1673 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
1674 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
1675 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
1676 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
1677 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
1678 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
1679 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
1680 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
1681 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
1682 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
1683 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
1684 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
1685 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
1686 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
1687 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
1688 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
1689 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
1690 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
1691 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
1692 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
1693 '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
1694 'Semibold'
1695]
1696
1697cffStandardStringCount = 391
1698assert len(cffStandardStrings) == cffStandardStringCount
1699# build reverse mapping
1700cffStandardStringMapping = {}
1701for _i in range(cffStandardStringCount):
1702 cffStandardStringMapping[cffStandardStrings[_i]] = _i
jvrc60a44f2006-10-21 13:41:18 +00001703
1704cffISOAdobeStrings = [".notdef", "space", "exclam", "quotedbl", "numbersign",
1705"dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright",
1706"asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two",
1707"three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon",
1708"less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G",
1709"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
1710"X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum",
1711"underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
1712"k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
1713"braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent",
1714"sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle",
1715"quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl",
1716"endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet",
1717"quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis",
1718"perthousand", "questiondown", "grave", "acute", "circumflex", "tilde",
1719"macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
1720"ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE",
1721"ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls",
1722"onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus",
1723"Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn",
1724"threequarters", "twosuperior", "registered", "minus", "eth", "multiply",
1725"threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
1726"Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave",
1727"Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute",
1728"Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute",
1729"Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute",
1730"acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute",
1731"ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis",
1732"igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde",
1733"scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis",
1734"zcaron"]
1735
1736cffISOAdobeStringCount = 229
1737assert len(cffISOAdobeStrings) == cffISOAdobeStringCount
1738
1739cffIExpertStrings = [".notdef", "space", "exclamsmall", "Hungarumlautsmall",
1740"dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall",
1741"parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader",
1742"comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle",
1743"twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle",
1744"sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon",
1745"commasuperior", "threequartersemdash", "periodsuperior", "questionsmall",
1746"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1747"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1748"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1749"parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
1750"Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall",
1751"Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
1752"Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall",
1753"Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall",
1754"exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall",
1755"Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall",
1756"figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall",
1757"onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth",
1758"threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds",
1759"zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior",
1760"fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior",
1761"zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior",
1762"fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior",
1763"centinferior", "dollarinferior", "periodinferior", "commainferior",
1764"Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall",
1765"Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall",
1766"Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall",
1767"Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall",
1768"Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall",
1769"Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall",
1770"Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall",
1771"Ydieresissmall"]
1772
1773cffExpertStringCount = 166
1774assert len(cffIExpertStrings) == cffExpertStringCount
1775
1776cffExpertSubsetStrings = [".notdef", "space", "dollaroldstyle",
1777"dollarsuperior", "parenleftsuperior", "parenrightsuperior", "twodotenleader",
1778"onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle",
1779"oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle",
1780"sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon",
1781"semicolon", "commasuperior", "threequartersemdash", "periodsuperior",
1782"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1783"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1784"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1785"parenrightinferior", "hyphensuperior", "colonmonetary", "onefitted", "rupiah",
1786"centoldstyle", "figuredash", "hypheninferior", "onequarter", "onehalf",
1787"threequarters", "oneeighth", "threeeighths", "fiveeighths", "seveneighths",
1788"onethird", "twothirds", "zerosuperior", "onesuperior", "twosuperior",
1789"threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior",
1790"eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior",
1791"threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior",
1792"eightinferior", "nineinferior", "centinferior", "dollarinferior",
1793"periodinferior", "commainferior"]
1794
1795cffExpertSubsetStringCount = 87
1796assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount