From 33eb9b6965ef3145889e7eeef1f6358baf30f487 Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Fri, 13 Feb 2026 17:13:30 +1000 Subject: [PATCH] Fix double-counted margins for shared spanning labels at space=0 --- ultraplot/figure.py | 4 ++++ ultraplot/tests/test_subplots.py | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/ultraplot/figure.py b/ultraplot/figure.py index 344c8d9b4..784cbf5f4 100644 --- a/ultraplot/figure.py +++ b/ultraplot/figure.py @@ -2558,6 +2558,10 @@ def _update_axis_label(self, side, axs): props = ("ha", "va", "rotation", "rotation_mode") suplab = suplabs[ax] = self.text(0, 0, "") suplab.update({prop: getattr(axlab, "get_" + prop)() for prop in props}) + # Spanning labels are positioned manually while regular axis labels are + # replaced by space placeholders to reserve bbox space. Exclude these + # figure-level labels from tight layout to avoid double counting. + suplab.set_in_layout(False) # Copy text from the central label to the spanning label # NOTE: Must use spaces rather than newlines, otherwise tight layout diff --git a/ultraplot/tests/test_subplots.py b/ultraplot/tests/test_subplots.py index 0ecb74066..c4d7c6d96 100644 --- a/ultraplot/tests/test_subplots.py +++ b/ultraplot/tests/test_subplots.py @@ -277,6 +277,29 @@ def test_subset_share_xlabels_override(): uplt.close(fig) +def test_spanning_labels_excluded_from_tight_layout_bbox(): + """ + Spanning x/y labels should not be counted twice by tight layout. + + Regression test: with ``space=0``, including the figure-level spanning labels + in layout caused oversized left/bottom margins. + """ + fig, ax = uplt.subplots(space=0, refwidth="10em") + ax.format(xticks="null", yticks="null", xlabel="x axis", ylabel="y axis") + fig.canvas.draw() + + assert fig._supxlabel_dict + assert fig._supylabel_dict + assert all(not lab.get_in_layout() for lab in fig._supxlabel_dict.values()) + assert all(not lab.get_in_layout() for lab in fig._supylabel_dict.values()) + + left, bottom, _, _ = ax.get_position().bounds + assert left < 0.25 + assert bottom < 0.25 + + uplt.close(fig) + + def test_subset_share_xlabels_implicit(): fig, ax = uplt.subplots(ncols=2, nrows=2, share="labels", span=False) ax[0, 0].format(xlabel="Top-left X")