Skip to content

Commit c80e8ae

Browse files
authored
Merge pull request matplotlib#30514 from anntzer/dvipkparse
Prepare for MetaFont/PK font support.
2 parents 799f260 + b98840b commit c80e8ae

File tree

3 files changed

+84
-18
lines changed

3 files changed

+84
-18
lines changed

lib/matplotlib/dviread.py

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import fontTools.agl
3333
import numpy as np
3434

35+
import matplotlib as mpl
3536
from matplotlib import _api, cbook, font_manager
3637
from matplotlib.ft2font import LoadFlags
3738

@@ -127,7 +128,13 @@ def glyph_name_or_index(self):
127128
def _as_unicode_or_name(self):
128129
if self.font.subfont:
129130
raise NotImplementedError("Indexing TTC fonts is not supported yet")
130-
face = font_manager.get_font(self.font.resolve_path())
131+
path = self.font.resolve_path()
132+
if path.name.lower().endswith("pk"):
133+
# PK fonts have no encoding information; report glyphs as ASCII but
134+
# with a "?" to indicate that this is just a guess.
135+
return (f"{chr(self.glyph)}?" if chr(self.glyph).isprintable() else
136+
f"pk{self.glyph:#02x}")
137+
face = font_manager.get_font(path)
131138
glyph_name = face.get_glyph_name(self.index)
132139
glyph_str = fontTools.agl.toUnicode(glyph_name)
133140
return glyph_str or glyph_name
@@ -758,13 +765,24 @@ def _height_depth_of(self, char):
758765

759766
def resolve_path(self):
760767
if self._path is None:
761-
psfont = PsfontsMap(find_tex_file("pdftex.map"))[self.texname]
762-
if psfont.filename is None:
763-
raise ValueError("No usable font file found for {} ({}); "
764-
"the font may lack a Type-1 version"
765-
.format(psfont.psname.decode("ascii"),
766-
psfont.texname.decode("ascii")))
767-
self._path = Path(psfont.filename)
768+
fontmap = PsfontsMap(find_tex_file("pdftex.map"))
769+
try:
770+
psfont = fontmap[self.texname]
771+
except LookupError as exc:
772+
try:
773+
find_tex_file(f"{self.texname.decode('ascii')}.mf")
774+
except FileNotFoundError:
775+
raise exc from None
776+
else:
777+
self._path = Path(find_tex_file(
778+
f"{self.texname.decode('ascii')}.600pk"))
779+
else:
780+
if psfont.filename is None:
781+
raise ValueError("No usable font file found for {} ({}); "
782+
"the font may lack a Type-1 version"
783+
.format(psfont.psname.decode("ascii"),
784+
psfont.texname.decode("ascii")))
785+
self._path = Path(psfont.filename)
768786
return self._path
769787

770788
@cached_property
@@ -773,6 +791,8 @@ def subfont(self):
773791

774792
@cached_property
775793
def effects(self):
794+
if self.resolve_path().match("*.600pk"):
795+
return {}
776796
return PsfontsMap(find_tex_file("pdftex.map"))[self.texname].effects
777797

778798
def _index_dvi_to_freetype(self, idx):
@@ -1233,9 +1253,12 @@ def __new__(cls):
12331253

12341254
def _new_proc(self):
12351255
return subprocess.Popen(
1236-
["luatex", "--luaonly",
1237-
str(cbook._get_data_path("kpsewhich.lua"))],
1238-
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
1256+
["luatex", "--luaonly", str(cbook._get_data_path("kpsewhich.lua"))],
1257+
# mktexpk logs to stderr; suppress that.
1258+
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
1259+
# Store generated pk fonts in our own cache.
1260+
env={"MT_VARTEXFONTS": str(Path(mpl.get_cachedir(), "vartexfonts")),
1261+
**os.environ})
12391262

12401263
def search(self, filename):
12411264
if self._proc.poll() is not None: # Dead, restart it.
@@ -1287,13 +1310,16 @@ def find_tex_file(filename):
12871310
kwargs = {'env': {**os.environ, 'command_line_encoding': 'utf-8'},
12881311
'encoding': 'utf-8'}
12891312
else: # On POSIX, run through the equivalent of os.fsdecode().
1290-
kwargs = {'encoding': sys.getfilesystemencoding(),
1313+
kwargs = {'env': {**os.environ},
1314+
'encoding': sys.getfilesystemencoding(),
12911315
'errors': 'surrogateescape'}
1316+
kwargs['env'].update(
1317+
MT_VARTEXFONTS=str(Path(mpl.get_cachedir(), "vartexfonts")))
12921318

12931319
try:
1294-
path = (cbook._check_and_log_subprocess(['kpsewhich', filename],
1295-
_log, **kwargs)
1296-
.rstrip('\n'))
1320+
path = cbook._check_and_log_subprocess(
1321+
['kpsewhich', '-mktex=pk', filename], _log, **kwargs,
1322+
).rstrip('\n')
12971323
except (FileNotFoundError, RuntimeError):
12981324
path = None
12991325

@@ -1327,7 +1353,6 @@ def _print_fields(*args):
13271353
print(" ".join(map("{:>11}".format, args)))
13281354

13291355
with Dvi(args.filename, args.dpi) as dvi:
1330-
fontmap = PsfontsMap(find_tex_file('pdftex.map'))
13311356
for page in dvi:
13321357
print(f"=== NEW PAGE === "
13331358
f"(w: {page.width}, h: {page.height}, d: {page.descent})")
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
-- see dviread._LuatexKpsewhich
22
kpse.set_program_name("latex")
3-
while true do print(kpse.lookup(io.read():gsub("\r", ""))); io.flush(); end
3+
kpse.init_prog("", 600, "ljfour")
4+
while true do print(kpse.lookup(io.read():gsub("\r", ""), {mktexpk=true})); io.flush(); end

lib/matplotlib/tests/test_dviread.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import shutil
44

55
from matplotlib import cbook, dviread as dr
6-
from matplotlib.testing import subprocess_run_for_testing
6+
from matplotlib.testing import subprocess_run_for_testing, _has_tex_package
77
import pytest
88

99

@@ -105,3 +105,43 @@ def test_dviread(tmp_path, engine, monkeypatch):
105105
]
106106
correct = json.loads((dirpath / f"{engine}.json").read_text())
107107
assert data == correct
108+
109+
110+
@pytest.mark.skipif(shutil.which("latex") is None, reason="latex is not available")
111+
@pytest.mark.skipif(not _has_tex_package("concmath"), reason="needs concmath.sty")
112+
def test_dviread_pk(tmp_path):
113+
(tmp_path / "test.tex").write_text(r"""
114+
\documentclass{article}
115+
\usepackage{concmath}
116+
\pagestyle{empty}
117+
\begin{document}
118+
Hi!
119+
\end{document}
120+
""")
121+
subprocess_run_for_testing(
122+
["latex", "test.tex"], cwd=tmp_path, check=True, capture_output=True)
123+
with dr.Dvi(tmp_path / "test.dvi", None) as dvi:
124+
pages = [*dvi]
125+
data = [
126+
{
127+
"text": [
128+
[
129+
t.x, t.y,
130+
t._as_unicode_or_name(),
131+
t.font.resolve_path().name,
132+
round(t.font.size, 2),
133+
t.font.effects,
134+
] for t in page.text
135+
],
136+
"boxes": [[b.x, b.y, b.height, b.width] for b in page.boxes]
137+
} for page in pages
138+
]
139+
correct = [{
140+
'boxes': [],
141+
'text': [
142+
[5046272, 4128768, 'H?', 'ccr10.600pk', 9.96, {}],
143+
[5530510, 4128768, 'i?', 'ccr10.600pk', 9.96, {}],
144+
[5716195, 4128768, '!?', 'ccr10.600pk', 9.96, {}],
145+
],
146+
}]
147+
assert data == correct

0 commit comments

Comments
 (0)