Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4514212

Browse files
committed
Call kpsewhich with more arguments at one time
This should improve performance if there is a significant startup cost to running kpsewhich, as reported by some users in #4880.
1 parent e987a58 commit 4514212

File tree

4 files changed

+144
-10
lines changed

4 files changed

+144
-10
lines changed

lib/matplotlib/dviread.py

Lines changed: 111 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
from six.moves import xrange
2424

2525
from collections import namedtuple
26-
from contextlib import closing
2726
from functools import partial, wraps
2827
import logging
2928
import numpy as np
@@ -185,8 +184,7 @@ def wrapper(self, byte):
185184
class Dvi(object):
186185
"""
187186
A reader for a dvi ("device-independent") file, as produced by TeX.
188-
The current implementation can only iterate through pages in order,
189-
and does not even attempt to verify the postamble.
187+
The current implementation can only iterate through pages in order.
190188
191189
This class can be used as a context manager to close the underlying
192190
file upon exit. Pages can be read via iteration. Here is an overly
@@ -195,33 +193,55 @@ class Dvi(object):
195193
>>> with matplotlib.dviread.Dvi('input.dvi', 72) as dvi:
196194
>>> for page in dvi:
197195
>>> print ''.join(unichr(t.glyph) for t in page.text)
196+
197+
Parameters
198+
----------
199+
200+
filename : str
201+
dvi file to read
202+
dpi : number or None
203+
Dots per inch, can be floating-point; this affects the
204+
coordinates returned. Use None to get TeX's internal units
205+
which are likely only useful for debugging.
206+
cache : _tex_support_cache instance, optional
207+
Support file cache instance, defaults to the _tex_support_cache
208+
singleton.
198209
"""
199210
# dispatch table
200211
_dtable = [None for _ in xrange(256)]
201212
dispatch = partial(_dispatch, _dtable)
202213

203-
def __init__(self, filename, dpi):
214+
def __init__(self, filename, dpi, cache=None):
204215
"""
205216
Read the data from the file named *filename* and convert
206217
TeX's internal units to units of *dpi* per inch.
207218
*dpi* only sets the units and does not limit the resolution.
208219
Use None to return TeX's internal units.
209220
"""
210221
_log.debug('Dvi: %s', filename)
222+
if cache is None:
223+
cache = _tex_support_cache.get_cache()
224+
self.cache = cache
211225
self.file = open(filename, 'rb')
212226
self.dpi = dpi
213227
self.fonts = {}
214228
self.state = _dvistate.pre
215229
self.baseline = self._get_baseline(filename)
230+
self.fontnames = sorted(set(self._read_fonts()))
231+
# populate kpsewhich cache with font pathnames
232+
find_tex_files([x + suffix for x in self.fontnames
233+
for suffix in ('.tfm', '.vf', '.pfb')],
234+
cache)
235+
cache.optimize()
216236

217237
def _get_baseline(self, filename):
218238
if rcParams['text.latex.preview']:
219239
base, ext = os.path.splitext(filename)
220240
baseline_filename = base + ".baseline"
221241
if os.path.exists(baseline_filename):
222242
with open(baseline_filename, 'rb') as fd:
223-
l = fd.read().split()
224-
height, depth, width = l
243+
line = fd.read().split()
244+
height, depth, width = line
225245
return float(depth)
226246
return None
227247

@@ -308,6 +328,62 @@ def _output(self):
308328
return Page(text=text, boxes=boxes, width=(maxx-minx)*d,
309329
height=(maxy_pure-miny)*d, descent=descent)
310330

331+
def _read_fonts(self):
332+
"""Read the postamble of the file and return a list of fonts used."""
333+
334+
file = self.file
335+
offset = -1
336+
while offset > -100:
337+
file.seek(offset, 2)
338+
byte = ord(file.read(1)[0])
339+
if byte != 223:
340+
break
341+
offset -= 1
342+
if offset >= -4:
343+
raise ValueError(
344+
"malformed dvi file %s: too few 223 bytes" % file.name)
345+
if byte != 2:
346+
raise ValueError(
347+
("malformed dvi file %s: post-postamble "
348+
"identification byte not 2") % file.name)
349+
file.seek(offset - 4, 2)
350+
offset = struct.unpack('!I', file.read(4))[0]
351+
file.seek(offset, 0)
352+
try:
353+
byte = ord(file.read(1)[0])
354+
except TypeError:
355+
# "ord() expected a character, but string of length 0 found"
356+
raise ValueError(
357+
"malformed dvi file %s: postamble offset %d out of range"
358+
% (file.name, offset))
359+
if byte != 248:
360+
raise ValueError(
361+
"malformed dvi file %s: postamble not found at offset %d"
362+
% (file.name, offset))
363+
364+
fonts = []
365+
file.seek(28, 1)
366+
while True:
367+
byte = ord(file.read(1)[0])
368+
if 243 <= byte <= 246:
369+
_, _, _, _, a, length = (
370+
_arg_olen1(self, byte-243),
371+
_arg(4, False, self, None),
372+
_arg(4, False, self, None),
373+
_arg(4, False, self, None),
374+
_arg(1, False, self, None),
375+
_arg(1, False, self, None))
376+
fontname = file.read(a + length)[-length:].decode('ascii')
377+
fonts.append(fontname)
378+
elif byte == 249:
379+
break
380+
else:
381+
raise ValueError(
382+
"malformed dvi file %s: opcode %d in postamble"
383+
% (file.name, byte))
384+
file.seek(0, 0)
385+
return fonts
386+
311387
def _read(self):
312388
"""
313389
Read one page from the file. Return True if successful,
@@ -616,6 +692,10 @@ class Vf(Dvi):
616692
----------
617693
618694
filename : string or bytestring
695+
vf file to read
696+
cache : _tex_support_cache instance, optional
697+
Support file cache instance, defaults to the _tex_support_cache
698+
singleton.
619699
620700
Notes
621701
-----
@@ -626,8 +706,8 @@ class Vf(Dvi):
626706
but replaces the `_read` loop and dispatch mechanism.
627707
"""
628708

629-
def __init__(self, filename):
630-
Dvi.__init__(self, filename, 0)
709+
def __init__(self, filename, cache=None):
710+
Dvi.__init__(self, filename, dpi=0, cache=cache)
631711
try:
632712
self._first_font = None
633713
self._chars = {}
@@ -638,6 +718,27 @@ def __init__(self, filename):
638718
def __getitem__(self, code):
639719
return self._chars[code]
640720

721+
def _read_fonts(self):
722+
"""Read through the font-definition section of the vf file
723+
and return the list of font names."""
724+
fonts = []
725+
self.file.seek(0, 0)
726+
while True:
727+
byte = ord(self.file.read(1)[0])
728+
if byte <= 242 or byte >= 248:
729+
break
730+
elif 243 <= byte <= 246:
731+
_ = self._arg(byte - 242)
732+
_, _, _, a, length = [self._arg(x) for x in (4, 4, 4, 1, 1)]
733+
fontname = self.file.read(a + length)[-length:].decode('ascii')
734+
fonts.append(fontname)
735+
elif byte == 247:
736+
_, k = self._arg(1), self._arg(1)
737+
_ = self.file.read(k)
738+
_, _ = self._arg(4), self._arg(4)
739+
self.file.seek(0, 0)
740+
return fonts
741+
641742
def _read(self):
642743
"""
643744
Read one page from the file. Return True if successful,
@@ -674,8 +775,8 @@ def _read(self):
674775
self._init_packet(packet_len)
675776
elif 243 <= byte <= 246:
676777
k = self._arg(byte - 242, byte == 246)
677-
c, s, d, a, l = [self._arg(x) for x in (4, 4, 4, 1, 1)]
678-
self._fnt_def_real(k, c, s, d, a, l)
778+
c, s, d, a, length = [self._arg(x) for x in (4, 4, 4, 1, 1)]
779+
self._fnt_def_real(k, c, s, d, a, length)
679780
if self._first_font is None:
680781
self._first_font = k
681782
elif byte == 247: # preamble
Binary file not shown.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
(FAMILY TESTING)
2+
(COMMENT Test data for matplotlib)
3+
(COMMENT Run vptovf virtual.vpl to obtain virtual.vf)
4+
(FACE O 352)
5+
(CODINGSCHEME TEX TEXT)
6+
(DESIGNSIZE R 10.0)
7+
(FONTDIMEN
8+
(SLANT R 0.0)
9+
(SPACE R 0.333334)
10+
(STRETCH R 0.166667)
11+
(SHRINK R 0.111112)
12+
(XHEIGHT R 0.430555)
13+
(QUAD R 1.000003)
14+
(EXTRASPACE R 0.111112)
15+
)
16+
(MAPFONT D 0
17+
(FONTNAME cmr10)
18+
(FONTDSIZE R 10.0)
19+
)
20+
(MAPFONT D 1
21+
(FONTNAME cmex10)
22+
(FONTDSIZE R 10.0)
23+
)

lib/matplotlib/tests/test_dviread.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,16 @@ def test_dviread():
8080
assert data == correct
8181

8282

83+
@skip_if_command_unavailable(["kpsewhich", "-version"])
84+
def test_dviread_get_fonts():
85+
dir = os.path.join(os.path.dirname(__file__), 'baseline_images', 'dviread')
86+
with dr.Dvi(os.path.join(dir, 'test.dvi'), None) as dvi:
87+
assert dvi.fontnames == \
88+
['cmex10', 'cmmi10', 'cmmi5', 'cmr10', 'cmr5', 'cmr7']
89+
with dr.Vf(os.path.join(dir, 'virtual.vf')) as vf:
90+
assert vf.fontnames == ['cmex10', 'cmr10']
91+
92+
8393
def test_tex_support_cache(tmpdir):
8494
dbfile = str(tmpdir / "test.db")
8595
cache = dr._tex_support_cache(filename=dbfile)

0 commit comments

Comments
 (0)