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
26 unicodestring
= {" ": "space",
122 "\xa1": "exclamdown",
131 "\xaa": "ordfeminine",
132 "\xab": "guillemotleft",
133 "\xac": "logicalnot",
135 "\xae": "registered",
141 "\xb7": "periodcentered",
143 "\xba": "ordmasculine",
144 "\xbb": "guillemotright",
145 "\xbc": "onequarter",
147 "\xbe": "threequarters",
148 "\xbf": "questiondown",
151 "\xc2": "Acircumflex",
159 "\xca": "Ecircumflex",
163 "\xce": "Icircumflex",
169 "\xd4": "Ocircumflex",
176 "\xdb": "Ucircumflex",
180 "\xdf": "germandbls",
183 "\xe2": "acircumflex",
191 "\xea": "ecircumflex",
195 "\xee": "icircumflex",
201 "\xf4": "ocircumflex",
208 "\xfb": "ucircumflex",
221 "\u0108": "Ccircumflex",
222 "\u0109": "ccircumflex",
223 "\u010a": "Cdotaccent",
224 "\u010b": "cdotaccent",
235 "\u0116": "Edotaccent",
236 "\u0117": "edotaccent",
241 "\u011c": "Gcircumflex",
242 "\u011d": "gcircumflex",
245 "\u0120": "Gdotaccent",
246 "\u0121": "gdotaccent",
247 "\u0122": "Gcommaaccent",
248 "\u0123": "gcommaaccent",
249 "\u0124": "Hcircumflex",
250 "\u0125": "hcircumflex",
261 "\u0130": "Idotaccent",
262 "\u0131": "dotlessi",
265 "\u0134": "Jcircumflex",
266 "\u0135": "jcircumflex",
267 "\u0136": "Kcommaaccent",
268 "\u0137": "kcommaaccent",
269 "\u0138": "kgreenlandic",
272 "\u013b": "Lcommaaccent",
273 "\u013c": "lcommaaccent",
282 "\u0145": "Ncommaaccent",
283 "\u0146": "ncommaaccent",
286 "\u0149": "napostrophe",
293 "\u0150": "Ohungarumlaut",
294 "\u0151": "ohungarumlaut",
299 "\u0156": "Rcommaaccent",
300 "\u0157": "rcommaaccent",
305 "\u015c": "Scircumflex",
306 "\u015d": "scircumflex",
307 "\u015e": "Scedilla",
308 "\u015f": "scedilla",
311 "\u0162": "Tcommaaccent",
312 "\u0163": "tcommaaccent",
325 "\u0170": "Uhungarumlaut",
326 "\u0171": "uhungarumlaut",
329 "\u0174": "Wcircumflex",
330 "\u0175": "wcircumflex",
331 "\u0176": "Ycircumflex",
332 "\u0177": "ycircumflex",
333 "\u0178": "Ydieresis",
336 "\u017b": "Zdotaccent",
337 "\u017c": "zdotaccent",
348 "\u01fa": "Aringacute",
349 "\u01fb": "aringacute",
352 "\u01fe": "Oslashacute",
353 "\u01ff": "oslashacute",
354 "\u0218": "Scommaaccent",
355 "\u0219": "scommaaccent",
356 "\u02bc": "afii57929",
357 "\u02bd": "afii64937",
358 "\u02c6": "circumflex",
362 "\u02d9": "dotaccent",
366 "\u02dd": "hungarumlaut",
367 "\u0300": "gravecomb",
368 "\u0301": "acutecomb",
369 "\u0303": "tildecomb",
370 "\u0309": "hookabovecomb",
371 "\u0323": "dotbelowcomb",
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",
407 "\u03aa": "Iotadieresis",
408 "\u03ab": "Upsilondieresis",
409 "\u03ac": "alphatonos",
410 "\u03ad": "epsilontonos",
411 "\u03ae": "etatonos",
412 "\u03af": "iotatonos",
413 "\u03b0": "upsilondieresistonos",
439 "\u03ca": "iotadieresis",
440 "\u03cb": "upsilondieresis",
441 "\u03cc": "omicrontonos",
442 "\u03cd": "upsilontonos",
443 "\u03ce": "omegatonos",
445 "\u03d2": "Upsilon1",
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",
673 "\u1e84": "Wdieresis",
674 "\u1e85": "wdieresis",
677 "\u200c": "afii61664",
681 "\u2012": "figuredash",
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",
694 "\u2021": "daggerdbl",
696 "\u2024": "onedotenleader",
697 "\u2025": "twodotenleader",
698 "\u2026": "ellipsis",
699 "\u202c": "afii61573",
700 "\u202d": "afii61574",
701 "\u202e": "afii61575",
702 "\u2030": "perthousand",
705 "\u2039": "guilsinglleft",
706 "\u203a": "guilsinglright",
707 "\u203c": "exclamdbl",
708 "\u2044": "fraction",
709 "\u20a1": "colonmonetary",
713 "\u20aa": "afii57636",
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",
726 "\u2153": "onethird",
727 "\u2154": "twothirds",
728 "\u215b": "oneeighth",
729 "\u215c": "threeeighths",
730 "\u215d": "fiveeighths",
731 "\u215e": "seveneighths",
732 "\u2190": "arrowleft",
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",
751 "\u2209": "notelement",
752 "\u220b": "suchthat",
754 "\u2211": "summation",
756 "\u2215": "fraction",
757 "\u2217": "asteriskmath",
758 "\u2219": "periodcentered",
760 "\u221d": "proportional",
761 "\u221e": "infinity",
762 "\u221f": "orthogonal",
764 "\u2227": "logicaland",
765 "\u2228": "logicalor",
766 "\u2229": "intersection",
768 "\u222b": "integral",
769 "\u2234": "therefore",
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",
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",
840 "\u25a0": "filledbox",
844 "\u25ac": "filledrect",
852 "\u25d8": "invbullet",
853 "\u25d9": "invcircle",
854 "\u25e6": "openbullet",
855 "\u263a": "smileface",
856 "\u263b": "invsmileface",
864 "\u266a": "musicalnote",
865 "\u266b": "musicalnotedbl",
869 class AFMError(Exception):
876 _READ_CHARMETRICS
= 3
883 # various parsing functions
888 raise AFMError("Expecting int, got '%s'" % s
)
892 if s
[0] != "<" or s
[-1] != ">":
894 return int(s
[1:-1], 16)
896 raise AFMError("Expecting hexadecimal int, got '%s'" % s
)
902 raise AFMError("Expecting float, got '%s'" % s
)
904 def _parsefloats(s
, nos
):
907 result
= list(map(float, numbers
))
908 if len(result
) != nos
:
911 raise AFMError("Expecting list of %d numbers, got '%s'" % (s
, nos
))
915 # XXX: check for invalid characters in s
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):
932 self
.widths
= [None] * 2
935 self
.vvector
= vvector
938 if ligatures
is None:
941 self
.ligatures
= ligatures
945 def __init__(self
, degree
, min_ptsize
, min_kern
, max_ptsize
, max_kern
):
947 self
.min_ptsize
= min_ptsize
948 self
.min_kern
= min_kern
949 self
.max_ptsize
= max_ptsize
950 self
.max_kern
= max_kern
954 def __init__(self
, name1
, name2
, x
, y
):
962 def __init__(self
, name
, 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
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
):
1022 key
, args
= line
.split(None, 1)
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
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
)
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
)
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
1111 # and according to the AFM specs also all other unknown keys
1113 return _READ_MAIN
, None
1115 def _processline_direction(self
, line
, direction
):
1117 key
, args
= line
.split(None, 1)
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
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
1144 # raise AFMError("Fewer character metrics than expected")
1145 return _READ_MAIN
, None
1149 for s
in line
.split(";"):
1153 key
, args
= s
.split(None, 1)
1155 if char
is not None:
1156 raise AFMError("Cannot define char code twice")
1157 char
= AFMcharmetrics(_parseint(args
))
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
1165 char
.widths
[1] = _parsefloat(args
), 0
1166 elif key
== "WY" or key
== "W0Y":
1167 char
.widths
[0] = 0, _parsefloat(args
)
1169 char
.widths
[1] = 0, _parsefloat(args
)
1170 elif key
== "W" or key
== "W0":
1171 char
.widths
[0] = _parsefloats(args
, 2)
1173 char
.widths
[1] = _parsefloats(args
, 2)
1175 char
.vvector
= _parsefloats(args
, 2)
1177 # XXX: we should check that name is valid (no whitespace, etc.)
1179 char
.name
= _parsestr(args
)
1181 char
.bbox
= _parsefloats(args
, 4)
1183 successor
, ligature
= args
.split(None, 1)
1184 char
.ligatures
.append((_parsestr(successor
), ligature
))
1186 raise AFMError("Undefined command in character widths specification: '%s'", s
)
1188 raise AFMError("Character metrics not defined")
1190 self
.charmetrics
.append(char
)
1192 self
.charmetricsdict
[char
.name
] = char
1193 return _READ_CHARMETRICS
, charno
+1
1195 def _processline_kerndata(self
, line
):
1197 key
, args
= line
.split(None, 1)
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
)
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
1226 raise AFMError("Unsupported key %s in kerning data section" % key
)
1228 def _processline_trackkern(self
, line
, i
):
1230 key
, args
= line
.split(None, 1)
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
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
1251 key
, args
= line
.split(None, 1)
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
1261 # raise AFMError("Fewer kerning pairs than expected")
1262 return _READ_KERNDATA
, None
1266 name1
, name2
, x
, y
= args
.split()
1268 raise AFMError("Expecting name1, name2, x, y, got '%s'" % args
)
1273 hex1
, hex2
, x
, y
= args
.split()
1275 raise AFMError("Expecting <hex1>, <hex2>, x, y, got '%s'" % args
)
1276 name1
= _parsehex(hex1
)
1277 name2
= _parsehex(hex2
)
1282 name1
, name2
, x
= args
.split()
1284 raise AFMError("Expecting name1, name2, x, got '%s'" % args
)
1289 name1
, name2
, y
= args
.split()
1291 raise AFMError("Expecting name1, name2, y, got '%s'" % args
)
1295 raise AFMError("Unknown key '%s' in kern pair section" % key
)
1296 kernpair
= AFMkernpair(name1
, name2
, x
, y
)
1297 self
.kernpairs
[direction
].append(kernpair
)
1299 self
.kernpairsdict1
[name1
, name2
] = kernpair
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
1313 for s
in line
.split(";"):
1317 key
, args
= s
.split(None, 1)
1320 name
, no
= args
.split()
1322 raise AFMError("Expecting name number, got '%s'" % args
)
1326 name1
, x
, y
= args
.split()
1328 raise AFMError("Expecting name x y, got '%s'" % args
)
1329 parts
.append((name1
, _parsefloat(x
), _parsefloat(y
)))
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
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
1346 line
= line
[:-1].strip()
1347 mstate
, sstate
= state
1348 if mstate
== _READ_START
:
1349 state
= self
._processline
_start
(line
)
1351 # except for the first line, any empty will be ignored
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
)
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
):
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
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
1400 kernpair
= self
.kernpairsdict
.get((glyphnames
[i
-1], glyphname
))
1402 if size_pt
is not None:
1403 result
[2*i
-1] = kernpair
.x
*size_pt
/1000.0
1405 result
[2*i
-1] = kernpair
.x
1408 def writePDFfontinfo(self
, file, seriffont
=False, symbolfont
=True):
1410 if self
.isfixedpitchs
[0]:
1418 if self
.italicangles
[0]:
1420 file.write("/Flags %d\n" % flags
)
1421 if self
.italicangles
[0] is not None:
1422 file.write("/ItalicAngle %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]
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]
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
))
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
)
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:
1449 elif self
.weight
is not None and ("bold" in self
.weight
.lower() or "black" in self
.weight
.lower()):
1450 stemv
= 120 # guessed default
1452 stemv
= 70 # guessed default
1453 file.write("/StemV %d\n" % stemv
)