Skip to content
Merged
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
72 changes: 72 additions & 0 deletions Tests/test_imagetext.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from __future__ import annotations

import pytest

from PIL import Image, ImageDraw, ImageFont, ImageText

from .helper import assert_image_similar_tofile, skip_unless_feature

FONT_PATH = "Tests/fonts/FreeMono.ttf"


@pytest.fixture(
scope="module",
params=[
pytest.param(ImageFont.Layout.BASIC),
pytest.param(ImageFont.Layout.RAQM, marks=skip_unless_feature("raqm")),
],
)
def layout_engine(request: pytest.FixtureRequest) -> ImageFont.Layout:
return request.param


@pytest.fixture(scope="module")
def font(layout_engine: ImageFont.Layout) -> ImageFont.FreeTypeFont:
return ImageFont.truetype(FONT_PATH, 20, layout_engine=layout_engine)


def test_get_length(font: ImageFont.FreeTypeFont) -> None:
assert ImageText.Text("A", font).get_length() == 12
assert ImageText.Text("AB", font).get_length() == 24
assert ImageText.Text("M", font).get_length() == 12
assert ImageText.Text("y", font).get_length() == 12
assert ImageText.Text("a", font).get_length() == 12


def test_get_bbox(font: ImageFont.FreeTypeFont) -> None:
assert ImageText.Text("A", font).get_bbox() == (0, 4, 12, 16)
assert ImageText.Text("AB", font).get_bbox() == (0, 4, 24, 16)
assert ImageText.Text("M", font).get_bbox() == (0, 4, 12, 16)
assert ImageText.Text("y", font).get_bbox() == (0, 7, 12, 20)
assert ImageText.Text("a", font).get_bbox() == (0, 7, 12, 16)


def test_standard_embedded_color(layout_engine: ImageFont.Layout) -> None:
font = ImageFont.truetype(FONT_PATH, 40, layout_engine=layout_engine)
text = ImageText.Text("Hello World!", font)
text.embed_color()

im = Image.new("RGB", (300, 64), "white")
draw = ImageDraw.Draw(im)
draw.text((10, 10), text, "#fa6")

assert_image_similar_tofile(im, "Tests/images/standard_embedded.png", 3.1)


@skip_unless_feature("freetype2")
def test_stroke() -> None:
for suffix, stroke_fill in {"same": None, "different": "#0f0"}.items():
# Arrange
im = Image.new("RGB", (120, 130))
draw = ImageDraw.Draw(im)
font = ImageFont.truetype(FONT_PATH, 120)
text = ImageText.Text("A", font)
text.stroke(2, stroke_fill)

# Act
draw.text((12, 12), text, "#f00")

# Assert
assert_image_similar_tofile(
im, "Tests/images/imagedraw_stroke_" + suffix + ".png", 3.1
)
4 changes: 4 additions & 0 deletions docs/reference/ImageDraw.rst
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,8 @@ Methods
hello_world = hello + world # kerning is disabled, no need to adjust
assert hello_world == draw.textlength("HelloWorld", font, features=["-kern"]) # True

.. seealso:: :py:meth:`PIL.ImageText.Text.get_length`

.. versionadded:: 8.0.0

:param text: Text to be measured. May not contain any newline characters.
Expand Down Expand Up @@ -683,6 +685,8 @@ Methods
1/64 pixel precision. The bounding box includes extra margins for
some fonts, e.g. italics or accents.

.. seealso:: :py:meth:`PIL.ImageText.Text.get_bbox`

.. versionadded:: 8.0.0

:param xy: The anchor coordinates of the text.
Expand Down
61 changes: 61 additions & 0 deletions docs/reference/ImageText.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
.. py:module:: PIL.ImageText
.. py:currentmodule:: PIL.ImageText

:py:mod:`~PIL.ImageText` module
===============================

The :py:mod:`~PIL.ImageText` module defines a :py:class:`~PIL.ImageText.Text` class.
Instances of this class provide a way to use fonts with text strings or bytes. The
result is a simple API to apply styling to pieces of text and measure or draw them.

Example
-------

::

from PIL import Image, ImageDraw, ImageFont, ImageText
font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 24)

text = ImageText.Text("Hello world", font)
text.embed_color()
text.stroke(2, "#0f0")

print(text.get_length()) # 154.0
print(text.get_bbox()) # (-2, 3, 156, 22)

im = Image.new("RGB", text.get_bbox()[2:])
d = ImageDraw.Draw(im)
d.text((0, 0), text, "#f00")

Comparison
----------

Without ``ImageText.Text``::

from PIL import Image, ImageDraw
im = Image.new(mode, size)
d = ImageDraw.Draw(im)

d.textlength(text, font, direction, features, language, embedded_color)
d.multiline_textbbox(xy, text, font, anchor, spacing, align, direction, features, language, stroke_width, embedded_color)
d.text(xy, text, fill, font, anchor, spacing, align, direction, features, language, stroke_width, stroke_fill, embedded_color)

With ``ImageText.Text``::

from PIL import ImageText
text = ImageText.Text(text, font, mode, spacing, direction, features, language)
text.embed_color()
text.stroke(stroke_width, stroke_fill)

text.get_length()
text.get_bbox(xy, anchor, align)

im = Image.new(mode, size)
d = ImageDraw.Draw(im)
d.text(xy, text, fill, anchor=anchor, align=align)

Methods
-------

.. autoclass:: PIL.ImageText.Text
:members:
1 change: 1 addition & 0 deletions docs/reference/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Reference
ImageSequence
ImageShow
ImageStat
ImageText
ImageTk
ImageTransform
ImageWin
Expand Down
33 changes: 33 additions & 0 deletions docs/releasenotes/12.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,39 @@ Image.alpha_composite: LA images

:py:meth:`~PIL.Image.alpha_composite` can now use LA images as well as RGBA.

API additions
=============

Added ImageText.Text
^^^^^^^^^^^^^^^^^^^^

:py:class:`PIL.ImageText.Text` has been added, as a simpler way to use fonts with text
strings or bytes.

Without ``ImageText.Text``::

from PIL import Image, ImageDraw
im = Image.new(mode, size)
d = ImageDraw.Draw(im)

d.textlength(text, font, direction, features, language, embedded_color)
d.multiline_textbbox(xy, text, font, anchor, spacing, align, direction, features, language, stroke_width, embedded_color)
d.text(xy, text, fill, font, anchor, spacing, align, direction, features, language, stroke_width, stroke_fill, embedded_color)

With ``ImageText.Text``::

from PIL import ImageText
text = ImageText.Text(text, font, mode, spacing, direction, features, language)
text.embed_color()
text.stroke(stroke_width, stroke_fill)

text.get_length()
text.get_bbox(xy, anchor, align)

im = Image.new(mode, size)
d = ImageDraw.Draw(im)
d.text(xy, text, fill, anchor=anchor, align=align)

Other changes
=============

Expand Down
Loading
Loading