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 io
, os
.path
, re
, warnings
24 from pyx
import font
, config
25 from pyx
.font
import t1file
, afmfile
, pfmfile
26 from pyx
.dvi
import encfile
28 class UnsupportedFontFormat(Exception):
31 class UnsupportedPSFragment(Exception):
34 class ParseError(Exception):
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
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
56 self
._encoding
= _marker
60 match
= self
.tokenpattern
.match(s
)
62 if match
.groups()[0] is not None:
63 tokens
.append('"%s"' % match
.groups()[0])
65 tokens
.append(match
.groups()[2])
68 raise ParseError("Cannot tokenize string '%s'" % s
)
70 next_token_is_encfile
= False
72 if next_token_is_encfile
:
73 self
.encodingfilename
= token
74 next_token_is_encfile
= False
75 elif token
.startswith("<"):
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")
92 raise ParseError("Unknown token '%s'" % token
)
93 elif token
.startswith('"'):
94 pscode
= token
[1:-1].split()
95 # parse standard postscript code fragments
100 raise UnsupportedPSFragment("Unsupported Postscript fragment '%s'" % pscode
)
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
)
109 raise UnsupportedPSFragment("Unsupported Postscript fragment '%s %s'" % (arg
, cmd
))
111 if self
.texname
is None:
114 self
.basepsname
= token
115 if self
.basepsname
is None:
116 self
.basepsname
= self
.texname
118 def getfontname(self
):
119 return self
.basepsname
122 if self
._font
is None:
123 if self
.fontfilename
is not None:
124 fontfile
= config
.open(self
.fontfilename
, [config
.format
.type1
])
125 t1font
= t1file
.from_PF_bytes(fontfile
.read())
127 assert self
.basepsname
== t1font
.name
, "corrupt MAP file"
129 metricfile
= config
.open(os
.path
.splitext(self
.fontfilename
)[0], [config
.format
.afm
], ascii
=True)
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
= config
.open("%s.pfm" % os
.path
.splitext(self
.fontfilename
)[0], [config
.format
.type1
])
137 self
._font
= font
.T1font(t1font
)
139 self
._font
= font
.T1font(t1font
, pfmfile
.PFMfile(metricfile
, t1font
))
142 self
._font
= font
.T1font(t1font
, afmfile
.AFMfile(metricfile
))
146 metricfile
= config
.open(self
.basepsname
, [config
.format
.afm
], ascii
=True)
147 self
._font
= font
.T1builtinfont(self
.basepsname
, afmfile
.AFMfile(metricfile
))
151 def getencoding(self
):
152 if self
._encoding
is _marker
:
153 if self
.encodingfilename
is not None:
154 encodingfile
= config
.open(self
.encodingfilename
, [config
.format
.tex_ps_header
])
155 ef
= encfile
.ENCfile(encodingfile
.read().decode("ascii", errors
="surrogateescape"))
157 assert ef
.name
== "/%s" % self
.reencodefont
158 self
._encoding
= ef
.vector
161 self
._encoding
= None
162 return self
._encoding
165 return ("'%s' is '%s' read from '%s' encoded as '%s'" %
166 (self
.texname
, self
.basepsname
, self
.fontfile
, repr(self
.encodingfile
)))
170 def readfontmap(filenames
):
171 """ read font map from filename (without path) """
173 for filename
in filenames
:
174 mapfile
= config
.open(filename
, [config
.format
.fontmap
, config
.format
.dvips_config
], ascii
=True)
176 for line
in mapfile
.readlines():
179 if not (line
=="" or line
[0] in (" ", "%", "*", ";" , "#")):
182 except (ParseError
, UnsupportedPSFragment
) as e
:
183 warnings
.warn("Ignoring line %i in mapping file '%s': %s" % (lineno
, filename
, e
))
184 except UnsupportedFontFormat
as e
:
187 fontmap
[fm
.texname
] = fm