blob: f851befc0d89ecde023b261a85f807b99fff2955 [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#
jvra2ad5442002-05-17 18:36:07 +00004# $Id: cffLib.py,v 1.17 2002-05-17 18:36:07 jvr Exp $
Justec46d161999-12-20 22:02:10 +00005#
Just7842e561999-12-16 21:34:53 +00006
7import struct, sstruct
8import string
9import types
Just528614e2000-01-16 22:14:02 +000010from fontTools.misc import psCharStrings
Just7842e561999-12-16 21:34:53 +000011
12
jvr767102e2002-05-17 07:06:32 +000013DEBUG = 0
14
15
Just7842e561999-12-16 21:34:53 +000016cffHeaderFormat = """
17 major: B
18 minor: B
19 hdrSize: B
20 offSize: B
21"""
22
23class CFFFontSet:
24
25 def __init__(self):
jvr4756b3a2002-05-16 18:17:32 +000026 pass
Just7842e561999-12-16 21:34:53 +000027
jvra2a75b32002-05-13 11:25:17 +000028 def decompile(self, file):
29 sstruct.unpack(cffHeaderFormat, file.read(4), self)
Just7842e561999-12-16 21:34:53 +000030 assert self.major == 1 and self.minor == 0, \
31 "unknown CFF format: %d.%d" % (self.major, self.minor)
Just7842e561999-12-16 21:34:53 +000032
jvr767102e2002-05-17 07:06:32 +000033 self.fontNames = list(Index(file, "fontNames"))
jvr4756b3a2002-05-16 18:17:32 +000034 self.topDictIndex = TopDictIndex(file)
jvr767102e2002-05-17 07:06:32 +000035 self.strings = IndexedStrings(file)
jvra2ad5442002-05-17 18:36:07 +000036 self.GlobalSubrs = CharStringIndex(file, name="GlobalSubrsIndex")
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):
44 return self.fontNames[:]
45
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:
53 raise KeyError, name
jvr016ca762002-05-16 18:38:03 +000054 return self.topDictIndex[index]
Just7842e561999-12-16 21:34:53 +000055
56 def compile(self):
57 strings = IndexedStrings()
58 XXXX
59
60 def toXML(self, xmlWriter, progress=None):
61 xmlWriter.newline()
62 for fontName in self.fontNames:
63 xmlWriter.begintag("CFFFont", name=fontName)
64 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +000065 font = self[fontName]
Just7842e561999-12-16 21:34:53 +000066 font.toXML(xmlWriter, progress)
67 xmlWriter.endtag("CFFFont")
68 xmlWriter.newline()
69 xmlWriter.newline()
70 xmlWriter.begintag("GlobalSubrs")
71 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +000072 self.GlobalSubrs.toXML(xmlWriter, progress)
Just7842e561999-12-16 21:34:53 +000073 xmlWriter.endtag("GlobalSubrs")
74 xmlWriter.newline()
75 xmlWriter.newline()
76
77 def fromXML(self, (name, attrs, content)):
78 xxx
79
80
jvr4756b3a2002-05-16 18:17:32 +000081class Index:
Just7842e561999-12-16 21:34:53 +000082
jvr4756b3a2002-05-16 18:17:32 +000083 """This class represents what the CFF spec calls an INDEX."""
Just7842e561999-12-16 21:34:53 +000084
jvr767102e2002-05-17 07:06:32 +000085 def __init__(self, file, name=None):
86 if name is None:
87 name = self.__class__.__name__
88 if DEBUG:
89 print "loading %s at %s" % (name, file.tell())
jvr4756b3a2002-05-16 18:17:32 +000090 self.file = file
jvra2ad5442002-05-17 18:36:07 +000091 count = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +000092 self.count = count
93 self.items = [None] * count
94 if count == 0:
95 self.offsets = []
96 return
jvra2ad5442002-05-17 18:36:07 +000097 offSize = readCard8(file)
jvr767102e2002-05-17 07:06:32 +000098 if DEBUG:
99 print "index count: %s offSize: %s" % (count, offSize)
100 assert offSize <= 4, "offSize too large: %s" % offSize
jvr4756b3a2002-05-16 18:17:32 +0000101 self.offsets = offsets = []
102 pad = '\0' * (4 - offSize)
103 for index in range(count+1):
104 chunk = file.read(offSize)
105 chunk = pad + chunk
106 offset, = struct.unpack(">L", chunk)
107 offsets.append(int(offset))
108 self.offsetBase = file.tell() - 1
109 file.seek(self.offsetBase + offsets[-1]) # pretend we've read the whole lot
Just7842e561999-12-16 21:34:53 +0000110
jvr4756b3a2002-05-16 18:17:32 +0000111 def __len__(self):
112 return self.count
jvra2a75b32002-05-13 11:25:17 +0000113
jvr4756b3a2002-05-16 18:17:32 +0000114 def __getitem__(self, index):
115 item = self.items[index]
116 if item is not None:
117 return item
118 offset = self.offsets[index] + self.offsetBase
119 size = self.offsets[index+1] - self.offsets[index]
120 file = self.file
121 file.seek(offset)
122 data = file.read(size)
123 assert len(data) == size
jvra2ad5442002-05-17 18:36:07 +0000124 item = self.produceItem(index, data, file, offset, size)
jvr4756b3a2002-05-16 18:17:32 +0000125 self.items[index] = item
126 return item
127
jvra2ad5442002-05-17 18:36:07 +0000128 def produceItem(self, index, data, file, offset, size):
jvr4756b3a2002-05-16 18:17:32 +0000129 return data
130
131
jvr767102e2002-05-17 07:06:32 +0000132class CharStringIndex(Index):
jvr4756b3a2002-05-16 18:17:32 +0000133
jvra2ad5442002-05-17 18:36:07 +0000134 def __init__(self, file, globalSubrs=None, private=None, fdSelect=None, fdArray=None,
135 name=None):
136 Index.__init__(self, file, name)
137 self.globalSubrs = globalSubrs
138 self.private = private
139 self.fdSelect = fdSelect
140 self.fdArray = fdArray
141
142 def produceItem(self, index, data, file, offset, size):
143 if self.private is not None:
144 private = self.private
145 elif self.fdArray is not None:
146 private = self.fdArray[self.fdSelect[index]].Private
147 else:
148 private = None
149 if hasattr(private, "Subrs"):
150 subrs = private.Subrs
151 else:
152 subrs = []
153 return psCharStrings.T2CharString(data, subrs=subrs, globalSubrs=self.globalSubrs)
jvr4756b3a2002-05-16 18:17:32 +0000154
155 def toXML(self, xmlWriter, progress):
jvra2ad5442002-05-17 18:36:07 +0000156 fdSelect = self.fdSelect
jvr4756b3a2002-05-16 18:17:32 +0000157 for i in range(len(self)):
158 xmlWriter.begintag("CharString", index=i)
159 xmlWriter.newline()
160 self[i].toXML(xmlWriter)
161 xmlWriter.endtag("CharString")
162 xmlWriter.newline()
jvra2ad5442002-05-17 18:36:07 +0000163
164 def getItemAndSelector(self, index):
165 fdSelect = self.fdSelect
166 if fdSelect is None:
167 sel = None
168 else:
169 sel = fdSelect[index]
170 return self[index], sel
171
jvr4756b3a2002-05-16 18:17:32 +0000172
jvr767102e2002-05-17 07:06:32 +0000173class TopDictIndex(Index):
jvra2ad5442002-05-17 18:36:07 +0000174
175 def produceItem(self, index, data, file, offset, size):
jvr767102e2002-05-17 07:06:32 +0000176 top = TopDict(self.strings, file, offset, self.GlobalSubrs)
177 top.decompile(data)
178 return top
jvra2ad5442002-05-17 18:36:07 +0000179
180 def toXML(self, xmlWriter, progress):
181 for i in range(len(self)):
182 xmlWriter.begintag("FontDict", index=i)
183 xmlWriter.newline()
184 self[i].toXML(xmlWriter, progress)
185 xmlWriter.endtag("FontDict")
186 xmlWriter.newline()
jvr767102e2002-05-17 07:06:32 +0000187
188
jvr4756b3a2002-05-16 18:17:32 +0000189class CharStrings:
190
jvra2ad5442002-05-17 18:36:07 +0000191 def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray):
192 self.charStringsIndex = CharStringIndex(file, globalSubrs, private, fdSelect, fdArray)
jvr4756b3a2002-05-16 18:17:32 +0000193 self.nameToIndex = nameToIndex = {}
194 for i in range(len(charset)):
195 nameToIndex[charset[i]] = i
196
197 def keys(self):
198 return self.nameToIndex.keys()
199
jvr016ca762002-05-16 18:38:03 +0000200 def values(self):
jvr767102e2002-05-17 07:06:32 +0000201 return self.charStringsIndex
jvr016ca762002-05-16 18:38:03 +0000202
jvr4756b3a2002-05-16 18:17:32 +0000203 def has_key(self, name):
204 return self.nameToIndex.has_key(name)
205
jvr767102e2002-05-17 07:06:32 +0000206 def __len__(self):
207 return len(self.charStringsIndex)
208
jvr4756b3a2002-05-16 18:17:32 +0000209 def __getitem__(self, name):
210 index = self.nameToIndex[name]
211 return self.charStringsIndex[index]
212
jvra2ad5442002-05-17 18:36:07 +0000213 def getItemAndSelector(self, name):
214 index = self.nameToIndex[name]
215 return self.charStringsIndex.getItemAndSelector(index)
216
jvr4756b3a2002-05-16 18:17:32 +0000217 def toXML(self, xmlWriter, progress):
218 names = self.keys()
219 names.sort()
220 for name in names:
jvra2ad5442002-05-17 18:36:07 +0000221 charStr, fdSelect = self.getItemAndSelector(name)
222 if fdSelect is None:
223 xmlWriter.begintag("CharString", name=name)
224 else:
225 xmlWriter.begintag("CharString",
226 [('name', name), ('fdSelect', fdSelect)])
jvr4756b3a2002-05-16 18:17:32 +0000227 xmlWriter.newline()
228 self[name].toXML(xmlWriter)
229 xmlWriter.endtag("CharString")
230 xmlWriter.newline()
231
232
jvra2ad5442002-05-17 18:36:07 +0000233def readCard8(file):
234 return ord(file.read(1))
235
236def readCard16(file):
237 value, = struct.unpack(">H", file.read(2))
238 return value
239
jvr4756b3a2002-05-16 18:17:32 +0000240def buildOperatorDict(table):
241 d = {}
242 for op, name, arg, default, conv in table:
243 d[op] = (name, arg)
244 return d
245
246def buildOrder(table):
247 l = []
248 for op, name, arg, default, conv in table:
249 l.append(name)
250 return l
251
252def buildDefaults(table):
253 d = {}
254 for op, name, arg, default, conv in table:
255 if default is not None:
256 d[name] = default
257 return d
258
259def buildConverters(table):
260 d = {}
261 for op, name, arg, default, conv in table:
262 d[name] = conv
263 return d
264
265
jvra2ad5442002-05-17 18:36:07 +0000266class XMLConverter:
267 def xmlWrite(self, xmlWriter, name, value):
268 xmlWriter.begintag(name)
269 xmlWriter.newline()
270 value.toXML(xmlWriter, None)
271 xmlWriter.endtag(name)
272 xmlWriter.newline()
273
274class PrivateDictConverter(XMLConverter):
jvr4756b3a2002-05-16 18:17:32 +0000275 def read(self, parent, value):
276 size, offset = value
277 file = parent.file
278 pr = PrivateDict(parent.strings, file, offset)
279 file.seek(offset)
280 data = file.read(size)
281 len(data) == size
282 pr.decompile(data)
283 return pr
jvr4756b3a2002-05-16 18:17:32 +0000284
jvra2ad5442002-05-17 18:36:07 +0000285class SubrsConverter(XMLConverter):
jvr4756b3a2002-05-16 18:17:32 +0000286 def read(self, parent, value):
287 file = parent.file
288 file.seek(parent.offset + value) # Offset(self)
jvra2ad5442002-05-17 18:36:07 +0000289 return CharStringIndex(file, name="SubrsIndex")
jvr4756b3a2002-05-16 18:17:32 +0000290
jvra2ad5442002-05-17 18:36:07 +0000291class CharStringsConverter(XMLConverter):
jvr4756b3a2002-05-16 18:17:32 +0000292 def read(self, parent, value):
293 file = parent.file
jvr767102e2002-05-17 07:06:32 +0000294 charset = parent.charset
jvra2ad5442002-05-17 18:36:07 +0000295 globalSubrs = parent.GlobalSubrs
296 if hasattr(parent, "ROS"):
297 fdSelect, fdArray = parent.FDSelect, parent.FDArray
298 private = None
299 else:
300 fdSelect, fdArray = None, None
301 private = parent.Private
jvr4756b3a2002-05-16 18:17:32 +0000302 file.seek(value) # Offset(0)
jvra2ad5442002-05-17 18:36:07 +0000303 return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
jvr4756b3a2002-05-16 18:17:32 +0000304
305class CharsetConverter:
306 def read(self, parent, value):
307 isCID = hasattr(parent, "ROS")
308 if value > 2:
309 numGlyphs = parent.numGlyphs
310 file = parent.file
311 file.seek(value)
jvra2ad5442002-05-17 18:36:07 +0000312 format = readCard8(file)
Just7842e561999-12-16 21:34:53 +0000313 if format == 0:
jvr1890b952002-05-15 07:41:30 +0000314 raise NotImplementedError
jvr4756b3a2002-05-16 18:17:32 +0000315 elif format == 1 or format == 2:
316 charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
Just7842e561999-12-16 21:34:53 +0000317 elif format == 3:
jvr1890b952002-05-15 07:41:30 +0000318 raise NotImplementedError
Just7842e561999-12-16 21:34:53 +0000319 else:
jvr1890b952002-05-15 07:41:30 +0000320 raise NotImplementedError
jvr4756b3a2002-05-16 18:17:32 +0000321 assert len(charset) == numGlyphs
jvr1890b952002-05-15 07:41:30 +0000322 else:
jvra2ad5442002-05-17 18:36:07 +0000323 if isCID or not hasattr(parent, "CharStrings"):
jvr4756b3a2002-05-16 18:17:32 +0000324 assert value == 0
325 charset = None
326 elif value == 0:
327 charset = ISOAdobe
328 elif value == 1:
329 charset = Expert
330 elif value == 2:
331 charset = ExpertSubset
jvr1890b952002-05-15 07:41:30 +0000332 # self.charset:
333 # 0: ISOAdobe (or CID font!)
334 # 1: Expert
335 # 2: ExpertSubset
jvr4756b3a2002-05-16 18:17:32 +0000336 charset = None #
337 return charset
338 def xmlWrite(self, xmlWriter, name, value):
339 # XXX GlyphOrder needs to be stored *somewhere*, but not here...
340 xmlWriter.simpletag("charset", value=value)
341 xmlWriter.newline()
342
343
344def parseCharset(numGlyphs, file, strings, isCID, format):
345 charset = ['.notdef']
346 count = 1
347 if format == 1:
jvra2ad5442002-05-17 18:36:07 +0000348 nLeftFunc = readCard8
jvr4756b3a2002-05-16 18:17:32 +0000349 else:
jvra2ad5442002-05-17 18:36:07 +0000350 nLeftFunc = readCard16
jvr4756b3a2002-05-16 18:17:32 +0000351 while count < numGlyphs:
jvra2ad5442002-05-17 18:36:07 +0000352 first = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000353 nLeft = nLeftFunc(file)
354 if isCID:
355 for CID in range(first, first+nLeft+1):
356 charset.append(CID)
jvr1890b952002-05-15 07:41:30 +0000357 else:
jvr4756b3a2002-05-16 18:17:32 +0000358 for SID in range(first, first+nLeft+1):
359 charset.append(strings[SID])
360 count = count + nLeft + 1
361 return charset
362
363
jvra2ad5442002-05-17 18:36:07 +0000364class FDArrayConverter(XMLConverter):
365 def read(self, parent, value):
366 file = parent.file
367 file.seek(value)
368 fdArray = TopDictIndex(file)
369 fdArray.strings = parent.strings
370 fdArray.GlobalSubrs = parent.GlobalSubrs
371 return fdArray
372
373
374class FDSelectConverter:
375 def read(self, parent, value):
376 file = parent.file
377 file.seek(value)
378 format = readCard8(file)
379 numGlyphs = parent.numGlyphs
380 if format == 0:
381 from array import array
382 fdSelect = array("B", file.read(numGlyphs)).tolist()
383 elif format == 3:
384 fdSelect = [None] * numGlyphs
385 nRanges = readCard16(file)
386 prev = None
387 for i in range(nRanges):
388 first = readCard16(file)
389 if prev is not None:
390 for glyphID in range(prev, first):
391 fdSelect[glyphID] = fd
392 prev = first
393 fd = readCard8(file)
394 if prev is not None:
395 first = readCard16(file)
396 for glyphID in range(prev, first):
397 fdSelect[glyphID] = fd
398 else:
399 assert 0, "unsupported FDSelect format: %s" % format
400 return fdSelect
401 def xmlWrite(self, xmlWriter, name, value):
402 pass
403
404
jvr4756b3a2002-05-16 18:17:32 +0000405topDictOperators = [
406# opcode name argument type default converter
407 (0, 'version', 'SID', None, None),
408 (1, 'Notice', 'SID', None, None),
409 ((12, 0), 'Copyright', 'SID', None, None),
410 (2, 'FullName', 'SID', None, None),
411 (3, 'FamilyName', 'SID', None, None),
412 (4, 'Weight', 'SID', None, None),
413 ((12, 1), 'isFixedPitch', 'number', 0, None),
414 ((12, 2), 'ItalicAngle', 'number', 0, None),
415 ((12, 3), 'UnderlinePosition', 'number', None, None),
416 ((12, 4), 'UnderlineThickness', 'number', 50, None),
417 ((12, 5), 'PaintType', 'number', 0, None),
418 ((12, 6), 'CharstringType', 'number', 2, None),
419 ((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0], None),
420 (13, 'UniqueID', 'number', None, None),
421 (5, 'FontBBox', 'array', [0,0,0,0], None),
422 ((12, 8), 'StrokeWidth', 'number', 0, None),
423 (14, 'XUID', 'array', None, None),
424 (15, 'charset', 'number', 0, CharsetConverter()),
425 (16, 'Encoding', 'number', 0, None),
426 (18, 'Private', ('number','number'), None, PrivateDictConverter()),
jvra2ad5442002-05-17 18:36:07 +0000427 (17, 'CharStrings', 'number', None, CharStringsConverter()),
jvr4756b3a2002-05-16 18:17:32 +0000428 ((12, 20), 'SyntheticBase', 'number', None, None),
429 ((12, 21), 'PostScript', 'SID', None, None),
430 ((12, 22), 'BaseFontName', 'SID', None, None),
431 ((12, 23), 'BaseFontBlend', 'delta', None, None),
432 ((12, 30), 'ROS', ('SID','SID','number'), None, None),
433 ((12, 31), 'CIDFontVersion', 'number', 0, None),
434 ((12, 32), 'CIDFontRevision', 'number', 0, None),
435 ((12, 33), 'CIDFontType', 'number', 0, None),
436 ((12, 34), 'CIDCount', 'number', 8720, None),
437 ((12, 35), 'UIDBase', 'number', None, None),
jvra2ad5442002-05-17 18:36:07 +0000438 ((12, 36), 'FDArray', 'number', None, FDArrayConverter()),
439 ((12, 37), 'FDSelect', 'number', None, FDSelectConverter()),
jvr4756b3a2002-05-16 18:17:32 +0000440 ((12, 38), 'FontName', 'SID', None, None),
441]
442
443privateDictOperators = [
444# opcode name argument type default converter
445 (6, 'BlueValues', 'delta', None, None),
446 (7, 'OtherBlues', 'delta', None, None),
447 (8, 'FamilyBlues', 'delta', None, None),
448 (9, 'FamilyOtherBlues', 'delta', None, None),
449 ((12, 9), 'BlueScale', 'number', 0.039625, None),
450 ((12, 10), 'BlueShift', 'number', 7, None),
451 ((12, 11), 'BlueFuzz', 'number', 1, None),
452 (10, 'StdHW', 'number', None, None),
453 (11, 'StdVW', 'number', None, None),
454 ((12, 12), 'StemSnapH', 'delta', None, None),
455 ((12, 13), 'StemSnapV', 'delta', None, None),
456 ((12, 14), 'ForceBold', 'number', 0, None),
457 ((12, 17), 'LanguageGroup', 'number', 0, None),
458 ((12, 18), 'ExpansionFactor', 'number', 0.06, None),
459 ((12, 19), 'initialRandomSeed', 'number', 0, None),
460 (20, 'defaultWidthX', 'number', 0, None),
461 (21, 'nominalWidthX', 'number', 0, None),
462 (19, 'Subrs', 'number', None, SubrsConverter()),
463]
464
465
466class TopDictDecompiler(psCharStrings.DictDecompiler):
467 operators = buildOperatorDict(topDictOperators)
468
469
470class PrivateDictDecompiler(psCharStrings.DictDecompiler):
471 operators = buildOperatorDict(privateDictOperators)
472
473
474
475class BaseDict:
476
477 def __init__(self, strings, file, offset):
478 self.rawDict = {}
jvr767102e2002-05-17 07:06:32 +0000479 if DEBUG:
480 print "loading %s at %s" % (self, offset)
jvr4756b3a2002-05-16 18:17:32 +0000481 self.file = file
482 self.offset = offset
483 self.strings = strings
484
485 def decompile(self, data):
486 dec = self.decompiler(self.strings)
487 dec.decompile(data)
488 self.rawDict = dec.getDict()
489 self.postDecompile()
490
491 def postDecompile(self):
492 pass
493
494 def __getattr__(self, name):
495 value = self.rawDict.get(name)
496 if value is None:
497 value = self.defaults.get(name)
498 if value is None:
499 raise AttributeError, name
500 conv = self.converters[name]
501 if conv is not None:
502 value = conv.read(self, value)
503 setattr(self, name, value)
504 return value
505
506 def toXML(self, xmlWriter, progress):
507 for name in self.order:
508 value = getattr(self, name, None)
509 if value is None:
510 continue
511 conv = self.converters.get(name)
512 if conv is not None:
513 conv.xmlWrite(xmlWriter, name, value)
514 else:
515 if isinstance(value, types.ListType):
516 value = " ".join(map(str, value))
517 xmlWriter.simpletag(name, value=value)
518 xmlWriter.newline()
519
520
521class TopDict(BaseDict):
522
523 defaults = buildDefaults(topDictOperators)
524 converters = buildConverters(topDictOperators)
525 order = buildOrder(topDictOperators)
526 decompiler = TopDictDecompiler
Just7842e561999-12-16 21:34:53 +0000527
jvr016ca762002-05-16 18:38:03 +0000528 def __init__(self, strings, file, offset, GlobalSubrs):
529 BaseDict.__init__(self, strings, file, offset)
530 self.GlobalSubrs = GlobalSubrs
531
Just7842e561999-12-16 21:34:53 +0000532 def getGlyphOrder(self):
533 return self.charset
534
jvr4756b3a2002-05-16 18:17:32 +0000535 def postDecompile(self):
536 offset = self.rawDict.get("CharStrings")
537 if offset is None:
538 return
539 # get the number of glyphs beforehand.
540 self.file.seek(offset)
jvra2ad5442002-05-17 18:36:07 +0000541 self.numGlyphs = readCard16(self.file)
Just7842e561999-12-16 21:34:53 +0000542
jvr016ca762002-05-16 18:38:03 +0000543 def toXML(self, xmlWriter, progress):
544 self.decompileAllCharStrings()
545 BaseDict.toXML(self, xmlWriter, progress)
jvr767102e2002-05-17 07:06:32 +0000546
Just7842e561999-12-16 21:34:53 +0000547 def decompileAllCharStrings(self):
jvra2ad5442002-05-17 18:36:07 +0000548 if not hasattr(self, "CharStrings"):
549 return
550 for charString in self.CharStrings.values():
551 charString.decompile()
Just7842e561999-12-16 21:34:53 +0000552
553
jvr4756b3a2002-05-16 18:17:32 +0000554class PrivateDict(BaseDict):
555 defaults = buildDefaults(privateDictOperators)
556 converters = buildConverters(privateDictOperators)
557 order = buildOrder(privateDictOperators)
558 decompiler = PrivateDictDecompiler
Just7842e561999-12-16 21:34:53 +0000559
560
jvre3275582002-05-14 12:22:03 +0000561class IndexedStrings:
562
jvr767102e2002-05-17 07:06:32 +0000563 """SID -> string mapping."""
564
565 def __init__(self, file=None):
566 if file is None:
jvre3275582002-05-14 12:22:03 +0000567 strings = []
jvr767102e2002-05-17 07:06:32 +0000568 else:
569 strings = list(Index(file, "IndexedStrings"))
jvre3275582002-05-14 12:22:03 +0000570 self.strings = strings
571
572 def __getitem__(self, SID):
573 if SID < cffStandardStringCount:
574 return cffStandardStrings[SID]
575 else:
576 return self.strings[SID - cffStandardStringCount]
577
578 def getSID(self, s):
579 if not hasattr(self, "stringMapping"):
580 self.buildStringMapping()
581 if cffStandardStringMapping.has_key(s):
582 SID = cffStandardStringMapping[s]
583 if self.stringMapping.has_key(s):
584 SID = self.stringMapping[s]
585 else:
586 SID = len(self.strings) + cffStandardStringCount
587 self.strings.append(s)
588 self.stringMapping[s] = SID
589 return SID
590
591 def getStrings(self):
592 return self.strings
593
594 def buildStringMapping(self):
595 self.stringMapping = {}
596 for index in range(len(self.strings)):
597 self.stringMapping[self.strings[index]] = index + cffStandardStringCount
598
599
Just7842e561999-12-16 21:34:53 +0000600# The 391 Standard Strings as used in the CFF format.
601# from Adobe Technical None #5176, version 1.0, 18 March 1998
602
603cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
604 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
605 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
606 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
607 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C',
608 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
609 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
610 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
611 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
612 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
613 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
614 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
615 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
616 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
617 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
618 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
619 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
620 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
621 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
622 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
623 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
624 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
625 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
626 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
627 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
628 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
629 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
630 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
631 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
632 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
633 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
634 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
635 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
636 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
637 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
638 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
639 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
640 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
641 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
642 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
643 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
644 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
645 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
646 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
647 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
648 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
649 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
650 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
651 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
652 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
653 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
654 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
655 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
656 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
657 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
658 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
659 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
660 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
661 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
662 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
663 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
664 '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
665 'Semibold'
666]
667
668cffStandardStringCount = 391
669assert len(cffStandardStrings) == cffStandardStringCount
670# build reverse mapping
671cffStandardStringMapping = {}
672for _i in range(cffStandardStringCount):
673 cffStandardStringMapping[cffStandardStrings[_i]] = _i
674
675