blob: 17201ab655d76c96749e01373994d0cd8e8d37f3 [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#
jvrdbc2c172002-05-13 16:19:51 +00004# $Id: cffLib.py,v 1.8 2002-05-13 16:19:37 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
jvrdbc2c172002-05-13 16:19:51 +000013topDictOperators = [
14# opcode name argument type
15 (0, 'version', 'SID'),
16 (1, 'Notice', 'SID'),
17 (2, 'FullName', 'SID'),
18 (3, 'FamilyName', 'SID'),
19 (4, 'Weight', 'SID'),
20 (5, 'FontBBox', 'array'),
21 (13, 'UniqueID', 'number'),
22 (14, 'XUID', 'array'),
23 (15, 'charset', 'number'),
24 (16, 'Encoding', 'number'),
25 (17, 'CharStrings', 'number'),
26 (18, 'Private', ('number', 'number')),
27 ((12, 0), 'Copyright', 'SID'),
28 ((12, 1), 'isFixedPitch', 'number'),
29 ((12, 2), 'ItalicAngle', 'number'),
30 ((12, 3), 'UnderlinePosition', 'number'),
31 ((12, 4), 'UnderlineThickness', 'number'),
32 ((12, 5), 'PaintType', 'number'),
33 ((12, 6), 'CharstringType', 'number'),
34 ((12, 7), 'FontMatrix', 'array'),
35 ((12, 8), 'StrokeWidth', 'number'),
36 ((12, 20), 'SyntheticBase', 'number'),
37 ((12, 21), 'PostScript', 'SID'),
38 ((12, 22), 'BaseFontName', 'SID'),
39 # CID additions
40 ((12, 30), 'ROS', ('SID', 'SID', 'number')),
41 ((12, 31), 'CIDFontVersion', 'number'),
42 ((12, 32), 'CIDFontRevision', 'number'),
43 ((12, 33), 'CIDFontType', 'number'),
44 ((12, 34), 'CIDCount', 'number'),
45 ((12, 35), 'UIDBase', 'number'),
46 ((12, 36), 'FDArray', 'number'),
47 ((12, 37), 'FDSelect', 'number'),
48 ((12, 38), 'FontName', 'SID'),
49 # MM, Chameleon. Pft.
50]
51
52topDictDefaults = {
53 'isFixedPitch': 0,
54 'ItalicAngle': 0,
55 'UnderlineThickness': 50,
56 'PaintType': 0,
57 'CharstringType': 2,
58 'FontMatrix': [0.001, 0, 0, 0.001, 0, 0],
59 'FontBBox': [0, 0, 0, 0],
60 'StrokeWidth': 0,
61 'charset': 0,
62 'Encoding': 0,
63 # CID defaults
64 'CIDFontVersion': 0,
65 'CIDFontRevision': 0,
66 'CIDFontType': 0,
67 'CIDCount': 8720,
68}
69
70class TopDictDecompiler(psCharStrings.DictDecompiler):
71
72 operators = psCharStrings.buildOperatorDict(topDictOperators)
73 dictDefaults = topDictDefaults
74
75
76privateDictOperators = [
77# opcode name argument type
78 (6, 'BlueValues', 'array'),
79 (7, 'OtherBlues', 'array'),
80 (8, 'FamilyBlues', 'array'),
81 (9, 'FamilyOtherBlues', 'array'),
82 (10, 'StdHW', 'number'),
83 (11, 'StdVW', 'number'),
84 (19, 'Subrs', 'number'),
85 (20, 'defaultWidthX', 'number'),
86 (21, 'nominalWidthX', 'number'),
87 ((12, 9), 'BlueScale', 'number'),
88 ((12, 10), 'BlueShift', 'number'),
89 ((12, 11), 'BlueFuzz', 'number'),
90 ((12, 12), 'StemSnapH', 'array'),
91 ((12, 13), 'StemSnapV', 'array'),
92 ((12, 14), 'ForceBold', 'number'),
93 ((12, 15), 'ForceBoldThreshold', 'number'),
94 ((12, 16), 'lenIV', 'number'),
95 ((12, 17), 'LanguageGroup', 'number'),
96 ((12, 18), 'ExpansionFactor', 'number'),
97 ((12, 19), 'initialRandomSeed', 'number'),
98]
99
100privateDictDefaults = {
101 'defaultWidthX': 0,
102 'nominalWidthX': 0,
103 'BlueScale': 0.039625,
104 'BlueShift': 7,
105 'BlueFuzz': 1,
106 'ForceBold': 0,
107 'ForceBoldThreshold': 0,
108 'lenIV': -1,
109 'LanguageGroup': 0,
110 'ExpansionFactor': 0.06,
111 'initialRandomSeed': 0,
112}
113
114class PrivateDictDecompiler(psCharStrings.DictDecompiler):
115
116 operators = psCharStrings.buildOperatorDict(privateDictOperators)
117 dictDefaults = privateDictDefaults
118
119
120
121
Just7842e561999-12-16 21:34:53 +0000122cffHeaderFormat = """
123 major: B
124 minor: B
125 hdrSize: B
126 offSize: B
127"""
128
129class CFFFontSet:
130
131 def __init__(self):
132 self.fonts = {}
133
jvra2a75b32002-05-13 11:25:17 +0000134 def decompile(self, file):
135 sstruct.unpack(cffHeaderFormat, file.read(4), self)
Just7842e561999-12-16 21:34:53 +0000136 assert self.major == 1 and self.minor == 0, \
137 "unknown CFF format: %d.%d" % (self.major, self.minor)
Just7842e561999-12-16 21:34:53 +0000138
jvra2a75b32002-05-13 11:25:17 +0000139 self.fontNames = readINDEX(file)
140 topDicts = readINDEX(file)
141 strings = IndexedStrings(readINDEX(file))
142 globalSubrs = readINDEX(file)
Just7842e561999-12-16 21:34:53 +0000143 self.GlobalSubrs = map(psCharStrings.T2CharString, globalSubrs)
144
145 for i in range(len(topDicts)):
146 font = self.fonts[self.fontNames[i]] = CFFFont()
147 font.GlobalSubrs = self.GlobalSubrs # Hmm.
jvrdbc2c172002-05-13 16:19:51 +0000148 file.seek(0, 0)
jvra2a75b32002-05-13 11:25:17 +0000149 font.decompile(file, topDicts[i], strings, self) # maybe only 'on demand'?
Just7842e561999-12-16 21:34:53 +0000150
151 def compile(self):
152 strings = IndexedStrings()
153 XXXX
154
155 def toXML(self, xmlWriter, progress=None):
156 xmlWriter.newline()
157 for fontName in self.fontNames:
158 xmlWriter.begintag("CFFFont", name=fontName)
159 xmlWriter.newline()
160 font = self.fonts[fontName]
161 font.toXML(xmlWriter, progress)
162 xmlWriter.endtag("CFFFont")
163 xmlWriter.newline()
164 xmlWriter.newline()
165 xmlWriter.begintag("GlobalSubrs")
166 xmlWriter.newline()
167 for i in range(len(self.GlobalSubrs)):
168 xmlWriter.newline()
169 xmlWriter.begintag("CharString", id=i)
170 xmlWriter.newline()
171 self.GlobalSubrs[i].toXML(xmlWriter)
172 xmlWriter.endtag("CharString")
173 xmlWriter.newline()
174 xmlWriter.newline()
175 xmlWriter.endtag("GlobalSubrs")
176 xmlWriter.newline()
177 xmlWriter.newline()
178
179 def fromXML(self, (name, attrs, content)):
180 xxx
181
182
183class IndexedStrings:
184
185 def __init__(self, strings=None):
186 if strings is None:
187 strings = []
188 self.strings = strings
189
190 def __getitem__(self, SID):
191 if SID < cffStandardStringCount:
192 return cffStandardStrings[SID]
193 else:
194 return self.strings[SID - cffStandardStringCount]
195
196 def getSID(self, s):
197 if not hasattr(self, "stringMapping"):
198 self.buildStringMapping()
199 if cffStandardStringMapping.has_key(s):
200 SID = cffStandardStringMapping[s]
201 if self.stringMapping.has_key(s):
202 SID = self.stringMapping[s]
203 else:
204 SID = len(self.strings) + cffStandardStringCount
205 self.strings.append(s)
206 self.stringMapping[s] = SID
207 return SID
208
209 def getStrings(self):
210 return self.strings
211
212 def buildStringMapping(self):
213 self.stringMapping = {}
214 for index in range(len(self.strings)):
215 self.stringMapping[self.strings[index]] = index + cffStandardStringCount
216
217
218class CFFFont:
219
jvrdbc2c172002-05-13 16:19:51 +0000220 defaults = topDictDefaults
Just7842e561999-12-16 21:34:53 +0000221
222 def __init__(self):
223 pass
224
225 def __getattr__(self, attr):
226 if not self.defaults.has_key(attr):
227 raise AttributeError, attr
228 return self.defaults[attr]
229
230 def fromDict(self, dict):
231 self.__dict__.update(dict)
232
jvra2a75b32002-05-13 11:25:17 +0000233 def decompileCID(self, data, strings):
234 offset = self.FDArray
235 fontDicts, restdata = readINDEX(data[offset:])
236 subFonts = []
237 for topDictData in fontDicts:
238 subFont = CFFFont()
239 subFonts.append(subFont)
240 subFont.decompile(data, topDictData, strings, None)
241
242 raise NotImplementedError
243
244 def decompile(self, file, topDictData, strings, fontSet):
jvrdbc2c172002-05-13 16:19:51 +0000245 top = TopDictDecompiler(strings)
Just7842e561999-12-16 21:34:53 +0000246 top.decompile(topDictData)
247 self.fromDict(top.getDict())
248
jvra2a75b32002-05-13 11:25:17 +0000249 if hasattr(self, "ROS"):
250 isCID = 1
251 # XXX CID subFonts
252 else:
253 isCID = 0
254 size, offset = self.Private
255 file.seek(offset, 0)
256 privateData = file.read(size)
257 file.seek(offset, 0)
258 assert len(privateData) == size
259 self.Private = PrivateDict()
260 self.Private.decompile(file, privateData, strings)
Just7842e561999-12-16 21:34:53 +0000261
jvra2a75b32002-05-13 11:25:17 +0000262 file.seek(self.CharStrings)
263 rawCharStrings = readINDEX(file)
Just7842e561999-12-16 21:34:53 +0000264 nGlyphs = len(rawCharStrings)
265
266 # get charset (or rather: get glyphNames)
jvra2a75b32002-05-13 11:25:17 +0000267 if self.charset == 0:
Just7842e561999-12-16 21:34:53 +0000268 xxx # standard charset
269 else:
jvra2a75b32002-05-13 11:25:17 +0000270 file.seek(self.charset)
271 format = ord(file.read(1))
Just7842e561999-12-16 21:34:53 +0000272 if format == 0:
273 xxx
274 elif format == 1:
jvra2a75b32002-05-13 11:25:17 +0000275 charset = parseCharsetFormat1(nGlyphs, file, strings, isCID)
Just7842e561999-12-16 21:34:53 +0000276 elif format == 2:
jvra2a75b32002-05-13 11:25:17 +0000277 charset = parseCharsetFormat2(nGlyphs, file, strings, isCID)
Just7842e561999-12-16 21:34:53 +0000278 elif format == 3:
279 xxx
280 else:
281 xxx
jvra2a75b32002-05-13 11:25:17 +0000282 self.charset = charset
Just7842e561999-12-16 21:34:53 +0000283
jvra2a75b32002-05-13 11:25:17 +0000284 assert len(charset) == nGlyphs
Just7842e561999-12-16 21:34:53 +0000285 self.CharStrings = charStrings = {}
286 if self.CharstringType == 2:
287 # Type 2 CharStrings
288 charStringClass = psCharStrings.T2CharString
289 else:
290 # Type 1 CharStrings
291 charStringClass = psCharStrings.T1CharString
292 for i in range(nGlyphs):
jvra2a75b32002-05-13 11:25:17 +0000293 charStrings[charset[i]] = charStringClass(rawCharStrings[i])
Just7842e561999-12-16 21:34:53 +0000294 assert len(charStrings) == nGlyphs
295
296 # XXX Encoding!
297 encoding = self.Encoding
298 if encoding not in (0, 1):
299 # encoding is an _offset_ from the beginning of 'data' to an encoding subtable
300 XXX
301 self.Encoding = encoding
302
303 def getGlyphOrder(self):
304 return self.charset
305
306 def setGlyphOrder(self, glyphOrder):
307 self.charset = glyphOrder
308
309 def decompileAllCharStrings(self):
310 if self.CharstringType == 2:
311 # Type 2 CharStrings
312 decompiler = psCharStrings.SimpleT2Decompiler(self.Private.Subrs, self.GlobalSubrs)
313 for charString in self.CharStrings.values():
314 if charString.needsDecompilation():
315 decompiler.reset()
316 decompiler.execute(charString)
317 else:
318 # Type 1 CharStrings
319 for charString in self.CharStrings.values():
320 charString.decompile()
321
322 def toXML(self, xmlWriter, progress=None):
323 xmlWriter.newline()
324 # first dump the simple values
325 self.toXMLSimpleValues(xmlWriter)
326
327 # dump charset
328 # XXX
329
330 # decompile all charstrings
331 if progress:
332 progress.setlabel("Decompiling CharStrings...")
333 self.decompileAllCharStrings()
334
335 # dump private dict
336 xmlWriter.begintag("Private")
337 xmlWriter.newline()
338 self.Private.toXML(xmlWriter)
339 xmlWriter.endtag("Private")
340 xmlWriter.newline()
341
342 self.toXMLCharStrings(xmlWriter, progress)
343
344 def toXMLSimpleValues(self, xmlWriter):
345 keys = self.__dict__.keys()
346 keys.remove("CharStrings")
347 keys.remove("Private")
348 keys.remove("charset")
349 keys.remove("GlobalSubrs")
350 keys.sort()
351 for key in keys:
352 value = getattr(self, key)
353 if key == "Encoding":
354 if value == 0:
355 # encoding is (Adobe) Standard Encoding
356 value = "StandardEncoding"
357 elif value == 1:
358 # encoding is Expert Encoding
359 value = "ExpertEncoding"
360 if type(value) == types.ListType:
361 value = string.join(map(str, value), " ")
362 else:
363 value = str(value)
364 xmlWriter.begintag(key)
365 if hasattr(value, "toXML"):
366 xmlWriter.newline()
367 value.toXML(xmlWriter)
368 xmlWriter.newline()
369 else:
370 xmlWriter.write(value)
371 xmlWriter.endtag(key)
372 xmlWriter.newline()
373 xmlWriter.newline()
374
375 def toXMLCharStrings(self, xmlWriter, progress=None):
376 charStrings = self.CharStrings
377 xmlWriter.newline()
378 xmlWriter.begintag("CharStrings")
379 xmlWriter.newline()
380 glyphNames = charStrings.keys()
381 glyphNames.sort()
382 for glyphName in glyphNames:
383 if progress:
384 progress.setlabel("Dumping 'CFF ' table... (%s)" % glyphName)
385 progress.increment()
386 xmlWriter.newline()
387 charString = charStrings[glyphName]
388 xmlWriter.begintag("CharString", name=glyphName)
389 xmlWriter.newline()
390 charString.toXML(xmlWriter)
391 xmlWriter.endtag("CharString")
392 xmlWriter.newline()
393 xmlWriter.newline()
394 xmlWriter.endtag("CharStrings")
395 xmlWriter.newline()
396
397
398class PrivateDict:
399
jvrdbc2c172002-05-13 16:19:51 +0000400 defaults = privateDictDefaults
Just7842e561999-12-16 21:34:53 +0000401
402 def __init__(self):
403 pass
404
405 def decompile(self, data, privateData, strings):
jvrdbc2c172002-05-13 16:19:51 +0000406 p = PrivateDictDecompiler(strings)
Just7842e561999-12-16 21:34:53 +0000407 p.decompile(privateData)
408 self.fromDict(p.getDict())
409
410 # get local subrs
411 #print "YYY Private.Subrs:", self.Subrs
Just8ab68262000-03-28 10:37:25 +0000412 if hasattr(self, "Subrs"):
413 chunk = data[self.Subrs:]
414 localSubrs, restdata = readINDEX(chunk)
415 self.Subrs = map(psCharStrings.T2CharString, localSubrs)
416 else:
417 self.Subrs = []
Just7842e561999-12-16 21:34:53 +0000418
419 def toXML(self, xmlWriter):
420 xmlWriter.newline()
421 keys = self.__dict__.keys()
422 keys.remove("Subrs")
423 for key in keys:
424 value = getattr(self, key)
425 if type(value) == types.ListType:
426 value = string.join(map(str, value), " ")
427 else:
428 value = str(value)
429 xmlWriter.begintag(key)
430 xmlWriter.write(value)
431 xmlWriter.endtag(key)
432 xmlWriter.newline()
433 # write subroutines
434 xmlWriter.newline()
435 xmlWriter.begintag("Subrs")
436 xmlWriter.newline()
437 for i in range(len(self.Subrs)):
438 xmlWriter.newline()
439 xmlWriter.begintag("CharString", id=i)
440 xmlWriter.newline()
441 self.Subrs[i].toXML(xmlWriter)
442 xmlWriter.endtag("CharString")
443 xmlWriter.newline()
444 xmlWriter.newline()
445 xmlWriter.endtag("Subrs")
446 xmlWriter.newline()
447 xmlWriter.newline()
448
449 def __getattr__(self, attr):
450 if not self.defaults.has_key(attr):
451 raise AttributeError, attr
452 return self.defaults[attr]
453
454 def fromDict(self, dict):
455 self.__dict__.update(dict)
456
457
jvra2a75b32002-05-13 11:25:17 +0000458def readINDEX(file):
459 count, = struct.unpack(">H", file.read(2))
460 offSize = ord(file.read(1))
Just7842e561999-12-16 21:34:53 +0000461 offsets = []
462 for index in range(count+1):
jvra2a75b32002-05-13 11:25:17 +0000463 chunk = file.read(offSize)
Just7842e561999-12-16 21:34:53 +0000464 chunk = '\0' * (4 - offSize) + chunk
465 offset, = struct.unpack(">L", chunk)
466 offset = int(offset)
467 offsets.append(offset)
Just7842e561999-12-16 21:34:53 +0000468 prev = offsets[0]
469 stuff = []
Just8ab68262000-03-28 10:37:25 +0000470 next = offsets[0]
Just7842e561999-12-16 21:34:53 +0000471 for next in offsets[1:]:
jvra2a75b32002-05-13 11:25:17 +0000472 chunk = file.read(next - prev)
Just7842e561999-12-16 21:34:53 +0000473 assert len(chunk) == next - prev
474 stuff.append(chunk)
475 prev = next
jvra2a75b32002-05-13 11:25:17 +0000476 return stuff
Just7842e561999-12-16 21:34:53 +0000477
478
jvra2a75b32002-05-13 11:25:17 +0000479def parseCharsetFormat1(nGlyphs, file, strings, isCID):
480 charset = ['.notdef']
Just7842e561999-12-16 21:34:53 +0000481 count = 1
482 while count < nGlyphs:
jvra2a75b32002-05-13 11:25:17 +0000483 first, = struct.unpack(">H", file.read(2))
484 nLeft = ord(file.read(1))
485 if isCID:
486 for CID in range(first, first+nLeft+1):
487 charset.append(CID)
488 else:
489 for SID in range(first, first+nLeft+1):
490 charset.append(strings[SID])
Just7842e561999-12-16 21:34:53 +0000491 count = count + nLeft + 1
jvra2a75b32002-05-13 11:25:17 +0000492 return charset
Just7842e561999-12-16 21:34:53 +0000493
494
jvra2a75b32002-05-13 11:25:17 +0000495def parseCharsetFormat2(nGlyphs, file, strings, isCID):
496 charset = ['.notdef']
Just7842e561999-12-16 21:34:53 +0000497 count = 1
498 while count < nGlyphs:
jvra2a75b32002-05-13 11:25:17 +0000499 first, = struct.unpack(">H", file.read(2))
500 nLeft, = struct.unpack(">H", file.read(2))
501 if isCID:
502 for CID in range(first, first+nLeft+1):
503 charset.append(CID)
504 else:
505 for SID in range(first, first+nLeft+1):
506 charset.append(strings[SID])
Just7842e561999-12-16 21:34:53 +0000507 count = count + nLeft + 1
jvra2a75b32002-05-13 11:25:17 +0000508 return charset
Just7842e561999-12-16 21:34:53 +0000509
510
511# The 391 Standard Strings as used in the CFF format.
512# from Adobe Technical None #5176, version 1.0, 18 March 1998
513
514cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
515 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
516 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
517 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
518 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C',
519 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
520 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
521 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
522 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
523 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
524 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
525 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
526 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
527 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
528 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
529 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
530 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
531 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
532 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
533 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
534 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
535 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
536 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
537 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
538 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
539 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
540 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
541 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
542 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
543 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
544 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
545 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
546 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
547 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
548 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
549 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
550 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
551 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
552 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
553 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
554 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
555 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
556 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
557 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
558 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
559 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
560 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
561 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
562 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
563 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
564 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
565 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
566 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
567 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
568 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
569 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
570 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
571 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
572 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
573 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
574 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
575 '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
576 'Semibold'
577]
578
579cffStandardStringCount = 391
580assert len(cffStandardStrings) == cffStandardStringCount
581# build reverse mapping
582cffStandardStringMapping = {}
583for _i in range(cffStandardStringCount):
584 cffStandardStringMapping[cffStandardStrings[_i]] = _i
585
586