Skip to content

Commit 14a2b55

Browse files
committed
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 .
1 parent 77993d5 commit 14a2b55

File tree

4 files changed

+37
-3
lines changed

4 files changed

+37
-3
lines changed

lib/matplotlib/testing/decorators.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import matplotlib.units
1616
import matplotlib.testing
1717
from matplotlib import _pylab_helpers, cbook, ft2font, pyplot as plt, ticker
18+
import matplotlib.text as mtext
1819
from .compare import comparable_formats, compare_images, make_test_filename
1920
from .exceptions import ImageComparisonFailure
2021

@@ -117,11 +118,12 @@ class _ImageComparisonBase:
117118
any code that would be specific to any testing framework.
118119
"""
119120

120-
def __init__(self, func, tol, remove_text, savefig_kwargs):
121+
def __init__(self, func, tol, remove_text, replace_text, savefig_kwargs):
121122
self.func = func
122123
self.baseline_dir, self.result_dir = _image_directories(func)
123124
self.tol = tol
124125
self.remove_text = remove_text
126+
self.replace_text = replace_text
125127
self.savefig_kwargs = savefig_kwargs
126128

127129
def copy_baseline(self, baseline, extension):
@@ -164,19 +166,23 @@ def compare(self, fig, baseline, extension, *, _lock=False):
164166
lock = (cbook._lock_path(actual_path)
165167
if _lock else contextlib.nullcontext())
166168
with lock:
169+
if self.replace_text:
170+
mtext.Text._draw_bbox_only = True
167171
try:
168172
fig.savefig(actual_path, **kwargs)
169173
finally:
170174
# Matplotlib has an autouse fixture to close figures, but this
171175
# makes things more convenient for third-party users.
172176
plt.close(fig)
177+
if self.replace_text:
178+
mtext.Text._draw_bbox_only = False
173179
expected_path = self.copy_baseline(baseline, extension)
174180
_raise_on_image_difference(expected_path, actual_path, self.tol)
175181

176182

177183
def _pytest_image_comparison(baseline_images, extensions, tol,
178-
freetype_version, remove_text, savefig_kwargs,
179-
style):
184+
freetype_version, remove_text, replace_text,
185+
savefig_kwargs, style):
180186
"""
181187
Decorate function with image comparison for pytest.
182188
@@ -212,6 +218,7 @@ def wrapper(*args, extension, request, **kwargs):
212218
pytest.skip(f"Cannot compare {extension} files {reason}")
213219

214220
img = _ImageComparisonBase(func, tol=tol, remove_text=remove_text,
221+
replace_text=replace_text,
215222
savefig_kwargs=savefig_kwargs)
216223
matplotlib.testing.set_font_settings_for_testing()
217224

@@ -259,6 +266,7 @@ def wrapper(*args, extension, request, **kwargs):
259266

260267
def image_comparison(baseline_images, extensions=None, tol=0,
261268
freetype_version=None, remove_text=False,
269+
replace_text=False,
262270
savefig_kwarg=None,
263271
# Default of mpl_test_settings fixture and cleanup too.
264272
style=("classic", "_classic_test_patch")):
@@ -344,6 +352,7 @@ def image_comparison(baseline_images, extensions=None, tol=0,
344352
return _pytest_image_comparison(
345353
baseline_images=baseline_images, extensions=extensions, tol=tol,
346354
freetype_version=freetype_version, remove_text=remove_text,
355+
replace_text=replace_text,
347356
savefig_kwargs=savefig_kwarg, style=style)
348357

349358

18.9 KB
Loading

lib/matplotlib/tests/test_text.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,3 +1190,15 @@ def test_ytick_rotation_mode():
11901190
tick.set_rotation(angle)
11911191

11921192
plt.subplots_adjust(left=0.4, right=0.6, top=.99, bottom=.01)
1193+
1194+
1195+
@image_comparison(baseline_images=['replace_text'],
1196+
replace_text=True, extensions=['png'], style='mpl20')
1197+
def test_replace_text():
1198+
fig, ax = plt.subplots()
1199+
ax.plot([2, 1], label='line 1')
1200+
ax.plot([3, 2], label='line 2')
1201+
ax.legend(title='mylegend')
1202+
ax.set_title('testing text replacement')
1203+
ax.set_xlabel('xlabel')
1204+
ax.set_ylabel('ylabel')

lib/matplotlib/text.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ class Text(Artist):
9797

9898
zorder = 3
9999
_charsize_cache = dict()
100+
_draw_bbox_only = False
101+
"""
102+
A helper flag to replace texts by a bounding box. This is primarily
103+
intended for use in figure comparison tests.
104+
For sipmlicity, the flag is evaluated at draw time and thus applies
105+
to all drawn text.
106+
"""
100107

101108
def __repr__(self):
102109
return f"Text({self._x}, {self._y}, {self._text!r})"
@@ -778,6 +785,9 @@ def draw(self, renderer):
778785

779786
# Update the location and size of the bbox
780787
# (`.patches.FancyBboxPatch`), and draw it.
788+
if self._draw_bbox_only:
789+
self.set_bbox(dict(facecolor='none', edgecolor='red', pad=0))
790+
781791
if self._bbox_patch:
782792
self.update_bbox_position_size(renderer)
783793
self._bbox_patch.draw(renderer)
@@ -793,6 +803,9 @@ def draw(self, renderer):
793803

794804
for line, wh, x, y in info:
795805

806+
if self._draw_bbox_only:
807+
break
808+
796809
mtext = self if len(info) == 1 else None
797810
x = x + posx
798811
y = y + posy

0 commit comments

Comments
 (0)