1from datetime import datetime
2import io
3import warnings
4
5import numpy as np
6from numpy.testing import assert_almost_equal
7import pytest
8
9import matplotlib as mpl
10from matplotlib.backend_bases import MouseEvent
11from matplotlib.font_manager import FontProperties
12import matplotlib.patches as mpatches
13import matplotlib.pyplot as plt
14import matplotlib.transforms as mtransforms
15from matplotlib.testing.decorators import check_figures_equal, image_comparison
16from matplotlib.text import Text
17
18
19needs_usetex = pytest.mark.skipif(
20    not mpl.checkdep_usetex(True),
21    reason="This test needs a TeX installation")
22
23
24@image_comparison(['font_styles'])
25def test_font_styles():
26
27    def find_matplotlib_font(**kw):
28        prop = FontProperties(**kw)
29        path = findfont(prop, directory=mpl.get_data_path())
30        return FontProperties(fname=path)
31
32    from matplotlib.font_manager import FontProperties, findfont
33    warnings.filterwarnings(
34        'ignore',
35        r"findfont: Font family \[u?'Foo'\] not found. Falling back to .",
36        UserWarning,
37        module='matplotlib.font_manager')
38
39    fig, ax = plt.subplots()
40
41    normal_font = find_matplotlib_font(
42        family="sans-serif",
43        style="normal",
44        variant="normal",
45        size=14)
46    ax.annotate(
47        "Normal Font",
48        (0.1, 0.1),
49        xycoords='axes fraction',
50        fontproperties=normal_font)
51
52    bold_font = find_matplotlib_font(
53        family="Foo",
54        style="normal",
55        variant="normal",
56        weight="bold",
57        stretch=500,
58        size=14)
59    ax.annotate(
60        "Bold Font",
61        (0.1, 0.2),
62        xycoords='axes fraction',
63        fontproperties=bold_font)
64
65    bold_italic_font = find_matplotlib_font(
66        family="sans serif",
67        style="italic",
68        variant="normal",
69        weight=750,
70        stretch=500,
71        size=14)
72    ax.annotate(
73        "Bold Italic Font",
74        (0.1, 0.3),
75        xycoords='axes fraction',
76        fontproperties=bold_italic_font)
77
78    light_font = find_matplotlib_font(
79        family="sans-serif",
80        style="normal",
81        variant="normal",
82        weight=200,
83        stretch=500,
84        size=14)
85    ax.annotate(
86        "Light Font",
87        (0.1, 0.4),
88        xycoords='axes fraction',
89        fontproperties=light_font)
90
91    condensed_font = find_matplotlib_font(
92        family="sans-serif",
93        style="normal",
94        variant="normal",
95        weight=500,
96        stretch=100,
97        size=14)
98    ax.annotate(
99        "Condensed Font",
100        (0.1, 0.5),
101        xycoords='axes fraction',
102        fontproperties=condensed_font)
103
104    ax.set_xticks([])
105    ax.set_yticks([])
106
107
108@image_comparison(['multiline'])
109def test_multiline():
110    plt.figure()
111    ax = plt.subplot(1, 1, 1)
112    ax.set_title("multiline\ntext alignment")
113
114    plt.text(
115        0.2, 0.5, "TpTpTp\n$M$\nTpTpTp", size=20, ha="center", va="top")
116
117    plt.text(
118        0.5, 0.5, "TpTpTp\n$M^{M^{M^{M}}}$\nTpTpTp", size=20,
119        ha="center", va="top")
120
121    plt.text(
122        0.8, 0.5, "TpTpTp\n$M_{q_{q_{q}}}$\nTpTpTp", size=20,
123        ha="center", va="top")
124
125    plt.xlim(0, 1)
126    plt.ylim(0, 0.8)
127
128    ax.set_xticks([])
129    ax.set_yticks([])
130
131
132@image_comparison(['multiline2'], style='mpl20')
133def test_multiline2():
134    # Remove this line when this test image is regenerated.
135    plt.rcParams['text.kerning_factor'] = 6
136
137    fig, ax = plt.subplots()
138
139    ax.set_xlim([0, 1.4])
140    ax.set_ylim([0, 2])
141    ax.axhline(0.5, color='C2', linewidth=0.3)
142    sts = ['Line', '2 Lineg\n 2 Lg', '$\\sum_i x $', 'hi $\\sum_i x $\ntest',
143           'test\n $\\sum_i x $', '$\\sum_i x $\n $\\sum_i x $']
144    renderer = fig.canvas.get_renderer()
145
146    def draw_box(ax, tt):
147        r = mpatches.Rectangle((0, 0), 1, 1, clip_on=False,
148                               transform=ax.transAxes)
149        r.set_bounds(
150            tt.get_window_extent(renderer)
151            .transformed(ax.transAxes.inverted())
152            .bounds)
153        ax.add_patch(r)
154
155    horal = 'left'
156    for nn, st in enumerate(sts):
157        tt = ax.text(0.2 * nn + 0.1, 0.5, st, horizontalalignment=horal,
158                     verticalalignment='bottom')
159        draw_box(ax, tt)
160    ax.text(1.2, 0.5, 'Bottom align', color='C2')
161
162    ax.axhline(1.3, color='C2', linewidth=0.3)
163    for nn, st in enumerate(sts):
164        tt = ax.text(0.2 * nn + 0.1, 1.3, st, horizontalalignment=horal,
165                     verticalalignment='top')
166        draw_box(ax, tt)
167    ax.text(1.2, 1.3, 'Top align', color='C2')
168
169    ax.axhline(1.8, color='C2', linewidth=0.3)
170    for nn, st in enumerate(sts):
171        tt = ax.text(0.2 * nn + 0.1, 1.8, st, horizontalalignment=horal,
172                     verticalalignment='baseline')
173        draw_box(ax, tt)
174    ax.text(1.2, 1.8, 'Baseline align', color='C2')
175
176    ax.axhline(0.1, color='C2', linewidth=0.3)
177    for nn, st in enumerate(sts):
178        tt = ax.text(0.2 * nn + 0.1, 0.1, st, horizontalalignment=horal,
179                     verticalalignment='bottom', rotation=20)
180        draw_box(ax, tt)
181    ax.text(1.2, 0.1, 'Bot align, rot20', color='C2')
182
183
184@image_comparison(['antialiased.png'])
185def test_antialiasing():
186    mpl.rcParams['text.antialiased'] = True
187
188    fig = plt.figure(figsize=(5.25, 0.75))
189    fig.text(0.5, 0.75, "antialiased", horizontalalignment='center',
190             verticalalignment='center')
191    fig.text(0.5, 0.25, r"$\sqrt{x}$", horizontalalignment='center',
192             verticalalignment='center')
193    # NOTE: We don't need to restore the rcParams here, because the
194    # test cleanup will do it for us.  In fact, if we do it here, it
195    # will turn antialiasing back off before the images are actually
196    # rendered.
197
198
199def test_afm_kerning():
200    fn = mpl.font_manager.findfont("Helvetica", fontext="afm")
201    with open(fn, 'rb') as fh:
202        afm = mpl.afm.AFM(fh)
203    assert afm.string_width_height('VAVAVAVAVAVA') == (7174.0, 718)
204
205
206@image_comparison(['text_contains.png'])
207def test_contains():
208    fig = plt.figure()
209    ax = plt.axes()
210
211    mevent = MouseEvent('button_press_event', fig.canvas, 0.5, 0.5, 1, None)
212
213    xs = np.linspace(0.25, 0.75, 30)
214    ys = np.linspace(0.25, 0.75, 30)
215    xs, ys = np.meshgrid(xs, ys)
216
217    txt = plt.text(
218        0.5, 0.4, 'hello world', ha='center', fontsize=30, rotation=30)
219    # uncomment to draw the text's bounding box
220    # txt.set_bbox(dict(edgecolor='black', facecolor='none'))
221
222    # draw the text. This is important, as the contains method can only work
223    # when a renderer exists.
224    fig.canvas.draw()
225
226    for x, y in zip(xs.flat, ys.flat):
227        mevent.x, mevent.y = plt.gca().transAxes.transform([x, y])
228        contains, _ = txt.contains(mevent)
229        color = 'yellow' if contains else 'red'
230
231        # capture the viewLim, plot a point, and reset the viewLim
232        vl = ax.viewLim.frozen()
233        ax.plot(x, y, 'o', color=color)
234        ax.viewLim.set(vl)
235
236
237def test_annotation_contains():
238    # Check that Annotation.contains looks at the bboxes of the text and the
239    # arrow separately, not at the joint bbox.
240    fig, ax = plt.subplots()
241    ann = ax.annotate(
242        "hello", xy=(.4, .4), xytext=(.6, .6), arrowprops={"arrowstyle": "->"})
243    fig.canvas.draw()   # Needed for the same reason as in test_contains.
244    event = MouseEvent(
245        "button_press_event", fig.canvas, *ax.transData.transform((.5, .6)))
246    assert ann.contains(event) == (False, {})
247
248
249@image_comparison(['titles'])
250def test_titles():
251    # left and right side titles
252    plt.figure()
253    ax = plt.subplot(1, 1, 1)
254    ax.set_title("left title", loc="left")
255    ax.set_title("right title", loc="right")
256    ax.set_xticks([])
257    ax.set_yticks([])
258
259
260@image_comparison(['text_alignment'], style='mpl20')
261def test_alignment():
262    plt.figure()
263    ax = plt.subplot(1, 1, 1)
264
265    x = 0.1
266    for rotation in (0, 30):
267        for alignment in ('top', 'bottom', 'baseline', 'center'):
268            ax.text(
269                x, 0.5, alignment + " Tj", va=alignment, rotation=rotation,
270                bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
271            ax.text(
272                x, 1.0, r'$\sum_{i=0}^{j}$', va=alignment, rotation=rotation)
273            x += 0.1
274
275    ax.plot([0, 1], [0.5, 0.5])
276    ax.plot([0, 1], [1.0, 1.0])
277
278    ax.set_xlim([0, 1])
279    ax.set_ylim([0, 1.5])
280    ax.set_xticks([])
281    ax.set_yticks([])
282
283
284@image_comparison(['axes_titles.png'])
285def test_axes_titles():
286    # Related to issue #3327
287    plt.figure()
288    ax = plt.subplot(1, 1, 1)
289    ax.set_title('center', loc='center', fontsize=20, fontweight=700)
290    ax.set_title('left', loc='left', fontsize=12, fontweight=400)
291    ax.set_title('right', loc='right', fontsize=12, fontweight=400)
292
293
294def test_set_position():
295    fig, ax = plt.subplots()
296
297    # test set_position
298    ann = ax.annotate(
299        'test', (0, 0), xytext=(0, 0), textcoords='figure pixels')
300    fig.canvas.draw()
301
302    init_pos = ann.get_window_extent(fig.canvas.renderer)
303    shift_val = 15
304    ann.set_position((shift_val, shift_val))
305    fig.canvas.draw()
306    post_pos = ann.get_window_extent(fig.canvas.renderer)
307
308    for a, b in zip(init_pos.min, post_pos.min):
309        assert a + shift_val == b
310
311    # test xyann
312    ann = ax.annotate(
313        'test', (0, 0), xytext=(0, 0), textcoords='figure pixels')
314    fig.canvas.draw()
315
316    init_pos = ann.get_window_extent(fig.canvas.renderer)
317    shift_val = 15
318    ann.xyann = (shift_val, shift_val)
319    fig.canvas.draw()
320    post_pos = ann.get_window_extent(fig.canvas.renderer)
321
322    for a, b in zip(init_pos.min, post_pos.min):
323        assert a + shift_val == b
324
325
326@pytest.mark.parametrize('text', ['', 'O'], ids=['empty', 'non-empty'])
327def test_non_default_dpi(text):
328    fig, ax = plt.subplots()
329
330    t1 = ax.text(0.5, 0.5, text, ha='left', va='bottom')
331    fig.canvas.draw()
332    dpi = fig.dpi
333
334    bbox1 = t1.get_window_extent()
335    bbox2 = t1.get_window_extent(dpi=dpi * 10)
336    np.testing.assert_allclose(bbox2.get_points(), bbox1.get_points() * 10,
337                               rtol=5e-2)
338    # Text.get_window_extent should not permanently change dpi.
339    assert fig.dpi == dpi
340
341
342def test_get_rotation_string():
343    assert mpl.text.get_rotation('horizontal') == 0.
344    assert mpl.text.get_rotation('vertical') == 90.
345    assert mpl.text.get_rotation('15.') == 15.
346
347
348def test_get_rotation_float():
349    for i in [15., 16.70, 77.4]:
350        assert mpl.text.get_rotation(i) == i
351
352
353def test_get_rotation_int():
354    for i in [67, 16, 41]:
355        assert mpl.text.get_rotation(i) == float(i)
356
357
358def test_get_rotation_raises():
359    with pytest.raises(ValueError):
360        mpl.text.get_rotation('hozirontal')
361
362
363def test_get_rotation_none():
364    assert mpl.text.get_rotation(None) == 0.0
365
366
367def test_get_rotation_mod360():
368    for i, j in zip([360., 377., 720+177.2], [0., 17., 177.2]):
369        assert_almost_equal(mpl.text.get_rotation(i), j)
370
371
372@pytest.mark.parametrize("ha", ["center", "right", "left"])
373@pytest.mark.parametrize("va", ["center", "top", "bottom",
374                                "baseline", "center_baseline"])
375def test_null_rotation_with_rotation_mode(ha, va):
376    fig, ax = plt.subplots()
377    kw = dict(rotation=0, va=va, ha=ha)
378    t0 = ax.text(.5, .5, 'test', rotation_mode='anchor', **kw)
379    t1 = ax.text(.5, .5, 'test', rotation_mode='default', **kw)
380    fig.canvas.draw()
381    assert_almost_equal(t0.get_window_extent(fig.canvas.renderer).get_points(),
382                        t1.get_window_extent(fig.canvas.renderer).get_points())
383
384
385@image_comparison(['text_bboxclip'])
386def test_bbox_clipping():
387    plt.text(0.9, 0.2, 'Is bbox clipped?', backgroundcolor='r', clip_on=True)
388    t = plt.text(0.9, 0.5, 'Is fancy bbox clipped?', clip_on=True)
389    t.set_bbox({"boxstyle": "round, pad=0.1"})
390
391
392@image_comparison(['annotation_negative_ax_coords.png'])
393def test_annotation_negative_ax_coords():
394    fig, ax = plt.subplots()
395
396    ax.annotate('+ pts',
397                xytext=[30, 20], textcoords='axes points',
398                xy=[30, 20], xycoords='axes points', fontsize=32)
399    ax.annotate('- pts',
400                xytext=[30, -20], textcoords='axes points',
401                xy=[30, -20], xycoords='axes points', fontsize=32,
402                va='top')
403    ax.annotate('+ frac',
404                xytext=[0.75, 0.05], textcoords='axes fraction',
405                xy=[0.75, 0.05], xycoords='axes fraction', fontsize=32)
406    ax.annotate('- frac',
407                xytext=[0.75, -0.05], textcoords='axes fraction',
408                xy=[0.75, -0.05], xycoords='axes fraction', fontsize=32,
409                va='top')
410
411    ax.annotate('+ pixels',
412                xytext=[160, 25], textcoords='axes pixels',
413                xy=[160, 25], xycoords='axes pixels', fontsize=32)
414    ax.annotate('- pixels',
415                xytext=[160, -25], textcoords='axes pixels',
416                xy=[160, -25], xycoords='axes pixels', fontsize=32,
417                va='top')
418
419
420@image_comparison(['annotation_negative_fig_coords.png'])
421def test_annotation_negative_fig_coords():
422    fig, ax = plt.subplots()
423
424    ax.annotate('+ pts',
425                xytext=[10, 120], textcoords='figure points',
426                xy=[10, 120], xycoords='figure points', fontsize=32)
427    ax.annotate('- pts',
428                xytext=[-10, 180], textcoords='figure points',
429                xy=[-10, 180], xycoords='figure points', fontsize=32,
430                va='top')
431    ax.annotate('+ frac',
432                xytext=[0.05, 0.55], textcoords='figure fraction',
433                xy=[0.05, 0.55], xycoords='figure fraction', fontsize=32)
434    ax.annotate('- frac',
435                xytext=[-0.05, 0.5], textcoords='figure fraction',
436                xy=[-0.05, 0.5], xycoords='figure fraction', fontsize=32,
437                va='top')
438
439    ax.annotate('+ pixels',
440                xytext=[50, 50], textcoords='figure pixels',
441                xy=[50, 50], xycoords='figure pixels', fontsize=32)
442    ax.annotate('- pixels',
443                xytext=[-50, 100], textcoords='figure pixels',
444                xy=[-50, 100], xycoords='figure pixels', fontsize=32,
445                va='top')
446
447
448def test_text_stale():
449    fig, (ax1, ax2) = plt.subplots(1, 2)
450    plt.draw_all()
451    assert not ax1.stale
452    assert not ax2.stale
453    assert not fig.stale
454
455    txt1 = ax1.text(.5, .5, 'aardvark')
456    assert ax1.stale
457    assert txt1.stale
458    assert fig.stale
459
460    ann1 = ax2.annotate('aardvark', xy=[.5, .5])
461    assert ax2.stale
462    assert ann1.stale
463    assert fig.stale
464
465    plt.draw_all()
466    assert not ax1.stale
467    assert not ax2.stale
468    assert not fig.stale
469
470
471@image_comparison(['agg_text_clip.png'])
472def test_agg_text_clip():
473    np.random.seed(1)
474    fig, (ax1, ax2) = plt.subplots(2)
475    for x, y in np.random.rand(10, 2):
476        ax1.text(x, y, "foo", clip_on=True)
477        ax2.text(x, y, "foo")
478
479
480def test_text_size_binding():
481    mpl.rcParams['font.size'] = 10
482    fp = mpl.font_manager.FontProperties(size='large')
483    sz1 = fp.get_size_in_points()
484    mpl.rcParams['font.size'] = 100
485
486    assert sz1 == fp.get_size_in_points()
487
488
489@image_comparison(['font_scaling.pdf'])
490def test_font_scaling():
491    mpl.rcParams['pdf.fonttype'] = 42
492    fig, ax = plt.subplots(figsize=(6.4, 12.4))
493    ax.xaxis.set_major_locator(plt.NullLocator())
494    ax.yaxis.set_major_locator(plt.NullLocator())
495    ax.set_ylim(-10, 600)
496
497    for i, fs in enumerate(range(4, 43, 2)):
498        ax.text(0.1, i*30, "{fs} pt font size".format(fs=fs), fontsize=fs)
499
500
501@pytest.mark.parametrize('spacing1, spacing2', [(0.4, 2), (2, 0.4), (2, 2)])
502def test_two_2line_texts(spacing1, spacing2):
503    text_string = 'line1\nline2'
504    fig = plt.figure()
505    renderer = fig.canvas.get_renderer()
506
507    text1 = plt.text(0.25, 0.5, text_string, linespacing=spacing1)
508    text2 = plt.text(0.25, 0.5, text_string, linespacing=spacing2)
509    fig.canvas.draw()
510
511    box1 = text1.get_window_extent(renderer=renderer)
512    box2 = text2.get_window_extent(renderer=renderer)
513
514    # line spacing only affects height
515    assert box1.width == box2.width
516    if spacing1 == spacing2:
517        assert box1.height == box2.height
518    else:
519        assert box1.height != box2.height
520
521
522def test_nonfinite_pos():
523    fig, ax = plt.subplots()
524    ax.text(0, np.nan, 'nan')
525    ax.text(np.inf, 0, 'inf')
526    fig.canvas.draw()
527
528
529def test_hinting_factor_backends():
530    plt.rcParams['text.hinting_factor'] = 1
531    fig = plt.figure()
532    t = fig.text(0.5, 0.5, 'some text')
533
534    fig.savefig(io.BytesIO(), format='svg')
535    expected = t.get_window_extent().intervalx
536
537    fig.savefig(io.BytesIO(), format='png')
538    # Backends should apply hinting_factor consistently (within 10%).
539    np.testing.assert_allclose(t.get_window_extent().intervalx, expected,
540                               rtol=0.1)
541
542
543@needs_usetex
544def test_usetex_is_copied():
545    # Indirectly tests that update_from (which is used to copy tick label
546    # properties) copies usetex state.
547    fig = plt.figure()
548    plt.rcParams["text.usetex"] = False
549    ax1 = fig.add_subplot(121)
550    plt.rcParams["text.usetex"] = True
551    ax2 = fig.add_subplot(122)
552    fig.canvas.draw()
553    for ax, usetex in [(ax1, False), (ax2, True)]:
554        for t in ax.xaxis.majorTicks:
555            assert t.label1.get_usetex() == usetex
556
557
558@needs_usetex
559def test_single_artist_usetex():
560    # Check that a single artist marked with usetex does not get passed through
561    # the mathtext parser at all (for the Agg backend) (the mathtext parser
562    # currently fails to parse \frac12, requiring \frac{1}{2} instead).
563    fig = plt.figure()
564    fig.text(.5, .5, r"$\frac12$", usetex=True)
565    fig.canvas.draw()
566
567
568@pytest.mark.parametrize("fmt", ["png", "pdf", "svg"])
569def test_single_artist_usenotex(fmt):
570    # Check that a single artist can be marked as not-usetex even though the
571    # rcParam is on ("2_2_2" fails if passed to TeX).  This currently skips
572    # postscript output as the ps renderer doesn't support mixing usetex and
573    # non-usetex.
574    plt.rcParams["text.usetex"] = True
575    fig = plt.figure()
576    fig.text(.5, .5, "2_2_2", usetex=False)
577    fig.savefig(io.BytesIO(), format=fmt)
578
579
580@image_comparison(['text_as_path_opacity.svg'])
581def test_text_as_path_opacity():
582    plt.figure()
583    plt.gca().set_axis_off()
584    plt.text(0.25, 0.25, 'c', color=(0, 0, 0, 0.5))
585    plt.text(0.25, 0.5, 'a', alpha=0.5)
586    plt.text(0.25, 0.75, 'x', alpha=0.5, color=(0, 0, 0, 1))
587
588
589@image_comparison(['text_as_text_opacity.svg'])
590def test_text_as_text_opacity():
591    mpl.rcParams['svg.fonttype'] = 'none'
592    plt.figure()
593    plt.gca().set_axis_off()
594    plt.text(0.25, 0.25, '50% using `color`', color=(0, 0, 0, 0.5))
595    plt.text(0.25, 0.5, '50% using `alpha`', alpha=0.5)
596    plt.text(0.25, 0.75, '50% using `alpha` and 100% `color`', alpha=0.5,
597             color=(0, 0, 0, 1))
598
599
600def test_text_repr():
601    # smoketest to make sure text repr doesn't error for category
602    plt.plot(['A', 'B'], [1, 2])
603    repr(plt.text(['A'], 0.5, 'Boo'))
604
605
606def test_annotation_update():
607    fig, ax = plt.subplots(1, 1)
608    an = ax.annotate('annotation', xy=(0.5, 0.5))
609    extent1 = an.get_window_extent(fig.canvas.get_renderer())
610    fig.tight_layout()
611    extent2 = an.get_window_extent(fig.canvas.get_renderer())
612
613    assert not np.allclose(extent1.get_points(), extent2.get_points(),
614                           rtol=1e-6)
615
616
617@check_figures_equal(extensions=["png"])
618def test_annotation_units(fig_test, fig_ref):
619    ax = fig_test.add_subplot()
620    ax.plot(datetime.now(), 1, "o")  # Implicitly set axes extents.
621    ax.annotate("x", (datetime.now(), 0.5), xycoords=("data", "axes fraction"),
622                # This used to crash before.
623                xytext=(0, 0), textcoords="offset points")
624    ax = fig_ref.add_subplot()
625    ax.plot(datetime.now(), 1, "o")
626    ax.annotate("x", (datetime.now(), 0.5), xycoords=("data", "axes fraction"))
627
628
629@image_comparison(['large_subscript_title.png'], style='mpl20')
630def test_large_subscript_title():
631    # Remove this line when this test image is regenerated.
632    plt.rcParams['text.kerning_factor'] = 6
633    plt.rcParams['axes.titley'] = None
634
635    fig, axs = plt.subplots(1, 2, figsize=(9, 2.5), constrained_layout=True)
636    ax = axs[0]
637    ax.set_title(r'$\sum_{i} x_i$')
638    ax.set_title('New way', loc='left')
639    ax.set_xticklabels('')
640
641    ax = axs[1]
642    ax.set_title(r'$\sum_{i} x_i$', y=1.01)
643    ax.set_title('Old Way', loc='left')
644    ax.set_xticklabels('')
645
646
647def test_wrap():
648    fig = plt.figure(figsize=(6, 4))
649    s = 'This is a very long text that should be wrapped multiple times.'
650    text = fig.text(0.7, 0.5, s, wrap=True)
651    fig.canvas.draw()
652    assert text._get_wrapped_text() == ('This is a very long\n'
653                                        'text that should be\n'
654                                        'wrapped multiple\n'
655                                        'times.')
656
657
658def test_long_word_wrap():
659    fig = plt.figure(figsize=(6, 4))
660    text = fig.text(9.5, 8, 'Alonglineoftexttowrap', wrap=True)
661    fig.canvas.draw()
662    assert text._get_wrapped_text() == 'Alonglineoftexttowrap'
663
664
665def test_wrap_no_wrap():
666    fig = plt.figure(figsize=(6, 4))
667    text = fig.text(0, 0, 'non wrapped text', wrap=True)
668    fig.canvas.draw()
669    assert text._get_wrapped_text() == 'non wrapped text'
670
671
672@check_figures_equal(extensions=["png"])
673def test_buffer_size(fig_test, fig_ref):
674    # On old versions of the Agg renderer, large non-ascii single-character
675    # strings (here, "€") would be rendered clipped because the rendering
676    # buffer would be set by the physical size of the smaller "a" character.
677    ax = fig_test.add_subplot()
678    ax.set_yticks([0, 1])
679    ax.set_yticklabels(["€", "a"])
680    ax.yaxis.majorTicks[1].label1.set_color("w")
681    ax = fig_ref.add_subplot()
682    ax.set_yticks([0, 1])
683    ax.set_yticklabels(["€", ""])
684
685
686def test_fontproperties_kwarg_precedence():
687    """Test that kwargs take precedence over fontproperties defaults."""
688    plt.figure()
689    text1 = plt.xlabel("value", fontproperties='Times New Roman', size=40.0)
690    text2 = plt.ylabel("counts", size=40.0, fontproperties='Times New Roman')
691    assert text1.get_size() == 40.0
692    assert text2.get_size() == 40.0
693
694
695def test_transform_rotates_text():
696    ax = plt.gca()
697    transform = mtransforms.Affine2D().rotate_deg(30)
698    text = ax.text(0, 0, 'test', transform=transform,
699                   transform_rotates_text=True)
700    result = text.get_rotation()
701    assert_almost_equal(result, 30)
702
703
704def test_update_mutate_input():
705    inp = dict(fontproperties=FontProperties(weight="bold"),
706               bbox=None)
707    cache = dict(inp)
708    t = Text()
709    t.update(inp)
710    assert inp['fontproperties'] == cache['fontproperties']
711    assert inp['bbox'] == cache['bbox']
712
713
714def test_invalid_color():
715    with pytest.raises(ValueError):
716        plt.figtext(.5, .5, "foo", c="foobar")
717
718
719@image_comparison(['text_pdf_kerning.pdf'], style='mpl20')
720def test_pdf_kerning():
721    plt.figure()
722    plt.figtext(0.1, 0.5, "ATATATATATATATATATA", size=30)
723