Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 121 additions & 1 deletion Tests/test_imagetext.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import pytest

from PIL import Image, ImageDraw, ImageFont, ImageText
from PIL import Image, ImageDraw, ImageFont, ImageText, features

from .helper import assert_image_similar_tofile, skip_unless_feature

Expand Down Expand Up @@ -81,3 +81,123 @@ def test_stroke() -> None:
assert_image_similar_tofile(
im, "Tests/images/imagedraw_stroke_" + suffix + ".png", 3.1
)


@pytest.mark.parametrize(
"data, width, expected",
(
("Hello World!", 100, "Hello World!"), # No wrap required
("Hello World!", 50, "Hello\nWorld!"), # Wrap word to a new line
# Keep multiple spaces within a line
("Keep multiple spaces", 90, "Keep multiple\nspaces"),
(" Keep\n leading space", 100, " Keep\n leading space"),
),
)
@pytest.mark.parametrize("string", (True, False))
def test_wrap(data: str, width: int, expected: str, string: bool) -> None:
if string:
text = ImageText.Text(data)
assert text.wrap(width) is None
assert text.text == expected
else:
text_bytes = ImageText.Text(data.encode())
assert text_bytes.wrap(width) is None
assert text_bytes.text == expected.encode()


def test_wrap_long_word() -> None:
text = ImageText.Text("Hello World!")
with pytest.raises(ValueError, match="Word does not fit within line"):
text.wrap(25)


def test_wrap_unsupported(font: ImageFont.FreeTypeFont) -> None:
transposed_font = ImageFont.TransposedFont(font)
text = ImageText.Text("Hello World!", transposed_font)
with pytest.raises(ValueError, match="TransposedFont not supported"):
text.wrap(50)

text = ImageText.Text("Hello World!", direction="ttb")
with pytest.raises(ValueError, match="Only ltr direction supported"):
text.wrap(50)


def test_wrap_height() -> None:
width = 50 if features.check_module("freetype2") else 60
text = ImageText.Text("Text does not fit within height")
wrapped = text.wrap(width, 25 if features.check_module("freetype2") else 40)
assert wrapped is not None
assert wrapped.text == " within height"
assert text.text == "Text does\nnot fit"

text = ImageText.Text("Text does not fit\nwithin height")
wrapped = text.wrap(width, 20)
assert wrapped is not None
assert wrapped.text == " not fit\nwithin height"
assert text.text == "Text does"

text = ImageText.Text("Text does not fit\n\nwithin height")
wrapped = text.wrap(width, 25 if features.check_module("freetype2") else 40)
assert wrapped is not None
assert wrapped.text == "\nwithin height"
assert text.text == "Text does\nnot fit"


def test_wrap_scaling_unsupported() -> None:
font = ImageFont.load_default_imagefont()
text = ImageText.Text("Hello World!", font)
with pytest.raises(ValueError, match="'scaling' only supports FreeTypeFont"):
text.wrap(50, scaling="shrink")

if features.check_module("freetype2"):
text = ImageText.Text("Hello World!")
with pytest.raises(ValueError, match="'scaling' requires 'height'"):
text.wrap(50, scaling="shrink")


@skip_unless_feature("freetype2")
def test_wrap_shrink() -> None:
# No scaling required
text = ImageText.Text("Hello World!")
assert isinstance(text.font, ImageFont.FreeTypeFont)
assert text.font.size == 10
assert text.wrap(50, 50, "shrink") is None
assert isinstance(text.font, ImageFont.FreeTypeFont)
assert text.font.size == 10

with pytest.raises(ValueError, match="Text could not be scaled"):
text.wrap(50, 15, ("shrink", 9))

assert text.wrap(50, 15, "shrink") is None
assert text.font.size == 8

text = ImageText.Text("Hello World!")
assert text.wrap(50, 15, ("shrink", 7)) is None
assert isinstance(text.font, ImageFont.FreeTypeFont)
assert text.font.size == 8


@skip_unless_feature("freetype2")
def test_wrap_grow() -> None:
# No scaling required
text = ImageText.Text("Hello World!")
assert isinstance(text.font, ImageFont.FreeTypeFont)
assert text.font.size == 10
assert text.wrap(58, 10, "grow") is None
assert isinstance(text.font, ImageFont.FreeTypeFont)
assert text.font.size == 10

with pytest.raises(ValueError, match="Text could not be scaled"):
text.wrap(50, 50, ("grow", 12))

assert text.wrap(50, 50, "grow") is None
assert text.font.size == 16

text = ImageText.Text("A\nB")
with pytest.raises(ValueError, match="Text could not be scaled"):
text.wrap(50, 10, "grow")

text = ImageText.Text("Hello World!")
assert text.wrap(50, 50, ("grow", 18)) is None
assert isinstance(text.font, ImageFont.FreeTypeFont)
assert text.font.size == 16
27 changes: 13 additions & 14 deletions src/PIL/ImageDraw.py
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ def draw_corners(pieslice: bool) -> None:
def text(
self,
xy: tuple[float, float],
text: AnyStr | ImageText.Text,
text: AnyStr | ImageText.Text[AnyStr],
fill: _Ink | None = None,
font: (
ImageFont.ImageFont
Expand Down Expand Up @@ -591,63 +591,62 @@ def getink(fill: _Ink | None) -> int:
else ink
)

for xy, anchor, line in image_text._split(xy, anchor, align):
for line in image_text._split(xy, anchor, align):

def draw_text(ink: int, stroke_width: float = 0) -> None:
mode = self.fontmode
if stroke_width == 0 and embedded_color:
mode = "RGBA"
coord = []
for i in range(2):
coord.append(int(xy[i]))
start = (math.modf(xy[0])[0], math.modf(xy[1])[0])
x = int(line.x)
y = int(line.y)
start = (math.modf(line.x)[0], math.modf(line.y)[0])
try:
mask, offset = image_text.font.getmask2( # type: ignore[union-attr,misc]
line,
line.text,
mode,
direction=direction,
features=features,
language=language,
stroke_width=stroke_width,
stroke_filled=True,
anchor=anchor,
anchor=line.anchor,
ink=ink,
start=start,
*args,
**kwargs,
)
coord = [coord[0] + offset[0], coord[1] + offset[1]]
x += offset[0]
y += offset[1]
except AttributeError:
try:
mask = image_text.font.getmask( # type: ignore[misc]
line,
line.text,
mode,
direction,
features,
language,
stroke_width,
anchor,
line.anchor,
ink,
start=start,
*args,
**kwargs,
)
except TypeError:
mask = image_text.font.getmask(line)
mask = image_text.font.getmask(line.text)
if mode == "RGBA":
# image_text.font.getmask2(mode="RGBA")
# returns color in RGB bands and mask in A
# extract mask and set text alpha
color, mask = mask, mask.getband(3)
ink_alpha = struct.pack("i", ink)[3]
color.fillband(3, ink_alpha)
x, y = coord
if self.im is not None:
self.im.paste(
color, (x, y, x + mask.size[0], y + mask.size[1]), mask
)
else:
self.draw.draw_bitmap(coord, mask, ink)
self.draw.draw_bitmap((x, y), mask, ink)

if stroke_ink is not None:
# Draw stroked text
Expand Down
Loading
Loading