From 14a2b55145b9552f72f7cf62258cc830811988e0 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Sat, 5 Apr 2025 09:06:37 +0200 Subject: [PATCH] ENH: Add option to relpace text with boxes in image_comparision test This should be the right level of simplification when text is relevant in image comparision tests but the exact characters do not matter. The drawn bboxes only rely on font metrics and thus give accurate sizes, while being insensitive to rendering details. This is a minimal proof-of-concept. Exact implementation may be improved . --- lib/matplotlib/testing/decorators.py | 15 ++++++++++++--- .../baseline_images/test_text/replace_text.png | Bin 0 -> 19340 bytes lib/matplotlib/tests/test_text.py | 12 ++++++++++++ lib/matplotlib/text.py | 13 +++++++++++++ 4 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 lib/matplotlib/tests/baseline_images/test_text/replace_text.png 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 0000000000000000000000000000000000000000..841fcf0320ffefdc55342f490916a5ec8a3ed3ac GIT binary patch literal 19340 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∾j)cNHz8wKkFv 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

1USK&sGvGvX8D8@!NfRP7Cn*lOcM`MKD*3Ok< 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 literal 0 HcmV?d00001 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