blob: f0a1307e6dd3b4f78e31927ee8f06c410c6b502b [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#
jvr016ca762002-05-16 18:38:03 +00004# $Id: cffLib.py,v 1.14 2002-05-16 18:38:03 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
13cffHeaderFormat = """
14 major: B
15 minor: B
16 hdrSize: B
17 offSize: B
18"""
19
20class CFFFontSet:
21
22 def __init__(self):
jvr4756b3a2002-05-16 18:17:32 +000023 pass
Just7842e561999-12-16 21:34:53 +000024
jvra2a75b32002-05-13 11:25:17 +000025 def decompile(self, file):
26 sstruct.unpack(cffHeaderFormat, file.read(4), self)
Just7842e561999-12-16 21:34:53 +000027 assert self.major == 1 and self.minor == 0, \
28 "unknown CFF format: %d.%d" % (self.major, self.minor)
Just7842e561999-12-16 21:34:53 +000029
jvr4756b3a2002-05-16 18:17:32 +000030 self.fontNames = list(Index(file))
31 self.topDictIndex = TopDictIndex(file)
32 self.strings = IndexedStrings(list(Index(file)))
33 self.GlobalSubrs = SubrsIndex(file)
34 self.topDictIndex.strings = self.strings
jvr016ca762002-05-16 18:38:03 +000035 self.topDictIndex.GlobalSubrs = self.GlobalSubrs
jvr4756b3a2002-05-16 18:17:32 +000036
37 def __len__(self):
38 return len(self.fontNames)
39
40 def keys(self):
41 return self.fontNames[:]
42
43 def __getitem__(self, name):
44 try:
45 index = self.fontNames.index(name)
46 except ValueError:
47 raise KeyError, name
jvr016ca762002-05-16 18:38:03 +000048 return self.topDictIndex[index]
Just7842e561999-12-16 21:34:53 +000049
50 def compile(self):
51 strings = IndexedStrings()
52 XXXX
53
54 def toXML(self, xmlWriter, progress=None):
55 xmlWriter.newline()
56 for fontName in self.fontNames:
57 xmlWriter.begintag("CFFFont", name=fontName)
58 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +000059 font = self[fontName]
Just7842e561999-12-16 21:34:53 +000060 font.toXML(xmlWriter, progress)
61 xmlWriter.endtag("CFFFont")
62 xmlWriter.newline()
63 xmlWriter.newline()
64 xmlWriter.begintag("GlobalSubrs")
65 xmlWriter.newline()
jvr4756b3a2002-05-16 18:17:32 +000066 self.GlobalSubrs.toXML(xmlWriter, progress)
Just7842e561999-12-16 21:34:53 +000067 xmlWriter.endtag("GlobalSubrs")
68 xmlWriter.newline()
69 xmlWriter.newline()
70
71 def fromXML(self, (name, attrs, content)):
72 xxx
73
74
jvr4756b3a2002-05-16 18:17:32 +000075class Index:
Just7842e561999-12-16 21:34:53 +000076
jvr4756b3a2002-05-16 18:17:32 +000077 """This class represents what the CFF spec calls an INDEX."""
Just7842e561999-12-16 21:34:53 +000078
jvr4756b3a2002-05-16 18:17:32 +000079 def __init__(self, file):
80 self.file = file
81 count, = struct.unpack(">H", file.read(2))
82 self.count = count
83 self.items = [None] * count
84 if count == 0:
85 self.offsets = []
86 return
87 offSize = ord(file.read(1))
88 self.offsets = offsets = []
89 pad = '\0' * (4 - offSize)
90 for index in range(count+1):
91 chunk = file.read(offSize)
92 chunk = pad + chunk
93 offset, = struct.unpack(">L", chunk)
94 offsets.append(int(offset))
95 self.offsetBase = file.tell() - 1
96 file.seek(self.offsetBase + offsets[-1]) # pretend we've read the whole lot
Just7842e561999-12-16 21:34:53 +000097
jvr4756b3a2002-05-16 18:17:32 +000098 def __len__(self):
99 return self.count
jvra2a75b32002-05-13 11:25:17 +0000100
jvr4756b3a2002-05-16 18:17:32 +0000101 def __getitem__(self, index):
102 item = self.items[index]
103 if item is not None:
104 return item
105 offset = self.offsets[index] + self.offsetBase
106 size = self.offsets[index+1] - self.offsets[index]
107 file = self.file
108 file.seek(offset)
109 data = file.read(size)
110 assert len(data) == size
111 item = self.produceItem(data, file, offset, size)
112 self.items[index] = item
113 return item
114
115 def produceItem(self, data, file, offset, size):
116 return data
117
118
119class SubrsIndex(Index):
120
121 def produceItem(self, data, file, offset, size):
122 return psCharStrings.T2CharString(data)
123
124 def toXML(self, xmlWriter, progress):
125 for i in range(len(self)):
126 xmlWriter.begintag("CharString", index=i)
127 xmlWriter.newline()
128 self[i].toXML(xmlWriter)
129 xmlWriter.endtag("CharString")
130 xmlWriter.newline()
131
132
133class CharStrings:
134
135 def __init__(self, file, charset):
136 self.charStringsIndex = SubrsIndex(file)
137 self.nameToIndex = nameToIndex = {}
138 for i in range(len(charset)):
139 nameToIndex[charset[i]] = i
140
141 def keys(self):
142 return self.nameToIndex.keys()
143
jvr016ca762002-05-16 18:38:03 +0000144 def values(self):
145 return list(self.charStringsIndex)
146
jvr4756b3a2002-05-16 18:17:32 +0000147 def has_key(self, name):
148 return self.nameToIndex.has_key(name)
149
150 def __getitem__(self, name):
151 index = self.nameToIndex[name]
152 return self.charStringsIndex[index]
153
154 def toXML(self, xmlWriter, progress):
155 names = self.keys()
156 names.sort()
157 for name in names:
158 xmlWriter.begintag("CharString", name=name)
159 xmlWriter.newline()
160 self[name].toXML(xmlWriter)
161 xmlWriter.endtag("CharString")
162 xmlWriter.newline()
163
164
165class TopDictIndex(Index):
166 def produceItem(self, data, file, offset, size):
jvr016ca762002-05-16 18:38:03 +0000167 top = TopDict(self.strings, file, offset, self.GlobalSubrs)
jvr4756b3a2002-05-16 18:17:32 +0000168 top.decompile(data)
169 return top
170
171
172def buildOperatorDict(table):
173 d = {}
174 for op, name, arg, default, conv in table:
175 d[op] = (name, arg)
176 return d
177
178def buildOrder(table):
179 l = []
180 for op, name, arg, default, conv in table:
181 l.append(name)
182 return l
183
184def buildDefaults(table):
185 d = {}
186 for op, name, arg, default, conv in table:
187 if default is not None:
188 d[name] = default
189 return d
190
191def buildConverters(table):
192 d = {}
193 for op, name, arg, default, conv in table:
194 d[name] = conv
195 return d
196
197
198class PrivateDictConverter:
199 def read(self, parent, value):
200 size, offset = value
201 file = parent.file
202 pr = PrivateDict(parent.strings, file, offset)
203 file.seek(offset)
204 data = file.read(size)
205 len(data) == size
206 pr.decompile(data)
207 return pr
208 def xmlWrite(self, xmlWriter, name, value):
209 xmlWriter.begintag(name)
210 xmlWriter.newline()
211 value.toXML(xmlWriter, None)
212 xmlWriter.endtag(name)
213 xmlWriter.newline()
214
215class SubrsConverter(PrivateDictConverter):
216 def read(self, parent, value):
217 file = parent.file
218 file.seek(parent.offset + value) # Offset(self)
219 return SubrsIndex(file)
220
221class CharStringsConverter(PrivateDictConverter):
222 def read(self, parent, value):
223 file = parent.file
224 file.seek(value) # Offset(0)
225 return CharStrings(file, parent.charset)
226
227class CharsetConverter:
228 def read(self, parent, value):
229 isCID = hasattr(parent, "ROS")
230 if value > 2:
231 numGlyphs = parent.numGlyphs
232 file = parent.file
233 file.seek(value)
jvra2a75b32002-05-13 11:25:17 +0000234 format = ord(file.read(1))
Just7842e561999-12-16 21:34:53 +0000235 if format == 0:
jvr1890b952002-05-15 07:41:30 +0000236 raise NotImplementedError
jvr4756b3a2002-05-16 18:17:32 +0000237 elif format == 1 or format == 2:
238 charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
Just7842e561999-12-16 21:34:53 +0000239 elif format == 3:
jvr1890b952002-05-15 07:41:30 +0000240 raise NotImplementedError
Just7842e561999-12-16 21:34:53 +0000241 else:
jvr1890b952002-05-15 07:41:30 +0000242 raise NotImplementedError
jvr4756b3a2002-05-16 18:17:32 +0000243 assert len(charset) == numGlyphs
jvr1890b952002-05-15 07:41:30 +0000244 else:
jvr4756b3a2002-05-16 18:17:32 +0000245 if isCID:
246 assert value == 0
247 charset = None
248 elif value == 0:
249 charset = ISOAdobe
250 elif value == 1:
251 charset = Expert
252 elif value == 2:
253 charset = ExpertSubset
jvr1890b952002-05-15 07:41:30 +0000254 # self.charset:
255 # 0: ISOAdobe (or CID font!)
256 # 1: Expert
257 # 2: ExpertSubset
jvr4756b3a2002-05-16 18:17:32 +0000258 charset = None #
259 return charset
260 def xmlWrite(self, xmlWriter, name, value):
261 # XXX GlyphOrder needs to be stored *somewhere*, but not here...
262 xmlWriter.simpletag("charset", value=value)
263 xmlWriter.newline()
264
265
266def parseCharset(numGlyphs, file, strings, isCID, format):
267 charset = ['.notdef']
268 count = 1
269 if format == 1:
270 def nLeftFunc(file):
271 return ord(file.read(1))
272 else:
273 def nLeftFunc(file):
274 return struct.unpack(">H", file.read(2))[0]
275 while count < numGlyphs:
276 first, = struct.unpack(">H", file.read(2))
277 nLeft = nLeftFunc(file)
278 if isCID:
279 for CID in range(first, first+nLeft+1):
280 charset.append(CID)
jvr1890b952002-05-15 07:41:30 +0000281 else:
jvr4756b3a2002-05-16 18:17:32 +0000282 for SID in range(first, first+nLeft+1):
283 charset.append(strings[SID])
284 count = count + nLeft + 1
285 return charset
286
287
288topDictOperators = [
289# opcode name argument type default converter
290 (0, 'version', 'SID', None, None),
291 (1, 'Notice', 'SID', None, None),
292 ((12, 0), 'Copyright', 'SID', None, None),
293 (2, 'FullName', 'SID', None, None),
294 (3, 'FamilyName', 'SID', None, None),
295 (4, 'Weight', 'SID', None, None),
296 ((12, 1), 'isFixedPitch', 'number', 0, None),
297 ((12, 2), 'ItalicAngle', 'number', 0, None),
298 ((12, 3), 'UnderlinePosition', 'number', None, None),
299 ((12, 4), 'UnderlineThickness', 'number', 50, None),
300 ((12, 5), 'PaintType', 'number', 0, None),
301 ((12, 6), 'CharstringType', 'number', 2, None),
302 ((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0], None),
303 (13, 'UniqueID', 'number', None, None),
304 (5, 'FontBBox', 'array', [0,0,0,0], None),
305 ((12, 8), 'StrokeWidth', 'number', 0, None),
306 (14, 'XUID', 'array', None, None),
307 (15, 'charset', 'number', 0, CharsetConverter()),
308 (16, 'Encoding', 'number', 0, None),
309 (18, 'Private', ('number','number'), None, PrivateDictConverter()),
310 (17, 'CharStrings', 'number', None, CharStringsConverter()), # XXX
311 ((12, 20), 'SyntheticBase', 'number', None, None),
312 ((12, 21), 'PostScript', 'SID', None, None),
313 ((12, 22), 'BaseFontName', 'SID', None, None),
314 ((12, 23), 'BaseFontBlend', 'delta', None, None),
315 ((12, 30), 'ROS', ('SID','SID','number'), None, None),
316 ((12, 31), 'CIDFontVersion', 'number', 0, None),
317 ((12, 32), 'CIDFontRevision', 'number', 0, None),
318 ((12, 33), 'CIDFontType', 'number', 0, None),
319 ((12, 34), 'CIDCount', 'number', 8720, None),
320 ((12, 35), 'UIDBase', 'number', None, None),
321 ((12, 36), 'FDArray', 'number', None, None),
322 ((12, 37), 'FDSelect', 'number', None, None),
323 ((12, 38), 'FontName', 'SID', None, None),
324]
325
326privateDictOperators = [
327# opcode name argument type default converter
328 (6, 'BlueValues', 'delta', None, None),
329 (7, 'OtherBlues', 'delta', None, None),
330 (8, 'FamilyBlues', 'delta', None, None),
331 (9, 'FamilyOtherBlues', 'delta', None, None),
332 ((12, 9), 'BlueScale', 'number', 0.039625, None),
333 ((12, 10), 'BlueShift', 'number', 7, None),
334 ((12, 11), 'BlueFuzz', 'number', 1, None),
335 (10, 'StdHW', 'number', None, None),
336 (11, 'StdVW', 'number', None, None),
337 ((12, 12), 'StemSnapH', 'delta', None, None),
338 ((12, 13), 'StemSnapV', 'delta', None, None),
339 ((12, 14), 'ForceBold', 'number', 0, None),
340 ((12, 17), 'LanguageGroup', 'number', 0, None),
341 ((12, 18), 'ExpansionFactor', 'number', 0.06, None),
342 ((12, 19), 'initialRandomSeed', 'number', 0, None),
343 (20, 'defaultWidthX', 'number', 0, None),
344 (21, 'nominalWidthX', 'number', 0, None),
345 (19, 'Subrs', 'number', None, SubrsConverter()),
346]
347
348
349class TopDictDecompiler(psCharStrings.DictDecompiler):
350 operators = buildOperatorDict(topDictOperators)
351
352
353class PrivateDictDecompiler(psCharStrings.DictDecompiler):
354 operators = buildOperatorDict(privateDictOperators)
355
356
357
358class BaseDict:
359
360 def __init__(self, strings, file, offset):
361 self.rawDict = {}
362 self.file = file
363 self.offset = offset
364 self.strings = strings
365
366 def decompile(self, data):
367 dec = self.decompiler(self.strings)
368 dec.decompile(data)
369 self.rawDict = dec.getDict()
370 self.postDecompile()
371
372 def postDecompile(self):
373 pass
374
375 def __getattr__(self, name):
376 value = self.rawDict.get(name)
377 if value is None:
378 value = self.defaults.get(name)
379 if value is None:
380 raise AttributeError, name
381 conv = self.converters[name]
382 if conv is not None:
383 value = conv.read(self, value)
384 setattr(self, name, value)
385 return value
386
387 def toXML(self, xmlWriter, progress):
388 for name in self.order:
389 value = getattr(self, name, None)
390 if value is None:
391 continue
392 conv = self.converters.get(name)
393 if conv is not None:
394 conv.xmlWrite(xmlWriter, name, value)
395 else:
396 if isinstance(value, types.ListType):
397 value = " ".join(map(str, value))
398 xmlWriter.simpletag(name, value=value)
399 xmlWriter.newline()
400
401
402class TopDict(BaseDict):
403
404 defaults = buildDefaults(topDictOperators)
405 converters = buildConverters(topDictOperators)
406 order = buildOrder(topDictOperators)
407 decompiler = TopDictDecompiler
Just7842e561999-12-16 21:34:53 +0000408
jvr016ca762002-05-16 18:38:03 +0000409 def __init__(self, strings, file, offset, GlobalSubrs):
410 BaseDict.__init__(self, strings, file, offset)
411 self.GlobalSubrs = GlobalSubrs
412
Just7842e561999-12-16 21:34:53 +0000413 def getGlyphOrder(self):
414 return self.charset
415
jvr4756b3a2002-05-16 18:17:32 +0000416 def postDecompile(self):
417 offset = self.rawDict.get("CharStrings")
418 if offset is None:
419 return
420 # get the number of glyphs beforehand.
421 self.file.seek(offset)
422 self.numGlyphs, = struct.unpack(">H", self.file.read(2))
Just7842e561999-12-16 21:34:53 +0000423
jvr016ca762002-05-16 18:38:03 +0000424 def toXML(self, xmlWriter, progress):
425 self.decompileAllCharStrings()
426 BaseDict.toXML(self, xmlWriter, progress)
Just7842e561999-12-16 21:34:53 +0000427 def decompileAllCharStrings(self):
428 if self.CharstringType == 2:
429 # Type 2 CharStrings
430 decompiler = psCharStrings.SimpleT2Decompiler(self.Private.Subrs, self.GlobalSubrs)
431 for charString in self.CharStrings.values():
432 if charString.needsDecompilation():
433 decompiler.reset()
434 decompiler.execute(charString)
435 else:
436 # Type 1 CharStrings
437 for charString in self.CharStrings.values():
438 charString.decompile()
Just7842e561999-12-16 21:34:53 +0000439
440
jvr4756b3a2002-05-16 18:17:32 +0000441class PrivateDict(BaseDict):
442 defaults = buildDefaults(privateDictOperators)
443 converters = buildConverters(privateDictOperators)
444 order = buildOrder(privateDictOperators)
445 decompiler = PrivateDictDecompiler
Just7842e561999-12-16 21:34:53 +0000446
447
Just7842e561999-12-16 21:34:53 +0000448
jvr4756b3a2002-05-16 18:17:32 +0000449# SID
jvre3275582002-05-14 12:22:03 +0000450
451class IndexedStrings:
452
453 def __init__(self, strings=None):
454 if strings is None:
455 strings = []
456 self.strings = strings
457
458 def __getitem__(self, SID):
459 if SID < cffStandardStringCount:
460 return cffStandardStrings[SID]
461 else:
462 return self.strings[SID - cffStandardStringCount]
463
464 def getSID(self, s):
465 if not hasattr(self, "stringMapping"):
466 self.buildStringMapping()
467 if cffStandardStringMapping.has_key(s):
468 SID = cffStandardStringMapping[s]
469 if self.stringMapping.has_key(s):
470 SID = self.stringMapping[s]
471 else:
472 SID = len(self.strings) + cffStandardStringCount
473 self.strings.append(s)
474 self.stringMapping[s] = SID
475 return SID
476
477 def getStrings(self):
478 return self.strings
479
480 def buildStringMapping(self):
481 self.stringMapping = {}
482 for index in range(len(self.strings)):
483 self.stringMapping[self.strings[index]] = index + cffStandardStringCount
484
485
Just7842e561999-12-16 21:34:53 +0000486# The 391 Standard Strings as used in the CFF format.
487# from Adobe Technical None #5176, version 1.0, 18 March 1998
488
489cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
490 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
491 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
492 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
493 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C',
494 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
495 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
496 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
497 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
498 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
499 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
500 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
501 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
502 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
503 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
504 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
505 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
506 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
507 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
508 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
509 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
510 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
511 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
512 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
513 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
514 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
515 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
516 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
517 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
518 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
519 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
520 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
521 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
522 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
523 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
524 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
525 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
526 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
527 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
528 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
529 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
530 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
531 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
532 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
533 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
534 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
535 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
536 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
537 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
538 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
539 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
540 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
541 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
542 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
543 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
544 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
545 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
546 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
547 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
548 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
549 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
550 '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
551 'Semibold'
552]
553
554cffStandardStringCount = 391
555assert len(cffStandardStrings) == cffStandardStringCount
556# build reverse mapping
557cffStandardStringMapping = {}
558for _i in range(cffStandardStringCount):
559 cffStandardStringMapping[cffStandardStrings[_i]] = _i
560
561