From 2e351d150180eb4aaa1aa9244376c74c59ebe9e2 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Sun, 2 Oct 2022 22:09:58 +0200 Subject: [PATCH] Backport PR #24047: Revert #22360: Let TeX handle multiline strings itself --- lib/matplotlib/dates.py | 12 +++++- lib/matplotlib/dviread.py | 32 +++++++++++++- .../baseline_images/test_usetex/eqnarray.png | Bin 0 -> 1322 bytes lib/matplotlib/tests/test_dates.py | 40 +++++++++--------- lib/matplotlib/tests/test_text.py | 21 +++++++-- lib/matplotlib/tests/test_usetex.py | 15 +++++++ lib/matplotlib/texmanager.py | 3 +- lib/matplotlib/text.py | 3 +- 8 files changed, 97 insertions(+), 29 deletions(-) create mode 100644 lib/matplotlib/tests/baseline_images/test_usetex/eqnarray.png diff --git a/lib/matplotlib/dates.py b/lib/matplotlib/dates.py index ac1a7d03c687..672ea2c3b003 100644 --- a/lib/matplotlib/dates.py +++ b/lib/matplotlib/dates.py @@ -173,6 +173,7 @@ import functools import logging import math +import re from dateutil.rrule import (rrule, MO, TU, WE, TH, FR, SA, SU, YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, @@ -597,7 +598,16 @@ def drange(dstart, dend, delta): def _wrap_in_tex(text): - return r"{\fontfamily{\familydefault}\selectfont " + text + "}" + p = r'([a-zA-Z]+)' + ret_text = re.sub(p, r'}$\1$\\mathdefault{', text) + + # Braces ensure symbols are not spaced like binary operators. + ret_text = ret_text.replace('-', '{-}').replace(':', '{:}') + # To not concatenate space between numbers. + ret_text = ret_text.replace(' ', r'\;') + ret_text = '$\\mathdefault{' + ret_text + '}$' + ret_text = ret_text.replace('$\\mathdefault{}$', '') + return ret_text ## date tickers and formatters ### diff --git a/lib/matplotlib/dviread.py b/lib/matplotlib/dviread.py index 8c83d8d6c508..296e67c4d5ff 100644 --- a/lib/matplotlib/dviread.py +++ b/lib/matplotlib/dviread.py @@ -352,10 +352,40 @@ 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 @@ -488,8 +518,6 @@ 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/baseline_images/test_usetex/eqnarray.png b/lib/matplotlib/tests/baseline_images/test_usetex/eqnarray.png new file mode 100644 index 0000000000000000000000000000000000000000..249f15d238ddacf216965801675667d6edb85da3 GIT binary patch literal 1322 zcmeAS@N?(olHy`uVBq!ia0vp^DImv~(l@_;SZF8i$S^J$mm}g-zS*UD3z4Tw8JdHArUurkeZhk1ec^Up8?SV6on+(UlN+ZFyYP4w^=jzVwKBY? zT>pOjC>S~I;nSzAd*jx>D(mA~I^kyx-&)gOf9-E|{rE$bd8wwe zeRZav{$K0CXVEAa)UtYN5XWJG$Z2oqOisO-pFNv#XaBk6#yAfFmJK@GQQ2E==lZBk zUMsyTLZ_gp&65Y~Jgof^CXZf+YHnhsK<8yukgjR(|ot6?ddpUVjaY zRfcQT&*nXU`0N?mfhrNn$f^@@*Q5 zmb~FUv(ZV{C428wBb{w!)qCS457p_PE}XOW|E62&Qzf$$-z(3be3E7JO`fe$yxVf$ zpJ2LX9+w>}XTS6!cXSIyry4Q;? zzYJI}J^KQ;rTL2GYl>Y6r2GK(r{{E4*ykMJuv|Vt#zF7N9jBrLoNwCa1bgr)$SdCG zJ(>G&-r3qR&QrA$-?u$!_-gh(!QjBtr>c`w93|S0@9VLD{Nw#@%Q+7Vc6|M|{r2tK ze;z(mJpcLY>S;2^HM0w4-q#jixP3eN=jWoGcYdAW_{;G#%VXVwi0Up8<=;^*+D~>X z?)2^2V7i;9A>zb+j+2bplCzbY|As!@;`GnWLyyNfW9{kWM2Ts^a@tq!JMR;obb1qy zqrigOZ<9=<60PPYna#dv-?UJysPBSb info.misses diff --git a/lib/matplotlib/tests/test_usetex.py b/lib/matplotlib/tests/test_usetex.py index 22309afdaf97..0f01ebaffb56 100644 --- a/lib/matplotlib/tests/test_usetex.py +++ b/lib/matplotlib/tests/test_usetex.py @@ -64,6 +64,21 @@ def test_mathdefault(): fig.canvas.draw() +@image_comparison(['eqnarray.png']) +def test_multiline_eqnarray(): + text = ( + r'\begin{eqnarray*}' + r'foo\\' + r'bar\\' + r'baz\\' + r'\end{eqnarray*}' + ) + + fig = plt.figure(figsize=(1, 1)) + fig.text(0.5, 0.5, text, usetex=True, + horizontalalignment='center', verticalalignment='center') + + @pytest.mark.parametrize("fontsize", [8, 10, 12]) def test_minus_no_descent(fontsize): # Test special-casing of minus descent in DviFont._height_depth_of, by diff --git a/lib/matplotlib/texmanager.py b/lib/matplotlib/texmanager.py index edac2fec9c79..f53377cf5597 100644 --- a/lib/matplotlib/texmanager.py +++ b/lib/matplotlib/texmanager.py @@ -230,8 +230,7 @@ def _get_tex_source(cls, tex, fontsize): r"% last line's baseline.", rf"\fontsize{{{fontsize}}}{{{baselineskip}}}%", r"\ifdefined\psfrag\else\hbox{}\fi%", - rf"{{\obeylines{fontcmd} {tex}}}%", - r"\special{matplotlibbaselinemarker}%", + rf"{{{fontcmd} {tex}}}%", r"\end{document}", ]) diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py index 3c998d540c48..ccf6ce879b2a 100644 --- a/lib/matplotlib/text.py +++ b/lib/matplotlib/text.py @@ -302,8 +302,7 @@ def _get_layout(self, renderer): of a rotated text when necessary. """ thisx, thisy = 0.0, 0.0 - text = self.get_text() - lines = [text] if self.get_usetex() else text.split("\n") # Not empty. + lines = self.get_text().split("\n") # Ensures lines is not empty. ws = [] hs = []