Applied upstream as r3028 r3025 r3024
[PyX/mjg.git] / pyx / dvi / mapfile.py
blob7e2b90a32fcb97ac09d151ab6fd10600dc7fb67d
1 import re, warnings
2 from pyx import font, pykpathsea
3 from pyx.font import t1file, afmfile
4 from pyx.dvi import encfile
6 class UnsupportedFontFormat(Exception):
7 pass
9 class UnsupportedPSFragment(Exception):
10 pass
12 _marker = object()
14 class MAPline:
16 tokenpattern = re.compile(r'"(.*?)("\s+|"$|$)|(.*?)(\s+|$)')
18 def __init__(self, s):
19 """ construct font mapping from line s of font mapping file """
20 self.texname = self.basepsname = self.fontfilename = None
22 # standard encoding
23 self.encodingfilename = None
25 # supported postscript fragments occuring in psfonts.map
26 # XXX extendfont not yet implemented
27 self.reencodefont = self.extendfont = self.slant = None
29 # cache for openend font and encoding
30 self._font = None
31 self._encoding = _marker
33 tokens = []
34 while len(s):
35 match = self.tokenpattern.match(s)
36 if match:
37 if match.groups()[0] is not None:
38 tokens.append('"%s"' % match.groups()[0])
39 else:
40 tokens.append(match.groups()[2])
41 s = s[match.end():]
42 else:
43 raise RuntimeError("Cannot tokenize string '%s'" % s)
45 next_token_is_encfile = False
46 for token in tokens:
47 if next_token_is_encfile:
48 self.encodingfilename = token
49 next_token_is_encfile = False
50 elif token.startswith("<"):
51 if token == "<":
52 next_token_is_encfile = True
53 elif token.startswith("<<"):
54 # XXX: support non-partial download here
55 self.fontfilename = token[2:]
56 elif token.startswith("<["):
57 self.encodingfilename = token[2:]
58 elif token.endswith(".pfa") or token.endswith(".pfb"):
59 self.fontfilename = token[1:]
60 elif token.endswith(".enc"):
61 self.encodingfilename = token[1:]
62 elif token.endswith(".ttf"):
63 raise UnsupportedFontFormat("TrueType font")
64 else:
65 raise RuntimeError("Unknown token '%s'" % token)
66 elif token.startswith('"'):
67 pscode = token[1:-1].split()
68 # parse standard postscript code fragments
69 while pscode:
70 try:
71 arg, cmd = pscode[:2]
72 except:
73 raise UnsupportedPSFragment("Unsupported Postscript fragment '%s'" % pscode)
74 pscode = pscode[2:]
75 if cmd == "ReEncodeFont":
76 self.reencodefont = arg
77 elif cmd == "ExtendFont":
78 self.extendfont = arg
79 elif cmd == "SlantFont":
80 self.slant = float(arg)
81 else:
82 raise UnsupportedPSFragment("Unsupported Postscript fragment '%s %s'" % (arg, cmd))
83 else:
84 if self.texname is None:
85 self.texname = token
86 else:
87 self.basepsname = token
88 if self.basepsname is None:
89 self.basepsname = self.texname
91 def getfontname(self):
92 return self.basepsname
94 def getfont(self):
95 if self._font is None:
96 if self.fontfilename is not None:
97 fontpath = pykpathsea.find_file(self.fontfilename, pykpathsea.kpse_type1_format)
98 if not fontpath:
99 raise RuntimeError("cannot find type 1 font %s" % self.fontfilename)
100 if fontpath.endswith(".pfb"):
101 t1font = t1file.PFBfile(fontpath)
102 else:
103 t1font = t1file.PFAfile(fontpath)
104 assert self.basepsname == t1font.name, "corrupt MAP file"
105 metricpath = pykpathsea.find_file(self.fontfilename.replace(".pfb", ".afm"), pykpathsea.kpse_afm_format)
106 if metricpath:
107 self._font = font.T1font(t1font, afmfile.AFMfile(metricpath))
108 else:
109 self._font = font.T1font(t1font, None)
110 else:
111 afmfilename = "%s.afm" % self.basepsname
112 metricpath = pykpathsea.find_file(afmfilename, pykpathsea.kpse_afm_format)
113 if not metricpath:
114 raise RuntimeError("cannot find type 1 font metric %s" % afmfilename)
115 self._font = font.T1builtinfont(self.basepsname, afmfile.AFMfile(metricpath))
116 return self._font
118 def getencoding(self):
119 if self._encoding is _marker:
120 if self.encodingfilename is not None:
121 encodingpath = pykpathsea.find_file(self.encodingfilename, pykpathsea.kpse_tex_ps_header_format)
122 if not encodingpath:
123 raise RuntimeError("cannot find font encoding file %s" % self.encodingfilename)
124 ef = encfile.ENCfile(encodingpath)
125 assert ef.name == "/%s" % self.reencodefont
126 self._encoding = ef.vector
128 else:
129 self._encoding = None
130 return self._encoding
132 def __str__(self):
133 return ("'%s' is '%s' read from '%s' encoded as '%s'" %
134 (self.texname, self.basepsname, self.fontfile, repr(self.encodingfile)))
136 # generate fontmap
138 def readfontmap(filenames):
139 """ read font map from filename (without path) """
140 fontmap = {}
141 for filename in filenames:
142 mappath = pykpathsea.find_file(filename, pykpathsea.kpse_fontmap_format)
143 # try also the oft-used registration as dvips config file
144 if not mappath:
145 mappath = pykpathsea.find_file(filename, pykpathsea.kpse_dvips_config_format)
146 if not mappath:
147 raise RuntimeError("cannot find font mapping file '%s'" % filename)
148 mapfile = open(mappath, "rU")
149 lineno = 0
150 for line in mapfile.readlines():
151 lineno += 1
152 line = line.rstrip()
153 if not (line=="" or line[0] in (" ", "%", "*", ";" , "#")):
154 try:
155 fm = MAPline(line)
156 except (RuntimeError, UnsupportedPSFragment), e:
157 warnings.warn("Ignoring line %i in mapping file '%s': %s" % (lineno, mappath, e))
158 except UnsupportedFontFormat, e:
159 pass
160 else:
161 fontmap[fm.texname] = fm
162 mapfile.close()
163 return fontmap