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

Skip to content

Commit 1ec1a3f

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 bb2c85f commit 1ec1a3f

File tree

9 files changed

+154
-10
lines changed

9 files changed

+154
-10
lines changed

lib/matplotlib/dviread.py

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

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

201222
def _get_baseline(self, filename):
202223
if rcParams['text.latex.preview']:
203224
base, ext = os.path.splitext(filename)
204225
baseline_filename = base + ".baseline"
205226
if os.path.exists(baseline_filename):
206227
with open(baseline_filename, 'rb') as fd:
207-
l = fd.read().split()
208-
height, depth, width = l
228+
line = fd.read().split()
229+
height, depth, width = line
209230
return float(depth)
210231
return None
211232

@@ -292,6 +313,61 @@ def _output(self):
292313
return Page(text=text, boxes=boxes, width=(maxx-minx)*d,
293314
height=(maxy_pure-miny)*d, descent=descent)
294315

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

604-
def __init__(self, filename):
605-
Dvi.__init__(self, filename, 0)
684+
def __init__(self, filename, cache=None):
685+
Dvi.__init__(self, filename, dpi=0, cache=cache)
606686
try:
607687
self._first_font = None
608688
self._chars = {}
@@ -613,6 +693,27 @@ def __init__(self, filename):
613693
def __getitem__(self, code):
614694
return self._chars[code]
615695

696+
def _read_fonts(self):
697+
"""Read through the font-definition section of the vf file
698+
and return the list of font names."""
699+
fonts = []
700+
self.file.seek(0, 0)
701+
while True:
702+
byte = self.file.read(1)[0]
703+
if byte <= 242 or byte >= 248:
704+
break
705+
elif 243 <= byte <= 246:
706+
_ = self._arg(byte - 242)
707+
_, _, _, a, length = [self._arg(x) for x in (4, 4, 4, 1, 1)]
708+
fontname = self.file.read(a + length)[-length:].decode('ascii')
709+
fonts.append(fontname)
710+
elif byte == 247:
711+
_, k = self._arg(1), self._arg(1)
712+
_ = self.file.read(k)
713+
_, _ = self._arg(4), self._arg(4)
714+
self.file.seek(0, 0)
715+
return fonts
716+
616717
def _read(self):
617718
"""
618719
Read one page from the file. Return True if successful,
@@ -650,8 +751,8 @@ def _read(self):
650751
self._init_packet(packet_len)
651752
elif 243 <= byte <= 246:
652753
k = self._arg(byte - 242, byte == 246)
653-
c, s, d, a, l = [self._arg(x) for x in (4, 4, 4, 1, 1)]
654-
self._fnt_def_real(k, c, s, d, a, l)
754+
c, s, d, a, length = [self._arg(x) for x in (4, 4, 4, 1, 1)]
755+
self._fnt_def_real(k, c, s, d, a, length)
655756
if self._first_font is None:
656757
self._first_font = k
657758
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: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,26 @@ 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"), (2, "post-postamble identification"),
93+
(3, "postamble offset"), (4, "postamble not found"),
94+
(5, "opcode 127 in postamble")]:
95+
with pytest.raises(ValueError) as e:
96+
dr.Dvi(os.path.join(dir, "broken%d.dvi" % n), None)
97+
assert message in str(e.value)
98+
99+
80100
def test_TeXSupportCache(tmpdir):
81101
dbfile = str(tmpdir / "test.db")
82102
cache = dr.TeXSupportCache(filename=dbfile)

0 commit comments

Comments
 (0)