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

Skip to content

Commit ba47418

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 3ce3061 commit ba47418

File tree

9 files changed

+156
-10
lines changed

9 files changed

+156
-10
lines changed

lib/matplotlib/dviread.py

Lines changed: 111 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -171,43 +171,64 @@ def wrapper(self, byte):
171171
class Dvi(object):
172172
"""
173173
A reader for a dvi ("device-independent") file, as produced by TeX.
174-
The current implementation can only iterate through pages in order,
175-
and does not even attempt to verify the postamble.
174+
The current implementation can only iterate through pages in order.
176175
177176
This class can be used as a context manager to close the underlying
178177
file upon exit. Pages can be read via iteration. Here is an overly
179178
simple way to extract text without trying to detect whitespace::
180179
181180
>>> with matplotlib.dviread.Dvi('input.dvi', 72) as dvi:
182181
>>> for page in dvi:
183-
>>> print(''.join(unichr(t.glyph) for t in page.text))
182+
>>> print(''.join(chr(t.glyph) for t in page.text))
183+
184+
Parameters
185+
----------
186+
187+
filename : str
188+
dvi file to read
189+
dpi : number or None
190+
Dots per inch, can be floating-point; this affects the
191+
coordinates returned. Use None to get TeX's internal units
192+
which are likely only useful for debugging.
193+
cache : TeXSupportCache instance, optional
194+
Support file cache instance, defaults to the TeXSupportCache
195+
singleton.
184196
"""
185197
# dispatch table
186198
_dtable = [None] * 256
187199
_dispatch = partial(_dispatch, _dtable)
188200

189-
def __init__(self, filename, dpi):
201+
def __init__(self, filename, dpi, cache=None):
190202
"""
191203
Read the data from the file named *filename* and convert
192204
TeX's internal units to units of *dpi* per inch.
193205
*dpi* only sets the units and does not limit the resolution.
194206
Use None to return TeX's internal units.
195207
"""
196208
_log.debug('Dvi: %s', filename)
209+
if cache is None:
210+
cache = TeXSupportCache.get_cache()
211+
self.cache = cache
197212
self.file = open(filename, 'rb')
198213
self.dpi = dpi
199214
self.fonts = {}
200215
self.state = _dvistate.pre
201216
self.baseline = self._get_baseline(filename)
217+
self.fontnames = sorted(set(self._read_fonts()))
218+
# populate kpsewhich cache with font pathnames
219+
find_tex_files([x + suffix for x in self.fontnames
220+
for suffix in ('.tfm', '.vf', '.pfb')],
221+
cache)
222+
cache.optimize()
202223

203224
def _get_baseline(self, filename):
204225
if rcParams['text.latex.preview']:
205226
base, ext = os.path.splitext(filename)
206227
baseline_filename = base + ".baseline"
207228
if os.path.exists(baseline_filename):
208229
with open(baseline_filename, 'rb') as fd:
209-
l = fd.read().split()
210-
height, depth, width = l
230+
line = fd.read().split()
231+
height, depth, width = line
211232
return float(depth)
212233
return None
213234

@@ -294,6 +315,61 @@ def _output(self):
294315
return Page(text=text, boxes=boxes, width=(maxx-minx)*d,
295316
height=(maxy_pure-miny)*d, descent=descent)
296317

318+
def _read_fonts(self):
319+
"""Read the postamble of the file and return a list of fonts used."""
320+
321+
file = self.file
322+
offset = -1
323+
while offset > -100:
324+
file.seek(offset, 2)
325+
byte = file.read(1)[0]
326+
if byte != 223:
327+
break
328+
offset -= 1
329+
if offset >= -4:
330+
raise ValueError(
331+
"malformed dvi file %s: too few 223 bytes" % file.name)
332+
if byte != 2:
333+
raise ValueError(
334+
("malformed dvi file %s: post-postamble "
335+
"identification byte not 2") % file.name)
336+
file.seek(offset - 4, 2)
337+
offset = struct.unpack('!I', file.read(4))[0]
338+
file.seek(offset, 0)
339+
try:
340+
byte = file.read(1)[0]
341+
except IndexError:
342+
raise ValueError(
343+
"malformed dvi file %s: postamble offset %d out of range"
344+
% (file.name, offset))
345+
if byte != 248:
346+
raise ValueError(
347+
"malformed dvi file %s: postamble not found at offset %d"
348+
% (file.name, offset))
349+
350+
fonts = []
351+
file.seek(28, 1)
352+
while True:
353+
byte = file.read(1)[0]
354+
if 243 <= byte <= 246:
355+
_, _, _, _, a, length = (
356+
_arg_olen1(self, byte-243),
357+
_arg(4, False, self, None),
358+
_arg(4, False, self, None),
359+
_arg(4, False, self, None),
360+
_arg(1, False, self, None),
361+
_arg(1, False, self, None))
362+
fontname = file.read(a + length)[-length:].decode('ascii')
363+
fonts.append(fontname)
364+
elif byte == 249:
365+
break
366+
else:
367+
raise ValueError(
368+
"malformed dvi file %s: opcode %d in postamble"
369+
% (file.name, byte))
370+
file.seek(0, 0)
371+
return fonts
372+
297373
def _read(self):
298374
"""
299375
Read one page from the file. Return True if successful,
@@ -593,6 +669,10 @@ class Vf(Dvi):
593669
----------
594670
595671
filename : string or bytestring
672+
vf file to read
673+
cache : TeXSupportCache instance, optional
674+
Support file cache instance, defaults to the TeXSupportCache
675+
singleton.
596676
597677
Notes
598678
-----
@@ -603,8 +683,8 @@ class Vf(Dvi):
603683
but replaces the `_read` loop and dispatch mechanism.
604684
"""
605685

606-
def __init__(self, filename):
607-
Dvi.__init__(self, filename, 0)
686+
def __init__(self, filename, cache=None):
687+
Dvi.__init__(self, filename, dpi=0, cache=cache)
608688
try:
609689
self._first_font = None
610690
self._chars = {}
@@ -615,6 +695,27 @@ def __init__(self, filename):
615695
def __getitem__(self, code):
616696
return self._chars[code]
617697

698+
def _read_fonts(self):
699+
"""Read through the font-definition section of the vf file
700+
and return the list of font names."""
701+
fonts = []
702+
self.file.seek(0, 0)
703+
while True:
704+
byte = self.file.read(1)[0]
705+
if byte <= 242 or byte >= 248:
706+
break
707+
elif 243 <= byte <= 246:
708+
_ = self._arg(byte - 242)
709+
_, _, _, a, length = [self._arg(x) for x in (4, 4, 4, 1, 1)]
710+
fontname = self.file.read(a + length)[-length:].decode('ascii')
711+
fonts.append(fontname)
712+
elif byte == 247:
713+
_, k = self._arg(1), self._arg(1)
714+
_ = self.file.read(k)
715+
_, _ = self._arg(4), self._arg(4)
716+
self.file.seek(0, 0)
717+
return fonts
718+
618719
def _read(self):
619720
"""
620721
Read one page from the file. Return True if successful,
@@ -652,8 +753,8 @@ def _read(self):
652753
self._init_packet(packet_len)
653754
elif 243 <= byte <= 246:
654755
k = self._arg(byte - 242, byte == 246)
655-
c, s, d, a, l = [self._arg(x) for x in (4, 4, 4, 1, 1)]
656-
self._fnt_def_real(k, c, s, d, a, l)
756+
c, s, d, a, length = [self._arg(x) for x in (4, 4, 4, 1, 1)]
757+
self._fnt_def_real(k, c, s, d, a, length)
657758
if self._first_font is None:
658759
self._first_font = k
659760
elif byte == 247: # preamble
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,28 @@ def test_dviread():
7777
assert data == correct
7878

7979

80+
@skip_if_command_unavailable(["kpsewhich", "-version"])
81+
def test_dviread_get_fonts():
82+
dir = os.path.join(os.path.dirname(__file__), 'baseline_images', 'dviread')
83+
with dr.Dvi(os.path.join(dir, 'test.dvi'), None) as dvi:
84+
assert dvi.fontnames == \
85+
['cmex10', 'cmmi10', 'cmmi5', 'cmr10', 'cmr5', 'cmr7']
86+
with dr.Vf(os.path.join(dir, 'virtual.vf')) as vf:
87+
assert vf.fontnames == ['cmex10', 'cmr10']
88+
89+
90+
def test_dviread_get_fonts_error_handling():
91+
dir = os.path.join(os.path.dirname(__file__), 'baseline_images', 'dviread')
92+
for n, message in [(1, "too few 223 bytes"),
93+
(2, "post-postamble identification"),
94+
(3, "postamble offset"),
95+
(4, "postamble not found"),
96+
(5, "opcode 127 in postamble")]:
97+
with pytest.raises(ValueError) as e:
98+
dr.Dvi(os.path.join(dir, "broken%d.dvi" % n), None)
99+
assert message in str(e.value)
100+
101+
80102
def test_TeXSupportCache(tmpdir):
81103
dbfile = str(tmpdir / "test.db")
82104
cache = dr.TeXSupportCache(filename=dbfile)

0 commit comments

Comments
 (0)