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 2fe38b5

Browse filesBrowse files
authored
Merge pull request #22360 from anntzer/multilinetex
Let TeX handle multiline strings itself.
2 parents 68d2dc2 + 3aaf81b commit 2fe38b5
Copy full SHA for 2fe38b5

File tree

Expand file treeCollapse file tree

4 files changed

+39
-85
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+39
-85
lines changed

‎lib/matplotlib/dviread.py

Copy file name to clipboardExpand all lines: lib/matplotlib/dviread.py
+2-29Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -291,40 +291,11 @@ def _read(self):
291291
Read one page from the file. Return True if successful,
292292
False if there were no more pages.
293293
"""
294-
# Pages appear to start with the sequence
295-
# bop (begin of page)
296-
# xxx comment
297-
# <push, ..., pop> # if using chemformula
298-
# down
299-
# push
300-
# down
301-
# <push, push, xxx, right, xxx, pop, pop> # if using xcolor
302-
# down
303-
# push
304-
# down (possibly multiple)
305-
# push <= here, v is the baseline position.
306-
# etc.
307-
# (dviasm is useful to explore this structure.)
308-
# Thus, we use the vertical position at the first time the stack depth
309-
# reaches 3, while at least three "downs" have been executed (excluding
310-
# those popped out (corresponding to the chemformula preamble)), as the
311-
# baseline (the "down" count is necessary to handle xcolor).
312-
down_stack = [0]
313294
self._baseline_v = None
314295
while True:
315296
byte = self.file.read(1)[0]
316297
self._dtable[byte](self, byte)
317298
name = self._dtable[byte].__name__
318-
if name == "_push":
319-
down_stack.append(down_stack[-1])
320-
elif name == "_pop":
321-
down_stack.pop()
322-
elif name == "_down":
323-
down_stack[-1] += 1
324-
if (self._baseline_v is None
325-
and len(getattr(self, "stack", [])) == 3
326-
and down_stack[-1] >= 4):
327-
self._baseline_v = self.v
328299
if byte == 140: # end of page
329300
return True
330301
if self.state is _dvistate.post_post: # end of file
@@ -457,6 +428,8 @@ def _fnt_num(self, new_f):
457428
@_dispatch(min=239, max=242, args=('ulen1',))
458429
def _xxx(self, datalen):
459430
special = self.file.read(datalen)
431+
if special == b'matplotlibbaselinemarker':
432+
self._baseline_v = self.v
460433
_log.debug(
461434
'Dvi._xxx: encountered special: %s',
462435
''.join([chr(ch) if 32 <= ch < 127 else '<%02x>' % ch

‎lib/matplotlib/tests/test_text.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_text.py
+3-18Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -790,26 +790,11 @@ def test_metrics_cache():
790790

791791
fig = plt.figure()
792792
fig.text(.3, .5, "foo\nbar")
793+
fig.text(.5, .5, "foo\nbar")
793794
fig.text(.3, .5, "foo\nbar", usetex=True)
794795
fig.text(.5, .5, "foo\nbar", usetex=True)
795796
fig.canvas.draw()
796-
renderer = fig._cachedRenderer
797-
ys = {} # mapping of strings to where they were drawn in y with draw_tex.
798-
799-
def call(*args, **kwargs):
800-
renderer, x, y, s, *_ = args
801-
ys.setdefault(s, set()).add(y)
802-
803-
renderer.draw_tex = call
804-
fig.canvas.draw()
805-
assert [*ys] == ["foo", "bar"]
806-
# Check that both TeX strings were drawn with the same y-position for both
807-
# single-line substrings. Previously, there used to be an incorrect cache
808-
# collision with the non-TeX string (drawn first here) whose metrics would
809-
# get incorrectly reused by the first TeX string.
810-
assert len(ys["foo"]) == len(ys["bar"]) == 1
811797

812798
info = mpl.text._get_text_metrics_with_cache_impl.cache_info()
813-
# Every string gets a miss for the first layouting (extents), then a hit
814-
# when drawing, but "foo\nbar" gets two hits as it's drawn twice.
815-
assert info.hits > info.misses
799+
# Each string gets drawn twice, so the second draw results in a hit.
800+
assert info.hits == info.misses

‎lib/matplotlib/texmanager.py

Copy file name to clipboardExpand all lines: lib/matplotlib/texmanager.py
+32-37Lines changed: 32 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,9 @@ def get_basefile(self, tex, fontsize, dpi=None):
161161
"""
162162
Return a filename based on a hash of the string, fontsize, and dpi.
163163
"""
164-
s = ''.join([tex, self.get_font_config(), '%f' % fontsize,
165-
self.get_custom_preamble(), str(dpi or '')])
164+
src = self._get_tex_source(tex, fontsize) + str(dpi)
166165
return os.path.join(
167-
self.texcache, hashlib.md5(s.encode('utf-8')).hexdigest())
166+
self.texcache, hashlib.md5(src.encode('utf-8')).hexdigest())
168167

169168
def get_font_preamble(self):
170169
"""
@@ -176,26 +175,44 @@ def get_custom_preamble(self):
176175
"""Return a string containing user additions to the tex preamble."""
177176
return rcParams['text.latex.preamble']
178177

179-
def _get_preamble(self):
178+
def _get_tex_source(self, tex, fontsize):
179+
"""Return the complete TeX source for processing a TeX string."""
180+
self.get_font_config() # Updates self._font_preamble.
181+
baselineskip = 1.25 * fontsize
182+
fontcmd = (r'\sffamily' if self._font_family == 'sans-serif' else
183+
r'\ttfamily' if self._font_family == 'monospace' else
184+
r'\rmfamily')
180185
return "\n".join([
181186
r"\documentclass{article}",
182-
# Pass-through \mathdefault, which is used in non-usetex mode to
183-
# use the default text font but was historically suppressed in
184-
# usetex mode.
187+
r"% Pass-through \mathdefault, which is used in non-usetex mode",
188+
r"% to use the default text font but was historically suppressed",
189+
r"% in usetex mode.",
185190
r"\newcommand{\mathdefault}[1]{#1}",
186191
self._font_preamble,
187192
r"\usepackage[utf8]{inputenc}",
188193
r"\DeclareUnicodeCharacter{2212}{\ensuremath{-}}",
189-
# geometry is loaded before the custom preamble as convert_psfrags
190-
# relies on a custom preamble to change the geometry.
194+
r"% geometry is loaded before the custom preamble as ",
195+
r"% convert_psfrags relies on a custom preamble to change the ",
196+
r"% geometry.",
191197
r"\usepackage[papersize=72in, margin=1in]{geometry}",
192198
self.get_custom_preamble(),
193-
# Use `underscore` package to take care of underscores in text
194-
# The [strings] option allows to use underscores in file names
199+
r"% Use `underscore` package to take care of underscores in text.",
200+
r"% The [strings] option allows to use underscores in file names.",
195201
_usepackage_if_not_loaded("underscore", option="strings"),
196-
# Custom packages (e.g. newtxtext) may already have loaded textcomp
197-
# with different options.
202+
r"% Custom packages (e.g. newtxtext) may already have loaded ",
203+
r"% textcomp with different options.",
198204
_usepackage_if_not_loaded("textcomp"),
205+
r"\pagestyle{empty}",
206+
r"\begin{document}",
207+
r"% The empty hbox ensures that a page is printed even for empty",
208+
r"% inputs, except when using psfrag which gets confused by it.",
209+
r"% matplotlibbaselinemarker is used by dviread to detect the",
210+
r"% last line's baseline.",
211+
rf"\fontsize{{{fontsize}}}{{{baselineskip}}}%",
212+
r"\ifdefined\psfrag\else\hbox{}\fi%",
213+
rf"{{\obeylines{fontcmd} {tex}}}%",
214+
r"\special{matplotlibbaselinemarker}%",
215+
r"\end{document}",
199216
])
200217

201218
def make_tex(self, tex, fontsize):
@@ -204,30 +221,8 @@ def make_tex(self, tex, fontsize):
204221
205222
Return the file name.
206223
"""
207-
basefile = self.get_basefile(tex, fontsize)
208-
texfile = '%s.tex' % basefile
209-
fontcmd = (r'\sffamily' if self._font_family == 'sans-serif' else
210-
r'\ttfamily' if self._font_family == 'monospace' else
211-
r'\rmfamily')
212-
tex_template = r"""
213-
%(preamble)s
214-
\pagestyle{empty}
215-
\begin{document}
216-
%% The empty hbox ensures that a page is printed even for empty inputs, except
217-
%% when using psfrag which gets confused by it.
218-
\fontsize{%(fontsize)f}{%(baselineskip)f}%%
219-
\ifdefined\psfrag\else\hbox{}\fi%%
220-
{%(fontcmd)s %(tex)s}
221-
\end{document}
222-
"""
223-
Path(texfile).write_text(tex_template % {
224-
"preamble": self._get_preamble(),
225-
"fontsize": fontsize,
226-
"baselineskip": fontsize * 1.25,
227-
"fontcmd": fontcmd,
228-
"tex": tex,
229-
}, encoding="utf-8")
230-
224+
texfile = self.get_basefile(tex, fontsize) + ".tex"
225+
Path(texfile).write_text(self._get_tex_source(tex, fontsize))
231226
return texfile
232227

233228
def _run_checked_subprocess(self, command, tex, *, cwd=None):

‎lib/matplotlib/text.py

Copy file name to clipboardExpand all lines: lib/matplotlib/text.py
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,8 @@ def _get_layout(self, renderer):
296296
of a rotated text when necessary.
297297
"""
298298
thisx, thisy = 0.0, 0.0
299-
lines = self.get_text().split("\n") # Ensures lines is not empty.
299+
text = self.get_text()
300+
lines = [text] if self.get_usetex() else text.split("\n") # Not empty.
300301

301302
ws = []
302303
hs = []

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.