Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Backport PR #24047 on branch v3.6.x (Revert #22360: Let TeX handle multiline strings itself) #24078

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion lib/matplotlib/dates.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 ###
Expand Down
32 changes: 30 additions & 2 deletions lib/matplotlib/dviread.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
# <push, ..., pop> # if using chemformula
# down
# push
# down
# <push, push, xxx, right, xxx, pop, pop> # 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
Expand Down Expand Up @@ -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
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 21 additions & 19 deletions lib/matplotlib/tests/test_dates.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,13 +322,13 @@ def callable_formatting_function(dates, _):

@pytest.mark.parametrize('delta, expected', [
(datetime.timedelta(weeks=52 * 200),
range(1990, 2171, 20)),
[r'$\mathdefault{%d}$' % year for year in range(1990, 2171, 20)]),
(datetime.timedelta(days=30),
['1990-01-%02d' % day for day in range(1, 32, 3)]),
[r'$\mathdefault{1990{-}01{-}%02d}$' % day for day in range(1, 32, 3)]),
(datetime.timedelta(hours=20),
['01-01 %02d' % hour for hour in range(0, 21, 2)]),
[r'$\mathdefault{01{-}01\;%02d}$' % hour for hour in range(0, 21, 2)]),
(datetime.timedelta(minutes=10),
['01 00:%02d' % minu for minu in range(0, 11)]),
[r'$\mathdefault{01\;00{:}%02d}$' % minu for minu in range(0, 11)]),
])
def test_date_formatter_usetex(delta, expected):
style.use("default")
Expand All @@ -341,8 +341,7 @@ def test_date_formatter_usetex(delta, expected):
locator.axis.set_view_interval(mdates.date2num(d1), mdates.date2num(d2))

formatter = mdates.AutoDateFormatter(locator, usetex=True)
assert [formatter(loc) for loc in locator()] == [
r'{\fontfamily{\familydefault}\selectfont %s}' % s for s in expected]
assert [formatter(loc) for loc in locator()] == expected


def test_drange():
Expand Down Expand Up @@ -645,14 +644,24 @@ def test_offset_changes():

@pytest.mark.parametrize('t_delta, expected', [
(datetime.timedelta(weeks=52 * 200),
range(1980, 2201, 20)),
['$\\mathdefault{%d}$' % (t, ) for t in range(1980, 2201, 20)]),
(datetime.timedelta(days=40),
['Jan', '05', '09', '13', '17', '21', '25', '29', 'Feb', '05', '09']),
['Jan', '$\\mathdefault{05}$', '$\\mathdefault{09}$',
'$\\mathdefault{13}$', '$\\mathdefault{17}$', '$\\mathdefault{21}$',
'$\\mathdefault{25}$', '$\\mathdefault{29}$', 'Feb',
'$\\mathdefault{05}$', '$\\mathdefault{09}$']),
(datetime.timedelta(hours=40),
['Jan-01', '04:00', '08:00', '12:00', '16:00', '20:00',
'Jan-02', '04:00', '08:00', '12:00', '16:00']),
['Jan$\\mathdefault{{-}01}$', '$\\mathdefault{04{:}00}$',
'$\\mathdefault{08{:}00}$', '$\\mathdefault{12{:}00}$',
'$\\mathdefault{16{:}00}$', '$\\mathdefault{20{:}00}$',
'Jan$\\mathdefault{{-}02}$', '$\\mathdefault{04{:}00}$',
'$\\mathdefault{08{:}00}$', '$\\mathdefault{12{:}00}$',
'$\\mathdefault{16{:}00}$']),
(datetime.timedelta(seconds=2),
['59.5', '00:00', '00.5', '01.0', '01.5', '02.0', '02.5']),
['$\\mathdefault{59.5}$', '$\\mathdefault{00{:}00}$',
'$\\mathdefault{00.5}$', '$\\mathdefault{01.0}$',
'$\\mathdefault{01.5}$', '$\\mathdefault{02.0}$',
'$\\mathdefault{02.5}$']),
])
def test_concise_formatter_usetex(t_delta, expected):
d1 = datetime.datetime(1997, 1, 1)
Expand All @@ -663,8 +672,7 @@ def test_concise_formatter_usetex(t_delta, expected):
locator.axis.set_view_interval(mdates.date2num(d1), mdates.date2num(d2))

formatter = mdates.ConciseDateFormatter(locator, usetex=True)
assert formatter.format_ticks(locator()) == [
r'{\fontfamily{\familydefault}\selectfont %s}' % s for s in expected]
assert formatter.format_ticks(locator()) == expected


def test_concise_formatter_formats():
Expand Down Expand Up @@ -1347,12 +1355,6 @@ def test_date_ticker_factory(span, expected_locator):
assert isinstance(locator, expected_locator)


def test_usetex_newline():
fig, ax = plt.subplots()
ax.xaxis.set_major_formatter(mdates.DateFormatter('%d/%m\n%Y'))
fig.canvas.draw()


def test_datetime_masked():
# make sure that all-masked data falls back to the viewlim
# set in convert.axisinfo....
Expand Down
21 changes: 18 additions & 3 deletions lib/matplotlib/tests/test_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -806,11 +806,26 @@ 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._get_renderer()
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()
# Each string gets drawn twice, so the second draw results in a hit.
assert info.hits == info.misses
# 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
15 changes: 15 additions & 0 deletions lib/matplotlib/tests/test_usetex.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions lib/matplotlib/texmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}",
])

Expand Down
3 changes: 1 addition & 2 deletions lib/matplotlib/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand Down