49
49
_log = logging .getLogger (__name__ )
50
50
51
51
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
-
58
52
def _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)
59
58
return int (float (x ))
60
59
61
60
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 (',' , '.' ))
63
70
64
71
65
72
def _to_str (x ):
@@ -84,18 +91,15 @@ def _to_bool(s):
84
91
85
92
def _sanity_check (fh ):
86
93
"""
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`.
89
95
"""
90
-
91
96
# Remember the file position in case the caller wants to
92
97
# do something else with the file.
93
98
pos = fh .tell ()
94
99
try :
95
100
line = next (fh )
96
101
finally :
97
102
fh .seek (pos , 0 )
98
-
99
103
# AFM spec, Section 4: The StartFontMetrics keyword [followed by a
100
104
# version number] must be the first line in the file, and the
101
105
# EndFontMetrics keyword must be the last non-empty line in the
@@ -122,7 +126,7 @@ def _parse_header(fh):
122
126
XHeight, Ascender, Descender, StartCharMetrics
123
127
124
128
"""
125
- headerConverters = {
129
+ header_converters = {
126
130
b'StartFontMetrics' : _to_float ,
127
131
b'FontName' : _to_str ,
128
132
b'FullName' : _to_str ,
@@ -131,10 +135,13 @@ def _parse_header(fh):
131
135
b'ItalicAngle' : _to_float ,
132
136
b'IsFixedPitch' : _to_bool ,
133
137
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 ,
136
140
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 ,
138
145
b'EncodingScheme' : _to_str ,
139
146
b'CapHeight' : _to_float , # Is the second version a mistake, or
140
147
b'Capheight' : _to_float , # do some AFM files contain 'Capheight'? -JKS
@@ -162,13 +169,15 @@ def _parse_header(fh):
162
169
val = b''
163
170
164
171
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 ]
169
173
except KeyError :
170
174
_log .error ('Found an unknown keyword in AFM header (was %r)' % key )
171
175
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
172
181
if key == b'StartCharMetrics' :
173
182
return d
174
183
raise RuntimeError ('Bad parse' )
0 commit comments