Skip to content

Commit b9cea08

Browse files
committed
Don't fallback to agg in tight_layout.get_renderer.
tight_layout.get_renderer currently falls back to agg if it is unable to get a renderer for the current canvas, but we actually have a way to coerce the canvas to produce a renderer, via backend_bases._get_renderer. This renderer gets used by the tight_layout machinery to estimate text size. Falling back to Agg would produce bad results in the (admittedly rare) cases where text size estimation yields very different results depending on the renderer -- the easiest way to trigger this is to use a "thin" font, e.g. Tex Gyre Chorus, while using the pdf backend with the "pdf.use14corefonts" rcParam set to True, which effectively forces Helvetica. e.g. ``` from pylab import * matplotlib.use("pdf") rcdefaults() rcParams.update({"pdf.use14corefonts": True, "font.family": "TeX Gyre Chorus"}) plot() margins(0) tight_layout(0) savefig("/tmp/test.pdf") ``` would crop the tick labels before this PR. (The real point is not to fix an obscure edge case, but actually to have fewer places that fall back to Agg as "favorited" backend.)
1 parent 49593b7 commit b9cea08

File tree

3 files changed

+26
-13
lines changed

3 files changed

+26
-13
lines changed

lib/matplotlib/backend_bases.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1501,7 +1501,7 @@ def __init__(self, name, canvas, key, x=0, y=0, guiEvent=None):
15011501
self.key = key
15021502

15031503

1504-
def _get_renderer(figure, print_method, *, draw_disabled=False):
1504+
def _get_renderer(figure, print_method=None, *, draw_disabled=False):
15051505
"""
15061506
Get the renderer that would be used to save a `~.Figure`, and cache it on
15071507
the figure.
@@ -1520,8 +1520,11 @@ class Done(Exception):
15201520
def _draw(renderer): raise Done(renderer)
15211521

15221522
with cbook._setattr_cm(figure, draw=_draw):
1523+
if print_method is None:
1524+
fmt = figure.canvas.get_default_filetype()
1525+
print_method = getattr(figure.canvas, f"print_{fmt}")
15231526
try:
1524-
print_method(io.BytesIO())
1527+
print_method(io.BytesIO(), dpi=figure.dpi)
15251528
except Done as exc:
15261529
renderer, = figure._cachedRenderer, = exc.args
15271530

@@ -2089,7 +2092,7 @@ def print_figure(
20892092
renderer = _get_renderer(
20902093
self.figure,
20912094
functools.partial(
2092-
print_method, dpi=dpi, orientation=orientation),
2095+
print_method, orientation=orientation),
20932096
draw_disabled=True)
20942097
self.figure.draw(renderer)
20952098
bbox_inches = self.figure.get_tightbbox(

lib/matplotlib/tests/test_tightlayout.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,3 +310,18 @@ def test_suptitle():
310310
t = ax.set_title("bar")
311311
fig.canvas.draw()
312312
assert st.get_window_extent().y0 > t.get_window_extent().y1
313+
314+
315+
@pytest.mark.backend("pdf")
316+
def test_non_agg_renderer(monkeypatch, recwarn):
317+
unpatched_init = mpl.backend_bases.RendererBase.__init__
318+
319+
def __init__(self, *args, **kwargs):
320+
# Check that we don't instantiate any other renderer than a pdf
321+
# renderer to perform pdf tight layout.
322+
assert isinstance(self, mpl.backends.backend_pdf.RendererPdf)
323+
unpatched_init(self, *args, **kwargs)
324+
325+
monkeypatch.setattr(mpl.backend_bases.RendererBase, "__init__", __init__)
326+
fig, ax = plt.subplots()
327+
fig.tight_layout()

lib/matplotlib/tight_layout.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -166,19 +166,14 @@ def auto_adjust_subplotpars(
166166

167167
def get_renderer(fig):
168168
if fig._cachedRenderer:
169-
renderer = fig._cachedRenderer
169+
return fig._cachedRenderer
170170
else:
171171
canvas = fig.canvas
172-
173172
if canvas and hasattr(canvas, "get_renderer"):
174-
renderer = canvas.get_renderer()
175-
else: # Some noninteractive backends have no renderer until draw time.
176-
cbook._warn_external("tight_layout: falling back to Agg renderer")
177-
from matplotlib.backends.backend_agg import FigureCanvasAgg
178-
canvas = FigureCanvasAgg(fig)
179-
renderer = canvas.get_renderer()
180-
181-
return renderer
173+
return canvas.get_renderer()
174+
else:
175+
from . import backend_bases
176+
return backend_bases._get_renderer(fig, draw_disabled=True)
182177

183178

184179
def get_subplotspec_list(axes_list, grid_spec=None):

0 commit comments

Comments
 (0)