switch to utf-8 file encoding, add some missing copyrights
[PyX/mjg.git] / pyx / dvi / mapfile.py
blob62664d60475e547eeb62e874bffe639ce3b745fb
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, USAimport re, warnings
23 import os.path, re
24 from pyx import font, filelocator
25 from pyx.font import t1file, afmfile
26 from pyx.dvi import encfile
28 class UnsupportedFontFormat(Exception):
29 pass
31 class UnsupportedPSFragment(Exception):
32 pass
34 _marker = object()
36 class MAPline:
38 tokenpattern = re.compile(r'"(.*?)("\s+|"$|$)|(.*?)(\s+|$)')
40 def __init__(self, s):
41 """ construct font mapping from line s of font mapping file """
42 self.texname = self.basepsname = self.fontfilename = None
44 # standard encoding
45 self.encodingfilename = None
47 # supported postscript fragments occuring in psfonts.map
48 # XXX extendfont not yet implemented
49 self.reencodefont = self.extendfont = self.slant = None
51 # cache for openend font and encoding
52 self._font = None
53 self._encoding = _marker
55 tokens = []
56 while len(s):
57 match = self.tokenpattern.match(s)
58 if match:
59 if match.groups()[0] is not None:
60 tokens.append('"%s"' % match.groups()[0])
61 else:
62 tokens.append(match.groups()[2])
63 s = s[match.end():]
64 else:
65 raise RuntimeError("Cannot tokenize string '%s'" % s)
67 next_token_is_encfile = False
68 for token in tokens:
69 if next_token_is_encfile:
70 self.encodingfilename = token
71 next_token_is_encfile = False
72 elif token.startswith("<"):
73 if token == "<":
74 next_token_is_encfile = True
75 elif token.startswith("<<"):
76 # XXX: support non-partial download here
77 self.fontfilename = token[2:]
78 elif token.startswith("<["):
79 self.encodingfilename = token[2:]
80 elif token.endswith(".pfa") or token.endswith(".pfb"):
81 self.fontfilename = token[1:]
82 elif token.endswith(".enc"):
83 self.encodingfilename = token[1:]
84 elif token.endswith(".ttf"):
85 raise UnsupportedFontFormat("TrueType font")
86 else:
87 raise RuntimeError("Unknown token '%s'" % token)
88 elif token.startswith('"'):
89 pscode = token[1:-1].split()
90 # parse standard postscript code fragments
91 while pscode:
92 try:
93 arg, cmd = pscode[:2]
94 except:
95 raise UnsupportedPSFragment("Unsupported Postscript fragment '%s'" % pscode)
96 pscode = pscode[2:]
97 if cmd == "ReEncodeFont":
98 self.reencodefont = arg
99 elif cmd == "ExtendFont":
100 self.extendfont = arg
101 elif cmd == "SlantFont":
102 self.slant = float(arg)
103 else:
104 raise UnsupportedPSFragment("Unsupported Postscript fragment '%s %s'" % (arg, cmd))
105 else:
106 if self.texname is None:
107 self.texname = token
108 else:
109 self.basepsname = token
110 if self.basepsname is None:
111 self.basepsname = self.texname
113 def getfontname(self):
114 return self.basepsname
116 def getfont(self):
117 if self._font is None:
118 if self.fontfilename is not None:
119 fontfile = filelocator.open(self.fontfilename, [filelocator.format.type1], "rb")
120 t1font = t1file.from_PF_bytes(fontfile.read())
121 fontfile.close()
122 assert self.basepsname == t1font.name, "corrupt MAP file"
123 try:
124 metricfile = filelocator.open(os.path.splitext(self.fontfilename)[0], [filelocator.format.afm])
125 except IOError:
126 self._font = font.T1font(t1font, None)
127 else:
128 self._font = font.T1font(t1font, afmfile.AFMfile(metricfile))
129 metricfile.close()
130 else:
131 metricfile = filelocator.open(self.basepsname, [filelocator.format.afm])
132 self._font = font.T1builtinfont(self.basepsname, afmfile.AFMfile(metricfile))
133 metricfile.close()
134 return self._font
136 def getencoding(self):
137 if self._encoding is _marker:
138 if self.encodingfilename is not None:
139 encodingfile = filelocator.open(self.encodingfilename, [filelocator.format.tex_ps_header], "rb")
140 ef = encfile.ENCfile(encodingfile.read())
141 encodingfile.close()
142 assert ef.name == "/%s" % self.reencodefont
143 self._encoding = ef.vector
145 else:
146 self._encoding = None
147 return self._encoding
149 def __str__(self):
150 return ("'%s' is '%s' read from '%s' encoded as '%s'" %
151 (self.texname, self.basepsname, self.fontfile, repr(self.encodingfile)))
153 # generate fontmap
155 def readfontmap(filenames):
156 """ read font map from filename (without path) """
157 fontmap = {}
158 for filename in filenames:
159 mapfile = filelocator.open(filename, [filelocator.format.fontmap, filelocator.format.dvips_config], mode="rU")
160 lineno = 0
161 for line in mapfile.readlines():
162 lineno += 1
163 line = line.rstrip()
164 if not (line=="" or line[0] in (" ", "%", "*", ";" , "#")):
165 try:
166 fm = MAPline(line)
167 except (RuntimeError, UnsupportedPSFragment), e:
168 warnings.warn("Ignoring line %i in mapping file '%s': %s" % (lineno, mappath, e))
169 except UnsupportedFontFormat, e:
170 pass
171 else:
172 fontmap[fm.texname] = fm
173 mapfile.close()
174 return fontmap