diff --git a/lib/matplotlib/dviread.py b/lib/matplotlib/dviread.py index 7f90a13f1086..4c2adabd1bb1 100644 --- a/lib/matplotlib/dviread.py +++ b/lib/matplotlib/dviread.py @@ -291,40 +291,11 @@ def _read(self): Read one page from the file. Return True if successful, False if there were no more pages. """ - # Pages appear to start with the sequence - # bop (begin of page) - # xxx comment - # # if using chemformula - # down - # push - # down - # # if using xcolor - # down - # push - # down (possibly multiple) - # push <= here, v is the baseline position. - # etc. - # (dviasm is useful to explore this structure.) - # Thus, we use the vertical position at the first time the stack depth - # reaches 3, while at least three "downs" have been executed (excluding - # those popped out (corresponding to the chemformula preamble)), as the - # baseline (the "down" count is necessary to handle xcolor). - down_stack = [0] self._baseline_v = None while True: byte = self.file.read(1)[0] self._dtable[byte](self, byte) name = self._dtable[byte].__name__ - if name == "_push": - down_stack.append(down_stack[-1]) - elif name == "_pop": - down_stack.pop() - elif name == "_down": - down_stack[-1] += 1 - if (self._baseline_v is None - and len(getattr(self, "stack", [])) == 3 - and down_stack[-1] >= 4): - self._baseline_v = self.v if byte == 140: # end of page return True if self.state is _dvistate.post_post: # end of file @@ -457,6 +428,8 @@ def _fnt_num(self, new_f): @_dispatch(min=239, max=242, args=('ulen1',)) def _xxx(self, datalen): special = self.file.read(datalen) + if special == b'matplotlibbaselinemarker': + self._baseline_v = self.v _log.debug( 'Dvi._xxx: encountered special: %s', ''.join([chr(ch) if 32 <= ch < 127 else '<%02x>' % ch diff --git a/lib/matplotlib/tests/test_text.py b/lib/matplotlib/tests/test_text.py index 0b703adadc25..70210c9d84fd 100644 --- a/lib/matplotlib/tests/test_text.py +++ b/lib/matplotlib/tests/test_text.py @@ -776,26 +776,11 @@ def test_metrics_cache(): fig = plt.figure() fig.text(.3, .5, "foo\nbar") + fig.text(.5, .5, "foo\nbar") fig.text(.3, .5, "foo\nbar", usetex=True) fig.text(.5, .5, "foo\nbar", usetex=True) fig.canvas.draw() - renderer = fig._cachedRenderer - ys = {} # mapping of strings to where they were drawn in y with draw_tex. - - def call(*args, **kwargs): - renderer, x, y, s, *_ = args - ys.setdefault(s, set()).add(y) - - renderer.draw_tex = call - fig.canvas.draw() - assert [*ys] == ["foo", "bar"] - # Check that both TeX strings were drawn with the same y-position for both - # single-line substrings. Previously, there used to be an incorrect cache - # collision with the non-TeX string (drawn first here) whose metrics would - # get incorrectly reused by the first TeX string. - assert len(ys["foo"]) == len(ys["bar"]) == 1 info = mpl.text._get_text_metrics_with_cache_impl.cache_info() - # Every string gets a miss for the first layouting (extents), then a hit - # when drawing, but "foo\nbar" gets two hits as it's drawn twice. - assert info.hits > info.misses + # Each string gets drawn twice, so the second draw results in a hit. + assert info.hits == info.misses diff --git a/lib/matplotlib/texmanager.py b/lib/matplotlib/texmanager.py index 5f94c52df237..8a7f4834147a 100644 --- a/lib/matplotlib/texmanager.py +++ b/lib/matplotlib/texmanager.py @@ -161,10 +161,9 @@ def get_basefile(self, tex, fontsize, dpi=None): """ Return a filename based on a hash of the string, fontsize, and dpi. """ - s = ''.join([tex, self.get_font_config(), '%f' % fontsize, - self.get_custom_preamble(), str(dpi or '')]) + src = self._get_tex_source(tex, fontsize) + str(dpi) return os.path.join( - self.texcache, hashlib.md5(s.encode('utf-8')).hexdigest()) + self.texcache, hashlib.md5(src.encode('utf-8')).hexdigest()) def get_font_preamble(self): """ @@ -176,26 +175,44 @@ def get_custom_preamble(self): """Return a string containing user additions to the tex preamble.""" return rcParams['text.latex.preamble'] - def _get_preamble(self): + def _get_tex_source(self, tex, fontsize): + """Return the complete TeX source for processing a TeX string.""" + self.get_font_config() # Updates self._font_preamble. + baselineskip = 1.25 * fontsize + fontcmd = (r'\sffamily' if self._font_family == 'sans-serif' else + r'\ttfamily' if self._font_family == 'monospace' else + r'\rmfamily') return "\n".join([ r"\documentclass{article}", - # Pass-through \mathdefault, which is used in non-usetex mode to - # use the default text font but was historically suppressed in - # usetex mode. + r"% Pass-through \mathdefault, which is used in non-usetex mode", + r"% to use the default text font but was historically suppressed", + r"% in usetex mode.", r"\newcommand{\mathdefault}[1]{#1}", self._font_preamble, r"\usepackage[utf8]{inputenc}", r"\DeclareUnicodeCharacter{2212}{\ensuremath{-}}", - # geometry is loaded before the custom preamble as convert_psfrags - # relies on a custom preamble to change the geometry. + r"% geometry is loaded before the custom preamble as ", + r"% convert_psfrags relies on a custom preamble to change the ", + r"% geometry.", r"\usepackage[papersize=72in, margin=1in]{geometry}", self.get_custom_preamble(), - # Use `underscore` package to take care of underscores in text - # The [strings] option allows to use underscores in file names + r"% Use `underscore` package to take care of underscores in text.", + r"% The [strings] option allows to use underscores in file names.", _usepackage_if_not_loaded("underscore", option="strings"), - # Custom packages (e.g. newtxtext) may already have loaded textcomp - # with different options. + r"% Custom packages (e.g. newtxtext) may already have loaded ", + r"% textcomp with different options.", _usepackage_if_not_loaded("textcomp"), + r"\pagestyle{empty}", + r"\begin{document}", + r"% The empty hbox ensures that a page is printed even for empty", + r"% inputs, except when using psfrag which gets confused by it.", + r"% matplotlibbaselinemarker is used by dviread to detect the", + r"% last line's baseline.", + rf"\fontsize{{{fontsize}}}{{{baselineskip}}}%", + r"\ifdefined\psfrag\else\hbox{}\fi%", + rf"{{\obeylines{fontcmd} {tex}}}%", + r"\special{matplotlibbaselinemarker}%", + r"\end{document}", ]) def make_tex(self, tex, fontsize): @@ -204,30 +221,8 @@ def make_tex(self, tex, fontsize): Return the file name. """ - basefile = self.get_basefile(tex, fontsize) - texfile = '%s.tex' % basefile - fontcmd = (r'\sffamily' if self._font_family == 'sans-serif' else - r'\ttfamily' if self._font_family == 'monospace' else - r'\rmfamily') - tex_template = r""" -%(preamble)s -\pagestyle{empty} -\begin{document} -%% The empty hbox ensures that a page is printed even for empty inputs, except -%% when using psfrag which gets confused by it. -\fontsize{%(fontsize)f}{%(baselineskip)f}%% -\ifdefined\psfrag\else\hbox{}\fi%% -{%(fontcmd)s %(tex)s} -\end{document} -""" - Path(texfile).write_text(tex_template % { - "preamble": self._get_preamble(), - "fontsize": fontsize, - "baselineskip": fontsize * 1.25, - "fontcmd": fontcmd, - "tex": tex, - }, encoding="utf-8") - + texfile = self.get_basefile(tex, fontsize) + ".tex" + Path(texfile).write_text(self._get_tex_source(tex, fontsize)) return texfile def _run_checked_subprocess(self, command, tex, *, cwd=None): diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py index a98b534d2ef6..f10f4bcfa8d1 100644 --- a/lib/matplotlib/text.py +++ b/lib/matplotlib/text.py @@ -295,7 +295,8 @@ def _get_layout(self, renderer): of a rotated text when necessary. """ thisx, thisy = 0.0, 0.0 - lines = self.get_text().split("\n") # Ensures lines is not empty. + text = self.get_text() + lines = [text] if self.get_usetex() else text.split("\n") # Not empty. ws = [] hs = [] pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy