Skip to content

Commit 649db05

Browse files
committed
Avoid repeatedly accessing enum values in type1font parsing.
Enum getattr is surprisingly slow; avoiding repeated attribute accesses speeds up ``` python -c 'from pylab import *; mpl.use("pdf"); rcParams["text.usetex"] = True; plot(); [savefig("/tmp/test.pdf", backend="pdf") for _ in range(100)]' ``` by ~5%
1 parent 252f963 commit 649db05

File tree

1 file changed

+28
-14
lines changed

1 file changed

+28
-14
lines changed

lib/matplotlib/type1font.py

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,17 @@ def _tokens(cls, text):
145145
A PostScript tokenizer. Yield (token, value) pairs such as
146146
(_TokenType.whitespace, ' ') or (_TokenType.name, '/Foobar').
147147
"""
148+
# Preload enum members for speed.
149+
tok_whitespace = _TokenType.whitespace
150+
tok_name = _TokenType.name
151+
tok_string = _TokenType.string
152+
tok_delimiter = _TokenType.delimiter
153+
tok_number = _TokenType.number
148154
pos = 0
149155
while pos < len(text):
150156
match = cls._whitespace_or_comment_re.match(text, pos)
151157
if match:
152-
yield (_TokenType.whitespace, match.group())
158+
yield (tok_whitespace, match.group())
153159
pos = match.end()
154160
elif text[pos] == b'(':
155161
start = pos
@@ -166,25 +172,25 @@ def _tokens(cls, text):
166172
depth -= 1
167173
else: # a backslash - skip the next character
168174
pos += 1
169-
yield (_TokenType.string, text[start:pos])
175+
yield (tok_string, text[start:pos])
170176
elif text[pos:pos + 2] in (b'<<', b'>>'):
171-
yield (_TokenType.delimiter, text[pos:pos + 2])
177+
yield (tok_delimiter, text[pos:pos + 2])
172178
pos += 2
173179
elif text[pos] == b'<':
174180
start = pos
175181
pos = text.index(b'>', pos)
176-
yield (_TokenType.string, text[start:pos])
182+
yield (tok_string, text[start:pos])
177183
else:
178184
match = cls._token_re.match(text, pos)
179185
if match:
180186
try:
181187
float(match.group())
182-
yield (_TokenType.number, match.group())
188+
yield (tok_number, match.group())
183189
except ValueError:
184-
yield (_TokenType.name, match.group())
190+
yield (tok_name, match.group())
185191
pos = match.end()
186192
else:
187-
yield (_TokenType.delimiter, text[pos:pos + 1])
193+
yield (tok_delimiter, text[pos:pos + 1])
188194
pos += 1
189195

190196
def _parse(self):
@@ -193,28 +199,33 @@ def _parse(self):
193199
of parsing is described in Chapter 10 "Adobe Type Manager
194200
Compatibility" of the Type-1 spec.
195201
"""
202+
# Preload enum members for speed.
203+
tok_whitespace = _TokenType.whitespace
204+
tok_name = _TokenType.name
205+
tok_string = _TokenType.string
206+
tok_number = _TokenType.number
196207
# Start with reasonable defaults
197208
prop = {'weight': 'Regular', 'ItalicAngle': 0.0, 'isFixedPitch': False,
198209
'UnderlinePosition': -100, 'UnderlineThickness': 50}
199210
filtered = ((token, value)
200211
for token, value in self._tokens(self.parts[0])
201-
if token is not _TokenType.whitespace)
212+
if token is not tok_whitespace)
202213
# The spec calls this an ASCII format; in Python 2.x we could
203214
# just treat the strings and names as opaque bytes but let's
204215
# turn them into proper Unicode, and be lenient in case of high bytes.
205216
def convert(x): return x.decode('ascii', 'replace')
206217
for token, value in filtered:
207-
if token is _TokenType.name and value.startswith(b'/'):
218+
if token is tok_name and value.startswith(b'/'):
208219
key = convert(value[1:])
209220
token, value = next(filtered)
210-
if token is _TokenType.name:
221+
if token is tok_name:
211222
if value in (b'true', b'false'):
212223
value = value == b'true'
213224
else:
214225
value = convert(value.lstrip(b'/'))
215-
elif token is _TokenType.string:
226+
elif token is tok_string:
216227
value = convert(value.lstrip(b'(').rstrip(b')'))
217-
elif token is _TokenType.number:
228+
elif token is tok_number:
218229
if b'.' in value:
219230
value = float(value)
220231
else:
@@ -240,6 +251,9 @@ def convert(x): return x.decode('ascii', 'replace')
240251

241252
@classmethod
242253
def _transformer(cls, tokens, slant, extend):
254+
tok_whitespace = _TokenType.whitespace
255+
tok_name = _TokenType.name
256+
243257
def fontname(name):
244258
result = name
245259
if slant:
@@ -275,7 +289,7 @@ def replacer(tokens):
275289
token, value = next(tokens) # name, e.g., /FontMatrix
276290
yield value
277291
token, value = next(tokens) # possible whitespace
278-
while token is _TokenType.whitespace:
292+
while token is tok_whitespace:
279293
yield value
280294
token, value = next(tokens)
281295
if value != b'[': # name/number/etc.
@@ -300,7 +314,7 @@ def suppress(tokens):
300314
b'/UniqueID': suppress}
301315

302316
for token, value in tokens:
303-
if token is _TokenType.name and value in table:
317+
if token is tok_name and value in table:
304318
yield from table[value](
305319
itertools.chain([(token, value)], tokens))
306320
else:

0 commit comments

Comments
 (0)