blob: 49b749e59857249af6cbdbaf5df5ecc78c231d60 [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#
jvrf6ff48b2008-03-07 19:49:25 +00004# $Id: cffLib.py,v 1.33 2008-03-07 19:49:25 jvr Exp $
Justec46d161999-12-20 22:02:10 +00005#
Just7842e561999-12-16 21:34:53 +00006
7import struct, sstruct
8import string
jvrf6ff48b2008-03-07 19:49:25 +00009from types import 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
jvr767102e2002-05-17 07:06:32 +000013DEBUG = 0
14
15
Just7842e561999-12-16 21:34:53 +000016cffHeaderFormat = """
17 major: B
18 minor: B
19 hdrSize: B
20 offSize: B
21"""
22
23class CFFFontSet:
24
25 def __init__(self):
jvr4756b3a2002-05-16 18:17:32 +000026 pass
Just7842e561999-12-16 21:34:53 +000027
jvr4e5af602002-05-24 09:58:04 +000028 def decompile(self, file, otFont):
jvra2a75b32002-05-13 11:25:17 +000029 sstruct.unpack(cffHeaderFormat, file.read(4), self)
Just7842e561999-12-16 21:34:53 +000030 assert self.major == 1 and self.minor == 0, \
31 "unknown CFF format: %d.%d" % (self.major, self.minor)
Just7842e561999-12-16 21:34:53 +000032
jvrf2cf9c52002-05-23 21:50:36 +000033 file.seek(self.hdrSize)
jvr4e5af602002-05-24 09:58:04 +000034 self.fontNames = list(Index(file))
jvr4756b3a2002-05-16 18:17:32 +000035 self.topDictIndex = TopDictIndex(file)
jvr767102e2002-05-17 07:06:32 +000036 self.strings = IndexedStrings(file)
jvr4e5af602002-05-24 09:58:04 +000037 self.GlobalSubrs = GlobalSubrsIndex(file)
jvr4756b3a2002-05-16 18:17:32 +000038 self.topDictIndex.strings = self.strings
jvr016ca762002-05-16 18:38:03 +000039 self.topDictIndex.GlobalSubrs = self.GlobalSubrs
jvr4756b3a2002-05-16 18:17:32 +000040
41 def __len__(self):
42 return len(self.fontNames)
43
44 def keys(self):
jvrce522412003-08-25 07:37:25 +000045 return list(self.fontNames)
jvr4756b3a2002-05-16 18:17:32 +000046
jvr767102e2002-05-17 07:06:32 +000047 def values(self):
48 return self.topDictIndex
49
jvr4756b3a2002-05-16 18:17:32 +000050 def __getitem__(self, name):
51 try:
52 index = self.fontNames.index(name)
53 except ValueError:
54 raise KeyError, name
jvr016ca762002-05-16 18:38:03 +000055 return self.topDictIndex[index]
Just7842e561999-12-16 21:34:53 +000056
jvr4e5af602002-05-24 09:58:04 +000057 def compile(self, file, otFont):
Just7842e561999-12-16 21:34:53 +000058 strings = IndexedStrings()
jvrf2cf9c52002-05-23 21:50:36 +000059 writer = CFFWriter()
60 writer.add(sstruct.pack(cffHeaderFormat, self))
61 fontNames = Index()
62 for name in self.fontNames:
63 fontNames.append(name)
64 writer.add(fontNames.getCompiler(strings, None))
65 topCompiler = self.topDictIndex.getCompiler(strings, None)
66 writer.add(topCompiler)
67 writer.add(strings.getCompiler())
68 writer.add(self.GlobalSubrs.getCompiler(strings, None))
69
jvr4e5af602002-05-24 09:58:04 +000070 for topDict in self.topDictIndex:
71 if not hasattr(topDict, "charset") or topDict.charset is None:
72 charset = otFont.getGlyphOrder()
73 topDict.charset = charset
74
jvrf2cf9c52002-05-23 21:50:36 +000075 for child in topCompiler.getChildren(strings):
76 writer.add(child)
77
jvrf2cf9c52002-05-23 21:50:36 +000078 writer.toFile(file)
Just7842e561999-12-16 21:34:53 +000079
80 def toXML(self, xmlWriter, progress=None):
81 xmlWriter.newline()
82 for fontName in self.fontNames:
83 xmlWriter.begintag("CFFFont", name=fontName)
84 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +000085 font = self[fontName]
Just7842e561999-12-16 21:34:53 +000086 font.toXML(xmlWriter, progress)
87 xmlWriter.endtag("CFFFont")
88 xmlWriter.newline()
89 xmlWriter.newline()
90 xmlWriter.begintag("GlobalSubrs")
91 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +000092 self.GlobalSubrs.toXML(xmlWriter, progress)
Just7842e561999-12-16 21:34:53 +000093 xmlWriter.endtag("GlobalSubrs")
94 xmlWriter.newline()
95 xmlWriter.newline()
96
97 def fromXML(self, (name, attrs, content)):
jvr4e5af602002-05-24 09:58:04 +000098 if not hasattr(self, "GlobalSubrs"):
99 self.GlobalSubrs = GlobalSubrsIndex()
100 self.major = 1
101 self.minor = 0
102 self.hdrSize = 4
103 self.offSize = 4 # XXX ??
104 if name == "CFFFont":
105 if not hasattr(self, "fontNames"):
106 self.fontNames = []
107 self.topDictIndex = TopDictIndex()
108 fontName = attrs["name"]
109 topDict = TopDict(GlobalSubrs=self.GlobalSubrs)
110 topDict.charset = None # gets filled in later
111 self.fontNames.append(fontName)
112 self.topDictIndex.append(topDict)
113 for element in content:
114 if isinstance(element, StringType):
115 continue
116 topDict.fromXML(element)
117 elif name == "GlobalSubrs":
118 for element in content:
119 if isinstance(element, StringType):
120 continue
121 name, attrs, content = element
jvr489d76a2003-08-24 19:56:16 +0000122 subr = psCharStrings.T2CharString()
jvr4e5af602002-05-24 09:58:04 +0000123 subr.fromXML((name, attrs, content))
124 self.GlobalSubrs.append(subr)
Just7842e561999-12-16 21:34:53 +0000125
126
jvrf2cf9c52002-05-23 21:50:36 +0000127class CFFWriter:
128
129 def __init__(self):
130 self.data = []
131
132 def add(self, table):
133 self.data.append(table)
134
135 def toFile(self, file):
136 lastPosList = None
137 count = 1
138 while 1:
jvr4e5af602002-05-24 09:58:04 +0000139 if DEBUG:
140 print "CFFWriter.toFile() iteration:", count
141 count = count + 1
jvrf2cf9c52002-05-23 21:50:36 +0000142 pos = 0
143 posList = [pos]
144 for item in self.data:
jvrf2cf9c52002-05-23 21:50:36 +0000145 if hasattr(item, "getDataLength"):
jvr4e5af602002-05-24 09:58:04 +0000146 endPos = pos + item.getDataLength()
jvrf2cf9c52002-05-23 21:50:36 +0000147 else:
jvr4e5af602002-05-24 09:58:04 +0000148 endPos = pos + len(item)
149 if hasattr(item, "setPos"):
150 item.setPos(pos, endPos)
151 pos = endPos
jvrf2cf9c52002-05-23 21:50:36 +0000152 posList.append(pos)
153 if posList == lastPosList:
154 break
155 lastPosList = posList
jvr4e5af602002-05-24 09:58:04 +0000156 if DEBUG:
157 print "CFFWriter.toFile() writing to file."
jvrf2cf9c52002-05-23 21:50:36 +0000158 begin = file.tell()
159 posList = [0]
160 for item in self.data:
161 if hasattr(item, "toFile"):
162 item.toFile(file)
163 else:
164 file.write(item)
165 posList.append(file.tell() - begin)
jvrf2cf9c52002-05-23 21:50:36 +0000166 assert posList == lastPosList
167
168
169def calcOffSize(largestOffset):
170 if largestOffset < 0x100:
171 offSize = 1
172 elif largestOffset < 0x10000:
173 offSize = 2
174 elif largestOffset < 0x1000000:
175 offSize = 3
176 else:
177 offSize = 4
178 return offSize
179
180
181class IndexCompiler:
182
183 def __init__(self, items, strings, parent):
184 self.items = self.getItems(items, strings)
185 self.parent = parent
186
187 def getItems(self, items, strings):
188 return items
189
190 def getOffsets(self):
191 pos = 1
192 offsets = [pos]
193 for item in self.items:
194 if hasattr(item, "getDataLength"):
195 pos = pos + item.getDataLength()
196 else:
197 pos = pos + len(item)
198 offsets.append(pos)
199 return offsets
200
201 def getDataLength(self):
202 lastOffset = self.getOffsets()[-1]
203 offSize = calcOffSize(lastOffset)
204 dataLength = (
205 2 + # count
206 1 + # offSize
207 (len(self.items) + 1) * offSize + # the offsets
208 lastOffset - 1 # size of object data
209 )
210 return dataLength
211
212 def toFile(self, file):
jvrf2cf9c52002-05-23 21:50:36 +0000213 offsets = self.getOffsets()
214 writeCard16(file, len(self.items))
215 offSize = calcOffSize(offsets[-1])
216 writeCard8(file, offSize)
217 offSize = -offSize
218 pack = struct.pack
219 for offset in offsets:
220 binOffset = pack(">l", offset)[offSize:]
221 assert len(binOffset) == -offSize
222 file.write(binOffset)
223 for item in self.items:
224 if hasattr(item, "toFile"):
225 item.toFile(file)
226 else:
227 file.write(item)
jvrf2cf9c52002-05-23 21:50:36 +0000228
229
230class IndexedStringsCompiler(IndexCompiler):
231
232 def getItems(self, items, strings):
233 return items.strings
234
235
236class TopDictIndexCompiler(IndexCompiler):
237
238 def getItems(self, items, strings):
239 out = []
240 for item in items:
241 out.append(item.getCompiler(strings, self))
242 return out
243
244 def getChildren(self, strings):
245 children = []
246 for topDict in self.items:
247 children.extend(topDict.getChildren(strings))
248 return children
249
250
jvred101512003-08-22 19:53:32 +0000251class FDArrayIndexCompiler(IndexCompiler):
252
253 def getItems(self, items, strings):
254 out = []
255 for item in items:
256 out.append(item.getCompiler(strings, self))
257 return out
258
259 def getChildren(self, strings):
260 children = []
261 for fontDict in self.items:
262 children.extend(fontDict.getChildren(strings))
263 return children
264
jvred101512003-08-22 19:53:32 +0000265 def toFile(self, file):
266 offsets = self.getOffsets()
267 writeCard16(file, len(self.items))
268 offSize = calcOffSize(offsets[-1])
269 writeCard8(file, offSize)
270 offSize = -offSize
271 pack = struct.pack
272 for offset in offsets:
273 binOffset = pack(">l", offset)[offSize:]
274 assert len(binOffset) == -offSize
275 file.write(binOffset)
276 for item in self.items:
277 if hasattr(item, "toFile"):
278 item.toFile(file)
279 else:
280 file.write(item)
281
282 def setPos(self, pos, endPos):
283 self.parent.rawDict["FDArray"] = pos
284
285
jvrf2cf9c52002-05-23 21:50:36 +0000286class GlobalSubrsCompiler(IndexCompiler):
287 def getItems(self, items, strings):
288 out = []
289 for cs in items:
290 cs.compile()
291 out.append(cs.bytecode)
292 return out
293
294class SubrsCompiler(GlobalSubrsCompiler):
jvr4e5af602002-05-24 09:58:04 +0000295 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000296 offset = pos - self.parent.pos
297 self.parent.rawDict["Subrs"] = offset
298
299class CharStringsCompiler(GlobalSubrsCompiler):
jvr4e5af602002-05-24 09:58:04 +0000300 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000301 self.parent.rawDict["CharStrings"] = pos
302
303
jvr4756b3a2002-05-16 18:17:32 +0000304class Index:
Just7842e561999-12-16 21:34:53 +0000305
jvr4756b3a2002-05-16 18:17:32 +0000306 """This class represents what the CFF spec calls an INDEX."""
Just7842e561999-12-16 21:34:53 +0000307
jvrf2cf9c52002-05-23 21:50:36 +0000308 compilerClass = IndexCompiler
309
jvr4e5af602002-05-24 09:58:04 +0000310 def __init__(self, file=None):
311 name = self.__class__.__name__
jvrf2cf9c52002-05-23 21:50:36 +0000312 if file is None:
313 self.items = []
314 return
jvr767102e2002-05-17 07:06:32 +0000315 if DEBUG:
316 print "loading %s at %s" % (name, file.tell())
jvr4756b3a2002-05-16 18:17:32 +0000317 self.file = file
jvra2ad5442002-05-17 18:36:07 +0000318 count = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000319 self.count = count
320 self.items = [None] * count
321 if count == 0:
jvrf2cf9c52002-05-23 21:50:36 +0000322 self.items = []
jvr4756b3a2002-05-16 18:17:32 +0000323 return
jvra2ad5442002-05-17 18:36:07 +0000324 offSize = readCard8(file)
jvr767102e2002-05-17 07:06:32 +0000325 if DEBUG:
jvrf2cf9c52002-05-23 21:50:36 +0000326 print " index count: %s offSize: %s" % (count, offSize)
jvr767102e2002-05-17 07:06:32 +0000327 assert offSize <= 4, "offSize too large: %s" % offSize
jvr4756b3a2002-05-16 18:17:32 +0000328 self.offsets = offsets = []
329 pad = '\0' * (4 - offSize)
330 for index in range(count+1):
331 chunk = file.read(offSize)
332 chunk = pad + chunk
333 offset, = struct.unpack(">L", chunk)
334 offsets.append(int(offset))
335 self.offsetBase = file.tell() - 1
336 file.seek(self.offsetBase + offsets[-1]) # pretend we've read the whole lot
jvrf2cf9c52002-05-23 21:50:36 +0000337 if DEBUG:
338 print " end of %s at %s" % (name, file.tell())
Just7842e561999-12-16 21:34:53 +0000339
jvr4756b3a2002-05-16 18:17:32 +0000340 def __len__(self):
jvrf2cf9c52002-05-23 21:50:36 +0000341 return len(self.items)
jvra2a75b32002-05-13 11:25:17 +0000342
jvr4756b3a2002-05-16 18:17:32 +0000343 def __getitem__(self, index):
344 item = self.items[index]
345 if item is not None:
346 return item
347 offset = self.offsets[index] + self.offsetBase
348 size = self.offsets[index+1] - self.offsets[index]
349 file = self.file
350 file.seek(offset)
351 data = file.read(size)
352 assert len(data) == size
jvra2ad5442002-05-17 18:36:07 +0000353 item = self.produceItem(index, data, file, offset, size)
jvr4756b3a2002-05-16 18:17:32 +0000354 self.items[index] = item
355 return item
356
jvra2ad5442002-05-17 18:36:07 +0000357 def produceItem(self, index, data, file, offset, size):
jvr4756b3a2002-05-16 18:17:32 +0000358 return data
jvr4756b3a2002-05-16 18:17:32 +0000359
jvrf2cf9c52002-05-23 21:50:36 +0000360 def append(self, item):
361 self.items.append(item)
362
363 def getCompiler(self, strings, parent):
364 return self.compilerClass(self, strings, parent)
365
366
367class GlobalSubrsIndex(Index):
368
369 compilerClass = GlobalSubrsCompiler
370
jvr4e5af602002-05-24 09:58:04 +0000371 def __init__(self, file=None, globalSubrs=None, private=None, fdSelect=None, fdArray=None):
372 Index.__init__(self, file)
jvra2ad5442002-05-17 18:36:07 +0000373 self.globalSubrs = globalSubrs
374 self.private = private
jvred101512003-08-22 19:53:32 +0000375 if fdSelect:
376 self.fdSelect = fdSelect
377 if fdArray:
378 self.fdArray = fdArray
jvra2ad5442002-05-17 18:36:07 +0000379
380 def produceItem(self, index, data, file, offset, size):
381 if self.private is not None:
382 private = self.private
jvred101512003-08-22 19:53:32 +0000383 elif hasattr(self, 'fdArray') and self.fdArray is not None:
jvra2ad5442002-05-17 18:36:07 +0000384 private = self.fdArray[self.fdSelect[index]].Private
385 else:
386 private = None
jvr489d76a2003-08-24 19:56:16 +0000387 return psCharStrings.T2CharString(data, private=private, globalSubrs=self.globalSubrs)
jvr4756b3a2002-05-16 18:17:32 +0000388
389 def toXML(self, xmlWriter, progress):
jvred101512003-08-22 19:53:32 +0000390 xmlWriter.comment("The 'index' attribute is only for humans; it is ignored when parsed.")
jvr4e5af602002-05-24 09:58:04 +0000391 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +0000392 for i in range(len(self)):
jvrb58176e2002-05-24 11:55:37 +0000393 subr = self[i]
394 if subr.needsDecompilation():
395 xmlWriter.begintag("CharString", index=i, raw=1)
396 else:
397 xmlWriter.begintag("CharString", index=i)
jvr4756b3a2002-05-16 18:17:32 +0000398 xmlWriter.newline()
jvrb58176e2002-05-24 11:55:37 +0000399 subr.toXML(xmlWriter)
jvr4756b3a2002-05-16 18:17:32 +0000400 xmlWriter.endtag("CharString")
401 xmlWriter.newline()
jvra2ad5442002-05-17 18:36:07 +0000402
jvr4e5af602002-05-24 09:58:04 +0000403 def fromXML(self, (name, attrs, content)):
404 if name <> "CharString":
405 return
jvr489d76a2003-08-24 19:56:16 +0000406 subr = psCharStrings.T2CharString()
jvr4e5af602002-05-24 09:58:04 +0000407 subr.fromXML((name, attrs, content))
408 self.append(subr)
409
jvra2ad5442002-05-17 18:36:07 +0000410 def getItemAndSelector(self, index):
jvred101512003-08-22 19:53:32 +0000411 sel = None
412 if hasattr(self, 'fdSelect'):
413 sel = self.fdSelect[index]
jvra2ad5442002-05-17 18:36:07 +0000414 return self[index], sel
jvrf2cf9c52002-05-23 21:50:36 +0000415
jvre2ca9b52002-09-09 14:18:39 +0000416
jvrf2cf9c52002-05-23 21:50:36 +0000417class SubrsIndex(GlobalSubrsIndex):
418 compilerClass = SubrsCompiler
419
jvr4756b3a2002-05-16 18:17:32 +0000420
jvr767102e2002-05-17 07:06:32 +0000421class TopDictIndex(Index):
jvra2ad5442002-05-17 18:36:07 +0000422
jvrf2cf9c52002-05-23 21:50:36 +0000423 compilerClass = TopDictIndexCompiler
424
jvra2ad5442002-05-17 18:36:07 +0000425 def produceItem(self, index, data, file, offset, size):
jvr767102e2002-05-17 07:06:32 +0000426 top = TopDict(self.strings, file, offset, self.GlobalSubrs)
427 top.decompile(data)
428 return top
jvra2ad5442002-05-17 18:36:07 +0000429
430 def toXML(self, xmlWriter, progress):
431 for i in range(len(self)):
432 xmlWriter.begintag("FontDict", index=i)
433 xmlWriter.newline()
434 self[i].toXML(xmlWriter, progress)
435 xmlWriter.endtag("FontDict")
436 xmlWriter.newline()
jvr767102e2002-05-17 07:06:32 +0000437
438
jvred101512003-08-22 19:53:32 +0000439class FDArrayIndex(TopDictIndex):
440
441 compilerClass = FDArrayIndexCompiler
442
443 def fromXML(self, (name, attrs, content)):
444 if name <> "FontDict":
445 return
446 fontDict = FontDict()
447 for element in content:
448 if isinstance(element, StringType):
449 continue
450 fontDict.fromXML(element)
451 self.append(fontDict)
452
453
454class FDSelect:
455 def __init__(self, file = None, numGlyphs = None, format=None):
456 if file:
457 # read data in from file
458 self.format = readCard8(file)
459 if self.format == 0:
460 from array import array
461 self.gidArray = array("B", file.read(numGlyphs)).tolist()
462 elif self.format == 3:
463 gidArray = [None] * numGlyphs
464 nRanges = readCard16(file)
465 prev = None
466 for i in range(nRanges):
467 first = readCard16(file)
468 if prev is not None:
469 for glyphID in range(prev, first):
470 gidArray[glyphID] = fd
471 prev = first
472 fd = readCard8(file)
473 if prev is not None:
474 first = readCard16(file)
475 for glyphID in range(prev, first):
476 gidArray[glyphID] = fd
477 self.gidArray = gidArray
478 else:
479 assert 0, "unsupported FDSelect format: %s" % format
480 else:
481 # reading from XML. Make empty gidArray,, and leave format as passed in.
482 # format == None will result in the smallest representation being used.
483 self.format = format
484 self.gidArray = []
485
486
487 def __len__(self):
488 return len(self.gidArray)
489
490 def __getitem__(self, index):
491 return self.gidArray[index]
492
493 def __setitem__(self, index, fdSelectValue):
494 self.gidArray[index] = fdSelectValue
495
496 def append(self, fdSelectValue):
497 self.gidArray.append(fdSelectValue)
498
499
jvr4756b3a2002-05-16 18:17:32 +0000500class CharStrings:
501
jvra2ad5442002-05-17 18:36:07 +0000502 def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray):
jvr4e5af602002-05-24 09:58:04 +0000503 if file is not None:
504 self.charStringsIndex = SubrsIndex(file, globalSubrs, private, fdSelect, fdArray)
505 self.charStrings = charStrings = {}
506 for i in range(len(charset)):
507 charStrings[charset[i]] = i
508 self.charStringsAreIndexed = 1
509 else:
510 self.charStrings = {}
511 self.charStringsAreIndexed = 0
512 self.globalSubrs = globalSubrs
513 self.private = private
jvred101512003-08-22 19:53:32 +0000514 if fdSelect != None:
515 self.fdSelect = fdSelect
516 if fdArray!= None:
517 self.fdArray = fdArray
jvr4756b3a2002-05-16 18:17:32 +0000518
519 def keys(self):
jvr4e5af602002-05-24 09:58:04 +0000520 return self.charStrings.keys()
jvr4756b3a2002-05-16 18:17:32 +0000521
jvr016ca762002-05-16 18:38:03 +0000522 def values(self):
jvr4e5af602002-05-24 09:58:04 +0000523 if self.charStringsAreIndexed:
524 return self.charStringsIndex
525 else:
526 return self.charStrings.values()
jvr016ca762002-05-16 18:38:03 +0000527
jvr4756b3a2002-05-16 18:17:32 +0000528 def has_key(self, name):
jvr4e5af602002-05-24 09:58:04 +0000529 return self.charStrings.has_key(name)
jvr4756b3a2002-05-16 18:17:32 +0000530
jvr767102e2002-05-17 07:06:32 +0000531 def __len__(self):
jvr4e5af602002-05-24 09:58:04 +0000532 return len(self.charStrings)
jvr767102e2002-05-17 07:06:32 +0000533
jvr4756b3a2002-05-16 18:17:32 +0000534 def __getitem__(self, name):
jvr4e5af602002-05-24 09:58:04 +0000535 charString = self.charStrings[name]
536 if self.charStringsAreIndexed:
537 charString = self.charStringsIndex[charString]
538 return charString
539
540 def __setitem__(self, name, charString):
541 if self.charStringsAreIndexed:
542 index = self.charStrings[name]
543 self.charStringsIndex[index] = charString
544 else:
545 self.charStrings[name] = charString
jvr4756b3a2002-05-16 18:17:32 +0000546
jvra2ad5442002-05-17 18:36:07 +0000547 def getItemAndSelector(self, name):
jvr4e5af602002-05-24 09:58:04 +0000548 if self.charStringsAreIndexed:
549 index = self.charStrings[name]
550 return self.charStringsIndex.getItemAndSelector(index)
551 else:
jvred101512003-08-22 19:53:32 +0000552 if hasattr(self, 'fdSelect'):
553 sel = self.fdSelect[index]
554 else:
555 raise KeyError("fdSelect array not yet defined.")
556 return self.charStrings[name], sel
jvra2ad5442002-05-17 18:36:07 +0000557
jvr4756b3a2002-05-16 18:17:32 +0000558 def toXML(self, xmlWriter, progress):
559 names = self.keys()
560 names.sort()
jvr7ce0a132002-07-23 16:42:11 +0000561 i = 0
562 step = 10
563 numGlyphs = len(names)
jvr4756b3a2002-05-16 18:17:32 +0000564 for name in names:
jvred101512003-08-22 19:53:32 +0000565 charStr, fdSelectIndex = self.getItemAndSelector(name)
jvrb58176e2002-05-24 11:55:37 +0000566 if charStr.needsDecompilation():
567 raw = [("raw", 1)]
568 else:
569 raw = []
jvred101512003-08-22 19:53:32 +0000570 if fdSelectIndex is None:
jvrb58176e2002-05-24 11:55:37 +0000571 xmlWriter.begintag("CharString", [('name', name)] + raw)
jvra2ad5442002-05-17 18:36:07 +0000572 else:
573 xmlWriter.begintag("CharString",
jvred101512003-08-22 19:53:32 +0000574 [('name', name), ('fdSelectIndex', fdSelectIndex)] + raw)
jvr4756b3a2002-05-16 18:17:32 +0000575 xmlWriter.newline()
jvrb58176e2002-05-24 11:55:37 +0000576 charStr.toXML(xmlWriter)
jvr4756b3a2002-05-16 18:17:32 +0000577 xmlWriter.endtag("CharString")
578 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000579 if not i % step and progress is not None:
580 progress.setLabel("Dumping 'CFF ' table... (%s)" % name)
581 progress.increment(step / float(numGlyphs))
582 i = i + 1
jvr4e5af602002-05-24 09:58:04 +0000583
584 def fromXML(self, (name, attrs, content)):
585 for element in content:
586 if isinstance(element, StringType):
587 continue
588 name, attrs, content = element
589 if name <> "CharString":
590 continue
jvred101512003-08-22 19:53:32 +0000591 fdID = -1
592 if hasattr(self, "fdArray"):
593 fdID = safeEval(attrs["fdSelectIndex"])
594 private = self.fdArray[fdID].Private
595 else:
596 private = self.private
597
jvr4e5af602002-05-24 09:58:04 +0000598 glyphName = attrs["name"]
jvr489d76a2003-08-24 19:56:16 +0000599 charString = psCharStrings.T2CharString(
600 private=private,
601 globalSubrs=self.globalSubrs)
jvr4e5af602002-05-24 09:58:04 +0000602 charString.fromXML((name, attrs, content))
jvred101512003-08-22 19:53:32 +0000603 if fdID >= 0:
604 charString.fdSelectIndex = fdID
jvr4e5af602002-05-24 09:58:04 +0000605 self[glyphName] = charString
jvr4756b3a2002-05-16 18:17:32 +0000606
607
jvra2ad5442002-05-17 18:36:07 +0000608def readCard8(file):
609 return ord(file.read(1))
610
611def readCard16(file):
612 value, = struct.unpack(">H", file.read(2))
613 return value
614
jvrf2cf9c52002-05-23 21:50:36 +0000615def writeCard8(file, value):
616 file.write(chr(value))
617
618def writeCard16(file, value):
619 file.write(struct.pack(">H", value))
620
621def packCard8(value):
622 return chr(value)
623
624def packCard16(value):
625 return struct.pack(">H", value)
626
jvr4756b3a2002-05-16 18:17:32 +0000627def buildOperatorDict(table):
628 d = {}
629 for op, name, arg, default, conv in table:
630 d[op] = (name, arg)
631 return d
632
jvrf2cf9c52002-05-23 21:50:36 +0000633def buildOpcodeDict(table):
634 d = {}
635 for op, name, arg, default, conv in table:
636 if type(op) == TupleType:
637 op = chr(op[0]) + chr(op[1])
638 else:
639 op = chr(op)
640 d[name] = (op, arg)
641 return d
642
jvr4756b3a2002-05-16 18:17:32 +0000643def buildOrder(table):
644 l = []
645 for op, name, arg, default, conv in table:
646 l.append(name)
647 return l
648
649def buildDefaults(table):
650 d = {}
651 for op, name, arg, default, conv in table:
652 if default is not None:
653 d[name] = default
654 return d
655
656def buildConverters(table):
657 d = {}
658 for op, name, arg, default, conv in table:
659 d[name] = conv
660 return d
661
662
jvr4e5af602002-05-24 09:58:04 +0000663class SimpleConverter:
jvr7ce02ea2002-05-17 20:04:05 +0000664 def read(self, parent, value):
665 return value
jvrf2cf9c52002-05-23 21:50:36 +0000666 def write(self, parent, value):
667 return value
jvr7ce0a132002-07-23 16:42:11 +0000668 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000669 xmlWriter.simpletag(name, value=value)
670 xmlWriter.newline()
671 def xmlRead(self, (name, attrs, content), parent):
672 return attrs["value"]
673
jvre2ca9b52002-09-09 14:18:39 +0000674class Latin1Converter(SimpleConverter):
675 def xmlRead(self, (name, attrs, content), parent):
676 s = unicode(attrs["value"], "utf-8")
677 return s.encode("latin-1")
678
679
jvr4e5af602002-05-24 09:58:04 +0000680def parseNum(s):
681 try:
682 value = int(s)
683 except:
684 value = float(s)
685 return value
686
687class NumberConverter(SimpleConverter):
688 def xmlRead(self, (name, attrs, content), parent):
689 return parseNum(attrs["value"])
690
691class ArrayConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000692 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000693 value = map(str, value)
694 xmlWriter.simpletag(name, value=" ".join(value))
695 xmlWriter.newline()
696 def xmlRead(self, (name, attrs, content), parent):
697 values = attrs["value"].split()
698 return map(parseNum, values)
699
700class TableConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000701 def xmlWrite(self, xmlWriter, name, value, progress):
jvra2ad5442002-05-17 18:36:07 +0000702 xmlWriter.begintag(name)
703 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000704 value.toXML(xmlWriter, progress)
jvra2ad5442002-05-17 18:36:07 +0000705 xmlWriter.endtag(name)
706 xmlWriter.newline()
jvr4e5af602002-05-24 09:58:04 +0000707 def xmlRead(self, (name, attrs, content), parent):
708 ob = self.getClass()()
709 for element in content:
710 if isinstance(element, StringType):
711 continue
712 ob.fromXML(element)
713 return ob
jvra2ad5442002-05-17 18:36:07 +0000714
jvr4e5af602002-05-24 09:58:04 +0000715class PrivateDictConverter(TableConverter):
716 def getClass(self):
717 return PrivateDict
jvr4756b3a2002-05-16 18:17:32 +0000718 def read(self, parent, value):
719 size, offset = value
720 file = parent.file
jvr4e5af602002-05-24 09:58:04 +0000721 priv = PrivateDict(parent.strings, file, offset)
jvr4756b3a2002-05-16 18:17:32 +0000722 file.seek(offset)
723 data = file.read(size)
724 len(data) == size
jvr4e5af602002-05-24 09:58:04 +0000725 priv.decompile(data)
726 return priv
jvrf2cf9c52002-05-23 21:50:36 +0000727 def write(self, parent, value):
728 return (0, 0) # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000729
jvr4e5af602002-05-24 09:58:04 +0000730class SubrsConverter(TableConverter):
731 def getClass(self):
732 return SubrsIndex
jvr4756b3a2002-05-16 18:17:32 +0000733 def read(self, parent, value):
734 file = parent.file
735 file.seek(parent.offset + value) # Offset(self)
jvr4e5af602002-05-24 09:58:04 +0000736 return SubrsIndex(file)
jvrf2cf9c52002-05-23 21:50:36 +0000737 def write(self, parent, value):
738 return 0 # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000739
jvr4e5af602002-05-24 09:58:04 +0000740class CharStringsConverter(TableConverter):
jvr4756b3a2002-05-16 18:17:32 +0000741 def read(self, parent, value):
742 file = parent.file
jvr767102e2002-05-17 07:06:32 +0000743 charset = parent.charset
jvra2ad5442002-05-17 18:36:07 +0000744 globalSubrs = parent.GlobalSubrs
745 if hasattr(parent, "ROS"):
746 fdSelect, fdArray = parent.FDSelect, parent.FDArray
747 private = None
748 else:
749 fdSelect, fdArray = None, None
750 private = parent.Private
jvr4756b3a2002-05-16 18:17:32 +0000751 file.seek(value) # Offset(0)
jvra2ad5442002-05-17 18:36:07 +0000752 return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
jvrf2cf9c52002-05-23 21:50:36 +0000753 def write(self, parent, value):
754 return 0 # dummy value
jvr4e5af602002-05-24 09:58:04 +0000755 def xmlRead(self, (name, attrs, content), parent):
jvred101512003-08-22 19:53:32 +0000756 if hasattr(parent, "ROS"):
757 # if it is a CID-keyed font, then the private Dict is extracted from the parent.FDArray
758 private, fdSelect, fdArray = None, parent.FDSelect, parent.FDArray
759 else:
760 # if it is a name-keyed font, then the private dict is in the top dict, and there is no fdArray.
761 private, fdSelect, fdArray = parent.Private, None, None
762 charStrings = CharStrings(None, None, parent.GlobalSubrs, private, fdSelect, fdArray)
jvr4e5af602002-05-24 09:58:04 +0000763 charStrings.fromXML((name, attrs, content))
764 return charStrings
jvr4756b3a2002-05-16 18:17:32 +0000765
766class CharsetConverter:
767 def read(self, parent, value):
768 isCID = hasattr(parent, "ROS")
769 if value > 2:
770 numGlyphs = parent.numGlyphs
771 file = parent.file
772 file.seek(value)
jvrf2cf9c52002-05-23 21:50:36 +0000773 if DEBUG:
774 print "loading charset at %s" % value
jvra2ad5442002-05-17 18:36:07 +0000775 format = readCard8(file)
Just7842e561999-12-16 21:34:53 +0000776 if format == 0:
jvrc60a44f2006-10-21 13:41:18 +0000777 charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
jvr4756b3a2002-05-16 18:17:32 +0000778 elif format == 1 or format == 2:
779 charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
Just7842e561999-12-16 21:34:53 +0000780 else:
jvr1890b952002-05-15 07:41:30 +0000781 raise NotImplementedError
jvr4756b3a2002-05-16 18:17:32 +0000782 assert len(charset) == numGlyphs
jvrf2cf9c52002-05-23 21:50:36 +0000783 if DEBUG:
784 print " charset end at %s" % file.tell()
jvrc60a44f2006-10-21 13:41:18 +0000785 else: # offset == 0 -> no charset data.
786 if isCID or not parent.rawDict.has_key("CharStrings"):
787 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 +0000788 charset = None
789 elif value == 0:
jvrc60a44f2006-10-21 13:41:18 +0000790 charset = cffISOAdobeStrings
jvr4756b3a2002-05-16 18:17:32 +0000791 elif value == 1:
jvrc60a44f2006-10-21 13:41:18 +0000792 charset = cffIExpertStrings
jvr4756b3a2002-05-16 18:17:32 +0000793 elif value == 2:
jvrc60a44f2006-10-21 13:41:18 +0000794 charset = cffExpertSubsetStrings
jvr4756b3a2002-05-16 18:17:32 +0000795 return charset
jvrc60a44f2006-10-21 13:41:18 +0000796
jvrf2cf9c52002-05-23 21:50:36 +0000797 def write(self, parent, value):
798 return 0 # dummy value
jvr7ce0a132002-07-23 16:42:11 +0000799 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000800 # XXX only write charset when not in OT/TTX context, where we
801 # dump charset as a separate "GlyphOrder" table.
802 ##xmlWriter.simpletag("charset")
803 xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
jvr4756b3a2002-05-16 18:17:32 +0000804 xmlWriter.newline()
jvr4e5af602002-05-24 09:58:04 +0000805 def xmlRead(self, (name, attrs, content), parent):
806 if 0:
807 return safeEval(attrs["value"])
jvr4756b3a2002-05-16 18:17:32 +0000808
809
jvrf2cf9c52002-05-23 21:50:36 +0000810class CharsetCompiler:
811
812 def __init__(self, strings, charset, parent):
813 assert charset[0] == '.notdef'
jvred101512003-08-22 19:53:32 +0000814 isCID = hasattr(parent.dictObj, "ROS")
815 data0 = packCharset0(charset, isCID, strings)
816 data = packCharset(charset, isCID, strings)
jvr6004baf2002-05-24 10:35:13 +0000817 if len(data) < len(data0):
818 self.data = data
819 else:
820 self.data = data0
jvrf2cf9c52002-05-23 21:50:36 +0000821 self.parent = parent
822
jvr4e5af602002-05-24 09:58:04 +0000823 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000824 self.parent.rawDict["charset"] = pos
825
826 def getDataLength(self):
827 return len(self.data)
828
829 def toFile(self, file):
830 file.write(self.data)
831
832
jvred101512003-08-22 19:53:32 +0000833def getCIDfromName(name, strings):
834 return int(name[3:])
835
836def getSIDfromName(name, strings):
837 return strings.getSID(name)
838
839def packCharset0(charset, isCID, strings):
jvr6004baf2002-05-24 10:35:13 +0000840 format = 0
841 data = [packCard8(format)]
jvred101512003-08-22 19:53:32 +0000842 if isCID:
843 getNameID = getCIDfromName
844 else:
845 getNameID = getSIDfromName
846
jvr6004baf2002-05-24 10:35:13 +0000847 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000848 data.append(packCard16(getNameID(name,strings)))
jvr6004baf2002-05-24 10:35:13 +0000849 return "".join(data)
850
jvred101512003-08-22 19:53:32 +0000851
852def packCharset(charset, isCID, strings):
jvr6004baf2002-05-24 10:35:13 +0000853 format = 1
jvr6004baf2002-05-24 10:35:13 +0000854 ranges = []
855 first = None
856 end = 0
jvred101512003-08-22 19:53:32 +0000857 if isCID:
858 getNameID = getCIDfromName
859 else:
860 getNameID = getSIDfromName
861
jvr6004baf2002-05-24 10:35:13 +0000862 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000863 SID = getNameID(name, strings)
jvr6004baf2002-05-24 10:35:13 +0000864 if first is None:
865 first = SID
866 elif end + 1 <> SID:
867 nLeft = end - first
868 if nLeft > 255:
869 format = 2
870 ranges.append((first, nLeft))
871 first = SID
872 end = SID
873 nLeft = end - first
874 if nLeft > 255:
875 format = 2
876 ranges.append((first, nLeft))
877
jvr74cd1ef2002-05-24 10:38:04 +0000878 data = [packCard8(format)]
jvr6004baf2002-05-24 10:35:13 +0000879 if format == 1:
880 nLeftFunc = packCard8
881 else:
882 nLeftFunc = packCard16
883 for first, nLeft in ranges:
884 data.append(packCard16(first) + nLeftFunc(nLeft))
jvrb58176e2002-05-24 11:55:37 +0000885 return "".join(data)
jvr6004baf2002-05-24 10:35:13 +0000886
jvrc60a44f2006-10-21 13:41:18 +0000887def parseCharset0(numGlyphs, file, strings, isCID):
jvrf2cf9c52002-05-23 21:50:36 +0000888 charset = [".notdef"]
jvrc60a44f2006-10-21 13:41:18 +0000889 if isCID:
890 for i in range(numGlyphs - 1):
891 CID = readCard16(file)
892 charset.append("cid" + string.zfill(str(CID), 5) )
893 else:
894 for i in range(numGlyphs - 1):
895 SID = readCard16(file)
896 charset.append(strings[SID])
jvrf2cf9c52002-05-23 21:50:36 +0000897 return charset
898
jvr4756b3a2002-05-16 18:17:32 +0000899def parseCharset(numGlyphs, file, strings, isCID, format):
900 charset = ['.notdef']
901 count = 1
902 if format == 1:
jvra2ad5442002-05-17 18:36:07 +0000903 nLeftFunc = readCard8
jvr4756b3a2002-05-16 18:17:32 +0000904 else:
jvra2ad5442002-05-17 18:36:07 +0000905 nLeftFunc = readCard16
jvr4756b3a2002-05-16 18:17:32 +0000906 while count < numGlyphs:
jvra2ad5442002-05-17 18:36:07 +0000907 first = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000908 nLeft = nLeftFunc(file)
909 if isCID:
910 for CID in range(first, first+nLeft+1):
jvrc60a44f2006-10-21 13:41:18 +0000911 charset.append("cid" + string.zfill(str(CID), 5) )
jvr1890b952002-05-15 07:41:30 +0000912 else:
jvr4756b3a2002-05-16 18:17:32 +0000913 for SID in range(first, first+nLeft+1):
914 charset.append(strings[SID])
915 count = count + nLeft + 1
916 return charset
917
918
jvrb9702ba2003-01-03 20:56:01 +0000919class EncodingCompiler:
920
921 def __init__(self, strings, encoding, parent):
922 assert not isinstance(encoding, StringType)
923 data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
924 data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
925 if len(data0) < len(data1):
926 self.data = data0
927 else:
928 self.data = data1
929 self.parent = parent
930
931 def setPos(self, pos, endPos):
932 self.parent.rawDict["Encoding"] = pos
933
934 def getDataLength(self):
935 return len(self.data)
936
937 def toFile(self, file):
938 file.write(self.data)
939
940
941class EncodingConverter(SimpleConverter):
942
943 def read(self, parent, value):
944 if value == 0:
945 return "StandardEncoding"
946 elif value == 1:
947 return "ExpertEncoding"
948 else:
949 assert value > 1
950 file = parent.file
951 file.seek(value)
952 if DEBUG:
953 print "loading Encoding at %s" % value
954 format = readCard8(file)
955 haveSupplement = format & 0x80
956 if haveSupplement:
957 raise NotImplementedError, "Encoding supplements are not yet supported"
958 format = format & 0x7f
959 if format == 0:
960 encoding = parseEncoding0(parent.charset, file, haveSupplement,
961 parent.strings)
962 elif format == 1:
963 encoding = parseEncoding1(parent.charset, file, haveSupplement,
964 parent.strings)
965 return encoding
966
967 def write(self, parent, value):
968 if value == "StandardEncoding":
969 return 0
970 elif value == "ExpertEncoding":
971 return 1
972 return 0 # dummy value
973
974 def xmlWrite(self, xmlWriter, name, value, progress):
975 if value in ("StandardEncoding", "ExpertEncoding"):
976 xmlWriter.simpletag(name, name=value)
977 xmlWriter.newline()
978 return
979 xmlWriter.begintag(name)
980 xmlWriter.newline()
981 for code in range(len(value)):
982 glyphName = value[code]
983 if glyphName != ".notdef":
984 xmlWriter.simpletag("map", code=hex(code), name=glyphName)
985 xmlWriter.newline()
986 xmlWriter.endtag(name)
987 xmlWriter.newline()
988
989 def xmlRead(self, (name, attrs, content), parent):
990 if attrs.has_key("name"):
991 return attrs["name"]
992 encoding = [".notdef"] * 256
993 for element in content:
994 if isinstance(element, StringType):
995 continue
996 name, attrs, content = element
997 code = safeEval(attrs["code"])
998 glyphName = attrs["name"]
999 encoding[code] = glyphName
1000 return encoding
1001
1002
1003def parseEncoding0(charset, file, haveSupplement, strings):
1004 nCodes = readCard8(file)
1005 encoding = [".notdef"] * 256
1006 for glyphID in range(1, nCodes + 1):
1007 code = readCard8(file)
1008 if code != 0:
1009 encoding[code] = charset[glyphID]
1010 return encoding
1011
1012def parseEncoding1(charset, file, haveSupplement, strings):
1013 nRanges = readCard8(file)
1014 encoding = [".notdef"] * 256
1015 glyphID = 1
1016 for i in range(nRanges):
1017 code = readCard8(file)
1018 nLeft = readCard8(file)
1019 for glyphID in range(glyphID, glyphID + nLeft + 1):
1020 encoding[code] = charset[glyphID]
1021 code = code + 1
1022 glyphID = glyphID + 1
1023 return encoding
1024
1025def packEncoding0(charset, encoding, strings):
1026 format = 0
1027 m = {}
1028 for code in range(len(encoding)):
1029 name = encoding[code]
1030 if name != ".notdef":
1031 m[name] = code
1032 codes = []
1033 for name in charset[1:]:
1034 code = m.get(name)
1035 codes.append(code)
1036
1037 while codes and codes[-1] is None:
1038 codes.pop()
1039
1040 data = [packCard8(format), packCard8(len(codes))]
1041 for code in codes:
1042 if code is None:
1043 code = 0
1044 data.append(packCard8(code))
1045 return "".join(data)
1046
1047def packEncoding1(charset, encoding, strings):
1048 format = 1
1049 m = {}
1050 for code in range(len(encoding)):
1051 name = encoding[code]
1052 if name != ".notdef":
1053 m[name] = code
1054 ranges = []
1055 first = None
1056 end = 0
1057 for name in charset[1:]:
1058 code = m.get(name, -1)
1059 if first is None:
1060 first = code
1061 elif end + 1 <> code:
1062 nLeft = end - first
1063 ranges.append((first, nLeft))
1064 first = code
1065 end = code
1066 nLeft = end - first
1067 ranges.append((first, nLeft))
1068
1069 # remove unencoded glyphs at the end.
1070 while ranges and ranges[-1][0] == -1:
1071 ranges.pop()
1072
1073 data = [packCard8(format), packCard8(len(ranges))]
1074 for first, nLeft in ranges:
1075 if first == -1: # unencoded
1076 first = 0
1077 data.append(packCard8(first) + packCard8(nLeft))
1078 return "".join(data)
1079
1080
jvr4e5af602002-05-24 09:58:04 +00001081class FDArrayConverter(TableConverter):
jvred101512003-08-22 19:53:32 +00001082
jvra2ad5442002-05-17 18:36:07 +00001083 def read(self, parent, value):
1084 file = parent.file
1085 file.seek(value)
jvred101512003-08-22 19:53:32 +00001086 fdArray = FDArrayIndex(file)
jvra2ad5442002-05-17 18:36:07 +00001087 fdArray.strings = parent.strings
1088 fdArray.GlobalSubrs = parent.GlobalSubrs
1089 return fdArray
1090
jvred101512003-08-22 19:53:32 +00001091 def write(self, parent, value):
1092 return 0 # dummy value
1093
1094 def xmlRead(self, (name, attrs, content), parent):
1095 fdArray = FDArrayIndex()
1096 for element in content:
1097 if isinstance(element, StringType):
1098 continue
1099 fdArray.fromXML(element)
1100 return fdArray
1101
jvra2ad5442002-05-17 18:36:07 +00001102
1103class FDSelectConverter:
jvred101512003-08-22 19:53:32 +00001104
jvra2ad5442002-05-17 18:36:07 +00001105 def read(self, parent, value):
1106 file = parent.file
1107 file.seek(value)
jvred101512003-08-22 19:53:32 +00001108 fdSelect = FDSelect(file, parent.numGlyphs)
1109 return fdSelect
1110
1111 def write(self, parent, value):
1112 return 0 # dummy value
1113
1114 # The FDSelect glyph data is written out to XML in the charstring keys,
1115 # so we write out only the format selector
jvr7ce0a132002-07-23 16:42:11 +00001116 def xmlWrite(self, xmlWriter, name, value, progress):
jvred101512003-08-22 19:53:32 +00001117 xmlWriter.simpletag(name, [('format', value.format)])
1118 xmlWriter.newline()
1119
1120 def xmlRead(self, (name, attrs, content), parent):
1121 format = safeEval(attrs["format"])
1122 file = None
1123 numGlyphs = None
1124 fdSelect = FDSelect(file, numGlyphs, format)
1125 return fdSelect
1126
1127
1128def packFDSelect0(fdSelectArray):
1129 format = 0
1130 data = [packCard8(format)]
1131 for index in fdSelectArray:
1132 data.append(packCard8(index))
1133 return "".join(data)
1134
1135
1136def packFDSelect3(fdSelectArray):
1137 format = 3
1138 fdRanges = []
1139 first = None
1140 end = 0
1141 lenArray = len(fdSelectArray)
1142 lastFDIndex = -1
1143 for i in range(lenArray):
1144 fdIndex = fdSelectArray[i]
1145 if lastFDIndex != fdIndex:
1146 fdRanges.append([i, fdIndex])
1147 lastFDIndex = fdIndex
1148 sentinelGID = i + 1
1149
1150 data = [packCard8(format)]
1151 data.append(packCard16( len(fdRanges) ))
1152 for fdRange in fdRanges:
1153 data.append(packCard16(fdRange[0]))
1154 data.append(packCard8(fdRange[1]))
1155 data.append(packCard16(sentinelGID))
1156 return "".join(data)
1157
1158
1159class FDSelectCompiler:
1160
1161 def __init__(self, fdSelect, parent):
1162 format = fdSelect.format
1163 fdSelectArray = fdSelect.gidArray
1164 if format == 0:
1165 self.data = packFDSelect0(fdSelectArray)
1166 elif format == 3:
1167 self.data = packFDSelect3(fdSelectArray)
1168 else:
1169 # choose smaller of the two formats
1170 data0 = packFDSelect0(fdSelectArray)
1171 data3 = packFDSelect3(fdSelectArray)
1172 if len(data0) < len(data3):
1173 self.data = data0
1174 fdSelect.format = 0
1175 else:
1176 self.data = data3
1177 fdSelect.format = 3
1178
1179 self.parent = parent
1180
1181 def setPos(self, pos, endPos):
1182 self.parent.rawDict["FDSelect"] = pos
1183
1184 def getDataLength(self):
1185 return len(self.data)
1186
1187 def toFile(self, file):
1188 file.write(self.data)
jvra2ad5442002-05-17 18:36:07 +00001189
1190
jvr4e5af602002-05-24 09:58:04 +00001191class ROSConverter(SimpleConverter):
jvred101512003-08-22 19:53:32 +00001192
jvr7ce0a132002-07-23 16:42:11 +00001193 def xmlWrite(self, xmlWriter, name, value, progress):
jvr155aa752002-05-17 19:58:49 +00001194 registry, order, supplement = value
jvrf2cf9c52002-05-23 21:50:36 +00001195 xmlWriter.simpletag(name, [('Registry', registry), ('Order', order),
1196 ('Supplement', supplement)])
jvr4afb2572002-05-18 20:07:01 +00001197 xmlWriter.newline()
jvr155aa752002-05-17 19:58:49 +00001198
jvred101512003-08-22 19:53:32 +00001199 def xmlRead(self, (name, attrs, content), parent):
1200 return (attrs['Registry'], attrs['Order'], safeEval(attrs['Supplement']))
1201
1202
jvr155aa752002-05-17 19:58:49 +00001203
jvr4756b3a2002-05-16 18:17:32 +00001204topDictOperators = [
1205# opcode name argument type default converter
jvr155aa752002-05-17 19:58:49 +00001206 ((12, 30), 'ROS', ('SID','SID','number'), None, ROSConverter()),
jvrf2cf9c52002-05-23 21:50:36 +00001207 ((12, 20), 'SyntheticBase', 'number', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001208 (0, 'version', 'SID', None, None),
jvre2ca9b52002-09-09 14:18:39 +00001209 (1, 'Notice', 'SID', None, Latin1Converter()),
1210 ((12, 0), 'Copyright', 'SID', None, Latin1Converter()),
jvr4756b3a2002-05-16 18:17:32 +00001211 (2, 'FullName', 'SID', None, None),
jvr155aa752002-05-17 19:58:49 +00001212 ((12, 38), 'FontName', 'SID', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001213 (3, 'FamilyName', 'SID', None, None),
1214 (4, 'Weight', 'SID', None, None),
1215 ((12, 1), 'isFixedPitch', 'number', 0, None),
1216 ((12, 2), 'ItalicAngle', 'number', 0, None),
1217 ((12, 3), 'UnderlinePosition', 'number', None, None),
1218 ((12, 4), 'UnderlineThickness', 'number', 50, None),
1219 ((12, 5), 'PaintType', 'number', 0, None),
1220 ((12, 6), 'CharstringType', 'number', 2, None),
1221 ((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0], None),
1222 (13, 'UniqueID', 'number', None, None),
1223 (5, 'FontBBox', 'array', [0,0,0,0], None),
1224 ((12, 8), 'StrokeWidth', 'number', 0, None),
1225 (14, 'XUID', 'array', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001226 ((12, 21), 'PostScript', 'SID', None, None),
1227 ((12, 22), 'BaseFontName', 'SID', None, None),
1228 ((12, 23), 'BaseFontBlend', 'delta', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001229 ((12, 31), 'CIDFontVersion', 'number', 0, None),
1230 ((12, 32), 'CIDFontRevision', 'number', 0, None),
1231 ((12, 33), 'CIDFontType', 'number', 0, None),
1232 ((12, 34), 'CIDCount', 'number', 8720, None),
jvred101512003-08-22 19:53:32 +00001233 (15, 'charset', 'number', 0, CharsetConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001234 ((12, 35), 'UIDBase', 'number', None, None),
jvrb9702ba2003-01-03 20:56:01 +00001235 (16, 'Encoding', 'number', 0, EncodingConverter()),
jvr155aa752002-05-17 19:58:49 +00001236 (18, 'Private', ('number','number'), None, PrivateDictConverter()),
jvred101512003-08-22 19:53:32 +00001237 ((12, 37), 'FDSelect', 'number', None, FDSelectConverter()),
1238 ((12, 36), 'FDArray', 'number', None, FDArrayConverter()),
jvr155aa752002-05-17 19:58:49 +00001239 (17, 'CharStrings', 'number', None, CharStringsConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001240]
1241
jvred101512003-08-22 19:53:32 +00001242# Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order,
1243# in order for the font to compile back from xml.
1244
1245
jvr4756b3a2002-05-16 18:17:32 +00001246privateDictOperators = [
1247# opcode name argument type default converter
1248 (6, 'BlueValues', 'delta', None, None),
1249 (7, 'OtherBlues', 'delta', None, None),
1250 (8, 'FamilyBlues', 'delta', None, None),
1251 (9, 'FamilyOtherBlues', 'delta', None, None),
1252 ((12, 9), 'BlueScale', 'number', 0.039625, None),
1253 ((12, 10), 'BlueShift', 'number', 7, None),
1254 ((12, 11), 'BlueFuzz', 'number', 1, None),
1255 (10, 'StdHW', 'number', None, None),
1256 (11, 'StdVW', 'number', None, None),
1257 ((12, 12), 'StemSnapH', 'delta', None, None),
1258 ((12, 13), 'StemSnapV', 'delta', None, None),
1259 ((12, 14), 'ForceBold', 'number', 0, None),
jvrf2cf9c52002-05-23 21:50:36 +00001260 ((12, 15), 'ForceBoldThreshold', 'number', None, None), # deprecated
1261 ((12, 16), 'lenIV', 'number', None, None), # deprecated
jvr4756b3a2002-05-16 18:17:32 +00001262 ((12, 17), 'LanguageGroup', 'number', 0, None),
1263 ((12, 18), 'ExpansionFactor', 'number', 0.06, None),
1264 ((12, 19), 'initialRandomSeed', 'number', 0, None),
1265 (20, 'defaultWidthX', 'number', 0, None),
1266 (21, 'nominalWidthX', 'number', 0, None),
1267 (19, 'Subrs', 'number', None, SubrsConverter()),
1268]
1269
jvr4e5af602002-05-24 09:58:04 +00001270def addConverters(table):
1271 for i in range(len(table)):
1272 op, name, arg, default, conv = table[i]
1273 if conv is not None:
1274 continue
1275 if arg in ("delta", "array"):
1276 conv = ArrayConverter()
1277 elif arg == "number":
1278 conv = NumberConverter()
1279 elif arg == "SID":
1280 conv = SimpleConverter()
1281 else:
1282 assert 0
1283 table[i] = op, name, arg, default, conv
1284
1285addConverters(privateDictOperators)
1286addConverters(topDictOperators)
1287
jvr4756b3a2002-05-16 18:17:32 +00001288
1289class TopDictDecompiler(psCharStrings.DictDecompiler):
1290 operators = buildOperatorDict(topDictOperators)
1291
1292
1293class PrivateDictDecompiler(psCharStrings.DictDecompiler):
1294 operators = buildOperatorDict(privateDictOperators)
1295
1296
jvrf2cf9c52002-05-23 21:50:36 +00001297class DictCompiler:
1298
1299 def __init__(self, dictObj, strings, parent):
1300 assert isinstance(strings, IndexedStrings)
1301 self.dictObj = dictObj
1302 self.strings = strings
1303 self.parent = parent
1304 rawDict = {}
1305 for name in dictObj.order:
1306 value = getattr(dictObj, name, None)
1307 if value is None:
1308 continue
1309 conv = dictObj.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001310 value = conv.write(dictObj, value)
jvrf2cf9c52002-05-23 21:50:36 +00001311 if value == dictObj.defaults.get(name):
1312 continue
1313 rawDict[name] = value
1314 self.rawDict = rawDict
1315
jvr4e5af602002-05-24 09:58:04 +00001316 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +00001317 pass
1318
1319 def getDataLength(self):
jvr4e5af602002-05-24 09:58:04 +00001320 return len(self.compile("getDataLength"))
jvrf2cf9c52002-05-23 21:50:36 +00001321
jvr4e5af602002-05-24 09:58:04 +00001322 def compile(self, reason):
1323 if DEBUG:
1324 print "-- compiling %s for %s" % (self.__class__.__name__, reason)
jvred101512003-08-22 19:53:32 +00001325 print "in baseDict: ", self
jvrf2cf9c52002-05-23 21:50:36 +00001326 rawDict = self.rawDict
1327 data = []
1328 for name in self.dictObj.order:
1329 value = rawDict.get(name)
1330 if value is None:
1331 continue
1332 op, argType = self.opcodes[name]
1333 if type(argType) == TupleType:
1334 l = len(argType)
1335 assert len(value) == l, "value doesn't match arg type"
1336 for i in range(l):
jvred101512003-08-22 19:53:32 +00001337 arg = argType[i]
jvrf2cf9c52002-05-23 21:50:36 +00001338 v = value[i]
1339 arghandler = getattr(self, "arg_" + arg)
1340 data.append(arghandler(v))
1341 else:
1342 arghandler = getattr(self, "arg_" + argType)
1343 data.append(arghandler(value))
1344 data.append(op)
1345 return "".join(data)
1346
1347 def toFile(self, file):
jvr4e5af602002-05-24 09:58:04 +00001348 file.write(self.compile("toFile"))
jvrf2cf9c52002-05-23 21:50:36 +00001349
1350 def arg_number(self, num):
1351 return encodeNumber(num)
1352 def arg_SID(self, s):
1353 return psCharStrings.encodeIntCFF(self.strings.getSID(s))
1354 def arg_array(self, value):
1355 data = []
1356 for num in value:
1357 data.append(encodeNumber(num))
1358 return "".join(data)
1359 def arg_delta(self, value):
1360 out = []
1361 last = 0
1362 for v in value:
1363 out.append(v - last)
1364 last = v
1365 data = []
1366 for num in out:
1367 data.append(encodeNumber(num))
1368 return "".join(data)
1369
1370
1371def encodeNumber(num):
jvrf6ff48b2008-03-07 19:49:25 +00001372 if isinstance(num, float):
jvrf2cf9c52002-05-23 21:50:36 +00001373 return psCharStrings.encodeFloat(num)
1374 else:
1375 return psCharStrings.encodeIntCFF(num)
1376
1377
1378class TopDictCompiler(DictCompiler):
1379
1380 opcodes = buildOpcodeDict(topDictOperators)
1381
1382 def getChildren(self, strings):
1383 children = []
jvred101512003-08-22 19:53:32 +00001384 if hasattr(self.dictObj, "charset") and self.dictObj.charset:
jvrf2cf9c52002-05-23 21:50:36 +00001385 children.append(CharsetCompiler(strings, self.dictObj.charset, self))
jvrb9702ba2003-01-03 20:56:01 +00001386 if hasattr(self.dictObj, "Encoding"):
1387 encoding = self.dictObj.Encoding
1388 if not isinstance(encoding, StringType):
1389 children.append(EncodingCompiler(strings, encoding, self))
jvrce522412003-08-25 07:37:25 +00001390 if hasattr(self.dictObj, "FDSelect"):
jvred101512003-08-22 19:53:32 +00001391 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1392 # issues about merging the FDArrays. Here I assume that
1393 # either the font was read from XML, and teh FDSelect indices are all
1394 # in the charstring data, or the FDSelect array is already fully defined.
1395 fdSelect = self.dictObj.FDSelect
1396 if len(fdSelect) == 0: # probably read in from XML; assume fdIndex in CharString data
1397 charStrings = self.dictObj.CharStrings
1398 for name in self.dictObj.charset:
1399 charstring = charStrings[name]
1400 fdSelect.append(charStrings[name].fdSelectIndex)
1401 fdSelectComp = FDSelectCompiler(fdSelect, self)
1402 children.append(fdSelectComp)
jvrf2cf9c52002-05-23 21:50:36 +00001403 if hasattr(self.dictObj, "CharStrings"):
1404 items = []
1405 charStrings = self.dictObj.CharStrings
1406 for name in self.dictObj.charset:
1407 items.append(charStrings[name])
1408 charStringsComp = CharStringsCompiler(items, strings, self)
1409 children.append(charStringsComp)
jvrce522412003-08-25 07:37:25 +00001410 if hasattr(self.dictObj, "FDArray"):
jvred101512003-08-22 19:53:32 +00001411 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1412 # issues about merging the FDArrays. Here I assume that the FDArray info is correct
1413 # and complete.
1414 fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self)
1415 children.append(fdArrayIndexComp)
1416 children.extend(fdArrayIndexComp.getChildren(strings))
1417 if hasattr(self.dictObj, "Private"):
1418 privComp = self.dictObj.Private.getCompiler(strings, self)
1419 children.append(privComp)
1420 children.extend(privComp.getChildren(strings))
1421 return children
1422
1423
1424class FontDictCompiler(DictCompiler):
1425
1426 opcodes = buildOpcodeDict(topDictOperators)
1427
1428 def getChildren(self, strings):
1429 children = []
jvrf2cf9c52002-05-23 21:50:36 +00001430 if hasattr(self.dictObj, "Private"):
1431 privComp = self.dictObj.Private.getCompiler(strings, self)
1432 children.append(privComp)
1433 children.extend(privComp.getChildren(strings))
1434 return children
1435
1436
1437class PrivateDictCompiler(DictCompiler):
1438
1439 opcodes = buildOpcodeDict(privateDictOperators)
1440
jvr4e5af602002-05-24 09:58:04 +00001441 def setPos(self, pos, endPos):
1442 size = endPos - pos
jvrf2cf9c52002-05-23 21:50:36 +00001443 self.parent.rawDict["Private"] = size, pos
1444 self.pos = pos
1445
1446 def getChildren(self, strings):
1447 children = []
1448 if hasattr(self.dictObj, "Subrs"):
1449 children.append(self.dictObj.Subrs.getCompiler(strings, self))
1450 return children
1451
jvr4756b3a2002-05-16 18:17:32 +00001452
1453class BaseDict:
1454
jvr4e5af602002-05-24 09:58:04 +00001455 def __init__(self, strings=None, file=None, offset=None):
jvr4756b3a2002-05-16 18:17:32 +00001456 self.rawDict = {}
jvr767102e2002-05-17 07:06:32 +00001457 if DEBUG:
jvrf2cf9c52002-05-23 21:50:36 +00001458 print "loading %s at %s" % (self.__class__.__name__, offset)
jvr4756b3a2002-05-16 18:17:32 +00001459 self.file = file
1460 self.offset = offset
1461 self.strings = strings
jvr155aa752002-05-17 19:58:49 +00001462 self.skipNames = []
jvr4756b3a2002-05-16 18:17:32 +00001463
1464 def decompile(self, data):
jvrf2cf9c52002-05-23 21:50:36 +00001465 if DEBUG:
1466 print " length %s is %s" % (self.__class__.__name__, len(data))
1467 dec = self.decompilerClass(self.strings)
jvr4756b3a2002-05-16 18:17:32 +00001468 dec.decompile(data)
1469 self.rawDict = dec.getDict()
1470 self.postDecompile()
1471
1472 def postDecompile(self):
1473 pass
1474
jvrf2cf9c52002-05-23 21:50:36 +00001475 def getCompiler(self, strings, parent):
1476 return self.compilerClass(self, strings, parent)
1477
jvr4756b3a2002-05-16 18:17:32 +00001478 def __getattr__(self, name):
1479 value = self.rawDict.get(name)
1480 if value is None:
1481 value = self.defaults.get(name)
1482 if value is None:
1483 raise AttributeError, name
1484 conv = self.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001485 value = conv.read(self, value)
jvr4756b3a2002-05-16 18:17:32 +00001486 setattr(self, name, value)
1487 return value
1488
1489 def toXML(self, xmlWriter, progress):
1490 for name in self.order:
jvr155aa752002-05-17 19:58:49 +00001491 if name in self.skipNames:
1492 continue
jvr4756b3a2002-05-16 18:17:32 +00001493 value = getattr(self, name, None)
1494 if value is None:
1495 continue
jvr4e5af602002-05-24 09:58:04 +00001496 conv = self.converters[name]
jvr7ce0a132002-07-23 16:42:11 +00001497 conv.xmlWrite(xmlWriter, name, value, progress)
jvr4e5af602002-05-24 09:58:04 +00001498
1499 def fromXML(self, (name, attrs, content)):
1500 conv = self.converters[name]
1501 value = conv.xmlRead((name, attrs, content), self)
1502 setattr(self, name, value)
jvr4756b3a2002-05-16 18:17:32 +00001503
1504
1505class TopDict(BaseDict):
1506
1507 defaults = buildDefaults(topDictOperators)
1508 converters = buildConverters(topDictOperators)
1509 order = buildOrder(topDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001510 decompilerClass = TopDictDecompiler
1511 compilerClass = TopDictCompiler
Just7842e561999-12-16 21:34:53 +00001512
jvr4e5af602002-05-24 09:58:04 +00001513 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
jvr016ca762002-05-16 18:38:03 +00001514 BaseDict.__init__(self, strings, file, offset)
1515 self.GlobalSubrs = GlobalSubrs
1516
Just7842e561999-12-16 21:34:53 +00001517 def getGlyphOrder(self):
1518 return self.charset
1519
jvr4756b3a2002-05-16 18:17:32 +00001520 def postDecompile(self):
1521 offset = self.rawDict.get("CharStrings")
1522 if offset is None:
1523 return
1524 # get the number of glyphs beforehand.
1525 self.file.seek(offset)
jvra2ad5442002-05-17 18:36:07 +00001526 self.numGlyphs = readCard16(self.file)
Just7842e561999-12-16 21:34:53 +00001527
jvr016ca762002-05-16 18:38:03 +00001528 def toXML(self, xmlWriter, progress):
jvr155aa752002-05-17 19:58:49 +00001529 if hasattr(self, "CharStrings"):
jvr7ce0a132002-07-23 16:42:11 +00001530 self.decompileAllCharStrings(progress)
jvred101512003-08-22 19:53:32 +00001531 if hasattr(self, "ROS"):
1532 self.skipNames = ['Encoding']
jvr155aa752002-05-17 19:58:49 +00001533 if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
1534 # these values have default values, but I only want them to show up
1535 # in CID fonts.
1536 self.skipNames = ['CIDFontVersion', 'CIDFontRevision', 'CIDFontType',
1537 'CIDCount']
jvr016ca762002-05-16 18:38:03 +00001538 BaseDict.toXML(self, xmlWriter, progress)
jvr767102e2002-05-17 07:06:32 +00001539
jvr7ce0a132002-07-23 16:42:11 +00001540 def decompileAllCharStrings(self, progress):
jvr4e5af602002-05-24 09:58:04 +00001541 # XXX only when doing ttdump -i?
jvr7ce0a132002-07-23 16:42:11 +00001542 i = 0
jvra2ad5442002-05-17 18:36:07 +00001543 for charString in self.CharStrings.values():
jvred101512003-08-22 19:53:32 +00001544 try:
1545 charString.decompile()
1546 except:
1547 print "Error in charstring ", i
1548 import sys
1549 type, value = sys. exc_info()[0:2]
1550 raise type(value)
jvr7ce0a132002-07-23 16:42:11 +00001551 if not i % 30 and progress:
1552 progress.increment(0) # update
1553 i = i + 1
Just7842e561999-12-16 21:34:53 +00001554
1555
jvred101512003-08-22 19:53:32 +00001556class FontDict(BaseDict):
1557
1558 defaults = buildDefaults(topDictOperators)
1559 converters = buildConverters(topDictOperators)
1560 order = buildOrder(topDictOperators)
1561 decompilerClass = None
1562 compilerClass = FontDictCompiler
1563
1564 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
1565 BaseDict.__init__(self, strings, file, offset)
1566 self.GlobalSubrs = GlobalSubrs
1567
1568 def getGlyphOrder(self):
1569 return self.charset
1570
1571 def toXML(self, xmlWriter, progress):
1572 self.skipNames = ['Encoding']
1573 BaseDict.toXML(self, xmlWriter, progress)
1574
1575
1576
jvr4756b3a2002-05-16 18:17:32 +00001577class PrivateDict(BaseDict):
1578 defaults = buildDefaults(privateDictOperators)
1579 converters = buildConverters(privateDictOperators)
1580 order = buildOrder(privateDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001581 decompilerClass = PrivateDictDecompiler
1582 compilerClass = PrivateDictCompiler
Just7842e561999-12-16 21:34:53 +00001583
1584
jvre3275582002-05-14 12:22:03 +00001585class IndexedStrings:
1586
jvr767102e2002-05-17 07:06:32 +00001587 """SID -> string mapping."""
1588
1589 def __init__(self, file=None):
1590 if file is None:
jvre3275582002-05-14 12:22:03 +00001591 strings = []
jvr767102e2002-05-17 07:06:32 +00001592 else:
jvr4e5af602002-05-24 09:58:04 +00001593 strings = list(Index(file))
jvre3275582002-05-14 12:22:03 +00001594 self.strings = strings
1595
jvrf2cf9c52002-05-23 21:50:36 +00001596 def getCompiler(self):
1597 return IndexedStringsCompiler(self, None, None)
1598
1599 def __len__(self):
1600 return len(self.strings)
1601
jvre3275582002-05-14 12:22:03 +00001602 def __getitem__(self, SID):
1603 if SID < cffStandardStringCount:
1604 return cffStandardStrings[SID]
1605 else:
1606 return self.strings[SID - cffStandardStringCount]
1607
1608 def getSID(self, s):
1609 if not hasattr(self, "stringMapping"):
1610 self.buildStringMapping()
1611 if cffStandardStringMapping.has_key(s):
1612 SID = cffStandardStringMapping[s]
jvrf2cf9c52002-05-23 21:50:36 +00001613 elif self.stringMapping.has_key(s):
jvre3275582002-05-14 12:22:03 +00001614 SID = self.stringMapping[s]
1615 else:
1616 SID = len(self.strings) + cffStandardStringCount
1617 self.strings.append(s)
1618 self.stringMapping[s] = SID
1619 return SID
1620
1621 def getStrings(self):
1622 return self.strings
1623
1624 def buildStringMapping(self):
1625 self.stringMapping = {}
1626 for index in range(len(self.strings)):
1627 self.stringMapping[self.strings[index]] = index + cffStandardStringCount
1628
1629
Just7842e561999-12-16 21:34:53 +00001630# The 391 Standard Strings as used in the CFF format.
1631# from Adobe Technical None #5176, version 1.0, 18 March 1998
1632
1633cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
1634 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
1635 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
1636 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
1637 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C',
1638 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
1639 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
1640 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
1641 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
1642 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
1643 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
1644 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
1645 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
1646 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
1647 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
1648 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
1649 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
1650 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
1651 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
1652 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
1653 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
1654 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
1655 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
1656 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
1657 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
1658 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
1659 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
1660 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
1661 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
1662 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
1663 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
1664 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
1665 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
1666 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
1667 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
1668 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
1669 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
1670 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
1671 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
1672 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
1673 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
1674 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
1675 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
1676 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
1677 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
1678 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
1679 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
1680 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
1681 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
1682 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
1683 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
1684 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
1685 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
1686 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
1687 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
1688 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
1689 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
1690 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
1691 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
1692 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
1693 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
1694 '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
1695 'Semibold'
1696]
1697
1698cffStandardStringCount = 391
1699assert len(cffStandardStrings) == cffStandardStringCount
1700# build reverse mapping
1701cffStandardStringMapping = {}
1702for _i in range(cffStandardStringCount):
1703 cffStandardStringMapping[cffStandardStrings[_i]] = _i
jvrc60a44f2006-10-21 13:41:18 +00001704
1705cffISOAdobeStrings = [".notdef", "space", "exclam", "quotedbl", "numbersign",
1706"dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright",
1707"asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two",
1708"three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon",
1709"less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G",
1710"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
1711"X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum",
1712"underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
1713"k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
1714"braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent",
1715"sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle",
1716"quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl",
1717"endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet",
1718"quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis",
1719"perthousand", "questiondown", "grave", "acute", "circumflex", "tilde",
1720"macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
1721"ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE",
1722"ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls",
1723"onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus",
1724"Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn",
1725"threequarters", "twosuperior", "registered", "minus", "eth", "multiply",
1726"threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
1727"Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave",
1728"Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute",
1729"Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute",
1730"Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute",
1731"acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute",
1732"ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis",
1733"igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde",
1734"scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis",
1735"zcaron"]
1736
1737cffISOAdobeStringCount = 229
1738assert len(cffISOAdobeStrings) == cffISOAdobeStringCount
1739
1740cffIExpertStrings = [".notdef", "space", "exclamsmall", "Hungarumlautsmall",
1741"dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall",
1742"parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader",
1743"comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle",
1744"twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle",
1745"sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon",
1746"commasuperior", "threequartersemdash", "periodsuperior", "questionsmall",
1747"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1748"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1749"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1750"parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
1751"Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall",
1752"Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
1753"Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall",
1754"Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall",
1755"exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall",
1756"Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall",
1757"figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall",
1758"onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth",
1759"threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds",
1760"zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior",
1761"fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior",
1762"zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior",
1763"fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior",
1764"centinferior", "dollarinferior", "periodinferior", "commainferior",
1765"Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall",
1766"Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall",
1767"Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall",
1768"Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall",
1769"Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall",
1770"Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall",
1771"Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall",
1772"Ydieresissmall"]
1773
1774cffExpertStringCount = 166
1775assert len(cffIExpertStrings) == cffExpertStringCount
1776
1777cffExpertSubsetStrings = [".notdef", "space", "dollaroldstyle",
1778"dollarsuperior", "parenleftsuperior", "parenrightsuperior", "twodotenleader",
1779"onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle",
1780"oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle",
1781"sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon",
1782"semicolon", "commasuperior", "threequartersemdash", "periodsuperior",
1783"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1784"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1785"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1786"parenrightinferior", "hyphensuperior", "colonmonetary", "onefitted", "rupiah",
1787"centoldstyle", "figuredash", "hypheninferior", "onequarter", "onehalf",
1788"threequarters", "oneeighth", "threeeighths", "fiveeighths", "seveneighths",
1789"onethird", "twothirds", "zerosuperior", "onesuperior", "twosuperior",
1790"threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior",
1791"eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior",
1792"threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior",
1793"eightinferior", "nineinferior", "centinferior", "dollarinferior",
1794"periodinferior", "commainferior"]
1795
1796cffExpertSubsetStringCount = 87
1797assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount