add a convenience function for mering attrs with default attrs, checking, and None...
[PyX.git] / font / afmfile.py
blob93dcad139f9e31c80c35356b5cc6de55f7bbca90
1 # -*- encoding: utf-8 -*-
4 # Copyright (C) 2006-2011 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2007-2011 André Wobst <wobsta@users.sourceforge.net>
7 # This file is part of PyX (http://pyx.sourceforge.net/).
9 # PyX is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # PyX is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with PyX; if not, write to the Free Software
21 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 import string
24 from . import metric
26 unicodestring = {" ": "space",
27 "!": "exclam",
28 "\"": "quotedbl",
29 "#": "numbersign",
30 "$": "dollar",
31 "%": "percent",
32 "&": "ampersand",
33 "'": "quotesingle",
34 "(": "parenleft",
35 ")": "parenright",
36 "*": "asterisk",
37 "+": "plus",
38 ",": "comma",
39 "-": "hyphen",
40 ".": "period",
41 "/": "slash",
42 "0": "zero",
43 "1": "one",
44 "2": "two",
45 "3": "three",
46 "4": "four",
47 "5": "five",
48 "6": "six",
49 "7": "seven",
50 "8": "eight",
51 "9": "nine",
52 ":": "colon",
53 ";": "semicolon",
54 "<": "less",
55 "=": "equal",
56 ">": "greater",
57 "?": "question",
58 "@": "at",
59 "A": "A",
60 "B": "B",
61 "C": "C",
62 "D": "D",
63 "E": "E",
64 "F": "F",
65 "G": "G",
66 "H": "H",
67 "I": "I",
68 "J": "J",
69 "K": "K",
70 "L": "L",
71 "M": "M",
72 "N": "N",
73 "O": "O",
74 "P": "P",
75 "Q": "Q",
76 "R": "R",
77 "S": "S",
78 "T": "T",
79 "U": "U",
80 "V": "V",
81 "W": "W",
82 "X": "X",
83 "Y": "Y",
84 "Z": "Z",
85 "[": "bracketleft",
86 "\\": "backslash",
87 "]": "bracketright",
88 "^": "asciicircum",
89 "_": "underscore",
90 "`": "grave",
91 "a": "a",
92 "b": "b",
93 "c": "c",
94 "d": "d",
95 "e": "e",
96 "f": "f",
97 "g": "g",
98 "h": "h",
99 "i": "i",
100 "j": "j",
101 "k": "k",
102 "l": "l",
103 "m": "m",
104 "n": "n",
105 "o": "o",
106 "p": "p",
107 "q": "q",
108 "r": "r",
109 "s": "s",
110 "t": "t",
111 "u": "u",
112 "v": "v",
113 "w": "w",
114 "x": "x",
115 "y": "y",
116 "z": "z",
117 "{": "braceleft",
118 "|": "bar",
119 "}": "braceright",
120 "~": "asciitilde",
121 "\xa0": "space",
122 "\xa1": "exclamdown",
123 "\xa2": "cent",
124 "\xa3": "sterling",
125 "\xa4": "currency",
126 "\xa5": "yen",
127 "\xa6": "brokenbar",
128 "\xa7": "section",
129 "\xa8": "dieresis",
130 "\xa9": "copyright",
131 "\xaa": "ordfeminine",
132 "\xab": "guillemotleft",
133 "\xac": "logicalnot",
134 "\xad": "hyphen",
135 "\xae": "registered",
136 "\xaf": "macron",
137 "\xb0": "degree",
138 "\xb1": "plusminus",
139 "\xb4": "acute",
140 "\xb6": "paragraph",
141 "\xb7": "periodcentered",
142 "\xb8": "cedilla",
143 "\xba": "ordmasculine",
144 "\xbb": "guillemotright",
145 "\xbc": "onequarter",
146 "\xbd": "onehalf",
147 "\xbe": "threequarters",
148 "\xbf": "questiondown",
149 "\xc0": "Agrave",
150 "\xc1": "Aacute",
151 "\xc2": "Acircumflex",
152 "\xc3": "Atilde",
153 "\xc4": "Adieresis",
154 "\xc5": "Aring",
155 "\xc6": "AE",
156 "\xc7": "Ccedilla",
157 "\xc8": "Egrave",
158 "\xc9": "Eacute",
159 "\xca": "Ecircumflex",
160 "\xcb": "Edieresis",
161 "\xcc": "Igrave",
162 "\xcd": "Iacute",
163 "\xce": "Icircumflex",
164 "\xcf": "Idieresis",
165 "\xd0": "Eth",
166 "\xd1": "Ntilde",
167 "\xd2": "Ograve",
168 "\xd3": "Oacute",
169 "\xd4": "Ocircumflex",
170 "\xd5": "Otilde",
171 "\xd6": "Odieresis",
172 "\xd7": "multiply",
173 "\xd8": "Oslash",
174 "\xd9": "Ugrave",
175 "\xda": "Uacute",
176 "\xdb": "Ucircumflex",
177 "\xdc": "Udieresis",
178 "\xdd": "Yacute",
179 "\xde": "Thorn",
180 "\xdf": "germandbls",
181 "\xe0": "agrave",
182 "\xe1": "aacute",
183 "\xe2": "acircumflex",
184 "\xe3": "atilde",
185 "\xe4": "adieresis",
186 "\xe5": "aring",
187 "\xe6": "ae",
188 "\xe7": "ccedilla",
189 "\xe8": "egrave",
190 "\xe9": "eacute",
191 "\xea": "ecircumflex",
192 "\xeb": "edieresis",
193 "\xec": "igrave",
194 "\xed": "iacute",
195 "\xee": "icircumflex",
196 "\xef": "idieresis",
197 "\xf0": "eth",
198 "\xf1": "ntilde",
199 "\xf2": "ograve",
200 "\xf3": "oacute",
201 "\xf4": "ocircumflex",
202 "\xf5": "otilde",
203 "\xf6": "odieresis",
204 "\xf7": "divide",
205 "\xf8": "oslash",
206 "\xf9": "ugrave",
207 "\xfa": "uacute",
208 "\xfb": "ucircumflex",
209 "\xfc": "udieresis",
210 "\xfd": "yacute",
211 "\xfe": "thorn",
212 "\xff": "ydieresis",
213 "\u0100": "Amacron",
214 "\u0101": "amacron",
215 "\u0102": "Abreve",
216 "\u0103": "abreve",
217 "\u0104": "Aogonek",
218 "\u0105": "aogonek",
219 "\u0106": "Cacute",
220 "\u0107": "cacute",
221 "\u0108": "Ccircumflex",
222 "\u0109": "ccircumflex",
223 "\u010a": "Cdotaccent",
224 "\u010b": "cdotaccent",
225 "\u010c": "Ccaron",
226 "\u010d": "ccaron",
227 "\u010e": "Dcaron",
228 "\u010f": "dcaron",
229 "\u0110": "Dcroat",
230 "\u0111": "dcroat",
231 "\u0112": "Emacron",
232 "\u0113": "emacron",
233 "\u0114": "Ebreve",
234 "\u0115": "ebreve",
235 "\u0116": "Edotaccent",
236 "\u0117": "edotaccent",
237 "\u0118": "Eogonek",
238 "\u0119": "eogonek",
239 "\u011a": "Ecaron",
240 "\u011b": "ecaron",
241 "\u011c": "Gcircumflex",
242 "\u011d": "gcircumflex",
243 "\u011e": "Gbreve",
244 "\u011f": "gbreve",
245 "\u0120": "Gdotaccent",
246 "\u0121": "gdotaccent",
247 "\u0122": "Gcommaaccent",
248 "\u0123": "gcommaaccent",
249 "\u0124": "Hcircumflex",
250 "\u0125": "hcircumflex",
251 "\u0126": "Hbar",
252 "\u0127": "hbar",
253 "\u0128": "Itilde",
254 "\u0129": "itilde",
255 "\u012a": "Imacron",
256 "\u012b": "imacron",
257 "\u012c": "Ibreve",
258 "\u012d": "ibreve",
259 "\u012e": "Iogonek",
260 "\u012f": "iogonek",
261 "\u0130": "Idotaccent",
262 "\u0131": "dotlessi",
263 "\u0132": "IJ",
264 "\u0133": "ij",
265 "\u0134": "Jcircumflex",
266 "\u0135": "jcircumflex",
267 "\u0136": "Kcommaaccent",
268 "\u0137": "kcommaaccent",
269 "\u0138": "kgreenlandic",
270 "\u0139": "Lacute",
271 "\u013a": "lacute",
272 "\u013b": "Lcommaaccent",
273 "\u013c": "lcommaaccent",
274 "\u013d": "Lcaron",
275 "\u013e": "lcaron",
276 "\u013f": "Ldot",
277 "\u0140": "ldot",
278 "\u0141": "Lslash",
279 "\u0142": "lslash",
280 "\u0143": "Nacute",
281 "\u0144": "nacute",
282 "\u0145": "Ncommaaccent",
283 "\u0146": "ncommaaccent",
284 "\u0147": "Ncaron",
285 "\u0148": "ncaron",
286 "\u0149": "napostrophe",
287 "\u014a": "Eng",
288 "\u014b": "eng",
289 "\u014c": "Omacron",
290 "\u014d": "omacron",
291 "\u014e": "Obreve",
292 "\u014f": "obreve",
293 "\u0150": "Ohungarumlaut",
294 "\u0151": "ohungarumlaut",
295 "\u0152": "OE",
296 "\u0153": "oe",
297 "\u0154": "Racute",
298 "\u0155": "racute",
299 "\u0156": "Rcommaaccent",
300 "\u0157": "rcommaaccent",
301 "\u0158": "Rcaron",
302 "\u0159": "rcaron",
303 "\u015a": "Sacute",
304 "\u015b": "sacute",
305 "\u015c": "Scircumflex",
306 "\u015d": "scircumflex",
307 "\u015e": "Scedilla",
308 "\u015f": "scedilla",
309 "\u0160": "Scaron",
310 "\u0161": "scaron",
311 "\u0162": "Tcommaaccent",
312 "\u0163": "tcommaaccent",
313 "\u0164": "Tcaron",
314 "\u0165": "tcaron",
315 "\u0166": "Tbar",
316 "\u0167": "tbar",
317 "\u0168": "Utilde",
318 "\u0169": "utilde",
319 "\u016a": "Umacron",
320 "\u016b": "umacron",
321 "\u016c": "Ubreve",
322 "\u016d": "ubreve",
323 "\u016e": "Uring",
324 "\u016f": "uring",
325 "\u0170": "Uhungarumlaut",
326 "\u0171": "uhungarumlaut",
327 "\u0172": "Uogonek",
328 "\u0173": "uogonek",
329 "\u0174": "Wcircumflex",
330 "\u0175": "wcircumflex",
331 "\u0176": "Ycircumflex",
332 "\u0177": "ycircumflex",
333 "\u0178": "Ydieresis",
334 "\u0179": "Zacute",
335 "\u017a": "zacute",
336 "\u017b": "Zdotaccent",
337 "\u017c": "zdotaccent",
338 "\u017d": "Zcaron",
339 "\u017e": "zcaron",
340 "\u017f": "longs",
341 "\u0192": "florin",
342 "\u01a0": "Ohorn",
343 "\u01a1": "ohorn",
344 "\u01af": "Uhorn",
345 "\u01b0": "uhorn",
346 "\u01e6": "Gcaron",
347 "\u01e7": "gcaron",
348 "\u01fa": "Aringacute",
349 "\u01fb": "aringacute",
350 "\u01fc": "AEacute",
351 "\u01fd": "aeacute",
352 "\u01fe": "Oslashacute",
353 "\u01ff": "oslashacute",
354 "\u0218": "Scommaaccent",
355 "\u0219": "scommaaccent",
356 "\u02bc": "afii57929",
357 "\u02bd": "afii64937",
358 "\u02c6": "circumflex",
359 "\u02c7": "caron",
360 "\u02c9": "macron",
361 "\u02d8": "breve",
362 "\u02d9": "dotaccent",
363 "\u02da": "ring",
364 "\u02db": "ogonek",
365 "\u02dc": "tilde",
366 "\u02dd": "hungarumlaut",
367 "\u0300": "gravecomb",
368 "\u0301": "acutecomb",
369 "\u0303": "tildecomb",
370 "\u0309": "hookabovecomb",
371 "\u0323": "dotbelowcomb",
372 "\u0384": "tonos",
373 "\u0385": "dieresistonos",
374 "\u0386": "Alphatonos",
375 "\u0387": "anoteleia",
376 "\u0388": "Epsilontonos",
377 "\u0389": "Etatonos",
378 "\u038a": "Iotatonos",
379 "\u038c": "Omicrontonos",
380 "\u038e": "Upsilontonos",
381 "\u038f": "Omegatonos",
382 "\u0390": "iotadieresistonos",
383 "\u0391": "Alpha",
384 "\u0392": "Beta",
385 "\u0393": "Gamma",
386 "\u0394": "Delta",
387 "\u0395": "Epsilon",
388 "\u0396": "Zeta",
389 "\u0397": "Eta",
390 "\u0398": "Theta",
391 "\u0399": "Iota",
392 "\u039a": "Kappa",
393 "\u039b": "Lambda",
394 "\u039c": "Mu",
395 "\u039d": "Nu",
396 "\u039e": "Xi",
397 "\u039f": "Omicron",
398 "\u03a0": "Pi",
399 "\u03a1": "Rho",
400 "\u03a3": "Sigma",
401 "\u03a4": "Tau",
402 "\u03a5": "Upsilon",
403 "\u03a6": "Phi",
404 "\u03a7": "Chi",
405 "\u03a8": "Psi",
406 "\u03a9": "Omega",
407 "\u03aa": "Iotadieresis",
408 "\u03ab": "Upsilondieresis",
409 "\u03ac": "alphatonos",
410 "\u03ad": "epsilontonos",
411 "\u03ae": "etatonos",
412 "\u03af": "iotatonos",
413 "\u03b0": "upsilondieresistonos",
414 "\u03b1": "alpha",
415 "\u03b2": "beta",
416 "\u03b3": "gamma",
417 "\u03b4": "delta",
418 "\u03b5": "epsilon",
419 "\u03b6": "zeta",
420 "\u03b7": "eta",
421 "\u03b8": "theta",
422 "\u03b9": "iota",
423 "\u03ba": "kappa",
424 "\u03bb": "lambda",
425 "\u03bc": "mu",
426 "\u03bd": "nu",
427 "\u03be": "xi",
428 "\u03bf": "omicron",
429 "\u03c0": "pi",
430 "\u03c1": "rho",
431 "\u03c2": "sigma1",
432 "\u03c3": "sigma",
433 "\u03c4": "tau",
434 "\u03c5": "upsilon",
435 "\u03c6": "phi",
436 "\u03c7": "chi",
437 "\u03c8": "psi",
438 "\u03c9": "omega",
439 "\u03ca": "iotadieresis",
440 "\u03cb": "upsilondieresis",
441 "\u03cc": "omicrontonos",
442 "\u03cd": "upsilontonos",
443 "\u03ce": "omegatonos",
444 "\u03d1": "theta1",
445 "\u03d2": "Upsilon1",
446 "\u03d5": "phi1",
447 "\u03d6": "omega1",
448 "\u0401": "afii10023",
449 "\u0402": "afii10051",
450 "\u0403": "afii10052",
451 "\u0404": "afii10053",
452 "\u0405": "afii10054",
453 "\u0406": "afii10055",
454 "\u0407": "afii10056",
455 "\u0408": "afii10057",
456 "\u0409": "afii10058",
457 "\u040a": "afii10059",
458 "\u040b": "afii10060",
459 "\u040c": "afii10061",
460 "\u040e": "afii10062",
461 "\u040f": "afii10145",
462 "\u0410": "afii10017",
463 "\u0411": "afii10018",
464 "\u0412": "afii10019",
465 "\u0413": "afii10020",
466 "\u0414": "afii10021",
467 "\u0415": "afii10022",
468 "\u0416": "afii10024",
469 "\u0417": "afii10025",
470 "\u0418": "afii10026",
471 "\u0419": "afii10027",
472 "\u041a": "afii10028",
473 "\u041b": "afii10029",
474 "\u041c": "afii10030",
475 "\u041d": "afii10031",
476 "\u041e": "afii10032",
477 "\u041f": "afii10033",
478 "\u0420": "afii10034",
479 "\u0421": "afii10035",
480 "\u0422": "afii10036",
481 "\u0423": "afii10037",
482 "\u0424": "afii10038",
483 "\u0425": "afii10039",
484 "\u0426": "afii10040",
485 "\u0427": "afii10041",
486 "\u0428": "afii10042",
487 "\u0429": "afii10043",
488 "\u042a": "afii10044",
489 "\u042b": "afii10045",
490 "\u042c": "afii10046",
491 "\u042d": "afii10047",
492 "\u042e": "afii10048",
493 "\u042f": "afii10049",
494 "\u0430": "afii10065",
495 "\u0431": "afii10066",
496 "\u0432": "afii10067",
497 "\u0433": "afii10068",
498 "\u0434": "afii10069",
499 "\u0435": "afii10070",
500 "\u0436": "afii10072",
501 "\u0437": "afii10073",
502 "\u0438": "afii10074",
503 "\u0439": "afii10075",
504 "\u043a": "afii10076",
505 "\u043b": "afii10077",
506 "\u043c": "afii10078",
507 "\u043d": "afii10079",
508 "\u043e": "afii10080",
509 "\u043f": "afii10081",
510 "\u0440": "afii10082",
511 "\u0441": "afii10083",
512 "\u0442": "afii10084",
513 "\u0443": "afii10085",
514 "\u0444": "afii10086",
515 "\u0445": "afii10087",
516 "\u0446": "afii10088",
517 "\u0447": "afii10089",
518 "\u0448": "afii10090",
519 "\u0449": "afii10091",
520 "\u044a": "afii10092",
521 "\u044b": "afii10093",
522 "\u044c": "afii10094",
523 "\u044d": "afii10095",
524 "\u044e": "afii10096",
525 "\u044f": "afii10097",
526 "\u0451": "afii10071",
527 "\u0452": "afii10099",
528 "\u0453": "afii10100",
529 "\u0454": "afii10101",
530 "\u0455": "afii10102",
531 "\u0456": "afii10103",
532 "\u0457": "afii10104",
533 "\u0458": "afii10105",
534 "\u0459": "afii10106",
535 "\u045a": "afii10107",
536 "\u045b": "afii10108",
537 "\u045c": "afii10109",
538 "\u045e": "afii10110",
539 "\u045f": "afii10193",
540 "\u0462": "afii10146",
541 "\u0463": "afii10194",
542 "\u0472": "afii10147",
543 "\u0473": "afii10195",
544 "\u0474": "afii10148",
545 "\u0475": "afii10196",
546 "\u0490": "afii10050",
547 "\u0491": "afii10098",
548 "\u04d9": "afii10846",
549 "\u05b0": "afii57799",
550 "\u05b1": "afii57801",
551 "\u05b2": "afii57800",
552 "\u05b3": "afii57802",
553 "\u05b4": "afii57793",
554 "\u05b5": "afii57794",
555 "\u05b6": "afii57795",
556 "\u05b7": "afii57798",
557 "\u05b8": "afii57797",
558 "\u05b9": "afii57806",
559 "\u05bb": "afii57796",
560 "\u05bc": "afii57807",
561 "\u05bd": "afii57839",
562 "\u05be": "afii57645",
563 "\u05bf": "afii57841",
564 "\u05c0": "afii57842",
565 "\u05c1": "afii57804",
566 "\u05c2": "afii57803",
567 "\u05c3": "afii57658",
568 "\u05d0": "afii57664",
569 "\u05d1": "afii57665",
570 "\u05d2": "afii57666",
571 "\u05d3": "afii57667",
572 "\u05d4": "afii57668",
573 "\u05d5": "afii57669",
574 "\u05d6": "afii57670",
575 "\u05d7": "afii57671",
576 "\u05d8": "afii57672",
577 "\u05d9": "afii57673",
578 "\u05da": "afii57674",
579 "\u05db": "afii57675",
580 "\u05dc": "afii57676",
581 "\u05dd": "afii57677",
582 "\u05de": "afii57678",
583 "\u05df": "afii57679",
584 "\u05e0": "afii57680",
585 "\u05e1": "afii57681",
586 "\u05e2": "afii57682",
587 "\u05e3": "afii57683",
588 "\u05e4": "afii57684",
589 "\u05e5": "afii57685",
590 "\u05e6": "afii57686",
591 "\u05e7": "afii57687",
592 "\u05e8": "afii57688",
593 "\u05e9": "afii57689",
594 "\u05ea": "afii57690",
595 "\u05f0": "afii57716",
596 "\u05f1": "afii57717",
597 "\u05f2": "afii57718",
598 "\u060c": "afii57388",
599 "\u061b": "afii57403",
600 "\u061f": "afii57407",
601 "\u0621": "afii57409",
602 "\u0622": "afii57410",
603 "\u0623": "afii57411",
604 "\u0624": "afii57412",
605 "\u0625": "afii57413",
606 "\u0626": "afii57414",
607 "\u0627": "afii57415",
608 "\u0628": "afii57416",
609 "\u0629": "afii57417",
610 "\u062a": "afii57418",
611 "\u062b": "afii57419",
612 "\u062c": "afii57420",
613 "\u062d": "afii57421",
614 "\u062e": "afii57422",
615 "\u062f": "afii57423",
616 "\u0630": "afii57424",
617 "\u0631": "afii57425",
618 "\u0632": "afii57426",
619 "\u0633": "afii57427",
620 "\u0634": "afii57428",
621 "\u0635": "afii57429",
622 "\u0636": "afii57430",
623 "\u0637": "afii57431",
624 "\u0638": "afii57432",
625 "\u0639": "afii57433",
626 "\u063a": "afii57434",
627 "\u0640": "afii57440",
628 "\u0641": "afii57441",
629 "\u0642": "afii57442",
630 "\u0643": "afii57443",
631 "\u0644": "afii57444",
632 "\u0645": "afii57445",
633 "\u0646": "afii57446",
634 "\u0647": "afii57470",
635 "\u0648": "afii57448",
636 "\u0649": "afii57449",
637 "\u064a": "afii57450",
638 "\u064b": "afii57451",
639 "\u064c": "afii57452",
640 "\u064d": "afii57453",
641 "\u064e": "afii57454",
642 "\u064f": "afii57455",
643 "\u0650": "afii57456",
644 "\u0651": "afii57457",
645 "\u0652": "afii57458",
646 "\u0660": "afii57392",
647 "\u0661": "afii57393",
648 "\u0662": "afii57394",
649 "\u0663": "afii57395",
650 "\u0664": "afii57396",
651 "\u0665": "afii57397",
652 "\u0666": "afii57398",
653 "\u0667": "afii57399",
654 "\u0668": "afii57400",
655 "\u0669": "afii57401",
656 "\u066a": "afii57381",
657 "\u066d": "afii63167",
658 "\u0679": "afii57511",
659 "\u067e": "afii57506",
660 "\u0686": "afii57507",
661 "\u0688": "afii57512",
662 "\u0691": "afii57513",
663 "\u0698": "afii57508",
664 "\u06a4": "afii57505",
665 "\u06af": "afii57509",
666 "\u06ba": "afii57514",
667 "\u06d2": "afii57519",
668 "\u06d5": "afii57534",
669 "\u1e80": "Wgrave",
670 "\u1e81": "wgrave",
671 "\u1e82": "Wacute",
672 "\u1e83": "wacute",
673 "\u1e84": "Wdieresis",
674 "\u1e85": "wdieresis",
675 "\u1ef2": "Ygrave",
676 "\u1ef3": "ygrave",
677 "\u200c": "afii61664",
678 "\u200d": "afii301",
679 "\u200e": "afii299",
680 "\u200f": "afii300",
681 "\u2012": "figuredash",
682 "\u2013": "endash",
683 "\u2014": "emdash",
684 "\u2015": "afii00208",
685 "\u2017": "underscoredbl",
686 "\u2018": "quoteleft",
687 "\u2019": "quoteright",
688 "\u201a": "quotesinglbase",
689 "\u201b": "quotereversed",
690 "\u201c": "quotedblleft",
691 "\u201d": "quotedblright",
692 "\u201e": "quotedblbase",
693 "\u2020": "dagger",
694 "\u2021": "daggerdbl",
695 "\u2022": "bullet",
696 "\u2024": "onedotenleader",
697 "\u2025": "twodotenleader",
698 "\u2026": "ellipsis",
699 "\u202c": "afii61573",
700 "\u202d": "afii61574",
701 "\u202e": "afii61575",
702 "\u2030": "perthousand",
703 "\u2032": "minute",
704 "\u2033": "second",
705 "\u2039": "guilsinglleft",
706 "\u203a": "guilsinglright",
707 "\u203c": "exclamdbl",
708 "\u2044": "fraction",
709 "\u20a1": "colonmonetary",
710 "\u20a3": "franc",
711 "\u20a4": "lira",
712 "\u20a7": "peseta",
713 "\u20aa": "afii57636",
714 "\u20ab": "dong",
715 "\u20ac": "Euro",
716 "\u2105": "afii61248",
717 "\u2111": "Ifraktur",
718 "\u2113": "afii61289",
719 "\u2116": "afii61352",
720 "\u2118": "weierstrass",
721 "\u211c": "Rfraktur",
722 "\u211e": "prescription",
723 "\u2122": "trademark",
724 "\u212e": "estimated",
725 "\u2135": "aleph",
726 "\u2153": "onethird",
727 "\u2154": "twothirds",
728 "\u215b": "oneeighth",
729 "\u215c": "threeeighths",
730 "\u215d": "fiveeighths",
731 "\u215e": "seveneighths",
732 "\u2190": "arrowleft",
733 "\u2191": "arrowup",
734 "\u2192": "arrowright",
735 "\u2193": "arrowdown",
736 "\u2194": "arrowboth",
737 "\u2195": "arrowupdn",
738 "\u21a8": "arrowupdnbse",
739 "\u21b5": "carriagereturn",
740 "\u21d0": "arrowdblleft",
741 "\u21d1": "arrowdblup",
742 "\u21d2": "arrowdblright",
743 "\u21d3": "arrowdbldown",
744 "\u21d4": "arrowdblboth",
745 "\u2200": "universal",
746 "\u2202": "partialdiff",
747 "\u2203": "existential",
748 "\u2205": "emptyset",
749 "\u2207": "gradient",
750 "\u2208": "element",
751 "\u2209": "notelement",
752 "\u220b": "suchthat",
753 "\u220f": "product",
754 "\u2211": "summation",
755 "\u2212": "minus",
756 "\u2215": "fraction",
757 "\u2217": "asteriskmath",
758 "\u2219": "periodcentered",
759 "\u221a": "radical",
760 "\u221d": "proportional",
761 "\u221e": "infinity",
762 "\u221f": "orthogonal",
763 "\u2220": "angle",
764 "\u2227": "logicaland",
765 "\u2228": "logicalor",
766 "\u2229": "intersection",
767 "\u222a": "union",
768 "\u222b": "integral",
769 "\u2234": "therefore",
770 "\u223c": "similar",
771 "\u2245": "congruent",
772 "\u2248": "approxequal",
773 "\u2260": "notequal",
774 "\u2261": "equivalence",
775 "\u2264": "lessequal",
776 "\u2265": "greaterequal",
777 "\u2282": "propersubset",
778 "\u2283": "propersuperset",
779 "\u2284": "notsubset",
780 "\u2286": "reflexsubset",
781 "\u2287": "reflexsuperset",
782 "\u2295": "circleplus",
783 "\u2297": "circlemultiply",
784 "\u22a5": "perpendicular",
785 "\u22c5": "dotmath",
786 "\u2302": "house",
787 "\u2310": "revlogicalnot",
788 "\u2320": "integraltp",
789 "\u2321": "integralbt",
790 "\u2329": "angleleft",
791 "\u232a": "angleright",
792 "\u2500": "SF100000",
793 "\u2502": "SF110000",
794 "\u250c": "SF010000",
795 "\u2510": "SF030000",
796 "\u2514": "SF020000",
797 "\u2518": "SF040000",
798 "\u251c": "SF080000",
799 "\u2524": "SF090000",
800 "\u252c": "SF060000",
801 "\u2534": "SF070000",
802 "\u253c": "SF050000",
803 "\u2550": "SF430000",
804 "\u2551": "SF240000",
805 "\u2552": "SF510000",
806 "\u2553": "SF520000",
807 "\u2554": "SF390000",
808 "\u2555": "SF220000",
809 "\u2556": "SF210000",
810 "\u2557": "SF250000",
811 "\u2558": "SF500000",
812 "\u2559": "SF490000",
813 "\u255a": "SF380000",
814 "\u255b": "SF280000",
815 "\u255c": "SF270000",
816 "\u255d": "SF260000",
817 "\u255e": "SF360000",
818 "\u255f": "SF370000",
819 "\u2560": "SF420000",
820 "\u2561": "SF190000",
821 "\u2562": "SF200000",
822 "\u2563": "SF230000",
823 "\u2564": "SF470000",
824 "\u2565": "SF480000",
825 "\u2566": "SF410000",
826 "\u2567": "SF450000",
827 "\u2568": "SF460000",
828 "\u2569": "SF400000",
829 "\u256a": "SF540000",
830 "\u256b": "SF530000",
831 "\u256c": "SF440000",
832 "\u2580": "upblock",
833 "\u2584": "dnblock",
834 "\u2588": "block",
835 "\u258c": "lfblock",
836 "\u2590": "rtblock",
837 "\u2591": "ltshade",
838 "\u2592": "shade",
839 "\u2593": "dkshade",
840 "\u25a0": "filledbox",
841 "\u25a1": "H22073",
842 "\u25aa": "H18543",
843 "\u25ab": "H18551",
844 "\u25ac": "filledrect",
845 "\u25b2": "triagup",
846 "\u25ba": "triagrt",
847 "\u25bc": "triagdn",
848 "\u25c4": "triaglf",
849 "\u25ca": "lozenge",
850 "\u25cb": "circle",
851 "\u25cf": "H18533",
852 "\u25d8": "invbullet",
853 "\u25d9": "invcircle",
854 "\u25e6": "openbullet",
855 "\u263a": "smileface",
856 "\u263b": "invsmileface",
857 "\u263c": "sun",
858 "\u2640": "female",
859 "\u2642": "male",
860 "\u2660": "spade",
861 "\u2663": "club",
862 "\u2665": "heart",
863 "\u2666": "diamond",
864 "\u266a": "musicalnote",
865 "\u266b": "musicalnotedbl",
866 "\ufb01": "fi",
867 "\ufb02": "fl"}
869 class AFMError(Exception):
870 pass
872 # reader states
873 _READ_START = 0
874 _READ_MAIN = 1
875 _READ_DIRECTION = 2
876 _READ_CHARMETRICS = 3
877 _READ_KERNDATA = 4
878 _READ_TRACKKERN = 5
879 _READ_KERNPAIRS = 6
880 _READ_COMPOSITES = 7
881 _READ_END = 8
883 # various parsing functions
884 def _parseint(s):
885 try:
886 return int(s)
887 except:
888 raise AFMError("Expecting int, got '%s'" % s)
890 def _parsehex(s):
891 try:
892 if s[0] != "<" or s[-1] != ">":
893 raise AFMError()
894 return int(s[1:-1], 16)
895 except:
896 raise AFMError("Expecting hexadecimal int, got '%s'" % s)
898 def _parsefloat(s):
899 try:
900 return float(s)
901 except:
902 raise AFMError("Expecting float, got '%s'" % s)
904 def _parsefloats(s, nos):
905 try:
906 numbers = s.split()
907 result = list(map(float, numbers))
908 if len(result) != nos:
909 raise AFMError()
910 except:
911 raise AFMError("Expecting list of %d numbers, got '%s'" % (s, nos))
912 return result
914 def _parsestr(s):
915 # XXX: check for invalid characters in s
916 return s
918 def _parsebool(s):
919 s = s.rstrip()
920 if s == "true":
921 return True
922 elif s == "false":
923 return False
924 else:
925 raise AFMError("Expecting boolean, got '%s'" % s)
928 class AFMcharmetrics:
929 def __init__(self, code, widths=None, vvector=None, name=None, bbox=None, ligatures=None):
930 self.code = code
931 if widths is None:
932 self.widths = [None] * 2
933 else:
934 self.widths = widths
935 self.vvector = vvector
936 self.name = name
937 self.bbox = bbox
938 if ligatures is None:
939 self.ligatures = []
940 else:
941 self.ligatures = ligatures
944 class AFMtrackkern:
945 def __init__(self, degree, min_ptsize, min_kern, max_ptsize, max_kern):
946 self.degree = degree
947 self.min_ptsize = min_ptsize
948 self.min_kern = min_kern
949 self.max_ptsize = max_ptsize
950 self.max_kern = max_kern
953 class AFMkernpair:
954 def __init__(self, name1, name2, x, y):
955 self.name1 = name1
956 self.name2 = name2
957 self.x = x
958 self.y = y
961 class AFMcomposite:
962 def __init__(self, name, parts):
963 self.name = name
964 self.parts = parts
967 class AFMfile(metric.metric):
969 def __init__(self, file):
970 self.afmversion = None # version, required
971 self.metricssets = 0 # int, optional
972 self.fontname = None # str, required
973 self.fullname = None # str, optional
974 self.familyname = None # str, optional
975 self.weight = None # str, optional
976 self.fontbbox = None # 4 floats, required
977 self.version = None # str, optional
978 self.notice = None # str, optional
979 self.encodingscheme = None # str, optional
980 self.mappingscheme = None # int, optional (not present in base font programs)
981 self.escchar = None # int, required if mappingscheme == 3
982 self.characterset = None # str, optional
983 self.characters = None # int, optional
984 self.isbasefont = True # bool, optional
985 self.vvector = None # 2 floats, required if metricssets == 2
986 self.isfixedv = None # bool, default: true if vvector present, false otherwise
987 self.capheight = None # float, optional
988 self.xheight = None # float, optional
989 self.ascender = None # float, optional
990 self.descender = None # float, optional
991 self.stdhw = None # float, optional
992 self.stdvw = None # float, optional
993 self.underlinepositions = [None] * 2 # int, optional (for each direction)
994 self.underlinethicknesses = [None] * 2 # float, optional (for each direction)
995 self.italicangles = [None] * 2 # float, optional (for each direction)
996 self.charwidths = [None] * 2 # 2 floats, optional (for each direction)
997 self.isfixedpitchs = [None] * 2 # bool, optional (for each direction)
998 self.expected_entries = None # if set, internal variable to verify number of expected entries in a section
999 self.charmetrics = None # list of character metrics information, optional
1000 self.charmetricsdict = {} # helper dictionary mapping glyph names to character metrics information
1001 self.trackkerns = None # list of track kernings, optional
1002 self.kernpairs = [None] * 2 # list of list of kerning pairs (for each direction), optional
1003 self.kernpairsdict = {} # helper dictionary mapping glyph names to kerning pairs, first direction
1004 self.kernpairsdict1 = {} # helper dictionary mapping glyph names to kerning pairs, second direction
1005 self.composites = None # list of composite character data sets, optional
1006 self.parse(file)
1007 if self.isfixedv is None:
1008 self.isfixedv = self.vvector is not None
1009 # XXX we should check the constraints on some parameters
1011 # the following methods process a line when the reader is in the corresponding
1012 # state and return the new state
1013 def _processline_start(self, line):
1014 key, args = line.split(None, 1)
1015 if key != "StartFontMetrics":
1016 raise AFMError("Expecting StartFontMetrics, no found")
1017 self.afmversion = tuple(map(int, args.split(".")))
1018 return _READ_MAIN, None
1020 def _processline_main(self, line):
1021 try:
1022 key, args = line.split(None, 1)
1023 except ValueError:
1024 key = line
1025 args = None
1026 if key == "Comment":
1027 return _READ_MAIN, None
1028 elif key == "MetricsSets":
1029 self.metricssets = _parseint(args)
1030 if direction is not None:
1031 raise AFMError("MetricsSets not allowed after first (implicit) StartDirection")
1032 elif key == "FontName":
1033 self.fontname = _parsestr(args)
1034 elif key == "FullName":
1035 self.fullname = _parsestr(args)
1036 elif key == "FamilyName":
1037 self.familyname = _parsestr(args)
1038 elif key == "Weight":
1039 self.weight = _parsestr(args)
1040 elif key == "FontBBox":
1041 self.fontbbox = _parsefloats(args, 4)
1042 elif key == "Version":
1043 if args is not None:
1044 self.version = _parsestr(args)
1045 elif key == "Notice":
1046 self.notice = _parsestr(args)
1047 elif key == "EncodingScheme":
1048 self.encodingscheme = _parsestr(args)
1049 elif key == "MappingScheme":
1050 self.mappingscheme = _parseint(args)
1051 elif key == "EscChar":
1052 self.escchar = _parseint(args)
1053 elif key == "CharacterSet":
1054 self.characterset = _parsestr(args)
1055 elif key == "Characters":
1056 self.characters = _parseint(args)
1057 elif key == "IsBaseFont":
1058 self.isbasefont = _parsebool(args)
1059 elif key == "VVector":
1060 self.vvector = _parsefloats(args, 2)
1061 elif key == "IsFixedV":
1062 self.isfixedv = _parsebool(args)
1063 elif key == "CapHeight":
1064 self.capheight = _parsefloat(args)
1065 elif key == "XHeight":
1066 self.xheight = _parsefloat(args)
1067 elif key == "Ascender":
1068 self.ascender = _parsefloat(args)
1069 elif key == "Descender":
1070 self.descender = _parsefloat(args)
1071 elif key == "StdHW":
1072 self.stdhw = _parsefloat(args)
1073 elif key == "StdVW":
1074 self.stdvw = _parsefloat(args)
1075 elif key == "StartDirection":
1076 direction = _parseint(args)
1077 if 0 <= direction <= 2:
1078 return _READ_DIRECTION, direction
1079 else:
1080 raise AFMError("Wrong direction number %d" % direction)
1081 elif (key == "UnderlinePosition" or key == "UnderlineThickness" or key == "ItalicAngle" or
1082 key == "Charwidth" or key == "IsFixedPitch"):
1083 # we implicitly entered a direction section, so we should process the line again
1084 return self._processline_direction(line, 0)
1085 elif key == "StartCharMetrics":
1086 if self.charmetrics is not None:
1087 raise AFMError("Multiple character metrics sections")
1088 if self.afmversion >= (2, 0):
1089 self.expected_entries = _parseint(args)
1090 else:
1091 self.expected_entries = None
1092 self.charmetrics = []
1093 return _READ_CHARMETRICS, 0
1094 elif key == "StartKernData":
1095 return _READ_KERNDATA, None
1096 elif key == "StartComposites":
1097 if self.composites is not None:
1098 raise AFMError("Multiple composite character data sections")
1099 if args is not None:
1100 self.expected_entries = _parseint(args)
1101 else:
1102 self.expected_entries = None
1103 self.composites = []
1104 return _READ_COMPOSITES, 0
1105 elif key == "EndFontMetrics":
1106 return _READ_END, None
1107 elif key[0] in string.ascii_lowercase:
1108 # ignoring private commands
1109 pass
1110 else:
1111 # and according to the AFM specs also all other unknown keys
1112 pass
1113 return _READ_MAIN, None
1115 def _processline_direction(self, line, direction):
1116 try:
1117 key, args = line.split(None, 1)
1118 except ValueError:
1119 key = line.strip()
1120 if key == "UnderlinePosition":
1121 self.underlinepositions[direction] = _parsefloat(args)
1122 elif key == "UnderlineThickness":
1123 self.underlinethicknesses[direction] = _parsefloat(args)
1124 elif key == "ItalicAngle":
1125 self.italicangles[direction] = _parsefloat(args)
1126 elif key == "Charwidth":
1127 self.charwidths[direction] = _parsefloats(args, 2)
1128 elif key == "IsFixedPitch":
1129 self.isfixedpitchs[direction] = _parsebool(args)
1130 elif key == "EndDirection":
1131 return _READ_MAIN, None
1132 else:
1133 # we assume that we are implicitly leaving the direction section again,
1134 # so try to reprocess the line in the header reader state
1135 return self._processline_main(line)
1136 return _READ_DIRECTION, direction
1138 def _processline_charmetrics(self, line, charno):
1139 if line.rstrip() == "EndCharMetrics":
1140 if self.expected_entries is not None and charno != self.expected_entries:
1141 # This seems to be a rather common error in AFM files, so we do not raise
1142 # an exception here, but just graticiously accept the file
1143 pass
1144 # raise AFMError("Fewer character metrics than expected")
1145 return _READ_MAIN, None
1147 has_name = False
1148 char = None
1149 for s in line.split(";"):
1150 s = s.strip()
1151 if not s:
1152 continue
1153 key, args = s.split(None, 1)
1154 if key == "C":
1155 if char is not None:
1156 raise AFMError("Cannot define char code twice")
1157 char = AFMcharmetrics(_parseint(args))
1158 elif key == "CH":
1159 if char is not None:
1160 raise AFMError("Cannot define char code twice")
1161 char = AFMcharmetrics(_parsehex(args))
1162 elif key == "WX" or key == "W0X":
1163 char.widths[0] = _parsefloat(args), 0
1164 elif key == "W1X":
1165 char.widths[1] = _parsefloat(args), 0
1166 elif key == "WY" or key == "W0Y":
1167 char.widths[0] = 0, _parsefloat(args)
1168 elif key == "W1Y":
1169 char.widths[1] = 0, _parsefloat(args)
1170 elif key == "W" or key == "W0":
1171 char.widths[0] = _parsefloats(args, 2)
1172 elif key == "W1":
1173 char.widths[1] = _parsefloats(args, 2)
1174 elif key == "VV":
1175 char.vvector = _parsefloats(args, 2)
1176 elif key == "N":
1177 # XXX: we should check that name is valid (no whitespace, etc.)
1178 has_name = True
1179 char.name = _parsestr(args)
1180 elif key == "B":
1181 char.bbox = _parsefloats(args, 4)
1182 elif key == "L":
1183 successor, ligature = args.split(None, 1)
1184 char.ligatures.append((_parsestr(successor), ligature))
1185 else:
1186 raise AFMError("Undefined command in character widths specification: '%s'", s)
1187 if char is None:
1188 raise AFMError("Character metrics not defined")
1190 self.charmetrics.append(char)
1191 if has_name:
1192 self.charmetricsdict[char.name] = char
1193 return _READ_CHARMETRICS, charno+1
1195 def _processline_kerndata(self, line):
1196 try:
1197 key, args = line.split(None, 1)
1198 except ValueError:
1199 key = line
1200 args = None
1201 if key == "Comment":
1202 return _READ_KERNDATA, None
1203 if key == "StartTrackKern":
1204 if self.trackkerns is not None:
1205 raise AFMError("Multiple track kernings data sections")
1206 self.trackkerns = [None] * _parseint(args)
1207 return _READ_TRACKKERN, 0
1208 elif key == "StartKernPairs" or key == "StartKernPairs0":
1209 if self.kernpairs[0] is not None:
1210 raise AFMError("Multiple kerning pairs data sections for direction 0")
1211 if args is not None:
1212 self.expected_entries = _parseint(args)
1213 else:
1214 self.expected_entries = None
1215 self.kernpairs[0] = []
1216 return _READ_KERNPAIRS, (0, 0)
1217 elif key == "StartKernPairs1":
1218 if self.kernpairs[1] is not None:
1219 raise AFMError("Multiple kerning pairs data sections for direction 1")
1220 self.expected_entries = _parseint(args)
1221 self.kernpairs[1] = []
1222 return _READ_KERNPAIRS, (1, 0)
1223 elif key == "EndKernData":
1224 return _READ_MAIN, None
1225 else:
1226 raise AFMError("Unsupported key %s in kerning data section" % key)
1228 def _processline_trackkern(self, line, i):
1229 try:
1230 key, args = line.split(None, 1)
1231 except ValueError:
1232 key = line
1233 if key == "Comment":
1234 return _READ_TRACKKERN, i
1235 elif key == "TrackKern":
1236 if i >= len(self.trackkerns):
1237 raise AFMError("More track kerning data sets than expected")
1238 degrees, args = args.split(None, 1)
1239 self.trackkerns[i] = AFMtrackkern(int(degrees), *_parsefloats(args, 4))
1240 return _READ_TRACKKERN, i+1
1241 elif key == "EndTrackKern":
1242 if i < len(self.trackkerns):
1243 raise AFMError("Fewer track kerning data sets than expected")
1244 return _READ_KERNDATA, None
1245 else:
1246 raise AFMError("Unsupported key %s in kerning data section" % key)
1248 def _processline_kernpairs(self, line, xxx_todo_changeme):
1249 (direction, i) = xxx_todo_changeme
1250 try:
1251 key, args = line.split(None, 1)
1252 except ValueError:
1253 key = line
1254 if key == "Comment":
1255 return _READ_KERNPAIRS, (direction, i)
1256 elif key == "EndKernPairs":
1257 if i != self.expected_entries:
1258 # This seems to be a rather common error in AFM files, so we do not raise
1259 # an exception here, but just graticiously accept the file
1260 pass
1261 # raise AFMError("Fewer kerning pairs than expected")
1262 return _READ_KERNDATA, None
1263 else:
1264 if key == "KP":
1265 try:
1266 name1, name2, x, y = args.split()
1267 except:
1268 raise AFMError("Expecting name1, name2, x, y, got '%s'" % args)
1269 x = _parsefloat(x)
1270 y = _parsefloat(y)
1271 elif key == "KPH":
1272 try:
1273 hex1, hex2, x, y = args.split()
1274 except:
1275 raise AFMError("Expecting <hex1>, <hex2>, x, y, got '%s'" % args)
1276 name1 = _parsehex(hex1)
1277 name2 = _parsehex(hex2)
1278 x = _parsefloat(x)
1279 y = _parsefloat(y)
1280 elif key == "KPX":
1281 try:
1282 name1, name2, x = args.split()
1283 except:
1284 raise AFMError("Expecting name1, name2, x, got '%s'" % args)
1285 x = _parsefloat(x)
1286 y = 0
1287 elif key == "KPY":
1288 try:
1289 name1, name2, y = args.split()
1290 except:
1291 raise AFMError("Expecting name1, name2, y, got '%s'" % args)
1292 x = 0
1293 y = _parsefloat(y)
1294 else:
1295 raise AFMError("Unknown key '%s' in kern pair section" % key)
1296 kernpair = AFMkernpair(name1, name2, x, y)
1297 self.kernpairs[direction].append(kernpair)
1298 if direction:
1299 self.kernpairsdict1[name1, name2] = kernpair
1300 else:
1301 self.kernpairsdict[name1, name2] = kernpair
1302 return _READ_KERNPAIRS, (direction, i+1)
1304 def _processline_composites(self, line, i):
1305 if line == "EndComposites":
1306 if self.expected_entries is not None and i != self.expected_entries:
1307 raise AFMError("Fewer composites than expected")
1308 return _READ_MAIN, None
1310 name = None
1311 no = None
1312 parts = []
1313 for s in line.split(";"):
1314 s = s.strip()
1315 if not s:
1316 continue
1317 key, args = s.split(None, 1)
1318 if key == "CC":
1319 try:
1320 name, no = args.split()
1321 except:
1322 raise AFMError("Expecting name number, got '%s'" % args)
1323 no = _parseint(no)
1324 elif key == "PCC":
1325 try:
1326 name1, x, y = args.split()
1327 except:
1328 raise AFMError("Expecting name x y, got '%s'" % args)
1329 parts.append((name1, _parsefloat(x), _parsefloat(y)))
1330 else:
1331 raise AFMError("Unknown key '%s' in composite character data section" % key)
1332 if len(parts) != no:
1333 raise AFMError("Wrong number of composite characters")
1334 return _READ_COMPOSITES, i+1
1336 def parse(self, f):
1337 # state of the reader, consisting of
1338 # - the main state, i.e. the type of the section
1339 # - a parameter sstate
1340 state = _READ_START, None
1341 # Note that we do a line by line processing here, since one
1342 # of the states (_READ_DIRECTION) can be entered implicitly, i.e.
1343 # without a corresponding StartDirection section and we thus
1344 # may need to reprocess a line in the context of the new state
1345 for line in f:
1346 line = line[:-1].strip()
1347 mstate, sstate = state
1348 if mstate == _READ_START:
1349 state = self._processline_start(line)
1350 else:
1351 # except for the first line, any empty will be ignored
1352 if not line:
1353 continue
1354 if mstate == _READ_MAIN:
1355 state = self._processline_main(line)
1356 elif mstate == _READ_DIRECTION:
1357 state = self._processline_direction(line, sstate)
1358 elif mstate == _READ_CHARMETRICS:
1359 state = self._processline_charmetrics(line, sstate)
1360 elif mstate == _READ_KERNDATA:
1361 state = self._processline_kerndata(line)
1362 elif mstate == _READ_TRACKKERN:
1363 state = self._processline_trackkern(line, sstate)
1364 elif mstate == _READ_KERNPAIRS:
1365 state = self._processline_kernpairs(line, sstate)
1366 elif mstate == _READ_COMPOSITES:
1367 state = self._processline_composites(line, sstate)
1368 else:
1369 raise AFMError("Undefined state in AFM reader")
1371 def width_ds(self, glyphname):
1372 return self.charmetricsdict[glyphname].widths[0][0]
1374 def width_pt(self, glyphnames, size_pt):
1375 return sum([self.charmetricsdict[glyphname].widths[0][0] for glyphname in glyphnames])*size_pt/1000.0
1377 def height_pt(self, glyphnames, size_pt):
1378 return max([self.charmetricsdict[glyphname].bbox[3] for glyphname in glyphnames])*size_pt/1000.0
1380 def depth_pt(self, glyphnames, size_pt):
1381 return min([self.charmetricsdict[glyphname].bbox[1] for glyphname in glyphnames])*size_pt/1000.0
1383 def resolveligatures(self, glyphnames):
1384 i = 1
1385 while i < len(glyphnames):
1386 for glyphname, replacement in self.charmetricsdict[glyphnames[i-1]].ligatures:
1387 if glyphname == glyphnames[i]:
1388 glyphnames[i-1] = replacement
1389 del glyphnames[i]
1390 break
1391 else:
1392 i += 1
1393 return glyphnames
1395 def resolvekernings(self, glyphnames, size_pt=None):
1396 result = [None]*(2*len(glyphnames)-1)
1397 for i, glyphname in enumerate(glyphnames):
1398 result[2*i] = glyphname
1399 if i:
1400 kernpair = self.kernpairsdict.get((glyphnames[i-1], glyphname))
1401 if kernpair:
1402 if size_pt is not None:
1403 result[2*i-1] = kernpair.x*size_pt/1000.0
1404 else:
1405 result[2*i-1] = kernpair.x
1406 return result
1408 def writePDFfontinfo(self, file, seriffont=False, symbolfont=True):
1409 flags = 0
1410 if self.isfixedpitchs[0]:
1411 flags += 1<<0
1412 if seriffont:
1413 flags += 1<<1
1414 if symbolfont:
1415 flags += 1<<2
1416 else:
1417 flags += 1<<5
1418 if self.italicangles[0]:
1419 flags += 1<<6
1420 file.write("/Flags %d\n" % flags)
1421 if self.italicangles[0] is not None:
1422 file.write("/ItalicAngles %d\n" % self.italicangles[0])
1423 if self.ascender is not None:
1424 ascent = self.ascender
1425 elif self.fontbbox is not None:
1426 ascent = self.fontbbox[3]
1427 else:
1428 ascent = 1000 # guessed default
1429 file.write("/Ascent %d\n" % ascent)
1430 if self.descender is not None:
1431 descent = self.descender
1432 elif self.fontbbox is not None:
1433 descent = self.fontbbox[3]
1434 else:
1435 descent = -200 # guessed default
1436 file.write("/Descent %d\n" % descent)
1437 if self.fontbbox is not None:
1438 file.write("/FontBBox [%d %d %d %d]\n" % tuple(self.fontbbox))
1439 else:
1440 # the fontbbox is required, so we have to have to provide some default
1441 file.write("/FontBBox [0 %d 1000 %d]\n" % (descent, ascent))
1442 if self.capheight is not None:
1443 file.write("/CapHeight %d\n" % self.capheight)
1444 else:
1445 # the CapHeight is required, so we have to have to provide some default
1446 file.write("/CapHeight %d\n" % ascent)
1447 if self.stdvw is not None:
1448 stemv = self.stdvw
1449 elif self.weight is not None and ("bold" in self.weight.lower() or "black" in self.weight.lower()):
1450 stemv = 120 # guessed default
1451 else:
1452 stemv = 70 # guessed default
1453 file.write("/StemV %d\n" % stemv)