From 25a3f0a100a31ccd9e740c6416eaff5e36c1b21a Mon Sep 17 00:00:00 2001 From: Casey Brooks Date: Thu, 25 Dec 2025 05:24:51 +0000 Subject: [PATCH] fix(pickle): preserve original dpi on reload --- lib/matplotlib/figure.py | 8 ++++++++ lib/matplotlib/tests/test_pickle.py | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index c55864243a75..de4237b87a05 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -3043,6 +3043,12 @@ def __setstate__(self, state): self.__dict__ = state + # Preserve the original DPI that may differ from the figure DPI on + # HiDPI backends. FigureCanvasBase will overwrite ``_original_dpi`` + # when it reattaches the canvas, so capture it first and restore it + # afterwards. + original_dpi = getattr(self, "_original_dpi", self.dpi) + # re-initialise some of the unstored state information FigureCanvasBase(self) # Set self.canvas. @@ -3056,6 +3062,8 @@ def __setstate__(self, state): pylab_helpers.Gcf._set_new_active_manager(mgr) plt.draw_if_interactive() + self._original_dpi = original_dpi + self.stale = True def add_axobserver(self, func): diff --git a/lib/matplotlib/tests/test_pickle.py b/lib/matplotlib/tests/test_pickle.py index 0d0a5e597767..60bb35248c6f 100644 --- a/lib/matplotlib/tests/test_pickle.py +++ b/lib/matplotlib/tests/test_pickle.py @@ -217,6 +217,25 @@ def test_unpickle_canvas(): assert fig2.canvas is not None +def test_hidpi_pickle_unpickle_does_not_double_dpi(): + fig = plt.figure(dpi=100) + + fig.canvas._set_device_pixel_ratio(2) + assert fig.dpi == pytest.approx(200) + assert getattr(fig, "_original_dpi", None) == pytest.approx(100) + + buffer = BytesIO() + pickle.dump(fig, buffer, pickle.HIGHEST_PROTOCOL) + buffer.seek(0) + + restored = pickle.load(buffer) + assert restored.dpi == pytest.approx(200) + + restored.canvas._set_device_pixel_ratio(2) + assert restored.dpi == pytest.approx(200) + assert getattr(restored, "_original_dpi", None) == pytest.approx(100) + + def test_mpl_toolkits(): ax = parasite_axes.host_axes([0, 0, 1, 1]) assert type(pickle.loads(pickle.dumps(ax))) == parasite_axes.HostAxes