Skip to content

Commit 8b5516b

Browse files
authored
Merge pull request #23427 from anntzer/pgf_
Tweak pgf escapes.
2 parents 72cee0a + e8acd57 commit 8b5516b

File tree

2 files changed

+32
-21
lines changed

2 files changed

+32
-21
lines changed

lib/matplotlib/backends/backend_pgf.py

+15-10
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ def _get_preamble():
7070
path = pathlib.Path(fm.findfont(family))
7171
preamble.append(r"\%s{%s}[Path=\detokenize{%s/}]" % (
7272
command, path.name, path.parent.as_posix()))
73+
preamble.append(mpl.texmanager._usepackage_if_not_loaded(
74+
"underscore", option="strings")) # Documented as "must come last".
7375
return "\n".join(preamble)
7476

7577

@@ -84,9 +86,8 @@ def _get_preamble():
8486
_NO_ESCAPE = r"(?<!\\)(?:\\\\)*"
8587
_split_math = re.compile(_NO_ESCAPE + r"\$").split
8688
_replace_escapetext = functools.partial(
87-
# When the next character is _, ^, $, or % (not preceded by an escape),
88-
# insert a backslash.
89-
re.compile(_NO_ESCAPE + "(?=[_^$%])").sub, "\\\\")
89+
# When the next character is an unescaped % or ^, insert a backslash.
90+
re.compile(_NO_ESCAPE + "(?=[%^])").sub, "\\\\")
9091
_replace_mathdefault = functools.partial(
9192
# Replace \mathdefault (when not preceded by an escape) by empty string.
9293
re.compile(_NO_ESCAPE + r"(\\mathdefault)").sub, "")
@@ -106,7 +107,7 @@ def _tex_escape(text):
106107
``$`` with ``\(\displaystyle %s\)``. Escaped math separators (``\$``)
107108
are ignored.
108109
109-
The following characters are escaped in text segments: ``_^$%``
110+
The following characters are escaped in text segments: ``^%``
110111
"""
111112
# Sometimes, matplotlib adds the unknown command \mathdefault.
112113
# Not using \mathnormal instead since this looks odd for the latex cm font.
@@ -309,8 +310,10 @@ def __init__(self):
309310
test_input = self.latex_header + latex_end
310311
stdout, stderr = latex.communicate(test_input)
311312
if latex.returncode != 0:
312-
raise LatexError("LaTeX returned an error, probably missing font "
313-
"or error in preamble.", stdout)
313+
raise LatexError(
314+
f"LaTeX errored (probably missing font or error in preamble) "
315+
f"while processing the following input:\n{test_input}",
316+
stdout)
314317

315318
self.latex = None # Will be set up on first use.
316319
# Per-instance cache.
@@ -358,14 +361,16 @@ def _get_box_metrics(self, tex):
358361
try:
359362
answer = self._expect_prompt()
360363
except LatexError as err:
361-
raise ValueError("Error measuring {!r}\nLaTeX Output:\n{}"
364+
# Here and below, use '{}' instead of {!r} to avoid doubling all
365+
# backslashes.
366+
raise ValueError("Error measuring {}\nLaTeX Output:\n{}"
362367
.format(tex, err.latex_output)) from err
363368
try:
364369
# Parse metrics from the answer string. Last line is prompt, and
365370
# next-to-last-line is blank line from \typeout.
366371
width, height, offset = answer.splitlines()[-3].split(",")
367372
except Exception as err:
368-
raise ValueError("Error measuring {!r}\nLaTeX Output:\n{}"
373+
raise ValueError("Error measuring {}\nLaTeX Output:\n{}"
369374
.format(tex, answer)) from err
370375
w, h, o = float(width[:-2]), float(height[:-2]), float(offset[:-2])
371376
# The height returned from LaTeX goes from base to top;
@@ -864,8 +869,8 @@ def print_pdf(self, fname_or_fh, *, metadata=None, **kwargs):
864869
r"\documentclass[12pt]{minimal}",
865870
r"\usepackage[papersize={%fin,%fin}, margin=0in]{geometry}"
866871
% (w, h),
867-
_get_preamble(),
868872
r"\usepackage{pgf}",
873+
_get_preamble(),
869874
r"\begin{document}",
870875
r"\centering",
871876
r"\input{figure.pgf}",
@@ -975,8 +980,8 @@ def _write_header(self, width_inches, height_inches):
975980
r"\documentclass[12pt]{minimal}",
976981
r"\usepackage[papersize={%fin,%fin}, margin=0in]{geometry}"
977982
% (width_inches, height_inches),
978-
_get_preamble(),
979983
r"\usepackage{pgf}",
984+
_get_preamble(),
980985
r"\setlength{\parindent}{0pt}",
981986
r"\begin{document}%",
982987
])

lib/matplotlib/tests/test_backend_pgf.py

+17-11
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,23 @@ def compare_figure(fname, savefig_kwargs={}, tol=0):
3333
raise ImageComparisonFailure(err)
3434

3535

36+
@pytest.mark.parametrize('plain_text, escaped_text', [
37+
(r'quad_sum: $\sum x_i^2$', r'quad_sum: \(\displaystyle \sum x_i^2\)'),
38+
('% not a comment', r'\% not a comment'),
39+
('^not', r'\^not'),
40+
])
41+
def test_tex_escape(plain_text, escaped_text):
42+
assert _tex_escape(plain_text) == escaped_text
43+
44+
45+
@needs_pgf_xelatex
46+
@pytest.mark.backend('pgf')
47+
def test_tex_special_chars(tmp_path):
48+
fig = plt.figure()
49+
fig.text(.5, .5, "_^ $a_b^c$")
50+
fig.savefig(tmp_path / "test.pdf") # Should not error.
51+
52+
3653
def create_figure():
3754
plt.figure()
3855
x = np.linspace(0, 1, 15)
@@ -59,17 +76,6 @@ def create_figure():
5976
plt.ylim(0, 1)
6077

6178

62-
@pytest.mark.parametrize('plain_text, escaped_text', [
63-
(r'quad_sum: $\sum x_i^2$', r'quad\_sum: \(\displaystyle \sum x_i^2\)'),
64-
(r'no \$splits \$ here', r'no \$splits \$ here'),
65-
('with_underscores', r'with\_underscores'),
66-
('% not a comment', r'\% not a comment'),
67-
('^not', r'\^not'),
68-
])
69-
def test_tex_escape(plain_text, escaped_text):
70-
assert _tex_escape(plain_text) == escaped_text
71-
72-
7379
# test compiling a figure to pdf with xelatex
7480
@needs_pgf_xelatex
7581
@pytest.mark.backend('pgf')

0 commit comments

Comments
 (0)