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

Skip to content

Commit e2b7d20

Browse files
anntzerLangQi99
authored andcommitted
Prepare for MetaFont/PK font support.
TeX MetaFont (.mf) fonts (e.g., from `\usepackage{concmath}`) need to be converted to raster (.pk) fonts before usage. In dvipng or pdftex this is done by invoking mktexpk via kpsewhich. This patch ensures that our calls to kpsewhich likewise also generate and finds the raster files (in Matplotlib's own temporary cache) when needed. The resolution (600dpi) is the documented default (see e.g. the -D option in `man kpsewhich`). Note that this is only a preliminary step towards full MF/PK font support, which would also require parsing the PK file and embedding the glyphs into our final output.
1 parent 0a744f0 commit e2b7d20

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})")

lib/matplotlib/mpl-data/kpsewhich.lua

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)