blob: 51a2c102e34022ff19e11f0fdd45ad86cce58ece [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#
jvr155aa752002-05-17 19:58:49 +00004# $Id: cffLib.py,v 1.18 2002-05-17 19:58:49 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
jvr155aa752002-05-17 19:58:49 +0000405class ROSConverter:
406 def read(self, parent, value):
407 return value
408 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