diff --git a/Tests/fonts/LICENSE.txt b/Tests/fonts/LICENSE.txt index b9488cb94..528eed9ef 100644 --- a/Tests/fonts/LICENSE.txt +++ b/Tests/fonts/LICENSE.txt @@ -1,5 +1,6 @@ NotoNastaliqUrdu-Regular.ttf and NotoSansSymbols-Regular.ttf, from https://github.com/googlei18n/noto-fonts +NotoSans-Regular.ttf, from https://www.google.com/get/noto/ NotoSansJP-Thin.otf, from https://www.google.com/get/noto/help/cjk/ AdobeVFPrototype.ttf, from https://github.com/adobe-fonts/adobe-variable-font-prototype TINY5x3GX.ttf, from http://velvetyne.fr/fonts/tiny diff --git a/Tests/fonts/NotoSans-Regular.ttf b/Tests/fonts/NotoSans-Regular.ttf new file mode 100644 index 000000000..a1b8994ed Binary files /dev/null and b/Tests/fonts/NotoSans-Regular.ttf differ diff --git a/Tests/images/test_anchor_quick_ls.png b/Tests/images/test_anchor_quick_ls.png new file mode 100644 index 000000000..524c417c3 Binary files /dev/null and b/Tests/images/test_anchor_quick_ls.png differ diff --git a/Tests/images/test_anchor_quick_ma.png b/Tests/images/test_anchor_quick_ma.png new file mode 100644 index 000000000..cfff27f7d Binary files /dev/null and b/Tests/images/test_anchor_quick_ma.png differ diff --git a/Tests/images/test_anchor_quick_mb.png b/Tests/images/test_anchor_quick_mb.png new file mode 100644 index 000000000..ff11f478e Binary files /dev/null and b/Tests/images/test_anchor_quick_mb.png differ diff --git a/Tests/images/test_anchor_quick_md.png b/Tests/images/test_anchor_quick_md.png new file mode 100644 index 000000000..5cbccb170 Binary files /dev/null and b/Tests/images/test_anchor_quick_md.png differ diff --git a/Tests/images/test_anchor_quick_mm.png b/Tests/images/test_anchor_quick_mm.png new file mode 100644 index 000000000..500294c3b Binary files /dev/null and b/Tests/images/test_anchor_quick_mm.png differ diff --git a/Tests/images/test_anchor_quick_ms.png b/Tests/images/test_anchor_quick_ms.png new file mode 100644 index 000000000..b1012463e Binary files /dev/null and b/Tests/images/test_anchor_quick_ms.png differ diff --git a/Tests/images/test_anchor_quick_mt.png b/Tests/images/test_anchor_quick_mt.png new file mode 100644 index 000000000..19423e51a Binary files /dev/null and b/Tests/images/test_anchor_quick_mt.png differ diff --git a/Tests/images/test_anchor_quick_rs.png b/Tests/images/test_anchor_quick_rs.png new file mode 100644 index 000000000..20a5e6c6e Binary files /dev/null and b/Tests/images/test_anchor_quick_rs.png differ diff --git a/Tests/images/test_anchor_ttb_f_lt.png b/Tests/images/test_anchor_ttb_f_lt.png new file mode 100644 index 000000000..5f70a65c4 Binary files /dev/null and b/Tests/images/test_anchor_ttb_f_lt.png differ diff --git a/Tests/images/test_anchor_ttb_f_mm.png b/Tests/images/test_anchor_ttb_f_mm.png new file mode 100644 index 000000000..e7be557d2 Binary files /dev/null and b/Tests/images/test_anchor_ttb_f_mm.png differ diff --git a/Tests/images/test_anchor_ttb_f_rb.png b/Tests/images/test_anchor_ttb_f_rb.png new file mode 100644 index 000000000..b78e2f954 Binary files /dev/null and b/Tests/images/test_anchor_ttb_f_rb.png differ diff --git a/Tests/images/test_anchor_ttb_f_sm.png b/Tests/images/test_anchor_ttb_f_sm.png new file mode 100644 index 000000000..f6dc7c70f Binary files /dev/null and b/Tests/images/test_anchor_ttb_f_sm.png differ diff --git a/Tests/images/test_combine_caron.png b/Tests/images/test_combine_caron.png new file mode 100644 index 000000000..1097f4be5 Binary files /dev/null and b/Tests/images/test_combine_caron.png differ diff --git a/Tests/images/test_combine_caron_below.png b/Tests/images/test_combine_caron_below.png new file mode 100644 index 000000000..6e7d88a92 Binary files /dev/null and b/Tests/images/test_combine_caron_below.png differ diff --git a/Tests/images/test_combine_caron_below_lb.png b/Tests/images/test_combine_caron_below_lb.png new file mode 100644 index 000000000..f59e722b2 Binary files /dev/null and b/Tests/images/test_combine_caron_below_lb.png differ diff --git a/Tests/images/test_combine_caron_below_ld.png b/Tests/images/test_combine_caron_below_ld.png new file mode 100644 index 000000000..540ab7d42 Binary files /dev/null and b/Tests/images/test_combine_caron_below_ld.png differ diff --git a/Tests/images/test_combine_caron_below_ls.png b/Tests/images/test_combine_caron_below_ls.png new file mode 100644 index 000000000..1109b4ee6 Binary files /dev/null and b/Tests/images/test_combine_caron_below_ls.png differ diff --git a/Tests/images/test_combine_caron_below_ttb.png b/Tests/images/test_combine_caron_below_ttb.png new file mode 100644 index 000000000..5c7576de0 Binary files /dev/null and b/Tests/images/test_combine_caron_below_ttb.png differ diff --git a/Tests/images/test_combine_caron_below_ttb_lb.png b/Tests/images/test_combine_caron_below_ttb_lb.png new file mode 100644 index 000000000..bacd6a141 Binary files /dev/null and b/Tests/images/test_combine_caron_below_ttb_lb.png differ diff --git a/Tests/images/test_combine_caron_la.png b/Tests/images/test_combine_caron_la.png new file mode 100644 index 000000000..1097f4be5 Binary files /dev/null and b/Tests/images/test_combine_caron_la.png differ diff --git a/Tests/images/test_combine_caron_ls.png b/Tests/images/test_combine_caron_ls.png new file mode 100644 index 000000000..1a721873c Binary files /dev/null and b/Tests/images/test_combine_caron_ls.png differ diff --git a/Tests/images/test_combine_caron_lt.png b/Tests/images/test_combine_caron_lt.png new file mode 100644 index 000000000..91e50d45f Binary files /dev/null and b/Tests/images/test_combine_caron_lt.png differ diff --git a/Tests/images/test_combine_caron_ttb.png b/Tests/images/test_combine_caron_ttb.png new file mode 100644 index 000000000..a94be2f0a Binary files /dev/null and b/Tests/images/test_combine_caron_ttb.png differ diff --git a/Tests/images/test_combine_caron_ttb_lt.png b/Tests/images/test_combine_caron_ttb_lt.png new file mode 100644 index 000000000..a94be2f0a Binary files /dev/null and b/Tests/images/test_combine_caron_ttb_lt.png differ diff --git a/Tests/images/test_combine_double_breve_below.png b/Tests/images/test_combine_double_breve_below.png new file mode 100644 index 000000000..30252107f Binary files /dev/null and b/Tests/images/test_combine_double_breve_below.png differ diff --git a/Tests/images/test_combine_double_breve_below_ma.png b/Tests/images/test_combine_double_breve_below_ma.png new file mode 100644 index 000000000..aea09538f Binary files /dev/null and b/Tests/images/test_combine_double_breve_below_ma.png differ diff --git a/Tests/images/test_combine_double_breve_below_ra.png b/Tests/images/test_combine_double_breve_below_ra.png new file mode 100644 index 000000000..febd3ab67 Binary files /dev/null and b/Tests/images/test_combine_double_breve_below_ra.png differ diff --git a/Tests/images/test_combine_double_breve_below_ttb.png b/Tests/images/test_combine_double_breve_below_ttb.png new file mode 100644 index 000000000..8e42c0d16 Binary files /dev/null and b/Tests/images/test_combine_double_breve_below_ttb.png differ diff --git a/Tests/images/test_combine_double_breve_below_ttb_mt.png b/Tests/images/test_combine_double_breve_below_ttb_mt.png new file mode 100644 index 000000000..9a755e8f8 Binary files /dev/null and b/Tests/images/test_combine_double_breve_below_ttb_mt.png differ diff --git a/Tests/images/test_combine_double_breve_below_ttb_rt.png b/Tests/images/test_combine_double_breve_below_ttb_rt.png new file mode 100644 index 000000000..b954a5415 Binary files /dev/null and b/Tests/images/test_combine_double_breve_below_ttb_rt.png differ diff --git a/Tests/images/test_combine_double_breve_below_ttb_st.png b/Tests/images/test_combine_double_breve_below_ttb_st.png new file mode 100644 index 000000000..b6b08145e Binary files /dev/null and b/Tests/images/test_combine_double_breve_below_ttb_st.png differ diff --git a/Tests/images/test_combine_overline.png b/Tests/images/test_combine_overline.png new file mode 100644 index 000000000..dc5e86361 Binary files /dev/null and b/Tests/images/test_combine_overline.png differ diff --git a/Tests/images/test_combine_overline_la.png b/Tests/images/test_combine_overline_la.png new file mode 100644 index 000000000..dc5e86361 Binary files /dev/null and b/Tests/images/test_combine_overline_la.png differ diff --git a/Tests/images/test_combine_overline_ra.png b/Tests/images/test_combine_overline_ra.png new file mode 100644 index 000000000..cbb2d472d Binary files /dev/null and b/Tests/images/test_combine_overline_ra.png differ diff --git a/Tests/images/test_combine_overline_ttb.png b/Tests/images/test_combine_overline_ttb.png new file mode 100644 index 000000000..f74538d9c Binary files /dev/null and b/Tests/images/test_combine_overline_ttb.png differ diff --git a/Tests/images/test_combine_overline_ttb_mt.png b/Tests/images/test_combine_overline_ttb_mt.png new file mode 100644 index 000000000..e915543d6 Binary files /dev/null and b/Tests/images/test_combine_overline_ttb_mt.png differ diff --git a/Tests/images/test_combine_overline_ttb_rt.png b/Tests/images/test_combine_overline_ttb_rt.png new file mode 100644 index 000000000..186d6ee84 Binary files /dev/null and b/Tests/images/test_combine_overline_ttb_rt.png differ diff --git a/Tests/images/test_combine_overline_ttb_st.png b/Tests/images/test_combine_overline_ttb_st.png new file mode 100644 index 000000000..e915543d6 Binary files /dev/null and b/Tests/images/test_combine_overline_ttb_st.png differ diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 0f9f20eb1..121fc13a5 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -1020,7 +1020,7 @@ def test_stroke(): font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 120) # Act - draw.text((10, 10), "A", "#f00", font, stroke_width=2, stroke_fill=stroke_fill) + draw.text((12, 12), "A", "#f00", font, stroke_width=2, stroke_fill=stroke_fill) # Assert assert_image_similar_tofile( @@ -1036,7 +1036,7 @@ def test_stroke_descender(): font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 120) # Act - draw.text((10, 0), "y", "#f00", font, stroke_width=2, stroke_fill="#0f0") + draw.text((12, 2), "y", "#f00", font, stroke_width=2, stroke_fill="#0f0") # Assert assert_image_similar_tofile(im, "Tests/images/imagedraw_stroke_descender.png", 6.76) @@ -1051,7 +1051,7 @@ def test_stroke_multiline(): # Act draw.multiline_text( - (10, 10), "A\nB", "#f00", font, stroke_width=2, stroke_fill="#0f0" + (12, 12), "A\nB", "#f00", font, stroke_width=2, stroke_fill="#0f0" ) # Assert diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 3e9a7f1cf..ca2dc1210 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -750,6 +750,54 @@ class TestImageFont: font.set_variation_by_axes([100]) self._check_text(font, "Tests/images/variation_tiny_axes.png", 32.5) + @pytest.mark.parametrize( + "anchor", + ( + # test horizontal anchors + "ls", + "ms", + "rs", + # test vertical anchors + "ma", + "mt", + "mm", + "mb", + "md", + ), + ) + def test_anchor(self, anchor): + name, text = "quick", "Quick" + path = "Tests/images/test_anchor_%s_%s.png" % (name, anchor) + f = ImageFont.truetype( + "Tests/fonts/NotoSans-Regular.ttf", 48, layout_engine=self.LAYOUT_ENGINE + ) + + im = Image.new("RGB", (200, 200), "white") + d = ImageDraw.Draw(im) + d.line(((0, 100), (200, 100)), "gray") + d.line(((100, 0), (100, 200)), "gray") + d.text((100, 100), text, fill="black", anchor=anchor, font=f) + + with Image.open(path) as expected: + assert_image_similar(im, expected, 7) + + def test_anchor_invalid(self): + font = self.get_font() + im = Image.new("RGB", (100, 100), "white") + d = ImageDraw.Draw(im) + d.font = font + + for anchor in ["", "l", "a", "lax", "sa", "xa", "lx"]: + pytest.raises(ValueError, lambda: font.getmask2("hello", anchor=anchor)) + pytest.raises(ValueError, lambda: d.text((0, 0), "hello", anchor=anchor)) + pytest.raises( + ValueError, lambda: d.multiline_text((0, 0), "foo\nbar", anchor=anchor) + ) + for anchor in ["lt", "lb"]: + pytest.raises( + ValueError, lambda: d.multiline_text((0, 0), "foo\nbar", anchor=anchor) + ) + @skip_unless_feature("raqm") class TestImageFont_RaqmLayout(TestImageFont): diff --git a/Tests/test_imagefontctl.py b/Tests/test_imagefontctl.py index edd6fab57..7fceecfb9 100644 --- a/Tests/test_imagefontctl.py +++ b/Tests/test_imagefontctl.py @@ -1,6 +1,7 @@ import pytest +from packaging.version import parse as parse_version -from PIL import Image, ImageDraw, ImageFont +from PIL import Image, ImageDraw, ImageFont, features from .helper import assert_image_similar, skip_unless_feature @@ -123,7 +124,7 @@ def test_text_direction_ttb_stroke(): draw = ImageDraw.Draw(im) try: draw.text( - (25, 25), + (27, 27), "あい", font=ttf, fill=500, @@ -206,3 +207,118 @@ def test_language(): target = "Tests/images/test_language.png" with Image.open(target) as target_img: assert_image_similar(im, target_img, 0.5) + + +@pytest.mark.parametrize("anchor", ("lt", "mm", "rb", "sm")) +def test_anchor_ttb(anchor): + if parse_version(features.version_module("freetype2")) < parse_version("2.5.1"): + # FreeType 2.5.1 README: Miscellaneous Changes: + # Improved computation of emulated vertical metrics for TrueType fonts. + pytest.skip("FreeType <2.5.1 has incompatible ttb metrics") + + text = "f" + path = "Tests/images/test_anchor_ttb_%s_%s.png" % (text, anchor) + f = ImageFont.truetype("Tests/fonts/NotoSans-Regular.ttf", 120) + + im = Image.new("RGB", (200, 400), "white") + d = ImageDraw.Draw(im) + d.line(((0, 200), (200, 200)), "gray") + d.line(((100, 0), (100, 400)), "gray") + try: + d.text((100, 200), text, fill="black", anchor=anchor, direction="ttb", font=f) + except ValueError as ex: + if str(ex) == "libraqm 0.7 or greater required for 'ttb' direction": + pytest.skip("libraqm 0.7 or greater not available") + + with Image.open(path) as expected: + assert_image_similar(im, expected, 1) # fails at 5 + + +combine_tests = ( + # extends above (e.g. issue #4553) + ("caron", "a\u030C\u030C\u030C\u030C\u030Cb", None, None, 0.08), + ("caron_la", "a\u030C\u030C\u030C\u030C\u030Cb", "la", None, 0.08), + ("caron_lt", "a\u030C\u030C\u030C\u030C\u030Cb", "lt", None, 0.08), + ("caron_ls", "a\u030C\u030C\u030C\u030C\u030Cb", "ls", None, 0.08), + ("caron_ttb", "ca" + ("\u030C" * 15) + "b", None, "ttb", 0.3), + ("caron_ttb_lt", "ca" + ("\u030C" * 15) + "b", "lt", "ttb", 0.3), + # extends below + ("caron_below", "a\u032C\u032C\u032C\u032C\u032Cb", None, None, 0.02), + ("caron_below_ld", "a\u032C\u032C\u032C\u032C\u032Cb", "ld", None, 0.02), + ("caron_below_lb", "a\u032C\u032C\u032C\u032C\u032Cb", "lb", None, 0.02), + ("caron_below_ls", "a\u032C\u032C\u032C\u032C\u032Cb", "ls", None, 0.02), + ("caron_below_ttb", "a" + ("\u032C" * 15) + "b", None, "ttb", 0.03), + ("caron_below_ttb_lb", "a" + ("\u032C" * 15) + "b", "lb", "ttb", 0.03), + # extends to the right (e.g. issue #3745) + ("double_breve_below", "a\u035Ci", None, None, 0.02), + ("double_breve_below_ma", "a\u035Ci", "ma", None, 0.02), + ("double_breve_below_ra", "a\u035Ci", "ra", None, 0.02), + ("double_breve_below_ttb", "a\u035Cb", None, "ttb", 0.02), + ("double_breve_below_ttb_rt", "a\u035Cb", "rt", "ttb", 0.02), + ("double_breve_below_ttb_mt", "a\u035Cb", "mt", "ttb", 0.02), + ("double_breve_below_ttb_st", "a\u035Cb", "st", "ttb", 0.02), + # extends to the left (fail=0.064) + ("overline", "i\u0305", None, None, 0.02), + ("overline_la", "i\u0305", "la", None, 0.02), + ("overline_ra", "i\u0305", "ra", None, 0.02), + ("overline_ttb", "i\u0305", None, "ttb", 0.02), + ("overline_ttb_rt", "i\u0305", "rt", "ttb", 0.02), + ("overline_ttb_mt", "i\u0305", "mt", "ttb", 0.02), + ("overline_ttb_st", "i\u0305", "st", "ttb", 0.02), +) + + +# this tests various combining characters for anchor alignment and clipping +@pytest.mark.parametrize( + "name,text,anchor,dir,epsilon", combine_tests, ids=[r[0] for r in combine_tests] +) +def test_combine(name, text, dir, anchor, epsilon): + if ( + parse_version(features.version_module("freetype2")) < parse_version("2.5.1") + and dir == "ttb" + ): + # FreeType 2.5.1 README: Miscellaneous Changes: + # Improved computation of emulated vertical metrics for TrueType fonts. + pytest.skip("FreeType <2.5.1 has incompatible ttb metrics") + + path = "Tests/images/test_combine_%s.png" % name + f = ImageFont.truetype("Tests/fonts/NotoSans-Regular.ttf", 48) + + im = Image.new("RGB", (400, 400), "white") + d = ImageDraw.Draw(im) + d.line(((0, 200), (400, 200)), "gray") + d.line(((200, 0), (200, 400)), "gray") + try: + d.text((200, 200), text, fill="black", anchor=anchor, direction=dir, font=f) + except ValueError as ex: + if str(ex) == "libraqm 0.7 or greater required for 'ttb' direction": + pytest.skip("libraqm 0.7 or greater not available") + + with Image.open(path) as expected: + assert_image_similar(im, expected, epsilon) + + +def test_anchor_invalid_ttb(): + font = ImageFont.truetype(FONT_PATH, FONT_SIZE) + im = Image.new("RGB", (100, 100), "white") + d = ImageDraw.Draw(im) + d.font = font + + for anchor in ["", "l", "a", "lax", "xa", "la", "ls", "ld", "lx"]: + pytest.raises( + ValueError, lambda: font.getmask2("hello", anchor=anchor, direction="ttb") + ) + pytest.raises( + ValueError, lambda: d.text((0, 0), "hello", anchor=anchor, direction="ttb") + ) + pytest.raises( + ValueError, + lambda: d.multiline_text( + (0, 0), "foo\nbar", anchor=anchor, direction="ttb" + ), + ) + # ttb multiline text does not support anchors at all + pytest.raises( + ValueError, + lambda: d.multiline_text((0, 0), "foo\nbar", anchor="mm", direction="ttb"), + )