blob: d0c35f206aa71552df54e186bd380e5da183e5df [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
Behdad Esfahbodac1b4352013-11-27 04:15:34 -0500139 while True:
jvr4e5af602002-05-24 09:58:04 +0000140 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500141 print("CFFWriter.toFile() iteration:", count)
jvr4e5af602002-05-24 09:58:04 +0000142 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:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500158 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:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500317 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:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500327 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:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500339 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):
Behdad Esfahbodac1b4352013-11-27 04:15:34 -0500561 names = sorted(self.keys())
jvr7ce0a132002-07-23 16:42:11 +0000562 i = 0
563 step = 10
564 numGlyphs = len(names)
jvr4756b3a2002-05-16 18:17:32 +0000565 for name in names:
jvred101512003-08-22 19:53:32 +0000566 charStr, fdSelectIndex = self.getItemAndSelector(name)
jvrb58176e2002-05-24 11:55:37 +0000567 if charStr.needsDecompilation():
568 raw = [("raw", 1)]
569 else:
570 raw = []
jvred101512003-08-22 19:53:32 +0000571 if fdSelectIndex is None:
jvrb58176e2002-05-24 11:55:37 +0000572 xmlWriter.begintag("CharString", [('name', name)] + raw)
jvra2ad5442002-05-17 18:36:07 +0000573 else:
574 xmlWriter.begintag("CharString",
jvred101512003-08-22 19:53:32 +0000575 [('name', name), ('fdSelectIndex', fdSelectIndex)] + raw)
jvr4756b3a2002-05-16 18:17:32 +0000576 xmlWriter.newline()
jvrb58176e2002-05-24 11:55:37 +0000577 charStr.toXML(xmlWriter)
jvr4756b3a2002-05-16 18:17:32 +0000578 xmlWriter.endtag("CharString")
579 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000580 if not i % step and progress is not None:
581 progress.setLabel("Dumping 'CFF ' table... (%s)" % name)
582 progress.increment(step / float(numGlyphs))
583 i = i + 1
jvr4e5af602002-05-24 09:58:04 +0000584
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500585 def fromXML(self, name, attrs, content):
jvr4e5af602002-05-24 09:58:04 +0000586 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000587 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000588 continue
589 name, attrs, content = element
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500590 if name != "CharString":
jvr4e5af602002-05-24 09:58:04 +0000591 continue
jvred101512003-08-22 19:53:32 +0000592 fdID = -1
593 if hasattr(self, "fdArray"):
594 fdID = safeEval(attrs["fdSelectIndex"])
595 private = self.fdArray[fdID].Private
596 else:
597 private = self.private
598
jvr4e5af602002-05-24 09:58:04 +0000599 glyphName = attrs["name"]
jvr489d76a2003-08-24 19:56:16 +0000600 charString = psCharStrings.T2CharString(
601 private=private,
602 globalSubrs=self.globalSubrs)
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500603 charString.fromXML(name, attrs, content)
jvred101512003-08-22 19:53:32 +0000604 if fdID >= 0:
605 charString.fdSelectIndex = fdID
jvr4e5af602002-05-24 09:58:04 +0000606 self[glyphName] = charString
jvr4756b3a2002-05-16 18:17:32 +0000607
608
jvra2ad5442002-05-17 18:36:07 +0000609def readCard8(file):
610 return ord(file.read(1))
611
612def readCard16(file):
613 value, = struct.unpack(">H", file.read(2))
614 return value
615
jvrf2cf9c52002-05-23 21:50:36 +0000616def writeCard8(file, value):
617 file.write(chr(value))
618
619def writeCard16(file, value):
620 file.write(struct.pack(">H", value))
621
622def packCard8(value):
623 return chr(value)
624
625def packCard16(value):
626 return struct.pack(">H", value)
627
jvr4756b3a2002-05-16 18:17:32 +0000628def buildOperatorDict(table):
629 d = {}
630 for op, name, arg, default, conv in table:
631 d[op] = (name, arg)
632 return d
633
jvrf2cf9c52002-05-23 21:50:36 +0000634def buildOpcodeDict(table):
635 d = {}
636 for op, name, arg, default, conv in table:
jvr2a9bcde2008-03-07 19:56:17 +0000637 if isinstance(op, tuple):
jvrf2cf9c52002-05-23 21:50:36 +0000638 op = chr(op[0]) + chr(op[1])
639 else:
640 op = chr(op)
641 d[name] = (op, arg)
642 return d
643
jvr4756b3a2002-05-16 18:17:32 +0000644def buildOrder(table):
645 l = []
646 for op, name, arg, default, conv in table:
647 l.append(name)
648 return l
649
650def buildDefaults(table):
651 d = {}
652 for op, name, arg, default, conv in table:
653 if default is not None:
654 d[name] = default
655 return d
656
657def buildConverters(table):
658 d = {}
659 for op, name, arg, default, conv in table:
660 d[name] = conv
661 return d
662
663
jvr4e5af602002-05-24 09:58:04 +0000664class SimpleConverter:
jvr7ce02ea2002-05-17 20:04:05 +0000665 def read(self, parent, value):
666 return value
jvrf2cf9c52002-05-23 21:50:36 +0000667 def write(self, parent, value):
668 return value
jvr7ce0a132002-07-23 16:42:11 +0000669 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000670 xmlWriter.simpletag(name, value=value)
671 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500672 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000673 return attrs["value"]
674
jvre2ca9b52002-09-09 14:18:39 +0000675class Latin1Converter(SimpleConverter):
Behdad Esfahbod8c5c9662013-10-28 13:20:00 +0100676 def xmlWrite(self, xmlWriter, name, value, progress):
677 # Store as UTF-8
678 value = unicode(value, "latin-1").encode("utf-8")
679 xmlWriter.simpletag(name, value=value)
680 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500681 def xmlRead(self, name, attrs, content, parent):
jvre2ca9b52002-09-09 14:18:39 +0000682 s = unicode(attrs["value"], "utf-8")
683 return s.encode("latin-1")
684
685
jvr4e5af602002-05-24 09:58:04 +0000686def parseNum(s):
687 try:
688 value = int(s)
689 except:
690 value = float(s)
691 return value
692
693class NumberConverter(SimpleConverter):
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500694 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000695 return parseNum(attrs["value"])
696
697class ArrayConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000698 def xmlWrite(self, xmlWriter, name, value, progress):
Behdad Esfahbode5ca7962013-11-27 04:38:16 -0500699 value = " ".join(map(str, value))
700 xmlWriter.simpletag(name, value=value)
jvr4e5af602002-05-24 09:58:04 +0000701 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500702 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000703 values = attrs["value"].split()
Behdad Esfahbode5ca7962013-11-27 04:38:16 -0500704 return [parseNum(value) for value in values]
jvr4e5af602002-05-24 09:58:04 +0000705
706class TableConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000707 def xmlWrite(self, xmlWriter, name, value, progress):
jvra2ad5442002-05-17 18:36:07 +0000708 xmlWriter.begintag(name)
709 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000710 value.toXML(xmlWriter, progress)
jvra2ad5442002-05-17 18:36:07 +0000711 xmlWriter.endtag(name)
712 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500713 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000714 ob = self.getClass()()
715 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000716 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000717 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500718 name, attrs, content = element
719 ob.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000720 return ob
jvra2ad5442002-05-17 18:36:07 +0000721
jvr4e5af602002-05-24 09:58:04 +0000722class PrivateDictConverter(TableConverter):
723 def getClass(self):
724 return PrivateDict
jvr4756b3a2002-05-16 18:17:32 +0000725 def read(self, parent, value):
726 size, offset = value
727 file = parent.file
jvr4e5af602002-05-24 09:58:04 +0000728 priv = PrivateDict(parent.strings, file, offset)
jvr4756b3a2002-05-16 18:17:32 +0000729 file.seek(offset)
730 data = file.read(size)
731 len(data) == size
jvr4e5af602002-05-24 09:58:04 +0000732 priv.decompile(data)
733 return priv
jvrf2cf9c52002-05-23 21:50:36 +0000734 def write(self, parent, value):
735 return (0, 0) # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000736
jvr4e5af602002-05-24 09:58:04 +0000737class SubrsConverter(TableConverter):
738 def getClass(self):
739 return SubrsIndex
jvr4756b3a2002-05-16 18:17:32 +0000740 def read(self, parent, value):
741 file = parent.file
742 file.seek(parent.offset + value) # Offset(self)
jvr4e5af602002-05-24 09:58:04 +0000743 return SubrsIndex(file)
jvrf2cf9c52002-05-23 21:50:36 +0000744 def write(self, parent, value):
745 return 0 # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000746
jvr4e5af602002-05-24 09:58:04 +0000747class CharStringsConverter(TableConverter):
jvr4756b3a2002-05-16 18:17:32 +0000748 def read(self, parent, value):
749 file = parent.file
jvr767102e2002-05-17 07:06:32 +0000750 charset = parent.charset
jvra2ad5442002-05-17 18:36:07 +0000751 globalSubrs = parent.GlobalSubrs
752 if hasattr(parent, "ROS"):
753 fdSelect, fdArray = parent.FDSelect, parent.FDArray
754 private = None
755 else:
756 fdSelect, fdArray = None, None
757 private = parent.Private
jvr4756b3a2002-05-16 18:17:32 +0000758 file.seek(value) # Offset(0)
jvra2ad5442002-05-17 18:36:07 +0000759 return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
jvrf2cf9c52002-05-23 21:50:36 +0000760 def write(self, parent, value):
761 return 0 # dummy value
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500762 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +0000763 if hasattr(parent, "ROS"):
764 # if it is a CID-keyed font, then the private Dict is extracted from the parent.FDArray
765 private, fdSelect, fdArray = None, parent.FDSelect, parent.FDArray
766 else:
767 # if it is a name-keyed font, then the private dict is in the top dict, and there is no fdArray.
768 private, fdSelect, fdArray = parent.Private, None, None
769 charStrings = CharStrings(None, None, parent.GlobalSubrs, private, fdSelect, fdArray)
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500770 charStrings.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000771 return charStrings
jvr4756b3a2002-05-16 18:17:32 +0000772
773class CharsetConverter:
774 def read(self, parent, value):
775 isCID = hasattr(parent, "ROS")
776 if value > 2:
777 numGlyphs = parent.numGlyphs
778 file = parent.file
779 file.seek(value)
jvrf2cf9c52002-05-23 21:50:36 +0000780 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500781 print("loading charset at %s" % value)
jvra2ad5442002-05-17 18:36:07 +0000782 format = readCard8(file)
Just7842e561999-12-16 21:34:53 +0000783 if format == 0:
jvrc60a44f2006-10-21 13:41:18 +0000784 charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
jvr4756b3a2002-05-16 18:17:32 +0000785 elif format == 1 or format == 2:
786 charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
Just7842e561999-12-16 21:34:53 +0000787 else:
jvr1890b952002-05-15 07:41:30 +0000788 raise NotImplementedError
jvr4756b3a2002-05-16 18:17:32 +0000789 assert len(charset) == numGlyphs
jvrf2cf9c52002-05-23 21:50:36 +0000790 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500791 print(" charset end at %s" % file.tell())
jvrc60a44f2006-10-21 13:41:18 +0000792 else: # offset == 0 -> no charset data.
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500793 if isCID or "CharStrings" not in parent.rawDict:
jvrc60a44f2006-10-21 13:41:18 +0000794 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 +0000795 charset = None
796 elif value == 0:
jvrc60a44f2006-10-21 13:41:18 +0000797 charset = cffISOAdobeStrings
jvr4756b3a2002-05-16 18:17:32 +0000798 elif value == 1:
jvrc60a44f2006-10-21 13:41:18 +0000799 charset = cffIExpertStrings
jvr4756b3a2002-05-16 18:17:32 +0000800 elif value == 2:
jvrc60a44f2006-10-21 13:41:18 +0000801 charset = cffExpertSubsetStrings
jvr4756b3a2002-05-16 18:17:32 +0000802 return charset
jvrc60a44f2006-10-21 13:41:18 +0000803
jvrf2cf9c52002-05-23 21:50:36 +0000804 def write(self, parent, value):
805 return 0 # dummy value
jvr7ce0a132002-07-23 16:42:11 +0000806 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000807 # XXX only write charset when not in OT/TTX context, where we
808 # dump charset as a separate "GlyphOrder" table.
809 ##xmlWriter.simpletag("charset")
810 xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
jvr4756b3a2002-05-16 18:17:32 +0000811 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500812 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000813 if 0:
814 return safeEval(attrs["value"])
jvr4756b3a2002-05-16 18:17:32 +0000815
816
jvrf2cf9c52002-05-23 21:50:36 +0000817class CharsetCompiler:
818
819 def __init__(self, strings, charset, parent):
820 assert charset[0] == '.notdef'
jvred101512003-08-22 19:53:32 +0000821 isCID = hasattr(parent.dictObj, "ROS")
822 data0 = packCharset0(charset, isCID, strings)
823 data = packCharset(charset, isCID, strings)
jvr6004baf2002-05-24 10:35:13 +0000824 if len(data) < len(data0):
825 self.data = data
826 else:
827 self.data = data0
jvrf2cf9c52002-05-23 21:50:36 +0000828 self.parent = parent
829
jvr4e5af602002-05-24 09:58:04 +0000830 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000831 self.parent.rawDict["charset"] = pos
832
833 def getDataLength(self):
834 return len(self.data)
835
836 def toFile(self, file):
837 file.write(self.data)
838
839
jvred101512003-08-22 19:53:32 +0000840def getCIDfromName(name, strings):
841 return int(name[3:])
842
843def getSIDfromName(name, strings):
844 return strings.getSID(name)
845
846def packCharset0(charset, isCID, strings):
jvr6004baf2002-05-24 10:35:13 +0000847 format = 0
848 data = [packCard8(format)]
jvred101512003-08-22 19:53:32 +0000849 if isCID:
850 getNameID = getCIDfromName
851 else:
852 getNameID = getSIDfromName
853
jvr6004baf2002-05-24 10:35:13 +0000854 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000855 data.append(packCard16(getNameID(name,strings)))
jvr6004baf2002-05-24 10:35:13 +0000856 return "".join(data)
857
jvred101512003-08-22 19:53:32 +0000858
859def packCharset(charset, isCID, strings):
jvr6004baf2002-05-24 10:35:13 +0000860 format = 1
jvr6004baf2002-05-24 10:35:13 +0000861 ranges = []
862 first = None
863 end = 0
jvred101512003-08-22 19:53:32 +0000864 if isCID:
865 getNameID = getCIDfromName
866 else:
867 getNameID = getSIDfromName
868
jvr6004baf2002-05-24 10:35:13 +0000869 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000870 SID = getNameID(name, strings)
jvr6004baf2002-05-24 10:35:13 +0000871 if first is None:
872 first = SID
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500873 elif end + 1 != SID:
jvr6004baf2002-05-24 10:35:13 +0000874 nLeft = end - first
875 if nLeft > 255:
876 format = 2
877 ranges.append((first, nLeft))
878 first = SID
879 end = SID
880 nLeft = end - first
881 if nLeft > 255:
882 format = 2
883 ranges.append((first, nLeft))
884
jvr74cd1ef2002-05-24 10:38:04 +0000885 data = [packCard8(format)]
jvr6004baf2002-05-24 10:35:13 +0000886 if format == 1:
887 nLeftFunc = packCard8
888 else:
889 nLeftFunc = packCard16
890 for first, nLeft in ranges:
891 data.append(packCard16(first) + nLeftFunc(nLeft))
jvrb58176e2002-05-24 11:55:37 +0000892 return "".join(data)
jvr6004baf2002-05-24 10:35:13 +0000893
jvrc60a44f2006-10-21 13:41:18 +0000894def parseCharset0(numGlyphs, file, strings, isCID):
jvrf2cf9c52002-05-23 21:50:36 +0000895 charset = [".notdef"]
jvrc60a44f2006-10-21 13:41:18 +0000896 if isCID:
897 for i in range(numGlyphs - 1):
898 CID = readCard16(file)
899 charset.append("cid" + string.zfill(str(CID), 5) )
900 else:
901 for i in range(numGlyphs - 1):
902 SID = readCard16(file)
903 charset.append(strings[SID])
jvrf2cf9c52002-05-23 21:50:36 +0000904 return charset
905
jvr4756b3a2002-05-16 18:17:32 +0000906def parseCharset(numGlyphs, file, strings, isCID, format):
907 charset = ['.notdef']
908 count = 1
909 if format == 1:
jvra2ad5442002-05-17 18:36:07 +0000910 nLeftFunc = readCard8
jvr4756b3a2002-05-16 18:17:32 +0000911 else:
jvra2ad5442002-05-17 18:36:07 +0000912 nLeftFunc = readCard16
jvr4756b3a2002-05-16 18:17:32 +0000913 while count < numGlyphs:
jvra2ad5442002-05-17 18:36:07 +0000914 first = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000915 nLeft = nLeftFunc(file)
916 if isCID:
917 for CID in range(first, first+nLeft+1):
jvrc60a44f2006-10-21 13:41:18 +0000918 charset.append("cid" + string.zfill(str(CID), 5) )
jvr1890b952002-05-15 07:41:30 +0000919 else:
jvr4756b3a2002-05-16 18:17:32 +0000920 for SID in range(first, first+nLeft+1):
921 charset.append(strings[SID])
922 count = count + nLeft + 1
923 return charset
924
925
jvrb9702ba2003-01-03 20:56:01 +0000926class EncodingCompiler:
927
928 def __init__(self, strings, encoding, parent):
jvr2a9bcde2008-03-07 19:56:17 +0000929 assert not isinstance(encoding, basestring)
jvrb9702ba2003-01-03 20:56:01 +0000930 data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
931 data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
932 if len(data0) < len(data1):
933 self.data = data0
934 else:
935 self.data = data1
936 self.parent = parent
937
938 def setPos(self, pos, endPos):
939 self.parent.rawDict["Encoding"] = pos
940
941 def getDataLength(self):
942 return len(self.data)
943
944 def toFile(self, file):
945 file.write(self.data)
946
947
948class EncodingConverter(SimpleConverter):
949
950 def read(self, parent, value):
951 if value == 0:
952 return "StandardEncoding"
953 elif value == 1:
954 return "ExpertEncoding"
955 else:
956 assert value > 1
957 file = parent.file
958 file.seek(value)
959 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500960 print("loading Encoding at %s" % value)
jvrb9702ba2003-01-03 20:56:01 +0000961 format = readCard8(file)
962 haveSupplement = format & 0x80
963 if haveSupplement:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -0500964 raise NotImplementedError("Encoding supplements are not yet supported")
jvrb9702ba2003-01-03 20:56:01 +0000965 format = format & 0x7f
966 if format == 0:
967 encoding = parseEncoding0(parent.charset, file, haveSupplement,
968 parent.strings)
969 elif format == 1:
970 encoding = parseEncoding1(parent.charset, file, haveSupplement,
971 parent.strings)
972 return encoding
973
974 def write(self, parent, value):
975 if value == "StandardEncoding":
976 return 0
977 elif value == "ExpertEncoding":
978 return 1
979 return 0 # dummy value
980
981 def xmlWrite(self, xmlWriter, name, value, progress):
982 if value in ("StandardEncoding", "ExpertEncoding"):
983 xmlWriter.simpletag(name, name=value)
984 xmlWriter.newline()
985 return
986 xmlWriter.begintag(name)
987 xmlWriter.newline()
988 for code in range(len(value)):
989 glyphName = value[code]
990 if glyphName != ".notdef":
991 xmlWriter.simpletag("map", code=hex(code), name=glyphName)
992 xmlWriter.newline()
993 xmlWriter.endtag(name)
994 xmlWriter.newline()
995
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500996 def xmlRead(self, name, attrs, content, parent):
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500997 if "name" in attrs:
jvrb9702ba2003-01-03 20:56:01 +0000998 return attrs["name"]
999 encoding = [".notdef"] * 256
1000 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +00001001 if isinstance(element, basestring):
jvrb9702ba2003-01-03 20:56:01 +00001002 continue
1003 name, attrs, content = element
1004 code = safeEval(attrs["code"])
1005 glyphName = attrs["name"]
1006 encoding[code] = glyphName
1007 return encoding
1008
1009
1010def parseEncoding0(charset, file, haveSupplement, strings):
1011 nCodes = readCard8(file)
1012 encoding = [".notdef"] * 256
1013 for glyphID in range(1, nCodes + 1):
1014 code = readCard8(file)
1015 if code != 0:
1016 encoding[code] = charset[glyphID]
1017 return encoding
1018
1019def parseEncoding1(charset, file, haveSupplement, strings):
1020 nRanges = readCard8(file)
1021 encoding = [".notdef"] * 256
1022 glyphID = 1
1023 for i in range(nRanges):
1024 code = readCard8(file)
1025 nLeft = readCard8(file)
1026 for glyphID in range(glyphID, glyphID + nLeft + 1):
1027 encoding[code] = charset[glyphID]
1028 code = code + 1
1029 glyphID = glyphID + 1
1030 return encoding
1031
1032def packEncoding0(charset, encoding, strings):
1033 format = 0
1034 m = {}
1035 for code in range(len(encoding)):
1036 name = encoding[code]
1037 if name != ".notdef":
1038 m[name] = code
1039 codes = []
1040 for name in charset[1:]:
1041 code = m.get(name)
1042 codes.append(code)
1043
1044 while codes and codes[-1] is None:
1045 codes.pop()
1046
1047 data = [packCard8(format), packCard8(len(codes))]
1048 for code in codes:
1049 if code is None:
1050 code = 0
1051 data.append(packCard8(code))
1052 return "".join(data)
1053
1054def packEncoding1(charset, encoding, strings):
1055 format = 1
1056 m = {}
1057 for code in range(len(encoding)):
1058 name = encoding[code]
1059 if name != ".notdef":
1060 m[name] = code
1061 ranges = []
1062 first = None
1063 end = 0
1064 for name in charset[1:]:
1065 code = m.get(name, -1)
1066 if first is None:
1067 first = code
Behdad Esfahbod180ace62013-11-27 02:40:30 -05001068 elif end + 1 != code:
jvrb9702ba2003-01-03 20:56:01 +00001069 nLeft = end - first
1070 ranges.append((first, nLeft))
1071 first = code
1072 end = code
1073 nLeft = end - first
1074 ranges.append((first, nLeft))
1075
1076 # remove unencoded glyphs at the end.
1077 while ranges and ranges[-1][0] == -1:
1078 ranges.pop()
1079
1080 data = [packCard8(format), packCard8(len(ranges))]
1081 for first, nLeft in ranges:
1082 if first == -1: # unencoded
1083 first = 0
1084 data.append(packCard8(first) + packCard8(nLeft))
1085 return "".join(data)
1086
1087
jvr4e5af602002-05-24 09:58:04 +00001088class FDArrayConverter(TableConverter):
jvred101512003-08-22 19:53:32 +00001089
jvra2ad5442002-05-17 18:36:07 +00001090 def read(self, parent, value):
1091 file = parent.file
1092 file.seek(value)
jvred101512003-08-22 19:53:32 +00001093 fdArray = FDArrayIndex(file)
jvra2ad5442002-05-17 18:36:07 +00001094 fdArray.strings = parent.strings
1095 fdArray.GlobalSubrs = parent.GlobalSubrs
1096 return fdArray
1097
jvred101512003-08-22 19:53:32 +00001098 def write(self, parent, value):
1099 return 0 # dummy value
1100
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001101 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001102 fdArray = FDArrayIndex()
1103 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +00001104 if isinstance(element, basestring):
jvred101512003-08-22 19:53:32 +00001105 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001106 name, attrs, content = element
1107 fdArray.fromXML(name, attrs, content)
jvred101512003-08-22 19:53:32 +00001108 return fdArray
1109
jvra2ad5442002-05-17 18:36:07 +00001110
1111class FDSelectConverter:
jvred101512003-08-22 19:53:32 +00001112
jvra2ad5442002-05-17 18:36:07 +00001113 def read(self, parent, value):
1114 file = parent.file
1115 file.seek(value)
jvred101512003-08-22 19:53:32 +00001116 fdSelect = FDSelect(file, parent.numGlyphs)
1117 return fdSelect
1118
1119 def write(self, parent, value):
1120 return 0 # dummy value
1121
1122 # The FDSelect glyph data is written out to XML in the charstring keys,
1123 # so we write out only the format selector
jvr7ce0a132002-07-23 16:42:11 +00001124 def xmlWrite(self, xmlWriter, name, value, progress):
jvred101512003-08-22 19:53:32 +00001125 xmlWriter.simpletag(name, [('format', value.format)])
1126 xmlWriter.newline()
1127
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001128 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001129 format = safeEval(attrs["format"])
1130 file = None
1131 numGlyphs = None
1132 fdSelect = FDSelect(file, numGlyphs, format)
1133 return fdSelect
1134
1135
1136def packFDSelect0(fdSelectArray):
1137 format = 0
1138 data = [packCard8(format)]
1139 for index in fdSelectArray:
1140 data.append(packCard8(index))
1141 return "".join(data)
1142
1143
1144def packFDSelect3(fdSelectArray):
1145 format = 3
1146 fdRanges = []
1147 first = None
1148 end = 0
1149 lenArray = len(fdSelectArray)
1150 lastFDIndex = -1
1151 for i in range(lenArray):
1152 fdIndex = fdSelectArray[i]
1153 if lastFDIndex != fdIndex:
1154 fdRanges.append([i, fdIndex])
1155 lastFDIndex = fdIndex
1156 sentinelGID = i + 1
1157
1158 data = [packCard8(format)]
1159 data.append(packCard16( len(fdRanges) ))
1160 for fdRange in fdRanges:
1161 data.append(packCard16(fdRange[0]))
1162 data.append(packCard8(fdRange[1]))
1163 data.append(packCard16(sentinelGID))
1164 return "".join(data)
1165
1166
1167class FDSelectCompiler:
1168
1169 def __init__(self, fdSelect, parent):
1170 format = fdSelect.format
1171 fdSelectArray = fdSelect.gidArray
1172 if format == 0:
1173 self.data = packFDSelect0(fdSelectArray)
1174 elif format == 3:
1175 self.data = packFDSelect3(fdSelectArray)
1176 else:
1177 # choose smaller of the two formats
1178 data0 = packFDSelect0(fdSelectArray)
1179 data3 = packFDSelect3(fdSelectArray)
1180 if len(data0) < len(data3):
1181 self.data = data0
1182 fdSelect.format = 0
1183 else:
1184 self.data = data3
1185 fdSelect.format = 3
1186
1187 self.parent = parent
1188
1189 def setPos(self, pos, endPos):
1190 self.parent.rawDict["FDSelect"] = pos
1191
1192 def getDataLength(self):
1193 return len(self.data)
1194
1195 def toFile(self, file):
1196 file.write(self.data)
jvra2ad5442002-05-17 18:36:07 +00001197
1198
jvr4e5af602002-05-24 09:58:04 +00001199class ROSConverter(SimpleConverter):
jvred101512003-08-22 19:53:32 +00001200
jvr7ce0a132002-07-23 16:42:11 +00001201 def xmlWrite(self, xmlWriter, name, value, progress):
jvr155aa752002-05-17 19:58:49 +00001202 registry, order, supplement = value
jvrf2cf9c52002-05-23 21:50:36 +00001203 xmlWriter.simpletag(name, [('Registry', registry), ('Order', order),
1204 ('Supplement', supplement)])
jvr4afb2572002-05-18 20:07:01 +00001205 xmlWriter.newline()
jvr155aa752002-05-17 19:58:49 +00001206
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001207 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001208 return (attrs['Registry'], attrs['Order'], safeEval(attrs['Supplement']))
1209
1210
jvr155aa752002-05-17 19:58:49 +00001211
jvr4756b3a2002-05-16 18:17:32 +00001212topDictOperators = [
1213# opcode name argument type default converter
jvr155aa752002-05-17 19:58:49 +00001214 ((12, 30), 'ROS', ('SID','SID','number'), None, ROSConverter()),
jvrf2cf9c52002-05-23 21:50:36 +00001215 ((12, 20), 'SyntheticBase', 'number', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001216 (0, 'version', 'SID', None, None),
jvre2ca9b52002-09-09 14:18:39 +00001217 (1, 'Notice', 'SID', None, Latin1Converter()),
1218 ((12, 0), 'Copyright', 'SID', None, Latin1Converter()),
jvr4756b3a2002-05-16 18:17:32 +00001219 (2, 'FullName', 'SID', None, None),
jvr155aa752002-05-17 19:58:49 +00001220 ((12, 38), 'FontName', 'SID', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001221 (3, 'FamilyName', 'SID', None, None),
1222 (4, 'Weight', 'SID', None, None),
1223 ((12, 1), 'isFixedPitch', 'number', 0, None),
1224 ((12, 2), 'ItalicAngle', 'number', 0, None),
1225 ((12, 3), 'UnderlinePosition', 'number', None, None),
1226 ((12, 4), 'UnderlineThickness', 'number', 50, None),
1227 ((12, 5), 'PaintType', 'number', 0, None),
1228 ((12, 6), 'CharstringType', 'number', 2, None),
1229 ((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0], None),
1230 (13, 'UniqueID', 'number', None, None),
1231 (5, 'FontBBox', 'array', [0,0,0,0], None),
1232 ((12, 8), 'StrokeWidth', 'number', 0, None),
1233 (14, 'XUID', 'array', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001234 ((12, 21), 'PostScript', 'SID', None, None),
1235 ((12, 22), 'BaseFontName', 'SID', None, None),
1236 ((12, 23), 'BaseFontBlend', 'delta', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001237 ((12, 31), 'CIDFontVersion', 'number', 0, None),
1238 ((12, 32), 'CIDFontRevision', 'number', 0, None),
1239 ((12, 33), 'CIDFontType', 'number', 0, None),
1240 ((12, 34), 'CIDCount', 'number', 8720, None),
jvred101512003-08-22 19:53:32 +00001241 (15, 'charset', 'number', 0, CharsetConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001242 ((12, 35), 'UIDBase', 'number', None, None),
jvrb9702ba2003-01-03 20:56:01 +00001243 (16, 'Encoding', 'number', 0, EncodingConverter()),
jvr155aa752002-05-17 19:58:49 +00001244 (18, 'Private', ('number','number'), None, PrivateDictConverter()),
jvred101512003-08-22 19:53:32 +00001245 ((12, 37), 'FDSelect', 'number', None, FDSelectConverter()),
1246 ((12, 36), 'FDArray', 'number', None, FDArrayConverter()),
jvr155aa752002-05-17 19:58:49 +00001247 (17, 'CharStrings', 'number', None, CharStringsConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001248]
1249
jvred101512003-08-22 19:53:32 +00001250# Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order,
1251# in order for the font to compile back from xml.
1252
1253
jvr4756b3a2002-05-16 18:17:32 +00001254privateDictOperators = [
1255# opcode name argument type default converter
1256 (6, 'BlueValues', 'delta', None, None),
1257 (7, 'OtherBlues', 'delta', None, None),
1258 (8, 'FamilyBlues', 'delta', None, None),
1259 (9, 'FamilyOtherBlues', 'delta', None, None),
1260 ((12, 9), 'BlueScale', 'number', 0.039625, None),
1261 ((12, 10), 'BlueShift', 'number', 7, None),
1262 ((12, 11), 'BlueFuzz', 'number', 1, None),
1263 (10, 'StdHW', 'number', None, None),
1264 (11, 'StdVW', 'number', None, None),
1265 ((12, 12), 'StemSnapH', 'delta', None, None),
1266 ((12, 13), 'StemSnapV', 'delta', None, None),
1267 ((12, 14), 'ForceBold', 'number', 0, None),
jvrf2cf9c52002-05-23 21:50:36 +00001268 ((12, 15), 'ForceBoldThreshold', 'number', None, None), # deprecated
1269 ((12, 16), 'lenIV', 'number', None, None), # deprecated
jvr4756b3a2002-05-16 18:17:32 +00001270 ((12, 17), 'LanguageGroup', 'number', 0, None),
1271 ((12, 18), 'ExpansionFactor', 'number', 0.06, None),
1272 ((12, 19), 'initialRandomSeed', 'number', 0, None),
1273 (20, 'defaultWidthX', 'number', 0, None),
1274 (21, 'nominalWidthX', 'number', 0, None),
1275 (19, 'Subrs', 'number', None, SubrsConverter()),
1276]
1277
jvr4e5af602002-05-24 09:58:04 +00001278def addConverters(table):
1279 for i in range(len(table)):
1280 op, name, arg, default, conv = table[i]
1281 if conv is not None:
1282 continue
1283 if arg in ("delta", "array"):
1284 conv = ArrayConverter()
1285 elif arg == "number":
1286 conv = NumberConverter()
1287 elif arg == "SID":
1288 conv = SimpleConverter()
1289 else:
1290 assert 0
1291 table[i] = op, name, arg, default, conv
1292
1293addConverters(privateDictOperators)
1294addConverters(topDictOperators)
1295
jvr4756b3a2002-05-16 18:17:32 +00001296
1297class TopDictDecompiler(psCharStrings.DictDecompiler):
1298 operators = buildOperatorDict(topDictOperators)
1299
1300
1301class PrivateDictDecompiler(psCharStrings.DictDecompiler):
1302 operators = buildOperatorDict(privateDictOperators)
1303
1304
jvrf2cf9c52002-05-23 21:50:36 +00001305class DictCompiler:
1306
1307 def __init__(self, dictObj, strings, parent):
1308 assert isinstance(strings, IndexedStrings)
1309 self.dictObj = dictObj
1310 self.strings = strings
1311 self.parent = parent
1312 rawDict = {}
1313 for name in dictObj.order:
1314 value = getattr(dictObj, name, None)
1315 if value is None:
1316 continue
1317 conv = dictObj.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001318 value = conv.write(dictObj, value)
jvrf2cf9c52002-05-23 21:50:36 +00001319 if value == dictObj.defaults.get(name):
1320 continue
1321 rawDict[name] = value
1322 self.rawDict = rawDict
1323
jvr4e5af602002-05-24 09:58:04 +00001324 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +00001325 pass
1326
1327 def getDataLength(self):
jvr4e5af602002-05-24 09:58:04 +00001328 return len(self.compile("getDataLength"))
jvrf2cf9c52002-05-23 21:50:36 +00001329
jvr4e5af602002-05-24 09:58:04 +00001330 def compile(self, reason):
1331 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001332 print("-- compiling %s for %s" % (self.__class__.__name__, reason))
1333 print("in baseDict: ", self)
jvrf2cf9c52002-05-23 21:50:36 +00001334 rawDict = self.rawDict
1335 data = []
1336 for name in self.dictObj.order:
1337 value = rawDict.get(name)
1338 if value is None:
1339 continue
1340 op, argType = self.opcodes[name]
jvr2a9bcde2008-03-07 19:56:17 +00001341 if isinstance(argType, tuple):
jvrf2cf9c52002-05-23 21:50:36 +00001342 l = len(argType)
1343 assert len(value) == l, "value doesn't match arg type"
1344 for i in range(l):
jvred101512003-08-22 19:53:32 +00001345 arg = argType[i]
jvrf2cf9c52002-05-23 21:50:36 +00001346 v = value[i]
1347 arghandler = getattr(self, "arg_" + arg)
1348 data.append(arghandler(v))
1349 else:
1350 arghandler = getattr(self, "arg_" + argType)
1351 data.append(arghandler(value))
1352 data.append(op)
1353 return "".join(data)
1354
1355 def toFile(self, file):
jvr4e5af602002-05-24 09:58:04 +00001356 file.write(self.compile("toFile"))
jvrf2cf9c52002-05-23 21:50:36 +00001357
1358 def arg_number(self, num):
1359 return encodeNumber(num)
1360 def arg_SID(self, s):
1361 return psCharStrings.encodeIntCFF(self.strings.getSID(s))
1362 def arg_array(self, value):
1363 data = []
1364 for num in value:
1365 data.append(encodeNumber(num))
1366 return "".join(data)
1367 def arg_delta(self, value):
1368 out = []
1369 last = 0
1370 for v in value:
1371 out.append(v - last)
1372 last = v
1373 data = []
1374 for num in out:
1375 data.append(encodeNumber(num))
1376 return "".join(data)
1377
1378
1379def encodeNumber(num):
jvrf6ff48b2008-03-07 19:49:25 +00001380 if isinstance(num, float):
jvrf2cf9c52002-05-23 21:50:36 +00001381 return psCharStrings.encodeFloat(num)
1382 else:
1383 return psCharStrings.encodeIntCFF(num)
1384
1385
1386class TopDictCompiler(DictCompiler):
1387
1388 opcodes = buildOpcodeDict(topDictOperators)
1389
1390 def getChildren(self, strings):
1391 children = []
jvred101512003-08-22 19:53:32 +00001392 if hasattr(self.dictObj, "charset") and self.dictObj.charset:
jvrf2cf9c52002-05-23 21:50:36 +00001393 children.append(CharsetCompiler(strings, self.dictObj.charset, self))
jvrb9702ba2003-01-03 20:56:01 +00001394 if hasattr(self.dictObj, "Encoding"):
1395 encoding = self.dictObj.Encoding
jvr2a9bcde2008-03-07 19:56:17 +00001396 if not isinstance(encoding, basestring):
jvrb9702ba2003-01-03 20:56:01 +00001397 children.append(EncodingCompiler(strings, encoding, self))
jvrce522412003-08-25 07:37:25 +00001398 if hasattr(self.dictObj, "FDSelect"):
jvred101512003-08-22 19:53:32 +00001399 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1400 # issues about merging the FDArrays. Here I assume that
1401 # either the font was read from XML, and teh FDSelect indices are all
1402 # in the charstring data, or the FDSelect array is already fully defined.
1403 fdSelect = self.dictObj.FDSelect
1404 if len(fdSelect) == 0: # probably read in from XML; assume fdIndex in CharString data
1405 charStrings = self.dictObj.CharStrings
1406 for name in self.dictObj.charset:
1407 charstring = charStrings[name]
1408 fdSelect.append(charStrings[name].fdSelectIndex)
1409 fdSelectComp = FDSelectCompiler(fdSelect, self)
1410 children.append(fdSelectComp)
jvrf2cf9c52002-05-23 21:50:36 +00001411 if hasattr(self.dictObj, "CharStrings"):
1412 items = []
1413 charStrings = self.dictObj.CharStrings
1414 for name in self.dictObj.charset:
1415 items.append(charStrings[name])
1416 charStringsComp = CharStringsCompiler(items, strings, self)
1417 children.append(charStringsComp)
jvrce522412003-08-25 07:37:25 +00001418 if hasattr(self.dictObj, "FDArray"):
jvred101512003-08-22 19:53:32 +00001419 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1420 # issues about merging the FDArrays. Here I assume that the FDArray info is correct
1421 # and complete.
1422 fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self)
1423 children.append(fdArrayIndexComp)
1424 children.extend(fdArrayIndexComp.getChildren(strings))
1425 if hasattr(self.dictObj, "Private"):
1426 privComp = self.dictObj.Private.getCompiler(strings, self)
1427 children.append(privComp)
1428 children.extend(privComp.getChildren(strings))
1429 return children
1430
1431
1432class FontDictCompiler(DictCompiler):
1433
1434 opcodes = buildOpcodeDict(topDictOperators)
1435
1436 def getChildren(self, strings):
1437 children = []
jvrf2cf9c52002-05-23 21:50:36 +00001438 if hasattr(self.dictObj, "Private"):
1439 privComp = self.dictObj.Private.getCompiler(strings, self)
1440 children.append(privComp)
1441 children.extend(privComp.getChildren(strings))
1442 return children
1443
1444
1445class PrivateDictCompiler(DictCompiler):
1446
1447 opcodes = buildOpcodeDict(privateDictOperators)
1448
jvr4e5af602002-05-24 09:58:04 +00001449 def setPos(self, pos, endPos):
1450 size = endPos - pos
jvrf2cf9c52002-05-23 21:50:36 +00001451 self.parent.rawDict["Private"] = size, pos
1452 self.pos = pos
1453
1454 def getChildren(self, strings):
1455 children = []
1456 if hasattr(self.dictObj, "Subrs"):
1457 children.append(self.dictObj.Subrs.getCompiler(strings, self))
1458 return children
1459
jvr4756b3a2002-05-16 18:17:32 +00001460
1461class BaseDict:
1462
jvr4e5af602002-05-24 09:58:04 +00001463 def __init__(self, strings=None, file=None, offset=None):
jvr4756b3a2002-05-16 18:17:32 +00001464 self.rawDict = {}
jvr767102e2002-05-17 07:06:32 +00001465 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001466 print("loading %s at %s" % (self.__class__.__name__, offset))
jvr4756b3a2002-05-16 18:17:32 +00001467 self.file = file
1468 self.offset = offset
1469 self.strings = strings
jvr155aa752002-05-17 19:58:49 +00001470 self.skipNames = []
jvr4756b3a2002-05-16 18:17:32 +00001471
1472 def decompile(self, data):
jvrf2cf9c52002-05-23 21:50:36 +00001473 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001474 print(" length %s is %s" % (self.__class__.__name__, len(data)))
jvrf2cf9c52002-05-23 21:50:36 +00001475 dec = self.decompilerClass(self.strings)
jvr4756b3a2002-05-16 18:17:32 +00001476 dec.decompile(data)
1477 self.rawDict = dec.getDict()
1478 self.postDecompile()
1479
1480 def postDecompile(self):
1481 pass
1482
jvrf2cf9c52002-05-23 21:50:36 +00001483 def getCompiler(self, strings, parent):
1484 return self.compilerClass(self, strings, parent)
1485
jvr4756b3a2002-05-16 18:17:32 +00001486 def __getattr__(self, name):
1487 value = self.rawDict.get(name)
1488 if value is None:
1489 value = self.defaults.get(name)
1490 if value is None:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -05001491 raise AttributeError(name)
jvr4756b3a2002-05-16 18:17:32 +00001492 conv = self.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001493 value = conv.read(self, value)
jvr4756b3a2002-05-16 18:17:32 +00001494 setattr(self, name, value)
1495 return value
1496
1497 def toXML(self, xmlWriter, progress):
1498 for name in self.order:
jvr155aa752002-05-17 19:58:49 +00001499 if name in self.skipNames:
1500 continue
jvr4756b3a2002-05-16 18:17:32 +00001501 value = getattr(self, name, None)
1502 if value is None:
1503 continue
jvr4e5af602002-05-24 09:58:04 +00001504 conv = self.converters[name]
jvr7ce0a132002-07-23 16:42:11 +00001505 conv.xmlWrite(xmlWriter, name, value, progress)
jvr4e5af602002-05-24 09:58:04 +00001506
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001507 def fromXML(self, name, attrs, content):
jvr4e5af602002-05-24 09:58:04 +00001508 conv = self.converters[name]
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001509 value = conv.xmlRead(name, attrs, content, self)
jvr4e5af602002-05-24 09:58:04 +00001510 setattr(self, name, value)
jvr4756b3a2002-05-16 18:17:32 +00001511
1512
1513class TopDict(BaseDict):
1514
1515 defaults = buildDefaults(topDictOperators)
1516 converters = buildConverters(topDictOperators)
1517 order = buildOrder(topDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001518 decompilerClass = TopDictDecompiler
1519 compilerClass = TopDictCompiler
Just7842e561999-12-16 21:34:53 +00001520
jvr4e5af602002-05-24 09:58:04 +00001521 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
jvr016ca762002-05-16 18:38:03 +00001522 BaseDict.__init__(self, strings, file, offset)
1523 self.GlobalSubrs = GlobalSubrs
1524
Just7842e561999-12-16 21:34:53 +00001525 def getGlyphOrder(self):
1526 return self.charset
1527
jvr4756b3a2002-05-16 18:17:32 +00001528 def postDecompile(self):
1529 offset = self.rawDict.get("CharStrings")
1530 if offset is None:
1531 return
1532 # get the number of glyphs beforehand.
1533 self.file.seek(offset)
jvra2ad5442002-05-17 18:36:07 +00001534 self.numGlyphs = readCard16(self.file)
Just7842e561999-12-16 21:34:53 +00001535
jvr016ca762002-05-16 18:38:03 +00001536 def toXML(self, xmlWriter, progress):
jvr155aa752002-05-17 19:58:49 +00001537 if hasattr(self, "CharStrings"):
jvr7ce0a132002-07-23 16:42:11 +00001538 self.decompileAllCharStrings(progress)
jvred101512003-08-22 19:53:32 +00001539 if hasattr(self, "ROS"):
1540 self.skipNames = ['Encoding']
jvr155aa752002-05-17 19:58:49 +00001541 if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
1542 # these values have default values, but I only want them to show up
1543 # in CID fonts.
1544 self.skipNames = ['CIDFontVersion', 'CIDFontRevision', 'CIDFontType',
1545 'CIDCount']
jvr016ca762002-05-16 18:38:03 +00001546 BaseDict.toXML(self, xmlWriter, progress)
jvr767102e2002-05-17 07:06:32 +00001547
jvr7ce0a132002-07-23 16:42:11 +00001548 def decompileAllCharStrings(self, progress):
jvr4e5af602002-05-24 09:58:04 +00001549 # XXX only when doing ttdump -i?
jvr7ce0a132002-07-23 16:42:11 +00001550 i = 0
jvra2ad5442002-05-17 18:36:07 +00001551 for charString in self.CharStrings.values():
jvred101512003-08-22 19:53:32 +00001552 try:
1553 charString.decompile()
1554 except:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001555 print("Error in charstring ", i)
jvred101512003-08-22 19:53:32 +00001556 import sys
1557 type, value = sys. exc_info()[0:2]
1558 raise type(value)
jvr7ce0a132002-07-23 16:42:11 +00001559 if not i % 30 and progress:
1560 progress.increment(0) # update
1561 i = i + 1
Just7842e561999-12-16 21:34:53 +00001562
1563
jvred101512003-08-22 19:53:32 +00001564class FontDict(BaseDict):
1565
1566 defaults = buildDefaults(topDictOperators)
1567 converters = buildConverters(topDictOperators)
1568 order = buildOrder(topDictOperators)
1569 decompilerClass = None
1570 compilerClass = FontDictCompiler
1571
1572 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
1573 BaseDict.__init__(self, strings, file, offset)
1574 self.GlobalSubrs = GlobalSubrs
1575
1576 def getGlyphOrder(self):
1577 return self.charset
1578
1579 def toXML(self, xmlWriter, progress):
1580 self.skipNames = ['Encoding']
1581 BaseDict.toXML(self, xmlWriter, progress)
1582
1583
1584
jvr4756b3a2002-05-16 18:17:32 +00001585class PrivateDict(BaseDict):
1586 defaults = buildDefaults(privateDictOperators)
1587 converters = buildConverters(privateDictOperators)
1588 order = buildOrder(privateDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001589 decompilerClass = PrivateDictDecompiler
1590 compilerClass = PrivateDictCompiler
Just7842e561999-12-16 21:34:53 +00001591
1592
jvre3275582002-05-14 12:22:03 +00001593class IndexedStrings:
1594
jvr767102e2002-05-17 07:06:32 +00001595 """SID -> string mapping."""
1596
1597 def __init__(self, file=None):
1598 if file is None:
jvre3275582002-05-14 12:22:03 +00001599 strings = []
jvr767102e2002-05-17 07:06:32 +00001600 else:
jvr4e5af602002-05-24 09:58:04 +00001601 strings = list(Index(file))
jvre3275582002-05-14 12:22:03 +00001602 self.strings = strings
1603
jvrf2cf9c52002-05-23 21:50:36 +00001604 def getCompiler(self):
1605 return IndexedStringsCompiler(self, None, None)
1606
1607 def __len__(self):
1608 return len(self.strings)
1609
jvre3275582002-05-14 12:22:03 +00001610 def __getitem__(self, SID):
1611 if SID < cffStandardStringCount:
1612 return cffStandardStrings[SID]
1613 else:
1614 return self.strings[SID - cffStandardStringCount]
1615
1616 def getSID(self, s):
1617 if not hasattr(self, "stringMapping"):
1618 self.buildStringMapping()
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -05001619 if s in cffStandardStringMapping:
jvre3275582002-05-14 12:22:03 +00001620 SID = cffStandardStringMapping[s]
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -05001621 elif s in self.stringMapping:
jvre3275582002-05-14 12:22:03 +00001622 SID = self.stringMapping[s]
1623 else:
1624 SID = len(self.strings) + cffStandardStringCount
1625 self.strings.append(s)
1626 self.stringMapping[s] = SID
1627 return SID
1628
1629 def getStrings(self):
1630 return self.strings
1631
1632 def buildStringMapping(self):
1633 self.stringMapping = {}
1634 for index in range(len(self.strings)):
1635 self.stringMapping[self.strings[index]] = index + cffStandardStringCount
1636
1637
Just7842e561999-12-16 21:34:53 +00001638# The 391 Standard Strings as used in the CFF format.
1639# from Adobe Technical None #5176, version 1.0, 18 March 1998
1640
1641cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
1642 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
1643 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
1644 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
1645 'semicolon', 'less', 'equal', 'greater', 'question', 'at', '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', 'bracketleft', 'backslash',
1648 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
1649 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
1650 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
1651 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
1652 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
1653 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
1654 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
1655 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
1656 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
1657 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
1658 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
1659 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
1660 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
1661 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
1662 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
1663 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
1664 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
1665 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
1666 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
1667 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
1668 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
1669 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
1670 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
1671 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
1672 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
1673 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
1674 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
1675 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
1676 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
1677 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
1678 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
1679 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
1680 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
1681 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
1682 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
1683 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
1684 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
1685 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
1686 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
1687 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
1688 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
1689 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
1690 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
1691 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
1692 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
1693 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
1694 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
1695 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
1696 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
1697 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
1698 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
1699 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
1700 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
1701 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
1702 '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
1703 'Semibold'
1704]
1705
1706cffStandardStringCount = 391
1707assert len(cffStandardStrings) == cffStandardStringCount
1708# build reverse mapping
1709cffStandardStringMapping = {}
1710for _i in range(cffStandardStringCount):
1711 cffStandardStringMapping[cffStandardStrings[_i]] = _i
jvrc60a44f2006-10-21 13:41:18 +00001712
1713cffISOAdobeStrings = [".notdef", "space", "exclam", "quotedbl", "numbersign",
1714"dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright",
1715"asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two",
1716"three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon",
1717"less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G",
1718"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
1719"X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum",
1720"underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
1721"k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
1722"braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent",
1723"sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle",
1724"quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl",
1725"endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet",
1726"quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis",
1727"perthousand", "questiondown", "grave", "acute", "circumflex", "tilde",
1728"macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
1729"ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE",
1730"ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls",
1731"onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus",
1732"Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn",
1733"threequarters", "twosuperior", "registered", "minus", "eth", "multiply",
1734"threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
1735"Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave",
1736"Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute",
1737"Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute",
1738"Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute",
1739"acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute",
1740"ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis",
1741"igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde",
1742"scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis",
1743"zcaron"]
1744
1745cffISOAdobeStringCount = 229
1746assert len(cffISOAdobeStrings) == cffISOAdobeStringCount
1747
1748cffIExpertStrings = [".notdef", "space", "exclamsmall", "Hungarumlautsmall",
1749"dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall",
1750"parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader",
1751"comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle",
1752"twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle",
1753"sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon",
1754"commasuperior", "threequartersemdash", "periodsuperior", "questionsmall",
1755"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1756"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1757"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1758"parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
1759"Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall",
1760"Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
1761"Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall",
1762"Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall",
1763"exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall",
1764"Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall",
1765"figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall",
1766"onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth",
1767"threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds",
1768"zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior",
1769"fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior",
1770"zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior",
1771"fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior",
1772"centinferior", "dollarinferior", "periodinferior", "commainferior",
1773"Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall",
1774"Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall",
1775"Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall",
1776"Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall",
1777"Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall",
1778"Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall",
1779"Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall",
1780"Ydieresissmall"]
1781
1782cffExpertStringCount = 166
1783assert len(cffIExpertStrings) == cffExpertStringCount
1784
1785cffExpertSubsetStrings = [".notdef", "space", "dollaroldstyle",
1786"dollarsuperior", "parenleftsuperior", "parenrightsuperior", "twodotenleader",
1787"onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle",
1788"oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle",
1789"sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon",
1790"semicolon", "commasuperior", "threequartersemdash", "periodsuperior",
1791"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1792"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1793"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1794"parenrightinferior", "hyphensuperior", "colonmonetary", "onefitted", "rupiah",
1795"centoldstyle", "figuredash", "hypheninferior", "onequarter", "onehalf",
1796"threequarters", "oneeighth", "threeeighths", "fiveeighths", "seveneighths",
1797"onethird", "twothirds", "zerosuperior", "onesuperior", "twosuperior",
1798"threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior",
1799"eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior",
1800"threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior",
1801"eightinferior", "nineinferior", "centinferior", "dollarinferior",
1802"periodinferior", "commainferior"]
1803
1804cffExpertSubsetStringCount = 87
1805assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount