diff --git a/doc/api/next_api_changes/2018-06-24-JMK.rst b/doc/api/next_api_changes/2018-06-24-JMK.rst new file mode 100644 index 000000000000..d7749d7a6e73 --- /dev/null +++ b/doc/api/next_api_changes/2018-06-24-JMK.rst @@ -0,0 +1,7 @@ +Text alignment fixes made +------------------------- + +Text alignment was incorrect, in particular for multiline text objects +with large descenders (i.e. subscripts) and rotated text. These have been +fixed and made more consistent, but could make old code that has compensated +no longer have the correct alignment. diff --git a/lib/matplotlib/tests/baseline_images/test_text/multiline2.pdf b/lib/matplotlib/tests/baseline_images/test_text/multiline2.pdf new file mode 100644 index 000000000000..70cd50471844 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_text/multiline2.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/multiline2.png b/lib/matplotlib/tests/baseline_images/test_text/multiline2.png new file mode 100644 index 000000000000..691608e36065 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_text/multiline2.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/multiline2.svg b/lib/matplotlib/tests/baseline_images/test_text/multiline2.svg new file mode 100644 index 000000000000..f0e372271158 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_text/multiline2.svg @@ -0,0 +1,1559 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_text/text_alignment.pdf b/lib/matplotlib/tests/baseline_images/test_text/text_alignment.pdf index a6e4fcf45d8a..ffba4f7754c3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/text_alignment.pdf and b/lib/matplotlib/tests/baseline_images/test_text/text_alignment.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/text_alignment.png b/lib/matplotlib/tests/baseline_images/test_text/text_alignment.png index b28ea9027bc7..7da3bff035ed 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/text_alignment.png and b/lib/matplotlib/tests/baseline_images/test_text/text_alignment.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/text_alignment.svg b/lib/matplotlib/tests/baseline_images/test_text/text_alignment.svg index 13adbe4deae7..f860e3c793fa 100644 --- a/lib/matplotlib/tests/baseline_images/test_text/text_alignment.svg +++ b/lib/matplotlib/tests/baseline_images/test_text/text_alignment.svg @@ -1,8 +1,8 @@ - - + + - - + + - + - + - + - + - + - + - - - + @@ -93,7 +93,7 @@ L 2.6875 54.6875 L 9.28125 54.6875 L 9.28125 70.21875 z -" id="DejaVuSans-74"/> +" id="DejaVuSans-116"/> +z +" id="DejaVuSans-111"/> - +z +" id="DejaVuSans-112"/> + +" id="DejaVuSans-84"/> +" id="DejaVuSans-106"/> - - - - - - - + + + + + + + - - - - - + + + + + - - - - - - + + + + + + - + @@ -371,6 +292,7 @@ Q 18.109375 17.390625 22.1875 11.75 Q 26.265625 6.109375 33.40625 6.109375 Q 40.53125 6.109375 44.609375 11.75 Q 48.6875 17.390625 48.6875 27.296875 +z M 18.109375 46.390625 Q 20.953125 51.265625 25.265625 53.625 Q 29.59375 56 35.59375 56 @@ -385,7 +307,7 @@ L 9.078125 0 L 9.078125 75.984375 L 18.109375 75.984375 z -" id="DejaVuSans-62"/> +" id="DejaVuSans-98"/> +z +" id="DejaVuSans-109"/> - - - - - - - - - - + + + + + + + + + + - - - - - - + + + + + + - + @@ -484,7 +407,8 @@ Q 14.9375 54.109375 19.578125 55.046875 Q 24.21875 56 28.609375 56 Q 40.484375 56 46.34375 49.84375 Q 52.203125 43.703125 52.203125 31.203125 -" id="DejaVuSans-61"/> +z +" id="DejaVuSans-97"/> +z +" id="DejaVuSans-115"/> +" id="DejaVuSans-101"/> +" id="DejaVuSans-108"/> +" id="DejaVuSans-105"/> +z +" id="DejaVuSans-110"/> - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - + + + + + + - + @@ -633,7 +560,8 @@ Q 5.515625 40.671875 12.859375 48.328125 Q 20.21875 56 33.015625 56 Q 37.15625 56 41.109375 55.140625 Q 45.0625 54.296875 48.78125 52.59375 -" id="DejaVuSans-63"/> +z +" id="DejaVuSans-99"/> +" id="DejaVuSans-114"/> - - - - - - - - - - + + + + + + + + + + - - - - - - + + + + + + - + - - - - - - - + + + + + + + - - - - - - + + + + + + - + - - - - - - - - - - + + + + + + + + + + - - - - - - + + + + + + - + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - + + + + + + - + - - - - - - - - - - + + + + + + + + + + - - - - - - + + + + + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_text/text_contains.png b/lib/matplotlib/tests/baseline_images/test_text/text_contains.png index 93b3433dceb1..6b2013fac31f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/text_contains.png and b/lib/matplotlib/tests/baseline_images/test_text/text_contains.png differ diff --git a/lib/matplotlib/tests/test_text.py b/lib/matplotlib/tests/test_text.py index a001aadd39a3..0743179825d0 100644 --- a/lib/matplotlib/tests/test_text.py +++ b/lib/matplotlib/tests/test_text.py @@ -6,6 +6,7 @@ import pytest import matplotlib +import matplotlib.patches as mpatches import matplotlib.pyplot as plt from matplotlib.testing.decorators import image_comparison @@ -126,6 +127,54 @@ def test_multiline(): ax.set_yticks([]) +@image_comparison(baseline_images=['multiline2'], style='mpl20') +def test_multiline2(): + fig, ax = plt.subplots() + + ax.set_xlim([0, 1.4]) + ax.set_ylim([0, 2]) + ax.axhline(0.5, color='C2', linewidth=0.3) + sts = ['Line', '2 Lineg\n 2 Lg', '$\sum_i x $', 'hi $\sum_i x $\ntest', + 'test\n $\sum_i x $', '$\sum_i x $\n $\sum_i x $'] + renderer = fig.canvas.get_renderer() + + def draw_box(ax, tt): + bb = tt.get_window_extent(renderer) + bbt = bb.inverse_transformed(ax.transAxes) + r = mpatches.Rectangle((0, 0), 1, 1, clip_on=False, + transform=ax.transAxes) + r.set_bounds(bbt.bounds) + ax.add_patch(r) + + horal = 'left' + for nn, st in enumerate(sts): + tt = ax.text(0.2 * nn + 0.1, 0.5, st, horizontalalignment=horal, + verticalalignment='bottom') + draw_box(ax, tt) + ax.text(1.2, 0.5, 'Bottom align', color='C2') + + ax.axhline(1.3, color='C2', linewidth=0.3) + for nn, st in enumerate(sts): + tt = ax.text(0.2 * nn + 0.1, 1.3, st, horizontalalignment=horal, + verticalalignment='top') + draw_box(ax, tt) + ax.text(1.2, 1.3, 'Top align', color='C2') + + ax.axhline(1.8, color='C2', linewidth=0.3) + for nn, st in enumerate(sts): + tt = ax.text(0.2 * nn + 0.1, 1.8, st, horizontalalignment=horal, + verticalalignment='baseline') + draw_box(ax, tt) + ax.text(1.2, 1.8, 'Baseline align', color='C2') + + ax.axhline(0.1, color='C2', linewidth=0.3) + for nn, st in enumerate(sts): + tt = ax.text(0.2 * nn + 0.1, 0.1, st, horizontalalignment=horal, + verticalalignment='bottom', rotation=20) + draw_box(ax, tt) + ax.text(1.2, 0.1, 'Bot align, rot20', color='C2') + + @image_comparison(baseline_images=['antialiased'], extensions=['png']) def test_antialiasing(): matplotlib.rcParams['text.antialiased'] = True @@ -166,7 +215,7 @@ def test_contains(): xs, ys = np.meshgrid(xs, ys) txt = plt.text( - 0.48, 0.52, 'hello world', ha='center', fontsize=30, rotation=30) + 0.5, 0.4, 'hello world', ha='center', fontsize=30, rotation=30) # uncomment to draw the text's bounding box # txt.set_bbox(dict(edgecolor='black', facecolor='none')) @@ -196,7 +245,7 @@ def test_titles(): ax.set_yticks([]) -@image_comparison(baseline_images=['text_alignment']) +@image_comparison(baseline_images=['text_alignment'], style='mpl20') def test_alignment(): plt.figure() ax = plt.subplot(1, 1, 1) diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py index b49bac53a893..cac7ce8f4552 100644 --- a/lib/matplotlib/text.py +++ b/lib/matplotlib/text.py @@ -297,8 +297,7 @@ def _get_layout(self, renderer): whs = np.zeros((len(lines), 2)) horizLayout = np.zeros((len(lines), 4)) - # Find full vertical extent of font, - # including ascenders and descenders: + # Full vertical extent of font, including ascenders and descenders: tmp, lp_h, lp_bl = renderer.get_text_width_height_descent('lp', self._fontproperties, ismath=False) @@ -324,14 +323,21 @@ def _get_layout(self, renderer): whs[i] = w, h baseline = (h - d) - thisy - thisy -= max(offsety, (h - d) * self._linespacing) + if i == 0: + # position at baseline + thisy = -(h - d) + else: + # put baseline a good distance from bottom of previous line + thisy -= max(offsety, (h - d) * self._linespacing) horizLayout[i] = thisx, thisy, w, h thisy -= d width = max(width, w) descent = d - ymin = horizLayout[-1][1] - ymax = horizLayout[0][1] + horizLayout[0][3] + # Bounding box definition: + ymax = 0 + # ymin is baseline of previous line minus the descent of this line + ymin = horizLayout[-1][1] - descent height = ymax - ymin xmax = xmin + width @@ -351,7 +357,6 @@ def _get_layout(self, renderer): # the corners of the unrotated bounding box cornersHoriz = np.array( [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)], float) - cornersHoriz[:, 1] -= descent # now rotate the bbox cornersRotated = M.transform(cornersHoriz) @@ -386,7 +391,7 @@ def _get_layout(self, renderer): elif valign == 'top': offsety = (ymin + height) elif valign == 'baseline': - offsety = (ymin + height) - baseline + offsety = ymin + descent elif valign == 'center_baseline': offsety = ymin + height - baseline / 2.0 else: diff --git a/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/text3d.pdf b/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/text3d.pdf index 29418da8fe36..5f51c7e78b0c 100644 Binary files a/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/text3d.pdf and b/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/text3d.pdf differ diff --git a/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/text3d.png b/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/text3d.png index deb9ef3dd252..2264cffc4b0f 100644 Binary files a/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/text3d.png and b/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/text3d.png differ diff --git a/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/text3d.svg b/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/text3d.svg index 7ff89bd7058e..3a166583267c 100644 --- a/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/text3d.svg +++ b/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/text3d.svg @@ -1,7 +1,7 @@ - + - - - - - - - - + + - - + - + + - +" id="DejaVuSans-115"/> - - - - - - - + + + + + + + - - - - - - - - + - - + + - - + - - + + - - +" id="DejaVuSans-52"/> - - + + - - + - - + + - - + - - + + - - +" id="DejaVuSans-49"/> - - - + + + - - +" id="DejaVuSans-89"/> - - - - - - - + + + + + + + - - - - - - - - - + + - - - + + - - - + + - - - + + - - - + + - - - - + + + - - +" id="DejaVuSans-90"/> - - - - - - - + + + + + + + - - - - - - - - - + + - - - + + - - - + + - - - + + - - - + + - - - - + + + @@ -703,465 +713,474 @@ L 478.01998 112.499596 - + - + - + - - - - - - + - + +" id="DejaVuSans-61"/> + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - + +" id="DejaVuSans-121"/> - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - + +" id="DejaVuSans-55"/> + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + - - + - + +" id="DejaVuSans-116"/> - - - - - - - + + + + + + +