blob: 8170a9896eced37f98689002dd522c31d80803b6 [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
Just528614e2000-01-16 22:14:02 +00009from fontTools.misc import psCharStrings
jvr4e5af602002-05-24 09:58:04 +000010from fontTools.misc.textTools import safeEval
Just7842e561999-12-16 21:34:53 +000011
Behdad Esfahbod2a9b8682013-11-27 05:52:33 -050012try:
13 basestring
14except NameError:
15 basestring = str
16
jvr767102e2002-05-17 07:06:32 +000017DEBUG = 0
18
19
Just7842e561999-12-16 21:34:53 +000020cffHeaderFormat = """
21 major: B
22 minor: B
23 hdrSize: B
24 offSize: B
25"""
26
27class CFFFontSet:
28
29 def __init__(self):
jvr4756b3a2002-05-16 18:17:32 +000030 pass
Just7842e561999-12-16 21:34:53 +000031
jvr4e5af602002-05-24 09:58:04 +000032 def decompile(self, file, otFont):
jvra2a75b32002-05-13 11:25:17 +000033 sstruct.unpack(cffHeaderFormat, file.read(4), self)
Just7842e561999-12-16 21:34:53 +000034 assert self.major == 1 and self.minor == 0, \
35 "unknown CFF format: %d.%d" % (self.major, self.minor)
Just7842e561999-12-16 21:34:53 +000036
jvrf2cf9c52002-05-23 21:50:36 +000037 file.seek(self.hdrSize)
jvr4e5af602002-05-24 09:58:04 +000038 self.fontNames = list(Index(file))
jvr4756b3a2002-05-16 18:17:32 +000039 self.topDictIndex = TopDictIndex(file)
jvr767102e2002-05-17 07:06:32 +000040 self.strings = IndexedStrings(file)
jvr4e5af602002-05-24 09:58:04 +000041 self.GlobalSubrs = GlobalSubrsIndex(file)
jvr4756b3a2002-05-16 18:17:32 +000042 self.topDictIndex.strings = self.strings
jvr016ca762002-05-16 18:38:03 +000043 self.topDictIndex.GlobalSubrs = self.GlobalSubrs
jvr4756b3a2002-05-16 18:17:32 +000044
45 def __len__(self):
46 return len(self.fontNames)
47
48 def keys(self):
jvrce522412003-08-25 07:37:25 +000049 return list(self.fontNames)
jvr4756b3a2002-05-16 18:17:32 +000050
jvr767102e2002-05-17 07:06:32 +000051 def values(self):
52 return self.topDictIndex
53
jvr4756b3a2002-05-16 18:17:32 +000054 def __getitem__(self, name):
55 try:
56 index = self.fontNames.index(name)
57 except ValueError:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -050058 raise KeyError(name)
jvr016ca762002-05-16 18:38:03 +000059 return self.topDictIndex[index]
Just7842e561999-12-16 21:34:53 +000060
jvr4e5af602002-05-24 09:58:04 +000061 def compile(self, file, otFont):
Just7842e561999-12-16 21:34:53 +000062 strings = IndexedStrings()
jvrf2cf9c52002-05-23 21:50:36 +000063 writer = CFFWriter()
64 writer.add(sstruct.pack(cffHeaderFormat, self))
65 fontNames = Index()
66 for name in self.fontNames:
67 fontNames.append(name)
68 writer.add(fontNames.getCompiler(strings, None))
69 topCompiler = self.topDictIndex.getCompiler(strings, None)
70 writer.add(topCompiler)
71 writer.add(strings.getCompiler())
72 writer.add(self.GlobalSubrs.getCompiler(strings, None))
73
jvr4e5af602002-05-24 09:58:04 +000074 for topDict in self.topDictIndex:
75 if not hasattr(topDict, "charset") or topDict.charset is None:
76 charset = otFont.getGlyphOrder()
77 topDict.charset = charset
78
jvrf2cf9c52002-05-23 21:50:36 +000079 for child in topCompiler.getChildren(strings):
80 writer.add(child)
81
jvrf2cf9c52002-05-23 21:50:36 +000082 writer.toFile(file)
Just7842e561999-12-16 21:34:53 +000083
84 def toXML(self, xmlWriter, progress=None):
85 xmlWriter.newline()
86 for fontName in self.fontNames:
87 xmlWriter.begintag("CFFFont", name=fontName)
88 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +000089 font = self[fontName]
Just7842e561999-12-16 21:34:53 +000090 font.toXML(xmlWriter, progress)
91 xmlWriter.endtag("CFFFont")
92 xmlWriter.newline()
93 xmlWriter.newline()
94 xmlWriter.begintag("GlobalSubrs")
95 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +000096 self.GlobalSubrs.toXML(xmlWriter, progress)
Just7842e561999-12-16 21:34:53 +000097 xmlWriter.endtag("GlobalSubrs")
98 xmlWriter.newline()
99 xmlWriter.newline()
100
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500101 def fromXML(self, name, attrs, content):
jvr4e5af602002-05-24 09:58:04 +0000102 if not hasattr(self, "GlobalSubrs"):
103 self.GlobalSubrs = GlobalSubrsIndex()
104 self.major = 1
105 self.minor = 0
106 self.hdrSize = 4
107 self.offSize = 4 # XXX ??
108 if name == "CFFFont":
109 if not hasattr(self, "fontNames"):
110 self.fontNames = []
111 self.topDictIndex = TopDictIndex()
112 fontName = attrs["name"]
113 topDict = TopDict(GlobalSubrs=self.GlobalSubrs)
114 topDict.charset = None # gets filled in later
115 self.fontNames.append(fontName)
116 self.topDictIndex.append(topDict)
117 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000118 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000119 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500120 name, attrs, content = element
121 topDict.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000122 elif name == "GlobalSubrs":
123 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000124 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000125 continue
126 name, attrs, content = element
jvr489d76a2003-08-24 19:56:16 +0000127 subr = psCharStrings.T2CharString()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500128 subr.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000129 self.GlobalSubrs.append(subr)
Just7842e561999-12-16 21:34:53 +0000130
131
jvrf2cf9c52002-05-23 21:50:36 +0000132class CFFWriter:
133
134 def __init__(self):
135 self.data = []
136
137 def add(self, table):
138 self.data.append(table)
139
140 def toFile(self, file):
141 lastPosList = None
142 count = 1
Behdad Esfahbodac1b4352013-11-27 04:15:34 -0500143 while True:
jvr4e5af602002-05-24 09:58:04 +0000144 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500145 print("CFFWriter.toFile() iteration:", count)
jvr4e5af602002-05-24 09:58:04 +0000146 count = count + 1
jvrf2cf9c52002-05-23 21:50:36 +0000147 pos = 0
148 posList = [pos]
149 for item in self.data:
jvrf2cf9c52002-05-23 21:50:36 +0000150 if hasattr(item, "getDataLength"):
jvr4e5af602002-05-24 09:58:04 +0000151 endPos = pos + item.getDataLength()
jvrf2cf9c52002-05-23 21:50:36 +0000152 else:
jvr4e5af602002-05-24 09:58:04 +0000153 endPos = pos + len(item)
154 if hasattr(item, "setPos"):
155 item.setPos(pos, endPos)
156 pos = endPos
jvrf2cf9c52002-05-23 21:50:36 +0000157 posList.append(pos)
158 if posList == lastPosList:
159 break
160 lastPosList = posList
jvr4e5af602002-05-24 09:58:04 +0000161 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500162 print("CFFWriter.toFile() writing to file.")
jvrf2cf9c52002-05-23 21:50:36 +0000163 begin = file.tell()
164 posList = [0]
165 for item in self.data:
166 if hasattr(item, "toFile"):
167 item.toFile(file)
168 else:
169 file.write(item)
170 posList.append(file.tell() - begin)
jvrf2cf9c52002-05-23 21:50:36 +0000171 assert posList == lastPosList
172
173
174def calcOffSize(largestOffset):
175 if largestOffset < 0x100:
176 offSize = 1
177 elif largestOffset < 0x10000:
178 offSize = 2
179 elif largestOffset < 0x1000000:
180 offSize = 3
181 else:
182 offSize = 4
183 return offSize
184
185
186class IndexCompiler:
187
188 def __init__(self, items, strings, parent):
189 self.items = self.getItems(items, strings)
190 self.parent = parent
191
192 def getItems(self, items, strings):
193 return items
194
195 def getOffsets(self):
196 pos = 1
197 offsets = [pos]
198 for item in self.items:
199 if hasattr(item, "getDataLength"):
200 pos = pos + item.getDataLength()
201 else:
202 pos = pos + len(item)
203 offsets.append(pos)
204 return offsets
205
206 def getDataLength(self):
207 lastOffset = self.getOffsets()[-1]
208 offSize = calcOffSize(lastOffset)
209 dataLength = (
210 2 + # count
211 1 + # offSize
212 (len(self.items) + 1) * offSize + # the offsets
213 lastOffset - 1 # size of object data
214 )
215 return dataLength
216
217 def toFile(self, file):
jvrf2cf9c52002-05-23 21:50:36 +0000218 offsets = self.getOffsets()
219 writeCard16(file, len(self.items))
220 offSize = calcOffSize(offsets[-1])
221 writeCard8(file, offSize)
222 offSize = -offSize
223 pack = struct.pack
224 for offset in offsets:
225 binOffset = pack(">l", offset)[offSize:]
226 assert len(binOffset) == -offSize
227 file.write(binOffset)
228 for item in self.items:
229 if hasattr(item, "toFile"):
230 item.toFile(file)
231 else:
232 file.write(item)
jvrf2cf9c52002-05-23 21:50:36 +0000233
234
235class IndexedStringsCompiler(IndexCompiler):
236
237 def getItems(self, items, strings):
238 return items.strings
239
240
241class TopDictIndexCompiler(IndexCompiler):
242
243 def getItems(self, items, strings):
244 out = []
245 for item in items:
246 out.append(item.getCompiler(strings, self))
247 return out
248
249 def getChildren(self, strings):
250 children = []
251 for topDict in self.items:
252 children.extend(topDict.getChildren(strings))
253 return children
254
255
jvred101512003-08-22 19:53:32 +0000256class FDArrayIndexCompiler(IndexCompiler):
257
258 def getItems(self, items, strings):
259 out = []
260 for item in items:
261 out.append(item.getCompiler(strings, self))
262 return out
263
264 def getChildren(self, strings):
265 children = []
266 for fontDict in self.items:
267 children.extend(fontDict.getChildren(strings))
268 return children
269
jvred101512003-08-22 19:53:32 +0000270 def toFile(self, file):
271 offsets = self.getOffsets()
272 writeCard16(file, len(self.items))
273 offSize = calcOffSize(offsets[-1])
274 writeCard8(file, offSize)
275 offSize = -offSize
276 pack = struct.pack
277 for offset in offsets:
278 binOffset = pack(">l", offset)[offSize:]
279 assert len(binOffset) == -offSize
280 file.write(binOffset)
281 for item in self.items:
282 if hasattr(item, "toFile"):
283 item.toFile(file)
284 else:
285 file.write(item)
286
287 def setPos(self, pos, endPos):
288 self.parent.rawDict["FDArray"] = pos
289
290
jvrf2cf9c52002-05-23 21:50:36 +0000291class GlobalSubrsCompiler(IndexCompiler):
292 def getItems(self, items, strings):
293 out = []
294 for cs in items:
295 cs.compile()
296 out.append(cs.bytecode)
297 return out
298
299class SubrsCompiler(GlobalSubrsCompiler):
jvr4e5af602002-05-24 09:58:04 +0000300 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000301 offset = pos - self.parent.pos
302 self.parent.rawDict["Subrs"] = offset
303
304class CharStringsCompiler(GlobalSubrsCompiler):
jvr4e5af602002-05-24 09:58:04 +0000305 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000306 self.parent.rawDict["CharStrings"] = pos
307
308
jvr4756b3a2002-05-16 18:17:32 +0000309class Index:
Just7842e561999-12-16 21:34:53 +0000310
jvr4756b3a2002-05-16 18:17:32 +0000311 """This class represents what the CFF spec calls an INDEX."""
Just7842e561999-12-16 21:34:53 +0000312
jvrf2cf9c52002-05-23 21:50:36 +0000313 compilerClass = IndexCompiler
314
jvr4e5af602002-05-24 09:58:04 +0000315 def __init__(self, file=None):
316 name = self.__class__.__name__
jvrf2cf9c52002-05-23 21:50:36 +0000317 if file is None:
318 self.items = []
319 return
jvr767102e2002-05-17 07:06:32 +0000320 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500321 print("loading %s at %s" % (name, file.tell()))
jvr4756b3a2002-05-16 18:17:32 +0000322 self.file = file
jvra2ad5442002-05-17 18:36:07 +0000323 count = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000324 self.count = count
325 self.items = [None] * count
326 if count == 0:
jvrf2cf9c52002-05-23 21:50:36 +0000327 self.items = []
jvr4756b3a2002-05-16 18:17:32 +0000328 return
jvra2ad5442002-05-17 18:36:07 +0000329 offSize = readCard8(file)
jvr767102e2002-05-17 07:06:32 +0000330 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500331 print(" index count: %s offSize: %s" % (count, offSize))
jvr767102e2002-05-17 07:06:32 +0000332 assert offSize <= 4, "offSize too large: %s" % offSize
jvr4756b3a2002-05-16 18:17:32 +0000333 self.offsets = offsets = []
334 pad = '\0' * (4 - offSize)
335 for index in range(count+1):
336 chunk = file.read(offSize)
337 chunk = pad + chunk
338 offset, = struct.unpack(">L", chunk)
339 offsets.append(int(offset))
340 self.offsetBase = file.tell() - 1
341 file.seek(self.offsetBase + offsets[-1]) # pretend we've read the whole lot
jvrf2cf9c52002-05-23 21:50:36 +0000342 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500343 print(" end of %s at %s" % (name, file.tell()))
Just7842e561999-12-16 21:34:53 +0000344
jvr4756b3a2002-05-16 18:17:32 +0000345 def __len__(self):
jvrf2cf9c52002-05-23 21:50:36 +0000346 return len(self.items)
jvra2a75b32002-05-13 11:25:17 +0000347
jvr4756b3a2002-05-16 18:17:32 +0000348 def __getitem__(self, index):
349 item = self.items[index]
350 if item is not None:
351 return item
352 offset = self.offsets[index] + self.offsetBase
353 size = self.offsets[index+1] - self.offsets[index]
354 file = self.file
355 file.seek(offset)
356 data = file.read(size)
357 assert len(data) == size
jvra2ad5442002-05-17 18:36:07 +0000358 item = self.produceItem(index, data, file, offset, size)
jvr4756b3a2002-05-16 18:17:32 +0000359 self.items[index] = item
360 return item
361
jvra2ad5442002-05-17 18:36:07 +0000362 def produceItem(self, index, data, file, offset, size):
jvr4756b3a2002-05-16 18:17:32 +0000363 return data
jvr4756b3a2002-05-16 18:17:32 +0000364
jvrf2cf9c52002-05-23 21:50:36 +0000365 def append(self, item):
366 self.items.append(item)
367
368 def getCompiler(self, strings, parent):
369 return self.compilerClass(self, strings, parent)
370
371
372class GlobalSubrsIndex(Index):
373
374 compilerClass = GlobalSubrsCompiler
375
jvr4e5af602002-05-24 09:58:04 +0000376 def __init__(self, file=None, globalSubrs=None, private=None, fdSelect=None, fdArray=None):
377 Index.__init__(self, file)
jvra2ad5442002-05-17 18:36:07 +0000378 self.globalSubrs = globalSubrs
379 self.private = private
jvred101512003-08-22 19:53:32 +0000380 if fdSelect:
381 self.fdSelect = fdSelect
382 if fdArray:
383 self.fdArray = fdArray
jvra2ad5442002-05-17 18:36:07 +0000384
385 def produceItem(self, index, data, file, offset, size):
386 if self.private is not None:
387 private = self.private
jvred101512003-08-22 19:53:32 +0000388 elif hasattr(self, 'fdArray') and self.fdArray is not None:
jvra2ad5442002-05-17 18:36:07 +0000389 private = self.fdArray[self.fdSelect[index]].Private
390 else:
391 private = None
jvr489d76a2003-08-24 19:56:16 +0000392 return psCharStrings.T2CharString(data, private=private, globalSubrs=self.globalSubrs)
jvr4756b3a2002-05-16 18:17:32 +0000393
394 def toXML(self, xmlWriter, progress):
jvred101512003-08-22 19:53:32 +0000395 xmlWriter.comment("The 'index' attribute is only for humans; it is ignored when parsed.")
jvr4e5af602002-05-24 09:58:04 +0000396 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +0000397 for i in range(len(self)):
jvrb58176e2002-05-24 11:55:37 +0000398 subr = self[i]
399 if subr.needsDecompilation():
400 xmlWriter.begintag("CharString", index=i, raw=1)
401 else:
402 xmlWriter.begintag("CharString", index=i)
jvr4756b3a2002-05-16 18:17:32 +0000403 xmlWriter.newline()
jvrb58176e2002-05-24 11:55:37 +0000404 subr.toXML(xmlWriter)
jvr4756b3a2002-05-16 18:17:32 +0000405 xmlWriter.endtag("CharString")
406 xmlWriter.newline()
jvra2ad5442002-05-17 18:36:07 +0000407
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500408 def fromXML(self, name, attrs, content):
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500409 if name != "CharString":
jvr4e5af602002-05-24 09:58:04 +0000410 return
jvr489d76a2003-08-24 19:56:16 +0000411 subr = psCharStrings.T2CharString()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500412 subr.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000413 self.append(subr)
414
jvra2ad5442002-05-17 18:36:07 +0000415 def getItemAndSelector(self, index):
jvred101512003-08-22 19:53:32 +0000416 sel = None
417 if hasattr(self, 'fdSelect'):
418 sel = self.fdSelect[index]
jvra2ad5442002-05-17 18:36:07 +0000419 return self[index], sel
jvrf2cf9c52002-05-23 21:50:36 +0000420
jvre2ca9b52002-09-09 14:18:39 +0000421
jvrf2cf9c52002-05-23 21:50:36 +0000422class SubrsIndex(GlobalSubrsIndex):
423 compilerClass = SubrsCompiler
424
jvr4756b3a2002-05-16 18:17:32 +0000425
jvr767102e2002-05-17 07:06:32 +0000426class TopDictIndex(Index):
jvra2ad5442002-05-17 18:36:07 +0000427
jvrf2cf9c52002-05-23 21:50:36 +0000428 compilerClass = TopDictIndexCompiler
429
jvra2ad5442002-05-17 18:36:07 +0000430 def produceItem(self, index, data, file, offset, size):
jvr767102e2002-05-17 07:06:32 +0000431 top = TopDict(self.strings, file, offset, self.GlobalSubrs)
432 top.decompile(data)
433 return top
jvra2ad5442002-05-17 18:36:07 +0000434
435 def toXML(self, xmlWriter, progress):
436 for i in range(len(self)):
437 xmlWriter.begintag("FontDict", index=i)
438 xmlWriter.newline()
439 self[i].toXML(xmlWriter, progress)
440 xmlWriter.endtag("FontDict")
441 xmlWriter.newline()
jvr767102e2002-05-17 07:06:32 +0000442
443
jvred101512003-08-22 19:53:32 +0000444class FDArrayIndex(TopDictIndex):
445
446 compilerClass = FDArrayIndexCompiler
447
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500448 def fromXML(self, name, attrs, content):
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500449 if name != "FontDict":
jvred101512003-08-22 19:53:32 +0000450 return
451 fontDict = FontDict()
452 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000453 if isinstance(element, basestring):
jvred101512003-08-22 19:53:32 +0000454 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500455 name, attrs, content = element
456 fontDict.fromXML(name, attrs, content)
jvred101512003-08-22 19:53:32 +0000457 self.append(fontDict)
458
459
460class FDSelect:
461 def __init__(self, file = None, numGlyphs = None, format=None):
462 if file:
463 # read data in from file
464 self.format = readCard8(file)
465 if self.format == 0:
466 from array import array
467 self.gidArray = array("B", file.read(numGlyphs)).tolist()
468 elif self.format == 3:
469 gidArray = [None] * numGlyphs
470 nRanges = readCard16(file)
471 prev = None
472 for i in range(nRanges):
473 first = readCard16(file)
474 if prev is not None:
475 for glyphID in range(prev, first):
476 gidArray[glyphID] = fd
477 prev = first
478 fd = readCard8(file)
479 if prev is not None:
480 first = readCard16(file)
481 for glyphID in range(prev, first):
482 gidArray[glyphID] = fd
483 self.gidArray = gidArray
484 else:
485 assert 0, "unsupported FDSelect format: %s" % format
486 else:
487 # reading from XML. Make empty gidArray,, and leave format as passed in.
488 # format == None will result in the smallest representation being used.
489 self.format = format
490 self.gidArray = []
491
492
493 def __len__(self):
494 return len(self.gidArray)
495
496 def __getitem__(self, index):
497 return self.gidArray[index]
498
499 def __setitem__(self, index, fdSelectValue):
500 self.gidArray[index] = fdSelectValue
501
502 def append(self, fdSelectValue):
503 self.gidArray.append(fdSelectValue)
504
505
jvr4756b3a2002-05-16 18:17:32 +0000506class CharStrings:
507
jvra2ad5442002-05-17 18:36:07 +0000508 def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray):
jvr4e5af602002-05-24 09:58:04 +0000509 if file is not None:
510 self.charStringsIndex = SubrsIndex(file, globalSubrs, private, fdSelect, fdArray)
511 self.charStrings = charStrings = {}
512 for i in range(len(charset)):
513 charStrings[charset[i]] = i
514 self.charStringsAreIndexed = 1
515 else:
516 self.charStrings = {}
517 self.charStringsAreIndexed = 0
518 self.globalSubrs = globalSubrs
519 self.private = private
jvred101512003-08-22 19:53:32 +0000520 if fdSelect != None:
521 self.fdSelect = fdSelect
522 if fdArray!= None:
523 self.fdArray = fdArray
jvr4756b3a2002-05-16 18:17:32 +0000524
525 def keys(self):
Behdad Esfahbodc2297cd2013-11-27 06:26:55 -0500526 return list(self.charStrings.keys())
jvr4756b3a2002-05-16 18:17:32 +0000527
jvr016ca762002-05-16 18:38:03 +0000528 def values(self):
jvr4e5af602002-05-24 09:58:04 +0000529 if self.charStringsAreIndexed:
530 return self.charStringsIndex
531 else:
Behdad Esfahbodc2297cd2013-11-27 06:26:55 -0500532 return list(self.charStrings.values())
jvr016ca762002-05-16 18:38:03 +0000533
jvr4756b3a2002-05-16 18:17:32 +0000534 def has_key(self, name):
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500535 return name in self.charStrings
jvr4756b3a2002-05-16 18:17:32 +0000536
jvr767102e2002-05-17 07:06:32 +0000537 def __len__(self):
jvr4e5af602002-05-24 09:58:04 +0000538 return len(self.charStrings)
jvr767102e2002-05-17 07:06:32 +0000539
jvr4756b3a2002-05-16 18:17:32 +0000540 def __getitem__(self, name):
jvr4e5af602002-05-24 09:58:04 +0000541 charString = self.charStrings[name]
542 if self.charStringsAreIndexed:
543 charString = self.charStringsIndex[charString]
544 return charString
545
546 def __setitem__(self, name, charString):
547 if self.charStringsAreIndexed:
548 index = self.charStrings[name]
549 self.charStringsIndex[index] = charString
550 else:
551 self.charStrings[name] = charString
jvr4756b3a2002-05-16 18:17:32 +0000552
jvra2ad5442002-05-17 18:36:07 +0000553 def getItemAndSelector(self, name):
jvr4e5af602002-05-24 09:58:04 +0000554 if self.charStringsAreIndexed:
555 index = self.charStrings[name]
556 return self.charStringsIndex.getItemAndSelector(index)
557 else:
jvred101512003-08-22 19:53:32 +0000558 if hasattr(self, 'fdSelect'):
jvr91bca422012-10-18 12:49:22 +0000559 sel = self.fdSelect[index] # index is not defined at this point. Read R. ?
jvred101512003-08-22 19:53:32 +0000560 else:
561 raise KeyError("fdSelect array not yet defined.")
562 return self.charStrings[name], sel
jvra2ad5442002-05-17 18:36:07 +0000563
jvr4756b3a2002-05-16 18:17:32 +0000564 def toXML(self, xmlWriter, progress):
Behdad Esfahbodac1b4352013-11-27 04:15:34 -0500565 names = sorted(self.keys())
jvr7ce0a132002-07-23 16:42:11 +0000566 i = 0
567 step = 10
568 numGlyphs = len(names)
jvr4756b3a2002-05-16 18:17:32 +0000569 for name in names:
jvred101512003-08-22 19:53:32 +0000570 charStr, fdSelectIndex = self.getItemAndSelector(name)
jvrb58176e2002-05-24 11:55:37 +0000571 if charStr.needsDecompilation():
572 raw = [("raw", 1)]
573 else:
574 raw = []
jvred101512003-08-22 19:53:32 +0000575 if fdSelectIndex is None:
jvrb58176e2002-05-24 11:55:37 +0000576 xmlWriter.begintag("CharString", [('name', name)] + raw)
jvra2ad5442002-05-17 18:36:07 +0000577 else:
578 xmlWriter.begintag("CharString",
jvred101512003-08-22 19:53:32 +0000579 [('name', name), ('fdSelectIndex', fdSelectIndex)] + raw)
jvr4756b3a2002-05-16 18:17:32 +0000580 xmlWriter.newline()
jvrb58176e2002-05-24 11:55:37 +0000581 charStr.toXML(xmlWriter)
jvr4756b3a2002-05-16 18:17:32 +0000582 xmlWriter.endtag("CharString")
583 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000584 if not i % step and progress is not None:
585 progress.setLabel("Dumping 'CFF ' table... (%s)" % name)
586 progress.increment(step / float(numGlyphs))
587 i = i + 1
jvr4e5af602002-05-24 09:58:04 +0000588
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500589 def fromXML(self, name, attrs, content):
jvr4e5af602002-05-24 09:58:04 +0000590 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000591 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000592 continue
593 name, attrs, content = element
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500594 if name != "CharString":
jvr4e5af602002-05-24 09:58:04 +0000595 continue
jvred101512003-08-22 19:53:32 +0000596 fdID = -1
597 if hasattr(self, "fdArray"):
598 fdID = safeEval(attrs["fdSelectIndex"])
599 private = self.fdArray[fdID].Private
600 else:
601 private = self.private
602
jvr4e5af602002-05-24 09:58:04 +0000603 glyphName = attrs["name"]
jvr489d76a2003-08-24 19:56:16 +0000604 charString = psCharStrings.T2CharString(
605 private=private,
606 globalSubrs=self.globalSubrs)
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500607 charString.fromXML(name, attrs, content)
jvred101512003-08-22 19:53:32 +0000608 if fdID >= 0:
609 charString.fdSelectIndex = fdID
jvr4e5af602002-05-24 09:58:04 +0000610 self[glyphName] = charString
jvr4756b3a2002-05-16 18:17:32 +0000611
612
jvra2ad5442002-05-17 18:36:07 +0000613def readCard8(file):
614 return ord(file.read(1))
615
616def readCard16(file):
617 value, = struct.unpack(">H", file.read(2))
618 return value
619
jvrf2cf9c52002-05-23 21:50:36 +0000620def writeCard8(file, value):
621 file.write(chr(value))
622
623def writeCard16(file, value):
624 file.write(struct.pack(">H", value))
625
626def packCard8(value):
627 return chr(value)
628
629def packCard16(value):
630 return struct.pack(">H", value)
631
jvr4756b3a2002-05-16 18:17:32 +0000632def buildOperatorDict(table):
633 d = {}
634 for op, name, arg, default, conv in table:
635 d[op] = (name, arg)
636 return d
637
jvrf2cf9c52002-05-23 21:50:36 +0000638def buildOpcodeDict(table):
639 d = {}
640 for op, name, arg, default, conv in table:
jvr2a9bcde2008-03-07 19:56:17 +0000641 if isinstance(op, tuple):
jvrf2cf9c52002-05-23 21:50:36 +0000642 op = chr(op[0]) + chr(op[1])
643 else:
644 op = chr(op)
645 d[name] = (op, arg)
646 return d
647
jvr4756b3a2002-05-16 18:17:32 +0000648def buildOrder(table):
649 l = []
650 for op, name, arg, default, conv in table:
651 l.append(name)
652 return l
653
654def buildDefaults(table):
655 d = {}
656 for op, name, arg, default, conv in table:
657 if default is not None:
658 d[name] = default
659 return d
660
661def buildConverters(table):
662 d = {}
663 for op, name, arg, default, conv in table:
664 d[name] = conv
665 return d
666
667
jvr4e5af602002-05-24 09:58:04 +0000668class SimpleConverter:
jvr7ce02ea2002-05-17 20:04:05 +0000669 def read(self, parent, value):
670 return value
jvrf2cf9c52002-05-23 21:50:36 +0000671 def write(self, parent, value):
672 return value
jvr7ce0a132002-07-23 16:42:11 +0000673 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000674 xmlWriter.simpletag(name, value=value)
675 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500676 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000677 return attrs["value"]
678
jvre2ca9b52002-09-09 14:18:39 +0000679class Latin1Converter(SimpleConverter):
Behdad Esfahbod8c5c9662013-10-28 13:20:00 +0100680 def xmlWrite(self, xmlWriter, name, value, progress):
681 # Store as UTF-8
682 value = unicode(value, "latin-1").encode("utf-8")
683 xmlWriter.simpletag(name, value=value)
684 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500685 def xmlRead(self, name, attrs, content, parent):
jvre2ca9b52002-09-09 14:18:39 +0000686 s = unicode(attrs["value"], "utf-8")
687 return s.encode("latin-1")
688
689
jvr4e5af602002-05-24 09:58:04 +0000690def parseNum(s):
691 try:
692 value = int(s)
693 except:
694 value = float(s)
695 return value
696
697class NumberConverter(SimpleConverter):
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500698 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000699 return parseNum(attrs["value"])
700
701class ArrayConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000702 def xmlWrite(self, xmlWriter, name, value, progress):
Behdad Esfahbode5ca7962013-11-27 04:38:16 -0500703 value = " ".join(map(str, value))
704 xmlWriter.simpletag(name, value=value)
jvr4e5af602002-05-24 09:58:04 +0000705 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500706 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000707 values = attrs["value"].split()
Behdad Esfahbode5ca7962013-11-27 04:38:16 -0500708 return [parseNum(value) for value in values]
jvr4e5af602002-05-24 09:58:04 +0000709
710class TableConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000711 def xmlWrite(self, xmlWriter, name, value, progress):
jvra2ad5442002-05-17 18:36:07 +0000712 xmlWriter.begintag(name)
713 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000714 value.toXML(xmlWriter, progress)
jvra2ad5442002-05-17 18:36:07 +0000715 xmlWriter.endtag(name)
716 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500717 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000718 ob = self.getClass()()
719 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000720 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000721 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500722 name, attrs, content = element
723 ob.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000724 return ob
jvra2ad5442002-05-17 18:36:07 +0000725
jvr4e5af602002-05-24 09:58:04 +0000726class PrivateDictConverter(TableConverter):
727 def getClass(self):
728 return PrivateDict
jvr4756b3a2002-05-16 18:17:32 +0000729 def read(self, parent, value):
730 size, offset = value
731 file = parent.file
jvr4e5af602002-05-24 09:58:04 +0000732 priv = PrivateDict(parent.strings, file, offset)
jvr4756b3a2002-05-16 18:17:32 +0000733 file.seek(offset)
734 data = file.read(size)
735 len(data) == size
jvr4e5af602002-05-24 09:58:04 +0000736 priv.decompile(data)
737 return priv
jvrf2cf9c52002-05-23 21:50:36 +0000738 def write(self, parent, value):
739 return (0, 0) # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000740
jvr4e5af602002-05-24 09:58:04 +0000741class SubrsConverter(TableConverter):
742 def getClass(self):
743 return SubrsIndex
jvr4756b3a2002-05-16 18:17:32 +0000744 def read(self, parent, value):
745 file = parent.file
746 file.seek(parent.offset + value) # Offset(self)
jvr4e5af602002-05-24 09:58:04 +0000747 return SubrsIndex(file)
jvrf2cf9c52002-05-23 21:50:36 +0000748 def write(self, parent, value):
749 return 0 # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000750
jvr4e5af602002-05-24 09:58:04 +0000751class CharStringsConverter(TableConverter):
jvr4756b3a2002-05-16 18:17:32 +0000752 def read(self, parent, value):
753 file = parent.file
jvr767102e2002-05-17 07:06:32 +0000754 charset = parent.charset
jvra2ad5442002-05-17 18:36:07 +0000755 globalSubrs = parent.GlobalSubrs
756 if hasattr(parent, "ROS"):
757 fdSelect, fdArray = parent.FDSelect, parent.FDArray
758 private = None
759 else:
760 fdSelect, fdArray = None, None
761 private = parent.Private
jvr4756b3a2002-05-16 18:17:32 +0000762 file.seek(value) # Offset(0)
jvra2ad5442002-05-17 18:36:07 +0000763 return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
jvrf2cf9c52002-05-23 21:50:36 +0000764 def write(self, parent, value):
765 return 0 # dummy value
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500766 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +0000767 if hasattr(parent, "ROS"):
768 # if it is a CID-keyed font, then the private Dict is extracted from the parent.FDArray
769 private, fdSelect, fdArray = None, parent.FDSelect, parent.FDArray
770 else:
771 # if it is a name-keyed font, then the private dict is in the top dict, and there is no fdArray.
772 private, fdSelect, fdArray = parent.Private, None, None
773 charStrings = CharStrings(None, None, parent.GlobalSubrs, private, fdSelect, fdArray)
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500774 charStrings.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000775 return charStrings
jvr4756b3a2002-05-16 18:17:32 +0000776
777class CharsetConverter:
778 def read(self, parent, value):
779 isCID = hasattr(parent, "ROS")
780 if value > 2:
781 numGlyphs = parent.numGlyphs
782 file = parent.file
783 file.seek(value)
jvrf2cf9c52002-05-23 21:50:36 +0000784 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500785 print("loading charset at %s" % value)
jvra2ad5442002-05-17 18:36:07 +0000786 format = readCard8(file)
Just7842e561999-12-16 21:34:53 +0000787 if format == 0:
jvrc60a44f2006-10-21 13:41:18 +0000788 charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
jvr4756b3a2002-05-16 18:17:32 +0000789 elif format == 1 or format == 2:
790 charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
Just7842e561999-12-16 21:34:53 +0000791 else:
jvr1890b952002-05-15 07:41:30 +0000792 raise NotImplementedError
jvr4756b3a2002-05-16 18:17:32 +0000793 assert len(charset) == numGlyphs
jvrf2cf9c52002-05-23 21:50:36 +0000794 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500795 print(" charset end at %s" % file.tell())
jvrc60a44f2006-10-21 13:41:18 +0000796 else: # offset == 0 -> no charset data.
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500797 if isCID or "CharStrings" not in parent.rawDict:
jvrc60a44f2006-10-21 13:41:18 +0000798 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 +0000799 charset = None
800 elif value == 0:
jvrc60a44f2006-10-21 13:41:18 +0000801 charset = cffISOAdobeStrings
jvr4756b3a2002-05-16 18:17:32 +0000802 elif value == 1:
jvrc60a44f2006-10-21 13:41:18 +0000803 charset = cffIExpertStrings
jvr4756b3a2002-05-16 18:17:32 +0000804 elif value == 2:
jvrc60a44f2006-10-21 13:41:18 +0000805 charset = cffExpertSubsetStrings
jvr4756b3a2002-05-16 18:17:32 +0000806 return charset
jvrc60a44f2006-10-21 13:41:18 +0000807
jvrf2cf9c52002-05-23 21:50:36 +0000808 def write(self, parent, value):
809 return 0 # dummy value
jvr7ce0a132002-07-23 16:42:11 +0000810 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000811 # XXX only write charset when not in OT/TTX context, where we
812 # dump charset as a separate "GlyphOrder" table.
813 ##xmlWriter.simpletag("charset")
814 xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
jvr4756b3a2002-05-16 18:17:32 +0000815 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500816 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000817 if 0:
818 return safeEval(attrs["value"])
jvr4756b3a2002-05-16 18:17:32 +0000819
820
jvrf2cf9c52002-05-23 21:50:36 +0000821class CharsetCompiler:
822
823 def __init__(self, strings, charset, parent):
824 assert charset[0] == '.notdef'
jvred101512003-08-22 19:53:32 +0000825 isCID = hasattr(parent.dictObj, "ROS")
826 data0 = packCharset0(charset, isCID, strings)
827 data = packCharset(charset, isCID, strings)
jvr6004baf2002-05-24 10:35:13 +0000828 if len(data) < len(data0):
829 self.data = data
830 else:
831 self.data = data0
jvrf2cf9c52002-05-23 21:50:36 +0000832 self.parent = parent
833
jvr4e5af602002-05-24 09:58:04 +0000834 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000835 self.parent.rawDict["charset"] = pos
836
837 def getDataLength(self):
838 return len(self.data)
839
840 def toFile(self, file):
841 file.write(self.data)
842
843
jvred101512003-08-22 19:53:32 +0000844def getCIDfromName(name, strings):
845 return int(name[3:])
846
847def getSIDfromName(name, strings):
848 return strings.getSID(name)
849
850def packCharset0(charset, isCID, strings):
jvr6004baf2002-05-24 10:35:13 +0000851 format = 0
852 data = [packCard8(format)]
jvred101512003-08-22 19:53:32 +0000853 if isCID:
854 getNameID = getCIDfromName
855 else:
856 getNameID = getSIDfromName
857
jvr6004baf2002-05-24 10:35:13 +0000858 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000859 data.append(packCard16(getNameID(name,strings)))
jvr6004baf2002-05-24 10:35:13 +0000860 return "".join(data)
861
jvred101512003-08-22 19:53:32 +0000862
863def packCharset(charset, isCID, strings):
jvr6004baf2002-05-24 10:35:13 +0000864 format = 1
jvr6004baf2002-05-24 10:35:13 +0000865 ranges = []
866 first = None
867 end = 0
jvred101512003-08-22 19:53:32 +0000868 if isCID:
869 getNameID = getCIDfromName
870 else:
871 getNameID = getSIDfromName
872
jvr6004baf2002-05-24 10:35:13 +0000873 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000874 SID = getNameID(name, strings)
jvr6004baf2002-05-24 10:35:13 +0000875 if first is None:
876 first = SID
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500877 elif end + 1 != SID:
jvr6004baf2002-05-24 10:35:13 +0000878 nLeft = end - first
879 if nLeft > 255:
880 format = 2
881 ranges.append((first, nLeft))
882 first = SID
883 end = SID
884 nLeft = end - first
885 if nLeft > 255:
886 format = 2
887 ranges.append((first, nLeft))
888
jvr74cd1ef2002-05-24 10:38:04 +0000889 data = [packCard8(format)]
jvr6004baf2002-05-24 10:35:13 +0000890 if format == 1:
891 nLeftFunc = packCard8
892 else:
893 nLeftFunc = packCard16
894 for first, nLeft in ranges:
895 data.append(packCard16(first) + nLeftFunc(nLeft))
jvrb58176e2002-05-24 11:55:37 +0000896 return "".join(data)
jvr6004baf2002-05-24 10:35:13 +0000897
jvrc60a44f2006-10-21 13:41:18 +0000898def parseCharset0(numGlyphs, file, strings, isCID):
jvrf2cf9c52002-05-23 21:50:36 +0000899 charset = [".notdef"]
jvrc60a44f2006-10-21 13:41:18 +0000900 if isCID:
901 for i in range(numGlyphs - 1):
902 CID = readCard16(file)
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500903 charset.append("cid" + str(CID).zfill(5))
jvrc60a44f2006-10-21 13:41:18 +0000904 else:
905 for i in range(numGlyphs - 1):
906 SID = readCard16(file)
907 charset.append(strings[SID])
jvrf2cf9c52002-05-23 21:50:36 +0000908 return charset
909
jvr4756b3a2002-05-16 18:17:32 +0000910def parseCharset(numGlyphs, file, strings, isCID, format):
911 charset = ['.notdef']
912 count = 1
913 if format == 1:
jvra2ad5442002-05-17 18:36:07 +0000914 nLeftFunc = readCard8
jvr4756b3a2002-05-16 18:17:32 +0000915 else:
jvra2ad5442002-05-17 18:36:07 +0000916 nLeftFunc = readCard16
jvr4756b3a2002-05-16 18:17:32 +0000917 while count < numGlyphs:
jvra2ad5442002-05-17 18:36:07 +0000918 first = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000919 nLeft = nLeftFunc(file)
920 if isCID:
921 for CID in range(first, first+nLeft+1):
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500922 charset.append("cid" + str(CID).zfill(5))
jvr1890b952002-05-15 07:41:30 +0000923 else:
jvr4756b3a2002-05-16 18:17:32 +0000924 for SID in range(first, first+nLeft+1):
925 charset.append(strings[SID])
926 count = count + nLeft + 1
927 return charset
928
929
jvrb9702ba2003-01-03 20:56:01 +0000930class EncodingCompiler:
931
932 def __init__(self, strings, encoding, parent):
jvr2a9bcde2008-03-07 19:56:17 +0000933 assert not isinstance(encoding, basestring)
jvrb9702ba2003-01-03 20:56:01 +0000934 data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
935 data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
936 if len(data0) < len(data1):
937 self.data = data0
938 else:
939 self.data = data1
940 self.parent = parent
941
942 def setPos(self, pos, endPos):
943 self.parent.rawDict["Encoding"] = pos
944
945 def getDataLength(self):
946 return len(self.data)
947
948 def toFile(self, file):
949 file.write(self.data)
950
951
952class EncodingConverter(SimpleConverter):
953
954 def read(self, parent, value):
955 if value == 0:
956 return "StandardEncoding"
957 elif value == 1:
958 return "ExpertEncoding"
959 else:
960 assert value > 1
961 file = parent.file
962 file.seek(value)
963 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500964 print("loading Encoding at %s" % value)
jvrb9702ba2003-01-03 20:56:01 +0000965 format = readCard8(file)
966 haveSupplement = format & 0x80
967 if haveSupplement:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -0500968 raise NotImplementedError("Encoding supplements are not yet supported")
jvrb9702ba2003-01-03 20:56:01 +0000969 format = format & 0x7f
970 if format == 0:
971 encoding = parseEncoding0(parent.charset, file, haveSupplement,
972 parent.strings)
973 elif format == 1:
974 encoding = parseEncoding1(parent.charset, file, haveSupplement,
975 parent.strings)
976 return encoding
977
978 def write(self, parent, value):
979 if value == "StandardEncoding":
980 return 0
981 elif value == "ExpertEncoding":
982 return 1
983 return 0 # dummy value
984
985 def xmlWrite(self, xmlWriter, name, value, progress):
986 if value in ("StandardEncoding", "ExpertEncoding"):
987 xmlWriter.simpletag(name, name=value)
988 xmlWriter.newline()
989 return
990 xmlWriter.begintag(name)
991 xmlWriter.newline()
992 for code in range(len(value)):
993 glyphName = value[code]
994 if glyphName != ".notdef":
995 xmlWriter.simpletag("map", code=hex(code), name=glyphName)
996 xmlWriter.newline()
997 xmlWriter.endtag(name)
998 xmlWriter.newline()
999
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001000 def xmlRead(self, name, attrs, content, parent):
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -05001001 if "name" in attrs:
jvrb9702ba2003-01-03 20:56:01 +00001002 return attrs["name"]
1003 encoding = [".notdef"] * 256
1004 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +00001005 if isinstance(element, basestring):
jvrb9702ba2003-01-03 20:56:01 +00001006 continue
1007 name, attrs, content = element
1008 code = safeEval(attrs["code"])
1009 glyphName = attrs["name"]
1010 encoding[code] = glyphName
1011 return encoding
1012
1013
1014def parseEncoding0(charset, file, haveSupplement, strings):
1015 nCodes = readCard8(file)
1016 encoding = [".notdef"] * 256
1017 for glyphID in range(1, nCodes + 1):
1018 code = readCard8(file)
1019 if code != 0:
1020 encoding[code] = charset[glyphID]
1021 return encoding
1022
1023def parseEncoding1(charset, file, haveSupplement, strings):
1024 nRanges = readCard8(file)
1025 encoding = [".notdef"] * 256
1026 glyphID = 1
1027 for i in range(nRanges):
1028 code = readCard8(file)
1029 nLeft = readCard8(file)
1030 for glyphID in range(glyphID, glyphID + nLeft + 1):
1031 encoding[code] = charset[glyphID]
1032 code = code + 1
1033 glyphID = glyphID + 1
1034 return encoding
1035
1036def packEncoding0(charset, encoding, strings):
1037 format = 0
1038 m = {}
1039 for code in range(len(encoding)):
1040 name = encoding[code]
1041 if name != ".notdef":
1042 m[name] = code
1043 codes = []
1044 for name in charset[1:]:
1045 code = m.get(name)
1046 codes.append(code)
1047
1048 while codes and codes[-1] is None:
1049 codes.pop()
1050
1051 data = [packCard8(format), packCard8(len(codes))]
1052 for code in codes:
1053 if code is None:
1054 code = 0
1055 data.append(packCard8(code))
1056 return "".join(data)
1057
1058def packEncoding1(charset, encoding, strings):
1059 format = 1
1060 m = {}
1061 for code in range(len(encoding)):
1062 name = encoding[code]
1063 if name != ".notdef":
1064 m[name] = code
1065 ranges = []
1066 first = None
1067 end = 0
1068 for name in charset[1:]:
1069 code = m.get(name, -1)
1070 if first is None:
1071 first = code
Behdad Esfahbod180ace62013-11-27 02:40:30 -05001072 elif end + 1 != code:
jvrb9702ba2003-01-03 20:56:01 +00001073 nLeft = end - first
1074 ranges.append((first, nLeft))
1075 first = code
1076 end = code
1077 nLeft = end - first
1078 ranges.append((first, nLeft))
1079
1080 # remove unencoded glyphs at the end.
1081 while ranges and ranges[-1][0] == -1:
1082 ranges.pop()
1083
1084 data = [packCard8(format), packCard8(len(ranges))]
1085 for first, nLeft in ranges:
1086 if first == -1: # unencoded
1087 first = 0
1088 data.append(packCard8(first) + packCard8(nLeft))
1089 return "".join(data)
1090
1091
jvr4e5af602002-05-24 09:58:04 +00001092class FDArrayConverter(TableConverter):
jvred101512003-08-22 19:53:32 +00001093
jvra2ad5442002-05-17 18:36:07 +00001094 def read(self, parent, value):
1095 file = parent.file
1096 file.seek(value)
jvred101512003-08-22 19:53:32 +00001097 fdArray = FDArrayIndex(file)
jvra2ad5442002-05-17 18:36:07 +00001098 fdArray.strings = parent.strings
1099 fdArray.GlobalSubrs = parent.GlobalSubrs
1100 return fdArray
1101
jvred101512003-08-22 19:53:32 +00001102 def write(self, parent, value):
1103 return 0 # dummy value
1104
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001105 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001106 fdArray = FDArrayIndex()
1107 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +00001108 if isinstance(element, basestring):
jvred101512003-08-22 19:53:32 +00001109 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001110 name, attrs, content = element
1111 fdArray.fromXML(name, attrs, content)
jvred101512003-08-22 19:53:32 +00001112 return fdArray
1113
jvra2ad5442002-05-17 18:36:07 +00001114
1115class FDSelectConverter:
jvred101512003-08-22 19:53:32 +00001116
jvra2ad5442002-05-17 18:36:07 +00001117 def read(self, parent, value):
1118 file = parent.file
1119 file.seek(value)
jvred101512003-08-22 19:53:32 +00001120 fdSelect = FDSelect(file, parent.numGlyphs)
1121 return fdSelect
1122
1123 def write(self, parent, value):
1124 return 0 # dummy value
1125
1126 # The FDSelect glyph data is written out to XML in the charstring keys,
1127 # so we write out only the format selector
jvr7ce0a132002-07-23 16:42:11 +00001128 def xmlWrite(self, xmlWriter, name, value, progress):
jvred101512003-08-22 19:53:32 +00001129 xmlWriter.simpletag(name, [('format', value.format)])
1130 xmlWriter.newline()
1131
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001132 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001133 format = safeEval(attrs["format"])
1134 file = None
1135 numGlyphs = None
1136 fdSelect = FDSelect(file, numGlyphs, format)
1137 return fdSelect
1138
1139
1140def packFDSelect0(fdSelectArray):
1141 format = 0
1142 data = [packCard8(format)]
1143 for index in fdSelectArray:
1144 data.append(packCard8(index))
1145 return "".join(data)
1146
1147
1148def packFDSelect3(fdSelectArray):
1149 format = 3
1150 fdRanges = []
1151 first = None
1152 end = 0
1153 lenArray = len(fdSelectArray)
1154 lastFDIndex = -1
1155 for i in range(lenArray):
1156 fdIndex = fdSelectArray[i]
1157 if lastFDIndex != fdIndex:
1158 fdRanges.append([i, fdIndex])
1159 lastFDIndex = fdIndex
1160 sentinelGID = i + 1
1161
1162 data = [packCard8(format)]
1163 data.append(packCard16( len(fdRanges) ))
1164 for fdRange in fdRanges:
1165 data.append(packCard16(fdRange[0]))
1166 data.append(packCard8(fdRange[1]))
1167 data.append(packCard16(sentinelGID))
1168 return "".join(data)
1169
1170
1171class FDSelectCompiler:
1172
1173 def __init__(self, fdSelect, parent):
1174 format = fdSelect.format
1175 fdSelectArray = fdSelect.gidArray
1176 if format == 0:
1177 self.data = packFDSelect0(fdSelectArray)
1178 elif format == 3:
1179 self.data = packFDSelect3(fdSelectArray)
1180 else:
1181 # choose smaller of the two formats
1182 data0 = packFDSelect0(fdSelectArray)
1183 data3 = packFDSelect3(fdSelectArray)
1184 if len(data0) < len(data3):
1185 self.data = data0
1186 fdSelect.format = 0
1187 else:
1188 self.data = data3
1189 fdSelect.format = 3
1190
1191 self.parent = parent
1192
1193 def setPos(self, pos, endPos):
1194 self.parent.rawDict["FDSelect"] = pos
1195
1196 def getDataLength(self):
1197 return len(self.data)
1198
1199 def toFile(self, file):
1200 file.write(self.data)
jvra2ad5442002-05-17 18:36:07 +00001201
1202
jvr4e5af602002-05-24 09:58:04 +00001203class ROSConverter(SimpleConverter):
jvred101512003-08-22 19:53:32 +00001204
jvr7ce0a132002-07-23 16:42:11 +00001205 def xmlWrite(self, xmlWriter, name, value, progress):
jvr155aa752002-05-17 19:58:49 +00001206 registry, order, supplement = value
jvrf2cf9c52002-05-23 21:50:36 +00001207 xmlWriter.simpletag(name, [('Registry', registry), ('Order', order),
1208 ('Supplement', supplement)])
jvr4afb2572002-05-18 20:07:01 +00001209 xmlWriter.newline()
jvr155aa752002-05-17 19:58:49 +00001210
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001211 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001212 return (attrs['Registry'], attrs['Order'], safeEval(attrs['Supplement']))
1213
1214
jvr155aa752002-05-17 19:58:49 +00001215
jvr4756b3a2002-05-16 18:17:32 +00001216topDictOperators = [
1217# opcode name argument type default converter
jvr155aa752002-05-17 19:58:49 +00001218 ((12, 30), 'ROS', ('SID','SID','number'), None, ROSConverter()),
jvrf2cf9c52002-05-23 21:50:36 +00001219 ((12, 20), 'SyntheticBase', 'number', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001220 (0, 'version', 'SID', None, None),
jvre2ca9b52002-09-09 14:18:39 +00001221 (1, 'Notice', 'SID', None, Latin1Converter()),
1222 ((12, 0), 'Copyright', 'SID', None, Latin1Converter()),
jvr4756b3a2002-05-16 18:17:32 +00001223 (2, 'FullName', 'SID', None, None),
jvr155aa752002-05-17 19:58:49 +00001224 ((12, 38), 'FontName', 'SID', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001225 (3, 'FamilyName', 'SID', None, None),
1226 (4, 'Weight', 'SID', None, None),
1227 ((12, 1), 'isFixedPitch', 'number', 0, None),
1228 ((12, 2), 'ItalicAngle', 'number', 0, None),
1229 ((12, 3), 'UnderlinePosition', 'number', None, None),
1230 ((12, 4), 'UnderlineThickness', 'number', 50, None),
1231 ((12, 5), 'PaintType', 'number', 0, None),
1232 ((12, 6), 'CharstringType', 'number', 2, None),
1233 ((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0], None),
1234 (13, 'UniqueID', 'number', None, None),
1235 (5, 'FontBBox', 'array', [0,0,0,0], None),
1236 ((12, 8), 'StrokeWidth', 'number', 0, None),
1237 (14, 'XUID', 'array', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001238 ((12, 21), 'PostScript', 'SID', None, None),
1239 ((12, 22), 'BaseFontName', 'SID', None, None),
1240 ((12, 23), 'BaseFontBlend', 'delta', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001241 ((12, 31), 'CIDFontVersion', 'number', 0, None),
1242 ((12, 32), 'CIDFontRevision', 'number', 0, None),
1243 ((12, 33), 'CIDFontType', 'number', 0, None),
1244 ((12, 34), 'CIDCount', 'number', 8720, None),
jvred101512003-08-22 19:53:32 +00001245 (15, 'charset', 'number', 0, CharsetConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001246 ((12, 35), 'UIDBase', 'number', None, None),
jvrb9702ba2003-01-03 20:56:01 +00001247 (16, 'Encoding', 'number', 0, EncodingConverter()),
jvr155aa752002-05-17 19:58:49 +00001248 (18, 'Private', ('number','number'), None, PrivateDictConverter()),
jvred101512003-08-22 19:53:32 +00001249 ((12, 37), 'FDSelect', 'number', None, FDSelectConverter()),
1250 ((12, 36), 'FDArray', 'number', None, FDArrayConverter()),
jvr155aa752002-05-17 19:58:49 +00001251 (17, 'CharStrings', 'number', None, CharStringsConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001252]
1253
jvred101512003-08-22 19:53:32 +00001254# Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order,
1255# in order for the font to compile back from xml.
1256
1257
jvr4756b3a2002-05-16 18:17:32 +00001258privateDictOperators = [
1259# opcode name argument type default converter
1260 (6, 'BlueValues', 'delta', None, None),
1261 (7, 'OtherBlues', 'delta', None, None),
1262 (8, 'FamilyBlues', 'delta', None, None),
1263 (9, 'FamilyOtherBlues', 'delta', None, None),
1264 ((12, 9), 'BlueScale', 'number', 0.039625, None),
1265 ((12, 10), 'BlueShift', 'number', 7, None),
1266 ((12, 11), 'BlueFuzz', 'number', 1, None),
1267 (10, 'StdHW', 'number', None, None),
1268 (11, 'StdVW', 'number', None, None),
1269 ((12, 12), 'StemSnapH', 'delta', None, None),
1270 ((12, 13), 'StemSnapV', 'delta', None, None),
1271 ((12, 14), 'ForceBold', 'number', 0, None),
jvrf2cf9c52002-05-23 21:50:36 +00001272 ((12, 15), 'ForceBoldThreshold', 'number', None, None), # deprecated
1273 ((12, 16), 'lenIV', 'number', None, None), # deprecated
jvr4756b3a2002-05-16 18:17:32 +00001274 ((12, 17), 'LanguageGroup', 'number', 0, None),
1275 ((12, 18), 'ExpansionFactor', 'number', 0.06, None),
1276 ((12, 19), 'initialRandomSeed', 'number', 0, None),
1277 (20, 'defaultWidthX', 'number', 0, None),
1278 (21, 'nominalWidthX', 'number', 0, None),
1279 (19, 'Subrs', 'number', None, SubrsConverter()),
1280]
1281
jvr4e5af602002-05-24 09:58:04 +00001282def addConverters(table):
1283 for i in range(len(table)):
1284 op, name, arg, default, conv = table[i]
1285 if conv is not None:
1286 continue
1287 if arg in ("delta", "array"):
1288 conv = ArrayConverter()
1289 elif arg == "number":
1290 conv = NumberConverter()
1291 elif arg == "SID":
1292 conv = SimpleConverter()
1293 else:
1294 assert 0
1295 table[i] = op, name, arg, default, conv
1296
1297addConverters(privateDictOperators)
1298addConverters(topDictOperators)
1299
jvr4756b3a2002-05-16 18:17:32 +00001300
1301class TopDictDecompiler(psCharStrings.DictDecompiler):
1302 operators = buildOperatorDict(topDictOperators)
1303
1304
1305class PrivateDictDecompiler(psCharStrings.DictDecompiler):
1306 operators = buildOperatorDict(privateDictOperators)
1307
1308
jvrf2cf9c52002-05-23 21:50:36 +00001309class DictCompiler:
1310
1311 def __init__(self, dictObj, strings, parent):
1312 assert isinstance(strings, IndexedStrings)
1313 self.dictObj = dictObj
1314 self.strings = strings
1315 self.parent = parent
1316 rawDict = {}
1317 for name in dictObj.order:
1318 value = getattr(dictObj, name, None)
1319 if value is None:
1320 continue
1321 conv = dictObj.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001322 value = conv.write(dictObj, value)
jvrf2cf9c52002-05-23 21:50:36 +00001323 if value == dictObj.defaults.get(name):
1324 continue
1325 rawDict[name] = value
1326 self.rawDict = rawDict
1327
jvr4e5af602002-05-24 09:58:04 +00001328 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +00001329 pass
1330
1331 def getDataLength(self):
jvr4e5af602002-05-24 09:58:04 +00001332 return len(self.compile("getDataLength"))
jvrf2cf9c52002-05-23 21:50:36 +00001333
jvr4e5af602002-05-24 09:58:04 +00001334 def compile(self, reason):
1335 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001336 print("-- compiling %s for %s" % (self.__class__.__name__, reason))
1337 print("in baseDict: ", self)
jvrf2cf9c52002-05-23 21:50:36 +00001338 rawDict = self.rawDict
1339 data = []
1340 for name in self.dictObj.order:
1341 value = rawDict.get(name)
1342 if value is None:
1343 continue
1344 op, argType = self.opcodes[name]
jvr2a9bcde2008-03-07 19:56:17 +00001345 if isinstance(argType, tuple):
jvrf2cf9c52002-05-23 21:50:36 +00001346 l = len(argType)
1347 assert len(value) == l, "value doesn't match arg type"
1348 for i in range(l):
jvred101512003-08-22 19:53:32 +00001349 arg = argType[i]
jvrf2cf9c52002-05-23 21:50:36 +00001350 v = value[i]
1351 arghandler = getattr(self, "arg_" + arg)
1352 data.append(arghandler(v))
1353 else:
1354 arghandler = getattr(self, "arg_" + argType)
1355 data.append(arghandler(value))
1356 data.append(op)
1357 return "".join(data)
1358
1359 def toFile(self, file):
jvr4e5af602002-05-24 09:58:04 +00001360 file.write(self.compile("toFile"))
jvrf2cf9c52002-05-23 21:50:36 +00001361
1362 def arg_number(self, num):
1363 return encodeNumber(num)
1364 def arg_SID(self, s):
1365 return psCharStrings.encodeIntCFF(self.strings.getSID(s))
1366 def arg_array(self, value):
1367 data = []
1368 for num in value:
1369 data.append(encodeNumber(num))
1370 return "".join(data)
1371 def arg_delta(self, value):
1372 out = []
1373 last = 0
1374 for v in value:
1375 out.append(v - last)
1376 last = v
1377 data = []
1378 for num in out:
1379 data.append(encodeNumber(num))
1380 return "".join(data)
1381
1382
1383def encodeNumber(num):
jvrf6ff48b2008-03-07 19:49:25 +00001384 if isinstance(num, float):
jvrf2cf9c52002-05-23 21:50:36 +00001385 return psCharStrings.encodeFloat(num)
1386 else:
1387 return psCharStrings.encodeIntCFF(num)
1388
1389
1390class TopDictCompiler(DictCompiler):
1391
1392 opcodes = buildOpcodeDict(topDictOperators)
1393
1394 def getChildren(self, strings):
1395 children = []
jvred101512003-08-22 19:53:32 +00001396 if hasattr(self.dictObj, "charset") and self.dictObj.charset:
jvrf2cf9c52002-05-23 21:50:36 +00001397 children.append(CharsetCompiler(strings, self.dictObj.charset, self))
jvrb9702ba2003-01-03 20:56:01 +00001398 if hasattr(self.dictObj, "Encoding"):
1399 encoding = self.dictObj.Encoding
jvr2a9bcde2008-03-07 19:56:17 +00001400 if not isinstance(encoding, basestring):
jvrb9702ba2003-01-03 20:56:01 +00001401 children.append(EncodingCompiler(strings, encoding, self))
jvrce522412003-08-25 07:37:25 +00001402 if hasattr(self.dictObj, "FDSelect"):
jvred101512003-08-22 19:53:32 +00001403 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1404 # issues about merging the FDArrays. Here I assume that
1405 # either the font was read from XML, and teh FDSelect indices are all
1406 # in the charstring data, or the FDSelect array is already fully defined.
1407 fdSelect = self.dictObj.FDSelect
1408 if len(fdSelect) == 0: # probably read in from XML; assume fdIndex in CharString data
1409 charStrings = self.dictObj.CharStrings
1410 for name in self.dictObj.charset:
1411 charstring = charStrings[name]
1412 fdSelect.append(charStrings[name].fdSelectIndex)
1413 fdSelectComp = FDSelectCompiler(fdSelect, self)
1414 children.append(fdSelectComp)
jvrf2cf9c52002-05-23 21:50:36 +00001415 if hasattr(self.dictObj, "CharStrings"):
1416 items = []
1417 charStrings = self.dictObj.CharStrings
1418 for name in self.dictObj.charset:
1419 items.append(charStrings[name])
1420 charStringsComp = CharStringsCompiler(items, strings, self)
1421 children.append(charStringsComp)
jvrce522412003-08-25 07:37:25 +00001422 if hasattr(self.dictObj, "FDArray"):
jvred101512003-08-22 19:53:32 +00001423 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1424 # issues about merging the FDArrays. Here I assume that the FDArray info is correct
1425 # and complete.
1426 fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self)
1427 children.append(fdArrayIndexComp)
1428 children.extend(fdArrayIndexComp.getChildren(strings))
1429 if hasattr(self.dictObj, "Private"):
1430 privComp = self.dictObj.Private.getCompiler(strings, self)
1431 children.append(privComp)
1432 children.extend(privComp.getChildren(strings))
1433 return children
1434
1435
1436class FontDictCompiler(DictCompiler):
1437
1438 opcodes = buildOpcodeDict(topDictOperators)
1439
1440 def getChildren(self, strings):
1441 children = []
jvrf2cf9c52002-05-23 21:50:36 +00001442 if hasattr(self.dictObj, "Private"):
1443 privComp = self.dictObj.Private.getCompiler(strings, self)
1444 children.append(privComp)
1445 children.extend(privComp.getChildren(strings))
1446 return children
1447
1448
1449class PrivateDictCompiler(DictCompiler):
1450
1451 opcodes = buildOpcodeDict(privateDictOperators)
1452
jvr4e5af602002-05-24 09:58:04 +00001453 def setPos(self, pos, endPos):
1454 size = endPos - pos
jvrf2cf9c52002-05-23 21:50:36 +00001455 self.parent.rawDict["Private"] = size, pos
1456 self.pos = pos
1457
1458 def getChildren(self, strings):
1459 children = []
1460 if hasattr(self.dictObj, "Subrs"):
1461 children.append(self.dictObj.Subrs.getCompiler(strings, self))
1462 return children
1463
jvr4756b3a2002-05-16 18:17:32 +00001464
1465class BaseDict:
1466
jvr4e5af602002-05-24 09:58:04 +00001467 def __init__(self, strings=None, file=None, offset=None):
jvr4756b3a2002-05-16 18:17:32 +00001468 self.rawDict = {}
jvr767102e2002-05-17 07:06:32 +00001469 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001470 print("loading %s at %s" % (self.__class__.__name__, offset))
jvr4756b3a2002-05-16 18:17:32 +00001471 self.file = file
1472 self.offset = offset
1473 self.strings = strings
jvr155aa752002-05-17 19:58:49 +00001474 self.skipNames = []
jvr4756b3a2002-05-16 18:17:32 +00001475
1476 def decompile(self, data):
jvrf2cf9c52002-05-23 21:50:36 +00001477 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001478 print(" length %s is %s" % (self.__class__.__name__, len(data)))
jvrf2cf9c52002-05-23 21:50:36 +00001479 dec = self.decompilerClass(self.strings)
jvr4756b3a2002-05-16 18:17:32 +00001480 dec.decompile(data)
1481 self.rawDict = dec.getDict()
1482 self.postDecompile()
1483
1484 def postDecompile(self):
1485 pass
1486
jvrf2cf9c52002-05-23 21:50:36 +00001487 def getCompiler(self, strings, parent):
1488 return self.compilerClass(self, strings, parent)
1489
jvr4756b3a2002-05-16 18:17:32 +00001490 def __getattr__(self, name):
1491 value = self.rawDict.get(name)
1492 if value is None:
1493 value = self.defaults.get(name)
1494 if value is None:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -05001495 raise AttributeError(name)
jvr4756b3a2002-05-16 18:17:32 +00001496 conv = self.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001497 value = conv.read(self, value)
jvr4756b3a2002-05-16 18:17:32 +00001498 setattr(self, name, value)
1499 return value
1500
1501 def toXML(self, xmlWriter, progress):
1502 for name in self.order:
jvr155aa752002-05-17 19:58:49 +00001503 if name in self.skipNames:
1504 continue
jvr4756b3a2002-05-16 18:17:32 +00001505 value = getattr(self, name, None)
1506 if value is None:
1507 continue
jvr4e5af602002-05-24 09:58:04 +00001508 conv = self.converters[name]
jvr7ce0a132002-07-23 16:42:11 +00001509 conv.xmlWrite(xmlWriter, name, value, progress)
jvr4e5af602002-05-24 09:58:04 +00001510
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001511 def fromXML(self, name, attrs, content):
jvr4e5af602002-05-24 09:58:04 +00001512 conv = self.converters[name]
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001513 value = conv.xmlRead(name, attrs, content, self)
jvr4e5af602002-05-24 09:58:04 +00001514 setattr(self, name, value)
jvr4756b3a2002-05-16 18:17:32 +00001515
1516
1517class TopDict(BaseDict):
1518
1519 defaults = buildDefaults(topDictOperators)
1520 converters = buildConverters(topDictOperators)
1521 order = buildOrder(topDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001522 decompilerClass = TopDictDecompiler
1523 compilerClass = TopDictCompiler
Just7842e561999-12-16 21:34:53 +00001524
jvr4e5af602002-05-24 09:58:04 +00001525 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
jvr016ca762002-05-16 18:38:03 +00001526 BaseDict.__init__(self, strings, file, offset)
1527 self.GlobalSubrs = GlobalSubrs
1528
Just7842e561999-12-16 21:34:53 +00001529 def getGlyphOrder(self):
1530 return self.charset
1531
jvr4756b3a2002-05-16 18:17:32 +00001532 def postDecompile(self):
1533 offset = self.rawDict.get("CharStrings")
1534 if offset is None:
1535 return
1536 # get the number of glyphs beforehand.
1537 self.file.seek(offset)
jvra2ad5442002-05-17 18:36:07 +00001538 self.numGlyphs = readCard16(self.file)
Just7842e561999-12-16 21:34:53 +00001539
jvr016ca762002-05-16 18:38:03 +00001540 def toXML(self, xmlWriter, progress):
jvr155aa752002-05-17 19:58:49 +00001541 if hasattr(self, "CharStrings"):
jvr7ce0a132002-07-23 16:42:11 +00001542 self.decompileAllCharStrings(progress)
jvred101512003-08-22 19:53:32 +00001543 if hasattr(self, "ROS"):
1544 self.skipNames = ['Encoding']
jvr155aa752002-05-17 19:58:49 +00001545 if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
1546 # these values have default values, but I only want them to show up
1547 # in CID fonts.
1548 self.skipNames = ['CIDFontVersion', 'CIDFontRevision', 'CIDFontType',
1549 'CIDCount']
jvr016ca762002-05-16 18:38:03 +00001550 BaseDict.toXML(self, xmlWriter, progress)
jvr767102e2002-05-17 07:06:32 +00001551
jvr7ce0a132002-07-23 16:42:11 +00001552 def decompileAllCharStrings(self, progress):
jvr4e5af602002-05-24 09:58:04 +00001553 # XXX only when doing ttdump -i?
jvr7ce0a132002-07-23 16:42:11 +00001554 i = 0
jvra2ad5442002-05-17 18:36:07 +00001555 for charString in self.CharStrings.values():
jvred101512003-08-22 19:53:32 +00001556 try:
1557 charString.decompile()
1558 except:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001559 print("Error in charstring ", i)
jvred101512003-08-22 19:53:32 +00001560 import sys
1561 type, value = sys. exc_info()[0:2]
1562 raise type(value)
jvr7ce0a132002-07-23 16:42:11 +00001563 if not i % 30 and progress:
1564 progress.increment(0) # update
1565 i = i + 1
Just7842e561999-12-16 21:34:53 +00001566
1567
jvred101512003-08-22 19:53:32 +00001568class FontDict(BaseDict):
1569
1570 defaults = buildDefaults(topDictOperators)
1571 converters = buildConverters(topDictOperators)
1572 order = buildOrder(topDictOperators)
1573 decompilerClass = None
1574 compilerClass = FontDictCompiler
1575
1576 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
1577 BaseDict.__init__(self, strings, file, offset)
1578 self.GlobalSubrs = GlobalSubrs
1579
1580 def getGlyphOrder(self):
1581 return self.charset
1582
1583 def toXML(self, xmlWriter, progress):
1584 self.skipNames = ['Encoding']
1585 BaseDict.toXML(self, xmlWriter, progress)
1586
1587
1588
jvr4756b3a2002-05-16 18:17:32 +00001589class PrivateDict(BaseDict):
1590 defaults = buildDefaults(privateDictOperators)
1591 converters = buildConverters(privateDictOperators)
1592 order = buildOrder(privateDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001593 decompilerClass = PrivateDictDecompiler
1594 compilerClass = PrivateDictCompiler
Just7842e561999-12-16 21:34:53 +00001595
1596
jvre3275582002-05-14 12:22:03 +00001597class IndexedStrings:
1598
jvr767102e2002-05-17 07:06:32 +00001599 """SID -> string mapping."""
1600
1601 def __init__(self, file=None):
1602 if file is None:
jvre3275582002-05-14 12:22:03 +00001603 strings = []
jvr767102e2002-05-17 07:06:32 +00001604 else:
jvr4e5af602002-05-24 09:58:04 +00001605 strings = list(Index(file))
jvre3275582002-05-14 12:22:03 +00001606 self.strings = strings
1607
jvrf2cf9c52002-05-23 21:50:36 +00001608 def getCompiler(self):
1609 return IndexedStringsCompiler(self, None, None)
1610
1611 def __len__(self):
1612 return len(self.strings)
1613
jvre3275582002-05-14 12:22:03 +00001614 def __getitem__(self, SID):
1615 if SID < cffStandardStringCount:
1616 return cffStandardStrings[SID]
1617 else:
1618 return self.strings[SID - cffStandardStringCount]
1619
1620 def getSID(self, s):
1621 if not hasattr(self, "stringMapping"):
1622 self.buildStringMapping()
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -05001623 if s in cffStandardStringMapping:
jvre3275582002-05-14 12:22:03 +00001624 SID = cffStandardStringMapping[s]
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -05001625 elif s in self.stringMapping:
jvre3275582002-05-14 12:22:03 +00001626 SID = self.stringMapping[s]
1627 else:
1628 SID = len(self.strings) + cffStandardStringCount
1629 self.strings.append(s)
1630 self.stringMapping[s] = SID
1631 return SID
1632
1633 def getStrings(self):
1634 return self.strings
1635
1636 def buildStringMapping(self):
1637 self.stringMapping = {}
1638 for index in range(len(self.strings)):
1639 self.stringMapping[self.strings[index]] = index + cffStandardStringCount
1640
1641
Just7842e561999-12-16 21:34:53 +00001642# The 391 Standard Strings as used in the CFF format.
1643# from Adobe Technical None #5176, version 1.0, 18 March 1998
1644
1645cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
1646 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
1647 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
1648 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
1649 'semicolon', 'less', 'equal', 'greater', 'question', 'at', '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', 'bracketleft', 'backslash',
1652 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
1653 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
1654 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
1655 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
1656 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
1657 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
1658 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
1659 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
1660 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
1661 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
1662 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
1663 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
1664 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
1665 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
1666 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
1667 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
1668 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
1669 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
1670 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
1671 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
1672 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
1673 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
1674 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
1675 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
1676 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
1677 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
1678 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
1679 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
1680 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
1681 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
1682 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
1683 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
1684 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
1685 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
1686 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
1687 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
1688 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
1689 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
1690 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
1691 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
1692 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
1693 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
1694 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
1695 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
1696 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
1697 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
1698 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
1699 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
1700 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
1701 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
1702 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
1703 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
1704 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
1705 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
1706 '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
1707 'Semibold'
1708]
1709
1710cffStandardStringCount = 391
1711assert len(cffStandardStrings) == cffStandardStringCount
1712# build reverse mapping
1713cffStandardStringMapping = {}
1714for _i in range(cffStandardStringCount):
1715 cffStandardStringMapping[cffStandardStrings[_i]] = _i
jvrc60a44f2006-10-21 13:41:18 +00001716
1717cffISOAdobeStrings = [".notdef", "space", "exclam", "quotedbl", "numbersign",
1718"dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright",
1719"asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two",
1720"three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon",
1721"less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G",
1722"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
1723"X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum",
1724"underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
1725"k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
1726"braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent",
1727"sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle",
1728"quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl",
1729"endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet",
1730"quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis",
1731"perthousand", "questiondown", "grave", "acute", "circumflex", "tilde",
1732"macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
1733"ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE",
1734"ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls",
1735"onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus",
1736"Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn",
1737"threequarters", "twosuperior", "registered", "minus", "eth", "multiply",
1738"threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
1739"Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave",
1740"Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute",
1741"Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute",
1742"Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute",
1743"acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute",
1744"ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis",
1745"igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde",
1746"scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis",
1747"zcaron"]
1748
1749cffISOAdobeStringCount = 229
1750assert len(cffISOAdobeStrings) == cffISOAdobeStringCount
1751
1752cffIExpertStrings = [".notdef", "space", "exclamsmall", "Hungarumlautsmall",
1753"dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall",
1754"parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader",
1755"comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle",
1756"twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle",
1757"sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon",
1758"commasuperior", "threequartersemdash", "periodsuperior", "questionsmall",
1759"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1760"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1761"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1762"parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
1763"Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall",
1764"Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
1765"Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall",
1766"Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall",
1767"exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall",
1768"Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall",
1769"figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall",
1770"onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth",
1771"threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds",
1772"zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior",
1773"fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior",
1774"zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior",
1775"fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior",
1776"centinferior", "dollarinferior", "periodinferior", "commainferior",
1777"Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall",
1778"Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall",
1779"Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall",
1780"Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall",
1781"Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall",
1782"Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall",
1783"Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall",
1784"Ydieresissmall"]
1785
1786cffExpertStringCount = 166
1787assert len(cffIExpertStrings) == cffExpertStringCount
1788
1789cffExpertSubsetStrings = [".notdef", "space", "dollaroldstyle",
1790"dollarsuperior", "parenleftsuperior", "parenrightsuperior", "twodotenleader",
1791"onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle",
1792"oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle",
1793"sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon",
1794"semicolon", "commasuperior", "threequartersemdash", "periodsuperior",
1795"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1796"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1797"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1798"parenrightinferior", "hyphensuperior", "colonmonetary", "onefitted", "rupiah",
1799"centoldstyle", "figuredash", "hypheninferior", "onequarter", "onehalf",
1800"threequarters", "oneeighth", "threeeighths", "fiveeighths", "seveneighths",
1801"onethird", "twothirds", "zerosuperior", "onesuperior", "twosuperior",
1802"threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior",
1803"eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior",
1804"threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior",
1805"eightinferior", "nineinferior", "centinferior", "dollarinferior",
1806"periodinferior", "commainferior"]
1807
1808cffExpertSubsetStringCount = 87
1809assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount