From 83c19d8f3ef27245d79573d1e3b4d6a60042b2a7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 26 Jan 2025 13:38:27 +1100 Subject: [PATCH 1/4] Added missing anchor --- src/PIL/_typing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PIL/_typing.py b/src/PIL/_typing.py index 6aba6ebe9..f1889f07e 100644 --- a/src/PIL/_typing.py +++ b/src/PIL/_typing.py @@ -70,6 +70,7 @@ Anchor = Literal[ "rd", "sa", "st", + "sm", "ss", "sb", "sd", From dd7e092513eebbe552eb6f69dd4db607aa5fb674 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 26 Jan 2025 13:52:43 +1100 Subject: [PATCH 2/4] Use anchor and direction in ImageFont --- src/PIL/ImageFont.py | 16 ++++++++-------- src/PIL/_imagingft.pyi | 11 ++++++----- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index d8c265560..46a620271 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -37,7 +37,7 @@ from types import ModuleType from typing import IO, TYPE_CHECKING, Any, BinaryIO, TypedDict, cast from . import Image, features -from ._typing import StrOrBytesPath +from ._typing import Anchor, Direction, StrOrBytesPath from ._util import DeferredError, is_path if TYPE_CHECKING: @@ -313,7 +313,7 @@ class FreeTypeFont: self, text: str | bytes, mode: str = "", - direction: str | None = None, + direction: Direction | None = None, features: list[str] | None = None, language: str | None = None, ) -> float: @@ -392,11 +392,11 @@ class FreeTypeFont: self, text: str | bytes, mode: str = "", - direction: str | None = None, + direction: Direction | None = None, features: list[str] | None = None, language: str | None = None, stroke_width: float = 0, - anchor: str | None = None, + anchor: Anchor | None = None, ) -> tuple[float, float, float, float]: """ Returns bounding box (in pixels) of given text relative to given anchor @@ -458,11 +458,11 @@ class FreeTypeFont: self, text: str | bytes, mode: str = "", - direction: str | None = None, + direction: Direction | None = None, features: list[str] | None = None, language: str | None = None, stroke_width: float = 0, - anchor: str | None = None, + anchor: Anchor | None = None, ink: int = 0, start: tuple[float, float] | None = None, ) -> Image.core.ImagingCore: @@ -549,11 +549,11 @@ class FreeTypeFont: self, text: str | bytes, mode: str = "", - direction: str | None = None, + direction: Direction | None = None, features: list[str] | None = None, language: str | None = None, stroke_width: float = 0, - anchor: str | None = None, + anchor: Anchor | None = None, ink: int = 0, start: tuple[float, float] | None = None, *args: Any, diff --git a/src/PIL/_imagingft.pyi b/src/PIL/_imagingft.pyi index 9cc9822f5..a33df7621 100644 --- a/src/PIL/_imagingft.pyi +++ b/src/PIL/_imagingft.pyi @@ -1,6 +1,7 @@ from typing import Any, Callable from . import ImageFont, _imaging +from ._typing import Anchor, Direction class Font: @property @@ -24,11 +25,11 @@ class Font: string: str | bytes, fill: Callable[[int, int], _imaging.ImagingCore], mode: str, - dir: str | None, + dir: Direction | None, features: list[str] | None, lang: str | None, stroke_width: float, - anchor: str | None, + anchor: Anchor | None, foreground_ink_long: int, x_start: float, y_start: float, @@ -38,17 +39,17 @@ class Font: self, string: str | bytes | bytearray, mode: str, - dir: str | None, + dir: Direction | None, features: list[str] | None, lang: str | None, - anchor: str | None, + anchor: Anchor | None, /, ) -> tuple[tuple[int, int], tuple[int, int]]: ... def getlength( self, string: str | bytes, mode: str, - dir: str | None, + dir: Direction | None, features: list[str] | None, lang: str | None, /, From 0e0515ce956ee596e9313e82bb8f1088f13dc541 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 26 Jan 2025 14:19:03 +1100 Subject: [PATCH 3/4] Moved Align into ImageDraw --- Tests/test_imagefont.py | 30 ++++++++++++++++-------------- Tests/test_imagefontctl.py | 23 ++++++++++++----------- docs/example/anchors.py | 12 ++++++++---- src/PIL/ImageDraw.py | 4 +++- src/PIL/_typing.py | 2 -- 5 files changed, 39 insertions(+), 32 deletions(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 6a0a940b9..f195e9aaf 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -14,7 +14,7 @@ import pytest from packaging.version import parse as parse_version from PIL import Image, ImageDraw, ImageFont, features -from PIL._typing import StrOrBytesPath +from PIL._typing import Anchor, StrOrBytesPath from .helper import ( assert_image_equal, @@ -257,7 +257,7 @@ def test_render_multiline_text(font: ImageFont.FreeTypeFont) -> None: "align, ext", (("left", ""), ("center", "_center"), ("right", "_right")) ) def test_render_multiline_text_align( - font: ImageFont.FreeTypeFont, align: str, ext: str + font: ImageFont.FreeTypeFont, align: ImageDraw.Align, ext: str ) -> None: im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) @@ -272,7 +272,7 @@ def test_unknown_align(font: ImageFont.FreeTypeFont) -> None: # Act/Assert with pytest.raises(ValueError): - draw.multiline_text((0, 0), TEST_TEXT, font=font, align="unknown") + draw.multiline_text((0, 0), TEST_TEXT, font=font, align="unknown") # type: ignore[arg-type] def test_draw_align(font: ImageFont.FreeTypeFont) -> None: @@ -795,7 +795,7 @@ def test_variation_set_by_axes(font: ImageFont.FreeTypeFont) -> None: ids=("ls", "ms", "rs", "ma", "mt", "mm", "mb", "md"), ) def test_anchor( - layout_engine: ImageFont.Layout, anchor: str, left: int, top: int + layout_engine: ImageFont.Layout, anchor: Anchor, left: int, top: int ) -> None: name, text = "quick", "Quick" path = f"Tests/images/test_anchor_{name}_{anchor}.png" @@ -842,7 +842,7 @@ def test_anchor( ), ) def test_anchor_multiline( - layout_engine: ImageFont.Layout, anchor: str, align: str + layout_engine: ImageFont.Layout, anchor: Anchor, align: ImageDraw.Align ) -> None: target = f"Tests/images/test_anchor_multiline_{anchor}_{align}.png" text = "a\nlong\ntext sample" @@ -868,22 +868,24 @@ def test_anchor_invalid(font: ImageFont.FreeTypeFont) -> None: for anchor in ["", "l", "a", "lax", "sa", "xa", "lx"]: with pytest.raises(ValueError): - font.getmask2("hello", anchor=anchor) + font.getmask2("hello", anchor=anchor) # type: ignore[arg-type] with pytest.raises(ValueError): - font.getbbox("hello", anchor=anchor) + font.getbbox("hello", anchor=anchor) # type: ignore[arg-type] with pytest.raises(ValueError): - d.text((0, 0), "hello", anchor=anchor) + d.text((0, 0), "hello", anchor=anchor) # type: ignore[arg-type] with pytest.raises(ValueError): - d.textbbox((0, 0), "hello", anchor=anchor) + d.textbbox((0, 0), "hello", anchor=anchor) # type: ignore[arg-type] with pytest.raises(ValueError): - d.multiline_text((0, 0), "foo\nbar", anchor=anchor) + d.multiline_text((0, 0), "foo\nbar", anchor=anchor) # type: ignore[arg-type] with pytest.raises(ValueError): - d.multiline_textbbox((0, 0), "foo\nbar", anchor=anchor) - for anchor in ["lt", "lb"]: + d.multiline_textbbox((0, 0), "foo\nbar", anchor=anchor) # type: ignore[arg-type] + + anchors: list[Anchor] = ["lt", "lb"] + for anchor2 in anchors: with pytest.raises(ValueError): - d.multiline_text((0, 0), "foo\nbar", anchor=anchor) + d.multiline_text((0, 0), "foo\nbar", anchor=anchor2) with pytest.raises(ValueError): - d.multiline_textbbox((0, 0), "foo\nbar", anchor=anchor) + d.multiline_textbbox((0, 0), "foo\nbar", anchor=anchor2) @pytest.mark.parametrize("bpp", (1, 2, 4, 8)) diff --git a/Tests/test_imagefontctl.py b/Tests/test_imagefontctl.py index 24c7b871a..f6a551381 100644 --- a/Tests/test_imagefontctl.py +++ b/Tests/test_imagefontctl.py @@ -3,6 +3,7 @@ from __future__ import annotations import pytest from PIL import Image, ImageDraw, ImageFont +from PIL._typing import Anchor, Direction from .helper import assert_image_similar_tofile, skip_unless_feature @@ -216,7 +217,7 @@ def test_getlength( d = ImageDraw.Draw(im) try: - assert d.textlength(text, ttf, direction) == expected + assert d.textlength(text, ttf, direction) == expected # type: ignore[arg-type] except ValueError as ex: if ( direction == "ttb" @@ -232,7 +233,7 @@ def test_getlength( ("i" + ("\u030C" * 15) + "i", "i" + "\u032C" * 15 + "i", "\u035Cii", "i\u0305i"), ids=("caron-above", "caron-below", "double-breve", "overline"), ) -def test_getlength_combine(mode: str, direction: str, text: str) -> None: +def test_getlength_combine(mode: str, direction: Direction, text: str) -> None: if text == "i\u0305i" and direction == "ttb": pytest.skip("fails with this font") @@ -252,7 +253,7 @@ def test_getlength_combine(mode: str, direction: str, text: str) -> None: @pytest.mark.parametrize("anchor", ("lt", "mm", "rb", "sm")) -def test_anchor_ttb(anchor: str) -> None: +def test_anchor_ttb(anchor: Anchor) -> None: text = "f" path = f"Tests/images/test_anchor_ttb_{text}_{anchor}.png" f = ImageFont.truetype("Tests/fonts/NotoSans-Regular.ttf", 120) @@ -309,7 +310,7 @@ combine_tests = ( "name, text, anchor, dir, epsilon", combine_tests, ids=[r[0] for r in combine_tests] ) def test_combine( - name: str, text: str, dir: str | None, anchor: str | None, epsilon: float + name: str, text: str, anchor: Anchor | None, dir: Direction | None, epsilon: float ) -> None: path = f"Tests/images/test_combine_{name}.png" f = ImageFont.truetype("Tests/fonts/NotoSans-Regular.ttf", 48) @@ -341,7 +342,7 @@ def test_combine( ("rm", "right"), # pass with getsize ), ) -def test_combine_multiline(anchor: str, align: str) -> None: +def test_combine_multiline(anchor: Anchor, align: ImageDraw.Align) -> None: # test that multiline text uses getlength, not getsize or getbbox path = f"Tests/images/test_combine_multiline_{anchor}_{align}.png" @@ -367,17 +368,17 @@ def test_anchor_invalid_ttb() -> None: for anchor in ["", "l", "a", "lax", "xa", "la", "ls", "ld", "lx"]: with pytest.raises(ValueError): - font.getmask2("hello", anchor=anchor, direction="ttb") + font.getmask2("hello", anchor=anchor, direction="ttb") # type: ignore[arg-type] with pytest.raises(ValueError): - font.getbbox("hello", anchor=anchor, direction="ttb") + font.getbbox("hello", anchor=anchor, direction="ttb") # type: ignore[arg-type] with pytest.raises(ValueError): - d.text((0, 0), "hello", anchor=anchor, direction="ttb") + d.text((0, 0), "hello", anchor=anchor, direction="ttb") # type: ignore[arg-type] with pytest.raises(ValueError): - d.textbbox((0, 0), "hello", anchor=anchor, direction="ttb") + d.textbbox((0, 0), "hello", anchor=anchor, direction="ttb") # type: ignore[arg-type] with pytest.raises(ValueError): - d.multiline_text((0, 0), "foo\nbar", anchor=anchor, direction="ttb") + d.multiline_text((0, 0), "foo\nbar", anchor=anchor, direction="ttb") # type: ignore[arg-type] with pytest.raises(ValueError): - d.multiline_textbbox((0, 0), "foo\nbar", anchor=anchor, direction="ttb") + d.multiline_textbbox((0, 0), "foo\nbar", anchor=anchor, direction="ttb") # type: ignore[arg-type] # ttb multiline text does not support anchors at all with pytest.raises(ValueError): d.multiline_text((0, 0), "foo\nbar", anchor="mm", direction="ttb") diff --git a/docs/example/anchors.py b/docs/example/anchors.py index 2ee11103f..23a6e933a 100644 --- a/docs/example/anchors.py +++ b/docs/example/anchors.py @@ -1,11 +1,12 @@ from __future__ import annotations from PIL import Image, ImageDraw, ImageFont +from PIL._typing import Anchor font = ImageFont.truetype("Tests/fonts/NotoSans-Regular.ttf", 16) -def test(anchor: str) -> Image.Image: +def test(anchor: Anchor) -> Image.Image: im = Image.new("RGBA", (200, 100), "white") d = ImageDraw.Draw(im) d.line(((100, 0), (100, 100)), "gray") @@ -17,9 +18,12 @@ def test(anchor: str) -> Image.Image: if __name__ == "__main__": im = Image.new("RGBA", (600, 300), "white") d = ImageDraw.Draw(im) - for y, row in enumerate( - (("ma", "mt", "mm"), ("ms", "mb", "md"), ("ls", "ms", "rs")) - ): + anchors: list[list[Anchor]] = [ + ["ma", "mt", "mm"], + ["ms", "mb", "md"], + ["ls", "ms", "rs"], + ] + for y, row in enumerate(anchors): for x, anchor in enumerate(row): im.paste(test(anchor), (x * 200, y * 100)) if x != 0: diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 1012dcbc0..d574ce2ea 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -39,7 +39,7 @@ from typing import TYPE_CHECKING, Any, AnyStr, Callable, Literal, Union, cast from . import Image, ImageColor from ._deprecate import deprecate -from ._typing import Align, Anchor, Coords, Direction +from ._typing import Anchor, Coords, Direction # experimental access to the outline API Outline: Callable[[], Image.core._Outline] | None @@ -51,6 +51,8 @@ except AttributeError: if TYPE_CHECKING: from . import ImageDraw2, ImageFont + Align = Literal["left", "center", "right"] + _Ink = Union[float, tuple[int, ...], str] """ diff --git a/src/PIL/_typing.py b/src/PIL/_typing.py index f1889f07e..55af3819b 100644 --- a/src/PIL/_typing.py +++ b/src/PIL/_typing.py @@ -76,8 +76,6 @@ Anchor = Literal[ "sd", ] -Align = Literal["left", "center", "right"] - Direction = Literal["rtl", "ltr", "ttb"] __all__ = ["Buffer", "IntegralLike", "StrOrBytesPath", "SupportsRead", "TypeGuard"] From eed59263eb4dedb917de444717c3c154908f2582 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 26 Jan 2025 15:16:51 +1100 Subject: [PATCH 4/4] Moved Anchor and Direction into ImageFont --- Tests/test_imagefont.py | 8 ++++---- Tests/test_imagefontctl.py | 15 ++++++++++----- docs/example/anchors.py | 5 ++--- docs/reference/ImageFont.rst | 5 +++++ src/PIL/ImageDraw.py | 20 ++++++++++---------- src/PIL/ImageFont.py | 33 +++++++++++++++++++++++++++++++-- src/PIL/_imagingft.pyi | 11 +++++------ src/PIL/_typing.py | 31 +------------------------------ 8 files changed, 68 insertions(+), 60 deletions(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index f195e9aaf..75413aed8 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -14,7 +14,7 @@ import pytest from packaging.version import parse as parse_version from PIL import Image, ImageDraw, ImageFont, features -from PIL._typing import Anchor, StrOrBytesPath +from PIL._typing import StrOrBytesPath from .helper import ( assert_image_equal, @@ -795,7 +795,7 @@ def test_variation_set_by_axes(font: ImageFont.FreeTypeFont) -> None: ids=("ls", "ms", "rs", "ma", "mt", "mm", "mb", "md"), ) def test_anchor( - layout_engine: ImageFont.Layout, anchor: Anchor, left: int, top: int + layout_engine: ImageFont.Layout, anchor: ImageFont.Anchor, left: int, top: int ) -> None: name, text = "quick", "Quick" path = f"Tests/images/test_anchor_{name}_{anchor}.png" @@ -842,7 +842,7 @@ def test_anchor( ), ) def test_anchor_multiline( - layout_engine: ImageFont.Layout, anchor: Anchor, align: ImageDraw.Align + layout_engine: ImageFont.Layout, anchor: ImageFont.Anchor, align: ImageDraw.Align ) -> None: target = f"Tests/images/test_anchor_multiline_{anchor}_{align}.png" text = "a\nlong\ntext sample" @@ -880,7 +880,7 @@ def test_anchor_invalid(font: ImageFont.FreeTypeFont) -> None: with pytest.raises(ValueError): d.multiline_textbbox((0, 0), "foo\nbar", anchor=anchor) # type: ignore[arg-type] - anchors: list[Anchor] = ["lt", "lb"] + anchors: list[ImageFont.Anchor] = ["lt", "lb"] for anchor2 in anchors: with pytest.raises(ValueError): d.multiline_text((0, 0), "foo\nbar", anchor=anchor2) diff --git a/Tests/test_imagefontctl.py b/Tests/test_imagefontctl.py index f6a551381..a48cafbbf 100644 --- a/Tests/test_imagefontctl.py +++ b/Tests/test_imagefontctl.py @@ -3,7 +3,6 @@ from __future__ import annotations import pytest from PIL import Image, ImageDraw, ImageFont -from PIL._typing import Anchor, Direction from .helper import assert_image_similar_tofile, skip_unless_feature @@ -233,7 +232,9 @@ def test_getlength( ("i" + ("\u030C" * 15) + "i", "i" + "\u032C" * 15 + "i", "\u035Cii", "i\u0305i"), ids=("caron-above", "caron-below", "double-breve", "overline"), ) -def test_getlength_combine(mode: str, direction: Direction, text: str) -> None: +def test_getlength_combine( + mode: str, direction: ImageFont.Direction, text: str +) -> None: if text == "i\u0305i" and direction == "ttb": pytest.skip("fails with this font") @@ -253,7 +254,7 @@ def test_getlength_combine(mode: str, direction: Direction, text: str) -> None: @pytest.mark.parametrize("anchor", ("lt", "mm", "rb", "sm")) -def test_anchor_ttb(anchor: Anchor) -> None: +def test_anchor_ttb(anchor: ImageFont.Anchor) -> None: text = "f" path = f"Tests/images/test_anchor_ttb_{text}_{anchor}.png" f = ImageFont.truetype("Tests/fonts/NotoSans-Regular.ttf", 120) @@ -310,7 +311,11 @@ combine_tests = ( "name, text, anchor, dir, epsilon", combine_tests, ids=[r[0] for r in combine_tests] ) def test_combine( - name: str, text: str, anchor: Anchor | None, dir: Direction | None, epsilon: float + name: str, + text: str, + anchor: ImageFont.Anchor | None, + dir: ImageFont.Direction | None, + epsilon: float, ) -> None: path = f"Tests/images/test_combine_{name}.png" f = ImageFont.truetype("Tests/fonts/NotoSans-Regular.ttf", 48) @@ -342,7 +347,7 @@ def test_combine( ("rm", "right"), # pass with getsize ), ) -def test_combine_multiline(anchor: Anchor, align: ImageDraw.Align) -> None: +def test_combine_multiline(anchor: ImageFont.Anchor, align: ImageDraw.Align) -> None: # test that multiline text uses getlength, not getsize or getbbox path = f"Tests/images/test_combine_multiline_{anchor}_{align}.png" diff --git a/docs/example/anchors.py b/docs/example/anchors.py index 23a6e933a..d4d4f3c8f 100644 --- a/docs/example/anchors.py +++ b/docs/example/anchors.py @@ -1,12 +1,11 @@ from __future__ import annotations from PIL import Image, ImageDraw, ImageFont -from PIL._typing import Anchor font = ImageFont.truetype("Tests/fonts/NotoSans-Regular.ttf", 16) -def test(anchor: Anchor) -> Image.Image: +def test(anchor: ImageFont.Anchor) -> Image.Image: im = Image.new("RGBA", (200, 100), "white") d = ImageDraw.Draw(im) d.line(((100, 0), (100, 100)), "gray") @@ -18,7 +17,7 @@ def test(anchor: Anchor) -> Image.Image: if __name__ == "__main__": im = Image.new("RGBA", (600, 300), "white") d = ImageDraw.Draw(im) - anchors: list[list[Anchor]] = [ + anchors: list[list[ImageFont.Anchor]] = [ ["ma", "mt", "mm"], ["ms", "mb", "md"], ["ls", "ms", "rs"], diff --git a/docs/reference/ImageFont.rst b/docs/reference/ImageFont.rst index d9d9cac6e..98c9a2386 100644 --- a/docs/reference/ImageFont.rst +++ b/docs/reference/ImageFont.rst @@ -92,6 +92,11 @@ Constants raise a :py:exc:`ValueError` if the number of characters is over this limit. The check can be disabled by setting ``ImageFont.MAX_STRING_LENGTH = None``. +.. class:: Anchor + + Type hint literal with the possible anchor values. See :ref:`text-anchors` for + details. + Dictionaries ------------ diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index d574ce2ea..e86ddd7b8 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -39,7 +39,7 @@ from typing import TYPE_CHECKING, Any, AnyStr, Callable, Literal, Union, cast from . import Image, ImageColor from ._deprecate import deprecate -from ._typing import Anchor, Coords, Direction +from ._typing import Coords # experimental access to the outline API Outline: Callable[[], Image.core._Outline] | None @@ -585,10 +585,10 @@ class ImageDraw: | ImageFont.TransposedFont | None ) = None, - anchor: Anchor | None = None, + anchor: ImageFont.Anchor | None = None, spacing: float = 4, align: Align = "left", - direction: Direction | None = None, + direction: ImageFont.Direction | None = None, features: list[str] | None = None, language: str | None = None, stroke_width: float = 0, @@ -710,10 +710,10 @@ class ImageDraw: | ImageFont.TransposedFont | None ) = None, - anchor: Anchor | None = None, + anchor: ImageFont.Anchor | None = None, spacing: float = 4, align: Align = "left", - direction: Direction | None = None, + direction: ImageFont.Direction | None = None, features: list[str] | None = None, language: str | None = None, stroke_width: float = 0, @@ -800,7 +800,7 @@ class ImageDraw: | ImageFont.TransposedFont | None ) = None, - direction: Direction | None = None, + direction: ImageFont.Direction | None = None, features: list[str] | None = None, language: str | None = None, embedded_color: bool = False, @@ -830,10 +830,10 @@ class ImageDraw: | ImageFont.TransposedFont | None ) = None, - anchor: Anchor | None = None, + anchor: ImageFont.Anchor | None = None, spacing: float = 4, align: Align = "left", - direction: Direction | None = None, + direction: ImageFont.Direction | None = None, features: list[str] | None = None, language: str | None = None, stroke_width: float = 0, @@ -880,10 +880,10 @@ class ImageDraw: | ImageFont.TransposedFont | None ) = None, - anchor: Anchor | None = None, + anchor: ImageFont.Anchor | None = None, spacing: float = 4, align: Align = "left", - direction: Direction | None = None, + direction: ImageFont.Direction | None = None, features: list[str] | None = None, language: str | None = None, stroke_width: float = 0, diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 46a620271..48462e2c1 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -34,10 +34,10 @@ import warnings from enum import IntEnum from io import BytesIO from types import ModuleType -from typing import IO, TYPE_CHECKING, Any, BinaryIO, TypedDict, cast +from typing import IO, TYPE_CHECKING, Any, BinaryIO, Literal, TypedDict, cast from . import Image, features -from ._typing import Anchor, Direction, StrOrBytesPath +from ._typing import StrOrBytesPath from ._util import DeferredError, is_path if TYPE_CHECKING: @@ -45,6 +45,35 @@ if TYPE_CHECKING: from ._imaging import ImagingFont from ._imagingft import Font + Anchor = Literal[ + "la", + "lt", + "lm", + "ls", + "lb", + "ld", + "ma", + "mt", + "mm", + "ms", + "mb", + "md", + "ra", + "rt", + "rm", + "rs", + "rb", + "rd", + "sa", + "st", + "sm", + "ss", + "sb", + "sd", + ] + + Direction = Literal["rtl", "ltr", "ttb"] + class Axis(TypedDict): minimum: int | None diff --git a/src/PIL/_imagingft.pyi b/src/PIL/_imagingft.pyi index a33df7621..e32deec2e 100644 --- a/src/PIL/_imagingft.pyi +++ b/src/PIL/_imagingft.pyi @@ -1,7 +1,6 @@ from typing import Any, Callable from . import ImageFont, _imaging -from ._typing import Anchor, Direction class Font: @property @@ -25,11 +24,11 @@ class Font: string: str | bytes, fill: Callable[[int, int], _imaging.ImagingCore], mode: str, - dir: Direction | None, + dir: ImageFont.Direction | None, features: list[str] | None, lang: str | None, stroke_width: float, - anchor: Anchor | None, + anchor: ImageFont.Anchor | None, foreground_ink_long: int, x_start: float, y_start: float, @@ -39,17 +38,17 @@ class Font: self, string: str | bytes | bytearray, mode: str, - dir: Direction | None, + dir: ImageFont.Direction | None, features: list[str] | None, lang: str | None, - anchor: Anchor | None, + anchor: ImageFont.Anchor | None, /, ) -> tuple[tuple[int, int], tuple[int, int]]: ... def getlength( self, string: str | bytes, mode: str, - dir: Direction | None, + dir: ImageFont.Direction | None, features: list[str] | None, lang: str | None, /, diff --git a/src/PIL/_typing.py b/src/PIL/_typing.py index 55af3819b..d5955ac52 100644 --- a/src/PIL/_typing.py +++ b/src/PIL/_typing.py @@ -3,7 +3,7 @@ from __future__ import annotations import os import sys from collections.abc import Sequence -from typing import TYPE_CHECKING, Any, Literal, Protocol, TypeVar, Union +from typing import TYPE_CHECKING, Any, Protocol, TypeVar, Union if TYPE_CHECKING: from numbers import _IntegralLike as IntegralLike @@ -49,33 +49,4 @@ class SupportsRead(Protocol[_T_co]): StrOrBytesPath = Union[str, bytes, os.PathLike[str], os.PathLike[bytes]] -Anchor = Literal[ - "la", - "lt", - "lm", - "ls", - "lb", - "ld", - "ma", - "mt", - "mm", - "ms", - "mb", - "md", - "ra", - "rt", - "rm", - "rs", - "rb", - "rd", - "sa", - "st", - "sm", - "ss", - "sb", - "sd", -] - -Direction = Literal["rtl", "ltr", "ttb"] - __all__ = ["Buffer", "IntegralLike", "StrOrBytesPath", "SupportsRead", "TypeGuard"]