Skip to content

Fix baseline alignment when using usetex. #16476

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
merged 3 commits into from
Apr 14, 2020
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
3 changes: 1 addition & 2 deletions lib/matplotlib/backends/backend_ps.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,8 +484,7 @@ def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None):
r'\psfrag{%s}[Bl][Bl][1][%f]{\fontsize{%f}{%f}%s}' % (
thetext, angle, fontsize, fontsize*1.25, tex))
else:
# Stick to the bottom alignment, but this may give incorrect
# baseline some times.
# Stick to the bottom alignment.
pos = _nums_to_str(x-corr, y-bl)
self.psfrag.append(
r'\psfrag{%s}[bl][bl][1][%f]{\fontsize{%f}{%f}%s}' % (
Expand Down
21 changes: 21 additions & 0 deletions lib/matplotlib/dviread.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,12 @@ def _output(self):
maxx = max(maxx, x + w)
maxy = max(maxy, y + e)
maxy_pure = max(maxy_pure, y)
if self._baseline_v is not None:
maxy_pure = self._baseline_v # This should normally be the case.
self._baseline_v = None

if not self.text and not self.boxes: # Avoid infs/nans from inf+/-inf.
return Page(text=[], boxes=[], width=0, height=0, descent=0)

if self.dpi is None:
# special case for ease of debugging: output raw dvi coordinates
Expand Down Expand Up @@ -301,9 +307,24 @@ 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
# down
# push
# down, down
# push
# down (possibly multiple)
# push <= here, v is the baseline position.
# etc.
# (dviasm is useful to explore this structure.)
self._baseline_v = None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want to put this in __init__ as well? Are we sure that _output will never be called without _read being called first?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we'd already have errored out earlier anyways, because self.text wouldn't have been defined either.

while True:
byte = self.file.read(1)[0]
self._dtable[byte](self, byte)
if (self._baseline_v is None
and len(getattr(self, "stack", [])) == 3):
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
Binary file not shown.
Binary file modified lib/matplotlib/tests/baseline_images/test_usetex/test_usetex.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 23 additions & 11 deletions lib/matplotlib/tests/test_usetex.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import platform

import numpy as np
import pytest

Expand All @@ -12,24 +10,38 @@
pytestmark = pytest.mark.skip('Missing TeX of Ghostscript or dvipng')


@image_comparison(baseline_images=['test_usetex'],
extensions=['pdf', 'png'],
tol={'aarch64': 2.868, 'x86_64': 2.868}.get(
platform.machine(), 0.3
))
@image_comparison(
baseline_images=['test_usetex'],
extensions=['pdf', 'png'],
style="mpl20")
def test_usetex():
mpl.rcParams['text.usetex'] = True
fig = plt.figure()
ax = fig.add_subplot(111)
ax.text(0.1, 0.2,
kwargs = {"verticalalignment": "baseline", "size": 24,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is significantly better 👍

"bbox": dict(pad=0, edgecolor="k", facecolor="none")}
ax.text(0.2, 0.7,
# the \LaTeX macro exercises character sizing and placement,
# \left[ ... \right\} draw some variable-height characters,
# \sqrt and \frac draw horizontal rules, \mathrm changes the font
r'\LaTeX\ $\left[\int\limits_e^{2e}'
r'\sqrt\frac{\log^3 x}{x}\,\mathrm{d}x \right\}$',
fontsize=24)
ax.set_xticks([])
ax.set_yticks([])
**kwargs)
ax.text(0.2, 0.3, "lg", **kwargs)
ax.text(0.4, 0.3, r"$\frac{1}{2}\pi$", **kwargs)
ax.text(0.6, 0.3, "$p^{3^A}$", **kwargs)
ax.text(0.8, 0.3, "$p_{3_2}$", **kwargs)
for x in {t.get_position()[0] for t in ax.texts}:
ax.axvline(x)
for y in {t.get_position()[1] for t in ax.texts}:
ax.axhline(y)
ax.set_axis_off()


@check_figures_equal()
def test_empty(fig_test, fig_ref):
mpl.rcParams['text.usetex'] = True
fig_test.text(.5, .5, "% a comment")


@check_figures_equal()
Expand Down
18 changes: 13 additions & 5 deletions lib/matplotlib/texmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ def make_tex(self, tex, fontsize):
\usepackage[papersize={72in,72in},body={70in,70in},margin={1in,1in}]{geometry}
\pagestyle{empty}
\begin{document}
\fontsize{%f}{%f}%s
%% The empty hbox ensures that a page is printed even for empty inputs.
\fontsize{%f}{%f}\hbox{}%s
\end{document}
""" % (self._get_preamble(), fontsize, fontsize * 1.25, fontcmd % tex),
encoding='utf-8')
Expand Down Expand Up @@ -350,9 +351,16 @@ def make_png(self, tex, fontsize, dpi):
# see get_rgba for a discussion of the background
if not os.path.exists(pngfile):
dvifile = self.make_dvi(tex, fontsize)
self._run_checked_subprocess(
["dvipng", "-bg", "Transparent", "-D", str(dpi),
"-T", "tight", "-o", pngfile, dvifile], tex)
cmd = ["dvipng", "-bg", "Transparent", "-D", str(dpi),
"-T", "tight", "-o", pngfile, dvifile]
# When testing, disable FreeType rendering for reproducibility; but
# dvipng 1.16 has a bug (fixed in f3ff241) that breaks --freetype0
# mode, so for it we keep FreeType enabled; the image will be
# slightly off.
if (getattr(mpl, "_called_from_pytest", False)
and mpl._get_executable_info("dvipng").version != "1.16"):
cmd.insert(1, "--freetype0")
self._run_checked_subprocess(cmd, tex)
return pngfile

def get_grey(self, tex, fontsize=None, dpi=None):
Expand Down Expand Up @@ -403,7 +411,7 @@ def get_text_width_height_descent(self, tex, fontsize, renderer=None):
return width, height + depth, depth

else:
# use dviread. It sometimes returns a wrong descent.
# use dviread.
dvifile = self.make_dvi(tex, fontsize)
with dviread.Dvi(dvifile, 72 * dpi_fraction) as dvi:
page, = dvi
Expand Down