blob: 2bccee6e85150ea2307b97fa20a6448bdae77f8a [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
jvr767102e2002-05-17 07:06:32 +000012DEBUG = 0
13
14
Just7842e561999-12-16 21:34:53 +000015cffHeaderFormat = """
16 major: B
17 minor: B
18 hdrSize: B
19 offSize: B
20"""
21
22class CFFFontSet:
23
24 def __init__(self):
jvr4756b3a2002-05-16 18:17:32 +000025 pass
Just7842e561999-12-16 21:34:53 +000026
jvr4e5af602002-05-24 09:58:04 +000027 def decompile(self, file, otFont):
jvra2a75b32002-05-13 11:25:17 +000028 sstruct.unpack(cffHeaderFormat, file.read(4), self)
Just7842e561999-12-16 21:34:53 +000029 assert self.major == 1 and self.minor == 0, \
30 "unknown CFF format: %d.%d" % (self.major, self.minor)
Just7842e561999-12-16 21:34:53 +000031
jvrf2cf9c52002-05-23 21:50:36 +000032 file.seek(self.hdrSize)
jvr4e5af602002-05-24 09:58:04 +000033 self.fontNames = list(Index(file))
jvr4756b3a2002-05-16 18:17:32 +000034 self.topDictIndex = TopDictIndex(file)
jvr767102e2002-05-17 07:06:32 +000035 self.strings = IndexedStrings(file)
jvr4e5af602002-05-24 09:58:04 +000036 self.GlobalSubrs = GlobalSubrsIndex(file)
jvr4756b3a2002-05-16 18:17:32 +000037 self.topDictIndex.strings = self.strings
jvr016ca762002-05-16 18:38:03 +000038 self.topDictIndex.GlobalSubrs = self.GlobalSubrs
jvr4756b3a2002-05-16 18:17:32 +000039
40 def __len__(self):
41 return len(self.fontNames)
42
43 def keys(self):
jvrce522412003-08-25 07:37:25 +000044 return list(self.fontNames)
jvr4756b3a2002-05-16 18:17:32 +000045
jvr767102e2002-05-17 07:06:32 +000046 def values(self):
47 return self.topDictIndex
48
jvr4756b3a2002-05-16 18:17:32 +000049 def __getitem__(self, name):
50 try:
51 index = self.fontNames.index(name)
52 except ValueError:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -050053 raise KeyError(name)
jvr016ca762002-05-16 18:38:03 +000054 return self.topDictIndex[index]
Just7842e561999-12-16 21:34:53 +000055
jvr4e5af602002-05-24 09:58:04 +000056 def compile(self, file, otFont):
Just7842e561999-12-16 21:34:53 +000057 strings = IndexedStrings()
jvrf2cf9c52002-05-23 21:50:36 +000058 writer = CFFWriter()
59 writer.add(sstruct.pack(cffHeaderFormat, self))
60 fontNames = Index()
61 for name in self.fontNames:
62 fontNames.append(name)
63 writer.add(fontNames.getCompiler(strings, None))
64 topCompiler = self.topDictIndex.getCompiler(strings, None)
65 writer.add(topCompiler)
66 writer.add(strings.getCompiler())
67 writer.add(self.GlobalSubrs.getCompiler(strings, None))
68
jvr4e5af602002-05-24 09:58:04 +000069 for topDict in self.topDictIndex:
70 if not hasattr(topDict, "charset") or topDict.charset is None:
71 charset = otFont.getGlyphOrder()
72 topDict.charset = charset
73
jvrf2cf9c52002-05-23 21:50:36 +000074 for child in topCompiler.getChildren(strings):
75 writer.add(child)
76
jvrf2cf9c52002-05-23 21:50:36 +000077 writer.toFile(file)
Just7842e561999-12-16 21:34:53 +000078
79 def toXML(self, xmlWriter, progress=None):
80 xmlWriter.newline()
81 for fontName in self.fontNames:
82 xmlWriter.begintag("CFFFont", name=fontName)
83 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +000084 font = self[fontName]
Just7842e561999-12-16 21:34:53 +000085 font.toXML(xmlWriter, progress)
86 xmlWriter.endtag("CFFFont")
87 xmlWriter.newline()
88 xmlWriter.newline()
89 xmlWriter.begintag("GlobalSubrs")
90 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +000091 self.GlobalSubrs.toXML(xmlWriter, progress)
Just7842e561999-12-16 21:34:53 +000092 xmlWriter.endtag("GlobalSubrs")
93 xmlWriter.newline()
94 xmlWriter.newline()
95
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050096 def fromXML(self, name, attrs, content):
jvr4e5af602002-05-24 09:58:04 +000097 if not hasattr(self, "GlobalSubrs"):
98 self.GlobalSubrs = GlobalSubrsIndex()
99 self.major = 1
100 self.minor = 0
101 self.hdrSize = 4
102 self.offSize = 4 # XXX ??
103 if name == "CFFFont":
104 if not hasattr(self, "fontNames"):
105 self.fontNames = []
106 self.topDictIndex = TopDictIndex()
107 fontName = attrs["name"]
108 topDict = TopDict(GlobalSubrs=self.GlobalSubrs)
109 topDict.charset = None # gets filled in later
110 self.fontNames.append(fontName)
111 self.topDictIndex.append(topDict)
112 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000113 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000114 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500115 name, attrs, content = element
116 topDict.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000117 elif name == "GlobalSubrs":
118 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000119 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000120 continue
121 name, attrs, content = element
jvr489d76a2003-08-24 19:56:16 +0000122 subr = psCharStrings.T2CharString()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500123 subr.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000124 self.GlobalSubrs.append(subr)
Just7842e561999-12-16 21:34:53 +0000125
126
jvrf2cf9c52002-05-23 21:50:36 +0000127class CFFWriter:
128
129 def __init__(self):
130 self.data = []
131
132 def add(self, table):
133 self.data.append(table)
134
135 def toFile(self, file):
136 lastPosList = None
137 count = 1
Behdad Esfahbodac1b4352013-11-27 04:15:34 -0500138 while True:
jvr4e5af602002-05-24 09:58:04 +0000139 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500140 print("CFFWriter.toFile() iteration:", count)
jvr4e5af602002-05-24 09:58:04 +0000141 count = count + 1
jvrf2cf9c52002-05-23 21:50:36 +0000142 pos = 0
143 posList = [pos]
144 for item in self.data:
jvrf2cf9c52002-05-23 21:50:36 +0000145 if hasattr(item, "getDataLength"):
jvr4e5af602002-05-24 09:58:04 +0000146 endPos = pos + item.getDataLength()
jvrf2cf9c52002-05-23 21:50:36 +0000147 else:
jvr4e5af602002-05-24 09:58:04 +0000148 endPos = pos + len(item)
149 if hasattr(item, "setPos"):
150 item.setPos(pos, endPos)
151 pos = endPos
jvrf2cf9c52002-05-23 21:50:36 +0000152 posList.append(pos)
153 if posList == lastPosList:
154 break
155 lastPosList = posList
jvr4e5af602002-05-24 09:58:04 +0000156 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500157 print("CFFWriter.toFile() writing to file.")
jvrf2cf9c52002-05-23 21:50:36 +0000158 begin = file.tell()
159 posList = [0]
160 for item in self.data:
161 if hasattr(item, "toFile"):
162 item.toFile(file)
163 else:
164 file.write(item)
165 posList.append(file.tell() - begin)
jvrf2cf9c52002-05-23 21:50:36 +0000166 assert posList == lastPosList
167
168
169def calcOffSize(largestOffset):
170 if largestOffset < 0x100:
171 offSize = 1
172 elif largestOffset < 0x10000:
173 offSize = 2
174 elif largestOffset < 0x1000000:
175 offSize = 3
176 else:
177 offSize = 4
178 return offSize
179
180
181class IndexCompiler:
182
183 def __init__(self, items, strings, parent):
184 self.items = self.getItems(items, strings)
185 self.parent = parent
186
187 def getItems(self, items, strings):
188 return items
189
190 def getOffsets(self):
191 pos = 1
192 offsets = [pos]
193 for item in self.items:
194 if hasattr(item, "getDataLength"):
195 pos = pos + item.getDataLength()
196 else:
197 pos = pos + len(item)
198 offsets.append(pos)
199 return offsets
200
201 def getDataLength(self):
202 lastOffset = self.getOffsets()[-1]
203 offSize = calcOffSize(lastOffset)
204 dataLength = (
205 2 + # count
206 1 + # offSize
207 (len(self.items) + 1) * offSize + # the offsets
208 lastOffset - 1 # size of object data
209 )
210 return dataLength
211
212 def toFile(self, file):
jvrf2cf9c52002-05-23 21:50:36 +0000213 offsets = self.getOffsets()
214 writeCard16(file, len(self.items))
215 offSize = calcOffSize(offsets[-1])
216 writeCard8(file, offSize)
217 offSize = -offSize
218 pack = struct.pack
219 for offset in offsets:
220 binOffset = pack(">l", offset)[offSize:]
221 assert len(binOffset) == -offSize
222 file.write(binOffset)
223 for item in self.items:
224 if hasattr(item, "toFile"):
225 item.toFile(file)
226 else:
227 file.write(item)
jvrf2cf9c52002-05-23 21:50:36 +0000228
229
230class IndexedStringsCompiler(IndexCompiler):
231
232 def getItems(self, items, strings):
233 return items.strings
234
235
236class TopDictIndexCompiler(IndexCompiler):
237
238 def getItems(self, items, strings):
239 out = []
240 for item in items:
241 out.append(item.getCompiler(strings, self))
242 return out
243
244 def getChildren(self, strings):
245 children = []
246 for topDict in self.items:
247 children.extend(topDict.getChildren(strings))
248 return children
249
250
jvred101512003-08-22 19:53:32 +0000251class FDArrayIndexCompiler(IndexCompiler):
252
253 def getItems(self, items, strings):
254 out = []
255 for item in items:
256 out.append(item.getCompiler(strings, self))
257 return out
258
259 def getChildren(self, strings):
260 children = []
261 for fontDict in self.items:
262 children.extend(fontDict.getChildren(strings))
263 return children
264
jvred101512003-08-22 19:53:32 +0000265 def toFile(self, file):
266 offsets = self.getOffsets()
267 writeCard16(file, len(self.items))
268 offSize = calcOffSize(offsets[-1])
269 writeCard8(file, offSize)
270 offSize = -offSize
271 pack = struct.pack
272 for offset in offsets:
273 binOffset = pack(">l", offset)[offSize:]
274 assert len(binOffset) == -offSize
275 file.write(binOffset)
276 for item in self.items:
277 if hasattr(item, "toFile"):
278 item.toFile(file)
279 else:
280 file.write(item)
281
282 def setPos(self, pos, endPos):
283 self.parent.rawDict["FDArray"] = pos
284
285
jvrf2cf9c52002-05-23 21:50:36 +0000286class GlobalSubrsCompiler(IndexCompiler):
287 def getItems(self, items, strings):
288 out = []
289 for cs in items:
290 cs.compile()
291 out.append(cs.bytecode)
292 return out
293
294class SubrsCompiler(GlobalSubrsCompiler):
jvr4e5af602002-05-24 09:58:04 +0000295 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000296 offset = pos - self.parent.pos
297 self.parent.rawDict["Subrs"] = offset
298
299class CharStringsCompiler(GlobalSubrsCompiler):
jvr4e5af602002-05-24 09:58:04 +0000300 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000301 self.parent.rawDict["CharStrings"] = pos
302
303
jvr4756b3a2002-05-16 18:17:32 +0000304class Index:
Just7842e561999-12-16 21:34:53 +0000305
jvr4756b3a2002-05-16 18:17:32 +0000306 """This class represents what the CFF spec calls an INDEX."""
Just7842e561999-12-16 21:34:53 +0000307
jvrf2cf9c52002-05-23 21:50:36 +0000308 compilerClass = IndexCompiler
309
jvr4e5af602002-05-24 09:58:04 +0000310 def __init__(self, file=None):
311 name = self.__class__.__name__
jvrf2cf9c52002-05-23 21:50:36 +0000312 if file is None:
313 self.items = []
314 return
jvr767102e2002-05-17 07:06:32 +0000315 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500316 print("loading %s at %s" % (name, file.tell()))
jvr4756b3a2002-05-16 18:17:32 +0000317 self.file = file
jvra2ad5442002-05-17 18:36:07 +0000318 count = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000319 self.count = count
320 self.items = [None] * count
321 if count == 0:
jvrf2cf9c52002-05-23 21:50:36 +0000322 self.items = []
jvr4756b3a2002-05-16 18:17:32 +0000323 return
jvra2ad5442002-05-17 18:36:07 +0000324 offSize = readCard8(file)
jvr767102e2002-05-17 07:06:32 +0000325 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500326 print(" index count: %s offSize: %s" % (count, offSize))
jvr767102e2002-05-17 07:06:32 +0000327 assert offSize <= 4, "offSize too large: %s" % offSize
jvr4756b3a2002-05-16 18:17:32 +0000328 self.offsets = offsets = []
329 pad = '\0' * (4 - offSize)
330 for index in range(count+1):
331 chunk = file.read(offSize)
332 chunk = pad + chunk
333 offset, = struct.unpack(">L", chunk)
334 offsets.append(int(offset))
335 self.offsetBase = file.tell() - 1
336 file.seek(self.offsetBase + offsets[-1]) # pretend we've read the whole lot
jvrf2cf9c52002-05-23 21:50:36 +0000337 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500338 print(" end of %s at %s" % (name, file.tell()))
Just7842e561999-12-16 21:34:53 +0000339
jvr4756b3a2002-05-16 18:17:32 +0000340 def __len__(self):
jvrf2cf9c52002-05-23 21:50:36 +0000341 return len(self.items)
jvra2a75b32002-05-13 11:25:17 +0000342
jvr4756b3a2002-05-16 18:17:32 +0000343 def __getitem__(self, index):
344 item = self.items[index]
345 if item is not None:
346 return item
347 offset = self.offsets[index] + self.offsetBase
348 size = self.offsets[index+1] - self.offsets[index]
349 file = self.file
350 file.seek(offset)
351 data = file.read(size)
352 assert len(data) == size
jvra2ad5442002-05-17 18:36:07 +0000353 item = self.produceItem(index, data, file, offset, size)
jvr4756b3a2002-05-16 18:17:32 +0000354 self.items[index] = item
355 return item
356
jvra2ad5442002-05-17 18:36:07 +0000357 def produceItem(self, index, data, file, offset, size):
jvr4756b3a2002-05-16 18:17:32 +0000358 return data
jvr4756b3a2002-05-16 18:17:32 +0000359
jvrf2cf9c52002-05-23 21:50:36 +0000360 def append(self, item):
361 self.items.append(item)
362
363 def getCompiler(self, strings, parent):
364 return self.compilerClass(self, strings, parent)
365
366
367class GlobalSubrsIndex(Index):
368
369 compilerClass = GlobalSubrsCompiler
370
jvr4e5af602002-05-24 09:58:04 +0000371 def __init__(self, file=None, globalSubrs=None, private=None, fdSelect=None, fdArray=None):
372 Index.__init__(self, file)
jvra2ad5442002-05-17 18:36:07 +0000373 self.globalSubrs = globalSubrs
374 self.private = private
jvred101512003-08-22 19:53:32 +0000375 if fdSelect:
376 self.fdSelect = fdSelect
377 if fdArray:
378 self.fdArray = fdArray
jvra2ad5442002-05-17 18:36:07 +0000379
380 def produceItem(self, index, data, file, offset, size):
381 if self.private is not None:
382 private = self.private
jvred101512003-08-22 19:53:32 +0000383 elif hasattr(self, 'fdArray') and self.fdArray is not None:
jvra2ad5442002-05-17 18:36:07 +0000384 private = self.fdArray[self.fdSelect[index]].Private
385 else:
386 private = None
jvr489d76a2003-08-24 19:56:16 +0000387 return psCharStrings.T2CharString(data, private=private, globalSubrs=self.globalSubrs)
jvr4756b3a2002-05-16 18:17:32 +0000388
389 def toXML(self, xmlWriter, progress):
jvred101512003-08-22 19:53:32 +0000390 xmlWriter.comment("The 'index' attribute is only for humans; it is ignored when parsed.")
jvr4e5af602002-05-24 09:58:04 +0000391 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +0000392 for i in range(len(self)):
jvrb58176e2002-05-24 11:55:37 +0000393 subr = self[i]
394 if subr.needsDecompilation():
395 xmlWriter.begintag("CharString", index=i, raw=1)
396 else:
397 xmlWriter.begintag("CharString", index=i)
jvr4756b3a2002-05-16 18:17:32 +0000398 xmlWriter.newline()
jvrb58176e2002-05-24 11:55:37 +0000399 subr.toXML(xmlWriter)
jvr4756b3a2002-05-16 18:17:32 +0000400 xmlWriter.endtag("CharString")
401 xmlWriter.newline()
jvra2ad5442002-05-17 18:36:07 +0000402
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500403 def fromXML(self, name, attrs, content):
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500404 if name != "CharString":
jvr4e5af602002-05-24 09:58:04 +0000405 return
jvr489d76a2003-08-24 19:56:16 +0000406 subr = psCharStrings.T2CharString()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500407 subr.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000408 self.append(subr)
409
jvra2ad5442002-05-17 18:36:07 +0000410 def getItemAndSelector(self, index):
jvred101512003-08-22 19:53:32 +0000411 sel = None
412 if hasattr(self, 'fdSelect'):
413 sel = self.fdSelect[index]
jvra2ad5442002-05-17 18:36:07 +0000414 return self[index], sel
jvrf2cf9c52002-05-23 21:50:36 +0000415
jvre2ca9b52002-09-09 14:18:39 +0000416
jvrf2cf9c52002-05-23 21:50:36 +0000417class SubrsIndex(GlobalSubrsIndex):
418 compilerClass = SubrsCompiler
419
jvr4756b3a2002-05-16 18:17:32 +0000420
jvr767102e2002-05-17 07:06:32 +0000421class TopDictIndex(Index):
jvra2ad5442002-05-17 18:36:07 +0000422
jvrf2cf9c52002-05-23 21:50:36 +0000423 compilerClass = TopDictIndexCompiler
424
jvra2ad5442002-05-17 18:36:07 +0000425 def produceItem(self, index, data, file, offset, size):
jvr767102e2002-05-17 07:06:32 +0000426 top = TopDict(self.strings, file, offset, self.GlobalSubrs)
427 top.decompile(data)
428 return top
jvra2ad5442002-05-17 18:36:07 +0000429
430 def toXML(self, xmlWriter, progress):
431 for i in range(len(self)):
432 xmlWriter.begintag("FontDict", index=i)
433 xmlWriter.newline()
434 self[i].toXML(xmlWriter, progress)
435 xmlWriter.endtag("FontDict")
436 xmlWriter.newline()
jvr767102e2002-05-17 07:06:32 +0000437
438
jvred101512003-08-22 19:53:32 +0000439class FDArrayIndex(TopDictIndex):
440
441 compilerClass = FDArrayIndexCompiler
442
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500443 def fromXML(self, name, attrs, content):
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500444 if name != "FontDict":
jvred101512003-08-22 19:53:32 +0000445 return
446 fontDict = FontDict()
447 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000448 if isinstance(element, basestring):
jvred101512003-08-22 19:53:32 +0000449 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500450 name, attrs, content = element
451 fontDict.fromXML(name, attrs, content)
jvred101512003-08-22 19:53:32 +0000452 self.append(fontDict)
453
454
455class FDSelect:
456 def __init__(self, file = None, numGlyphs = None, format=None):
457 if file:
458 # read data in from file
459 self.format = readCard8(file)
460 if self.format == 0:
461 from array import array
462 self.gidArray = array("B", file.read(numGlyphs)).tolist()
463 elif self.format == 3:
464 gidArray = [None] * numGlyphs
465 nRanges = readCard16(file)
466 prev = None
467 for i in range(nRanges):
468 first = readCard16(file)
469 if prev is not None:
470 for glyphID in range(prev, first):
471 gidArray[glyphID] = fd
472 prev = first
473 fd = readCard8(file)
474 if prev is not None:
475 first = readCard16(file)
476 for glyphID in range(prev, first):
477 gidArray[glyphID] = fd
478 self.gidArray = gidArray
479 else:
480 assert 0, "unsupported FDSelect format: %s" % format
481 else:
482 # reading from XML. Make empty gidArray,, and leave format as passed in.
483 # format == None will result in the smallest representation being used.
484 self.format = format
485 self.gidArray = []
486
487
488 def __len__(self):
489 return len(self.gidArray)
490
491 def __getitem__(self, index):
492 return self.gidArray[index]
493
494 def __setitem__(self, index, fdSelectValue):
495 self.gidArray[index] = fdSelectValue
496
497 def append(self, fdSelectValue):
498 self.gidArray.append(fdSelectValue)
499
500
jvr4756b3a2002-05-16 18:17:32 +0000501class CharStrings:
502
jvra2ad5442002-05-17 18:36:07 +0000503 def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray):
jvr4e5af602002-05-24 09:58:04 +0000504 if file is not None:
505 self.charStringsIndex = SubrsIndex(file, globalSubrs, private, fdSelect, fdArray)
506 self.charStrings = charStrings = {}
507 for i in range(len(charset)):
508 charStrings[charset[i]] = i
509 self.charStringsAreIndexed = 1
510 else:
511 self.charStrings = {}
512 self.charStringsAreIndexed = 0
513 self.globalSubrs = globalSubrs
514 self.private = private
jvred101512003-08-22 19:53:32 +0000515 if fdSelect != None:
516 self.fdSelect = fdSelect
517 if fdArray!= None:
518 self.fdArray = fdArray
jvr4756b3a2002-05-16 18:17:32 +0000519
520 def keys(self):
jvr4e5af602002-05-24 09:58:04 +0000521 return self.charStrings.keys()
jvr4756b3a2002-05-16 18:17:32 +0000522
jvr016ca762002-05-16 18:38:03 +0000523 def values(self):
jvr4e5af602002-05-24 09:58:04 +0000524 if self.charStringsAreIndexed:
525 return self.charStringsIndex
526 else:
527 return self.charStrings.values()
jvr016ca762002-05-16 18:38:03 +0000528
jvr4756b3a2002-05-16 18:17:32 +0000529 def has_key(self, name):
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500530 return name in self.charStrings
jvr4756b3a2002-05-16 18:17:32 +0000531
jvr767102e2002-05-17 07:06:32 +0000532 def __len__(self):
jvr4e5af602002-05-24 09:58:04 +0000533 return len(self.charStrings)
jvr767102e2002-05-17 07:06:32 +0000534
jvr4756b3a2002-05-16 18:17:32 +0000535 def __getitem__(self, name):
jvr4e5af602002-05-24 09:58:04 +0000536 charString = self.charStrings[name]
537 if self.charStringsAreIndexed:
538 charString = self.charStringsIndex[charString]
539 return charString
540
541 def __setitem__(self, name, charString):
542 if self.charStringsAreIndexed:
543 index = self.charStrings[name]
544 self.charStringsIndex[index] = charString
545 else:
546 self.charStrings[name] = charString
jvr4756b3a2002-05-16 18:17:32 +0000547
jvra2ad5442002-05-17 18:36:07 +0000548 def getItemAndSelector(self, name):
jvr4e5af602002-05-24 09:58:04 +0000549 if self.charStringsAreIndexed:
550 index = self.charStrings[name]
551 return self.charStringsIndex.getItemAndSelector(index)
552 else:
jvred101512003-08-22 19:53:32 +0000553 if hasattr(self, 'fdSelect'):
jvr91bca422012-10-18 12:49:22 +0000554 sel = self.fdSelect[index] # index is not defined at this point. Read R. ?
jvred101512003-08-22 19:53:32 +0000555 else:
556 raise KeyError("fdSelect array not yet defined.")
557 return self.charStrings[name], sel
jvra2ad5442002-05-17 18:36:07 +0000558
jvr4756b3a2002-05-16 18:17:32 +0000559 def toXML(self, xmlWriter, progress):
Behdad Esfahbodac1b4352013-11-27 04:15:34 -0500560 names = sorted(self.keys())
jvr7ce0a132002-07-23 16:42:11 +0000561 i = 0
562 step = 10
563 numGlyphs = len(names)
jvr4756b3a2002-05-16 18:17:32 +0000564 for name in names:
jvred101512003-08-22 19:53:32 +0000565 charStr, fdSelectIndex = self.getItemAndSelector(name)
jvrb58176e2002-05-24 11:55:37 +0000566 if charStr.needsDecompilation():
567 raw = [("raw", 1)]
568 else:
569 raw = []
jvred101512003-08-22 19:53:32 +0000570 if fdSelectIndex is None:
jvrb58176e2002-05-24 11:55:37 +0000571 xmlWriter.begintag("CharString", [('name', name)] + raw)
jvra2ad5442002-05-17 18:36:07 +0000572 else:
573 xmlWriter.begintag("CharString",
jvred101512003-08-22 19:53:32 +0000574 [('name', name), ('fdSelectIndex', fdSelectIndex)] + raw)
jvr4756b3a2002-05-16 18:17:32 +0000575 xmlWriter.newline()
jvrb58176e2002-05-24 11:55:37 +0000576 charStr.toXML(xmlWriter)
jvr4756b3a2002-05-16 18:17:32 +0000577 xmlWriter.endtag("CharString")
578 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000579 if not i % step and progress is not None:
580 progress.setLabel("Dumping 'CFF ' table... (%s)" % name)
581 progress.increment(step / float(numGlyphs))
582 i = i + 1
jvr4e5af602002-05-24 09:58:04 +0000583
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500584 def fromXML(self, name, attrs, content):
jvr4e5af602002-05-24 09:58:04 +0000585 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000586 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000587 continue
588 name, attrs, content = element
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500589 if name != "CharString":
jvr4e5af602002-05-24 09:58:04 +0000590 continue
jvred101512003-08-22 19:53:32 +0000591 fdID = -1
592 if hasattr(self, "fdArray"):
593 fdID = safeEval(attrs["fdSelectIndex"])
594 private = self.fdArray[fdID].Private
595 else:
596 private = self.private
597
jvr4e5af602002-05-24 09:58:04 +0000598 glyphName = attrs["name"]
jvr489d76a2003-08-24 19:56:16 +0000599 charString = psCharStrings.T2CharString(
600 private=private,
601 globalSubrs=self.globalSubrs)
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500602 charString.fromXML(name, attrs, content)
jvred101512003-08-22 19:53:32 +0000603 if fdID >= 0:
604 charString.fdSelectIndex = fdID
jvr4e5af602002-05-24 09:58:04 +0000605 self[glyphName] = charString
jvr4756b3a2002-05-16 18:17:32 +0000606
607
jvra2ad5442002-05-17 18:36:07 +0000608def readCard8(file):
609 return ord(file.read(1))
610
611def readCard16(file):
612 value, = struct.unpack(">H", file.read(2))
613 return value
614
jvrf2cf9c52002-05-23 21:50:36 +0000615def writeCard8(file, value):
616 file.write(chr(value))
617
618def writeCard16(file, value):
619 file.write(struct.pack(">H", value))
620
621def packCard8(value):
622 return chr(value)
623
624def packCard16(value):
625 return struct.pack(">H", value)
626
jvr4756b3a2002-05-16 18:17:32 +0000627def buildOperatorDict(table):
628 d = {}
629 for op, name, arg, default, conv in table:
630 d[op] = (name, arg)
631 return d
632
jvrf2cf9c52002-05-23 21:50:36 +0000633def buildOpcodeDict(table):
634 d = {}
635 for op, name, arg, default, conv in table:
jvr2a9bcde2008-03-07 19:56:17 +0000636 if isinstance(op, tuple):
jvrf2cf9c52002-05-23 21:50:36 +0000637 op = chr(op[0]) + chr(op[1])
638 else:
639 op = chr(op)
640 d[name] = (op, arg)
641 return d
642
jvr4756b3a2002-05-16 18:17:32 +0000643def buildOrder(table):
644 l = []
645 for op, name, arg, default, conv in table:
646 l.append(name)
647 return l
648
649def buildDefaults(table):
650 d = {}
651 for op, name, arg, default, conv in table:
652 if default is not None:
653 d[name] = default
654 return d
655
656def buildConverters(table):
657 d = {}
658 for op, name, arg, default, conv in table:
659 d[name] = conv
660 return d
661
662
jvr4e5af602002-05-24 09:58:04 +0000663class SimpleConverter:
jvr7ce02ea2002-05-17 20:04:05 +0000664 def read(self, parent, value):
665 return value
jvrf2cf9c52002-05-23 21:50:36 +0000666 def write(self, parent, value):
667 return value
jvr7ce0a132002-07-23 16:42:11 +0000668 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000669 xmlWriter.simpletag(name, value=value)
670 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500671 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000672 return attrs["value"]
673
jvre2ca9b52002-09-09 14:18:39 +0000674class Latin1Converter(SimpleConverter):
Behdad Esfahbod8c5c9662013-10-28 13:20:00 +0100675 def xmlWrite(self, xmlWriter, name, value, progress):
676 # Store as UTF-8
677 value = unicode(value, "latin-1").encode("utf-8")
678 xmlWriter.simpletag(name, value=value)
679 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500680 def xmlRead(self, name, attrs, content, parent):
jvre2ca9b52002-09-09 14:18:39 +0000681 s = unicode(attrs["value"], "utf-8")
682 return s.encode("latin-1")
683
684
jvr4e5af602002-05-24 09:58:04 +0000685def parseNum(s):
686 try:
687 value = int(s)
688 except:
689 value = float(s)
690 return value
691
692class NumberConverter(SimpleConverter):
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500693 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000694 return parseNum(attrs["value"])
695
696class ArrayConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000697 def xmlWrite(self, xmlWriter, name, value, progress):
Behdad Esfahbode5ca7962013-11-27 04:38:16 -0500698 value = " ".join(map(str, value))
699 xmlWriter.simpletag(name, value=value)
jvr4e5af602002-05-24 09:58:04 +0000700 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500701 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000702 values = attrs["value"].split()
Behdad Esfahbode5ca7962013-11-27 04:38:16 -0500703 return [parseNum(value) for value in values]
jvr4e5af602002-05-24 09:58:04 +0000704
705class TableConverter(SimpleConverter):
jvr7ce0a132002-07-23 16:42:11 +0000706 def xmlWrite(self, xmlWriter, name, value, progress):
jvra2ad5442002-05-17 18:36:07 +0000707 xmlWriter.begintag(name)
708 xmlWriter.newline()
jvr7ce0a132002-07-23 16:42:11 +0000709 value.toXML(xmlWriter, progress)
jvra2ad5442002-05-17 18:36:07 +0000710 xmlWriter.endtag(name)
711 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500712 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000713 ob = self.getClass()()
714 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +0000715 if isinstance(element, basestring):
jvr4e5af602002-05-24 09:58:04 +0000716 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500717 name, attrs, content = element
718 ob.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000719 return ob
jvra2ad5442002-05-17 18:36:07 +0000720
jvr4e5af602002-05-24 09:58:04 +0000721class PrivateDictConverter(TableConverter):
722 def getClass(self):
723 return PrivateDict
jvr4756b3a2002-05-16 18:17:32 +0000724 def read(self, parent, value):
725 size, offset = value
726 file = parent.file
jvr4e5af602002-05-24 09:58:04 +0000727 priv = PrivateDict(parent.strings, file, offset)
jvr4756b3a2002-05-16 18:17:32 +0000728 file.seek(offset)
729 data = file.read(size)
730 len(data) == size
jvr4e5af602002-05-24 09:58:04 +0000731 priv.decompile(data)
732 return priv
jvrf2cf9c52002-05-23 21:50:36 +0000733 def write(self, parent, value):
734 return (0, 0) # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000735
jvr4e5af602002-05-24 09:58:04 +0000736class SubrsConverter(TableConverter):
737 def getClass(self):
738 return SubrsIndex
jvr4756b3a2002-05-16 18:17:32 +0000739 def read(self, parent, value):
740 file = parent.file
741 file.seek(parent.offset + value) # Offset(self)
jvr4e5af602002-05-24 09:58:04 +0000742 return SubrsIndex(file)
jvrf2cf9c52002-05-23 21:50:36 +0000743 def write(self, parent, value):
744 return 0 # dummy value
jvr4756b3a2002-05-16 18:17:32 +0000745
jvr4e5af602002-05-24 09:58:04 +0000746class CharStringsConverter(TableConverter):
jvr4756b3a2002-05-16 18:17:32 +0000747 def read(self, parent, value):
748 file = parent.file
jvr767102e2002-05-17 07:06:32 +0000749 charset = parent.charset
jvra2ad5442002-05-17 18:36:07 +0000750 globalSubrs = parent.GlobalSubrs
751 if hasattr(parent, "ROS"):
752 fdSelect, fdArray = parent.FDSelect, parent.FDArray
753 private = None
754 else:
755 fdSelect, fdArray = None, None
756 private = parent.Private
jvr4756b3a2002-05-16 18:17:32 +0000757 file.seek(value) # Offset(0)
jvra2ad5442002-05-17 18:36:07 +0000758 return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
jvrf2cf9c52002-05-23 21:50:36 +0000759 def write(self, parent, value):
760 return 0 # dummy value
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500761 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +0000762 if hasattr(parent, "ROS"):
763 # if it is a CID-keyed font, then the private Dict is extracted from the parent.FDArray
764 private, fdSelect, fdArray = None, parent.FDSelect, parent.FDArray
765 else:
766 # if it is a name-keyed font, then the private dict is in the top dict, and there is no fdArray.
767 private, fdSelect, fdArray = parent.Private, None, None
768 charStrings = CharStrings(None, None, parent.GlobalSubrs, private, fdSelect, fdArray)
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500769 charStrings.fromXML(name, attrs, content)
jvr4e5af602002-05-24 09:58:04 +0000770 return charStrings
jvr4756b3a2002-05-16 18:17:32 +0000771
772class CharsetConverter:
773 def read(self, parent, value):
774 isCID = hasattr(parent, "ROS")
775 if value > 2:
776 numGlyphs = parent.numGlyphs
777 file = parent.file
778 file.seek(value)
jvrf2cf9c52002-05-23 21:50:36 +0000779 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500780 print("loading charset at %s" % value)
jvra2ad5442002-05-17 18:36:07 +0000781 format = readCard8(file)
Just7842e561999-12-16 21:34:53 +0000782 if format == 0:
jvrc60a44f2006-10-21 13:41:18 +0000783 charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
jvr4756b3a2002-05-16 18:17:32 +0000784 elif format == 1 or format == 2:
785 charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
Just7842e561999-12-16 21:34:53 +0000786 else:
jvr1890b952002-05-15 07:41:30 +0000787 raise NotImplementedError
jvr4756b3a2002-05-16 18:17:32 +0000788 assert len(charset) == numGlyphs
jvrf2cf9c52002-05-23 21:50:36 +0000789 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500790 print(" charset end at %s" % file.tell())
jvrc60a44f2006-10-21 13:41:18 +0000791 else: # offset == 0 -> no charset data.
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500792 if isCID or "CharStrings" not in parent.rawDict:
jvrc60a44f2006-10-21 13:41:18 +0000793 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 +0000794 charset = None
795 elif value == 0:
jvrc60a44f2006-10-21 13:41:18 +0000796 charset = cffISOAdobeStrings
jvr4756b3a2002-05-16 18:17:32 +0000797 elif value == 1:
jvrc60a44f2006-10-21 13:41:18 +0000798 charset = cffIExpertStrings
jvr4756b3a2002-05-16 18:17:32 +0000799 elif value == 2:
jvrc60a44f2006-10-21 13:41:18 +0000800 charset = cffExpertSubsetStrings
jvr4756b3a2002-05-16 18:17:32 +0000801 return charset
jvrc60a44f2006-10-21 13:41:18 +0000802
jvrf2cf9c52002-05-23 21:50:36 +0000803 def write(self, parent, value):
804 return 0 # dummy value
jvr7ce0a132002-07-23 16:42:11 +0000805 def xmlWrite(self, xmlWriter, name, value, progress):
jvr4e5af602002-05-24 09:58:04 +0000806 # XXX only write charset when not in OT/TTX context, where we
807 # dump charset as a separate "GlyphOrder" table.
808 ##xmlWriter.simpletag("charset")
809 xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
jvr4756b3a2002-05-16 18:17:32 +0000810 xmlWriter.newline()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500811 def xmlRead(self, name, attrs, content, parent):
jvr4e5af602002-05-24 09:58:04 +0000812 if 0:
813 return safeEval(attrs["value"])
jvr4756b3a2002-05-16 18:17:32 +0000814
815
jvrf2cf9c52002-05-23 21:50:36 +0000816class CharsetCompiler:
817
818 def __init__(self, strings, charset, parent):
819 assert charset[0] == '.notdef'
jvred101512003-08-22 19:53:32 +0000820 isCID = hasattr(parent.dictObj, "ROS")
821 data0 = packCharset0(charset, isCID, strings)
822 data = packCharset(charset, isCID, strings)
jvr6004baf2002-05-24 10:35:13 +0000823 if len(data) < len(data0):
824 self.data = data
825 else:
826 self.data = data0
jvrf2cf9c52002-05-23 21:50:36 +0000827 self.parent = parent
828
jvr4e5af602002-05-24 09:58:04 +0000829 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +0000830 self.parent.rawDict["charset"] = pos
831
832 def getDataLength(self):
833 return len(self.data)
834
835 def toFile(self, file):
836 file.write(self.data)
837
838
jvred101512003-08-22 19:53:32 +0000839def getCIDfromName(name, strings):
840 return int(name[3:])
841
842def getSIDfromName(name, strings):
843 return strings.getSID(name)
844
845def packCharset0(charset, isCID, strings):
jvr6004baf2002-05-24 10:35:13 +0000846 format = 0
847 data = [packCard8(format)]
jvred101512003-08-22 19:53:32 +0000848 if isCID:
849 getNameID = getCIDfromName
850 else:
851 getNameID = getSIDfromName
852
jvr6004baf2002-05-24 10:35:13 +0000853 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000854 data.append(packCard16(getNameID(name,strings)))
jvr6004baf2002-05-24 10:35:13 +0000855 return "".join(data)
856
jvred101512003-08-22 19:53:32 +0000857
858def packCharset(charset, isCID, strings):
jvr6004baf2002-05-24 10:35:13 +0000859 format = 1
jvr6004baf2002-05-24 10:35:13 +0000860 ranges = []
861 first = None
862 end = 0
jvred101512003-08-22 19:53:32 +0000863 if isCID:
864 getNameID = getCIDfromName
865 else:
866 getNameID = getSIDfromName
867
jvr6004baf2002-05-24 10:35:13 +0000868 for name in charset[1:]:
jvred101512003-08-22 19:53:32 +0000869 SID = getNameID(name, strings)
jvr6004baf2002-05-24 10:35:13 +0000870 if first is None:
871 first = SID
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500872 elif end + 1 != SID:
jvr6004baf2002-05-24 10:35:13 +0000873 nLeft = end - first
874 if nLeft > 255:
875 format = 2
876 ranges.append((first, nLeft))
877 first = SID
878 end = SID
879 nLeft = end - first
880 if nLeft > 255:
881 format = 2
882 ranges.append((first, nLeft))
883
jvr74cd1ef2002-05-24 10:38:04 +0000884 data = [packCard8(format)]
jvr6004baf2002-05-24 10:35:13 +0000885 if format == 1:
886 nLeftFunc = packCard8
887 else:
888 nLeftFunc = packCard16
889 for first, nLeft in ranges:
890 data.append(packCard16(first) + nLeftFunc(nLeft))
jvrb58176e2002-05-24 11:55:37 +0000891 return "".join(data)
jvr6004baf2002-05-24 10:35:13 +0000892
jvrc60a44f2006-10-21 13:41:18 +0000893def parseCharset0(numGlyphs, file, strings, isCID):
jvrf2cf9c52002-05-23 21:50:36 +0000894 charset = [".notdef"]
jvrc60a44f2006-10-21 13:41:18 +0000895 if isCID:
896 for i in range(numGlyphs - 1):
897 CID = readCard16(file)
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500898 charset.append("cid" + str(CID).zfill(5))
jvrc60a44f2006-10-21 13:41:18 +0000899 else:
900 for i in range(numGlyphs - 1):
901 SID = readCard16(file)
902 charset.append(strings[SID])
jvrf2cf9c52002-05-23 21:50:36 +0000903 return charset
904
jvr4756b3a2002-05-16 18:17:32 +0000905def parseCharset(numGlyphs, file, strings, isCID, format):
906 charset = ['.notdef']
907 count = 1
908 if format == 1:
jvra2ad5442002-05-17 18:36:07 +0000909 nLeftFunc = readCard8
jvr4756b3a2002-05-16 18:17:32 +0000910 else:
jvra2ad5442002-05-17 18:36:07 +0000911 nLeftFunc = readCard16
jvr4756b3a2002-05-16 18:17:32 +0000912 while count < numGlyphs:
jvra2ad5442002-05-17 18:36:07 +0000913 first = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000914 nLeft = nLeftFunc(file)
915 if isCID:
916 for CID in range(first, first+nLeft+1):
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500917 charset.append("cid" + str(CID).zfill(5))
jvr1890b952002-05-15 07:41:30 +0000918 else:
jvr4756b3a2002-05-16 18:17:32 +0000919 for SID in range(first, first+nLeft+1):
920 charset.append(strings[SID])
921 count = count + nLeft + 1
922 return charset
923
924
jvrb9702ba2003-01-03 20:56:01 +0000925class EncodingCompiler:
926
927 def __init__(self, strings, encoding, parent):
jvr2a9bcde2008-03-07 19:56:17 +0000928 assert not isinstance(encoding, basestring)
jvrb9702ba2003-01-03 20:56:01 +0000929 data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
930 data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
931 if len(data0) < len(data1):
932 self.data = data0
933 else:
934 self.data = data1
935 self.parent = parent
936
937 def setPos(self, pos, endPos):
938 self.parent.rawDict["Encoding"] = pos
939
940 def getDataLength(self):
941 return len(self.data)
942
943 def toFile(self, file):
944 file.write(self.data)
945
946
947class EncodingConverter(SimpleConverter):
948
949 def read(self, parent, value):
950 if value == 0:
951 return "StandardEncoding"
952 elif value == 1:
953 return "ExpertEncoding"
954 else:
955 assert value > 1
956 file = parent.file
957 file.seek(value)
958 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500959 print("loading Encoding at %s" % value)
jvrb9702ba2003-01-03 20:56:01 +0000960 format = readCard8(file)
961 haveSupplement = format & 0x80
962 if haveSupplement:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -0500963 raise NotImplementedError("Encoding supplements are not yet supported")
jvrb9702ba2003-01-03 20:56:01 +0000964 format = format & 0x7f
965 if format == 0:
966 encoding = parseEncoding0(parent.charset, file, haveSupplement,
967 parent.strings)
968 elif format == 1:
969 encoding = parseEncoding1(parent.charset, file, haveSupplement,
970 parent.strings)
971 return encoding
972
973 def write(self, parent, value):
974 if value == "StandardEncoding":
975 return 0
976 elif value == "ExpertEncoding":
977 return 1
978 return 0 # dummy value
979
980 def xmlWrite(self, xmlWriter, name, value, progress):
981 if value in ("StandardEncoding", "ExpertEncoding"):
982 xmlWriter.simpletag(name, name=value)
983 xmlWriter.newline()
984 return
985 xmlWriter.begintag(name)
986 xmlWriter.newline()
987 for code in range(len(value)):
988 glyphName = value[code]
989 if glyphName != ".notdef":
990 xmlWriter.simpletag("map", code=hex(code), name=glyphName)
991 xmlWriter.newline()
992 xmlWriter.endtag(name)
993 xmlWriter.newline()
994
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500995 def xmlRead(self, name, attrs, content, parent):
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500996 if "name" in attrs:
jvrb9702ba2003-01-03 20:56:01 +0000997 return attrs["name"]
998 encoding = [".notdef"] * 256
999 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +00001000 if isinstance(element, basestring):
jvrb9702ba2003-01-03 20:56:01 +00001001 continue
1002 name, attrs, content = element
1003 code = safeEval(attrs["code"])
1004 glyphName = attrs["name"]
1005 encoding[code] = glyphName
1006 return encoding
1007
1008
1009def parseEncoding0(charset, file, haveSupplement, strings):
1010 nCodes = readCard8(file)
1011 encoding = [".notdef"] * 256
1012 for glyphID in range(1, nCodes + 1):
1013 code = readCard8(file)
1014 if code != 0:
1015 encoding[code] = charset[glyphID]
1016 return encoding
1017
1018def parseEncoding1(charset, file, haveSupplement, strings):
1019 nRanges = readCard8(file)
1020 encoding = [".notdef"] * 256
1021 glyphID = 1
1022 for i in range(nRanges):
1023 code = readCard8(file)
1024 nLeft = readCard8(file)
1025 for glyphID in range(glyphID, glyphID + nLeft + 1):
1026 encoding[code] = charset[glyphID]
1027 code = code + 1
1028 glyphID = glyphID + 1
1029 return encoding
1030
1031def packEncoding0(charset, encoding, strings):
1032 format = 0
1033 m = {}
1034 for code in range(len(encoding)):
1035 name = encoding[code]
1036 if name != ".notdef":
1037 m[name] = code
1038 codes = []
1039 for name in charset[1:]:
1040 code = m.get(name)
1041 codes.append(code)
1042
1043 while codes and codes[-1] is None:
1044 codes.pop()
1045
1046 data = [packCard8(format), packCard8(len(codes))]
1047 for code in codes:
1048 if code is None:
1049 code = 0
1050 data.append(packCard8(code))
1051 return "".join(data)
1052
1053def packEncoding1(charset, encoding, strings):
1054 format = 1
1055 m = {}
1056 for code in range(len(encoding)):
1057 name = encoding[code]
1058 if name != ".notdef":
1059 m[name] = code
1060 ranges = []
1061 first = None
1062 end = 0
1063 for name in charset[1:]:
1064 code = m.get(name, -1)
1065 if first is None:
1066 first = code
Behdad Esfahbod180ace62013-11-27 02:40:30 -05001067 elif end + 1 != code:
jvrb9702ba2003-01-03 20:56:01 +00001068 nLeft = end - first
1069 ranges.append((first, nLeft))
1070 first = code
1071 end = code
1072 nLeft = end - first
1073 ranges.append((first, nLeft))
1074
1075 # remove unencoded glyphs at the end.
1076 while ranges and ranges[-1][0] == -1:
1077 ranges.pop()
1078
1079 data = [packCard8(format), packCard8(len(ranges))]
1080 for first, nLeft in ranges:
1081 if first == -1: # unencoded
1082 first = 0
1083 data.append(packCard8(first) + packCard8(nLeft))
1084 return "".join(data)
1085
1086
jvr4e5af602002-05-24 09:58:04 +00001087class FDArrayConverter(TableConverter):
jvred101512003-08-22 19:53:32 +00001088
jvra2ad5442002-05-17 18:36:07 +00001089 def read(self, parent, value):
1090 file = parent.file
1091 file.seek(value)
jvred101512003-08-22 19:53:32 +00001092 fdArray = FDArrayIndex(file)
jvra2ad5442002-05-17 18:36:07 +00001093 fdArray.strings = parent.strings
1094 fdArray.GlobalSubrs = parent.GlobalSubrs
1095 return fdArray
1096
jvred101512003-08-22 19:53:32 +00001097 def write(self, parent, value):
1098 return 0 # dummy value
1099
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001100 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001101 fdArray = FDArrayIndex()
1102 for element in content:
jvr2a9bcde2008-03-07 19:56:17 +00001103 if isinstance(element, basestring):
jvred101512003-08-22 19:53:32 +00001104 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001105 name, attrs, content = element
1106 fdArray.fromXML(name, attrs, content)
jvred101512003-08-22 19:53:32 +00001107 return fdArray
1108
jvra2ad5442002-05-17 18:36:07 +00001109
1110class FDSelectConverter:
jvred101512003-08-22 19:53:32 +00001111
jvra2ad5442002-05-17 18:36:07 +00001112 def read(self, parent, value):
1113 file = parent.file
1114 file.seek(value)
jvred101512003-08-22 19:53:32 +00001115 fdSelect = FDSelect(file, parent.numGlyphs)
1116 return fdSelect
1117
1118 def write(self, parent, value):
1119 return 0 # dummy value
1120
1121 # The FDSelect glyph data is written out to XML in the charstring keys,
1122 # so we write out only the format selector
jvr7ce0a132002-07-23 16:42:11 +00001123 def xmlWrite(self, xmlWriter, name, value, progress):
jvred101512003-08-22 19:53:32 +00001124 xmlWriter.simpletag(name, [('format', value.format)])
1125 xmlWriter.newline()
1126
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001127 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001128 format = safeEval(attrs["format"])
1129 file = None
1130 numGlyphs = None
1131 fdSelect = FDSelect(file, numGlyphs, format)
1132 return fdSelect
1133
1134
1135def packFDSelect0(fdSelectArray):
1136 format = 0
1137 data = [packCard8(format)]
1138 for index in fdSelectArray:
1139 data.append(packCard8(index))
1140 return "".join(data)
1141
1142
1143def packFDSelect3(fdSelectArray):
1144 format = 3
1145 fdRanges = []
1146 first = None
1147 end = 0
1148 lenArray = len(fdSelectArray)
1149 lastFDIndex = -1
1150 for i in range(lenArray):
1151 fdIndex = fdSelectArray[i]
1152 if lastFDIndex != fdIndex:
1153 fdRanges.append([i, fdIndex])
1154 lastFDIndex = fdIndex
1155 sentinelGID = i + 1
1156
1157 data = [packCard8(format)]
1158 data.append(packCard16( len(fdRanges) ))
1159 for fdRange in fdRanges:
1160 data.append(packCard16(fdRange[0]))
1161 data.append(packCard8(fdRange[1]))
1162 data.append(packCard16(sentinelGID))
1163 return "".join(data)
1164
1165
1166class FDSelectCompiler:
1167
1168 def __init__(self, fdSelect, parent):
1169 format = fdSelect.format
1170 fdSelectArray = fdSelect.gidArray
1171 if format == 0:
1172 self.data = packFDSelect0(fdSelectArray)
1173 elif format == 3:
1174 self.data = packFDSelect3(fdSelectArray)
1175 else:
1176 # choose smaller of the two formats
1177 data0 = packFDSelect0(fdSelectArray)
1178 data3 = packFDSelect3(fdSelectArray)
1179 if len(data0) < len(data3):
1180 self.data = data0
1181 fdSelect.format = 0
1182 else:
1183 self.data = data3
1184 fdSelect.format = 3
1185
1186 self.parent = parent
1187
1188 def setPos(self, pos, endPos):
1189 self.parent.rawDict["FDSelect"] = pos
1190
1191 def getDataLength(self):
1192 return len(self.data)
1193
1194 def toFile(self, file):
1195 file.write(self.data)
jvra2ad5442002-05-17 18:36:07 +00001196
1197
jvr4e5af602002-05-24 09:58:04 +00001198class ROSConverter(SimpleConverter):
jvred101512003-08-22 19:53:32 +00001199
jvr7ce0a132002-07-23 16:42:11 +00001200 def xmlWrite(self, xmlWriter, name, value, progress):
jvr155aa752002-05-17 19:58:49 +00001201 registry, order, supplement = value
jvrf2cf9c52002-05-23 21:50:36 +00001202 xmlWriter.simpletag(name, [('Registry', registry), ('Order', order),
1203 ('Supplement', supplement)])
jvr4afb2572002-05-18 20:07:01 +00001204 xmlWriter.newline()
jvr155aa752002-05-17 19:58:49 +00001205
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001206 def xmlRead(self, name, attrs, content, parent):
jvred101512003-08-22 19:53:32 +00001207 return (attrs['Registry'], attrs['Order'], safeEval(attrs['Supplement']))
1208
1209
jvr155aa752002-05-17 19:58:49 +00001210
jvr4756b3a2002-05-16 18:17:32 +00001211topDictOperators = [
1212# opcode name argument type default converter
jvr155aa752002-05-17 19:58:49 +00001213 ((12, 30), 'ROS', ('SID','SID','number'), None, ROSConverter()),
jvrf2cf9c52002-05-23 21:50:36 +00001214 ((12, 20), 'SyntheticBase', 'number', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001215 (0, 'version', 'SID', None, None),
jvre2ca9b52002-09-09 14:18:39 +00001216 (1, 'Notice', 'SID', None, Latin1Converter()),
1217 ((12, 0), 'Copyright', 'SID', None, Latin1Converter()),
jvr4756b3a2002-05-16 18:17:32 +00001218 (2, 'FullName', 'SID', None, None),
jvr155aa752002-05-17 19:58:49 +00001219 ((12, 38), 'FontName', 'SID', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001220 (3, 'FamilyName', 'SID', None, None),
1221 (4, 'Weight', 'SID', None, None),
1222 ((12, 1), 'isFixedPitch', 'number', 0, None),
1223 ((12, 2), 'ItalicAngle', 'number', 0, None),
1224 ((12, 3), 'UnderlinePosition', 'number', None, None),
1225 ((12, 4), 'UnderlineThickness', 'number', 50, None),
1226 ((12, 5), 'PaintType', 'number', 0, None),
1227 ((12, 6), 'CharstringType', 'number', 2, None),
1228 ((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0], None),
1229 (13, 'UniqueID', 'number', None, None),
1230 (5, 'FontBBox', 'array', [0,0,0,0], None),
1231 ((12, 8), 'StrokeWidth', 'number', 0, None),
1232 (14, 'XUID', 'array', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001233 ((12, 21), 'PostScript', 'SID', None, None),
1234 ((12, 22), 'BaseFontName', 'SID', None, None),
1235 ((12, 23), 'BaseFontBlend', 'delta', None, None),
jvr4756b3a2002-05-16 18:17:32 +00001236 ((12, 31), 'CIDFontVersion', 'number', 0, None),
1237 ((12, 32), 'CIDFontRevision', 'number', 0, None),
1238 ((12, 33), 'CIDFontType', 'number', 0, None),
1239 ((12, 34), 'CIDCount', 'number', 8720, None),
jvred101512003-08-22 19:53:32 +00001240 (15, 'charset', 'number', 0, CharsetConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001241 ((12, 35), 'UIDBase', 'number', None, None),
jvrb9702ba2003-01-03 20:56:01 +00001242 (16, 'Encoding', 'number', 0, EncodingConverter()),
jvr155aa752002-05-17 19:58:49 +00001243 (18, 'Private', ('number','number'), None, PrivateDictConverter()),
jvred101512003-08-22 19:53:32 +00001244 ((12, 37), 'FDSelect', 'number', None, FDSelectConverter()),
1245 ((12, 36), 'FDArray', 'number', None, FDArrayConverter()),
jvr155aa752002-05-17 19:58:49 +00001246 (17, 'CharStrings', 'number', None, CharStringsConverter()),
jvr4756b3a2002-05-16 18:17:32 +00001247]
1248
jvred101512003-08-22 19:53:32 +00001249# Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order,
1250# in order for the font to compile back from xml.
1251
1252
jvr4756b3a2002-05-16 18:17:32 +00001253privateDictOperators = [
1254# opcode name argument type default converter
1255 (6, 'BlueValues', 'delta', None, None),
1256 (7, 'OtherBlues', 'delta', None, None),
1257 (8, 'FamilyBlues', 'delta', None, None),
1258 (9, 'FamilyOtherBlues', 'delta', None, None),
1259 ((12, 9), 'BlueScale', 'number', 0.039625, None),
1260 ((12, 10), 'BlueShift', 'number', 7, None),
1261 ((12, 11), 'BlueFuzz', 'number', 1, None),
1262 (10, 'StdHW', 'number', None, None),
1263 (11, 'StdVW', 'number', None, None),
1264 ((12, 12), 'StemSnapH', 'delta', None, None),
1265 ((12, 13), 'StemSnapV', 'delta', None, None),
1266 ((12, 14), 'ForceBold', 'number', 0, None),
jvrf2cf9c52002-05-23 21:50:36 +00001267 ((12, 15), 'ForceBoldThreshold', 'number', None, None), # deprecated
1268 ((12, 16), 'lenIV', 'number', None, None), # deprecated
jvr4756b3a2002-05-16 18:17:32 +00001269 ((12, 17), 'LanguageGroup', 'number', 0, None),
1270 ((12, 18), 'ExpansionFactor', 'number', 0.06, None),
1271 ((12, 19), 'initialRandomSeed', 'number', 0, None),
1272 (20, 'defaultWidthX', 'number', 0, None),
1273 (21, 'nominalWidthX', 'number', 0, None),
1274 (19, 'Subrs', 'number', None, SubrsConverter()),
1275]
1276
jvr4e5af602002-05-24 09:58:04 +00001277def addConverters(table):
1278 for i in range(len(table)):
1279 op, name, arg, default, conv = table[i]
1280 if conv is not None:
1281 continue
1282 if arg in ("delta", "array"):
1283 conv = ArrayConverter()
1284 elif arg == "number":
1285 conv = NumberConverter()
1286 elif arg == "SID":
1287 conv = SimpleConverter()
1288 else:
1289 assert 0
1290 table[i] = op, name, arg, default, conv
1291
1292addConverters(privateDictOperators)
1293addConverters(topDictOperators)
1294
jvr4756b3a2002-05-16 18:17:32 +00001295
1296class TopDictDecompiler(psCharStrings.DictDecompiler):
1297 operators = buildOperatorDict(topDictOperators)
1298
1299
1300class PrivateDictDecompiler(psCharStrings.DictDecompiler):
1301 operators = buildOperatorDict(privateDictOperators)
1302
1303
jvrf2cf9c52002-05-23 21:50:36 +00001304class DictCompiler:
1305
1306 def __init__(self, dictObj, strings, parent):
1307 assert isinstance(strings, IndexedStrings)
1308 self.dictObj = dictObj
1309 self.strings = strings
1310 self.parent = parent
1311 rawDict = {}
1312 for name in dictObj.order:
1313 value = getattr(dictObj, name, None)
1314 if value is None:
1315 continue
1316 conv = dictObj.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001317 value = conv.write(dictObj, value)
jvrf2cf9c52002-05-23 21:50:36 +00001318 if value == dictObj.defaults.get(name):
1319 continue
1320 rawDict[name] = value
1321 self.rawDict = rawDict
1322
jvr4e5af602002-05-24 09:58:04 +00001323 def setPos(self, pos, endPos):
jvrf2cf9c52002-05-23 21:50:36 +00001324 pass
1325
1326 def getDataLength(self):
jvr4e5af602002-05-24 09:58:04 +00001327 return len(self.compile("getDataLength"))
jvrf2cf9c52002-05-23 21:50:36 +00001328
jvr4e5af602002-05-24 09:58:04 +00001329 def compile(self, reason):
1330 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001331 print("-- compiling %s for %s" % (self.__class__.__name__, reason))
1332 print("in baseDict: ", self)
jvrf2cf9c52002-05-23 21:50:36 +00001333 rawDict = self.rawDict
1334 data = []
1335 for name in self.dictObj.order:
1336 value = rawDict.get(name)
1337 if value is None:
1338 continue
1339 op, argType = self.opcodes[name]
jvr2a9bcde2008-03-07 19:56:17 +00001340 if isinstance(argType, tuple):
jvrf2cf9c52002-05-23 21:50:36 +00001341 l = len(argType)
1342 assert len(value) == l, "value doesn't match arg type"
1343 for i in range(l):
jvred101512003-08-22 19:53:32 +00001344 arg = argType[i]
jvrf2cf9c52002-05-23 21:50:36 +00001345 v = value[i]
1346 arghandler = getattr(self, "arg_" + arg)
1347 data.append(arghandler(v))
1348 else:
1349 arghandler = getattr(self, "arg_" + argType)
1350 data.append(arghandler(value))
1351 data.append(op)
1352 return "".join(data)
1353
1354 def toFile(self, file):
jvr4e5af602002-05-24 09:58:04 +00001355 file.write(self.compile("toFile"))
jvrf2cf9c52002-05-23 21:50:36 +00001356
1357 def arg_number(self, num):
1358 return encodeNumber(num)
1359 def arg_SID(self, s):
1360 return psCharStrings.encodeIntCFF(self.strings.getSID(s))
1361 def arg_array(self, value):
1362 data = []
1363 for num in value:
1364 data.append(encodeNumber(num))
1365 return "".join(data)
1366 def arg_delta(self, value):
1367 out = []
1368 last = 0
1369 for v in value:
1370 out.append(v - last)
1371 last = v
1372 data = []
1373 for num in out:
1374 data.append(encodeNumber(num))
1375 return "".join(data)
1376
1377
1378def encodeNumber(num):
jvrf6ff48b2008-03-07 19:49:25 +00001379 if isinstance(num, float):
jvrf2cf9c52002-05-23 21:50:36 +00001380 return psCharStrings.encodeFloat(num)
1381 else:
1382 return psCharStrings.encodeIntCFF(num)
1383
1384
1385class TopDictCompiler(DictCompiler):
1386
1387 opcodes = buildOpcodeDict(topDictOperators)
1388
1389 def getChildren(self, strings):
1390 children = []
jvred101512003-08-22 19:53:32 +00001391 if hasattr(self.dictObj, "charset") and self.dictObj.charset:
jvrf2cf9c52002-05-23 21:50:36 +00001392 children.append(CharsetCompiler(strings, self.dictObj.charset, self))
jvrb9702ba2003-01-03 20:56:01 +00001393 if hasattr(self.dictObj, "Encoding"):
1394 encoding = self.dictObj.Encoding
jvr2a9bcde2008-03-07 19:56:17 +00001395 if not isinstance(encoding, basestring):
jvrb9702ba2003-01-03 20:56:01 +00001396 children.append(EncodingCompiler(strings, encoding, self))
jvrce522412003-08-25 07:37:25 +00001397 if hasattr(self.dictObj, "FDSelect"):
jvred101512003-08-22 19:53:32 +00001398 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1399 # issues about merging the FDArrays. Here I assume that
1400 # either the font was read from XML, and teh FDSelect indices are all
1401 # in the charstring data, or the FDSelect array is already fully defined.
1402 fdSelect = self.dictObj.FDSelect
1403 if len(fdSelect) == 0: # probably read in from XML; assume fdIndex in CharString data
1404 charStrings = self.dictObj.CharStrings
1405 for name in self.dictObj.charset:
1406 charstring = charStrings[name]
1407 fdSelect.append(charStrings[name].fdSelectIndex)
1408 fdSelectComp = FDSelectCompiler(fdSelect, self)
1409 children.append(fdSelectComp)
jvrf2cf9c52002-05-23 21:50:36 +00001410 if hasattr(self.dictObj, "CharStrings"):
1411 items = []
1412 charStrings = self.dictObj.CharStrings
1413 for name in self.dictObj.charset:
1414 items.append(charStrings[name])
1415 charStringsComp = CharStringsCompiler(items, strings, self)
1416 children.append(charStringsComp)
jvrce522412003-08-25 07:37:25 +00001417 if hasattr(self.dictObj, "FDArray"):
jvred101512003-08-22 19:53:32 +00001418 # I have not yet supported merging a ttx CFF-CID font, as there are interesting
1419 # issues about merging the FDArrays. Here I assume that the FDArray info is correct
1420 # and complete.
1421 fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self)
1422 children.append(fdArrayIndexComp)
1423 children.extend(fdArrayIndexComp.getChildren(strings))
1424 if hasattr(self.dictObj, "Private"):
1425 privComp = self.dictObj.Private.getCompiler(strings, self)
1426 children.append(privComp)
1427 children.extend(privComp.getChildren(strings))
1428 return children
1429
1430
1431class FontDictCompiler(DictCompiler):
1432
1433 opcodes = buildOpcodeDict(topDictOperators)
1434
1435 def getChildren(self, strings):
1436 children = []
jvrf2cf9c52002-05-23 21:50:36 +00001437 if hasattr(self.dictObj, "Private"):
1438 privComp = self.dictObj.Private.getCompiler(strings, self)
1439 children.append(privComp)
1440 children.extend(privComp.getChildren(strings))
1441 return children
1442
1443
1444class PrivateDictCompiler(DictCompiler):
1445
1446 opcodes = buildOpcodeDict(privateDictOperators)
1447
jvr4e5af602002-05-24 09:58:04 +00001448 def setPos(self, pos, endPos):
1449 size = endPos - pos
jvrf2cf9c52002-05-23 21:50:36 +00001450 self.parent.rawDict["Private"] = size, pos
1451 self.pos = pos
1452
1453 def getChildren(self, strings):
1454 children = []
1455 if hasattr(self.dictObj, "Subrs"):
1456 children.append(self.dictObj.Subrs.getCompiler(strings, self))
1457 return children
1458
jvr4756b3a2002-05-16 18:17:32 +00001459
1460class BaseDict:
1461
jvr4e5af602002-05-24 09:58:04 +00001462 def __init__(self, strings=None, file=None, offset=None):
jvr4756b3a2002-05-16 18:17:32 +00001463 self.rawDict = {}
jvr767102e2002-05-17 07:06:32 +00001464 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001465 print("loading %s at %s" % (self.__class__.__name__, offset))
jvr4756b3a2002-05-16 18:17:32 +00001466 self.file = file
1467 self.offset = offset
1468 self.strings = strings
jvr155aa752002-05-17 19:58:49 +00001469 self.skipNames = []
jvr4756b3a2002-05-16 18:17:32 +00001470
1471 def decompile(self, data):
jvrf2cf9c52002-05-23 21:50:36 +00001472 if DEBUG:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001473 print(" length %s is %s" % (self.__class__.__name__, len(data)))
jvrf2cf9c52002-05-23 21:50:36 +00001474 dec = self.decompilerClass(self.strings)
jvr4756b3a2002-05-16 18:17:32 +00001475 dec.decompile(data)
1476 self.rawDict = dec.getDict()
1477 self.postDecompile()
1478
1479 def postDecompile(self):
1480 pass
1481
jvrf2cf9c52002-05-23 21:50:36 +00001482 def getCompiler(self, strings, parent):
1483 return self.compilerClass(self, strings, parent)
1484
jvr4756b3a2002-05-16 18:17:32 +00001485 def __getattr__(self, name):
1486 value = self.rawDict.get(name)
1487 if value is None:
1488 value = self.defaults.get(name)
1489 if value is None:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -05001490 raise AttributeError(name)
jvr4756b3a2002-05-16 18:17:32 +00001491 conv = self.converters[name]
jvr4e5af602002-05-24 09:58:04 +00001492 value = conv.read(self, value)
jvr4756b3a2002-05-16 18:17:32 +00001493 setattr(self, name, value)
1494 return value
1495
1496 def toXML(self, xmlWriter, progress):
1497 for name in self.order:
jvr155aa752002-05-17 19:58:49 +00001498 if name in self.skipNames:
1499 continue
jvr4756b3a2002-05-16 18:17:32 +00001500 value = getattr(self, name, None)
1501 if value is None:
1502 continue
jvr4e5af602002-05-24 09:58:04 +00001503 conv = self.converters[name]
jvr7ce0a132002-07-23 16:42:11 +00001504 conv.xmlWrite(xmlWriter, name, value, progress)
jvr4e5af602002-05-24 09:58:04 +00001505
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001506 def fromXML(self, name, attrs, content):
jvr4e5af602002-05-24 09:58:04 +00001507 conv = self.converters[name]
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -05001508 value = conv.xmlRead(name, attrs, content, self)
jvr4e5af602002-05-24 09:58:04 +00001509 setattr(self, name, value)
jvr4756b3a2002-05-16 18:17:32 +00001510
1511
1512class TopDict(BaseDict):
1513
1514 defaults = buildDefaults(topDictOperators)
1515 converters = buildConverters(topDictOperators)
1516 order = buildOrder(topDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001517 decompilerClass = TopDictDecompiler
1518 compilerClass = TopDictCompiler
Just7842e561999-12-16 21:34:53 +00001519
jvr4e5af602002-05-24 09:58:04 +00001520 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
jvr016ca762002-05-16 18:38:03 +00001521 BaseDict.__init__(self, strings, file, offset)
1522 self.GlobalSubrs = GlobalSubrs
1523
Just7842e561999-12-16 21:34:53 +00001524 def getGlyphOrder(self):
1525 return self.charset
1526
jvr4756b3a2002-05-16 18:17:32 +00001527 def postDecompile(self):
1528 offset = self.rawDict.get("CharStrings")
1529 if offset is None:
1530 return
1531 # get the number of glyphs beforehand.
1532 self.file.seek(offset)
jvra2ad5442002-05-17 18:36:07 +00001533 self.numGlyphs = readCard16(self.file)
Just7842e561999-12-16 21:34:53 +00001534
jvr016ca762002-05-16 18:38:03 +00001535 def toXML(self, xmlWriter, progress):
jvr155aa752002-05-17 19:58:49 +00001536 if hasattr(self, "CharStrings"):
jvr7ce0a132002-07-23 16:42:11 +00001537 self.decompileAllCharStrings(progress)
jvred101512003-08-22 19:53:32 +00001538 if hasattr(self, "ROS"):
1539 self.skipNames = ['Encoding']
jvr155aa752002-05-17 19:58:49 +00001540 if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
1541 # these values have default values, but I only want them to show up
1542 # in CID fonts.
1543 self.skipNames = ['CIDFontVersion', 'CIDFontRevision', 'CIDFontType',
1544 'CIDCount']
jvr016ca762002-05-16 18:38:03 +00001545 BaseDict.toXML(self, xmlWriter, progress)
jvr767102e2002-05-17 07:06:32 +00001546
jvr7ce0a132002-07-23 16:42:11 +00001547 def decompileAllCharStrings(self, progress):
jvr4e5af602002-05-24 09:58:04 +00001548 # XXX only when doing ttdump -i?
jvr7ce0a132002-07-23 16:42:11 +00001549 i = 0
jvra2ad5442002-05-17 18:36:07 +00001550 for charString in self.CharStrings.values():
jvred101512003-08-22 19:53:32 +00001551 try:
1552 charString.decompile()
1553 except:
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -05001554 print("Error in charstring ", i)
jvred101512003-08-22 19:53:32 +00001555 import sys
1556 type, value = sys. exc_info()[0:2]
1557 raise type(value)
jvr7ce0a132002-07-23 16:42:11 +00001558 if not i % 30 and progress:
1559 progress.increment(0) # update
1560 i = i + 1
Just7842e561999-12-16 21:34:53 +00001561
1562
jvred101512003-08-22 19:53:32 +00001563class FontDict(BaseDict):
1564
1565 defaults = buildDefaults(topDictOperators)
1566 converters = buildConverters(topDictOperators)
1567 order = buildOrder(topDictOperators)
1568 decompilerClass = None
1569 compilerClass = FontDictCompiler
1570
1571 def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
1572 BaseDict.__init__(self, strings, file, offset)
1573 self.GlobalSubrs = GlobalSubrs
1574
1575 def getGlyphOrder(self):
1576 return self.charset
1577
1578 def toXML(self, xmlWriter, progress):
1579 self.skipNames = ['Encoding']
1580 BaseDict.toXML(self, xmlWriter, progress)
1581
1582
1583
jvr4756b3a2002-05-16 18:17:32 +00001584class PrivateDict(BaseDict):
1585 defaults = buildDefaults(privateDictOperators)
1586 converters = buildConverters(privateDictOperators)
1587 order = buildOrder(privateDictOperators)
jvrf2cf9c52002-05-23 21:50:36 +00001588 decompilerClass = PrivateDictDecompiler
1589 compilerClass = PrivateDictCompiler
Just7842e561999-12-16 21:34:53 +00001590
1591
jvre3275582002-05-14 12:22:03 +00001592class IndexedStrings:
1593
jvr767102e2002-05-17 07:06:32 +00001594 """SID -> string mapping."""
1595
1596 def __init__(self, file=None):
1597 if file is None:
jvre3275582002-05-14 12:22:03 +00001598 strings = []
jvr767102e2002-05-17 07:06:32 +00001599 else:
jvr4e5af602002-05-24 09:58:04 +00001600 strings = list(Index(file))
jvre3275582002-05-14 12:22:03 +00001601 self.strings = strings
1602
jvrf2cf9c52002-05-23 21:50:36 +00001603 def getCompiler(self):
1604 return IndexedStringsCompiler(self, None, None)
1605
1606 def __len__(self):
1607 return len(self.strings)
1608
jvre3275582002-05-14 12:22:03 +00001609 def __getitem__(self, SID):
1610 if SID < cffStandardStringCount:
1611 return cffStandardStrings[SID]
1612 else:
1613 return self.strings[SID - cffStandardStringCount]
1614
1615 def getSID(self, s):
1616 if not hasattr(self, "stringMapping"):
1617 self.buildStringMapping()
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -05001618 if s in cffStandardStringMapping:
jvre3275582002-05-14 12:22:03 +00001619 SID = cffStandardStringMapping[s]
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -05001620 elif s in self.stringMapping:
jvre3275582002-05-14 12:22:03 +00001621 SID = self.stringMapping[s]
1622 else:
1623 SID = len(self.strings) + cffStandardStringCount
1624 self.strings.append(s)
1625 self.stringMapping[s] = SID
1626 return SID
1627
1628 def getStrings(self):
1629 return self.strings
1630
1631 def buildStringMapping(self):
1632 self.stringMapping = {}
1633 for index in range(len(self.strings)):
1634 self.stringMapping[self.strings[index]] = index + cffStandardStringCount
1635
1636
Just7842e561999-12-16 21:34:53 +00001637# The 391 Standard Strings as used in the CFF format.
1638# from Adobe Technical None #5176, version 1.0, 18 March 1998
1639
1640cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
1641 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
1642 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
1643 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
1644 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C',
1645 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
1646 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
1647 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
1648 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
1649 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
1650 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
1651 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
1652 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
1653 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
1654 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
1655 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
1656 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
1657 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
1658 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
1659 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
1660 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
1661 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
1662 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
1663 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
1664 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
1665 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
1666 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
1667 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
1668 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
1669 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
1670 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
1671 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
1672 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
1673 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
1674 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
1675 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
1676 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
1677 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
1678 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
1679 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
1680 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
1681 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
1682 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
1683 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
1684 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
1685 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
1686 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
1687 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
1688 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
1689 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
1690 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
1691 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
1692 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
1693 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
1694 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
1695 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
1696 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
1697 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
1698 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
1699 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
1700 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
1701 '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
1702 'Semibold'
1703]
1704
1705cffStandardStringCount = 391
1706assert len(cffStandardStrings) == cffStandardStringCount
1707# build reverse mapping
1708cffStandardStringMapping = {}
1709for _i in range(cffStandardStringCount):
1710 cffStandardStringMapping[cffStandardStrings[_i]] = _i
jvrc60a44f2006-10-21 13:41:18 +00001711
1712cffISOAdobeStrings = [".notdef", "space", "exclam", "quotedbl", "numbersign",
1713"dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright",
1714"asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two",
1715"three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon",
1716"less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G",
1717"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
1718"X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum",
1719"underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
1720"k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
1721"braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent",
1722"sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle",
1723"quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl",
1724"endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet",
1725"quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis",
1726"perthousand", "questiondown", "grave", "acute", "circumflex", "tilde",
1727"macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
1728"ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE",
1729"ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls",
1730"onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus",
1731"Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn",
1732"threequarters", "twosuperior", "registered", "minus", "eth", "multiply",
1733"threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
1734"Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave",
1735"Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute",
1736"Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute",
1737"Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute",
1738"acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute",
1739"ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis",
1740"igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde",
1741"scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis",
1742"zcaron"]
1743
1744cffISOAdobeStringCount = 229
1745assert len(cffISOAdobeStrings) == cffISOAdobeStringCount
1746
1747cffIExpertStrings = [".notdef", "space", "exclamsmall", "Hungarumlautsmall",
1748"dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall",
1749"parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader",
1750"comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle",
1751"twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle",
1752"sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon",
1753"commasuperior", "threequartersemdash", "periodsuperior", "questionsmall",
1754"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1755"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1756"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1757"parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
1758"Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall",
1759"Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
1760"Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall",
1761"Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall",
1762"exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall",
1763"Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall",
1764"figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall",
1765"onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth",
1766"threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds",
1767"zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior",
1768"fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior",
1769"zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior",
1770"fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior",
1771"centinferior", "dollarinferior", "periodinferior", "commainferior",
1772"Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall",
1773"Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall",
1774"Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall",
1775"Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall",
1776"Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall",
1777"Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall",
1778"Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall",
1779"Ydieresissmall"]
1780
1781cffExpertStringCount = 166
1782assert len(cffIExpertStrings) == cffExpertStringCount
1783
1784cffExpertSubsetStrings = [".notdef", "space", "dollaroldstyle",
1785"dollarsuperior", "parenleftsuperior", "parenrightsuperior", "twodotenleader",
1786"onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle",
1787"oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle",
1788"sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon",
1789"semicolon", "commasuperior", "threequartersemdash", "periodsuperior",
1790"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1791"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1792"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1793"parenrightinferior", "hyphensuperior", "colonmonetary", "onefitted", "rupiah",
1794"centoldstyle", "figuredash", "hypheninferior", "onequarter", "onehalf",
1795"threequarters", "oneeighth", "threeeighths", "fiveeighths", "seveneighths",
1796"onethird", "twothirds", "zerosuperior", "onesuperior", "twosuperior",
1797"threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior",
1798"eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior",
1799"threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior",
1800"eightinferior", "nineinferior", "centinferior", "dollarinferior",
1801"periodinferior", "commainferior"]
1802
1803cffExpertSubsetStringCount = 87
1804assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount