diff --git a/lib/matplotlib/testing/decorators.py b/lib/matplotlib/testing/decorators.py index af9ef48d66cc..140783c33be6 100644 --- a/lib/matplotlib/testing/decorators.py +++ b/lib/matplotlib/testing/decorators.py @@ -15,6 +15,7 @@ import matplotlib.units import matplotlib.testing from matplotlib import _pylab_helpers, cbook, ft2font, pyplot as plt, ticker +import matplotlib.text as mtext from .compare import comparable_formats, compare_images, make_test_filename from .exceptions import ImageComparisonFailure @@ -117,11 +118,12 @@ class _ImageComparisonBase: any code that would be specific to any testing framework. """ - def __init__(self, func, tol, remove_text, savefig_kwargs): + def __init__(self, func, tol, remove_text, replace_text, savefig_kwargs): self.func = func self.baseline_dir, self.result_dir = _image_directories(func) self.tol = tol self.remove_text = remove_text + self.replace_text = replace_text self.savefig_kwargs = savefig_kwargs def copy_baseline(self, baseline, extension): @@ -164,19 +166,23 @@ def compare(self, fig, baseline, extension, *, _lock=False): lock = (cbook._lock_path(actual_path) if _lock else contextlib.nullcontext()) with lock: + if self.replace_text: + mtext.Text._draw_bbox_only = True try: fig.savefig(actual_path, **kwargs) finally: # Matplotlib has an autouse fixture to close figures, but this # makes things more convenient for third-party users. plt.close(fig) + if self.replace_text: + mtext.Text._draw_bbox_only = False expected_path = self.copy_baseline(baseline, extension) _raise_on_image_difference(expected_path, actual_path, self.tol) def _pytest_image_comparison(baseline_images, extensions, tol, - freetype_version, remove_text, savefig_kwargs, - style): + freetype_version, remove_text, replace_text, + savefig_kwargs, style): """ Decorate function with image comparison for pytest. @@ -212,6 +218,7 @@ def wrapper(*args, extension, request, **kwargs): pytest.skip(f"Cannot compare {extension} files {reason}") img = _ImageComparisonBase(func, tol=tol, remove_text=remove_text, + replace_text=replace_text, savefig_kwargs=savefig_kwargs) matplotlib.testing.set_font_settings_for_testing() @@ -259,6 +266,7 @@ def wrapper(*args, extension, request, **kwargs): def image_comparison(baseline_images, extensions=None, tol=0, freetype_version=None, remove_text=False, + replace_text=False, savefig_kwarg=None, # Default of mpl_test_settings fixture and cleanup too. style=("classic", "_classic_test_patch")): @@ -344,6 +352,7 @@ def image_comparison(baseline_images, extensions=None, tol=0, return _pytest_image_comparison( baseline_images=baseline_images, extensions=extensions, tol=tol, freetype_version=freetype_version, remove_text=remove_text, + replace_text=replace_text, savefig_kwargs=savefig_kwarg, style=style) diff --git a/lib/matplotlib/tests/baseline_images/test_text/replace_text.png b/lib/matplotlib/tests/baseline_images/test_text/replace_text.png new file mode 100644 index 000000000000..841fcf0320ff Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_text/replace_text.png differ diff --git a/lib/matplotlib/tests/test_text.py b/lib/matplotlib/tests/test_text.py index f2b48f08981e..a1dbc5200ced 100644 --- a/lib/matplotlib/tests/test_text.py +++ b/lib/matplotlib/tests/test_text.py @@ -1190,3 +1190,15 @@ def test_ytick_rotation_mode(): tick.set_rotation(angle) plt.subplots_adjust(left=0.4, right=0.6, top=.99, bottom=.01) + + +@image_comparison(baseline_images=['replace_text'], + replace_text=True, extensions=['png'], style='mpl20') +def test_replace_text(): + fig, ax = plt.subplots() + ax.plot([2, 1], label='line 1') + ax.plot([3, 2], label='line 2') + ax.legend(title='mylegend') + ax.set_title('testing text replacement') + ax.set_xlabel('xlabel') + ax.set_ylabel('ylabel') diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py index 0eea81f416eb..ea7933d368b8 100644 --- a/lib/matplotlib/text.py +++ b/lib/matplotlib/text.py @@ -97,6 +97,13 @@ class Text(Artist): zorder = 3 _charsize_cache = dict() + _draw_bbox_only = False + """ + A helper flag to replace texts by a bounding box. This is primarily + intended for use in figure comparison tests. + For sipmlicity, the flag is evaluated at draw time and thus applies + to all drawn text. + """ def __repr__(self): return f"Text({self._x}, {self._y}, {self._text!r})" @@ -778,6 +785,9 @@ def draw(self, renderer): # Update the location and size of the bbox # (`.patches.FancyBboxPatch`), and draw it. + if self._draw_bbox_only: + self.set_bbox(dict(facecolor='none', edgecolor='red', pad=0)) + if self._bbox_patch: self.update_bbox_position_size(renderer) self._bbox_patch.draw(renderer) @@ -793,6 +803,9 @@ def draw(self, renderer): for line, wh, x, y in info: + if self._draw_bbox_only: + break + mtext = self if len(info) == 1 else None x = x + posx y = y + posy