4949_log = logging .getLogger (__name__ )
5050
5151
52- # some afm files have floats where we are expecting ints -- there is
53- # probably a better way to handle this (support floats, round rather
54- # than truncate). But I don't know what the best approach is now and
55- # this change to _to_int should at least prevent mpl from crashing on
56- # these JDH (2009-11-06)
57-
5852def _to_int (x ):
53+ # Some AFM files have floats where we are expecting ints -- there is
54+ # probably a better way to handle this (support floats, round rather
55+ # than truncate). But I don't know what the best approach is now and
56+ # this change to _to_int should at least prevent mpl from crashing on
57+ # these JDH (2009-11-06)
5958 return int (float (x ))
6059
6160
62- _to_float = float
61+ def _to_float (x ):
62+ # Some AFM files use "," instead of "." as decimal separator -- this
63+ # shouldn't be ambiguous (unless someone is wicked enough to use "," as
64+ # thousands separator...).
65+ if isinstance (x , bytes ):
66+ # Encoding doesn't really matter -- if we have codepoints >127 the call
67+ # to float() will error anyways.
68+ x = x .decode ('latin-1' )
69+ return float (x .replace (',' , '.' ))
6370
6471
6572def _to_str (x ):
@@ -84,18 +91,15 @@ def _to_bool(s):
8491
8592def _sanity_check (fh ):
8693 """
87- Check if the file at least looks like AFM.
88- If not, raise `RuntimeError`.
94+ Check if the file looks like AFM; if it doesn't, raise `RuntimeError`.
8995 """
90-
9196 # Remember the file position in case the caller wants to
9297 # do something else with the file.
9398 pos = fh .tell ()
9499 try :
95100 line = next (fh )
96101 finally :
97102 fh .seek (pos , 0 )
98-
99103 # AFM spec, Section 4: The StartFontMetrics keyword [followed by a
100104 # version number] must be the first line in the file, and the
101105 # EndFontMetrics keyword must be the last non-empty line in the
@@ -122,7 +126,7 @@ def _parse_header(fh):
122126 XHeight, Ascender, Descender, StartCharMetrics
123127
124128 """
125- headerConverters = {
129+ header_converters = {
126130 b'StartFontMetrics' : _to_float ,
127131 b'FontName' : _to_str ,
128132 b'FullName' : _to_str ,
@@ -131,10 +135,13 @@ def _parse_header(fh):
131135 b'ItalicAngle' : _to_float ,
132136 b'IsFixedPitch' : _to_bool ,
133137 b'FontBBox' : _to_list_of_ints ,
134- b'UnderlinePosition' : _to_int ,
135- b'UnderlineThickness' : _to_int ,
138+ b'UnderlinePosition' : _to_float ,
139+ b'UnderlineThickness' : _to_float ,
136140 b'Version' : _to_str ,
137- b'Notice' : _to_str ,
141+ # Some AFM files have non-ASCII characters (which are not allowed by
142+ # the spec). Given that there is actually no public API to even access
143+ # this field, just return it as straight bytes.
144+ b'Notice' : lambda x : x ,
138145 b'EncodingScheme' : _to_str ,
139146 b'CapHeight' : _to_float , # Is the second version a mistake, or
140147 b'Capheight' : _to_float , # do some AFM files contain 'Capheight'? -JKS
@@ -162,13 +169,15 @@ def _parse_header(fh):
162169 val = b''
163170
164171 try :
165- d [key ] = headerConverters [key ](val )
166- except ValueError :
167- _log .error ('Value error parsing header in AFM: %s, %s' , key , val )
168- continue
172+ converter = header_converters [key ]
169173 except KeyError :
170174 _log .error ('Found an unknown keyword in AFM header (was %r)' % key )
171175 continue
176+ try :
177+ d [key ] = converter (val )
178+ except ValueError :
179+ _log .error ('Value error parsing header in AFM: %s, %s' , key , val )
180+ continue
172181 if key == b'StartCharMetrics' :
173182 return d
174183 raise RuntimeError ('Bad parse' )
0 commit comments