Skip to content

Commit dfe4e55

Browse files
authored
Merge pull request #31107 from anntzer/supal
Fix confusion between text height and ascent in metrics calculations.
2 parents a6c2501 + 60f2310 commit dfe4e55

File tree

2 files changed

+43
-42
lines changed

2 files changed

+43
-42
lines changed

lib/matplotlib/offsetbox.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -799,23 +799,24 @@ def get_bbox(self, renderer):
799799
ismath="TeX" if self._text.get_usetex() else False,
800800
dpi=self.get_figure(root=True).dpi)
801801

802-
bbox, info, yd = self._text._get_layout(renderer)
802+
bbox, info = self._text._get_layout(renderer)
803+
_last_line, (_last_width, _last_ascent, last_descent), _last_xy = info[-1]
803804
w, h = bbox.size
804805

805806
self._baseline_transform.clear()
806807

807808
if len(info) > 1 and self._multilinebaseline:
808809
yd_new = 0.5 * h - 0.5 * (h_ - d_)
809-
self._baseline_transform.translate(0, yd - yd_new)
810-
yd = yd_new
810+
self._baseline_transform.translate(0, last_descent - yd_new)
811+
last_descent = yd_new
811812
else: # single line
812-
h_d = max(h_ - d_, h - yd)
813-
h = h_d + yd
813+
h_d = max(h_ - d_, h - last_descent)
814+
h = h_d + last_descent
814815

815816
ha = self._text.get_horizontalalignment()
816817
x0 = {"left": 0, "center": -w / 2, "right": -w}[ha]
817818

818-
return Bbox.from_bounds(x0, -yd, w, h)
819+
return Bbox.from_bounds(x0, -last_descent, w, h)
819820

820821
def draw(self, renderer):
821822
# docstring inherited

lib/matplotlib/text.py

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,22 @@ def _get_textbox(text, renderer):
3737
# called within the _get_textbox. So, it would be better to move this
3838
# function as a method with some refactoring of _get_layout method.
3939

40-
projected_xs = []
41-
projected_ys = []
40+
projected_xys = []
4241

4342
theta = np.deg2rad(text.get_rotation())
4443
tr = Affine2D().rotate(-theta)
4544

46-
_, parts, d = text._get_layout(renderer)
45+
_, parts = text._get_layout(renderer)
4746

48-
for t, wh, x, y in parts:
49-
w, h = wh
50-
51-
xt1, yt1 = tr.transform((x, y))
52-
yt1 -= d
53-
xt2, yt2 = xt1 + w, yt1 + h
54-
55-
projected_xs.extend([xt1, xt2])
56-
projected_ys.extend([yt1, yt2])
47+
for t, (w, a, d), xy in parts:
48+
xt, yt = tr.transform(xy)
49+
projected_xys.extend([
50+
(xt, yt + a),
51+
(xt, yt - d),
52+
(xt + w, yt + a),
53+
(xt + w, yt - d),
54+
])
55+
projected_xs, projected_ys = zip(*projected_xys)
5756

5857
xt_box, yt_box = min(projected_xs), min(projected_ys)
5958
w_box, h_box = max(projected_xs) - xt_box, max(projected_ys) - yt_box
@@ -434,15 +433,18 @@ def update_from(self, other):
434433

435434
def _get_layout(self, renderer):
436435
"""
437-
Return the extent (bbox) of the text together with
438-
multiple-alignment information. Note that it returns an extent
439-
of a rotated text when necessary.
436+
Return
437+
438+
- the (rotated) text bbox, and
439+
- a list of ``(line, (width, ascent, descent), xy)`` tuples for each line.
440440
"""
441441
thisx, thisy = 0.0, 0.0
442442
lines = self._get_wrapped_text().split("\n") # Ensures lines is not empty.
443443

444-
ws = []
445-
hs = []
444+
# Reminder: The ascent (a) goes from the baseline to the top and the
445+
# descent (d) from the baseline to the bottom; both are (typically)
446+
# nonnegative. The height h is the sum, h = a + d.
447+
wads = [] # (width, ascents, descents)
446448
xs = []
447449
ys = []
448450

@@ -451,7 +453,8 @@ def _get_layout(self, renderer):
451453
renderer, "lp", self._fontproperties,
452454
ismath="TeX" if self.get_usetex() else False,
453455
dpi=self.get_figure(root=True).dpi)
454-
min_dy = (lp_h - lp_d) * self._linespacing
456+
lp_a = lp_h - lp_d
457+
min_dy = lp_a * self._linespacing
455458

456459
for i, line in enumerate(lines):
457460
clean_line, ismath = self._preprocess_math(line)
@@ -462,25 +465,21 @@ def _get_layout(self, renderer):
462465
else:
463466
w = h = d = 0
464467

465-
# For multiline text, increase the line spacing when the text
466-
# net-height (excluding baseline) is larger than that of a "l"
467-
# (e.g., use of superscripts), which seems what TeX does.
468-
h = max(h, lp_h)
468+
a = h - d
469+
# To ensure good linespacing, pretend that the ascent (resp.
470+
# descent) of all lines is at least as large as "l" (resp. "p").
471+
a = max(a, lp_a)
469472
d = max(d, lp_d)
470473

471-
ws.append(w)
472-
hs.append(h)
473-
474474
# Metrics of the last line that are needed later:
475-
baseline = (h - d) - thisy
475+
baseline = a - thisy
476476

477-
if i == 0:
478-
# position at baseline
479-
thisy = -(h - d)
480-
else:
481-
# put baseline a good distance from bottom of previous line
482-
thisy -= max(min_dy, (h - d) * self._linespacing)
477+
if i == 0: # position at baseline
478+
thisy = -a
479+
else: # put baseline a good distance from bottom of previous line
480+
thisy -= max(min_dy, a * self._linespacing)
483481

482+
wads.append((w, a, d))
484483
xs.append(thisx) # == 0.
485484
ys.append(thisy)
486485

@@ -490,6 +489,7 @@ def _get_layout(self, renderer):
490489
descent = d
491490

492491
# Bounding box definition:
492+
ws = [w for w, a, d in wads]
493493
width = max(ws)
494494
xmin = 0
495495
xmax = width
@@ -587,7 +587,7 @@ def _get_layout(self, renderer):
587587
# now rotate the positions around the first (x, y) position
588588
xys = M.transform(offset_layout) - (offsetx, offsety)
589589

590-
return bbox, list(zip(lines, zip(ws, hs), *xys.T)), descent
590+
return bbox, list(zip(lines, wads, xys))
591591

592592
def set_bbox(self, rectprops):
593593
"""
@@ -840,7 +840,7 @@ def draw(self, renderer):
840840
renderer.open_group('text', self.get_gid())
841841

842842
with self._cm_set(text=self._get_wrapped_text()):
843-
bbox, info, descent = self._get_layout(renderer)
843+
bbox, info = self._get_layout(renderer)
844844
trans = self.get_transform()
845845

846846
# don't use self.get_position here, which refers to text
@@ -876,7 +876,7 @@ def draw(self, renderer):
876876

877877
angle = self.get_rotation()
878878

879-
for line, wh, x, y in info:
879+
for line, wad, (x, y) in info:
880880

881881
mtext = self if len(info) == 1 else None
882882
x = x + posx
@@ -1064,7 +1064,7 @@ def get_window_extent(self, renderer=None, dpi=None):
10641064
"want to call 'figure.draw_without_rendering()' first.")
10651065

10661066
with cbook._setattr_cm(fig, dpi=dpi):
1067-
bbox, info, descent = self._get_layout(self._renderer)
1067+
bbox, _ = self._get_layout(self._renderer)
10681068
x, y = self.get_unitless_position()
10691069
x, y = self.get_transform().transform((x, y))
10701070
bbox = bbox.translated(x, y)

0 commit comments

Comments
 (0)