blob: 1577606318f2ba969351ef3aefffcd4237e49cc0 [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
Behdad Esfahbod90057742013-11-27 13:58:09 -0500682 value = value.decode("latin-1").encode("utf-8")
Behdad Esfahbod8c5c9662013-10-28 13:20:00 +0100683 xmlWriter.simpletag(name, value=value)
684 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500685 def xmlRead(self, name, attrs, content, parent):
Behdad Esfahbod90057742013-11-27 13:58:09 -0500686 return attrs["value"].decode("utf-8").encode("latin-1")
jvre2ca9b52002-09-09 14:18:39 +0000687
688
jvr4e5af602002-05-24 09:58:04 +0000689def parseNum(s):
690 try:
691 value = int(s)
692 except:
693 value = float(s)
694 return value
695
696class NumberConverter(SimpleConverter):
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500697 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000698 return parseNum(attrs["value"])
699
700class ArrayConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000701 def xmlWrite(self, xmlWriter, name, value, progress):
Behdad Esfahbode5ca7962013-11-27 04:38:16 -0500702 value = " ".join(map(str, value))
703 xmlWriter.simpletag(name, value=value)
jvr4e5af602002-05-24 09:58:04 +0000704 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500705 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000706 values = attrs["value"].split()
Behdad Esfahbode5ca7962013-11-27 04:38:16 -0500707 return [parseNum(value) for value in values]
jvr4e5af602002-05-24 09:58:04 +0000708
709class TableConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000710 def xmlWrite(self, xmlWriter, name, value, progress):
jvra2ad5442002-05-17 18:36:07 +0000711 xmlWriter.begintag(name)
712 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000713 value.toXML(xmlWriter, progress)
jvra2ad5442002-05-17 18:36:07 +0000714 xmlWriter.endtag(name)
715 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500716 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000717 ob = self.getClass()()
718 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000719 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000720 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500721 name, attrs, content = element
722 ob.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000723 return ob
jvra2ad5442002-05-17 18:36:07 +0000724
jvr4e5af602002-05-24 09:58:04 +0000725class PrivateDictConverter(TableConverter):
726 def getClass(self):
727 return PrivateDict
jvr4756b3a2002-05-16 18:17:32 +0000728 def read(self, parent, value):
729 size, offset = value
730 file = parent.file
jvr4e5af602002-05-24 09:58:04 +0000731 priv = PrivateDict(parent.strings, file, offset)
jvr4756b3a2002-05-16 18:17:32 +0000732 file.seek(offset)
733 data = file.read(size)
734 len(data) == size
jvr4e5af602002-05-24 09:58:04 +0000735 priv.decompile(data)
736 return priv
jvrf2cf9c52002-05-23 21:50:36 +0000737 def write(self, parent, value):
738 return (0, 0) # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000739
jvr4e5af602002-05-24 09:58:04 +0000740class SubrsConverter(TableConverter):
741 def getClass(self):
742 return SubrsIndex
jvr4756b3a2002-05-16 18:17:32 +0000743 def read(self, parent, value):
744 file = parent.file
745 file.seek(parent.offset + value) # Offset(self)
jvr4e5af602002-05-24 09:58:04 +0000746 return SubrsIndex(file)
jvrf2cf9c52002-05-23 21:50:36 +0000747 def write(self, parent, value):
748 return 0 # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000749
jvr4e5af602002-05-24 09:58:04 +0000750class CharStringsConverter(TableConverter):
jvr4756b3a2002-05-16 18:17:32 +0000751 def read(self, parent, value):
752 file = parent.file
jvr767102e2002-05-17 07:06:32 +0000753 charset = parent.charset
jvra2ad5442002-05-17 18:36:07 +0000754 globalSubrs = parent.GlobalSubrs
755 if hasattr(parent, "ROS"):
756 fdSelect, fdArray = parent.FDSelect, parent.FDArray
757 private = None
758 else:
759 fdSelect, fdArray = None, None
760 private = parent.Private
jvr4756b3a2002-05-16 18:17:32 +0000761 file.seek(value) # Offset(0)
jvra2ad5442002-05-17 18:36:07 +0000762 return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
jvrf2cf9c52002-05-23 21:50:36 +0000763 def write(self, parent, value):
764 return 0 # dummy value
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500765 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +0000766 if hasattr(parent, "ROS"):
767 # if it is a CID-keyed font, then the private Dict is extracted from the parent.FDArray
768 private, fdSelect, fdArray = None, parent.FDSelect, parent.FDArray
769 else:
770 # if it is a name-keyed font, then the private dict is in the top dict, and there is no fdArray.
771 private, fdSelect, fdArray = parent.Private, None, None
772 charStrings = CharStrings(None, None, parent.GlobalSubrs, private, fdSelect, fdArray)
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500773 charStrings.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000774 return charStrings
jvr4756b3a2002-05-16 18:17:32 +0000775
776class CharsetConverter:
777 def read(self, parent, value):
778 isCID = hasattr(parent, "ROS")
779 if value > 2:
780 numGlyphs = parent.numGlyphs
781 file = parent.file
782 file.seek(value)
jvrf2cf9c52002-05-23 21:50:36 +0000783 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500784 print("loading charset at %s" % value)
jvra2ad5442002-05-17 18:36:07 +0000785 format = readCard8(file)
Just7842e561999-12-16 21:34:53 +0000786 if format == 0:
jvrc60a44f2006-10-21 13:41:18 +0000787 charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
jvr4756b3a2002-05-16 18:17:32 +0000788 elif format == 1 or format == 2:
789 charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
Just7842e561999-12-16 21:34:53 +0000790 else:
jvr1890b952002-05-15 07:41:30 +0000791 raise NotImplementedError
jvr4756b3a2002-05-16 18:17:32 +0000792 assert len(charset) == numGlyphs
jvrf2cf9c52002-05-23 21:50:36 +0000793 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500794 print(" charset end at %s" % file.tell())
jvrc60a44f2006-10-21 13:41:18 +0000795 else: # offset == 0 -> no charset data.
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500796 if isCID or "CharStrings" not in parent.rawDict:
jvrc60a44f2006-10-21 13:41:18 +0000797 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 +0000798 charset = None
799 elif value == 0:
jvrc60a44f2006-10-21 13:41:18 +0000800 charset = cffISOAdobeStrings
jvr4756b3a2002-05-16 18:17:32 +0000801 elif value == 1:
jvrc60a44f2006-10-21 13:41:18 +0000802 charset = cffIExpertStrings
jvr4756b3a2002-05-16 18:17:32 +0000803 elif value == 2:
jvrc60a44f2006-10-21 13:41:18 +0000804 charset = cffExpertSubsetStrings
jvr4756b3a2002-05-16 18:17:32 +0000805 return charset
jvrc60a44f2006-10-21 13:41:18 +0000806
jvrf2cf9c52002-05-23 21:50:36 +0000807 def write(self, parent, value):
808 return 0 # dummy value
jvr7ce0a132002-07-23 16:42:11 +0000809 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000810 # XXX only write charset when not in OT/TTX context, where we
811 # dump charset as a separate "GlyphOrder" table.
812 ##xmlWriter.simpletag("charset")
813 xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
jvr4756b3a2002-05-16 18:17:32 +0000814 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500815 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000816 if 0:
817 return safeEval(attrs["value"])
jvr4756b3a2002-05-16 18:17:32 +0000818
819
jvrf2cf9c52002-05-23 21:50:36 +0000820class CharsetCompiler:
821
822 def __init__(self, strings, charset, parent):
823 assert charset[0] == '.notdef'
jvred101512003-08-22 19:53:32 +0000824 isCID = hasattr(parent.dictObj, "ROS")
825 data0 = packCharset0(charset, isCID, strings)
826 data = packCharset(charset, isCID, strings)
jvr6004baf2002-05-24 10:35:13 +0000827 if len(data) < len(data0):
828 self.data = data
829 else:
830 self.data = data0
jvrf2cf9c52002-05-23 21:50:36 +0000831 self.parent = parent
832
jvr4e5af602002-05-24 09:58:04 +0000833 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000834 self.parent.rawDict["charset"] = pos
835
836 def getDataLength(self):
837 return len(self.data)
838
839 def toFile(self, file):
840 file.write(self.data)
841
842
jvred101512003-08-22 19:53:32 +0000843def getCIDfromName(name, strings):
844 return int(name[3:])
845
846def getSIDfromName(name, strings):
847 return strings.getSID(name)
848
849def packCharset0(charset, isCID, strings):
jvr6004baf2002-05-24 10:35:13 +0000850 format = 0
851 data = [packCard8(format)]
jvred101512003-08-22 19:53:32 +0000852 if isCID:
853 getNameID = getCIDfromName
854 else:
855 getNameID = getSIDfromName
856
jvr6004baf2002-05-24 10:35:13 +0000857 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000858 data.append(packCard16(getNameID(name,strings)))
jvr6004baf2002-05-24 10:35:13 +0000859 return "".join(data)
860
jvred101512003-08-22 19:53:32 +0000861
862def packCharset(charset, isCID, strings):
jvr6004baf2002-05-24 10:35:13 +0000863 format = 1
jvr6004baf2002-05-24 10:35:13 +0000864 ranges = []
865 first = None
866 end = 0
jvred101512003-08-22 19:53:32 +0000867 if isCID:
868 getNameID = getCIDfromName
869 else:
870 getNameID = getSIDfromName
871
jvr6004baf2002-05-24 10:35:13 +0000872 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000873 SID = getNameID(name, strings)
jvr6004baf2002-05-24 10:35:13 +0000874 if first is None:
875 first = SID
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500876 elif end + 1 != SID:
jvr6004baf2002-05-24 10:35:13 +0000877 nLeft = end - first
878 if nLeft > 255:
879 format = 2
880 ranges.append((first, nLeft))
881 first = SID
882 end = SID
883 nLeft = end - first
884 if nLeft > 255:
885 format = 2
886 ranges.append((first, nLeft))
887
jvr74cd1ef2002-05-24 10:38:04 +0000888 data = [packCard8(format)]
jvr6004baf2002-05-24 10:35:13 +0000889 if format == 1:
890 nLeftFunc = packCard8
891 else:
892 nLeftFunc = packCard16
893 for first, nLeft in ranges:
894 data.append(packCard16(first) + nLeftFunc(nLeft))
jvrb58176e2002-05-24 11:55:37 +0000895 return "".join(data)
jvr6004baf2002-05-24 10:35:13 +0000896
jvrc60a44f2006-10-21 13:41:18 +0000897def parseCharset0(numGlyphs, file, strings, isCID):
jvrf2cf9c52002-05-23 21:50:36 +0000898 charset = [".notdef"]
jvrc60a44f2006-10-21 13:41:18 +0000899 if isCID:
900 for i in range(numGlyphs - 1):
901 CID = readCard16(file)
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500902 charset.append("cid" + str(CID).zfill(5))
jvrc60a44f2006-10-21 13:41:18 +0000903 else:
904 for i in range(numGlyphs - 1):
905 SID = readCard16(file)
906 charset.append(strings[SID])
jvrf2cf9c52002-05-23 21:50:36 +0000907 return charset
908
jvr4756b3a2002-05-16 18:17:32 +0000909def parseCharset(numGlyphs, file, strings, isCID, format):
910 charset = ['.notdef']
911 count = 1
912 if format == 1:
jvra2ad5442002-05-17 18:36:07 +0000913 nLeftFunc = readCard8
jvr4756b3a2002-05-16 18:17:32 +0000914 else:
jvra2ad5442002-05-17 18:36:07 +0000915 nLeftFunc = readCard16
jvr4756b3a2002-05-16 18:17:32 +0000916 while count < numGlyphs:
jvra2ad5442002-05-17 18:36:07 +0000917 first = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000918 nLeft = nLeftFunc(file)
919 if isCID:
920 for CID in range(first, first+nLeft+1):
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500921 charset.append("cid" + str(CID).zfill(5))
jvr1890b952002-05-15 07:41:30 +0000922 else:
jvr4756b3a2002-05-16 18:17:32 +0000923 for SID in range(first, first+nLeft+1):
924 charset.append(strings[SID])
925 count = count + nLeft + 1
926 return charset
927
928
jvrb9702ba2003-01-03 20:56:01 +0000929class EncodingCompiler:
930
931 def __init__(self, strings, encoding, parent):
jvr2a9bcde2008-03-07 19:56:17 +0000932 assert not isinstance(encoding, basestring)
jvrb9702ba2003-01-03 20:56:01 +0000933 data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
934 data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
935 if len(data0) < len(data1):
936 self.data = data0
937 else:
938 self.data = data1
939 self.parent = parent
940
941 def setPos(self, pos, endPos):
942 self.parent.rawDict["Encoding"] = pos
943
944 def getDataLength(self):
945 return len(self.data)
946
947 def toFile(self, file):
948 file.write(self.data)
949
950
951class EncodingConverter(SimpleConverter):
952
953 def read(self, parent, value):
954 if value == 0:
955 return "StandardEncoding"
956 elif value == 1:
957 return "ExpertEncoding"
958 else:
959 assert value > 1
960 file = parent.file
961 file.seek(value)
962 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500963 print("loading Encoding at %s" % value)
jvrb9702ba2003-01-03 20:56:01 +0000964 format = readCard8(file)
965 haveSupplement = format & 0x80
966 if haveSupplement:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -0500967 raise NotImplementedError("Encoding supplements are not yet supported")
jvrb9702ba2003-01-03 20:56:01 +0000968 format = format & 0x7f
969 if format == 0:
970 encoding = parseEncoding0(parent.charset, file, haveSupplement,
971 parent.strings)
972 elif format == 1:
973 encoding = parseEncoding1(parent.charset, file, haveSupplement,
974 parent.strings)
975 return encoding
976
977 def write(self, parent, value):
978 if value == "StandardEncoding":
979 return 0
980 elif value == "ExpertEncoding":
981 return 1
982 return 0 # dummy value
983
984 def xmlWrite(self, xmlWriter, name, value, progress):
985 if value in ("StandardEncoding", "ExpertEncoding"):
986 xmlWriter.simpletag(name, name=value)
987 xmlWriter.newline()
988 return
989 xmlWriter.begintag(name)
990 xmlWriter.newline()
991 for code in range(len(value)):
992 glyphName = value[code]
993 if glyphName != ".notdef":
994 xmlWriter.simpletag("map", code=hex(code), name=glyphName)
995 xmlWriter.newline()
996 xmlWriter.endtag(name)
997 xmlWriter.newline()
998
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500999 def xmlRead(self, name, attrs, content, parent):
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -05001000 if "name" in attrs:
jvrb9702ba2003-01-03 20:56:01 +00001001 return attrs["name"]
1002 encoding = [".notdef"] * 256
1003 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +00001004 if isinstance(element, basestring):
jvrb9702ba2003-01-03 20:56:01 +00001005 continue
1006 name, attrs, content = element
1007 code = safeEval(attrs["code"])
1008 glyphName = attrs["name"]
1009 encoding[code] = glyphName
1010 return encoding
1011
1012
1013def parseEncoding0(charset, file, haveSupplement, strings):
1014 nCodes = readCard8(file)
1015 encoding = [".notdef"] * 256
1016 for glyphID in range(1, nCodes + 1):
1017 code = readCard8(file)
1018 if code != 0:
1019 encoding[code] = charset[glyphID]
1020 return encoding
1021
1022def parseEncoding1(charset, file, haveSupplement, strings):
1023 nRanges = readCard8(file)
1024 encoding = [".notdef"] * 256
1025 glyphID = 1
1026 for i in range(nRanges):
1027 code = readCard8(file)
1028 nLeft = readCard8(file)
1029 for glyphID in range(glyphID, glyphID + nLeft + 1):
1030 encoding[code] = charset[glyphID]
1031 code = code + 1
1032 glyphID = glyphID + 1
1033 return encoding
1034
1035def packEncoding0(charset, encoding, strings):
1036 format = 0
1037 m = {}
1038 for code in range(len(encoding)):
1039 name = encoding[code]
1040 if name != ".notdef":
1041 m[name] = code
1042 codes = []
1043 for name in charset[1:]:
1044 code = m.get(name)
1045 codes.append(code)
1046
1047 while codes and codes[-1] is None:
1048 codes.pop()
1049
1050 data = [packCard8(format), packCard8(len(codes))]
1051 for code in codes:
1052 if code is None:
1053 code = 0
1054 data.append(packCard8(code))
1055 return "".join(data)
1056
1057def packEncoding1(charset, encoding, strings):
1058 format = 1
1059 m = {}
1060 for code in range(len(encoding)):
1061 name = encoding[code]
1062 if name != ".notdef":
1063 m[name] = code
1064 ranges = []
1065 first = None
1066 end = 0
1067 for name in charset[1:]:
1068 code = m.get(name, -1)
1069 if first is None:
1070 first = code
Behdad Esfahbod180ace62013-11-27 02:40:30 -05001071 elif end + 1 != code:
jvrb9702ba2003-01-03 20:56:01 +00001072 nLeft = end - first
1073 ranges.append((first, nLeft))
1074 first = code
1075 end = code
1076 nLeft = end - first
1077 ranges.append((first, nLeft))
1078
1079 # remove unencoded glyphs at the end.
1080 while ranges and ranges[-1][0] == -1:
1081 ranges.pop()
1082
1083 data = [packCard8(format), packCard8(len(ranges))]
1084 for first, nLeft in ranges:
1085 if first == -1: # unencoded
1086 first = 0
1087 data.append(packCard8(first) + packCard8(nLeft))
1088 return "".join(data)
1089
1090
jvr4e5af602002-05-24 09:58:04 +00001091class FDArrayConverter(TableConverter):
jvred101512003-08-22 19:53:32 +00001092
jvra2ad5442002-05-17 18:36:07 +00001093 def read(self, parent, value):
1094 file = parent.file
1095 file.seek(value)
jvred101512003-08-22 19:53:32 +00001096 fdArray = FDArrayIndex(file)
jvra2ad5442002-05-17 18:36:07 +00001097 fdArray.strings = parent.strings
1098 fdArray.GlobalSubrs = parent.GlobalSubrs
1099 return fdArray
1100
jvred101512003-08-22 19:53:32 +00001101 def write(self, parent, value):
1102 return 0 # dummy value
1103
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001104 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001105 fdArray = FDArrayIndex()
1106 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +00001107 if isinstance(element, basestring):
jvred101512003-08-22 19:53:32 +00001108 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001109 name, attrs, content = element
1110 fdArray.fromXML(name, attrs, content)
jvred101512003-08-22 19:53:32 +00001111 return fdArray
1112
jvra2ad5442002-05-17 18:36:07 +00001113
1114class FDSelectConverter:
jvred101512003-08-22 19:53:32 +00001115
jvra2ad5442002-05-17 18:36:07 +00001116 def read(self, parent, value):
1117 file = parent.file
1118 file.seek(value)
jvred101512003-08-22 19:53:32 +00001119 fdSelect = FDSelect(file, parent.numGlyphs)
1120 return fdSelect
1121
1122 def write(self, parent, value):
1123 return 0 # dummy value
1124
1125 # The FDSelect glyph data is written out to XML in the charstring keys,
1126 # so we write out only the format selector
jvr7ce0a132002-07-23 16:42:11 +00001127 def xmlWrite(self, xmlWriter, name, value, progress):
jvred101512003-08-22 19:53:32 +00001128 xmlWriter.simpletag(name, [('format', value.format)])
1129 xmlWriter.newline()
1130
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001131 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001132 format = safeEval(attrs["format"])
1133 file = None
1134 numGlyphs = None
1135 fdSelect = FDSelect(file, numGlyphs, format)
1136 return fdSelect
1137
1138
1139def packFDSelect0(fdSelectArray):
1140 format = 0
1141 data = [packCard8(format)]
1142 for index in fdSelectArray:
1143 data.append(packCard8(index))
1144 return "".join(data)
1145
1146
1147def packFDSelect3(fdSelectArray):
1148 format = 3
1149 fdRanges = []
1150 first = None
1151 end = 0
1152 lenArray = len(fdSelectArray)
1153 lastFDIndex = -1
1154 for i in range(lenArray):
1155 fdIndex = fdSelectArray[i]
1156 if lastFDIndex != fdIndex:
1157 fdRanges.append([i, fdIndex])
1158 lastFDIndex = fdIndex
1159 sentinelGID = i + 1
1160
1161 data = [packCard8(format)]
1162 data.append(packCard16( len(fdRanges) ))
1163 for fdRange in fdRanges:
1164 data.append(packCard16(fdRange[0]))
1165 data.append(packCard8(fdRange[1]))
1166 data.append(packCard16(sentinelGID))
1167 return "".join(data)
1168
1169
1170class FDSelectCompiler:
1171
1172 def __init__(self, fdSelect, parent):
1173 format = fdSelect.format
1174 fdSelectArray = fdSelect.gidArray
1175 if format == 0:
1176 self.data = packFDSelect0(fdSelectArray)
1177 elif format == 3:
1178 self.data = packFDSelect3(fdSelectArray)
1179 else:
1180 # choose smaller of the two formats
1181 data0 = packFDSelect0(fdSelectArray)
1182 data3 = packFDSelect3(fdSelectArray)
1183 if len(data0) < len(data3):
1184 self.data = data0
1185 fdSelect.format = 0
1186 else:
1187 self.data = data3
1188 fdSelect.format = 3
1189
1190 self.parent = parent
1191
1192 def setPos(self, pos, endPos):
1193 self.parent.rawDict["FDSelect"] = pos
1194
1195 def getDataLength(self):
1196 return len(self.data)
1197
1198 def toFile(self, file):
1199 file.write(self.data)
jvra2ad5442002-05-17 18:36:07 +00001200
1201
jvr4e5af602002-05-24 09:58:04 +00001202class ROSConverter(SimpleConverter):
jvred101512003-08-22 19:53:32 +00001203
jvr7ce0a132002-07-23 16:42:11 +00001204 def xmlWrite(self, xmlWriter, name, value, progress):
jvr155aa752002-05-17 19:58:49 +00001205 registry, order, supplement = value
jvrf2cf9c52002-05-23 21:50:36 +00001206 xmlWriter.simpletag(name, [('Registry', registry), ('Order', order),
1207 ('Supplement', supplement)])
jvr4afb2572002-05-18 20:07:01 +00001208 xmlWriter.newline()
jvr155aa752002-05-17 19:58:49 +00001209
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001210 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001211 return (attrs['Registry'], attrs['Order'], safeEval(attrs['Supplement']))
1212
1213
jvr155aa752002-05-17 19:58:49 +00001214
jvr4756b3a2002-05-16 18:17:32 +00001215topDictOperators = [
1216# opcode name argument type default converter
jvr155aa752002-05-17 19:58:49 +00001217 ((12, 30), 'ROS', ('SID','SID','number'), None, ROSConverter()),
jvrf2cf9c52002-05-23 21:50:36 +00001218 ((12, 20), 'SyntheticBase', 'number', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001219 (0, 'version', 'SID', None, None),
jvre2ca9b52002-09-09 14:18:39 +00001220 (1, 'Notice', 'SID', None, Latin1Converter()),
1221 ((12, 0), 'Copyright', 'SID', None, Latin1Converter()),
jvr4756b3a2002-05-16 18:17:32 +00001222 (2, 'FullName', 'SID', None, None),
jvr155aa752002-05-17 19:58:49 +00001223 ((12, 38), 'FontName', 'SID', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001224 (3, 'FamilyName', 'SID', None, None),
1225 (4, 'Weight', 'SID', None, None),
1226 ((12, 1), 'isFixedPitch', 'number', 0, None),
1227 ((12, 2), 'ItalicAngle', 'number', 0, None),
1228 ((12, 3), 'UnderlinePosition', 'number', None, None),
1229 ((12, 4), 'UnderlineThickness', 'number', 50, None),
1230 ((12, 5), 'PaintType', 'number', 0, None),
1231 ((12, 6), 'CharstringType', 'number', 2, None),
1232 ((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0], None),
1233 (13, 'UniqueID', 'number', None, None),
1234 (5, 'FontBBox', 'array', [0,0,0,0], None),
1235 ((12, 8), 'StrokeWidth', 'number', 0, None),
1236 (14, 'XUID', 'array', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001237 ((12, 21), 'PostScript', 'SID', None, None),
1238 ((12, 22), 'BaseFontName', 'SID', None, None),
1239 ((12, 23), 'BaseFontBlend', 'delta', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001240 ((12, 31), 'CIDFontVersion', 'number', 0, None),
1241 ((12, 32), 'CIDFontRevision', 'number', 0, None),
1242 ((12, 33), 'CIDFontType', 'number', 0, None),
1243 ((12, 34), 'CIDCount', 'number', 8720, None),
jvred101512003-08-22 19:53:32 +00001244 (15, 'charset', 'number', 0, CharsetConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001245 ((12, 35), 'UIDBase', 'number', None, None),
jvrb9702ba2003-01-03 20:56:01 +00001246 (16, 'Encoding', 'number', 0, EncodingConverter()),
jvr155aa752002-05-17 19:58:49 +00001247 (18, 'Private', ('number','number'), None, PrivateDictConverter()),
jvred101512003-08-22 19:53:32 +00001248 ((12, 37), 'FDSelect', 'number', None, FDSelectConverter()),
1249 ((12, 36), 'FDArray', 'number', None, FDArrayConverter()),
jvr155aa752002-05-17 19:58:49 +00001250 (17, 'CharStrings', 'number', None, CharStringsConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001251]
1252
jvred101512003-08-22 19:53:32 +00001253# Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order,
1254# in order for the font to compile back from xml.
1255
1256
jvr4756b3a2002-05-16 18:17:32 +00001257privateDictOperators = [
1258# opcode name argument type default converter
1259 (6, 'BlueValues', 'delta', None, None),
1260 (7, 'OtherBlues', 'delta', None, None),
1261 (8, 'FamilyBlues', 'delta', None, None),
1262 (9, 'FamilyOtherBlues', 'delta', None, None),
1263 ((12, 9), 'BlueScale', 'number', 0.039625, None),
1264 ((12, 10), 'BlueShift', 'number', 7, None),
1265 ((12, 11), 'BlueFuzz', 'number', 1, None),
1266 (10, 'StdHW', 'number', None, None),
1267 (11, 'StdVW', 'number', None, None),
1268 ((12, 12), 'StemSnapH', 'delta', None, None),
1269 ((12, 13), 'StemSnapV', 'delta', None, None),
1270 ((12, 14), 'ForceBold', 'number', 0, None),
jvrf2cf9c52002-05-23 21:50:36 +00001271 ((12, 15), 'ForceBoldThreshold', 'number', None, None), # deprecated
1272 ((12, 16), 'lenIV', 'number', None, None), # deprecated
jvr4756b3a2002-05-16 18:17:32 +00001273 ((12, 17), 'LanguageGroup', 'number', 0, None),
1274 ((12, 18), 'ExpansionFactor', 'number', 0.06, None),
1275 ((12, 19), 'initialRandomSeed', 'number', 0, None),
1276 (20, 'defaultWidthX', 'number', 0, None),
1277 (21, 'nominalWidthX', 'number', 0, None),
1278 (19, 'Subrs', 'number', None, SubrsConverter()),
1279]
1280
jvr4e5af602002-05-24 09:58:04 +00001281def addConverters(table):
1282 for i in range(len(table)):
1283 op, name, arg, default, conv = table[i]
1284 if conv is not None:
1285 continue
1286 if arg in ("delta", "array"):
1287 conv = ArrayConverter()
1288 elif arg == "number":
1289 conv = NumberConverter()
1290 elif arg == "SID":
1291 conv = SimpleConverter()
1292 else:
1293 assert 0
1294 table[i] = op, name, arg, default, conv
1295
1296addConverters(privateDictOperators)
1297addConverters(topDictOperators)
1298
jvr4756b3a2002-05-16 18:17:32 +00001299
1300class TopDictDecompiler(psCharStrings.DictDecompiler):
1301 operators = buildOperatorDict(topDictOperators)
1302
1303
1304class PrivateDictDecompiler(psCharStrings.DictDecompiler):
1305 operators = buildOperatorDict(privateDictOperators)
1306
1307
jvrf2cf9c52002-05-23 21:50:36 +00001308class DictCompiler:
1309
1310 def __init__(self, dictObj, strings, parent):
1311 assert isinstance(strings, IndexedStrings)
1312 self.dictObj = dictObj
1313 self.strings = strings
1314 self.parent = parent
1315 rawDict = {}
1316 for name in dictObj.order:
1317 value = getattr(dictObj, name, None)
1318 if value is None:
1319 continue
1320 conv = dictObj.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001321 value = conv.write(dictObj, value)
jvrf2cf9c52002-05-23 21:50:36 +00001322 if value == dictObj.defaults.get(name):
1323 continue
1324 rawDict[name] = value
1325 self.rawDict = rawDict
1326
jvr4e5af602002-05-24 09:58:04 +00001327 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +00001328 pass
1329
1330 def getDataLength(self):
jvr4e5af602002-05-24 09:58:04 +00001331 return len(self.compile("getDataLength"))
jvrf2cf9c52002-05-23 21:50:36 +00001332
jvr4e5af602002-05-24 09:58:04 +00001333 def compile(self, reason):
1334 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001335 print("-- compiling %s for %s" % (self.__class__.__name__, reason))
1336 print("in baseDict: ", self)
jvrf2cf9c52002-05-23 21:50:36 +00001337 rawDict = self.rawDict
1338 data = []
1339 for name in self.dictObj.order:
1340 value = rawDict.get(name)
1341 if value is None:
1342 continue
1343 op, argType = self.opcodes[name]
jvr2a9bcde2008-03-07 19:56:17 +00001344 if isinstance(argType, tuple):
jvrf2cf9c52002-05-23 21:50:36 +00001345 l = len(argType)
1346 assert len(value) == l, "value doesn't match arg type"
1347 for i in range(l):
jvred101512003-08-22 19:53:32 +00001348 arg = argType[i]
jvrf2cf9c52002-05-23 21:50:36 +00001349 v = value[i]
1350 arghandler = getattr(self, "arg_" + arg)
1351 data.append(arghandler(v))
1352 else:
1353 arghandler = getattr(self, "arg_" + argType)
1354 data.append(arghandler(value))
1355 data.append(op)
1356 return "".join(data)
1357
1358 def toFile(self, file):
jvr4e5af602002-05-24 09:58:04 +00001359 file.write(self.compile("toFile"))
jvrf2cf9c52002-05-23 21:50:36 +00001360
1361 def arg_number(self, num):
1362 return encodeNumber(num)
1363 def arg_SID(self, s):
1364 return psCharStrings.encodeIntCFF(self.strings.getSID(s))
1365 def arg_array(self, value):
1366 data = []
1367 for num in value:
1368 data.append(encodeNumber(num))
1369 return "".join(data)
1370 def arg_delta(self, value):
1371 out = []
1372 last = 0
1373 for v in value:
1374 out.append(v - last)
1375 last = v
1376 data = []
1377 for num in out:
1378 data.append(encodeNumber(num))
1379 return "".join(data)
1380
1381
1382def encodeNumber(num):
jvrf6ff48b2008-03-07 19:49:25 +00001383 if isinstance(num, float):
jvrf2cf9c52002-05-23 21:50:36 +00001384 return psCharStrings.encodeFloat(num)
1385 else:
1386 return psCharStrings.encodeIntCFF(num)
1387
1388
1389class TopDictCompiler(DictCompiler):
1390
1391 opcodes = buildOpcodeDict(topDictOperators)
1392
1393 def getChildren(self, strings):
1394 children = []
jvred101512003-08-22 19:53:32 +00001395 if hasattr(self.dictObj, "charset") and self.dictObj.charset:
jvrf2cf9c52002-05-23 21:50:36 +00001396 children.append(CharsetCompiler(strings, self.dictObj.charset, self))
jvrb9702ba2003-01-03 20:56:01 +00001397 if hasattr(self.dictObj, "Encoding"):
1398 encoding = self.dictObj.Encoding
jvr2a9bcde2008-03-07 19:56:17 +00001399 if not isinstance(encoding, basestring):
jvrb9702ba2003-01-03 20:56:01 +00001400 children.append(EncodingCompiler(strings, encoding, self))
jvrce522412003-08-25 07:37:25 +00001401 if hasattr(self.dictObj, "FDSelect"):
jvred101512003-08-22 19:53:32 +00001402 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1403 # issues about merging the FDArrays. Here I assume that
1404 # either the font was read from XML, and teh FDSelect indices are all
1405 # in the charstring data, or the FDSelect array is already fully defined.
1406 fdSelect = self.dictObj.FDSelect
1407 if len(fdSelect) == 0: # probably read in from XML; assume fdIndex in CharString data
1408 charStrings = self.dictObj.CharStrings
1409 for name in self.dictObj.charset:
1410 charstring = charStrings[name]
1411 fdSelect.append(charStrings[name].fdSelectIndex)
1412 fdSelectComp = FDSelectCompiler(fdSelect, self)
1413 children.append(fdSelectComp)
jvrf2cf9c52002-05-23 21:50:36 +00001414 if hasattr(self.dictObj, "CharStrings"):
1415 items = []
1416 charStrings = self.dictObj.CharStrings
1417 for name in self.dictObj.charset:
1418 items.append(charStrings[name])
1419 charStringsComp = CharStringsCompiler(items, strings, self)
1420 children.append(charStringsComp)
jvrce522412003-08-25 07:37:25 +00001421 if hasattr(self.dictObj, "FDArray"):
jvred101512003-08-22 19:53:32 +00001422 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1423 # issues about merging the FDArrays. Here I assume that the FDArray info is correct
1424 # and complete.
1425 fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self)
1426 children.append(fdArrayIndexComp)
1427 children.extend(fdArrayIndexComp.getChildren(strings))
1428 if hasattr(self.dictObj, "Private"):
1429 privComp = self.dictObj.Private.getCompiler(strings, self)
1430 children.append(privComp)
1431 children.extend(privComp.getChildren(strings))
1432 return children
1433
1434
1435class FontDictCompiler(DictCompiler):
1436
1437 opcodes = buildOpcodeDict(topDictOperators)
1438
1439 def getChildren(self, strings):
1440 children = []
jvrf2cf9c52002-05-23 21:50:36 +00001441 if hasattr(self.dictObj, "Private"):
1442 privComp = self.dictObj.Private.getCompiler(strings, self)
1443 children.append(privComp)
1444 children.extend(privComp.getChildren(strings))
1445 return children
1446
1447
1448class PrivateDictCompiler(DictCompiler):
1449
1450 opcodes = buildOpcodeDict(privateDictOperators)
1451
jvr4e5af602002-05-24 09:58:04 +00001452 def setPos(self, pos, endPos):
1453 size = endPos - pos
jvrf2cf9c52002-05-23 21:50:36 +00001454 self.parent.rawDict["Private"] = size, pos
1455 self.pos = pos
1456
1457 def getChildren(self, strings):
1458 children = []
1459 if hasattr(self.dictObj, "Subrs"):
1460 children.append(self.dictObj.Subrs.getCompiler(strings, self))
1461 return children
1462
jvr4756b3a2002-05-16 18:17:32 +00001463
1464class BaseDict:
1465
jvr4e5af602002-05-24 09:58:04 +00001466 def __init__(self, strings=None, file=None, offset=None):
jvr4756b3a2002-05-16 18:17:32 +00001467 self.rawDict = {}
jvr767102e2002-05-17 07:06:32 +00001468 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001469 print("loading %s at %s" % (self.__class__.__name__, offset))
jvr4756b3a2002-05-16 18:17:32 +00001470 self.file = file
1471 self.offset = offset
1472 self.strings = strings
jvr155aa752002-05-17 19:58:49 +00001473 self.skipNames = []
jvr4756b3a2002-05-16 18:17:32 +00001474
1475 def decompile(self, data):
jvrf2cf9c52002-05-23 21:50:36 +00001476 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001477 print(" length %s is %s" % (self.__class__.__name__, len(data)))
jvrf2cf9c52002-05-23 21:50:36 +00001478 dec = self.decompilerClass(self.strings)
jvr4756b3a2002-05-16 18:17:32 +00001479 dec.decompile(data)
1480 self.rawDict = dec.getDict()
1481 self.postDecompile()
1482
1483 def postDecompile(self):
1484 pass
1485
jvrf2cf9c52002-05-23 21:50:36 +00001486 def getCompiler(self, strings, parent):
1487 return self.compilerClass(self, strings, parent)
1488
jvr4756b3a2002-05-16 18:17:32 +00001489 def __getattr__(self, name):
1490 value = self.rawDict.get(name)
1491 if value is None:
1492 value = self.defaults.get(name)
1493 if value is None:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -05001494 raise AttributeError(name)
jvr4756b3a2002-05-16 18:17:32 +00001495 conv = self.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001496 value = conv.read(self, value)
jvr4756b3a2002-05-16 18:17:32 +00001497 setattr(self, name, value)
1498 return value
1499
1500 def toXML(self, xmlWriter, progress):
1501 for name in self.order:
jvr155aa752002-05-17 19:58:49 +00001502 if name in self.skipNames:
1503 continue
jvr4756b3a2002-05-16 18:17:32 +00001504 value = getattr(self, name, None)
1505 if value is None:
1506 continue
jvr4e5af602002-05-24 09:58:04 +00001507 conv = self.converters[name]
jvr7ce0a132002-07-23 16:42:11 +00001508 conv.xmlWrite(xmlWriter, name, value, progress)
jvr4e5af602002-05-24 09:58:04 +00001509
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001510 def fromXML(self, name, attrs, content):
jvr4e5af602002-05-24 09:58:04 +00001511 conv = self.converters[name]
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001512 value = conv.xmlRead(name, attrs, content, self)
jvr4e5af602002-05-24 09:58:04 +00001513 setattr(self, name, value)
jvr4756b3a2002-05-16 18:17:32 +00001514
1515
1516class TopDict(BaseDict):
1517
1518 defaults = buildDefaults(topDictOperators)
1519 converters = buildConverters(topDictOperators)
1520 order = buildOrder(topDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001521 decompilerClass = TopDictDecompiler
1522 compilerClass = TopDictCompiler
Just7842e561999-12-16 21:34:53 +00001523
jvr4e5af602002-05-24 09:58:04 +00001524 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
jvr016ca762002-05-16 18:38:03 +00001525 BaseDict.__init__(self, strings, file, offset)
1526 self.GlobalSubrs = GlobalSubrs
1527
Just7842e561999-12-16 21:34:53 +00001528 def getGlyphOrder(self):
1529 return self.charset
1530
jvr4756b3a2002-05-16 18:17:32 +00001531 def postDecompile(self):
1532 offset = self.rawDict.get("CharStrings")
1533 if offset is None:
1534 return
1535 # get the number of glyphs beforehand.
1536 self.file.seek(offset)
jvra2ad5442002-05-17 18:36:07 +00001537 self.numGlyphs = readCard16(self.file)
Just7842e561999-12-16 21:34:53 +00001538
jvr016ca762002-05-16 18:38:03 +00001539 def toXML(self, xmlWriter, progress):
jvr155aa752002-05-17 19:58:49 +00001540 if hasattr(self, "CharStrings"):
jvr7ce0a132002-07-23 16:42:11 +00001541 self.decompileAllCharStrings(progress)
jvred101512003-08-22 19:53:32 +00001542 if hasattr(self, "ROS"):
1543 self.skipNames = ['Encoding']
jvr155aa752002-05-17 19:58:49 +00001544 if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
1545 # these values have default values, but I only want them to show up
1546 # in CID fonts.
1547 self.skipNames = ['CIDFontVersion', 'CIDFontRevision', 'CIDFontType',
1548 'CIDCount']
jvr016ca762002-05-16 18:38:03 +00001549 BaseDict.toXML(self, xmlWriter, progress)
jvr767102e2002-05-17 07:06:32 +00001550
jvr7ce0a132002-07-23 16:42:11 +00001551 def decompileAllCharStrings(self, progress):
jvr4e5af602002-05-24 09:58:04 +00001552 # XXX only when doing ttdump -i?
jvr7ce0a132002-07-23 16:42:11 +00001553 i = 0
jvra2ad5442002-05-17 18:36:07 +00001554 for charString in self.CharStrings.values():
jvred101512003-08-22 19:53:32 +00001555 try:
1556 charString.decompile()
1557 except:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001558 print("Error in charstring ", i)
jvred101512003-08-22 19:53:32 +00001559 import sys
1560 type, value = sys. exc_info()[0:2]
1561 raise type(value)
jvr7ce0a132002-07-23 16:42:11 +00001562 if not i % 30 and progress:
1563 progress.increment(0) # update
1564 i = i + 1
Just7842e561999-12-16 21:34:53 +00001565
1566
jvred101512003-08-22 19:53:32 +00001567class FontDict(BaseDict):
1568
1569 defaults = buildDefaults(topDictOperators)
1570 converters = buildConverters(topDictOperators)
1571 order = buildOrder(topDictOperators)
1572 decompilerClass = None
1573 compilerClass = FontDictCompiler
1574
1575 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
1576 BaseDict.__init__(self, strings, file, offset)
1577 self.GlobalSubrs = GlobalSubrs
1578
1579 def getGlyphOrder(self):
1580 return self.charset
1581
1582 def toXML(self, xmlWriter, progress):
1583 self.skipNames = ['Encoding']
1584 BaseDict.toXML(self, xmlWriter, progress)
1585
1586
1587
jvr4756b3a2002-05-16 18:17:32 +00001588class PrivateDict(BaseDict):
1589 defaults = buildDefaults(privateDictOperators)
1590 converters = buildConverters(privateDictOperators)
1591 order = buildOrder(privateDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001592 decompilerClass = PrivateDictDecompiler
1593 compilerClass = PrivateDictCompiler
Just7842e561999-12-16 21:34:53 +00001594
1595
jvre3275582002-05-14 12:22:03 +00001596class IndexedStrings:
1597
jvr767102e2002-05-17 07:06:32 +00001598 """SID -> string mapping."""
1599
1600 def __init__(self, file=None):
1601 if file is None:
jvre3275582002-05-14 12:22:03 +00001602 strings = []
jvr767102e2002-05-17 07:06:32 +00001603 else:
jvr4e5af602002-05-24 09:58:04 +00001604 strings = list(Index(file))
jvre3275582002-05-14 12:22:03 +00001605 self.strings = strings
1606
jvrf2cf9c52002-05-23 21:50:36 +00001607 def getCompiler(self):
1608 return IndexedStringsCompiler(self, None, None)
1609
1610 def __len__(self):
1611 return len(self.strings)
1612
jvre3275582002-05-14 12:22:03 +00001613 def __getitem__(self, SID):
1614 if SID < cffStandardStringCount:
1615 return cffStandardStrings[SID]
1616 else:
1617 return self.strings[SID - cffStandardStringCount]
1618
1619 def getSID(self, s):
1620 if not hasattr(self, "stringMapping"):
1621 self.buildStringMapping()
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -05001622 if s in cffStandardStringMapping:
jvre3275582002-05-14 12:22:03 +00001623 SID = cffStandardStringMapping[s]
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -05001624 elif s in self.stringMapping:
jvre3275582002-05-14 12:22:03 +00001625 SID = self.stringMapping[s]
1626 else:
1627 SID = len(self.strings) + cffStandardStringCount
1628 self.strings.append(s)
1629 self.stringMapping[s] = SID
1630 return SID
1631
1632 def getStrings(self):
1633 return self.strings
1634
1635 def buildStringMapping(self):
1636 self.stringMapping = {}
1637 for index in range(len(self.strings)):
1638 self.stringMapping[self.strings[index]] = index + cffStandardStringCount
1639
1640
Just7842e561999-12-16 21:34:53 +00001641# The 391 Standard Strings as used in the CFF format.
1642# from Adobe Technical None #5176, version 1.0, 18 March 1998
1643
1644cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
1645 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
1646 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
1647 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
1648 'semicolon', 'less', 'equal', 'greater', 'question', 'at', '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', 'bracketleft', 'backslash',
1651 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
1652 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
1653 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
1654 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
1655 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
1656 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
1657 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
1658 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
1659 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
1660 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
1661 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
1662 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
1663 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
1664 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
1665 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
1666 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
1667 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
1668 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
1669 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
1670 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
1671 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
1672 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
1673 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
1674 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
1675 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
1676 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
1677 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
1678 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
1679 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
1680 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
1681 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
1682 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
1683 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
1684 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
1685 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
1686 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
1687 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
1688 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
1689 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
1690 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
1691 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
1692 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
1693 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
1694 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
1695 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
1696 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
1697 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
1698 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
1699 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
1700 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
1701 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
1702 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
1703 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
1704 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
1705 '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
1706 'Semibold'
1707]
1708
1709cffStandardStringCount = 391
1710assert len(cffStandardStrings) == cffStandardStringCount
1711# build reverse mapping
1712cffStandardStringMapping = {}
1713for _i in range(cffStandardStringCount):
1714 cffStandardStringMapping[cffStandardStrings[_i]] = _i
jvrc60a44f2006-10-21 13:41:18 +00001715
1716cffISOAdobeStrings = [".notdef", "space", "exclam", "quotedbl", "numbersign",
1717"dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright",
1718"asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two",
1719"three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon",
1720"less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G",
1721"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
1722"X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum",
1723"underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
1724"k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
1725"braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent",
1726"sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle",
1727"quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl",
1728"endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet",
1729"quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis",
1730"perthousand", "questiondown", "grave", "acute", "circumflex", "tilde",
1731"macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
1732"ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE",
1733"ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls",
1734"onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus",
1735"Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn",
1736"threequarters", "twosuperior", "registered", "minus", "eth", "multiply",
1737"threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
1738"Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave",
1739"Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute",
1740"Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute",
1741"Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute",
1742"acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute",
1743"ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis",
1744"igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde",
1745"scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis",
1746"zcaron"]
1747
1748cffISOAdobeStringCount = 229
1749assert len(cffISOAdobeStrings) == cffISOAdobeStringCount
1750
1751cffIExpertStrings = [".notdef", "space", "exclamsmall", "Hungarumlautsmall",
1752"dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall",
1753"parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader",
1754"comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle",
1755"twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle",
1756"sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon",
1757"commasuperior", "threequartersemdash", "periodsuperior", "questionsmall",
1758"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1759"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1760"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1761"parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
1762"Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall",
1763"Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
1764"Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall",
1765"Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall",
1766"exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall",
1767"Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall",
1768"figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall",
1769"onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth",
1770"threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds",
1771"zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior",
1772"fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior",
1773"zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior",
1774"fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior",
1775"centinferior", "dollarinferior", "periodinferior", "commainferior",
1776"Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall",
1777"Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall",
1778"Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall",
1779"Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall",
1780"Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall",
1781"Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall",
1782"Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall",
1783"Ydieresissmall"]
1784
1785cffExpertStringCount = 166
1786assert len(cffIExpertStrings) == cffExpertStringCount
1787
1788cffExpertSubsetStrings = [".notdef", "space", "dollaroldstyle",
1789"dollarsuperior", "parenleftsuperior", "parenrightsuperior", "twodotenleader",
1790"onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle",
1791"oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle",
1792"sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon",
1793"semicolon", "commasuperior", "threequartersemdash", "periodsuperior",
1794"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1795"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1796"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1797"parenrightinferior", "hyphensuperior", "colonmonetary", "onefitted", "rupiah",
1798"centoldstyle", "figuredash", "hypheninferior", "onequarter", "onehalf",
1799"threequarters", "oneeighth", "threeeighths", "fiveeighths", "seveneighths",
1800"onethird", "twothirds", "zerosuperior", "onesuperior", "twosuperior",
1801"threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior",
1802"eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior",
1803"threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior",
1804"eightinferior", "nineinferior", "centinferior", "dollarinferior",
1805"periodinferior", "commainferior"]
1806
1807cffExpertSubsetStringCount = 87
1808assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount