blob: 54c9cf2c3287954847e85ba11c5e4b654075f899 [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
Behdad Esfahbod8413c102013-09-17 16:59:39 -04007import struct
8from fontTools.misc import sstruct
Just7842e561999-12-16 21:34:53 +00009import string
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:
jvr2a9bcde2008-03-07 19:56:17 +0000114 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000115 continue
116 topDict.fromXML(element)
117 elif name == "GlobalSubrs":
118 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000119 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000120 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:
jvr2a9bcde2008-03-07 19:56:17 +0000448 if isinstance(element, basestring):
jvred101512003-08-22 19:53:32 +0000449 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'):
jvr91bca422012-10-18 12:49:22 +0000553 sel = self.fdSelect[index] # index is not defined at this point. Read R. ?
jvred101512003-08-22 19:53:32 +0000554 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:
jvr2a9bcde2008-03-07 19:56:17 +0000586 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000587 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:
jvr2a9bcde2008-03-07 19:56:17 +0000636 if isinstance(op, tuple):
jvrf2cf9c52002-05-23 21:50:36 +0000637 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):
Behdad Esfahbod8c5c9662013-10-28 13:20:00 +0100675 def xmlWrite(self, xmlWriter, name, value, progress):
676 # Store as UTF-8
677 value = unicode(value, "latin-1").encode("utf-8")
678 xmlWriter.simpletag(name, value=value)
679 xmlWriter.newline()
jvre2ca9b52002-09-09 14:18:39 +0000680 def xmlRead(self, (name, attrs, content), parent):
681 s = unicode(attrs["value"], "utf-8")
682 return s.encode("latin-1")
683
684
jvr4e5af602002-05-24 09:58:04 +0000685def parseNum(s):
686 try:
687 value = int(s)
688 except:
689 value = float(s)
690 return value
691
692class NumberConverter(SimpleConverter):
693 def xmlRead(self, (name, attrs, content), parent):
694 return parseNum(attrs["value"])
695
696class ArrayConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000697 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000698 value = map(str, value)
699 xmlWriter.simpletag(name, value=" ".join(value))
700 xmlWriter.newline()
701 def xmlRead(self, (name, attrs, content), parent):
702 values = attrs["value"].split()
703 return map(parseNum, values)
704
705class TableConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000706 def xmlWrite(self, xmlWriter, name, value, progress):
jvra2ad5442002-05-17 18:36:07 +0000707 xmlWriter.begintag(name)
708 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000709 value.toXML(xmlWriter, progress)
jvra2ad5442002-05-17 18:36:07 +0000710 xmlWriter.endtag(name)
711 xmlWriter.newline()
jvr4e5af602002-05-24 09:58:04 +0000712 def xmlRead(self, (name, attrs, content), parent):
713 ob = self.getClass()()
714 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000715 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000716 continue
717 ob.fromXML(element)
718 return ob
jvra2ad5442002-05-17 18:36:07 +0000719
jvr4e5af602002-05-24 09:58:04 +0000720class PrivateDictConverter(TableConverter):
721 def getClass(self):
722 return PrivateDict
jvr4756b3a2002-05-16 18:17:32 +0000723 def read(self, parent, value):
724 size, offset = value
725 file = parent.file
jvr4e5af602002-05-24 09:58:04 +0000726 priv = PrivateDict(parent.strings, file, offset)
jvr4756b3a2002-05-16 18:17:32 +0000727 file.seek(offset)
728 data = file.read(size)
729 len(data) == size
jvr4e5af602002-05-24 09:58:04 +0000730 priv.decompile(data)
731 return priv
jvrf2cf9c52002-05-23 21:50:36 +0000732 def write(self, parent, value):
733 return (0, 0) # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000734
jvr4e5af602002-05-24 09:58:04 +0000735class SubrsConverter(TableConverter):
736 def getClass(self):
737 return SubrsIndex
jvr4756b3a2002-05-16 18:17:32 +0000738 def read(self, parent, value):
739 file = parent.file
740 file.seek(parent.offset + value) # Offset(self)
jvr4e5af602002-05-24 09:58:04 +0000741 return SubrsIndex(file)
jvrf2cf9c52002-05-23 21:50:36 +0000742 def write(self, parent, value):
743 return 0 # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000744
jvr4e5af602002-05-24 09:58:04 +0000745class CharStringsConverter(TableConverter):
jvr4756b3a2002-05-16 18:17:32 +0000746 def read(self, parent, value):
747 file = parent.file
jvr767102e2002-05-17 07:06:32 +0000748 charset = parent.charset
jvra2ad5442002-05-17 18:36:07 +0000749 globalSubrs = parent.GlobalSubrs
750 if hasattr(parent, "ROS"):
751 fdSelect, fdArray = parent.FDSelect, parent.FDArray
752 private = None
753 else:
754 fdSelect, fdArray = None, None
755 private = parent.Private
jvr4756b3a2002-05-16 18:17:32 +0000756 file.seek(value) # Offset(0)
jvra2ad5442002-05-17 18:36:07 +0000757 return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
jvrf2cf9c52002-05-23 21:50:36 +0000758 def write(self, parent, value):
759 return 0 # dummy value
jvr4e5af602002-05-24 09:58:04 +0000760 def xmlRead(self, (name, attrs, content), parent):
jvred101512003-08-22 19:53:32 +0000761 if hasattr(parent, "ROS"):
762 # if it is a CID-keyed font, then the private Dict is extracted from the parent.FDArray
763 private, fdSelect, fdArray = None, parent.FDSelect, parent.FDArray
764 else:
765 # if it is a name-keyed font, then the private dict is in the top dict, and there is no fdArray.
766 private, fdSelect, fdArray = parent.Private, None, None
767 charStrings = CharStrings(None, None, parent.GlobalSubrs, private, fdSelect, fdArray)
jvr4e5af602002-05-24 09:58:04 +0000768 charStrings.fromXML((name, attrs, content))
769 return charStrings
jvr4756b3a2002-05-16 18:17:32 +0000770
771class CharsetConverter:
772 def read(self, parent, value):
773 isCID = hasattr(parent, "ROS")
774 if value > 2:
775 numGlyphs = parent.numGlyphs
776 file = parent.file
777 file.seek(value)
jvrf2cf9c52002-05-23 21:50:36 +0000778 if DEBUG:
779 print "loading charset at %s" % value
jvra2ad5442002-05-17 18:36:07 +0000780 format = readCard8(file)
Just7842e561999-12-16 21:34:53 +0000781 if format == 0:
jvrc60a44f2006-10-21 13:41:18 +0000782 charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
jvr4756b3a2002-05-16 18:17:32 +0000783 elif format == 1 or format == 2:
784 charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
Just7842e561999-12-16 21:34:53 +0000785 else:
jvr1890b952002-05-15 07:41:30 +0000786 raise NotImplementedError
jvr4756b3a2002-05-16 18:17:32 +0000787 assert len(charset) == numGlyphs
jvrf2cf9c52002-05-23 21:50:36 +0000788 if DEBUG:
789 print " charset end at %s" % file.tell()
jvrc60a44f2006-10-21 13:41:18 +0000790 else: # offset == 0 -> no charset data.
791 if isCID or not parent.rawDict.has_key("CharStrings"):
792 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 +0000793 charset = None
794 elif value == 0:
jvrc60a44f2006-10-21 13:41:18 +0000795 charset = cffISOAdobeStrings
jvr4756b3a2002-05-16 18:17:32 +0000796 elif value == 1:
jvrc60a44f2006-10-21 13:41:18 +0000797 charset = cffIExpertStrings
jvr4756b3a2002-05-16 18:17:32 +0000798 elif value == 2:
jvrc60a44f2006-10-21 13:41:18 +0000799 charset = cffExpertSubsetStrings
jvr4756b3a2002-05-16 18:17:32 +0000800 return charset
jvrc60a44f2006-10-21 13:41:18 +0000801
jvrf2cf9c52002-05-23 21:50:36 +0000802 def write(self, parent, value):
803 return 0 # dummy value
jvr7ce0a132002-07-23 16:42:11 +0000804 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000805 # XXX only write charset when not in OT/TTX context, where we
806 # dump charset as a separate "GlyphOrder" table.
807 ##xmlWriter.simpletag("charset")
808 xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
jvr4756b3a2002-05-16 18:17:32 +0000809 xmlWriter.newline()
jvr4e5af602002-05-24 09:58:04 +0000810 def xmlRead(self, (name, attrs, content), parent):
811 if 0:
812 return safeEval(attrs["value"])
jvr4756b3a2002-05-16 18:17:32 +0000813
814
jvrf2cf9c52002-05-23 21:50:36 +0000815class CharsetCompiler:
816
817 def __init__(self, strings, charset, parent):
818 assert charset[0] == '.notdef'
jvred101512003-08-22 19:53:32 +0000819 isCID = hasattr(parent.dictObj, "ROS")
820 data0 = packCharset0(charset, isCID, strings)
821 data = packCharset(charset, isCID, strings)
jvr6004baf2002-05-24 10:35:13 +0000822 if len(data) < len(data0):
823 self.data = data
824 else:
825 self.data = data0
jvrf2cf9c52002-05-23 21:50:36 +0000826 self.parent = parent
827
jvr4e5af602002-05-24 09:58:04 +0000828 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000829 self.parent.rawDict["charset"] = pos
830
831 def getDataLength(self):
832 return len(self.data)
833
834 def toFile(self, file):
835 file.write(self.data)
836
837
jvred101512003-08-22 19:53:32 +0000838def getCIDfromName(name, strings):
839 return int(name[3:])
840
841def getSIDfromName(name, strings):
842 return strings.getSID(name)
843
844def packCharset0(charset, isCID, strings):
jvr6004baf2002-05-24 10:35:13 +0000845 format = 0
846 data = [packCard8(format)]
jvred101512003-08-22 19:53:32 +0000847 if isCID:
848 getNameID = getCIDfromName
849 else:
850 getNameID = getSIDfromName
851
jvr6004baf2002-05-24 10:35:13 +0000852 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000853 data.append(packCard16(getNameID(name,strings)))
jvr6004baf2002-05-24 10:35:13 +0000854 return "".join(data)
855
jvred101512003-08-22 19:53:32 +0000856
857def packCharset(charset, isCID, strings):
jvr6004baf2002-05-24 10:35:13 +0000858 format = 1
jvr6004baf2002-05-24 10:35:13 +0000859 ranges = []
860 first = None
861 end = 0
jvred101512003-08-22 19:53:32 +0000862 if isCID:
863 getNameID = getCIDfromName
864 else:
865 getNameID = getSIDfromName
866
jvr6004baf2002-05-24 10:35:13 +0000867 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000868 SID = getNameID(name, strings)
jvr6004baf2002-05-24 10:35:13 +0000869 if first is None:
870 first = SID
871 elif end + 1 <> SID:
872 nLeft = end - first
873 if nLeft > 255:
874 format = 2
875 ranges.append((first, nLeft))
876 first = SID
877 end = SID
878 nLeft = end - first
879 if nLeft > 255:
880 format = 2
881 ranges.append((first, nLeft))
882
jvr74cd1ef2002-05-24 10:38:04 +0000883 data = [packCard8(format)]
jvr6004baf2002-05-24 10:35:13 +0000884 if format == 1:
885 nLeftFunc = packCard8
886 else:
887 nLeftFunc = packCard16
888 for first, nLeft in ranges:
889 data.append(packCard16(first) + nLeftFunc(nLeft))
jvrb58176e2002-05-24 11:55:37 +0000890 return "".join(data)
jvr6004baf2002-05-24 10:35:13 +0000891
jvrc60a44f2006-10-21 13:41:18 +0000892def parseCharset0(numGlyphs, file, strings, isCID):
jvrf2cf9c52002-05-23 21:50:36 +0000893 charset = [".notdef"]
jvrc60a44f2006-10-21 13:41:18 +0000894 if isCID:
895 for i in range(numGlyphs - 1):
896 CID = readCard16(file)
897 charset.append("cid" + string.zfill(str(CID), 5) )
898 else:
899 for i in range(numGlyphs - 1):
900 SID = readCard16(file)
901 charset.append(strings[SID])
jvrf2cf9c52002-05-23 21:50:36 +0000902 return charset
903
jvr4756b3a2002-05-16 18:17:32 +0000904def parseCharset(numGlyphs, file, strings, isCID, format):
905 charset = ['.notdef']
906 count = 1
907 if format == 1:
jvra2ad5442002-05-17 18:36:07 +0000908 nLeftFunc = readCard8
jvr4756b3a2002-05-16 18:17:32 +0000909 else:
jvra2ad5442002-05-17 18:36:07 +0000910 nLeftFunc = readCard16
jvr4756b3a2002-05-16 18:17:32 +0000911 while count < numGlyphs:
jvra2ad5442002-05-17 18:36:07 +0000912 first = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000913 nLeft = nLeftFunc(file)
914 if isCID:
915 for CID in range(first, first+nLeft+1):
jvrc60a44f2006-10-21 13:41:18 +0000916 charset.append("cid" + string.zfill(str(CID), 5) )
jvr1890b952002-05-15 07:41:30 +0000917 else:
jvr4756b3a2002-05-16 18:17:32 +0000918 for SID in range(first, first+nLeft+1):
919 charset.append(strings[SID])
920 count = count + nLeft + 1
921 return charset
922
923
jvrb9702ba2003-01-03 20:56:01 +0000924class EncodingCompiler:
925
926 def __init__(self, strings, encoding, parent):
jvr2a9bcde2008-03-07 19:56:17 +0000927 assert not isinstance(encoding, basestring)
jvrb9702ba2003-01-03 20:56:01 +0000928 data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
929 data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
930 if len(data0) < len(data1):
931 self.data = data0
932 else:
933 self.data = data1
934 self.parent = parent
935
936 def setPos(self, pos, endPos):
937 self.parent.rawDict["Encoding"] = pos
938
939 def getDataLength(self):
940 return len(self.data)
941
942 def toFile(self, file):
943 file.write(self.data)
944
945
946class EncodingConverter(SimpleConverter):
947
948 def read(self, parent, value):
949 if value == 0:
950 return "StandardEncoding"
951 elif value == 1:
952 return "ExpertEncoding"
953 else:
954 assert value > 1
955 file = parent.file
956 file.seek(value)
957 if DEBUG:
958 print "loading Encoding at %s" % value
959 format = readCard8(file)
960 haveSupplement = format & 0x80
961 if haveSupplement:
962 raise NotImplementedError, "Encoding supplements are not yet supported"
963 format = format & 0x7f
964 if format == 0:
965 encoding = parseEncoding0(parent.charset, file, haveSupplement,
966 parent.strings)
967 elif format == 1:
968 encoding = parseEncoding1(parent.charset, file, haveSupplement,
969 parent.strings)
970 return encoding
971
972 def write(self, parent, value):
973 if value == "StandardEncoding":
974 return 0
975 elif value == "ExpertEncoding":
976 return 1
977 return 0 # dummy value
978
979 def xmlWrite(self, xmlWriter, name, value, progress):
980 if value in ("StandardEncoding", "ExpertEncoding"):
981 xmlWriter.simpletag(name, name=value)
982 xmlWriter.newline()
983 return
984 xmlWriter.begintag(name)
985 xmlWriter.newline()
986 for code in range(len(value)):
987 glyphName = value[code]
988 if glyphName != ".notdef":
989 xmlWriter.simpletag("map", code=hex(code), name=glyphName)
990 xmlWriter.newline()
991 xmlWriter.endtag(name)
992 xmlWriter.newline()
993
994 def xmlRead(self, (name, attrs, content), parent):
995 if attrs.has_key("name"):
996 return attrs["name"]
997 encoding = [".notdef"] * 256
998 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000999 if isinstance(element, basestring):
jvrb9702ba2003-01-03 20:56:01 +00001000 continue
1001 name, attrs, content = element
1002 code = safeEval(attrs["code"])
1003 glyphName = attrs["name"]
1004 encoding[code] = glyphName
1005 return encoding
1006
1007
1008def parseEncoding0(charset, file, haveSupplement, strings):
1009 nCodes = readCard8(file)
1010 encoding = [".notdef"] * 256
1011 for glyphID in range(1, nCodes + 1):
1012 code = readCard8(file)
1013 if code != 0:
1014 encoding[code] = charset[glyphID]
1015 return encoding
1016
1017def parseEncoding1(charset, file, haveSupplement, strings):
1018 nRanges = readCard8(file)
1019 encoding = [".notdef"] * 256
1020 glyphID = 1
1021 for i in range(nRanges):
1022 code = readCard8(file)
1023 nLeft = readCard8(file)
1024 for glyphID in range(glyphID, glyphID + nLeft + 1):
1025 encoding[code] = charset[glyphID]
1026 code = code + 1
1027 glyphID = glyphID + 1
1028 return encoding
1029
1030def packEncoding0(charset, encoding, strings):
1031 format = 0
1032 m = {}
1033 for code in range(len(encoding)):
1034 name = encoding[code]
1035 if name != ".notdef":
1036 m[name] = code
1037 codes = []
1038 for name in charset[1:]:
1039 code = m.get(name)
1040 codes.append(code)
1041
1042 while codes and codes[-1] is None:
1043 codes.pop()
1044
1045 data = [packCard8(format), packCard8(len(codes))]
1046 for code in codes:
1047 if code is None:
1048 code = 0
1049 data.append(packCard8(code))
1050 return "".join(data)
1051
1052def packEncoding1(charset, encoding, strings):
1053 format = 1
1054 m = {}
1055 for code in range(len(encoding)):
1056 name = encoding[code]
1057 if name != ".notdef":
1058 m[name] = code
1059 ranges = []
1060 first = None
1061 end = 0
1062 for name in charset[1:]:
1063 code = m.get(name, -1)
1064 if first is None:
1065 first = code
1066 elif end + 1 <> code:
1067 nLeft = end - first
1068 ranges.append((first, nLeft))
1069 first = code
1070 end = code
1071 nLeft = end - first
1072 ranges.append((first, nLeft))
1073
1074 # remove unencoded glyphs at the end.
1075 while ranges and ranges[-1][0] == -1:
1076 ranges.pop()
1077
1078 data = [packCard8(format), packCard8(len(ranges))]
1079 for first, nLeft in ranges:
1080 if first == -1: # unencoded
1081 first = 0
1082 data.append(packCard8(first) + packCard8(nLeft))
1083 return "".join(data)
1084
1085
jvr4e5af602002-05-24 09:58:04 +00001086class FDArrayConverter(TableConverter):
jvred101512003-08-22 19:53:32 +00001087
jvra2ad5442002-05-17 18:36:07 +00001088 def read(self, parent, value):
1089 file = parent.file
1090 file.seek(value)
jvred101512003-08-22 19:53:32 +00001091 fdArray = FDArrayIndex(file)
jvra2ad5442002-05-17 18:36:07 +00001092 fdArray.strings = parent.strings
1093 fdArray.GlobalSubrs = parent.GlobalSubrs
1094 return fdArray
1095
jvred101512003-08-22 19:53:32 +00001096 def write(self, parent, value):
1097 return 0 # dummy value
1098
1099 def xmlRead(self, (name, attrs, content), parent):
1100 fdArray = FDArrayIndex()
1101 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +00001102 if isinstance(element, basestring):
jvred101512003-08-22 19:53:32 +00001103 continue
1104 fdArray.fromXML(element)
1105 return fdArray
1106
jvra2ad5442002-05-17 18:36:07 +00001107
1108class FDSelectConverter:
jvred101512003-08-22 19:53:32 +00001109
jvra2ad5442002-05-17 18:36:07 +00001110 def read(self, parent, value):
1111 file = parent.file
1112 file.seek(value)
jvred101512003-08-22 19:53:32 +00001113 fdSelect = FDSelect(file, parent.numGlyphs)
1114 return fdSelect
1115
1116 def write(self, parent, value):
1117 return 0 # dummy value
1118
1119 # The FDSelect glyph data is written out to XML in the charstring keys,
1120 # so we write out only the format selector
jvr7ce0a132002-07-23 16:42:11 +00001121 def xmlWrite(self, xmlWriter, name, value, progress):
jvred101512003-08-22 19:53:32 +00001122 xmlWriter.simpletag(name, [('format', value.format)])
1123 xmlWriter.newline()
1124
1125 def xmlRead(self, (name, attrs, content), parent):
1126 format = safeEval(attrs["format"])
1127 file = None
1128 numGlyphs = None
1129 fdSelect = FDSelect(file, numGlyphs, format)
1130 return fdSelect
1131
1132
1133def packFDSelect0(fdSelectArray):
1134 format = 0
1135 data = [packCard8(format)]
1136 for index in fdSelectArray:
1137 data.append(packCard8(index))
1138 return "".join(data)
1139
1140
1141def packFDSelect3(fdSelectArray):
1142 format = 3
1143 fdRanges = []
1144 first = None
1145 end = 0
1146 lenArray = len(fdSelectArray)
1147 lastFDIndex = -1
1148 for i in range(lenArray):
1149 fdIndex = fdSelectArray[i]
1150 if lastFDIndex != fdIndex:
1151 fdRanges.append([i, fdIndex])
1152 lastFDIndex = fdIndex
1153 sentinelGID = i + 1
1154
1155 data = [packCard8(format)]
1156 data.append(packCard16( len(fdRanges) ))
1157 for fdRange in fdRanges:
1158 data.append(packCard16(fdRange[0]))
1159 data.append(packCard8(fdRange[1]))
1160 data.append(packCard16(sentinelGID))
1161 return "".join(data)
1162
1163
1164class FDSelectCompiler:
1165
1166 def __init__(self, fdSelect, parent):
1167 format = fdSelect.format
1168 fdSelectArray = fdSelect.gidArray
1169 if format == 0:
1170 self.data = packFDSelect0(fdSelectArray)
1171 elif format == 3:
1172 self.data = packFDSelect3(fdSelectArray)
1173 else:
1174 # choose smaller of the two formats
1175 data0 = packFDSelect0(fdSelectArray)
1176 data3 = packFDSelect3(fdSelectArray)
1177 if len(data0) < len(data3):
1178 self.data = data0
1179 fdSelect.format = 0
1180 else:
1181 self.data = data3
1182 fdSelect.format = 3
1183
1184 self.parent = parent
1185
1186 def setPos(self, pos, endPos):
1187 self.parent.rawDict["FDSelect"] = pos
1188
1189 def getDataLength(self):
1190 return len(self.data)
1191
1192 def toFile(self, file):
1193 file.write(self.data)
jvra2ad5442002-05-17 18:36:07 +00001194
1195
jvr4e5af602002-05-24 09:58:04 +00001196class ROSConverter(SimpleConverter):
jvred101512003-08-22 19:53:32 +00001197
jvr7ce0a132002-07-23 16:42:11 +00001198 def xmlWrite(self, xmlWriter, name, value, progress):
jvr155aa752002-05-17 19:58:49 +00001199 registry, order, supplement = value
jvrf2cf9c52002-05-23 21:50:36 +00001200 xmlWriter.simpletag(name, [('Registry', registry), ('Order', order),
1201 ('Supplement', supplement)])
jvr4afb2572002-05-18 20:07:01 +00001202 xmlWriter.newline()
jvr155aa752002-05-17 19:58:49 +00001203
jvred101512003-08-22 19:53:32 +00001204 def xmlRead(self, (name, attrs, content), parent):
1205 return (attrs['Registry'], attrs['Order'], safeEval(attrs['Supplement']))
1206
1207
jvr155aa752002-05-17 19:58:49 +00001208
jvr4756b3a2002-05-16 18:17:32 +00001209topDictOperators = [
1210# opcode name argument type default converter
jvr155aa752002-05-17 19:58:49 +00001211 ((12, 30), 'ROS', ('SID','SID','number'), None, ROSConverter()),
jvrf2cf9c52002-05-23 21:50:36 +00001212 ((12, 20), 'SyntheticBase', 'number', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001213 (0, 'version', 'SID', None, None),
jvre2ca9b52002-09-09 14:18:39 +00001214 (1, 'Notice', 'SID', None, Latin1Converter()),
1215 ((12, 0), 'Copyright', 'SID', None, Latin1Converter()),
jvr4756b3a2002-05-16 18:17:32 +00001216 (2, 'FullName', 'SID', None, None),
jvr155aa752002-05-17 19:58:49 +00001217 ((12, 38), 'FontName', 'SID', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001218 (3, 'FamilyName', 'SID', None, None),
1219 (4, 'Weight', 'SID', None, None),
1220 ((12, 1), 'isFixedPitch', 'number', 0, None),
1221 ((12, 2), 'ItalicAngle', 'number', 0, None),
1222 ((12, 3), 'UnderlinePosition', 'number', None, None),
1223 ((12, 4), 'UnderlineThickness', 'number', 50, None),
1224 ((12, 5), 'PaintType', 'number', 0, None),
1225 ((12, 6), 'CharstringType', 'number', 2, None),
1226 ((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0], None),
1227 (13, 'UniqueID', 'number', None, None),
1228 (5, 'FontBBox', 'array', [0,0,0,0], None),
1229 ((12, 8), 'StrokeWidth', 'number', 0, None),
1230 (14, 'XUID', 'array', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001231 ((12, 21), 'PostScript', 'SID', None, None),
1232 ((12, 22), 'BaseFontName', 'SID', None, None),
1233 ((12, 23), 'BaseFontBlend', 'delta', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001234 ((12, 31), 'CIDFontVersion', 'number', 0, None),
1235 ((12, 32), 'CIDFontRevision', 'number', 0, None),
1236 ((12, 33), 'CIDFontType', 'number', 0, None),
1237 ((12, 34), 'CIDCount', 'number', 8720, None),
jvred101512003-08-22 19:53:32 +00001238 (15, 'charset', 'number', 0, CharsetConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001239 ((12, 35), 'UIDBase', 'number', None, None),
jvrb9702ba2003-01-03 20:56:01 +00001240 (16, 'Encoding', 'number', 0, EncodingConverter()),
jvr155aa752002-05-17 19:58:49 +00001241 (18, 'Private', ('number','number'), None, PrivateDictConverter()),
jvred101512003-08-22 19:53:32 +00001242 ((12, 37), 'FDSelect', 'number', None, FDSelectConverter()),
1243 ((12, 36), 'FDArray', 'number', None, FDArrayConverter()),
jvr155aa752002-05-17 19:58:49 +00001244 (17, 'CharStrings', 'number', None, CharStringsConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001245]
1246
jvred101512003-08-22 19:53:32 +00001247# Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order,
1248# in order for the font to compile back from xml.
1249
1250
jvr4756b3a2002-05-16 18:17:32 +00001251privateDictOperators = [
1252# opcode name argument type default converter
1253 (6, 'BlueValues', 'delta', None, None),
1254 (7, 'OtherBlues', 'delta', None, None),
1255 (8, 'FamilyBlues', 'delta', None, None),
1256 (9, 'FamilyOtherBlues', 'delta', None, None),
1257 ((12, 9), 'BlueScale', 'number', 0.039625, None),
1258 ((12, 10), 'BlueShift', 'number', 7, None),
1259 ((12, 11), 'BlueFuzz', 'number', 1, None),
1260 (10, 'StdHW', 'number', None, None),
1261 (11, 'StdVW', 'number', None, None),
1262 ((12, 12), 'StemSnapH', 'delta', None, None),
1263 ((12, 13), 'StemSnapV', 'delta', None, None),
1264 ((12, 14), 'ForceBold', 'number', 0, None),
jvrf2cf9c52002-05-23 21:50:36 +00001265 ((12, 15), 'ForceBoldThreshold', 'number', None, None), # deprecated
1266 ((12, 16), 'lenIV', 'number', None, None), # deprecated
jvr4756b3a2002-05-16 18:17:32 +00001267 ((12, 17), 'LanguageGroup', 'number', 0, None),
1268 ((12, 18), 'ExpansionFactor', 'number', 0.06, None),
1269 ((12, 19), 'initialRandomSeed', 'number', 0, None),
1270 (20, 'defaultWidthX', 'number', 0, None),
1271 (21, 'nominalWidthX', 'number', 0, None),
1272 (19, 'Subrs', 'number', None, SubrsConverter()),
1273]
1274
jvr4e5af602002-05-24 09:58:04 +00001275def addConverters(table):
1276 for i in range(len(table)):
1277 op, name, arg, default, conv = table[i]
1278 if conv is not None:
1279 continue
1280 if arg in ("delta", "array"):
1281 conv = ArrayConverter()
1282 elif arg == "number":
1283 conv = NumberConverter()
1284 elif arg == "SID":
1285 conv = SimpleConverter()
1286 else:
1287 assert 0
1288 table[i] = op, name, arg, default, conv
1289
1290addConverters(privateDictOperators)
1291addConverters(topDictOperators)
1292
jvr4756b3a2002-05-16 18:17:32 +00001293
1294class TopDictDecompiler(psCharStrings.DictDecompiler):
1295 operators = buildOperatorDict(topDictOperators)
1296
1297
1298class PrivateDictDecompiler(psCharStrings.DictDecompiler):
1299 operators = buildOperatorDict(privateDictOperators)
1300
1301
jvrf2cf9c52002-05-23 21:50:36 +00001302class DictCompiler:
1303
1304 def __init__(self, dictObj, strings, parent):
1305 assert isinstance(strings, IndexedStrings)
1306 self.dictObj = dictObj
1307 self.strings = strings
1308 self.parent = parent
1309 rawDict = {}
1310 for name in dictObj.order:
1311 value = getattr(dictObj, name, None)
1312 if value is None:
1313 continue
1314 conv = dictObj.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001315 value = conv.write(dictObj, value)
jvrf2cf9c52002-05-23 21:50:36 +00001316 if value == dictObj.defaults.get(name):
1317 continue
1318 rawDict[name] = value
1319 self.rawDict = rawDict
1320
jvr4e5af602002-05-24 09:58:04 +00001321 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +00001322 pass
1323
1324 def getDataLength(self):
jvr4e5af602002-05-24 09:58:04 +00001325 return len(self.compile("getDataLength"))
jvrf2cf9c52002-05-23 21:50:36 +00001326
jvr4e5af602002-05-24 09:58:04 +00001327 def compile(self, reason):
1328 if DEBUG:
1329 print "-- compiling %s for %s" % (self.__class__.__name__, reason)
jvred101512003-08-22 19:53:32 +00001330 print "in baseDict: ", self
jvrf2cf9c52002-05-23 21:50:36 +00001331 rawDict = self.rawDict
1332 data = []
1333 for name in self.dictObj.order:
1334 value = rawDict.get(name)
1335 if value is None:
1336 continue
1337 op, argType = self.opcodes[name]
jvr2a9bcde2008-03-07 19:56:17 +00001338 if isinstance(argType, tuple):
jvrf2cf9c52002-05-23 21:50:36 +00001339 l = len(argType)
1340 assert len(value) == l, "value doesn't match arg type"
1341 for i in range(l):
jvred101512003-08-22 19:53:32 +00001342 arg = argType[i]
jvrf2cf9c52002-05-23 21:50:36 +00001343 v = value[i]
1344 arghandler = getattr(self, "arg_" + arg)
1345 data.append(arghandler(v))
1346 else:
1347 arghandler = getattr(self, "arg_" + argType)
1348 data.append(arghandler(value))
1349 data.append(op)
1350 return "".join(data)
1351
1352 def toFile(self, file):
jvr4e5af602002-05-24 09:58:04 +00001353 file.write(self.compile("toFile"))
jvrf2cf9c52002-05-23 21:50:36 +00001354
1355 def arg_number(self, num):
1356 return encodeNumber(num)
1357 def arg_SID(self, s):
1358 return psCharStrings.encodeIntCFF(self.strings.getSID(s))
1359 def arg_array(self, value):
1360 data = []
1361 for num in value:
1362 data.append(encodeNumber(num))
1363 return "".join(data)
1364 def arg_delta(self, value):
1365 out = []
1366 last = 0
1367 for v in value:
1368 out.append(v - last)
1369 last = v
1370 data = []
1371 for num in out:
1372 data.append(encodeNumber(num))
1373 return "".join(data)
1374
1375
1376def encodeNumber(num):
jvrf6ff48b2008-03-07 19:49:25 +00001377 if isinstance(num, float):
jvrf2cf9c52002-05-23 21:50:36 +00001378 return psCharStrings.encodeFloat(num)
1379 else:
1380 return psCharStrings.encodeIntCFF(num)
1381
1382
1383class TopDictCompiler(DictCompiler):
1384
1385 opcodes = buildOpcodeDict(topDictOperators)
1386
1387 def getChildren(self, strings):
1388 children = []
jvred101512003-08-22 19:53:32 +00001389 if hasattr(self.dictObj, "charset") and self.dictObj.charset:
jvrf2cf9c52002-05-23 21:50:36 +00001390 children.append(CharsetCompiler(strings, self.dictObj.charset, self))
jvrb9702ba2003-01-03 20:56:01 +00001391 if hasattr(self.dictObj, "Encoding"):
1392 encoding = self.dictObj.Encoding
jvr2a9bcde2008-03-07 19:56:17 +00001393 if not isinstance(encoding, basestring):
jvrb9702ba2003-01-03 20:56:01 +00001394 children.append(EncodingCompiler(strings, encoding, self))
jvrce522412003-08-25 07:37:25 +00001395 if hasattr(self.dictObj, "FDSelect"):
jvred101512003-08-22 19:53:32 +00001396 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1397 # issues about merging the FDArrays. Here I assume that
1398 # either the font was read from XML, and teh FDSelect indices are all
1399 # in the charstring data, or the FDSelect array is already fully defined.
1400 fdSelect = self.dictObj.FDSelect
1401 if len(fdSelect) == 0: # probably read in from XML; assume fdIndex in CharString data
1402 charStrings = self.dictObj.CharStrings
1403 for name in self.dictObj.charset:
1404 charstring = charStrings[name]
1405 fdSelect.append(charStrings[name].fdSelectIndex)
1406 fdSelectComp = FDSelectCompiler(fdSelect, self)
1407 children.append(fdSelectComp)
jvrf2cf9c52002-05-23 21:50:36 +00001408 if hasattr(self.dictObj, "CharStrings"):
1409 items = []
1410 charStrings = self.dictObj.CharStrings
1411 for name in self.dictObj.charset:
1412 items.append(charStrings[name])
1413 charStringsComp = CharStringsCompiler(items, strings, self)
1414 children.append(charStringsComp)
jvrce522412003-08-25 07:37:25 +00001415 if hasattr(self.dictObj, "FDArray"):
jvred101512003-08-22 19:53:32 +00001416 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1417 # issues about merging the FDArrays. Here I assume that the FDArray info is correct
1418 # and complete.
1419 fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self)
1420 children.append(fdArrayIndexComp)
1421 children.extend(fdArrayIndexComp.getChildren(strings))
1422 if hasattr(self.dictObj, "Private"):
1423 privComp = self.dictObj.Private.getCompiler(strings, self)
1424 children.append(privComp)
1425 children.extend(privComp.getChildren(strings))
1426 return children
1427
1428
1429class FontDictCompiler(DictCompiler):
1430
1431 opcodes = buildOpcodeDict(topDictOperators)
1432
1433 def getChildren(self, strings):
1434 children = []
jvrf2cf9c52002-05-23 21:50:36 +00001435 if hasattr(self.dictObj, "Private"):
1436 privComp = self.dictObj.Private.getCompiler(strings, self)
1437 children.append(privComp)
1438 children.extend(privComp.getChildren(strings))
1439 return children
1440
1441
1442class PrivateDictCompiler(DictCompiler):
1443
1444 opcodes = buildOpcodeDict(privateDictOperators)
1445
jvr4e5af602002-05-24 09:58:04 +00001446 def setPos(self, pos, endPos):
1447 size = endPos - pos
jvrf2cf9c52002-05-23 21:50:36 +00001448 self.parent.rawDict["Private"] = size, pos
1449 self.pos = pos
1450
1451 def getChildren(self, strings):
1452 children = []
1453 if hasattr(self.dictObj, "Subrs"):
1454 children.append(self.dictObj.Subrs.getCompiler(strings, self))
1455 return children
1456
jvr4756b3a2002-05-16 18:17:32 +00001457
1458class BaseDict:
1459
jvr4e5af602002-05-24 09:58:04 +00001460 def __init__(self, strings=None, file=None, offset=None):
jvr4756b3a2002-05-16 18:17:32 +00001461 self.rawDict = {}
jvr767102e2002-05-17 07:06:32 +00001462 if DEBUG:
jvrf2cf9c52002-05-23 21:50:36 +00001463 print "loading %s at %s" % (self.__class__.__name__, offset)
jvr4756b3a2002-05-16 18:17:32 +00001464 self.file = file
1465 self.offset = offset
1466 self.strings = strings
jvr155aa752002-05-17 19:58:49 +00001467 self.skipNames = []
jvr4756b3a2002-05-16 18:17:32 +00001468
1469 def decompile(self, data):
jvrf2cf9c52002-05-23 21:50:36 +00001470 if DEBUG:
1471 print " length %s is %s" % (self.__class__.__name__, len(data))
1472 dec = self.decompilerClass(self.strings)
jvr4756b3a2002-05-16 18:17:32 +00001473 dec.decompile(data)
1474 self.rawDict = dec.getDict()
1475 self.postDecompile()
1476
1477 def postDecompile(self):
1478 pass
1479
jvrf2cf9c52002-05-23 21:50:36 +00001480 def getCompiler(self, strings, parent):
1481 return self.compilerClass(self, strings, parent)
1482
jvr4756b3a2002-05-16 18:17:32 +00001483 def __getattr__(self, name):
1484 value = self.rawDict.get(name)
1485 if value is None:
1486 value = self.defaults.get(name)
1487 if value is None:
1488 raise AttributeError, name
1489 conv = self.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001490 value = conv.read(self, value)
jvr4756b3a2002-05-16 18:17:32 +00001491 setattr(self, name, value)
1492 return value
1493
1494 def toXML(self, xmlWriter, progress):
1495 for name in self.order:
jvr155aa752002-05-17 19:58:49 +00001496 if name in self.skipNames:
1497 continue
jvr4756b3a2002-05-16 18:17:32 +00001498 value = getattr(self, name, None)
1499 if value is None:
1500 continue
jvr4e5af602002-05-24 09:58:04 +00001501 conv = self.converters[name]
jvr7ce0a132002-07-23 16:42:11 +00001502 conv.xmlWrite(xmlWriter, name, value, progress)
jvr4e5af602002-05-24 09:58:04 +00001503
1504 def fromXML(self, (name, attrs, content)):
1505 conv = self.converters[name]
1506 value = conv.xmlRead((name, attrs, content), self)
1507 setattr(self, name, value)
jvr4756b3a2002-05-16 18:17:32 +00001508
1509
1510class TopDict(BaseDict):
1511
1512 defaults = buildDefaults(topDictOperators)
1513 converters = buildConverters(topDictOperators)
1514 order = buildOrder(topDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001515 decompilerClass = TopDictDecompiler
1516 compilerClass = TopDictCompiler
Just7842e561999-12-16 21:34:53 +00001517
jvr4e5af602002-05-24 09:58:04 +00001518 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
jvr016ca762002-05-16 18:38:03 +00001519 BaseDict.__init__(self, strings, file, offset)
1520 self.GlobalSubrs = GlobalSubrs
1521
Just7842e561999-12-16 21:34:53 +00001522 def getGlyphOrder(self):
1523 return self.charset
1524
jvr4756b3a2002-05-16 18:17:32 +00001525 def postDecompile(self):
1526 offset = self.rawDict.get("CharStrings")
1527 if offset is None:
1528 return
1529 # get the number of glyphs beforehand.
1530 self.file.seek(offset)
jvra2ad5442002-05-17 18:36:07 +00001531 self.numGlyphs = readCard16(self.file)
Just7842e561999-12-16 21:34:53 +00001532
jvr016ca762002-05-16 18:38:03 +00001533 def toXML(self, xmlWriter, progress):
jvr155aa752002-05-17 19:58:49 +00001534 if hasattr(self, "CharStrings"):
jvr7ce0a132002-07-23 16:42:11 +00001535 self.decompileAllCharStrings(progress)
jvred101512003-08-22 19:53:32 +00001536 if hasattr(self, "ROS"):
1537 self.skipNames = ['Encoding']
jvr155aa752002-05-17 19:58:49 +00001538 if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
1539 # these values have default values, but I only want them to show up
1540 # in CID fonts.
1541 self.skipNames = ['CIDFontVersion', 'CIDFontRevision', 'CIDFontType',
1542 'CIDCount']
jvr016ca762002-05-16 18:38:03 +00001543 BaseDict.toXML(self, xmlWriter, progress)
jvr767102e2002-05-17 07:06:32 +00001544
jvr7ce0a132002-07-23 16:42:11 +00001545 def decompileAllCharStrings(self, progress):
jvr4e5af602002-05-24 09:58:04 +00001546 # XXX only when doing ttdump -i?
jvr7ce0a132002-07-23 16:42:11 +00001547 i = 0
jvra2ad5442002-05-17 18:36:07 +00001548 for charString in self.CharStrings.values():
jvred101512003-08-22 19:53:32 +00001549 try:
1550 charString.decompile()
1551 except:
1552 print "Error in charstring ", i
1553 import sys
1554 type, value = sys. exc_info()[0:2]
1555 raise type(value)
jvr7ce0a132002-07-23 16:42:11 +00001556 if not i % 30 and progress:
1557 progress.increment(0) # update
1558 i = i + 1
Just7842e561999-12-16 21:34:53 +00001559
1560
jvred101512003-08-22 19:53:32 +00001561class FontDict(BaseDict):
1562
1563 defaults = buildDefaults(topDictOperators)
1564 converters = buildConverters(topDictOperators)
1565 order = buildOrder(topDictOperators)
1566 decompilerClass = None
1567 compilerClass = FontDictCompiler
1568
1569 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
1570 BaseDict.__init__(self, strings, file, offset)
1571 self.GlobalSubrs = GlobalSubrs
1572
1573 def getGlyphOrder(self):
1574 return self.charset
1575
1576 def toXML(self, xmlWriter, progress):
1577 self.skipNames = ['Encoding']
1578 BaseDict.toXML(self, xmlWriter, progress)
1579
1580
1581
jvr4756b3a2002-05-16 18:17:32 +00001582class PrivateDict(BaseDict):
1583 defaults = buildDefaults(privateDictOperators)
1584 converters = buildConverters(privateDictOperators)
1585 order = buildOrder(privateDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001586 decompilerClass = PrivateDictDecompiler
1587 compilerClass = PrivateDictCompiler
Just7842e561999-12-16 21:34:53 +00001588
1589
jvre3275582002-05-14 12:22:03 +00001590class IndexedStrings:
1591
jvr767102e2002-05-17 07:06:32 +00001592 """SID -> string mapping."""
1593
1594 def __init__(self, file=None):
1595 if file is None:
jvre3275582002-05-14 12:22:03 +00001596 strings = []
jvr767102e2002-05-17 07:06:32 +00001597 else:
jvr4e5af602002-05-24 09:58:04 +00001598 strings = list(Index(file))
jvre3275582002-05-14 12:22:03 +00001599 self.strings = strings
1600
jvrf2cf9c52002-05-23 21:50:36 +00001601 def getCompiler(self):
1602 return IndexedStringsCompiler(self, None, None)
1603
1604 def __len__(self):
1605 return len(self.strings)
1606
jvre3275582002-05-14 12:22:03 +00001607 def __getitem__(self, SID):
1608 if SID < cffStandardStringCount:
1609 return cffStandardStrings[SID]
1610 else:
1611 return self.strings[SID - cffStandardStringCount]
1612
1613 def getSID(self, s):
1614 if not hasattr(self, "stringMapping"):
1615 self.buildStringMapping()
1616 if cffStandardStringMapping.has_key(s):
1617 SID = cffStandardStringMapping[s]
jvrf2cf9c52002-05-23 21:50:36 +00001618 elif self.stringMapping.has_key(s):
jvre3275582002-05-14 12:22:03 +00001619 SID = self.stringMapping[s]
1620 else:
1621 SID = len(self.strings) + cffStandardStringCount
1622 self.strings.append(s)
1623 self.stringMapping[s] = SID
1624 return SID
1625
1626 def getStrings(self):
1627 return self.strings
1628
1629 def buildStringMapping(self):
1630 self.stringMapping = {}
1631 for index in range(len(self.strings)):
1632 self.stringMapping[self.strings[index]] = index + cffStandardStringCount
1633
1634
Just7842e561999-12-16 21:34:53 +00001635# The 391 Standard Strings as used in the CFF format.
1636# from Adobe Technical None #5176, version 1.0, 18 March 1998
1637
1638cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
1639 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
1640 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
1641 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
1642 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C',
1643 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
1644 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
1645 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
1646 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
1647 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
1648 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
1649 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
1650 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
1651 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
1652 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
1653 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
1654 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
1655 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
1656 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
1657 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
1658 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
1659 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
1660 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
1661 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
1662 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
1663 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
1664 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
1665 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
1666 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
1667 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
1668 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
1669 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
1670 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
1671 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
1672 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
1673 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
1674 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
1675 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
1676 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
1677 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
1678 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
1679 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
1680 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
1681 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
1682 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
1683 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
1684 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
1685 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
1686 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
1687 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
1688 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
1689 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
1690 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
1691 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
1692 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
1693 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
1694 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
1695 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
1696 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
1697 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
1698 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
1699 '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
1700 'Semibold'
1701]
1702
1703cffStandardStringCount = 391
1704assert len(cffStandardStrings) == cffStandardStringCount
1705# build reverse mapping
1706cffStandardStringMapping = {}
1707for _i in range(cffStandardStringCount):
1708 cffStandardStringMapping[cffStandardStrings[_i]] = _i
jvrc60a44f2006-10-21 13:41:18 +00001709
1710cffISOAdobeStrings = [".notdef", "space", "exclam", "quotedbl", "numbersign",
1711"dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright",
1712"asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two",
1713"three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon",
1714"less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G",
1715"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
1716"X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum",
1717"underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
1718"k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
1719"braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent",
1720"sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle",
1721"quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl",
1722"endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet",
1723"quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis",
1724"perthousand", "questiondown", "grave", "acute", "circumflex", "tilde",
1725"macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
1726"ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE",
1727"ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls",
1728"onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus",
1729"Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn",
1730"threequarters", "twosuperior", "registered", "minus", "eth", "multiply",
1731"threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
1732"Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave",
1733"Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute",
1734"Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute",
1735"Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute",
1736"acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute",
1737"ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis",
1738"igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde",
1739"scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis",
1740"zcaron"]
1741
1742cffISOAdobeStringCount = 229
1743assert len(cffISOAdobeStrings) == cffISOAdobeStringCount
1744
1745cffIExpertStrings = [".notdef", "space", "exclamsmall", "Hungarumlautsmall",
1746"dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall",
1747"parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader",
1748"comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle",
1749"twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle",
1750"sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon",
1751"commasuperior", "threequartersemdash", "periodsuperior", "questionsmall",
1752"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1753"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1754"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1755"parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
1756"Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall",
1757"Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
1758"Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall",
1759"Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall",
1760"exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall",
1761"Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall",
1762"figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall",
1763"onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth",
1764"threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds",
1765"zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior",
1766"fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior",
1767"zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior",
1768"fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior",
1769"centinferior", "dollarinferior", "periodinferior", "commainferior",
1770"Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall",
1771"Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall",
1772"Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall",
1773"Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall",
1774"Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall",
1775"Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall",
1776"Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall",
1777"Ydieresissmall"]
1778
1779cffExpertStringCount = 166
1780assert len(cffIExpertStrings) == cffExpertStringCount
1781
1782cffExpertSubsetStrings = [".notdef", "space", "dollaroldstyle",
1783"dollarsuperior", "parenleftsuperior", "parenrightsuperior", "twodotenleader",
1784"onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle",
1785"oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle",
1786"sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon",
1787"semicolon", "commasuperior", "threequartersemdash", "periodsuperior",
1788"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1789"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1790"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1791"parenrightinferior", "hyphensuperior", "colonmonetary", "onefitted", "rupiah",
1792"centoldstyle", "figuredash", "hypheninferior", "onequarter", "onehalf",
1793"threequarters", "oneeighth", "threeeighths", "fiveeighths", "seveneighths",
1794"onethird", "twothirds", "zerosuperior", "onesuperior", "twosuperior",
1795"threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior",
1796"eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior",
1797"threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior",
1798"eightinferior", "nineinferior", "centinferior", "dollarinferior",
1799"periodinferior", "commainferior"]
1800
1801cffExpertSubsetStringCount = 87
1802assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount