Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit b5c535b

Browse filesBrowse files
committed
Tweak pgf escapes.
- We don't need to escape underscores manually, but can rely on the underscore package like we already do for usetex. - We don't actually escape dollars (we parse them as math delimiters first). - Slightly tweak error message generation. - Move escaping tests before the big `create_figure` definition, which is used for further tests below.
1 parent 165df1b commit b5c535b
Copy full SHA for b5c535b

File tree

Expand file treeCollapse file tree

2 files changed

+28
-19
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+28
-19
lines changed

‎lib/matplotlib/backends/backend_pgf.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_pgf.py
+11-8Lines changed: 11 additions & 8 deletions
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.
@@ -358,14 +359,16 @@ def _get_box_metrics(self, tex):
358359
try:
359360
answer = self._expect_prompt()
360361
except LatexError as err:
361-
raise ValueError("Error measuring {!r}\nLaTeX Output:\n{}"
362+
# Here and below, use '{}' instead of {!r} to avoid doubling all
363+
# backslashes.
364+
raise ValueError("Error measuring {}\nLaTeX Output:\n{}"
362365
.format(tex, err.latex_output)) from err
363366
try:
364367
# Parse metrics from the answer string. Last line is prompt, and
365368
# next-to-last-line is blank line from \typeout.
366369
width, height, offset = answer.splitlines()[-3].split(",")
367370
except Exception as err:
368-
raise ValueError("Error measuring {!r}\nLaTeX Output:\n{}"
371+
raise ValueError("Error measuring {}\nLaTeX Output:\n{}"
369372
.format(tex, answer)) from err
370373
w, h, o = float(width[:-2]), float(height[:-2]), float(offset[:-2])
371374
# The height returned from LaTeX goes from base to top;
@@ -864,8 +867,8 @@ def print_pdf(self, fname_or_fh, *, metadata=None, **kwargs):
864867
r"\documentclass[12pt]{minimal}",
865868
r"\usepackage[papersize={%fin,%fin}, margin=0in]{geometry}"
866869
% (w, h),
867-
_get_preamble(),
868870
r"\usepackage{pgf}",
871+
_get_preamble(),
869872
r"\begin{document}",
870873
r"\centering",
871874
r"\input{figure.pgf}",
@@ -975,8 +978,8 @@ def _write_header(self, width_inches, height_inches):
975978
r"\documentclass[12pt]{minimal}",
976979
r"\usepackage[papersize={%fin,%fin}, margin=0in]{geometry}"
977980
% (width_inches, height_inches),
978-
_get_preamble(),
979981
r"\usepackage{pgf}",
982+
_get_preamble(),
980983
r"\setlength{\parindent}{0pt}",
981984
r"\begin{document}%",
982985
])

‎lib/matplotlib/tests/test_backend_pgf.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_backend_pgf.py
+17-11Lines changed: 17 additions & 11 deletions
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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.