blob: 7f41bb079f0f564752f5cb58da83aff08605fdb9 [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#
jvr7ce02ea2002-05-17 20:04:05 +00004# $Id: cffLib.py,v 1.19 2002-05-17 20:04:05 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
jvr7ce02ea2002-05-17 20:04:05 +0000266class BaseConverter:
267 def read(self, parent, value):
268 return value
jvra2ad5442002-05-17 18:36:07 +0000269 def xmlWrite(self, xmlWriter, name, value):
270 xmlWriter.begintag(name)
271 xmlWriter.newline()
272 value.toXML(xmlWriter, None)
273 xmlWriter.endtag(name)
274 xmlWriter.newline()
275
jvr7ce02ea2002-05-17 20:04:05 +0000276class PrivateDictConverter(BaseConverter):
jvr4756b3a2002-05-16 18:17:32 +0000277 def read(self, parent, value):
278 size, offset = value
279 file = parent.file
280 pr = PrivateDict(parent.strings, file, offset)
281 file.seek(offset)
282 data = file.read(size)
283 len(data) == size
284 pr.decompile(data)
285 return pr
jvr4756b3a2002-05-16 18:17:32 +0000286
jvr7ce02ea2002-05-17 20:04:05 +0000287class SubrsConverter(BaseConverter):
jvr4756b3a2002-05-16 18:17:32 +0000288 def read(self, parent, value):
289 file = parent.file
290 file.seek(parent.offset + value) # Offset(self)
jvra2ad5442002-05-17 18:36:07 +0000291 return CharStringIndex(file, name="SubrsIndex")
jvr4756b3a2002-05-16 18:17:32 +0000292
jvr7ce02ea2002-05-17 20:04:05 +0000293class CharStringsConverter(BaseConverter):
jvr4756b3a2002-05-16 18:17:32 +0000294 def read(self, parent, value):
295 file = parent.file
jvr767102e2002-05-17 07:06:32 +0000296 charset = parent.charset
jvra2ad5442002-05-17 18:36:07 +0000297 globalSubrs = parent.GlobalSubrs
298 if hasattr(parent, "ROS"):
299 fdSelect, fdArray = parent.FDSelect, parent.FDArray
300 private = None
301 else:
302 fdSelect, fdArray = None, None
303 private = parent.Private
jvr4756b3a2002-05-16 18:17:32 +0000304 file.seek(value) # Offset(0)
jvra2ad5442002-05-17 18:36:07 +0000305 return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
jvr4756b3a2002-05-16 18:17:32 +0000306
307class CharsetConverter:
308 def read(self, parent, value):
309 isCID = hasattr(parent, "ROS")
310 if value > 2:
311 numGlyphs = parent.numGlyphs
312 file = parent.file
313 file.seek(value)
jvra2ad5442002-05-17 18:36:07 +0000314 format = readCard8(file)
Just7842e561999-12-16 21:34:53 +0000315 if format == 0:
jvr1890b952002-05-15 07:41:30 +0000316 raise NotImplementedError
jvr4756b3a2002-05-16 18:17:32 +0000317 elif format == 1 or format == 2:
318 charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
Just7842e561999-12-16 21:34:53 +0000319 elif format == 3:
jvr1890b952002-05-15 07:41:30 +0000320 raise NotImplementedError
Just7842e561999-12-16 21:34:53 +0000321 else:
jvr1890b952002-05-15 07:41:30 +0000322 raise NotImplementedError
jvr4756b3a2002-05-16 18:17:32 +0000323 assert len(charset) == numGlyphs
jvr1890b952002-05-15 07:41:30 +0000324 else:
jvra2ad5442002-05-17 18:36:07 +0000325 if isCID or not hasattr(parent, "CharStrings"):
jvr4756b3a2002-05-16 18:17:32 +0000326 assert value == 0
327 charset = None
328 elif value == 0:
329 charset = ISOAdobe
330 elif value == 1:
331 charset = Expert
332 elif value == 2:
333 charset = ExpertSubset
jvr1890b952002-05-15 07:41:30 +0000334 # self.charset:
335 # 0: ISOAdobe (or CID font!)
336 # 1: Expert
337 # 2: ExpertSubset
jvr4756b3a2002-05-16 18:17:32 +0000338 charset = None #
339 return charset
340 def xmlWrite(self, xmlWriter, name, value):
341 # XXX GlyphOrder needs to be stored *somewhere*, but not here...
342 xmlWriter.simpletag("charset", value=value)
343 xmlWriter.newline()
344
345
346def parseCharset(numGlyphs, file, strings, isCID, format):
347 charset = ['.notdef']
348 count = 1
349 if format == 1:
jvra2ad5442002-05-17 18:36:07 +0000350 nLeftFunc = readCard8
jvr4756b3a2002-05-16 18:17:32 +0000351 else:
jvra2ad5442002-05-17 18:36:07 +0000352 nLeftFunc = readCard16
jvr4756b3a2002-05-16 18:17:32 +0000353 while count < numGlyphs:
jvra2ad5442002-05-17 18:36:07 +0000354 first = readCard16(file)
jvr4756b3a2002-05-16 18:17:32 +0000355 nLeft = nLeftFunc(file)
356 if isCID:
357 for CID in range(first, first+nLeft+1):
358 charset.append(CID)
jvr1890b952002-05-15 07:41:30 +0000359 else:
jvr4756b3a2002-05-16 18:17:32 +0000360 for SID in range(first, first+nLeft+1):
361 charset.append(strings[SID])
362 count = count + nLeft + 1
363 return charset
364
365
jvr7ce02ea2002-05-17 20:04:05 +0000366class FDArrayConverter(BaseConverter):
jvra2ad5442002-05-17 18:36:07 +0000367 def read(self, parent, value):
368 file = parent.file
369 file.seek(value)
370 fdArray = TopDictIndex(file)
371 fdArray.strings = parent.strings
372 fdArray.GlobalSubrs = parent.GlobalSubrs
373 return fdArray
374
375
376class FDSelectConverter:
377 def read(self, parent, value):
378 file = parent.file
379 file.seek(value)
380 format = readCard8(file)
381 numGlyphs = parent.numGlyphs
382 if format == 0:
383 from array import array
384 fdSelect = array("B", file.read(numGlyphs)).tolist()
385 elif format == 3:
386 fdSelect = [None] * numGlyphs
387 nRanges = readCard16(file)
388 prev = None
389 for i in range(nRanges):
390 first = readCard16(file)
391 if prev is not None:
392 for glyphID in range(prev, first):
393 fdSelect[glyphID] = fd
394 prev = first
395 fd = readCard8(file)
396 if prev is not None:
397 first = readCard16(file)
398 for glyphID in range(prev, first):
399 fdSelect[glyphID] = fd
400 else:
401 assert 0, "unsupported FDSelect format: %s" % format
402 return fdSelect
403 def xmlWrite(self, xmlWriter, name, value):
404 pass
405
406
jvr7ce02ea2002-05-17 20:04:05 +0000407class ROSConverter(BaseConverter):
jvr155aa752002-05-17 19:58:49 +0000408 def xmlWrite(self, xmlWriter, name, value):
409 registry, order, supplement = value
410 xmlWriter.simpletag(name, [('registry', registry), ('order', order),
411 ('supplement', supplement)])
412
413
jvr4756b3a2002-05-16 18:17:32 +0000414topDictOperators = [
415# opcode name argument type default converter
jvr155aa752002-05-17 19:58:49 +0000416 ((12, 30), 'ROS', ('SID','SID','number'), None, ROSConverter()),
jvr4756b3a2002-05-16 18:17:32 +0000417 (0, 'version', 'SID', None, None),
418 (1, 'Notice', 'SID', None, None),
419 ((12, 0), 'Copyright', 'SID', None, None),
420 (2, 'FullName', 'SID', None, None),
jvr155aa752002-05-17 19:58:49 +0000421 ((12, 38), 'FontName', 'SID', None, None),
jvr4756b3a2002-05-16 18:17:32 +0000422 (3, 'FamilyName', 'SID', None, None),
423 (4, 'Weight', 'SID', None, None),
424 ((12, 1), 'isFixedPitch', 'number', 0, None),
425 ((12, 2), 'ItalicAngle', 'number', 0, None),
426 ((12, 3), 'UnderlinePosition', 'number', None, None),
427 ((12, 4), 'UnderlineThickness', 'number', 50, None),
428 ((12, 5), 'PaintType', 'number', 0, None),
429 ((12, 6), 'CharstringType', 'number', 2, None),
430 ((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0], None),
431 (13, 'UniqueID', 'number', None, None),
432 (5, 'FontBBox', 'array', [0,0,0,0], None),
433 ((12, 8), 'StrokeWidth', 'number', 0, None),
434 (14, 'XUID', 'array', None, None),
435 (15, 'charset', 'number', 0, CharsetConverter()),
jvr4756b3a2002-05-16 18:17:32 +0000436 ((12, 20), 'SyntheticBase', 'number', None, None),
437 ((12, 21), 'PostScript', 'SID', None, None),
438 ((12, 22), 'BaseFontName', 'SID', None, None),
439 ((12, 23), 'BaseFontBlend', 'delta', None, None),
jvr4756b3a2002-05-16 18:17:32 +0000440 ((12, 31), 'CIDFontVersion', 'number', 0, None),
441 ((12, 32), 'CIDFontRevision', 'number', 0, None),
442 ((12, 33), 'CIDFontType', 'number', 0, None),
443 ((12, 34), 'CIDCount', 'number', 8720, None),
444 ((12, 35), 'UIDBase', 'number', None, None),
jvr155aa752002-05-17 19:58:49 +0000445 (16, 'Encoding', 'number', 0, None), # XXX
jvra2ad5442002-05-17 18:36:07 +0000446 ((12, 36), 'FDArray', 'number', None, FDArrayConverter()),
447 ((12, 37), 'FDSelect', 'number', None, FDSelectConverter()),
jvr155aa752002-05-17 19:58:49 +0000448 (18, 'Private', ('number','number'), None, PrivateDictConverter()),
449 (17, 'CharStrings', 'number', None, CharStringsConverter()),
jvr4756b3a2002-05-16 18:17:32 +0000450]
451
452privateDictOperators = [
453# opcode name argument type default converter
454 (6, 'BlueValues', 'delta', None, None),
455 (7, 'OtherBlues', 'delta', None, None),
456 (8, 'FamilyBlues', 'delta', None, None),
457 (9, 'FamilyOtherBlues', 'delta', None, None),
458 ((12, 9), 'BlueScale', 'number', 0.039625, None),
459 ((12, 10), 'BlueShift', 'number', 7, None),
460 ((12, 11), 'BlueFuzz', 'number', 1, None),
461 (10, 'StdHW', 'number', None, None),
462 (11, 'StdVW', 'number', None, None),
463 ((12, 12), 'StemSnapH', 'delta', None, None),
464 ((12, 13), 'StemSnapV', 'delta', None, None),
465 ((12, 14), 'ForceBold', 'number', 0, None),
466 ((12, 17), 'LanguageGroup', 'number', 0, None),
467 ((12, 18), 'ExpansionFactor', 'number', 0.06, None),
468 ((12, 19), 'initialRandomSeed', 'number', 0, None),
469 (20, 'defaultWidthX', 'number', 0, None),
470 (21, 'nominalWidthX', 'number', 0, None),
471 (19, 'Subrs', 'number', None, SubrsConverter()),
472]
473
474
475class TopDictDecompiler(psCharStrings.DictDecompiler):
476 operators = buildOperatorDict(topDictOperators)
477
478
479class PrivateDictDecompiler(psCharStrings.DictDecompiler):
480 operators = buildOperatorDict(privateDictOperators)
481
482
483
484class BaseDict:
485
486 def __init__(self, strings, file, offset):
487 self.rawDict = {}
jvr767102e2002-05-17 07:06:32 +0000488 if DEBUG:
489 print "loading %s at %s" % (self, offset)
jvr4756b3a2002-05-16 18:17:32 +0000490 self.file = file
491 self.offset = offset
492 self.strings = strings
jvr155aa752002-05-17 19:58:49 +0000493 self.skipNames = []
jvr4756b3a2002-05-16 18:17:32 +0000494
495 def decompile(self, data):
496 dec = self.decompiler(self.strings)
497 dec.decompile(data)
498 self.rawDict = dec.getDict()
499 self.postDecompile()
500
501 def postDecompile(self):
502 pass
503
504 def __getattr__(self, name):
505 value = self.rawDict.get(name)
506 if value is None:
507 value = self.defaults.get(name)
508 if value is None:
509 raise AttributeError, name
510 conv = self.converters[name]
511 if conv is not None:
512 value = conv.read(self, value)
513 setattr(self, name, value)
514 return value
515
516 def toXML(self, xmlWriter, progress):
517 for name in self.order:
jvr155aa752002-05-17 19:58:49 +0000518 if name in self.skipNames:
519 continue
jvr4756b3a2002-05-16 18:17:32 +0000520 value = getattr(self, name, None)
521 if value is None:
522 continue
523 conv = self.converters.get(name)
524 if conv is not None:
525 conv.xmlWrite(xmlWriter, name, value)
526 else:
527 if isinstance(value, types.ListType):
528 value = " ".join(map(str, value))
529 xmlWriter.simpletag(name, value=value)
530 xmlWriter.newline()
531
532
533class TopDict(BaseDict):
534
535 defaults = buildDefaults(topDictOperators)
536 converters = buildConverters(topDictOperators)
537 order = buildOrder(topDictOperators)
538 decompiler = TopDictDecompiler
Just7842e561999-12-16 21:34:53 +0000539
jvr016ca762002-05-16 18:38:03 +0000540 def __init__(self, strings, file, offset, GlobalSubrs):
541 BaseDict.__init__(self, strings, file, offset)
542 self.GlobalSubrs = GlobalSubrs
543
Just7842e561999-12-16 21:34:53 +0000544 def getGlyphOrder(self):
545 return self.charset
546
jvr4756b3a2002-05-16 18:17:32 +0000547 def postDecompile(self):
548 offset = self.rawDict.get("CharStrings")
549 if offset is None:
550 return
551 # get the number of glyphs beforehand.
552 self.file.seek(offset)
jvra2ad5442002-05-17 18:36:07 +0000553 self.numGlyphs = readCard16(self.file)
Just7842e561999-12-16 21:34:53 +0000554
jvr016ca762002-05-16 18:38:03 +0000555 def toXML(self, xmlWriter, progress):
jvr155aa752002-05-17 19:58:49 +0000556 if hasattr(self, "CharStrings"):
557 self.decompileAllCharStrings()
558 if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
559 # these values have default values, but I only want them to show up
560 # in CID fonts.
561 self.skipNames = ['CIDFontVersion', 'CIDFontRevision', 'CIDFontType',
562 'CIDCount']
jvr016ca762002-05-16 18:38:03 +0000563 BaseDict.toXML(self, xmlWriter, progress)
jvr767102e2002-05-17 07:06:32 +0000564
Just7842e561999-12-16 21:34:53 +0000565 def decompileAllCharStrings(self):
jvra2ad5442002-05-17 18:36:07 +0000566 for charString in self.CharStrings.values():
567 charString.decompile()
Just7842e561999-12-16 21:34:53 +0000568
569
jvr4756b3a2002-05-16 18:17:32 +0000570class PrivateDict(BaseDict):
571 defaults = buildDefaults(privateDictOperators)
572 converters = buildConverters(privateDictOperators)
573 order = buildOrder(privateDictOperators)
574 decompiler = PrivateDictDecompiler
Just7842e561999-12-16 21:34:53 +0000575
576
jvre3275582002-05-14 12:22:03 +0000577class IndexedStrings:
578
jvr767102e2002-05-17 07:06:32 +0000579 """SID -> string mapping."""
580
581 def __init__(self, file=None):
582 if file is None:
jvre3275582002-05-14 12:22:03 +0000583 strings = []
jvr767102e2002-05-17 07:06:32 +0000584 else:
585 strings = list(Index(file, "IndexedStrings"))
jvre3275582002-05-14 12:22:03 +0000586 self.strings = strings
587
588 def __getitem__(self, SID):
589 if SID < cffStandardStringCount:
590 return cffStandardStrings[SID]
591 else:
592 return self.strings[SID - cffStandardStringCount]
593
594 def getSID(self, s):
595 if not hasattr(self, "stringMapping"):
596 self.buildStringMapping()
597 if cffStandardStringMapping.has_key(s):
598 SID = cffStandardStringMapping[s]
599 if self.stringMapping.has_key(s):
600 SID = self.stringMapping[s]
601 else:
602 SID = len(self.strings) + cffStandardStringCount
603 self.strings.append(s)
604 self.stringMapping[s] = SID
605 return SID
606
607 def getStrings(self):
608 return self.strings
609
610 def buildStringMapping(self):
611 self.stringMapping = {}
612 for index in range(len(self.strings)):
613 self.stringMapping[self.strings[index]] = index + cffStandardStringCount
614
615
Just7842e561999-12-16 21:34:53 +0000616# The 391 Standard Strings as used in the CFF format.
617# from Adobe Technical None #5176, version 1.0, 18 March 1998
618
619cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
620 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
621 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
622 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
623 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C',
624 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
625 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
626 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
627 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
628 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
629 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
630 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
631 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
632 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
633 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
634 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
635 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
636 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
637 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
638 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
639 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
640 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
641 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
642 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
643 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
644 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
645 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
646 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
647 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
648 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
649 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
650 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
651 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
652 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
653 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
654 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
655 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
656 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
657 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
658 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
659 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
660 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
661 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
662 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
663 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
664 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
665 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
666 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
667 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
668 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
669 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
670 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
671 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
672 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
673 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
674 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
675 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
676 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
677 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
678 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
679 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
680 '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
681 'Semibold'
682]
683
684cffStandardStringCount = 391
685assert len(cffStandardStrings) == cffStandardStringCount
686# build reverse mapping
687cffStandardStringMapping = {}
688for _i in range(cffStandardStringCount):
689 cffStandardStringMapping[cffStandardStrings[_i]] = _i
690
691