Merge pull request #6381 from nulano/deprecate-getsize

Deprecate ImageFont.getsize and related functions
This commit is contained in:
mergify[bot] 2022-07-01 13:10:14 +00:00 committed by GitHub
commit 488589b4b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 408 additions and 100 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -33,9 +33,9 @@ def fuzz_font(data):
# different font objects.
return
font.getsize_multiline("ABC\nAaaa")
font.getbbox("ABC")
font.getmask("test text")
with Image.new(mode="RGBA", size=(200, 200)) as im:
draw = ImageDraw.Draw(im)
draw.multiline_textsize("ABC\nAaaa", font, stroke_width=2)
draw.multiline_textbbox((10, 10), "ABC\nAaaa", font, stroke_width=2)
draw.text((10, 10), "Test Text", font=font, fill="#000")

View File

@ -76,12 +76,19 @@ def test_textsize(request, tmp_path):
tempname = save_font(request, tmp_path)
font = ImageFont.load(tempname)
for i in range(255):
(dx, dy) = font.getsize(chr(i))
(ox, oy, dx, dy) = font.getbbox(chr(i))
assert ox == 0
assert oy == 0
assert dy == 20
assert dx in (0, 10)
assert font.getlength(chr(i)) == dx
with pytest.warns(DeprecationWarning) as log:
assert font.getsize(chr(i)) == (dx, dy)
assert len(log) == 1
for i in range(len(message)):
msg = message[: i + 1]
assert font.getsize(msg) == (len(msg) * 10, 20)
assert font.getlength(msg) == len(msg) * 10
assert font.getbbox(msg) == (0, 0, len(msg) * 10, 20)
def _test_high_characters(request, tmp_path, message):

View File

@ -101,13 +101,17 @@ def _test_textsize(request, tmp_path, encoding):
tempname = save_font(request, tmp_path, encoding)
font = ImageFont.load(tempname)
for i in range(255):
(dx, dy) = font.getsize(bytearray([i]))
(ox, oy, dx, dy) = font.getbbox(bytearray([i]))
assert ox == 0
assert oy == 0
assert dy == 20
assert dx in (0, 10)
assert font.getlength(bytearray([i])) == dx
message = charsets[encoding]["message"].encode(encoding)
for i in range(len(message)):
msg = message[: i + 1]
assert font.getsize(msg) == (len(msg) * 10, 20)
assert font.getlength(msg) == len(msg) * 10
assert font.getbbox(msg) == (0, 0, len(msg) * 10, 20)
def test_textsize_iso8859_1(request, tmp_path):

View File

@ -1232,21 +1232,39 @@ def test_textsize_empty_string():
# Act
# Should not cause 'SystemError: <built-in method getsize of
# ImagingFont object at 0x...> returned NULL without setting an error'
draw.textsize("")
draw.textsize("\n")
draw.textsize("test\n")
draw.textbbox((0, 0), "")
draw.textbbox((0, 0), "\n")
draw.textbbox((0, 0), "test\n")
draw.textlength("")
@skip_unless_feature("freetype2")
def test_textsize_stroke():
def test_textbbox_stroke():
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 20)
# Act / Assert
assert draw.textsize("A", font, stroke_width=2) == (16, 20)
assert draw.multiline_textsize("ABC\nAaaa", font, stroke_width=2) == (52, 44)
assert draw.textbbox((2, 2), "A", font, stroke_width=2) == (0, 4, 16, 20)
assert draw.textbbox((2, 2), "A", font, stroke_width=4) == (-2, 2, 18, 22)
assert draw.textbbox((2, 2), "ABC\nAaaa", font, stroke_width=2) == (0, 4, 52, 44)
assert draw.textbbox((2, 2), "ABC\nAaaa", font, stroke_width=4) == (-2, 2, 54, 50)
def test_textsize_deprecation():
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
with pytest.warns(DeprecationWarning) as log:
draw.textsize("Hello")
assert len(log) == 1
with pytest.warns(DeprecationWarning) as log:
draw.textsize("Hello\nWorld")
assert len(log) == 1
with pytest.warns(DeprecationWarning) as log:
draw.multiline_textsize("Hello\nWorld")
assert len(log) == 1
@skip_unless_feature("freetype2")

View File

@ -1,5 +1,7 @@
import os.path
import pytest
from PIL import Image, ImageDraw, ImageDraw2
from .helper import (
@ -205,7 +207,9 @@ def test_textsize():
font = ImageDraw2.Font("white", FONT_PATH)
# Act
size = draw.textsize("ImageDraw2", font)
with pytest.warns(DeprecationWarning) as log:
size = draw.textsize("ImageDraw2", font)
assert len(log) == 1
# Assert
assert size[1] == 12
@ -221,9 +225,10 @@ def test_textsize_empty_string():
# Act
# Should not cause 'SystemError: <built-in method getsize of
# ImagingFont object at 0x...> returned NULL without setting an error'
draw.textsize("", font)
draw.textsize("\n", font)
draw.textsize("test\n", font)
draw.textbbox((0, 0), "", font)
draw.textbbox((0, 0), "\n", font)
draw.textbbox((0, 0), "test\n", font)
draw.textlength("", font)
@skip_unless_feature("freetype2")

View File

@ -94,7 +94,7 @@ class TestImageFont:
def _render(self, font):
txt = "Hello World!"
ttf = ImageFont.truetype(font, FONT_SIZE, layout_engine=self.LAYOUT_ENGINE)
ttf.getsize(txt)
ttf.getbbox(txt)
img = Image.new("RGB", (256, 64), "white")
d = ImageDraw.Draw(img)
@ -135,15 +135,15 @@ class TestImageFont:
target = "Tests/images/transparent_background_text_L.png"
assert_image_similar_tofile(im.convert("L"), target, 0.01)
def test_textsize_equal(self):
def test_textbbox_equal(self):
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
ttf = self.get_font()
txt = "Hello World!"
size = draw.textsize(txt, ttf)
bbox = draw.textbbox((10, 10), txt, ttf)
draw.text((10, 10), txt, font=ttf)
draw.rectangle((10, 10, 10 + size[0], 10 + size[1]))
draw.rectangle(bbox)
assert_image_similar_tofile(
im, "Tests/images/rectangle_surrounding_text.png", 2.5
@ -184,7 +184,7 @@ class TestImageFont:
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
ttf = self.get_font()
line_spacing = draw.textsize("A", font=ttf)[1] + 4
line_spacing = ttf.getbbox("A")[3] + 4
lines = TEST_TEXT.split("\n")
y = 0
for line in lines:
@ -245,19 +245,39 @@ class TestImageFont:
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
# Test that textsize() correctly connects to multiline_textsize()
assert draw.textsize(TEST_TEXT, font=ttf) == draw.multiline_textsize(
TEST_TEXT, font=ttf
with pytest.warns(DeprecationWarning) as log:
# Test that textsize() correctly connects to multiline_textsize()
assert draw.textsize(TEST_TEXT, font=ttf) == draw.multiline_textsize(
TEST_TEXT, font=ttf
)
# Test that multiline_textsize corresponds to ImageFont.textsize()
# for single line text
assert ttf.getsize("A") == draw.multiline_textsize("A", font=ttf)
# Test that textsize() can pass on additional arguments
# to multiline_textsize()
draw.textsize(TEST_TEXT, font=ttf, spacing=4)
draw.textsize(TEST_TEXT, ttf, 4)
assert len(log) == 6
def test_multiline_bbox(self):
ttf = self.get_font()
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
# Test that textbbox() correctly connects to multiline_textbbox()
assert draw.textbbox((0, 0), TEST_TEXT, font=ttf) == draw.multiline_textbbox(
(0, 0), TEST_TEXT, font=ttf
)
# Test that multiline_textsize corresponds to ImageFont.textsize()
# Test that multiline_textbbox corresponds to ImageFont.textbbox()
# for single line text
assert ttf.getsize("A") == draw.multiline_textsize("A", font=ttf)
assert ttf.getbbox("A") == draw.multiline_textbbox((0, 0), "A", font=ttf)
# Test that textsize() can pass on additional arguments
# to multiline_textsize()
draw.textsize(TEST_TEXT, font=ttf, spacing=4)
draw.textsize(TEST_TEXT, ttf, 4)
# Test that textbbox() can pass on additional arguments
# to multiline_textbbox()
draw.textbbox((0, 0), TEST_TEXT, font=ttf, spacing=4)
def test_multiline_width(self):
ttf = self.get_font()
@ -265,9 +285,15 @@ class TestImageFont:
draw = ImageDraw.Draw(im)
assert (
draw.textsize("longest line", font=ttf)[0]
== draw.multiline_textsize("longest line\nline", font=ttf)[0]
draw.textbbox((0, 0), "longest line", font=ttf)[2]
== draw.multiline_textbbox((0, 0), "longest line\nline", font=ttf)[2]
)
with pytest.warns(DeprecationWarning) as log:
assert (
draw.textsize("longest line", font=ttf)[0]
== draw.multiline_textsize("longest line\nline", font=ttf)[0]
)
assert len(log) == 2
def test_multiline_spacing(self):
ttf = self.get_font()
@ -289,16 +315,33 @@ class TestImageFont:
# Original font
draw.font = font
box_size_a = draw.textsize(word)
with pytest.warns(DeprecationWarning) as log:
box_size_a = draw.textsize(word)
assert box_size_a == font.getsize(word)
assert len(log) == 2
bbox_a = draw.textbbox((10, 10), word)
# Rotated font
draw.font = transposed_font
box_size_b = draw.textsize(word)
with pytest.warns(DeprecationWarning) as log:
box_size_b = draw.textsize(word)
assert box_size_b == transposed_font.getsize(word)
assert len(log) == 2
bbox_b = draw.textbbox((20, 20), word)
# Check (w,h) of box a is (h,w) of box b
assert box_size_a[0] == box_size_b[1]
assert box_size_a[1] == box_size_b[0]
# Check bbox b is (20, 20, 20 + h, 20 + w)
assert bbox_b[0] == 20
assert bbox_b[1] == 20
assert bbox_b[2] == 20 + bbox_a[3] - bbox_a[1]
assert bbox_b[3] == 20 + bbox_a[2] - bbox_a[0]
# text length is undefined for vertical text
pytest.raises(ValueError, draw.textlength, word)
def test_unrotated_transposed_font(self):
img_grey = Image.new("L", (100, 100))
draw = ImageDraw.Draw(img_grey)
@ -310,15 +353,31 @@ class TestImageFont:
# Original font
draw.font = font
box_size_a = draw.textsize(word)
with pytest.warns(DeprecationWarning) as log:
box_size_a = draw.textsize(word)
assert len(log) == 1
bbox_a = draw.textbbox((10, 10), word)
length_a = draw.textlength(word)
# Rotated font
draw.font = transposed_font
box_size_b = draw.textsize(word)
with pytest.warns(DeprecationWarning) as log:
box_size_b = draw.textsize(word)
assert len(log) == 1
bbox_b = draw.textbbox((20, 20), word)
length_b = draw.textlength(word)
# Check boxes a and b are same size
assert box_size_a == box_size_b
# Check bbox b is (20, 20, 20 + w, 20 + h)
assert bbox_b[0] == 20
assert bbox_b[1] == 20
assert bbox_b[2] == 20 + bbox_a[2] - bbox_a[0]
assert bbox_b[3] == 20 + bbox_a[3] - bbox_a[1]
assert length_a == length_b
def test_rotated_transposed_font_get_mask(self):
# Arrange
text = "mask this"
@ -373,9 +432,11 @@ class TestImageFont:
text = "offset this"
# Act
offset = font.getoffset(text)
with pytest.warns(DeprecationWarning) as log:
offset = font.getoffset(text)
# Assert
assert len(log) == 1
assert offset == (0, 3)
def test_free_type_font_get_mask(self):
@ -417,11 +478,11 @@ class TestImageFont:
# Assert
assert_image_equal_tofile(im, "Tests/images/default_font.png")
def test_getsize_empty(self):
def test_getbbox_empty(self):
# issue #2614
font = self.get_font()
# should not crash.
assert (0, 0) == font.getsize("")
assert (0, 0, 0, 0) == font.getbbox("")
def test_render_empty(self):
# issue 2666
@ -438,7 +499,7 @@ class TestImageFont:
# issue #2826
font = ImageFont.load_default()
with pytest.raises(UnicodeEncodeError):
font.getsize("")
font.getbbox("")
def test_unicode_extended(self):
# issue #3777
@ -563,17 +624,29 @@ class TestImageFont:
assert t.font.x_ppem == 20
assert t.font.y_ppem == 20
assert t.font.glyphs == 4177
assert t.getsize("A") == (12, 16)
assert t.getsize("AB") == (24, 16)
assert t.getsize("M") == (12, 16)
assert t.getsize("y") == (12, 20)
assert t.getsize("a") == (12, 16)
assert t.getsize_multiline("A") == (12, 16)
assert t.getsize_multiline("AB") == (24, 16)
assert t.getsize_multiline("a") == (12, 16)
assert t.getsize_multiline("ABC\n") == (36, 36)
assert t.getsize_multiline("ABC\nA") == (36, 36)
assert t.getsize_multiline("ABC\nAaaa") == (48, 36)
assert t.getbbox("A") == (0, 4, 12, 16)
assert t.getbbox("AB") == (0, 4, 24, 16)
assert t.getbbox("M") == (0, 4, 12, 16)
assert t.getbbox("y") == (0, 7, 12, 20)
assert t.getbbox("a") == (0, 7, 12, 16)
assert t.getlength("A") == 12
assert t.getlength("AB") == 24
assert t.getlength("M") == 12
assert t.getlength("y") == 12
assert t.getlength("a") == 12
with pytest.warns(DeprecationWarning) as log:
assert t.getsize("A") == (12, 16)
assert t.getsize("AB") == (24, 16)
assert t.getsize("M") == (12, 16)
assert t.getsize("y") == (12, 20)
assert t.getsize("a") == (12, 16)
assert t.getsize_multiline("A") == (12, 16)
assert t.getsize_multiline("AB") == (24, 16)
assert t.getsize_multiline("a") == (12, 16)
assert t.getsize_multiline("ABC\n") == (36, 36)
assert t.getsize_multiline("ABC\nA") == (36, 36)
assert t.getsize_multiline("ABC\nAaaa") == (48, 36)
assert len(log) == 11
def test_getsize_stroke(self):
# Arrange
@ -581,14 +654,22 @@ class TestImageFont:
# Act / Assert
for stroke_width in [0, 2]:
assert t.getsize("A", stroke_width=stroke_width) == (
12 + stroke_width * 2,
16 + stroke_width * 2,
)
assert t.getsize_multiline("ABC\nAaaa", stroke_width=stroke_width) == (
48 + stroke_width * 2,
36 + stroke_width * 4,
assert t.getbbox("A", stroke_width=stroke_width) == (
0 - stroke_width,
4 - stroke_width,
12 + stroke_width,
16 + stroke_width,
)
with pytest.warns(DeprecationWarning) as log:
assert t.getsize("A", stroke_width=stroke_width) == (
12 + stroke_width * 2,
16 + stroke_width * 2,
)
assert t.getsize_multiline("ABC\nAaaa", stroke_width=stroke_width) == (
48 + stroke_width * 2,
36 + stroke_width * 4,
)
assert len(log) == 2
def test_complex_font_settings(self):
# Arrange
@ -720,8 +801,11 @@ class TestImageFont:
im = Image.new("RGB", (200, 200))
d = ImageDraw.Draw(im)
default_font = ImageFont.load_default()
with pytest.raises(ValueError):
d.textbbox((0, 0), "test", font=default_font)
with pytest.warns(DeprecationWarning) as log:
width, height = d.textsize("test", font=default_font)
assert len(log) == 1
assert d.textlength("test", font=default_font) == width
assert d.textbbox((0, 0), "test", font=default_font) == (0, 0, width, height)
@pytest.mark.parametrize(
"anchor, left, top",
@ -868,7 +952,7 @@ class TestImageFont:
def test_standard_embedded_color(self):
txt = "Hello World!"
ttf = ImageFont.truetype(FONT_PATH, 40, layout_engine=self.LAYOUT_ENGINE)
ttf.getsize(txt)
ttf.getbbox(txt)
im = Image.new("RGB", (300, 64), "white")
d = ImageDraw.Draw(im)

View File

@ -140,8 +140,8 @@ def test_ligature_features():
target = "Tests/images/test_ligature_features.png"
assert_image_similar_tofile(im, target, 0.5)
liga_size = ttf.getsize("fi", features=["-liga"])
assert liga_size == (13, 19)
liga_bbox = ttf.getbbox("fi", features=["-liga"])
assert liga_bbox == (0, 4, 13, 19)
def test_kerning_features():

View File

@ -178,6 +178,25 @@ Image.coerce_e
This undocumented method has been deprecated and will be removed in Pillow 10
(2023-07-01).
Font size and offset methods
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 9.2.0
Several functions for computing the size and offset of rendered text
have been deprecated and will be removed in Pillow 10 (2023-07-01):
=========================================================================== =============================================================================================================
Deprecated Use instead
=========================================================================== =============================================================================================================
:py:meth:`.FreeTypeFont.getsize` and :py:meth:`.FreeTypeFont.getoffset` :py:meth:`.FreeTypeFont.getbbox` and :py:meth:`.FreeTypeFont.getlength`
:py:meth:`.FreeTypeFont.getsize_multiline` :py:meth:`.ImageDraw.multiline_textbbox`
:py:meth:`.ImageFont.getsize` :py:meth:`.ImageFont.getbbox` and :py:meth:`.ImageFont.getlength`
:py:meth:`.TransposedFont.getsize` :py:meth:`.TransposedFont.getbbox` and :py:meth:`.TransposedFont.getlength`
:py:meth:`.ImageDraw.textsize` and :py:meth:`.ImageDraw.multiline_textsize` :py:meth:`.ImageDraw.textbbox`, :py:meth:`.ImageDraw.textlength` and :py:meth:`.ImageDraw.multiline_textbbox`
:py:meth:`.ImageDraw2.Draw.textsize` :py:meth:`.ImageDraw2.Draw.textbbox` and :py:meth:`.ImageDraw2.Draw.textlength`
=========================================================================== =============================================================================================================
Removed features
----------------

View File

@ -436,12 +436,14 @@ Methods
.. py:method:: ImageDraw.textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0)
Return the size of the given string, in pixels.
.. deprecated:: 9.2.0
Use :py:meth:`textlength()` to measure the offset of following text with
1/64 pixel precision.
Use :py:meth:`textbbox()` to get the exact bounding box based on an anchor.
Return the size of the given string, in pixels.
.. note:: For historical reasons this function measures text height from
the ascender line instead of the top, see :ref:`text-anchors`.
If you wish to measure text height from the top, it is recommended
@ -484,6 +486,10 @@ Methods
.. py:method:: ImageDraw.multiline_textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0)
.. deprecated:: 9.2.0
Use :py:meth:`.multiline_textbbox` instead.
Return the size of the given string, in pixels.
Use :py:meth:`textlength()` to measure the offset of following text with

View File

@ -56,6 +56,7 @@ Methods
.. autoclass:: PIL.ImageFont.TransposedFont
:members:
:undoc-members:
Constants
---------

View File

@ -40,6 +40,25 @@ Image.coerce_e
This undocumented method has been deprecated and will be removed in Pillow 10
(2023-07-01).
Font size and offset methods
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. deprecated:: 9.2.0
Several functions for computing the size and offset of rendered text
have been deprecated and will be removed in Pillow 10 (2023-07-01):
=========================================================================== =============================================================================================================
Deprecated Use instead
=========================================================================== =============================================================================================================
:py:meth:`.FreeTypeFont.getsize` and :py:meth:`.FreeTypeFont.getoffset` :py:meth:`.FreeTypeFont.getbbox` and :py:meth:`.FreeTypeFont.getlength`
:py:meth:`.FreeTypeFont.getsize_multiline` :py:meth:`.ImageDraw.multiline_textbbox`
:py:meth:`.ImageFont.getsize` :py:meth:`.ImageFont.getbbox` and :py:meth:`.ImageFont.getlength`
:py:meth:`.TransposedFont.getsize` :py:meth:`.TransposedFont.getbbox` and :py:meth:`.TransposedFont.getlength`
:py:meth:`.ImageDraw.textsize` and :py:meth:`.ImageDraw.multiline_textsize` :py:meth:`.ImageDraw.textbbox`, :py:meth:`.ImageDraw.textlength` and :py:meth:`.ImageDraw.multiline_textbbox`
:py:meth:`.ImageDraw2.Draw.textsize` :py:meth:`.ImageDraw2.Draw.textbbox` and :py:meth:`.ImageDraw2.Draw.textlength`
=========================================================================== =============================================================================================================
API Additions
=============

View File

@ -32,8 +32,10 @@
import math
import numbers
import warnings
from . import Image, ImageColor
from ._deprecate import deprecate
"""
A simple 2D drawing interface for PIL images.
@ -372,6 +374,19 @@ class ImageDraw:
return text.split(split_character)
def _multiline_spacing(self, font, spacing, stroke_width):
# this can be replaced with self.textbbox(...)[3] when textsize is removed
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
return (
self.textsize(
"A",
font=font,
stroke_width=stroke_width,
)[1]
+ spacing
)
def text(
self,
xy,
@ -511,9 +526,7 @@ class ImageDraw:
widths = []
max_width = 0
lines = self._multiline_split(text)
line_spacing = (
self.textsize("A", font=font, stroke_width=stroke_width)[1] + spacing
)
line_spacing = self._multiline_spacing(font, spacing, stroke_width)
for line in lines:
line_width = self.textlength(
line, font, direction=direction, features=features, language=language
@ -573,14 +586,31 @@ class ImageDraw:
stroke_width=0,
):
"""Get the size of a given string, in pixels."""
deprecate("textsize", 10, "textbbox or textlength")
if self._multiline_check(text):
return self.multiline_textsize(
text, font, spacing, direction, features, language, stroke_width
)
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
return self.multiline_textsize(
text,
font,
spacing,
direction,
features,
language,
stroke_width,
)
if font is None:
font = self.getfont()
return font.getsize(text, direction, features, language, stroke_width)
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
return font.getsize(
text,
direction,
features,
language,
stroke_width,
)
def multiline_textsize(
self,
@ -592,16 +622,23 @@ class ImageDraw:
language=None,
stroke_width=0,
):
deprecate("multiline_textsize", 10, "multiline_textbbox")
max_width = 0
lines = self._multiline_split(text)
line_spacing = (
self.textsize("A", font=font, stroke_width=stroke_width)[1] + spacing
)
for line in lines:
line_width, line_height = self.textsize(
line, font, spacing, direction, features, language, stroke_width
)
max_width = max(max_width, line_width)
line_spacing = self._multiline_spacing(font, spacing, stroke_width)
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
for line in lines:
line_width, line_height = self.textsize(
line,
font,
spacing,
direction,
features,
language,
stroke_width,
)
max_width = max(max_width, line_width)
return max_width, len(lines) * line_spacing - spacing
def textlength(
@ -625,9 +662,16 @@ class ImageDraw:
try:
return font.getlength(text, mode, direction, features, language)
except AttributeError:
size = self.textsize(
text, font, direction=direction, features=features, language=language
)
deprecate("textlength support for fonts without getlength", 10)
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
size = self.textsize(
text,
font,
direction=direction,
features=features,
language=language,
)
if direction == "ttb":
return size[1]
return size[0]
@ -667,10 +711,6 @@ class ImageDraw:
if font is None:
font = self.getfont()
from . import ImageFont
if not isinstance(font, ImageFont.FreeTypeFont):
raise ValueError("Only supported for TrueType fonts")
mode = "RGBA" if embedded_color else self.fontmode
bbox = font.getbbox(
text, mode, direction, features, language, stroke_width, anchor
@ -704,9 +744,7 @@ class ImageDraw:
widths = []
max_width = 0
lines = self._multiline_split(text)
line_spacing = (
self.textsize("A", font=font, stroke_width=stroke_width)[1] + spacing
)
line_spacing = self._multiline_spacing(font, spacing, stroke_width)
for line in lines:
line_width = self.textlength(
line,

View File

@ -24,7 +24,10 @@
"""
import warnings
from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath
from ._deprecate import deprecate
class Pen:
@ -172,8 +175,35 @@ class Draw:
def textsize(self, text, font):
"""
.. deprecated:: 9.2.0
Return the size of the given string, in pixels.
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textsize`
"""
return self.draw.textsize(text, font=font.font)
deprecate("textsize", 10, "textbbox or textlength")
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
return self.draw.textsize(text, font=font.font)
def textbbox(self, xy, text, font):
"""
Returns bounding box (in pixels) of given text.
:return: ``(left, top, right, bottom)`` bounding box
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textbbox`
"""
if self.transform:
xy = ImagePath.Path(xy)
xy.transform(self.transform)
return self.draw.textbbox(xy, text, font=font.font)
def textlength(self, text, font):
"""
Returns length (in pixels) of given text.
This is the amount by which following text should be offset.
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textlength`
"""
return self.draw.textlength(text, font=font.font)

View File

@ -137,12 +137,17 @@ class ImageFont:
def getsize(self, text, *args, **kwargs):
"""
.. deprecated:: 9.2.0
Use :py:meth:`.getbbox` or :py:meth:`.getlength` instead.
Returns width and height (in pixels) of given text.
:param text: Text to measure.
:return: (width, height)
"""
deprecate("getsize", 10, "getbbox or getlength")
return self.font.getsize(text)
def getmask(self, text, mode="", *args, **kwargs):
@ -165,6 +170,33 @@ class ImageFont:
"""
return self.font.getmask(text, mode)
def getbbox(self, text, *args, **kwargs):
"""
Returns bounding box (in pixels) of given text.
.. versionadded:: 9.2.0
:param text: Text to render.
:param mode: Used by some graphics drivers to indicate what mode the
driver prefers; if empty, the renderer may return either
mode. Note that the mode is always a string, to simplify
C-level implementations.
:return: ``(left, top, right, bottom)`` bounding box
"""
width, height = self.font.getsize(text)
return 0, 0, width, height
def getlength(self, text, *args, **kwargs):
"""
Returns length (in pixels) of given text.
This is the amount by which following text should be offset.
.. versionadded:: 9.2.0
"""
width, height = self.font.getsize(text)
return width
##
# Wrapper for FreeType fonts. Application code should use the
@ -386,16 +418,23 @@ class FreeTypeFont:
return left, top, left + width, top + height
def getsize(
self, text, direction=None, features=None, language=None, stroke_width=0
self,
text,
direction=None,
features=None,
language=None,
stroke_width=0,
):
"""
Returns width and height (in pixels) of given text if rendered in font with
provided direction, features, and language.
.. deprecated:: 9.2.0
Use :py:meth:`getlength()` to measure the offset of following text with
1/64 pixel precision.
Use :py:meth:`getbbox()` to get the exact bounding box based on an anchor.
Returns width and height (in pixels) of given text if rendered in font with
provided direction, features, and language.
.. note:: For historical reasons this function measures text height from
the ascender line instead of the top, see :ref:`text-anchors`.
If you wish to measure text height from the top, it is recommended
@ -438,6 +477,7 @@ class FreeTypeFont:
:return: (width, height)
"""
deprecate("getsize", 10, "getbbox or getlength")
# vertical offset is added for historical reasons
# see https://github.com/python-pillow/Pillow/pull/4910#discussion_r486682929
size, offset = self.font.getsize(text, "L", direction, features, language)
@ -456,6 +496,10 @@ class FreeTypeFont:
stroke_width=0,
):
"""
.. deprecated:: 9.2.0
Use :py:meth:`.ImageDraw.multiline_textbbox` instead.
Returns width and height (in pixels) of given text if rendered in font
with provided direction, features, and language, while respecting
newline characters.
@ -495,19 +539,26 @@ class FreeTypeFont:
:return: (width, height)
"""
deprecate("getsize_multiline", 10, "ImageDraw.multiline_textbbox")
max_width = 0
lines = self._multiline_split(text)
line_spacing = self.getsize("A", stroke_width=stroke_width)[1] + spacing
for line in lines:
line_width, line_height = self.getsize(
line, direction, features, language, stroke_width
)
max_width = max(max_width, line_width)
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
line_spacing = self.getsize("A", stroke_width=stroke_width)[1] + spacing
for line in lines:
line_width, line_height = self.getsize(
line, direction, features, language, stroke_width
)
max_width = max(max_width, line_width)
return max_width, len(lines) * line_spacing - spacing
def getoffset(self, text):
"""
.. deprecated:: 9.2.0
Use :py:meth:`.getbbox` instead.
Returns the offset of given text. This is the gap between the
starting coordinate and the first marking. Note that this gap is
included in the result of :py:func:`~PIL.ImageFont.FreeTypeFont.getsize`.
@ -516,6 +567,7 @@ class FreeTypeFont:
:return: A tuple of the x and y offset
"""
deprecate("getoffset", 10, "getbbox")
return self.font.getsize(text)[1]
def getmask(
@ -796,7 +848,15 @@ class TransposedFont:
self.orientation = orientation # any 'transpose' argument, or None
def getsize(self, text, *args, **kwargs):
w, h = self.font.getsize(text)
"""
.. deprecated:: 9.2.0
Use :py:meth:`.getbbox` or :py:meth:`.getlength` instead.
"""
deprecate("getsize", 10, "getbbox or getlength")
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
w, h = self.font.getsize(text)
if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270):
return h, w
return w, h
@ -807,6 +867,23 @@ class TransposedFont:
return im.transpose(self.orientation)
return im
def getbbox(self, text, *args, **kwargs):
# TransposedFont doesn't support getmask2, move top-left point to (0, 0)
# this has no effect on ImageFont and simulates anchor="lt" for FreeTypeFont
left, top, right, bottom = self.font.getbbox(text, *args, **kwargs)
width = right - left
height = bottom - top
if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270):
return 0, 0, height, width
return 0, 0, width, height
def getlength(self, text, *args, **kwargs):
if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270):
raise ValueError(
"text length is undefined for text rotated by 90 or 270 degrees"
)
return self.font.getlength(text, *args, **kwargs)
def load(filename):
"""