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