blob: 3db906568f59f4490ea7666bf278383fa7ae723e [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:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -050054 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
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050097 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
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500116 name, attrs, content = element
117 topDict.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000118 elif name == "GlobalSubrs":
119 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000120 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000121 continue
122 name, attrs, content = element
jvr489d76a2003-08-24 19:56:16 +0000123 subr = psCharStrings.T2CharString()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500124 subr.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000125 self.GlobalSubrs.append(subr)
Just7842e561999-12-16 21:34:53 +0000126
127
jvrf2cf9c52002-05-23 21:50:36 +0000128class CFFWriter:
129
130 def __init__(self):
131 self.data = []
132
133 def add(self, table):
134 self.data.append(table)
135
136 def toFile(self, file):
137 lastPosList = None
138 count = 1
139 while 1:
jvr4e5af602002-05-24 09:58:04 +0000140 if DEBUG:
141 print "CFFWriter.toFile() iteration:", count
142 count = count + 1
jvrf2cf9c52002-05-23 21:50:36 +0000143 pos = 0
144 posList = [pos]
145 for item in self.data:
jvrf2cf9c52002-05-23 21:50:36 +0000146 if hasattr(item, "getDataLength"):
jvr4e5af602002-05-24 09:58:04 +0000147 endPos = pos + item.getDataLength()
jvrf2cf9c52002-05-23 21:50:36 +0000148 else:
jvr4e5af602002-05-24 09:58:04 +0000149 endPos = pos + len(item)
150 if hasattr(item, "setPos"):
151 item.setPos(pos, endPos)
152 pos = endPos
jvrf2cf9c52002-05-23 21:50:36 +0000153 posList.append(pos)
154 if posList == lastPosList:
155 break
156 lastPosList = posList
jvr4e5af602002-05-24 09:58:04 +0000157 if DEBUG:
158 print "CFFWriter.toFile() writing to file."
jvrf2cf9c52002-05-23 21:50:36 +0000159 begin = file.tell()
160 posList = [0]
161 for item in self.data:
162 if hasattr(item, "toFile"):
163 item.toFile(file)
164 else:
165 file.write(item)
166 posList.append(file.tell() - begin)
jvrf2cf9c52002-05-23 21:50:36 +0000167 assert posList == lastPosList
168
169
170def calcOffSize(largestOffset):
171 if largestOffset < 0x100:
172 offSize = 1
173 elif largestOffset < 0x10000:
174 offSize = 2
175 elif largestOffset < 0x1000000:
176 offSize = 3
177 else:
178 offSize = 4
179 return offSize
180
181
182class IndexCompiler:
183
184 def __init__(self, items, strings, parent):
185 self.items = self.getItems(items, strings)
186 self.parent = parent
187
188 def getItems(self, items, strings):
189 return items
190
191 def getOffsets(self):
192 pos = 1
193 offsets = [pos]
194 for item in self.items:
195 if hasattr(item, "getDataLength"):
196 pos = pos + item.getDataLength()
197 else:
198 pos = pos + len(item)
199 offsets.append(pos)
200 return offsets
201
202 def getDataLength(self):
203 lastOffset = self.getOffsets()[-1]
204 offSize = calcOffSize(lastOffset)
205 dataLength = (
206 2 + # count
207 1 + # offSize
208 (len(self.items) + 1) * offSize + # the offsets
209 lastOffset - 1 # size of object data
210 )
211 return dataLength
212
213 def toFile(self, file):
jvrf2cf9c52002-05-23 21:50:36 +0000214 offsets = self.getOffsets()
215 writeCard16(file, len(self.items))
216 offSize = calcOffSize(offsets[-1])
217 writeCard8(file, offSize)
218 offSize = -offSize
219 pack = struct.pack
220 for offset in offsets:
221 binOffset = pack(">l", offset)[offSize:]
222 assert len(binOffset) == -offSize
223 file.write(binOffset)
224 for item in self.items:
225 if hasattr(item, "toFile"):
226 item.toFile(file)
227 else:
228 file.write(item)
jvrf2cf9c52002-05-23 21:50:36 +0000229
230
231class IndexedStringsCompiler(IndexCompiler):
232
233 def getItems(self, items, strings):
234 return items.strings
235
236
237class TopDictIndexCompiler(IndexCompiler):
238
239 def getItems(self, items, strings):
240 out = []
241 for item in items:
242 out.append(item.getCompiler(strings, self))
243 return out
244
245 def getChildren(self, strings):
246 children = []
247 for topDict in self.items:
248 children.extend(topDict.getChildren(strings))
249 return children
250
251
jvred101512003-08-22 19:53:32 +0000252class FDArrayIndexCompiler(IndexCompiler):
253
254 def getItems(self, items, strings):
255 out = []
256 for item in items:
257 out.append(item.getCompiler(strings, self))
258 return out
259
260 def getChildren(self, strings):
261 children = []
262 for fontDict in self.items:
263 children.extend(fontDict.getChildren(strings))
264 return children
265
jvred101512003-08-22 19:53:32 +0000266 def toFile(self, file):
267 offsets = self.getOffsets()
268 writeCard16(file, len(self.items))
269 offSize = calcOffSize(offsets[-1])
270 writeCard8(file, offSize)
271 offSize = -offSize
272 pack = struct.pack
273 for offset in offsets:
274 binOffset = pack(">l", offset)[offSize:]
275 assert len(binOffset) == -offSize
276 file.write(binOffset)
277 for item in self.items:
278 if hasattr(item, "toFile"):
279 item.toFile(file)
280 else:
281 file.write(item)
282
283 def setPos(self, pos, endPos):
284 self.parent.rawDict["FDArray"] = pos
285
286
jvrf2cf9c52002-05-23 21:50:36 +0000287class GlobalSubrsCompiler(IndexCompiler):
288 def getItems(self, items, strings):
289 out = []
290 for cs in items:
291 cs.compile()
292 out.append(cs.bytecode)
293 return out
294
295class SubrsCompiler(GlobalSubrsCompiler):
jvr4e5af602002-05-24 09:58:04 +0000296 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000297 offset = pos - self.parent.pos
298 self.parent.rawDict["Subrs"] = offset
299
300class CharStringsCompiler(GlobalSubrsCompiler):
jvr4e5af602002-05-24 09:58:04 +0000301 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000302 self.parent.rawDict["CharStrings"] = pos
303
304
jvr4756b3a2002-05-16 18:17:32 +0000305class Index:
Just7842e561999-12-16 21:34:53 +0000306
jvr4756b3a2002-05-16 18:17:32 +0000307 """This class represents what the CFF spec calls an INDEX."""
Just7842e561999-12-16 21:34:53 +0000308
jvrf2cf9c52002-05-23 21:50:36 +0000309 compilerClass = IndexCompiler
310
jvr4e5af602002-05-24 09:58:04 +0000311 def __init__(self, file=None):
312 name = self.__class__.__name__
jvrf2cf9c52002-05-23 21:50:36 +0000313 if file is None:
314 self.items = []
315 return
jvr767102e2002-05-17 07:06:32 +0000316 if DEBUG:
317 print "loading %s at %s" % (name, file.tell())
jvr4756b3a2002-05-16 18:17:32 +0000318 self.file = file
jvra2ad5442002-05-17 18:36:07 +0000319 count = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000320 self.count = count
321 self.items = [None] * count
322 if count == 0:
jvrf2cf9c52002-05-23 21:50:36 +0000323 self.items = []
jvr4756b3a2002-05-16 18:17:32 +0000324 return
jvra2ad5442002-05-17 18:36:07 +0000325 offSize = readCard8(file)
jvr767102e2002-05-17 07:06:32 +0000326 if DEBUG:
jvrf2cf9c52002-05-23 21:50:36 +0000327 print " index count: %s offSize: %s" % (count, offSize)
jvr767102e2002-05-17 07:06:32 +0000328 assert offSize <= 4, "offSize too large: %s" % offSize
jvr4756b3a2002-05-16 18:17:32 +0000329 self.offsets = offsets = []
330 pad = '\0' * (4 - offSize)
331 for index in range(count+1):
332 chunk = file.read(offSize)
333 chunk = pad + chunk
334 offset, = struct.unpack(">L", chunk)
335 offsets.append(int(offset))
336 self.offsetBase = file.tell() - 1
337 file.seek(self.offsetBase + offsets[-1]) # pretend we've read the whole lot
jvrf2cf9c52002-05-23 21:50:36 +0000338 if DEBUG:
339 print " end of %s at %s" % (name, file.tell())
Just7842e561999-12-16 21:34:53 +0000340
jvr4756b3a2002-05-16 18:17:32 +0000341 def __len__(self):
jvrf2cf9c52002-05-23 21:50:36 +0000342 return len(self.items)
jvra2a75b32002-05-13 11:25:17 +0000343
jvr4756b3a2002-05-16 18:17:32 +0000344 def __getitem__(self, index):
345 item = self.items[index]
346 if item is not None:
347 return item
348 offset = self.offsets[index] + self.offsetBase
349 size = self.offsets[index+1] - self.offsets[index]
350 file = self.file
351 file.seek(offset)
352 data = file.read(size)
353 assert len(data) == size
jvra2ad5442002-05-17 18:36:07 +0000354 item = self.produceItem(index, data, file, offset, size)
jvr4756b3a2002-05-16 18:17:32 +0000355 self.items[index] = item
356 return item
357
jvra2ad5442002-05-17 18:36:07 +0000358 def produceItem(self, index, data, file, offset, size):
jvr4756b3a2002-05-16 18:17:32 +0000359 return data
jvr4756b3a2002-05-16 18:17:32 +0000360
jvrf2cf9c52002-05-23 21:50:36 +0000361 def append(self, item):
362 self.items.append(item)
363
364 def getCompiler(self, strings, parent):
365 return self.compilerClass(self, strings, parent)
366
367
368class GlobalSubrsIndex(Index):
369
370 compilerClass = GlobalSubrsCompiler
371
jvr4e5af602002-05-24 09:58:04 +0000372 def __init__(self, file=None, globalSubrs=None, private=None, fdSelect=None, fdArray=None):
373 Index.__init__(self, file)
jvra2ad5442002-05-17 18:36:07 +0000374 self.globalSubrs = globalSubrs
375 self.private = private
jvred101512003-08-22 19:53:32 +0000376 if fdSelect:
377 self.fdSelect = fdSelect
378 if fdArray:
379 self.fdArray = fdArray
jvra2ad5442002-05-17 18:36:07 +0000380
381 def produceItem(self, index, data, file, offset, size):
382 if self.private is not None:
383 private = self.private
jvred101512003-08-22 19:53:32 +0000384 elif hasattr(self, 'fdArray') and self.fdArray is not None:
jvra2ad5442002-05-17 18:36:07 +0000385 private = self.fdArray[self.fdSelect[index]].Private
386 else:
387 private = None
jvr489d76a2003-08-24 19:56:16 +0000388 return psCharStrings.T2CharString(data, private=private, globalSubrs=self.globalSubrs)
jvr4756b3a2002-05-16 18:17:32 +0000389
390 def toXML(self, xmlWriter, progress):
jvred101512003-08-22 19:53:32 +0000391 xmlWriter.comment("The 'index' attribute is only for humans; it is ignored when parsed.")
jvr4e5af602002-05-24 09:58:04 +0000392 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +0000393 for i in range(len(self)):
jvrb58176e2002-05-24 11:55:37 +0000394 subr = self[i]
395 if subr.needsDecompilation():
396 xmlWriter.begintag("CharString", index=i, raw=1)
397 else:
398 xmlWriter.begintag("CharString", index=i)
jvr4756b3a2002-05-16 18:17:32 +0000399 xmlWriter.newline()
jvrb58176e2002-05-24 11:55:37 +0000400 subr.toXML(xmlWriter)
jvr4756b3a2002-05-16 18:17:32 +0000401 xmlWriter.endtag("CharString")
402 xmlWriter.newline()
jvra2ad5442002-05-17 18:36:07 +0000403
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500404 def fromXML(self, name, attrs, content):
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500405 if name != "CharString":
jvr4e5af602002-05-24 09:58:04 +0000406 return
jvr489d76a2003-08-24 19:56:16 +0000407 subr = psCharStrings.T2CharString()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500408 subr.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000409 self.append(subr)
410
jvra2ad5442002-05-17 18:36:07 +0000411 def getItemAndSelector(self, index):
jvred101512003-08-22 19:53:32 +0000412 sel = None
413 if hasattr(self, 'fdSelect'):
414 sel = self.fdSelect[index]
jvra2ad5442002-05-17 18:36:07 +0000415 return self[index], sel
jvrf2cf9c52002-05-23 21:50:36 +0000416
jvre2ca9b52002-09-09 14:18:39 +0000417
jvrf2cf9c52002-05-23 21:50:36 +0000418class SubrsIndex(GlobalSubrsIndex):
419 compilerClass = SubrsCompiler
420
jvr4756b3a2002-05-16 18:17:32 +0000421
jvr767102e2002-05-17 07:06:32 +0000422class TopDictIndex(Index):
jvra2ad5442002-05-17 18:36:07 +0000423
jvrf2cf9c52002-05-23 21:50:36 +0000424 compilerClass = TopDictIndexCompiler
425
jvra2ad5442002-05-17 18:36:07 +0000426 def produceItem(self, index, data, file, offset, size):
jvr767102e2002-05-17 07:06:32 +0000427 top = TopDict(self.strings, file, offset, self.GlobalSubrs)
428 top.decompile(data)
429 return top
jvra2ad5442002-05-17 18:36:07 +0000430
431 def toXML(self, xmlWriter, progress):
432 for i in range(len(self)):
433 xmlWriter.begintag("FontDict", index=i)
434 xmlWriter.newline()
435 self[i].toXML(xmlWriter, progress)
436 xmlWriter.endtag("FontDict")
437 xmlWriter.newline()
jvr767102e2002-05-17 07:06:32 +0000438
439
jvred101512003-08-22 19:53:32 +0000440class FDArrayIndex(TopDictIndex):
441
442 compilerClass = FDArrayIndexCompiler
443
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500444 def fromXML(self, name, attrs, content):
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500445 if name != "FontDict":
jvred101512003-08-22 19:53:32 +0000446 return
447 fontDict = FontDict()
448 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000449 if isinstance(element, basestring):
jvred101512003-08-22 19:53:32 +0000450 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500451 name, attrs, content = element
452 fontDict.fromXML(name, attrs, content)
jvred101512003-08-22 19:53:32 +0000453 self.append(fontDict)
454
455
456class FDSelect:
457 def __init__(self, file = None, numGlyphs = None, format=None):
458 if file:
459 # read data in from file
460 self.format = readCard8(file)
461 if self.format == 0:
462 from array import array
463 self.gidArray = array("B", file.read(numGlyphs)).tolist()
464 elif self.format == 3:
465 gidArray = [None] * numGlyphs
466 nRanges = readCard16(file)
467 prev = None
468 for i in range(nRanges):
469 first = readCard16(file)
470 if prev is not None:
471 for glyphID in range(prev, first):
472 gidArray[glyphID] = fd
473 prev = first
474 fd = readCard8(file)
475 if prev is not None:
476 first = readCard16(file)
477 for glyphID in range(prev, first):
478 gidArray[glyphID] = fd
479 self.gidArray = gidArray
480 else:
481 assert 0, "unsupported FDSelect format: %s" % format
482 else:
483 # reading from XML. Make empty gidArray,, and leave format as passed in.
484 # format == None will result in the smallest representation being used.
485 self.format = format
486 self.gidArray = []
487
488
489 def __len__(self):
490 return len(self.gidArray)
491
492 def __getitem__(self, index):
493 return self.gidArray[index]
494
495 def __setitem__(self, index, fdSelectValue):
496 self.gidArray[index] = fdSelectValue
497
498 def append(self, fdSelectValue):
499 self.gidArray.append(fdSelectValue)
500
501
jvr4756b3a2002-05-16 18:17:32 +0000502class CharStrings:
503
jvra2ad5442002-05-17 18:36:07 +0000504 def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray):
jvr4e5af602002-05-24 09:58:04 +0000505 if file is not None:
506 self.charStringsIndex = SubrsIndex(file, globalSubrs, private, fdSelect, fdArray)
507 self.charStrings = charStrings = {}
508 for i in range(len(charset)):
509 charStrings[charset[i]] = i
510 self.charStringsAreIndexed = 1
511 else:
512 self.charStrings = {}
513 self.charStringsAreIndexed = 0
514 self.globalSubrs = globalSubrs
515 self.private = private
jvred101512003-08-22 19:53:32 +0000516 if fdSelect != None:
517 self.fdSelect = fdSelect
518 if fdArray!= None:
519 self.fdArray = fdArray
jvr4756b3a2002-05-16 18:17:32 +0000520
521 def keys(self):
jvr4e5af602002-05-24 09:58:04 +0000522 return self.charStrings.keys()
jvr4756b3a2002-05-16 18:17:32 +0000523
jvr016ca762002-05-16 18:38:03 +0000524 def values(self):
jvr4e5af602002-05-24 09:58:04 +0000525 if self.charStringsAreIndexed:
526 return self.charStringsIndex
527 else:
528 return self.charStrings.values()
jvr016ca762002-05-16 18:38:03 +0000529
jvr4756b3a2002-05-16 18:17:32 +0000530 def has_key(self, name):
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500531 return name in self.charStrings
jvr4756b3a2002-05-16 18:17:32 +0000532
jvr767102e2002-05-17 07:06:32 +0000533 def __len__(self):
jvr4e5af602002-05-24 09:58:04 +0000534 return len(self.charStrings)
jvr767102e2002-05-17 07:06:32 +0000535
jvr4756b3a2002-05-16 18:17:32 +0000536 def __getitem__(self, name):
jvr4e5af602002-05-24 09:58:04 +0000537 charString = self.charStrings[name]
538 if self.charStringsAreIndexed:
539 charString = self.charStringsIndex[charString]
540 return charString
541
542 def __setitem__(self, name, charString):
543 if self.charStringsAreIndexed:
544 index = self.charStrings[name]
545 self.charStringsIndex[index] = charString
546 else:
547 self.charStrings[name] = charString
jvr4756b3a2002-05-16 18:17:32 +0000548
jvra2ad5442002-05-17 18:36:07 +0000549 def getItemAndSelector(self, name):
jvr4e5af602002-05-24 09:58:04 +0000550 if self.charStringsAreIndexed:
551 index = self.charStrings[name]
552 return self.charStringsIndex.getItemAndSelector(index)
553 else:
jvred101512003-08-22 19:53:32 +0000554 if hasattr(self, 'fdSelect'):
jvr91bca422012-10-18 12:49:22 +0000555 sel = self.fdSelect[index] # index is not defined at this point. Read R. ?
jvred101512003-08-22 19:53:32 +0000556 else:
557 raise KeyError("fdSelect array not yet defined.")
558 return self.charStrings[name], sel
jvra2ad5442002-05-17 18:36:07 +0000559
jvr4756b3a2002-05-16 18:17:32 +0000560 def toXML(self, xmlWriter, progress):
561 names = self.keys()
562 names.sort()
jvr7ce0a132002-07-23 16:42:11 +0000563 i = 0
564 step = 10
565 numGlyphs = len(names)
jvr4756b3a2002-05-16 18:17:32 +0000566 for name in names:
jvred101512003-08-22 19:53:32 +0000567 charStr, fdSelectIndex = self.getItemAndSelector(name)
jvrb58176e2002-05-24 11:55:37 +0000568 if charStr.needsDecompilation():
569 raw = [("raw", 1)]
570 else:
571 raw = []
jvred101512003-08-22 19:53:32 +0000572 if fdSelectIndex is None:
jvrb58176e2002-05-24 11:55:37 +0000573 xmlWriter.begintag("CharString", [('name', name)] + raw)
jvra2ad5442002-05-17 18:36:07 +0000574 else:
575 xmlWriter.begintag("CharString",
jvred101512003-08-22 19:53:32 +0000576 [('name', name), ('fdSelectIndex', fdSelectIndex)] + raw)
jvr4756b3a2002-05-16 18:17:32 +0000577 xmlWriter.newline()
jvrb58176e2002-05-24 11:55:37 +0000578 charStr.toXML(xmlWriter)
jvr4756b3a2002-05-16 18:17:32 +0000579 xmlWriter.endtag("CharString")
580 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000581 if not i % step and progress is not None:
582 progress.setLabel("Dumping 'CFF ' table... (%s)" % name)
583 progress.increment(step / float(numGlyphs))
584 i = i + 1
jvr4e5af602002-05-24 09:58:04 +0000585
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500586 def fromXML(self, name, attrs, content):
jvr4e5af602002-05-24 09:58:04 +0000587 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000588 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000589 continue
590 name, attrs, content = element
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500591 if name != "CharString":
jvr4e5af602002-05-24 09:58:04 +0000592 continue
jvred101512003-08-22 19:53:32 +0000593 fdID = -1
594 if hasattr(self, "fdArray"):
595 fdID = safeEval(attrs["fdSelectIndex"])
596 private = self.fdArray[fdID].Private
597 else:
598 private = self.private
599
jvr4e5af602002-05-24 09:58:04 +0000600 glyphName = attrs["name"]
jvr489d76a2003-08-24 19:56:16 +0000601 charString = psCharStrings.T2CharString(
602 private=private,
603 globalSubrs=self.globalSubrs)
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500604 charString.fromXML(name, attrs, content)
jvred101512003-08-22 19:53:32 +0000605 if fdID >= 0:
606 charString.fdSelectIndex = fdID
jvr4e5af602002-05-24 09:58:04 +0000607 self[glyphName] = charString
jvr4756b3a2002-05-16 18:17:32 +0000608
609
jvra2ad5442002-05-17 18:36:07 +0000610def readCard8(file):
611 return ord(file.read(1))
612
613def readCard16(file):
614 value, = struct.unpack(">H", file.read(2))
615 return value
616
jvrf2cf9c52002-05-23 21:50:36 +0000617def writeCard8(file, value):
618 file.write(chr(value))
619
620def writeCard16(file, value):
621 file.write(struct.pack(">H", value))
622
623def packCard8(value):
624 return chr(value)
625
626def packCard16(value):
627 return struct.pack(">H", value)
628
jvr4756b3a2002-05-16 18:17:32 +0000629def buildOperatorDict(table):
630 d = {}
631 for op, name, arg, default, conv in table:
632 d[op] = (name, arg)
633 return d
634
jvrf2cf9c52002-05-23 21:50:36 +0000635def buildOpcodeDict(table):
636 d = {}
637 for op, name, arg, default, conv in table:
jvr2a9bcde2008-03-07 19:56:17 +0000638 if isinstance(op, tuple):
jvrf2cf9c52002-05-23 21:50:36 +0000639 op = chr(op[0]) + chr(op[1])
640 else:
641 op = chr(op)
642 d[name] = (op, arg)
643 return d
644
jvr4756b3a2002-05-16 18:17:32 +0000645def buildOrder(table):
646 l = []
647 for op, name, arg, default, conv in table:
648 l.append(name)
649 return l
650
651def buildDefaults(table):
652 d = {}
653 for op, name, arg, default, conv in table:
654 if default is not None:
655 d[name] = default
656 return d
657
658def buildConverters(table):
659 d = {}
660 for op, name, arg, default, conv in table:
661 d[name] = conv
662 return d
663
664
jvr4e5af602002-05-24 09:58:04 +0000665class SimpleConverter:
jvr7ce02ea2002-05-17 20:04:05 +0000666 def read(self, parent, value):
667 return value
jvrf2cf9c52002-05-23 21:50:36 +0000668 def write(self, parent, value):
669 return value
jvr7ce0a132002-07-23 16:42:11 +0000670 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000671 xmlWriter.simpletag(name, value=value)
672 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500673 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000674 return attrs["value"]
675
jvre2ca9b52002-09-09 14:18:39 +0000676class Latin1Converter(SimpleConverter):
Behdad Esfahbod8c5c9662013-10-28 13:20:00 +0100677 def xmlWrite(self, xmlWriter, name, value, progress):
678 # Store as UTF-8
679 value = unicode(value, "latin-1").encode("utf-8")
680 xmlWriter.simpletag(name, value=value)
681 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500682 def xmlRead(self, name, attrs, content, parent):
jvre2ca9b52002-09-09 14:18:39 +0000683 s = unicode(attrs["value"], "utf-8")
684 return s.encode("latin-1")
685
686
jvr4e5af602002-05-24 09:58:04 +0000687def parseNum(s):
688 try:
689 value = int(s)
690 except:
691 value = float(s)
692 return value
693
694class NumberConverter(SimpleConverter):
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500695 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000696 return parseNum(attrs["value"])
697
698class ArrayConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000699 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000700 value = map(str, value)
701 xmlWriter.simpletag(name, value=" ".join(value))
702 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500703 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000704 values = attrs["value"].split()
705 return map(parseNum, values)
706
707class TableConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000708 def xmlWrite(self, xmlWriter, name, value, progress):
jvra2ad5442002-05-17 18:36:07 +0000709 xmlWriter.begintag(name)
710 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000711 value.toXML(xmlWriter, progress)
jvra2ad5442002-05-17 18:36:07 +0000712 xmlWriter.endtag(name)
713 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500714 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000715 ob = self.getClass()()
716 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000717 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000718 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500719 name, attrs, content = element
720 ob.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000721 return ob
jvra2ad5442002-05-17 18:36:07 +0000722
jvr4e5af602002-05-24 09:58:04 +0000723class PrivateDictConverter(TableConverter):
724 def getClass(self):
725 return PrivateDict
jvr4756b3a2002-05-16 18:17:32 +0000726 def read(self, parent, value):
727 size, offset = value
728 file = parent.file
jvr4e5af602002-05-24 09:58:04 +0000729 priv = PrivateDict(parent.strings, file, offset)
jvr4756b3a2002-05-16 18:17:32 +0000730 file.seek(offset)
731 data = file.read(size)
732 len(data) == size
jvr4e5af602002-05-24 09:58:04 +0000733 priv.decompile(data)
734 return priv
jvrf2cf9c52002-05-23 21:50:36 +0000735 def write(self, parent, value):
736 return (0, 0) # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000737
jvr4e5af602002-05-24 09:58:04 +0000738class SubrsConverter(TableConverter):
739 def getClass(self):
740 return SubrsIndex
jvr4756b3a2002-05-16 18:17:32 +0000741 def read(self, parent, value):
742 file = parent.file
743 file.seek(parent.offset + value) # Offset(self)
jvr4e5af602002-05-24 09:58:04 +0000744 return SubrsIndex(file)
jvrf2cf9c52002-05-23 21:50:36 +0000745 def write(self, parent, value):
746 return 0 # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000747
jvr4e5af602002-05-24 09:58:04 +0000748class CharStringsConverter(TableConverter):
jvr4756b3a2002-05-16 18:17:32 +0000749 def read(self, parent, value):
750 file = parent.file
jvr767102e2002-05-17 07:06:32 +0000751 charset = parent.charset
jvra2ad5442002-05-17 18:36:07 +0000752 globalSubrs = parent.GlobalSubrs
753 if hasattr(parent, "ROS"):
754 fdSelect, fdArray = parent.FDSelect, parent.FDArray
755 private = None
756 else:
757 fdSelect, fdArray = None, None
758 private = parent.Private
jvr4756b3a2002-05-16 18:17:32 +0000759 file.seek(value) # Offset(0)
jvra2ad5442002-05-17 18:36:07 +0000760 return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
jvrf2cf9c52002-05-23 21:50:36 +0000761 def write(self, parent, value):
762 return 0 # dummy value
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500763 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +0000764 if hasattr(parent, "ROS"):
765 # if it is a CID-keyed font, then the private Dict is extracted from the parent.FDArray
766 private, fdSelect, fdArray = None, parent.FDSelect, parent.FDArray
767 else:
768 # if it is a name-keyed font, then the private dict is in the top dict, and there is no fdArray.
769 private, fdSelect, fdArray = parent.Private, None, None
770 charStrings = CharStrings(None, None, parent.GlobalSubrs, private, fdSelect, fdArray)
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500771 charStrings.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000772 return charStrings
jvr4756b3a2002-05-16 18:17:32 +0000773
774class CharsetConverter:
775 def read(self, parent, value):
776 isCID = hasattr(parent, "ROS")
777 if value > 2:
778 numGlyphs = parent.numGlyphs
779 file = parent.file
780 file.seek(value)
jvrf2cf9c52002-05-23 21:50:36 +0000781 if DEBUG:
782 print "loading charset at %s" % value
jvra2ad5442002-05-17 18:36:07 +0000783 format = readCard8(file)
Just7842e561999-12-16 21:34:53 +0000784 if format == 0:
jvrc60a44f2006-10-21 13:41:18 +0000785 charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
jvr4756b3a2002-05-16 18:17:32 +0000786 elif format == 1 or format == 2:
787 charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
Just7842e561999-12-16 21:34:53 +0000788 else:
jvr1890b952002-05-15 07:41:30 +0000789 raise NotImplementedError
jvr4756b3a2002-05-16 18:17:32 +0000790 assert len(charset) == numGlyphs
jvrf2cf9c52002-05-23 21:50:36 +0000791 if DEBUG:
792 print " charset end at %s" % file.tell()
jvrc60a44f2006-10-21 13:41:18 +0000793 else: # offset == 0 -> no charset data.
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500794 if isCID or "CharStrings" not in parent.rawDict:
jvrc60a44f2006-10-21 13:41:18 +0000795 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 +0000796 charset = None
797 elif value == 0:
jvrc60a44f2006-10-21 13:41:18 +0000798 charset = cffISOAdobeStrings
jvr4756b3a2002-05-16 18:17:32 +0000799 elif value == 1:
jvrc60a44f2006-10-21 13:41:18 +0000800 charset = cffIExpertStrings
jvr4756b3a2002-05-16 18:17:32 +0000801 elif value == 2:
jvrc60a44f2006-10-21 13:41:18 +0000802 charset = cffExpertSubsetStrings
jvr4756b3a2002-05-16 18:17:32 +0000803 return charset
jvrc60a44f2006-10-21 13:41:18 +0000804
jvrf2cf9c52002-05-23 21:50:36 +0000805 def write(self, parent, value):
806 return 0 # dummy value
jvr7ce0a132002-07-23 16:42:11 +0000807 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000808 # XXX only write charset when not in OT/TTX context, where we
809 # dump charset as a separate "GlyphOrder" table.
810 ##xmlWriter.simpletag("charset")
811 xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
jvr4756b3a2002-05-16 18:17:32 +0000812 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500813 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000814 if 0:
815 return safeEval(attrs["value"])
jvr4756b3a2002-05-16 18:17:32 +0000816
817
jvrf2cf9c52002-05-23 21:50:36 +0000818class CharsetCompiler:
819
820 def __init__(self, strings, charset, parent):
821 assert charset[0] == '.notdef'
jvred101512003-08-22 19:53:32 +0000822 isCID = hasattr(parent.dictObj, "ROS")
823 data0 = packCharset0(charset, isCID, strings)
824 data = packCharset(charset, isCID, strings)
jvr6004baf2002-05-24 10:35:13 +0000825 if len(data) < len(data0):
826 self.data = data
827 else:
828 self.data = data0
jvrf2cf9c52002-05-23 21:50:36 +0000829 self.parent = parent
830
jvr4e5af602002-05-24 09:58:04 +0000831 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000832 self.parent.rawDict["charset"] = pos
833
834 def getDataLength(self):
835 return len(self.data)
836
837 def toFile(self, file):
838 file.write(self.data)
839
840
jvred101512003-08-22 19:53:32 +0000841def getCIDfromName(name, strings):
842 return int(name[3:])
843
844def getSIDfromName(name, strings):
845 return strings.getSID(name)
846
847def packCharset0(charset, isCID, strings):
jvr6004baf2002-05-24 10:35:13 +0000848 format = 0
849 data = [packCard8(format)]
jvred101512003-08-22 19:53:32 +0000850 if isCID:
851 getNameID = getCIDfromName
852 else:
853 getNameID = getSIDfromName
854
jvr6004baf2002-05-24 10:35:13 +0000855 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000856 data.append(packCard16(getNameID(name,strings)))
jvr6004baf2002-05-24 10:35:13 +0000857 return "".join(data)
858
jvred101512003-08-22 19:53:32 +0000859
860def packCharset(charset, isCID, strings):
jvr6004baf2002-05-24 10:35:13 +0000861 format = 1
jvr6004baf2002-05-24 10:35:13 +0000862 ranges = []
863 first = None
864 end = 0
jvred101512003-08-22 19:53:32 +0000865 if isCID:
866 getNameID = getCIDfromName
867 else:
868 getNameID = getSIDfromName
869
jvr6004baf2002-05-24 10:35:13 +0000870 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000871 SID = getNameID(name, strings)
jvr6004baf2002-05-24 10:35:13 +0000872 if first is None:
873 first = SID
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500874 elif end + 1 != SID:
jvr6004baf2002-05-24 10:35:13 +0000875 nLeft = end - first
876 if nLeft > 255:
877 format = 2
878 ranges.append((first, nLeft))
879 first = SID
880 end = SID
881 nLeft = end - first
882 if nLeft > 255:
883 format = 2
884 ranges.append((first, nLeft))
885
jvr74cd1ef2002-05-24 10:38:04 +0000886 data = [packCard8(format)]
jvr6004baf2002-05-24 10:35:13 +0000887 if format == 1:
888 nLeftFunc = packCard8
889 else:
890 nLeftFunc = packCard16
891 for first, nLeft in ranges:
892 data.append(packCard16(first) + nLeftFunc(nLeft))
jvrb58176e2002-05-24 11:55:37 +0000893 return "".join(data)
jvr6004baf2002-05-24 10:35:13 +0000894
jvrc60a44f2006-10-21 13:41:18 +0000895def parseCharset0(numGlyphs, file, strings, isCID):
jvrf2cf9c52002-05-23 21:50:36 +0000896 charset = [".notdef"]
jvrc60a44f2006-10-21 13:41:18 +0000897 if isCID:
898 for i in range(numGlyphs - 1):
899 CID = readCard16(file)
900 charset.append("cid" + string.zfill(str(CID), 5) )
901 else:
902 for i in range(numGlyphs - 1):
903 SID = readCard16(file)
904 charset.append(strings[SID])
jvrf2cf9c52002-05-23 21:50:36 +0000905 return charset
906
jvr4756b3a2002-05-16 18:17:32 +0000907def parseCharset(numGlyphs, file, strings, isCID, format):
908 charset = ['.notdef']
909 count = 1
910 if format == 1:
jvra2ad5442002-05-17 18:36:07 +0000911 nLeftFunc = readCard8
jvr4756b3a2002-05-16 18:17:32 +0000912 else:
jvra2ad5442002-05-17 18:36:07 +0000913 nLeftFunc = readCard16
jvr4756b3a2002-05-16 18:17:32 +0000914 while count < numGlyphs:
jvra2ad5442002-05-17 18:36:07 +0000915 first = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000916 nLeft = nLeftFunc(file)
917 if isCID:
918 for CID in range(first, first+nLeft+1):
jvrc60a44f2006-10-21 13:41:18 +0000919 charset.append("cid" + string.zfill(str(CID), 5) )
jvr1890b952002-05-15 07:41:30 +0000920 else:
jvr4756b3a2002-05-16 18:17:32 +0000921 for SID in range(first, first+nLeft+1):
922 charset.append(strings[SID])
923 count = count + nLeft + 1
924 return charset
925
926
jvrb9702ba2003-01-03 20:56:01 +0000927class EncodingCompiler:
928
929 def __init__(self, strings, encoding, parent):
jvr2a9bcde2008-03-07 19:56:17 +0000930 assert not isinstance(encoding, basestring)
jvrb9702ba2003-01-03 20:56:01 +0000931 data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
932 data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
933 if len(data0) < len(data1):
934 self.data = data0
935 else:
936 self.data = data1
937 self.parent = parent
938
939 def setPos(self, pos, endPos):
940 self.parent.rawDict["Encoding"] = pos
941
942 def getDataLength(self):
943 return len(self.data)
944
945 def toFile(self, file):
946 file.write(self.data)
947
948
949class EncodingConverter(SimpleConverter):
950
951 def read(self, parent, value):
952 if value == 0:
953 return "StandardEncoding"
954 elif value == 1:
955 return "ExpertEncoding"
956 else:
957 assert value > 1
958 file = parent.file
959 file.seek(value)
960 if DEBUG:
961 print "loading Encoding at %s" % value
962 format = readCard8(file)
963 haveSupplement = format & 0x80
964 if haveSupplement:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -0500965 raise NotImplementedError("Encoding supplements are not yet supported")
jvrb9702ba2003-01-03 20:56:01 +0000966 format = format & 0x7f
967 if format == 0:
968 encoding = parseEncoding0(parent.charset, file, haveSupplement,
969 parent.strings)
970 elif format == 1:
971 encoding = parseEncoding1(parent.charset, file, haveSupplement,
972 parent.strings)
973 return encoding
974
975 def write(self, parent, value):
976 if value == "StandardEncoding":
977 return 0
978 elif value == "ExpertEncoding":
979 return 1
980 return 0 # dummy value
981
982 def xmlWrite(self, xmlWriter, name, value, progress):
983 if value in ("StandardEncoding", "ExpertEncoding"):
984 xmlWriter.simpletag(name, name=value)
985 xmlWriter.newline()
986 return
987 xmlWriter.begintag(name)
988 xmlWriter.newline()
989 for code in range(len(value)):
990 glyphName = value[code]
991 if glyphName != ".notdef":
992 xmlWriter.simpletag("map", code=hex(code), name=glyphName)
993 xmlWriter.newline()
994 xmlWriter.endtag(name)
995 xmlWriter.newline()
996
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500997 def xmlRead(self, name, attrs, content, parent):
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500998 if "name" in attrs:
jvrb9702ba2003-01-03 20:56:01 +0000999 return attrs["name"]
1000 encoding = [".notdef"] * 256
1001 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +00001002 if isinstance(element, basestring):
jvrb9702ba2003-01-03 20:56:01 +00001003 continue
1004 name, attrs, content = element
1005 code = safeEval(attrs["code"])
1006 glyphName = attrs["name"]
1007 encoding[code] = glyphName
1008 return encoding
1009
1010
1011def parseEncoding0(charset, file, haveSupplement, strings):
1012 nCodes = readCard8(file)
1013 encoding = [".notdef"] * 256
1014 for glyphID in range(1, nCodes + 1):
1015 code = readCard8(file)
1016 if code != 0:
1017 encoding[code] = charset[glyphID]
1018 return encoding
1019
1020def parseEncoding1(charset, file, haveSupplement, strings):
1021 nRanges = readCard8(file)
1022 encoding = [".notdef"] * 256
1023 glyphID = 1
1024 for i in range(nRanges):
1025 code = readCard8(file)
1026 nLeft = readCard8(file)
1027 for glyphID in range(glyphID, glyphID + nLeft + 1):
1028 encoding[code] = charset[glyphID]
1029 code = code + 1
1030 glyphID = glyphID + 1
1031 return encoding
1032
1033def packEncoding0(charset, encoding, strings):
1034 format = 0
1035 m = {}
1036 for code in range(len(encoding)):
1037 name = encoding[code]
1038 if name != ".notdef":
1039 m[name] = code
1040 codes = []
1041 for name in charset[1:]:
1042 code = m.get(name)
1043 codes.append(code)
1044
1045 while codes and codes[-1] is None:
1046 codes.pop()
1047
1048 data = [packCard8(format), packCard8(len(codes))]
1049 for code in codes:
1050 if code is None:
1051 code = 0
1052 data.append(packCard8(code))
1053 return "".join(data)
1054
1055def packEncoding1(charset, encoding, strings):
1056 format = 1
1057 m = {}
1058 for code in range(len(encoding)):
1059 name = encoding[code]
1060 if name != ".notdef":
1061 m[name] = code
1062 ranges = []
1063 first = None
1064 end = 0
1065 for name in charset[1:]:
1066 code = m.get(name, -1)
1067 if first is None:
1068 first = code
Behdad Esfahbod180ace62013-11-27 02:40:30 -05001069 elif end + 1 != code:
jvrb9702ba2003-01-03 20:56:01 +00001070 nLeft = end - first
1071 ranges.append((first, nLeft))
1072 first = code
1073 end = code
1074 nLeft = end - first
1075 ranges.append((first, nLeft))
1076
1077 # remove unencoded glyphs at the end.
1078 while ranges and ranges[-1][0] == -1:
1079 ranges.pop()
1080
1081 data = [packCard8(format), packCard8(len(ranges))]
1082 for first, nLeft in ranges:
1083 if first == -1: # unencoded
1084 first = 0
1085 data.append(packCard8(first) + packCard8(nLeft))
1086 return "".join(data)
1087
1088
jvr4e5af602002-05-24 09:58:04 +00001089class FDArrayConverter(TableConverter):
jvred101512003-08-22 19:53:32 +00001090
jvra2ad5442002-05-17 18:36:07 +00001091 def read(self, parent, value):
1092 file = parent.file
1093 file.seek(value)
jvred101512003-08-22 19:53:32 +00001094 fdArray = FDArrayIndex(file)
jvra2ad5442002-05-17 18:36:07 +00001095 fdArray.strings = parent.strings
1096 fdArray.GlobalSubrs = parent.GlobalSubrs
1097 return fdArray
1098
jvred101512003-08-22 19:53:32 +00001099 def write(self, parent, value):
1100 return 0 # dummy value
1101
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001102 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001103 fdArray = FDArrayIndex()
1104 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +00001105 if isinstance(element, basestring):
jvred101512003-08-22 19:53:32 +00001106 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001107 name, attrs, content = element
1108 fdArray.fromXML(name, attrs, content)
jvred101512003-08-22 19:53:32 +00001109 return fdArray
1110
jvra2ad5442002-05-17 18:36:07 +00001111
1112class FDSelectConverter:
jvred101512003-08-22 19:53:32 +00001113
jvra2ad5442002-05-17 18:36:07 +00001114 def read(self, parent, value):
1115 file = parent.file
1116 file.seek(value)
jvred101512003-08-22 19:53:32 +00001117 fdSelect = FDSelect(file, parent.numGlyphs)
1118 return fdSelect
1119
1120 def write(self, parent, value):
1121 return 0 # dummy value
1122
1123 # The FDSelect glyph data is written out to XML in the charstring keys,
1124 # so we write out only the format selector
jvr7ce0a132002-07-23 16:42:11 +00001125 def xmlWrite(self, xmlWriter, name, value, progress):
jvred101512003-08-22 19:53:32 +00001126 xmlWriter.simpletag(name, [('format', value.format)])
1127 xmlWriter.newline()
1128
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001129 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001130 format = safeEval(attrs["format"])
1131 file = None
1132 numGlyphs = None
1133 fdSelect = FDSelect(file, numGlyphs, format)
1134 return fdSelect
1135
1136
1137def packFDSelect0(fdSelectArray):
1138 format = 0
1139 data = [packCard8(format)]
1140 for index in fdSelectArray:
1141 data.append(packCard8(index))
1142 return "".join(data)
1143
1144
1145def packFDSelect3(fdSelectArray):
1146 format = 3
1147 fdRanges = []
1148 first = None
1149 end = 0
1150 lenArray = len(fdSelectArray)
1151 lastFDIndex = -1
1152 for i in range(lenArray):
1153 fdIndex = fdSelectArray[i]
1154 if lastFDIndex != fdIndex:
1155 fdRanges.append([i, fdIndex])
1156 lastFDIndex = fdIndex
1157 sentinelGID = i + 1
1158
1159 data = [packCard8(format)]
1160 data.append(packCard16( len(fdRanges) ))
1161 for fdRange in fdRanges:
1162 data.append(packCard16(fdRange[0]))
1163 data.append(packCard8(fdRange[1]))
1164 data.append(packCard16(sentinelGID))
1165 return "".join(data)
1166
1167
1168class FDSelectCompiler:
1169
1170 def __init__(self, fdSelect, parent):
1171 format = fdSelect.format
1172 fdSelectArray = fdSelect.gidArray
1173 if format == 0:
1174 self.data = packFDSelect0(fdSelectArray)
1175 elif format == 3:
1176 self.data = packFDSelect3(fdSelectArray)
1177 else:
1178 # choose smaller of the two formats
1179 data0 = packFDSelect0(fdSelectArray)
1180 data3 = packFDSelect3(fdSelectArray)
1181 if len(data0) < len(data3):
1182 self.data = data0
1183 fdSelect.format = 0
1184 else:
1185 self.data = data3
1186 fdSelect.format = 3
1187
1188 self.parent = parent
1189
1190 def setPos(self, pos, endPos):
1191 self.parent.rawDict["FDSelect"] = pos
1192
1193 def getDataLength(self):
1194 return len(self.data)
1195
1196 def toFile(self, file):
1197 file.write(self.data)
jvra2ad5442002-05-17 18:36:07 +00001198
1199
jvr4e5af602002-05-24 09:58:04 +00001200class ROSConverter(SimpleConverter):
jvred101512003-08-22 19:53:32 +00001201
jvr7ce0a132002-07-23 16:42:11 +00001202 def xmlWrite(self, xmlWriter, name, value, progress):
jvr155aa752002-05-17 19:58:49 +00001203 registry, order, supplement = value
jvrf2cf9c52002-05-23 21:50:36 +00001204 xmlWriter.simpletag(name, [('Registry', registry), ('Order', order),
1205 ('Supplement', supplement)])
jvr4afb2572002-05-18 20:07:01 +00001206 xmlWriter.newline()
jvr155aa752002-05-17 19:58:49 +00001207
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001208 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001209 return (attrs['Registry'], attrs['Order'], safeEval(attrs['Supplement']))
1210
1211
jvr155aa752002-05-17 19:58:49 +00001212
jvr4756b3a2002-05-16 18:17:32 +00001213topDictOperators = [
1214# opcode name argument type default converter
jvr155aa752002-05-17 19:58:49 +00001215 ((12, 30), 'ROS', ('SID','SID','number'), None, ROSConverter()),
jvrf2cf9c52002-05-23 21:50:36 +00001216 ((12, 20), 'SyntheticBase', 'number', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001217 (0, 'version', 'SID', None, None),
jvre2ca9b52002-09-09 14:18:39 +00001218 (1, 'Notice', 'SID', None, Latin1Converter()),
1219 ((12, 0), 'Copyright', 'SID', None, Latin1Converter()),
jvr4756b3a2002-05-16 18:17:32 +00001220 (2, 'FullName', 'SID', None, None),
jvr155aa752002-05-17 19:58:49 +00001221 ((12, 38), 'FontName', 'SID', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001222 (3, 'FamilyName', 'SID', None, None),
1223 (4, 'Weight', 'SID', None, None),
1224 ((12, 1), 'isFixedPitch', 'number', 0, None),
1225 ((12, 2), 'ItalicAngle', 'number', 0, None),
1226 ((12, 3), 'UnderlinePosition', 'number', None, None),
1227 ((12, 4), 'UnderlineThickness', 'number', 50, None),
1228 ((12, 5), 'PaintType', 'number', 0, None),
1229 ((12, 6), 'CharstringType', 'number', 2, None),
1230 ((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0], None),
1231 (13, 'UniqueID', 'number', None, None),
1232 (5, 'FontBBox', 'array', [0,0,0,0], None),
1233 ((12, 8), 'StrokeWidth', 'number', 0, None),
1234 (14, 'XUID', 'array', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001235 ((12, 21), 'PostScript', 'SID', None, None),
1236 ((12, 22), 'BaseFontName', 'SID', None, None),
1237 ((12, 23), 'BaseFontBlend', 'delta', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001238 ((12, 31), 'CIDFontVersion', 'number', 0, None),
1239 ((12, 32), 'CIDFontRevision', 'number', 0, None),
1240 ((12, 33), 'CIDFontType', 'number', 0, None),
1241 ((12, 34), 'CIDCount', 'number', 8720, None),
jvred101512003-08-22 19:53:32 +00001242 (15, 'charset', 'number', 0, CharsetConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001243 ((12, 35), 'UIDBase', 'number', None, None),
jvrb9702ba2003-01-03 20:56:01 +00001244 (16, 'Encoding', 'number', 0, EncodingConverter()),
jvr155aa752002-05-17 19:58:49 +00001245 (18, 'Private', ('number','number'), None, PrivateDictConverter()),
jvred101512003-08-22 19:53:32 +00001246 ((12, 37), 'FDSelect', 'number', None, FDSelectConverter()),
1247 ((12, 36), 'FDArray', 'number', None, FDArrayConverter()),
jvr155aa752002-05-17 19:58:49 +00001248 (17, 'CharStrings', 'number', None, CharStringsConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001249]
1250
jvred101512003-08-22 19:53:32 +00001251# Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order,
1252# in order for the font to compile back from xml.
1253
1254
jvr4756b3a2002-05-16 18:17:32 +00001255privateDictOperators = [
1256# opcode name argument type default converter
1257 (6, 'BlueValues', 'delta', None, None),
1258 (7, 'OtherBlues', 'delta', None, None),
1259 (8, 'FamilyBlues', 'delta', None, None),
1260 (9, 'FamilyOtherBlues', 'delta', None, None),
1261 ((12, 9), 'BlueScale', 'number', 0.039625, None),
1262 ((12, 10), 'BlueShift', 'number', 7, None),
1263 ((12, 11), 'BlueFuzz', 'number', 1, None),
1264 (10, 'StdHW', 'number', None, None),
1265 (11, 'StdVW', 'number', None, None),
1266 ((12, 12), 'StemSnapH', 'delta', None, None),
1267 ((12, 13), 'StemSnapV', 'delta', None, None),
1268 ((12, 14), 'ForceBold', 'number', 0, None),
jvrf2cf9c52002-05-23 21:50:36 +00001269 ((12, 15), 'ForceBoldThreshold', 'number', None, None), # deprecated
1270 ((12, 16), 'lenIV', 'number', None, None), # deprecated
jvr4756b3a2002-05-16 18:17:32 +00001271 ((12, 17), 'LanguageGroup', 'number', 0, None),
1272 ((12, 18), 'ExpansionFactor', 'number', 0.06, None),
1273 ((12, 19), 'initialRandomSeed', 'number', 0, None),
1274 (20, 'defaultWidthX', 'number', 0, None),
1275 (21, 'nominalWidthX', 'number', 0, None),
1276 (19, 'Subrs', 'number', None, SubrsConverter()),
1277]
1278
jvr4e5af602002-05-24 09:58:04 +00001279def addConverters(table):
1280 for i in range(len(table)):
1281 op, name, arg, default, conv = table[i]
1282 if conv is not None:
1283 continue
1284 if arg in ("delta", "array"):
1285 conv = ArrayConverter()
1286 elif arg == "number":
1287 conv = NumberConverter()
1288 elif arg == "SID":
1289 conv = SimpleConverter()
1290 else:
1291 assert 0
1292 table[i] = op, name, arg, default, conv
1293
1294addConverters(privateDictOperators)
1295addConverters(topDictOperators)
1296
jvr4756b3a2002-05-16 18:17:32 +00001297
1298class TopDictDecompiler(psCharStrings.DictDecompiler):
1299 operators = buildOperatorDict(topDictOperators)
1300
1301
1302class PrivateDictDecompiler(psCharStrings.DictDecompiler):
1303 operators = buildOperatorDict(privateDictOperators)
1304
1305
jvrf2cf9c52002-05-23 21:50:36 +00001306class DictCompiler:
1307
1308 def __init__(self, dictObj, strings, parent):
1309 assert isinstance(strings, IndexedStrings)
1310 self.dictObj = dictObj
1311 self.strings = strings
1312 self.parent = parent
1313 rawDict = {}
1314 for name in dictObj.order:
1315 value = getattr(dictObj, name, None)
1316 if value is None:
1317 continue
1318 conv = dictObj.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001319 value = conv.write(dictObj, value)
jvrf2cf9c52002-05-23 21:50:36 +00001320 if value == dictObj.defaults.get(name):
1321 continue
1322 rawDict[name] = value
1323 self.rawDict = rawDict
1324
jvr4e5af602002-05-24 09:58:04 +00001325 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +00001326 pass
1327
1328 def getDataLength(self):
jvr4e5af602002-05-24 09:58:04 +00001329 return len(self.compile("getDataLength"))
jvrf2cf9c52002-05-23 21:50:36 +00001330
jvr4e5af602002-05-24 09:58:04 +00001331 def compile(self, reason):
1332 if DEBUG:
1333 print "-- compiling %s for %s" % (self.__class__.__name__, reason)
jvred101512003-08-22 19:53:32 +00001334 print "in baseDict: ", self
jvrf2cf9c52002-05-23 21:50:36 +00001335 rawDict = self.rawDict
1336 data = []
1337 for name in self.dictObj.order:
1338 value = rawDict.get(name)
1339 if value is None:
1340 continue
1341 op, argType = self.opcodes[name]
jvr2a9bcde2008-03-07 19:56:17 +00001342 if isinstance(argType, tuple):
jvrf2cf9c52002-05-23 21:50:36 +00001343 l = len(argType)
1344 assert len(value) == l, "value doesn't match arg type"
1345 for i in range(l):
jvred101512003-08-22 19:53:32 +00001346 arg = argType[i]
jvrf2cf9c52002-05-23 21:50:36 +00001347 v = value[i]
1348 arghandler = getattr(self, "arg_" + arg)
1349 data.append(arghandler(v))
1350 else:
1351 arghandler = getattr(self, "arg_" + argType)
1352 data.append(arghandler(value))
1353 data.append(op)
1354 return "".join(data)
1355
1356 def toFile(self, file):
jvr4e5af602002-05-24 09:58:04 +00001357 file.write(self.compile("toFile"))
jvrf2cf9c52002-05-23 21:50:36 +00001358
1359 def arg_number(self, num):
1360 return encodeNumber(num)
1361 def arg_SID(self, s):
1362 return psCharStrings.encodeIntCFF(self.strings.getSID(s))
1363 def arg_array(self, value):
1364 data = []
1365 for num in value:
1366 data.append(encodeNumber(num))
1367 return "".join(data)
1368 def arg_delta(self, value):
1369 out = []
1370 last = 0
1371 for v in value:
1372 out.append(v - last)
1373 last = v
1374 data = []
1375 for num in out:
1376 data.append(encodeNumber(num))
1377 return "".join(data)
1378
1379
1380def encodeNumber(num):
jvrf6ff48b2008-03-07 19:49:25 +00001381 if isinstance(num, float):
jvrf2cf9c52002-05-23 21:50:36 +00001382 return psCharStrings.encodeFloat(num)
1383 else:
1384 return psCharStrings.encodeIntCFF(num)
1385
1386
1387class TopDictCompiler(DictCompiler):
1388
1389 opcodes = buildOpcodeDict(topDictOperators)
1390
1391 def getChildren(self, strings):
1392 children = []
jvred101512003-08-22 19:53:32 +00001393 if hasattr(self.dictObj, "charset") and self.dictObj.charset:
jvrf2cf9c52002-05-23 21:50:36 +00001394 children.append(CharsetCompiler(strings, self.dictObj.charset, self))
jvrb9702ba2003-01-03 20:56:01 +00001395 if hasattr(self.dictObj, "Encoding"):
1396 encoding = self.dictObj.Encoding
jvr2a9bcde2008-03-07 19:56:17 +00001397 if not isinstance(encoding, basestring):
jvrb9702ba2003-01-03 20:56:01 +00001398 children.append(EncodingCompiler(strings, encoding, self))
jvrce522412003-08-25 07:37:25 +00001399 if hasattr(self.dictObj, "FDSelect"):
jvred101512003-08-22 19:53:32 +00001400 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1401 # issues about merging the FDArrays. Here I assume that
1402 # either the font was read from XML, and teh FDSelect indices are all
1403 # in the charstring data, or the FDSelect array is already fully defined.
1404 fdSelect = self.dictObj.FDSelect
1405 if len(fdSelect) == 0: # probably read in from XML; assume fdIndex in CharString data
1406 charStrings = self.dictObj.CharStrings
1407 for name in self.dictObj.charset:
1408 charstring = charStrings[name]
1409 fdSelect.append(charStrings[name].fdSelectIndex)
1410 fdSelectComp = FDSelectCompiler(fdSelect, self)
1411 children.append(fdSelectComp)
jvrf2cf9c52002-05-23 21:50:36 +00001412 if hasattr(self.dictObj, "CharStrings"):
1413 items = []
1414 charStrings = self.dictObj.CharStrings
1415 for name in self.dictObj.charset:
1416 items.append(charStrings[name])
1417 charStringsComp = CharStringsCompiler(items, strings, self)
1418 children.append(charStringsComp)
jvrce522412003-08-25 07:37:25 +00001419 if hasattr(self.dictObj, "FDArray"):
jvred101512003-08-22 19:53:32 +00001420 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1421 # issues about merging the FDArrays. Here I assume that the FDArray info is correct
1422 # and complete.
1423 fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self)
1424 children.append(fdArrayIndexComp)
1425 children.extend(fdArrayIndexComp.getChildren(strings))
1426 if hasattr(self.dictObj, "Private"):
1427 privComp = self.dictObj.Private.getCompiler(strings, self)
1428 children.append(privComp)
1429 children.extend(privComp.getChildren(strings))
1430 return children
1431
1432
1433class FontDictCompiler(DictCompiler):
1434
1435 opcodes = buildOpcodeDict(topDictOperators)
1436
1437 def getChildren(self, strings):
1438 children = []
jvrf2cf9c52002-05-23 21:50:36 +00001439 if hasattr(self.dictObj, "Private"):
1440 privComp = self.dictObj.Private.getCompiler(strings, self)
1441 children.append(privComp)
1442 children.extend(privComp.getChildren(strings))
1443 return children
1444
1445
1446class PrivateDictCompiler(DictCompiler):
1447
1448 opcodes = buildOpcodeDict(privateDictOperators)
1449
jvr4e5af602002-05-24 09:58:04 +00001450 def setPos(self, pos, endPos):
1451 size = endPos - pos
jvrf2cf9c52002-05-23 21:50:36 +00001452 self.parent.rawDict["Private"] = size, pos
1453 self.pos = pos
1454
1455 def getChildren(self, strings):
1456 children = []
1457 if hasattr(self.dictObj, "Subrs"):
1458 children.append(self.dictObj.Subrs.getCompiler(strings, self))
1459 return children
1460
jvr4756b3a2002-05-16 18:17:32 +00001461
1462class BaseDict:
1463
jvr4e5af602002-05-24 09:58:04 +00001464 def __init__(self, strings=None, file=None, offset=None):
jvr4756b3a2002-05-16 18:17:32 +00001465 self.rawDict = {}
jvr767102e2002-05-17 07:06:32 +00001466 if DEBUG:
jvrf2cf9c52002-05-23 21:50:36 +00001467 print "loading %s at %s" % (self.__class__.__name__, offset)
jvr4756b3a2002-05-16 18:17:32 +00001468 self.file = file
1469 self.offset = offset
1470 self.strings = strings
jvr155aa752002-05-17 19:58:49 +00001471 self.skipNames = []
jvr4756b3a2002-05-16 18:17:32 +00001472
1473 def decompile(self, data):
jvrf2cf9c52002-05-23 21:50:36 +00001474 if DEBUG:
1475 print " length %s is %s" % (self.__class__.__name__, len(data))
1476 dec = self.decompilerClass(self.strings)
jvr4756b3a2002-05-16 18:17:32 +00001477 dec.decompile(data)
1478 self.rawDict = dec.getDict()
1479 self.postDecompile()
1480
1481 def postDecompile(self):
1482 pass
1483
jvrf2cf9c52002-05-23 21:50:36 +00001484 def getCompiler(self, strings, parent):
1485 return self.compilerClass(self, strings, parent)
1486
jvr4756b3a2002-05-16 18:17:32 +00001487 def __getattr__(self, name):
1488 value = self.rawDict.get(name)
1489 if value is None:
1490 value = self.defaults.get(name)
1491 if value is None:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -05001492 raise AttributeError(name)
jvr4756b3a2002-05-16 18:17:32 +00001493 conv = self.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001494 value = conv.read(self, value)
jvr4756b3a2002-05-16 18:17:32 +00001495 setattr(self, name, value)
1496 return value
1497
1498 def toXML(self, xmlWriter, progress):
1499 for name in self.order:
jvr155aa752002-05-17 19:58:49 +00001500 if name in self.skipNames:
1501 continue
jvr4756b3a2002-05-16 18:17:32 +00001502 value = getattr(self, name, None)
1503 if value is None:
1504 continue
jvr4e5af602002-05-24 09:58:04 +00001505 conv = self.converters[name]
jvr7ce0a132002-07-23 16:42:11 +00001506 conv.xmlWrite(xmlWriter, name, value, progress)
jvr4e5af602002-05-24 09:58:04 +00001507
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001508 def fromXML(self, name, attrs, content):
jvr4e5af602002-05-24 09:58:04 +00001509 conv = self.converters[name]
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001510 value = conv.xmlRead(name, attrs, content, self)
jvr4e5af602002-05-24 09:58:04 +00001511 setattr(self, name, value)
jvr4756b3a2002-05-16 18:17:32 +00001512
1513
1514class TopDict(BaseDict):
1515
1516 defaults = buildDefaults(topDictOperators)
1517 converters = buildConverters(topDictOperators)
1518 order = buildOrder(topDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001519 decompilerClass = TopDictDecompiler
1520 compilerClass = TopDictCompiler
Just7842e561999-12-16 21:34:53 +00001521
jvr4e5af602002-05-24 09:58:04 +00001522 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
jvr016ca762002-05-16 18:38:03 +00001523 BaseDict.__init__(self, strings, file, offset)
1524 self.GlobalSubrs = GlobalSubrs
1525
Just7842e561999-12-16 21:34:53 +00001526 def getGlyphOrder(self):
1527 return self.charset
1528
jvr4756b3a2002-05-16 18:17:32 +00001529 def postDecompile(self):
1530 offset = self.rawDict.get("CharStrings")
1531 if offset is None:
1532 return
1533 # get the number of glyphs beforehand.
1534 self.file.seek(offset)
jvra2ad5442002-05-17 18:36:07 +00001535 self.numGlyphs = readCard16(self.file)
Just7842e561999-12-16 21:34:53 +00001536
jvr016ca762002-05-16 18:38:03 +00001537 def toXML(self, xmlWriter, progress):
jvr155aa752002-05-17 19:58:49 +00001538 if hasattr(self, "CharStrings"):
jvr7ce0a132002-07-23 16:42:11 +00001539 self.decompileAllCharStrings(progress)
jvred101512003-08-22 19:53:32 +00001540 if hasattr(self, "ROS"):
1541 self.skipNames = ['Encoding']
jvr155aa752002-05-17 19:58:49 +00001542 if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
1543 # these values have default values, but I only want them to show up
1544 # in CID fonts.
1545 self.skipNames = ['CIDFontVersion', 'CIDFontRevision', 'CIDFontType',
1546 'CIDCount']
jvr016ca762002-05-16 18:38:03 +00001547 BaseDict.toXML(self, xmlWriter, progress)
jvr767102e2002-05-17 07:06:32 +00001548
jvr7ce0a132002-07-23 16:42:11 +00001549 def decompileAllCharStrings(self, progress):
jvr4e5af602002-05-24 09:58:04 +00001550 # XXX only when doing ttdump -i?
jvr7ce0a132002-07-23 16:42:11 +00001551 i = 0
jvra2ad5442002-05-17 18:36:07 +00001552 for charString in self.CharStrings.values():
jvred101512003-08-22 19:53:32 +00001553 try:
1554 charString.decompile()
1555 except:
1556 print "Error in charstring ", i
1557 import sys
1558 type, value = sys. exc_info()[0:2]
1559 raise type(value)
jvr7ce0a132002-07-23 16:42:11 +00001560 if not i % 30 and progress:
1561 progress.increment(0) # update
1562 i = i + 1
Just7842e561999-12-16 21:34:53 +00001563
1564
jvred101512003-08-22 19:53:32 +00001565class FontDict(BaseDict):
1566
1567 defaults = buildDefaults(topDictOperators)
1568 converters = buildConverters(topDictOperators)
1569 order = buildOrder(topDictOperators)
1570 decompilerClass = None
1571 compilerClass = FontDictCompiler
1572
1573 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
1574 BaseDict.__init__(self, strings, file, offset)
1575 self.GlobalSubrs = GlobalSubrs
1576
1577 def getGlyphOrder(self):
1578 return self.charset
1579
1580 def toXML(self, xmlWriter, progress):
1581 self.skipNames = ['Encoding']
1582 BaseDict.toXML(self, xmlWriter, progress)
1583
1584
1585
jvr4756b3a2002-05-16 18:17:32 +00001586class PrivateDict(BaseDict):
1587 defaults = buildDefaults(privateDictOperators)
1588 converters = buildConverters(privateDictOperators)
1589 order = buildOrder(privateDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001590 decompilerClass = PrivateDictDecompiler
1591 compilerClass = PrivateDictCompiler
Just7842e561999-12-16 21:34:53 +00001592
1593
jvre3275582002-05-14 12:22:03 +00001594class IndexedStrings:
1595
jvr767102e2002-05-17 07:06:32 +00001596 """SID -> string mapping."""
1597
1598 def __init__(self, file=None):
1599 if file is None:
jvre3275582002-05-14 12:22:03 +00001600 strings = []
jvr767102e2002-05-17 07:06:32 +00001601 else:
jvr4e5af602002-05-24 09:58:04 +00001602 strings = list(Index(file))
jvre3275582002-05-14 12:22:03 +00001603 self.strings = strings
1604
jvrf2cf9c52002-05-23 21:50:36 +00001605 def getCompiler(self):
1606 return IndexedStringsCompiler(self, None, None)
1607
1608 def __len__(self):
1609 return len(self.strings)
1610
jvre3275582002-05-14 12:22:03 +00001611 def __getitem__(self, SID):
1612 if SID < cffStandardStringCount:
1613 return cffStandardStrings[SID]
1614 else:
1615 return self.strings[SID - cffStandardStringCount]
1616
1617 def getSID(self, s):
1618 if not hasattr(self, "stringMapping"):
1619 self.buildStringMapping()
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -05001620 if s in cffStandardStringMapping:
jvre3275582002-05-14 12:22:03 +00001621 SID = cffStandardStringMapping[s]
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -05001622 elif s in self.stringMapping:
jvre3275582002-05-14 12:22:03 +00001623 SID = self.stringMapping[s]
1624 else:
1625 SID = len(self.strings) + cffStandardStringCount
1626 self.strings.append(s)
1627 self.stringMapping[s] = SID
1628 return SID
1629
1630 def getStrings(self):
1631 return self.strings
1632
1633 def buildStringMapping(self):
1634 self.stringMapping = {}
1635 for index in range(len(self.strings)):
1636 self.stringMapping[self.strings[index]] = index + cffStandardStringCount
1637
1638
Just7842e561999-12-16 21:34:53 +00001639# The 391 Standard Strings as used in the CFF format.
1640# from Adobe Technical None #5176, version 1.0, 18 March 1998
1641
1642cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
1643 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
1644 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
1645 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
1646 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C',
1647 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
1648 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
1649 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
1650 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
1651 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
1652 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
1653 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
1654 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
1655 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
1656 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
1657 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
1658 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
1659 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
1660 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
1661 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
1662 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
1663 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
1664 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
1665 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
1666 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
1667 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
1668 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
1669 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
1670 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
1671 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
1672 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
1673 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
1674 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
1675 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
1676 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
1677 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
1678 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
1679 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
1680 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
1681 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
1682 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
1683 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
1684 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
1685 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
1686 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
1687 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
1688 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
1689 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
1690 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
1691 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
1692 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
1693 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
1694 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
1695 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
1696 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
1697 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
1698 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
1699 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
1700 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
1701 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
1702 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
1703 '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
1704 'Semibold'
1705]
1706
1707cffStandardStringCount = 391
1708assert len(cffStandardStrings) == cffStandardStringCount
1709# build reverse mapping
1710cffStandardStringMapping = {}
1711for _i in range(cffStandardStringCount):
1712 cffStandardStringMapping[cffStandardStrings[_i]] = _i
jvrc60a44f2006-10-21 13:41:18 +00001713
1714cffISOAdobeStrings = [".notdef", "space", "exclam", "quotedbl", "numbersign",
1715"dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright",
1716"asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two",
1717"three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon",
1718"less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G",
1719"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
1720"X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum",
1721"underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
1722"k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
1723"braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent",
1724"sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle",
1725"quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl",
1726"endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet",
1727"quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis",
1728"perthousand", "questiondown", "grave", "acute", "circumflex", "tilde",
1729"macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
1730"ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE",
1731"ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls",
1732"onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus",
1733"Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn",
1734"threequarters", "twosuperior", "registered", "minus", "eth", "multiply",
1735"threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
1736"Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave",
1737"Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute",
1738"Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute",
1739"Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute",
1740"acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute",
1741"ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis",
1742"igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde",
1743"scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis",
1744"zcaron"]
1745
1746cffISOAdobeStringCount = 229
1747assert len(cffISOAdobeStrings) == cffISOAdobeStringCount
1748
1749cffIExpertStrings = [".notdef", "space", "exclamsmall", "Hungarumlautsmall",
1750"dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall",
1751"parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader",
1752"comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle",
1753"twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle",
1754"sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon",
1755"commasuperior", "threequartersemdash", "periodsuperior", "questionsmall",
1756"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1757"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1758"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1759"parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
1760"Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall",
1761"Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
1762"Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall",
1763"Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall",
1764"exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall",
1765"Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall",
1766"figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall",
1767"onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth",
1768"threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds",
1769"zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior",
1770"fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior",
1771"zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior",
1772"fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior",
1773"centinferior", "dollarinferior", "periodinferior", "commainferior",
1774"Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall",
1775"Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall",
1776"Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall",
1777"Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall",
1778"Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall",
1779"Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall",
1780"Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall",
1781"Ydieresissmall"]
1782
1783cffExpertStringCount = 166
1784assert len(cffIExpertStrings) == cffExpertStringCount
1785
1786cffExpertSubsetStrings = [".notdef", "space", "dollaroldstyle",
1787"dollarsuperior", "parenleftsuperior", "parenrightsuperior", "twodotenleader",
1788"onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle",
1789"oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle",
1790"sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon",
1791"semicolon", "commasuperior", "threequartersemdash", "periodsuperior",
1792"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1793"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1794"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1795"parenrightinferior", "hyphensuperior", "colonmonetary", "onefitted", "rupiah",
1796"centoldstyle", "figuredash", "hypheninferior", "onequarter", "onehalf",
1797"threequarters", "oneeighth", "threeeighths", "fiveeighths", "seveneighths",
1798"onethird", "twothirds", "zerosuperior", "onesuperior", "twosuperior",
1799"threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior",
1800"eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior",
1801"threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior",
1802"eightinferior", "nineinferior", "centinferior", "dollarinferior",
1803"periodinferior", "commainferior"]
1804
1805cffExpertSubsetStringCount = 87
1806assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount