blob: bb76aa5f0607f985bfbe1dce73c1f62392a05ead [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#
jvr1890b952002-05-15 07:41:30 +00004# $Id: cffLib.py,v 1.12 2002-05-15 07:41:30 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)
jvr1890b952002-05-15 07:41:30 +000034
35 file.seek(4)
36 xfnames = Index(file)
37 xtopds = Index(file)
38 xstrings = Index(file)
39 xgsubrs = Index(file)
40
41 assert xfnames.toList() == self.fontNames
42 assert xtopds.toList() == topDicts
43 assert xstrings.toList() == strings.strings
44 assert xgsubrs.toList() == globalSubrs
45
Just7842e561999-12-16 21:34:53 +000046 self.GlobalSubrs = map(psCharStrings.T2CharString, globalSubrs)
47
48 for i in range(len(topDicts)):
49 font = self.fonts[self.fontNames[i]] = CFFFont()
jvrecf5a792002-05-14 13:51:51 +000050 font.GlobalSubrs = self.GlobalSubrs
51 file.seek(0)
jvr1890b952002-05-15 07:41:30 +000052 font.decompile(file, topDicts[i], strings, self)
Just7842e561999-12-16 21:34:53 +000053
54 def compile(self):
55 strings = IndexedStrings()
56 XXXX
57
58 def toXML(self, xmlWriter, progress=None):
59 xmlWriter.newline()
60 for fontName in self.fontNames:
61 xmlWriter.begintag("CFFFont", name=fontName)
62 xmlWriter.newline()
63 font = self.fonts[fontName]
64 font.toXML(xmlWriter, progress)
65 xmlWriter.endtag("CFFFont")
66 xmlWriter.newline()
67 xmlWriter.newline()
68 xmlWriter.begintag("GlobalSubrs")
69 xmlWriter.newline()
70 for i in range(len(self.GlobalSubrs)):
71 xmlWriter.newline()
jvr1890b952002-05-15 07:41:30 +000072 xmlWriter.begintag("CharString", index=i)
Just7842e561999-12-16 21:34:53 +000073 xmlWriter.newline()
74 self.GlobalSubrs[i].toXML(xmlWriter)
75 xmlWriter.endtag("CharString")
76 xmlWriter.newline()
77 xmlWriter.newline()
78 xmlWriter.endtag("GlobalSubrs")
79 xmlWriter.newline()
80 xmlWriter.newline()
81
82 def fromXML(self, (name, attrs, content)):
83 xxx
84
85
Just7842e561999-12-16 21:34:53 +000086class CFFFont:
87
Just7842e561999-12-16 21:34:53 +000088 def __init__(self):
89 pass
90
91 def __getattr__(self, attr):
jvra20285b2002-05-14 12:37:36 +000092 if not topDictDefaults.has_key(attr):
Just7842e561999-12-16 21:34:53 +000093 raise AttributeError, attr
jvra20285b2002-05-14 12:37:36 +000094 return topDictDefaults[attr]
Just7842e561999-12-16 21:34:53 +000095
jvr1890b952002-05-15 07:41:30 +000096 def fromDict(self, d):
97 self.__dict__.update(d)
jvra2a75b32002-05-13 11:25:17 +000098
99 def decompile(self, file, topDictData, strings, fontSet):
jvrdbc2c172002-05-13 16:19:51 +0000100 top = TopDictDecompiler(strings)
Just7842e561999-12-16 21:34:53 +0000101 top.decompile(topDictData)
102 self.fromDict(top.getDict())
103
jvra2a75b32002-05-13 11:25:17 +0000104 if hasattr(self, "ROS"):
105 isCID = 1
106 # XXX CID subFonts
jvrecf5a792002-05-14 13:51:51 +0000107 offset = self.FDArray
108 file.seek(offset)
109 fontDicts = readINDEX(file)
110 subFonts = self.subFonts = []
111 for topDictData in fontDicts:
112 subFont = CFFFont()
113 subFonts.append(subFont)
114 subFont.decompile(file, topDictData, strings, None)
115 # XXX
jvra2a75b32002-05-13 11:25:17 +0000116 else:
117 isCID = 0
118 size, offset = self.Private
jvrecf5a792002-05-14 13:51:51 +0000119 file.seek(offset)
jvra2a75b32002-05-13 11:25:17 +0000120 privateData = file.read(size)
jvrecf5a792002-05-14 13:51:51 +0000121 file.seek(offset)
jvra2a75b32002-05-13 11:25:17 +0000122 assert len(privateData) == size
123 self.Private = PrivateDict()
124 self.Private.decompile(file, privateData, strings)
Just7842e561999-12-16 21:34:53 +0000125
jvrecf5a792002-05-14 13:51:51 +0000126 if hasattr(self, "CharStrings"):
127 file.seek(self.CharStrings)
jvr1890b952002-05-15 07:41:30 +0000128 rawCharStrings = Index(file)
jvrecf5a792002-05-14 13:51:51 +0000129 nGlyphs = len(rawCharStrings)
Just7842e561999-12-16 21:34:53 +0000130
131 # get charset (or rather: get glyphNames)
jvr1890b952002-05-15 07:41:30 +0000132 if self.charset > 2:
jvra2a75b32002-05-13 11:25:17 +0000133 file.seek(self.charset)
134 format = ord(file.read(1))
Just7842e561999-12-16 21:34:53 +0000135 if format == 0:
jvr1890b952002-05-15 07:41:30 +0000136 raise NotImplementedError
Just7842e561999-12-16 21:34:53 +0000137 elif format == 1:
jvra2a75b32002-05-13 11:25:17 +0000138 charset = parseCharsetFormat1(nGlyphs, file, strings, isCID)
Just7842e561999-12-16 21:34:53 +0000139 elif format == 2:
jvra2a75b32002-05-13 11:25:17 +0000140 charset = parseCharsetFormat2(nGlyphs, file, strings, isCID)
Just7842e561999-12-16 21:34:53 +0000141 elif format == 3:
jvr1890b952002-05-15 07:41:30 +0000142 raise NotImplementedError
Just7842e561999-12-16 21:34:53 +0000143 else:
jvr1890b952002-05-15 07:41:30 +0000144 raise NotImplementedError
jvrecf5a792002-05-14 13:51:51 +0000145 self.charset = charset
146 assert len(charset) == nGlyphs
jvr1890b952002-05-15 07:41:30 +0000147 else:
148 # self.charset:
149 # 0: ISOAdobe (or CID font!)
150 # 1: Expert
151 # 2: ExpertSubset
152 pass
Just7842e561999-12-16 21:34:53 +0000153
jvr1890b952002-05-15 07:41:30 +0000154 if hasattr(self, "CharStrings"):
jvrecf5a792002-05-14 13:51:51 +0000155 self.CharStrings = charStrings = {}
156 if self.CharstringType == 2:
157 # Type 2 CharStrings
158 charStringClass = psCharStrings.T2CharString
159 else:
160 # Type 1 CharStrings
161 charStringClass = psCharStrings.T1CharString
jvrecf5a792002-05-14 13:51:51 +0000162 for i in range(nGlyphs):
163 charStrings[charset[i]] = charStringClass(rawCharStrings[i])
164 assert len(charStrings) == nGlyphs
Just7842e561999-12-16 21:34:53 +0000165
Just7842e561999-12-16 21:34:53 +0000166 encoding = self.Encoding
jvr1890b952002-05-15 07:41:30 +0000167 if encoding > 1:
168 # encoding is an offset from the beginning of 'data' to an encoding subtable
169 raise NotImplementedError
Just7842e561999-12-16 21:34:53 +0000170 self.Encoding = encoding
jvr1890b952002-05-15 07:41:30 +0000171 else:
172 # self.Encoding:
173 # 0 Standard Encoding
174 # 1 Expert Encoding
175 pass
Just7842e561999-12-16 21:34:53 +0000176
177 def getGlyphOrder(self):
178 return self.charset
179
180 def setGlyphOrder(self, glyphOrder):
181 self.charset = glyphOrder
182
183 def decompileAllCharStrings(self):
184 if self.CharstringType == 2:
185 # Type 2 CharStrings
186 decompiler = psCharStrings.SimpleT2Decompiler(self.Private.Subrs, self.GlobalSubrs)
187 for charString in self.CharStrings.values():
188 if charString.needsDecompilation():
189 decompiler.reset()
190 decompiler.execute(charString)
191 else:
192 # Type 1 CharStrings
193 for charString in self.CharStrings.values():
194 charString.decompile()
195
196 def toXML(self, xmlWriter, progress=None):
197 xmlWriter.newline()
jvr1890b952002-05-15 07:41:30 +0000198 genericToXML(self, topDictOrder, {'CharStrings': 'CharString'}, xmlWriter)
Just7842e561999-12-16 21:34:53 +0000199
200
201class PrivateDict:
202
Just7842e561999-12-16 21:34:53 +0000203 def __init__(self):
204 pass
205
jvra20285b2002-05-14 12:37:36 +0000206 def decompile(self, file, privateData, strings):
jvrdbc2c172002-05-13 16:19:51 +0000207 p = PrivateDictDecompiler(strings)
Just7842e561999-12-16 21:34:53 +0000208 p.decompile(privateData)
209 self.fromDict(p.getDict())
210
211 # get local subrs
Just8ab68262000-03-28 10:37:25 +0000212 if hasattr(self, "Subrs"):
jvra20285b2002-05-14 12:37:36 +0000213 file.seek(self.Subrs, 1)
jvr1890b952002-05-15 07:41:30 +0000214 localSubrs = Index(file)
Just8ab68262000-03-28 10:37:25 +0000215 self.Subrs = map(psCharStrings.T2CharString, localSubrs)
216 else:
217 self.Subrs = []
Just7842e561999-12-16 21:34:53 +0000218
219 def toXML(self, xmlWriter):
220 xmlWriter.newline()
jvr1890b952002-05-15 07:41:30 +0000221 genericToXML(self, privateDictOrder, {'Subrs': 'CharString'}, xmlWriter)
Just7842e561999-12-16 21:34:53 +0000222
223 def __getattr__(self, attr):
jvra20285b2002-05-14 12:37:36 +0000224 if not privateDictDefaults.has_key(attr):
Just7842e561999-12-16 21:34:53 +0000225 raise AttributeError, attr
jvra20285b2002-05-14 12:37:36 +0000226 return privateDictDefaults[attr]
Just7842e561999-12-16 21:34:53 +0000227
jvr1890b952002-05-15 07:41:30 +0000228 def fromDict(self, d):
229 self.__dict__.update(d)
Just7842e561999-12-16 21:34:53 +0000230
231
jvra2a75b32002-05-13 11:25:17 +0000232def readINDEX(file):
233 count, = struct.unpack(">H", file.read(2))
jvra20285b2002-05-14 12:37:36 +0000234 if count == 0:
235 return []
jvra2a75b32002-05-13 11:25:17 +0000236 offSize = ord(file.read(1))
Just7842e561999-12-16 21:34:53 +0000237 offsets = []
238 for index in range(count+1):
jvra2a75b32002-05-13 11:25:17 +0000239 chunk = file.read(offSize)
Just7842e561999-12-16 21:34:53 +0000240 chunk = '\0' * (4 - offSize) + chunk
241 offset, = struct.unpack(">L", chunk)
242 offset = int(offset)
243 offsets.append(offset)
jvra20285b2002-05-14 12:37:36 +0000244 offsetBase = file.tell() - 1
Just7842e561999-12-16 21:34:53 +0000245 prev = offsets[0]
246 stuff = []
Just7842e561999-12-16 21:34:53 +0000247 for next in offsets[1:]:
jvra20285b2002-05-14 12:37:36 +0000248 assert offsetBase + prev == file.tell()
jvra2a75b32002-05-13 11:25:17 +0000249 chunk = file.read(next - prev)
Just7842e561999-12-16 21:34:53 +0000250 assert len(chunk) == next - prev
251 stuff.append(chunk)
252 prev = next
jvra2a75b32002-05-13 11:25:17 +0000253 return stuff
Just7842e561999-12-16 21:34:53 +0000254
255
jvr1890b952002-05-15 07:41:30 +0000256class Index:
257
258 def __init__(self, file):
259 self.file = file
260 count, = struct.unpack(">H", file.read(2))
261 self.count = count
262 if count == 0:
263 self.offsets = []
264 return
265 offSize = ord(file.read(1))
266 self.offsets = offsets = []
267 pad = '\0' * (4 - offSize)
268 for index in range(count+1):
269 chunk = file.read(offSize)
270 chunk = pad + chunk
271 offset, = struct.unpack(">L", chunk)
272 offsets.append(int(offset))
273 self.offsetBase = file.tell() - 1
274 file.seek(self.offsetBase + offsets[-1])
275
276 def __len__(self):
277 return self.count
278
279 def __getitem__(self, index):
280 offset = self.offsets[index] + self.offsetBase
281 size = self.offsets[index+1] - self.offsets[index]
282 return FileString(self.file, offset, size)
283
284 def toList(self):
285 l = []
286 for item in self:
287 l.append(item[:])
288 return l
289
290
291class FileString:
292
293 def __init__(self, file, offset, size):
294 self.file = file
295 self.offset = offset
296 self.size = size
297 self.string = None
298
299 def __len__(self):
300 return self.size
301
302 def __getitem__(self, index):
303 return self.get()[index]
304
305 def __getslice__(self, i, j):
306 return self.get()[i:j]
307
308 def get(self):
309 if self.string is None:
310 self.file.seek(self.offset)
311 self.string = self.file.read(self.size)
312 return self.string
313
314
315def getItems(o):
316 if hasattr(o, "items"):
317 items = o.items()
318 items.sort()
319 return "name", items
320 else:
321 return "index", map(None, range(len(o)), o)
322
323
324def genericToXML(obj, order, arrayTypes, xmlWriter):
325 for name in order:
326 value = getattr(obj, name, None)
327 if value is None:
328 continue
329 if hasattr(value, "toXML"):
330 xmlWriter.newline()
331 xmlWriter.begintag(name)
332 xmlWriter.newline()
333 value.toXML(xmlWriter)
334 xmlWriter.endtag(name)
335 xmlWriter.newline()
336 xmlWriter.newline()
337 elif arrayTypes.has_key(name):
338 typeName = arrayTypes[name]
339 xmlWriter.newline()
340 xmlWriter.begintag(name)
341 xmlWriter.newline()
342 xmlWriter.newline()
343 label, items = getItems(value)
344 for k, v in items:
345 xmlWriter.begintag(typeName, [(label, k)])
346 xmlWriter.newline()
347 v.toXML(xmlWriter)
348 xmlWriter.endtag(typeName)
349 xmlWriter.newline()
350 xmlWriter.newline()
351 xmlWriter.endtag(name)
352 xmlWriter.newline()
353 xmlWriter.newline()
354 else:
355 if isinstance(value, types.ListType):
356 value = " ".join(map(str, value))
357 xmlWriter.simpletag(name, value=value)
358 xmlWriter.newline()
359
360
jvra2a75b32002-05-13 11:25:17 +0000361def parseCharsetFormat1(nGlyphs, file, strings, isCID):
362 charset = ['.notdef']
Just7842e561999-12-16 21:34:53 +0000363 count = 1
364 while count < nGlyphs:
jvra2a75b32002-05-13 11:25:17 +0000365 first, = struct.unpack(">H", file.read(2))
366 nLeft = ord(file.read(1))
367 if isCID:
368 for CID in range(first, first+nLeft+1):
369 charset.append(CID)
370 else:
371 for SID in range(first, first+nLeft+1):
372 charset.append(strings[SID])
Just7842e561999-12-16 21:34:53 +0000373 count = count + nLeft + 1
jvra2a75b32002-05-13 11:25:17 +0000374 return charset
Just7842e561999-12-16 21:34:53 +0000375
376
jvra2a75b32002-05-13 11:25:17 +0000377def parseCharsetFormat2(nGlyphs, file, strings, isCID):
378 charset = ['.notdef']
Just7842e561999-12-16 21:34:53 +0000379 count = 1
380 while count < nGlyphs:
jvra2a75b32002-05-13 11:25:17 +0000381 first, = struct.unpack(">H", file.read(2))
382 nLeft, = struct.unpack(">H", file.read(2))
383 if isCID:
384 for CID in range(first, first+nLeft+1):
385 charset.append(CID)
386 else:
387 for SID in range(first, first+nLeft+1):
388 charset.append(strings[SID])
Just7842e561999-12-16 21:34:53 +0000389 count = count + nLeft + 1
jvra2a75b32002-05-13 11:25:17 +0000390 return charset
Just7842e561999-12-16 21:34:53 +0000391
392
jvr1890b952002-05-15 07:41:30 +0000393def buildOperatorDict(table):
394 d = {}
395 for row in table:
396 d[row[0]] = row[1:3]
397 return d
398
399def buildOrder(table):
400 l = []
401 for row in table:
402 l.append(row[1])
403 return l
404
405def buildDefaults(table):
406 d = {}
407 for row in table:
408 if row[3] is not None:
409 d[row[1]] = row[3]
410 return d
411
412
jvre3275582002-05-14 12:22:03 +0000413topDictOperators = [
jvr1890b952002-05-15 07:41:30 +0000414# opcode name argument type default
415 (0, 'version', 'SID', None),
416 (1, 'Notice', 'SID', None),
417 ((12, 0), 'Copyright', 'SID', None),
418 (2, 'FullName', 'SID', None),
419 (3, 'FamilyName', 'SID', None),
420 (4, 'Weight', 'SID', None),
421 ((12, 1), 'isFixedPitch', 'number', 0),
422 ((12, 2), 'ItalicAngle', 'number', 0),
423 ((12, 3), 'UnderlinePosition', 'number', None),
424 ((12, 4), 'UnderlineThickness', 'number', 50),
425 ((12, 5), 'PaintType', 'number', 0),
426 ((12, 6), 'CharstringType', 'number', 2),
427 ((12, 7), 'FontMatrix', 'array', [0.001,0,0,0.001,0,0]),
428 (13, 'UniqueID', 'number', None),
429 (5, 'FontBBox', 'array', [0,0,0,0]),
430 ((12, 8), 'StrokeWidth', 'number', 0),
431 (14, 'XUID', 'array', None),
432 (15, 'charset', 'number', 0),
433 (16, 'Encoding', 'number', 0),
434 (18, 'Private', ('number','number'), None),
435 (17, 'CharStrings', 'number', None),
436 ((12, 20), 'SyntheticBase', 'number', None),
437 ((12, 21), 'PostScript', 'SID', None),
438 ((12, 22), 'BaseFontName', 'SID', None),
439 ((12, 23), 'BaseFontBlend', 'delta', None),
440 ((12, 30), 'ROS', ('SID','SID','number'), None),
441 ((12, 31), 'CIDFontVersion', 'number', 0),
442 ((12, 32), 'CIDFontRevision', 'number', 0),
443 ((12, 33), 'CIDFontType', 'number', 0),
444 ((12, 34), 'CIDCount', 'number', 8720),
445 ((12, 35), 'UIDBase', 'number', None),
446 ((12, 36), 'FDArray', 'number', None),
447 ((12, 37), 'FDSelect', 'number', None),
448 ((12, 38), 'FontName', 'SID', None),
jvre3275582002-05-14 12:22:03 +0000449]
450
jvr1890b952002-05-15 07:41:30 +0000451topDictDefaults = buildDefaults(topDictOperators)
452topDictOrder = buildOrder(topDictOperators)
jvre3275582002-05-14 12:22:03 +0000453
454class TopDictDecompiler(psCharStrings.DictDecompiler):
455
jvr1890b952002-05-15 07:41:30 +0000456 operators = buildOperatorDict(topDictOperators)
jvre3275582002-05-14 12:22:03 +0000457 dictDefaults = topDictDefaults
458
459
460privateDictOperators = [
jvr1890b952002-05-15 07:41:30 +0000461# opcode name argument type default
462 (6, 'BlueValues', 'delta', None),
463 (7, 'OtherBlues', 'delta', None),
464 (8, 'FamilyBlues', 'delta', None),
465 (9, 'FamilyOtherBlues', 'delta', None),
466 ((12, 9), 'BlueScale', 'number', 0.039625),
467 ((12, 10), 'BlueShift', 'number', 7),
468 ((12, 11), 'BlueFuzz', 'number', 1),
469 (10, 'StdHW', 'number', None),
470 (11, 'StdVW', 'number', None),
471 ((12, 12), 'StemSnapH', 'delta', None),
472 ((12, 13), 'StemSnapV', 'delta', None),
473 ((12, 14), 'ForceBold', 'number', 0),
474 ((12, 17), 'LanguageGroup', 'number', 0),
475 ((12, 18), 'ExpansionFactor', 'number', 0.06),
476 ((12, 19), 'initialRandomSeed', 'number', 0),
477 (20, 'defaultWidthX', 'number', 0),
478 (21, 'nominalWidthX', 'number', 0),
479 (19, 'Subrs', 'number', None),
jvre3275582002-05-14 12:22:03 +0000480]
481
jvr1890b952002-05-15 07:41:30 +0000482privateDictDefaults = buildDefaults(privateDictOperators)
483privateDictOrder = buildOrder(privateDictOperators)
jvre3275582002-05-14 12:22:03 +0000484
485class PrivateDictDecompiler(psCharStrings.DictDecompiler):
486
jvr1890b952002-05-15 07:41:30 +0000487 operators = buildOperatorDict(privateDictOperators)
jvre3275582002-05-14 12:22:03 +0000488 dictDefaults = privateDictDefaults
489
490
491class IndexedStrings:
492
493 def __init__(self, strings=None):
494 if strings is None:
495 strings = []
496 self.strings = strings
497
498 def __getitem__(self, SID):
499 if SID < cffStandardStringCount:
500 return cffStandardStrings[SID]
501 else:
502 return self.strings[SID - cffStandardStringCount]
503
504 def getSID(self, s):
505 if not hasattr(self, "stringMapping"):
506 self.buildStringMapping()
507 if cffStandardStringMapping.has_key(s):
508 SID = cffStandardStringMapping[s]
509 if self.stringMapping.has_key(s):
510 SID = self.stringMapping[s]
511 else:
512 SID = len(self.strings) + cffStandardStringCount
513 self.strings.append(s)
514 self.stringMapping[s] = SID
515 return SID
516
517 def getStrings(self):
518 return self.strings
519
520 def buildStringMapping(self):
521 self.stringMapping = {}
522 for index in range(len(self.strings)):
523 self.stringMapping[self.strings[index]] = index + cffStandardStringCount
524
525
Just7842e561999-12-16 21:34:53 +0000526# The 391 Standard Strings as used in the CFF format.
527# from Adobe Technical None #5176, version 1.0, 18 March 1998
528
529cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
530 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
531 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
532 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
533 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C',
534 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
535 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
536 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
537 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
538 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
539 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
540 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
541 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
542 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
543 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
544 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
545 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
546 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
547 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
548 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
549 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
550 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
551 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
552 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
553 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
554 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
555 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
556 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
557 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
558 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
559 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
560 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
561 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
562 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
563 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
564 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
565 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
566 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
567 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
568 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
569 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
570 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
571 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
572 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
573 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
574 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
575 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
576 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
577 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
578 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
579 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
580 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
581 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
582 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
583 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
584 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
585 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
586 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
587 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
588 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
589 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
590 '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
591 'Semibold'
592]
593
594cffStandardStringCount = 391
595assert len(cffStandardStrings) == cffStandardStringCount
596# build reverse mapping
597cffStandardStringMapping = {}
598for _i in range(cffStandardStringCount):
599 cffStandardStringMapping[cffStandardStrings[_i]] = _i
600
601