fix directory layout of tagging dir
[PyX.git] / pyx / dvi / mapfile.py
blob9126f3836657636a3367315d524fc3722c5c20f9
1 # -*- encoding: utf-8 -*-
4 # Copyright (C) 2007-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 os.path, re, warnings
24 from pyx import font, filelocator
25 from pyx.font import t1file, afmfile, pfmfile
26 from pyx.dvi import encfile
28 class UnsupportedFontFormat(Exception):
29 pass
31 class UnsupportedPSFragment(Exception):
32 pass
34 class ParseError(Exception):
35 pass
37 _marker = object()
39 class MAPline:
41 tokenpattern = re.compile(r'"(.*?)("\s+|"$|$)|(.*?)(\s+|$)')
43 def __init__(self, s):
44 """ construct font mapping from line s of font mapping file """
45 self.texname = self.basepsname = self.fontfilename = None
47 # standard encoding
48 self.encodingfilename = None
50 # supported postscript fragments occuring in psfonts.map
51 # XXX extendfont not yet implemented
52 self.reencodefont = self.extendfont = self.slant = None
54 # cache for openend font and encoding
55 self._font = None
56 self._encoding = _marker
58 tokens = []
59 while len(s):
60 match = self.tokenpattern.match(s)
61 if match:
62 if match.groups()[0] is not None:
63 tokens.append('"%s"' % match.groups()[0])
64 else:
65 tokens.append(match.groups()[2])
66 s = s[match.end():]
67 else:
68 raise ParseError("Cannot tokenize string '%s'" % s)
70 next_token_is_encfile = False
71 for token in tokens:
72 if next_token_is_encfile:
73 self.encodingfilename = token
74 next_token_is_encfile = False
75 elif token.startswith("<"):
76 if token == "<":
77 next_token_is_encfile = True
78 elif token.startswith("<<"):
79 # XXX: support non-partial download here
80 self.fontfilename = token[2:]
81 elif token.startswith("<["):
82 self.encodingfilename = token[2:]
83 elif token.endswith(".pfa") or token.endswith(".pfb"):
84 self.fontfilename = token[1:]
85 elif token.endswith(".enc"):
86 self.encodingfilename = token[1:]
87 elif token.endswith(".ttf"):
88 raise UnsupportedFontFormat("TrueType font")
89 elif token.endswith(".t42"):
90 raise UnsupportedFontFormat("Type 42 font")
91 else:
92 raise ParseError("Unknown token '%s'" % token)
93 elif token.startswith('"'):
94 pscode = token[1:-1].split()
95 # parse standard postscript code fragments
96 while pscode:
97 try:
98 arg, cmd = pscode[:2]
99 except:
100 raise UnsupportedPSFragment("Unsupported Postscript fragment '%s'" % pscode)
101 pscode = pscode[2:]
102 if cmd == "ReEncodeFont":
103 self.reencodefont = arg
104 elif cmd == "ExtendFont":
105 self.extendfont = arg
106 elif cmd == "SlantFont":
107 self.slant = float(arg)
108 else:
109 raise UnsupportedPSFragment("Unsupported Postscript fragment '%s %s'" % (arg, cmd))
110 else:
111 if self.texname is None:
112 self.texname = token
113 else:
114 self.basepsname = token
115 if self.basepsname is None:
116 self.basepsname = self.texname
118 def getfontname(self):
119 return self.basepsname
121 def getfont(self):
122 if self._font is None:
123 if self.fontfilename is not None:
124 fontfile = filelocator.open(self.fontfilename, [filelocator.format.type1], "rb")
125 t1font = t1file.from_PF_bytes(fontfile.read())
126 fontfile.close()
127 assert self.basepsname == t1font.name, "corrupt MAP file"
128 try:
129 metricfile = filelocator.open(os.path.splitext(self.fontfilename)[0], [filelocator.format.afm])
130 except IOError:
131 try:
132 # fallback by using the pfm instead of the afm font metric
133 # (in all major TeX distributions there is no pfm file format defined by kpsewhich, but
134 # we can use the type1 format and search for the file including the expected suffix)
135 metricfile = filelocator.open("%s.pfm" % os.path.splitext(self.fontfilename)[0], [filelocator.format.type1])
136 except IOError:
137 self._font = font.T1font(t1font)
138 else:
139 self._font = font.T1font(t1font, pfmfile.PFMfile(metricfile, t1font))
140 metricfile.close()
141 else:
142 self._font = font.T1font(t1font, afmfile.AFMfile(metricfile))
143 metricfile.close()
144 else:
145 metricfile = filelocator.open(self.basepsname, [filelocator.format.afm])
146 self._font = font.T1builtinfont(self.basepsname, afmfile.AFMfile(metricfile))
147 metricfile.close()
148 return self._font
150 def getencoding(self):
151 if self._encoding is _marker:
152 if self.encodingfilename is not None:
153 encodingfile = filelocator.open(self.encodingfilename, [filelocator.format.tex_ps_header], "rb")
154 ef = encfile.ENCfile(encodingfile.read())
155 encodingfile.close()
156 assert ef.name == "/%s" % self.reencodefont
157 self._encoding = ef.vector
159 else:
160 self._encoding = None
161 return self._encoding
163 def __str__(self):
164 return ("'%s' is '%s' read from '%s' encoded as '%s'" %
165 (self.texname, self.basepsname, self.fontfile, repr(self.encodingfile)))
167 # generate fontmap
169 def readfontmap(filenames):
170 """ read font map from filename (without path) """
171 fontmap = {}
172 for filename in filenames:
173 mapfile = filelocator.open(filename, [filelocator.format.fontmap, filelocator.format.dvips_config], mode="rU")
174 lineno = 0
175 for line in mapfile.readlines():
176 lineno += 1
177 line = line.rstrip()
178 if not (line=="" or line[0] in (" ", "%", "*", ";" , "#")):
179 try:
180 fm = MAPline(line)
181 except (ParseError, UnsupportedPSFragment), e:
182 warnings.warn("Ignoring line %i in mapping file '%s': %s" % (lineno, filename, e))
183 except UnsupportedFontFormat, e:
184 pass
185 else:
186 fontmap[fm.texname] = fm
187 mapfile.close()
188 return fontmap