diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index fda7bd1c9613..12208d08ef00 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -2259,6 +2259,7 @@ def print_figure( # Remove the figure manager, if any, to avoid resizing the GUI widget. with cbook._setattr_cm(self, manager=None), \ cbook._setattr_cm(self.figure, dpi=dpi), \ + cbook._setattr_cm(canvas, _device_pixel_ratio=1), \ cbook._setattr_cm(canvas, _is_saving=True), \ ExitStack() as stack: diff --git a/lib/matplotlib/backends/backend_cairo.py b/lib/matplotlib/backends/backend_cairo.py index 61cd66ce2e31..05a760542f4f 100644 --- a/lib/matplotlib/backends/backend_cairo.py +++ b/lib/matplotlib/backends/backend_cairo.py @@ -245,7 +245,7 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): ctx.save() ctx.select_font_face(*_cairo_font_args_from_font_prop(prop)) - ctx.set_font_size(prop.get_size_in_points() * self.dpi / 72) + ctx.set_font_size(self.points_to_pixels(prop.get_size_in_points())) opts = cairo.FontOptions() opts.set_antialias( cairo.ANTIALIAS_DEFAULT if mpl.rcParams["text.antialiased"] @@ -271,7 +271,7 @@ def _draw_mathtext(self, gc, x, y, s, prop, angle): ctx.move_to(ox, -oy) ctx.select_font_face( *_cairo_font_args_from_font_prop(ttfFontProperty(font))) - ctx.set_font_size(fontsize * self.dpi / 72) + ctx.set_font_size(self.points_to_pixels(fontsize)) ctx.show_text(chr(idx)) for ox, oy, w, h in rects: @@ -303,9 +303,7 @@ def get_text_width_height_descent(self, s, prop, ismath): # save/restore prevents the problem ctx.save() ctx.select_font_face(*_cairo_font_args_from_font_prop(prop)) - # Cairo (says it) uses 1/96 inch user space units, ref: cairo_gstate.c - # but if /96.0 is used the font is too small - ctx.set_font_size(prop.get_size_in_points() * self.dpi / 72) + ctx.set_font_size(self.points_to_pixels(prop.get_size_in_points())) y_bearing, w, h = ctx.text_extents(s)[1:4] ctx.restore() diff --git a/lib/matplotlib/backends/backend_gtk3cairo.py b/lib/matplotlib/backends/backend_gtk3cairo.py index af290902d8a6..9759e15adac8 100644 --- a/lib/matplotlib/backends/backend_gtk3cairo.py +++ b/lib/matplotlib/backends/backend_gtk3cairo.py @@ -28,6 +28,7 @@ def on_draw_event(self, widget, ctx): allocation.width, allocation.height) self._renderer.set_width_height( allocation.width, allocation.height) + self._renderer.dpi = self.figure.dpi self.figure.draw(self._renderer) diff --git a/lib/matplotlib/backends/backend_gtk4cairo.py b/lib/matplotlib/backends/backend_gtk4cairo.py index 391a1a372856..53d319392727 100644 --- a/lib/matplotlib/backends/backend_gtk4cairo.py +++ b/lib/matplotlib/backends/backend_gtk4cairo.py @@ -27,6 +27,7 @@ def on_draw_event(self, widget, ctx): allocation.width, allocation.height) self._renderer.set_width_height( allocation.width, allocation.height) + self._renderer.dpi = self.figure.dpi self.figure.draw(self._renderer) diff --git a/lib/matplotlib/backends/backend_qtcairo.py b/lib/matplotlib/backends/backend_qtcairo.py index 6d0a90b985b4..9619f4fdb686 100644 --- a/lib/matplotlib/backends/backend_qtcairo.py +++ b/lib/matplotlib/backends/backend_qtcairo.py @@ -13,6 +13,7 @@ def __init__(self, figure=None): def draw(self): if hasattr(self._renderer.gc, "ctx"): + self._renderer.dpi = self.figure.dpi self.figure.draw(self._renderer) super().draw() @@ -23,6 +24,7 @@ def paintEvent(self, event): surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) self._renderer.set_ctx_from_surface(surface) self._renderer.set_width_height(width, height) + self._renderer.dpi = self.figure.dpi self.figure.draw(self._renderer) buf = self._renderer.gc.ctx.get_target().get_data() if QT_API == "PyQt6": diff --git a/lib/matplotlib/backends/backend_tkcairo.py b/lib/matplotlib/backends/backend_tkcairo.py index a81fd0d92bb8..b4099db24828 100644 --- a/lib/matplotlib/backends/backend_tkcairo.py +++ b/lib/matplotlib/backends/backend_tkcairo.py @@ -18,6 +18,7 @@ def draw(self): surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) self._renderer.set_ctx_from_surface(surface) self._renderer.set_width_height(width, height) + self._renderer.dpi = self.figure.dpi self.figure.draw(self._renderer) buf = np.reshape(surface.get_data(), (height, width, 4)) _backend_tk.blit( diff --git a/lib/matplotlib/backends/backend_wxcairo.py b/lib/matplotlib/backends/backend_wxcairo.py index 6cb0b9d68414..230ebcf527d1 100644 --- a/lib/matplotlib/backends/backend_wxcairo.py +++ b/lib/matplotlib/backends/backend_wxcairo.py @@ -35,6 +35,7 @@ def draw(self, drawDC=None): surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) self._renderer.set_ctx_from_surface(surface) self._renderer.set_width_height(width, height) + self._renderer.dpi = self.figure.dpi self.figure.draw(self._renderer) self.bitmap = wxcairo.BitmapFromImageSurface(surface) self._isDrawn = True diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index 317528879304..bc2256ce3c54 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -6,6 +6,10 @@ from types import SimpleNamespace import warnings +import numpy as np +import pytest +from PIL import Image + import matplotlib as mpl from matplotlib import cbook, rcParams from matplotlib._api.deprecation import MatplotlibDeprecationWarning @@ -16,8 +20,6 @@ import matplotlib.pyplot as plt import matplotlib.dates as mdates import matplotlib.gridspec as gridspec -import numpy as np -import pytest @image_comparison(['figure_align_labels'], extensions=['png', 'svg'], @@ -496,6 +498,29 @@ def test_savefig_backend(): fig.savefig("test.png", backend="pdf") +@pytest.mark.parametrize('backend', [ + pytest.param('Agg', marks=[pytest.mark.backend('Agg')]), + pytest.param('Cairo', marks=[pytest.mark.backend('Cairo')]), +]) +def test_savefig_pixel_ratio(backend): + fig, ax = plt.subplots() + ax.plot([1, 2, 3]) + with io.BytesIO() as buf: + fig.savefig(buf, format='png') + ratio1 = Image.open(buf) + ratio1.load() + + fig, ax = plt.subplots() + ax.plot([1, 2, 3]) + fig.canvas._set_device_pixel_ratio(2) + with io.BytesIO() as buf: + fig.savefig(buf, format='png') + ratio2 = Image.open(buf) + ratio2.load() + + assert ratio1 == ratio2 + + def test_figure_repr(): fig = plt.figure(figsize=(10, 20), dpi=10) assert repr(fig) == "
"