Skip to content

Commit 7bf1a87

Browse files
authored
Merge pull request #7354 from radarhere/load_default
Added TrueType default font to allow for different sizes
2 parents e154e97 + c2d5088 commit 7bf1a87

9 files changed

+431
-61
lines changed
3.58 KB
Loading
1.95 KB
Loading

Tests/test_imagedraw.py

+29-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import contextlib
12
import os.path
23

34
import pytest
45

5-
from PIL import Image, ImageColor, ImageDraw, ImageFont
6+
from PIL import Image, ImageColor, ImageDraw, ImageFont, features
67

78
from .helper import (
89
assert_image_equal,
@@ -1353,7 +1354,33 @@ def test_setting_default_font():
13531354
assert draw.getfont() == font
13541355
finally:
13551356
ImageDraw.ImageDraw.font = None
1356-
assert isinstance(draw.getfont(), ImageFont.ImageFont)
1357+
assert isinstance(draw.getfont(), ImageFont.load_default().__class__)
1358+
1359+
1360+
def test_default_font_size():
1361+
freetype_support = features.check_module("freetype2")
1362+
text = "Default font at a specific size."
1363+
1364+
im = Image.new("RGB", (220, 25))
1365+
draw = ImageDraw.Draw(im)
1366+
with contextlib.nullcontext() if freetype_support else pytest.raises(ImportError):
1367+
draw.text((0, 0), text, font_size=16)
1368+
assert_image_equal_tofile(im, "Tests/images/imagedraw_default_font_size.png")
1369+
1370+
with contextlib.nullcontext() if freetype_support else pytest.raises(ImportError):
1371+
assert draw.textlength(text, font_size=16) == 216
1372+
1373+
with contextlib.nullcontext() if freetype_support else pytest.raises(ImportError):
1374+
assert draw.textbbox((0, 0), text, font_size=16) == (0, 3, 216, 19)
1375+
1376+
im = Image.new("RGB", (220, 25))
1377+
draw = ImageDraw.Draw(im)
1378+
with contextlib.nullcontext() if freetype_support else pytest.raises(ImportError):
1379+
draw.multiline_text((0, 0), text, font_size=16)
1380+
assert_image_equal_tofile(im, "Tests/images/imagedraw_default_font_size.png")
1381+
1382+
with contextlib.nullcontext() if freetype_support else pytest.raises(ImportError):
1383+
assert draw.multiline_textbbox((0, 0), text, font_size=16) == (0, 3, 216, 19)
13571384

13581385

13591386
@pytest.mark.parametrize("bbox", BBOX)

Tests/test_imagefont.py

+5-18
Original file line numberDiff line numberDiff line change
@@ -453,16 +453,19 @@ def test_load_non_font_bytes():
453453

454454
def test_default_font():
455455
# Arrange
456-
txt = 'This is a "better than nothing" default font.'
456+
txt = "This is a default font using FreeType support."
457457
im = Image.new(mode="RGB", size=(300, 100))
458458
draw = ImageDraw.Draw(im)
459459

460460
# Act
461461
default_font = ImageFont.load_default()
462462
draw.text((10, 10), txt, font=default_font)
463463

464+
larger_default_font = ImageFont.load_default(size=14)
465+
draw.text((10, 60), txt, font=larger_default_font)
466+
464467
# Assert
465-
assert_image_equal_tofile(im, "Tests/images/default_font.png")
468+
assert_image_equal_tofile(im, "Tests/images/default_font_freetype.png")
466469

467470

468471
@pytest.mark.parametrize("mode", (None, "1", "RGBA"))
@@ -485,14 +488,6 @@ def test_render_empty(font):
485488
assert_image_equal(im, target)
486489

487490

488-
def test_unicode_pilfont():
489-
# should not segfault, should return UnicodeDecodeError
490-
# issue #2826
491-
font = ImageFont.load_default()
492-
with pytest.raises(UnicodeEncodeError):
493-
font.getbbox("’")
494-
495-
496491
def test_unicode_extended(layout_engine):
497492
# issue #3777
498493
text = "A\u278A\U0001F12B"
@@ -722,14 +717,6 @@ def test_variation_set_by_axes(font):
722717
_check_text(font, "Tests/images/variation_tiny_axes.png", 32.5)
723718

724719

725-
def test_textbbox_non_freetypefont():
726-
im = Image.new("RGB", (200, 200))
727-
d = ImageDraw.Draw(im)
728-
default_font = ImageFont.load_default()
729-
assert d.textlength("test", font=default_font) == 24
730-
assert d.textbbox((0, 0), "test", font=default_font) == (0, 0, 24, 11)
731-
732-
733720
@pytest.mark.parametrize(
734721
"anchor, left, top",
735722
(

Tests/test_imagefontpil.py

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import pytest
2+
3+
from PIL import Image, ImageDraw, ImageFont, features
4+
5+
from .helper import assert_image_equal_tofile
6+
7+
pytestmark = pytest.mark.skipif(
8+
features.check_module("freetype2"),
9+
reason="PILfont superseded if FreeType is supported",
10+
)
11+
12+
13+
def test_default_font():
14+
# Arrange
15+
txt = 'This is a "better than nothing" default font.'
16+
im = Image.new(mode="RGB", size=(300, 100))
17+
draw = ImageDraw.Draw(im)
18+
19+
# Act
20+
default_font = ImageFont.load_default()
21+
draw.text((10, 10), txt, font=default_font)
22+
23+
# Assert
24+
assert_image_equal_tofile(im, "Tests/images/default_font.png")
25+
26+
27+
def test_size_without_freetype():
28+
with pytest.raises(ImportError):
29+
ImageFont.load_default(size=14)
30+
31+
32+
def test_unicode():
33+
# should not segfault, should return UnicodeDecodeError
34+
# issue #2826
35+
font = ImageFont.load_default()
36+
with pytest.raises(UnicodeEncodeError):
37+
font.getbbox("’")
38+
39+
40+
def test_textbbox():
41+
im = Image.new("RGB", (200, 200))
42+
d = ImageDraw.Draw(im)
43+
default_font = ImageFont.load_default()
44+
assert d.textlength("test", font=default_font) == 24
45+
assert d.textbbox((0, 0), "test", font=default_font) == (0, 0, 24, 11)

docs/reference/ImageDraw.rst

+35-5
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ Methods
351351

352352
Draw a shape.
353353

354-
.. py:method:: ImageDraw.text(xy, text, fill=None, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, stroke_fill=None, embedded_color=False)
354+
.. py:method:: ImageDraw.text(xy, text, fill=None, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, stroke_fill=None, embedded_color=False, font_size=None)
355355
356356
Draws the string at the given position.
357357

@@ -416,8 +416,14 @@ Methods
416416

417417
.. versionadded:: 8.0.0
418418

419+
:param font_size: If ``font`` is not provided, then the size to use for the default
420+
font.
421+
Keyword-only argument.
419422

420-
.. py:method:: ImageDraw.multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, stroke_fill=None, embedded_color=False)
423+
.. versionadded:: 10.1.0
424+
425+
426+
.. py:method:: ImageDraw.multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, stroke_fill=None, embedded_color=False, font_size=None)
421427
422428
Draws the string at the given position.
423429

@@ -477,7 +483,13 @@ Methods
477483

478484
.. versionadded:: 8.0.0
479485

480-
.. py:method:: ImageDraw.textlength(text, font=None, direction=None, features=None, language=None, embedded_color=False)
486+
:param font_size: If ``font`` is not provided, then the size to use for the default
487+
font.
488+
Keyword-only argument.
489+
490+
.. versionadded:: 10.1.0
491+
492+
.. py:method:: ImageDraw.textlength(text, font=None, direction=None, features=None, language=None, embedded_color=False, font_size=None)
481493
482494
Returns length (in pixels with 1/64 precision) of given text when rendered
483495
in font with provided direction, features, and language.
@@ -538,9 +550,15 @@ Methods
538550
It should be a `BCP 47 language code`_.
539551
Requires libraqm.
540552
:param embedded_color: Whether to use font embedded color glyphs (COLR, CBDT, SBIX).
553+
:param font_size: If ``font`` is not provided, then the size to use for the default
554+
font.
555+
Keyword-only argument.
556+
557+
.. versionadded:: 10.1.0
558+
541559
:return: Either width for horizontal text, or height for vertical text.
542560

543-
.. py:method:: ImageDraw.textbbox(xy, text, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, embedded_color=False)
561+
.. py:method:: ImageDraw.textbbox(xy, text, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, embedded_color=False, font_size=None)
544562
545563
Returns bounding box (in pixels) of given text relative to given anchor
546564
when rendered in font with provided direction, features, and language.
@@ -588,9 +606,15 @@ Methods
588606
Requires libraqm.
589607
:param stroke_width: The width of the text stroke.
590608
:param embedded_color: Whether to use font embedded color glyphs (COLR, CBDT, SBIX).
609+
:param font_size: If ``font`` is not provided, then the size to use for the default
610+
font.
611+
Keyword-only argument.
612+
613+
.. versionadded:: 10.1.0
614+
591615
:return: ``(left, top, right, bottom)`` bounding box
592616

593-
.. py:method:: ImageDraw.multiline_textbbox(xy, text, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, embedded_color=False)
617+
.. py:method:: ImageDraw.multiline_textbbox(xy, text, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, embedded_color=False, font_size=None)
594618
595619
Returns bounding box (in pixels) of given text relative to given anchor
596620
when rendered in font with provided direction, features, and language.
@@ -632,6 +656,12 @@ Methods
632656
Requires libraqm.
633657
:param stroke_width: The width of the text stroke.
634658
:param embedded_color: Whether to use font embedded color glyphs (COLR, CBDT, SBIX).
659+
:param font_size: If ``font`` is not provided, then the size to use for the default
660+
font.
661+
Keyword-only argument.
662+
663+
.. versionadded:: 10.1.0
664+
635665
:return: ``(left, top, right, bottom)`` bounding box
636666

637667
.. py:method:: getdraw(im=None, hints=None)

docs/releasenotes/10.1.0.rst

+27-9
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,6 @@ to be specified, rather than a single number for both dimensions. ::
4141
API Additions
4242
=============
4343

44-
ImageOps.cover
45-
^^^^^^^^^^^^^^
46-
47-
Returns a resized version of the image, so that the requested size is covered,
48-
while maintaining the original aspect ratio.
49-
50-
See :ref:`relative-resize` for a comparison between this and similar ``ImageOps``
51-
methods.
52-
5344
EpsImagePlugin.gs_binary
5445
^^^^^^^^^^^^^^^^^^^^^^^^
5546

@@ -69,6 +60,33 @@ channel, a palette with an alpha channel, or a "transparency" key in the
6960
Even if this attribute is true, the image might still appear solid, if all of
7061
the values shown within are opaque.
7162

63+
ImageOps.cover
64+
^^^^^^^^^^^^^^
65+
66+
Returns a resized version of the image, so that the requested size is covered,
67+
while maintaining the original aspect ratio.
68+
69+
See :ref:`relative-resize` for a comparison between this and similar ``ImageOps``
70+
methods.
71+
72+
size and font_size arguments when using default font
73+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
74+
75+
Pillow has had a "better than nothing" default font, which can only be drawn at
76+
one font size. Now, if FreeType support is available, a version of
77+
`Aileron Regular <https://dotcolon.net/font/aileron>`_ is loaded, which can be
78+
drawn at chosen font sizes.
79+
80+
The following ``size`` and ``font_size`` arguments can now be used to specify a
81+
font size for this new builtin font::
82+
83+
ImageFont.load_default(size=24)
84+
draw.text((0, 0), "test", font_size=24)
85+
draw.textlength((0, 0), "test", font_size=24)
86+
draw.textbbox((0, 0), "test", font_size=24)
87+
draw.multiline_text((0, 0), "test", font_size=24)
88+
draw.multiline_textbbox((0, 0), "test", font_size=24)
89+
7290
Other Changes
7391
=============
7492

0 commit comments

Comments
 (0)