Various fixes

This commit is contained in:
Sebastian Rittau 2024-05-07 15:59:20 +02:00
parent d44e9fccb1
commit d63caf266d
5 changed files with 47 additions and 53 deletions

View File

@ -41,7 +41,7 @@ import warnings
from collections.abc import Callable, MutableMapping from collections.abc import Callable, MutableMapping
from enum import IntEnum from enum import IntEnum
from types import ModuleType from types import ModuleType
from typing import IO, TYPE_CHECKING, Any, Literal, Protocol, Sequence, cast, overload from typing import IO, TYPE_CHECKING, Any, Literal, Protocol, Sequence, cast
# VERSION was removed in Pillow 6.0.0. # VERSION was removed in Pillow 6.0.0.
# PILLOW_VERSION was removed in Pillow 9.0.0. # PILLOW_VERSION was removed in Pillow 9.0.0.
@ -483,7 +483,9 @@ def _getscaleoffset(expr):
class _GetDataTransform(Protocol): class _GetDataTransform(Protocol):
def getdata(self) -> tuple[Transform, Sequence[float]]: ... def getdata(
self,
) -> tuple[Transform, Sequence[Any]]: ...
class Image: class Image:
@ -2690,41 +2692,11 @@ class Image:
# FIXME: the different transform methods need further explanation # FIXME: the different transform methods need further explanation
# instead of bloating the method docs, add a separate chapter. # instead of bloating the method docs, add a separate chapter.
@overload
def transform(
self,
size: tuple[int, int],
method: Transform | ImageTransformHandler,
data: Sequence[float],
resample: int = Resampling.NEAREST,
fill: int = 1,
fillcolor: float | tuple[float, ...] | str | None = None,
) -> Image: ...
@overload
def transform(
self,
size: tuple[int, int],
method: _GetDataTransform,
data: None = None,
resample: int = Resampling.NEAREST,
fill: int = 1,
fillcolor: float | tuple[float, ...] | str | None = None,
) -> Image: ...
@overload
def transform( def transform(
self, self,
size: tuple[int, int], size: tuple[int, int],
method: Transform | ImageTransformHandler | _GetDataTransform, method: Transform | ImageTransformHandler | _GetDataTransform,
data: Sequence[float] | None = None, data: Sequence[Any] | None = None,
resample: int = Resampling.NEAREST,
fill: int = 1,
fillcolor: float | tuple[float, ...] | str | None = None,
) -> Image: ...
def transform(
self,
size: tuple[int, int],
method: Transform | ImageTransformHandler | _GetDataTransform,
data: Sequence[float] | None = None,
resample: int = Resampling.NEAREST, resample: int = Resampling.NEAREST,
fill: int = 1, fill: int = 1,
fillcolor: float | tuple[float, ...] | str | None = None, fillcolor: float | tuple[float, ...] | str | None = None,
@ -2803,7 +2775,7 @@ class Image:
im.info = self.info.copy() im.info = self.info.copy()
if method == Transform.MESH: if method == Transform.MESH:
# list of quads # list of quads
for box, quad in data: for box, quad in cast(Sequence[tuple[float, float]], data):
im.__transformer( im.__transformer(
box, self, Transform.QUAD, quad, resample, fillcolor is None box, self, Transform.QUAD, quad, resample, fillcolor is None
) )
@ -2961,7 +2933,7 @@ class ImageTransformHandler:
self, self,
size: tuple[int, int], size: tuple[int, int],
image: Image, image: Image,
**options: dict[str, str | int | tuple[int, ...] | list[int]], **options: dict[str, str | int | tuple[int, ...] | list[int]] | int,
) -> Image: ) -> Image:
pass pass
@ -3830,7 +3802,7 @@ class Exif(_ExifBase):
return self._fixup_dict(info) return self._fixup_dict(info)
def _get_head(self): def _get_head(self):
version = b"\x2B" if self.bigtiff else b"\x2A" version = b"\x2b" if self.bigtiff else b"\x2a"
if self.endian == "<": if self.endian == "<":
head = b"II" + version + b"\x00" + o32le(8) head = b"II" + version + b"\x00" + o32le(8)
else: else:

View File

@ -118,7 +118,7 @@ class ImageDraw:
self.font = ImageFont.load_default() self.font = ImageFont.load_default()
return self.font return self.font
def _getfont(self, font_size: float | None): def _getfont(self, font_size: float | None) -> FreeTypeFont | ImageFont:
if font_size is not None: if font_size is not None:
from . import ImageFont from . import ImageFont
@ -451,13 +451,13 @@ class ImageDraw:
right[3] -= r + 1 right[3] -= r + 1
self.draw.draw_rectangle(right, ink, 1) self.draw.draw_rectangle(right, ink, 1)
def _multiline_check(self, text: str | bytes) -> bool: def _multiline_check(self, text: AnyStr) -> bool:
split_character = "\n" if isinstance(text, str) else b"\n" split_character = cast(AnyStr, "\n" if isinstance(text, str) else b"\n")
return split_character in text return split_character in text
def _multiline_split(self, text: AnyStr) -> list[AnyStr]: def _multiline_split(self, text: AnyStr) -> list[AnyStr]:
split_character = "\n" if isinstance(text, str) else b"\n" split_character = cast(AnyStr, "\n" if isinstance(text, str) else b"\n")
return text.split(split_character) return text.split(split_character)
@ -470,10 +470,10 @@ class ImageDraw:
def text( def text(
self, self,
xy: tuple[int, int], xy: tuple[float, float],
text, text: str,
fill=None, fill=None,
font=None, font: FreeTypeFont | ImageFont | None = None,
anchor=None, anchor=None,
spacing=4, spacing=4,
align="left", align="left",
@ -527,7 +527,7 @@ class ImageDraw:
coord.append(int(xy[i])) coord.append(int(xy[i]))
start.append(math.modf(xy[i])[0]) start.append(math.modf(xy[i])[0])
try: try:
mask, offset = font.getmask2( mask, offset = font.getmask2( # type: ignore[union-attr,misc]
text, text,
mode, mode,
direction=direction, direction=direction,
@ -543,7 +543,7 @@ class ImageDraw:
coord = [coord[0] + offset[0], coord[1] + offset[1]] coord = [coord[0] + offset[0], coord[1] + offset[1]]
except AttributeError: except AttributeError:
try: try:
mask = font.getmask( mask = font.getmask( # type: ignore[misc]
text, text,
mode, mode,
direction, direction,
@ -592,7 +592,7 @@ class ImageDraw:
def multiline_text( def multiline_text(
self, self,
xy: tuple[int, int], xy: tuple[float, float],
text, text,
fill=None, fill=None,
font=None, font=None,
@ -625,7 +625,7 @@ class ImageDraw:
font = self._getfont(font_size) font = self._getfont(font_size)
widths = [] widths = []
max_width = 0 max_width: float = 0
lines = self._multiline_split(text) lines = self._multiline_split(text)
line_spacing = self._multiline_spacing(font, spacing, stroke_width) line_spacing = self._multiline_spacing(font, spacing, stroke_width)
for line in lines: for line in lines:
@ -779,7 +779,7 @@ class ImageDraw:
font = self._getfont(font_size) font = self._getfont(font_size)
widths = [] widths = []
max_width = 0 max_width: float = 0
lines = self._multiline_split(text) lines = self._multiline_split(text)
line_spacing = self._multiline_spacing(font, spacing, stroke_width) line_spacing = self._multiline_spacing(font, spacing, stroke_width)
for line in lines: for line in lines:

View File

@ -35,11 +35,14 @@ from enum import IntEnum
from io import BytesIO from io import BytesIO
from typing import TYPE_CHECKING, BinaryIO from typing import TYPE_CHECKING, BinaryIO
from PIL import ImageFile
from . import Image from . import Image
from ._typing import StrOrBytesPath from ._typing import StrOrBytesPath
from ._util import is_directory, is_path from ._util import is_directory, is_path
if TYPE_CHECKING: if TYPE_CHECKING:
from ._imaging import ImagingFont
from ._imagingft import Font from ._imagingft import Font
@ -84,11 +87,11 @@ def _string_length_check(text: str | bytes) -> None:
class ImageFont: class ImageFont:
"""PIL font wrapper""" """PIL font wrapper"""
font: Font font: ImagingFont
def _load_pilfont(self, filename: str) -> None: def _load_pilfont(self, filename: str) -> None:
with open(filename, "rb") as fp: with open(filename, "rb") as fp:
image = None image: ImageFile.ImageFile | None = None
for ext in (".png", ".gif", ".pbm"): for ext in (".png", ".gif", ".pbm"):
if image: if image:
image.close() image.close()
@ -198,6 +201,8 @@ class ImageFont:
class FreeTypeFont: class FreeTypeFont:
"""FreeType font wrapper (requires _imagingft service)""" """FreeType font wrapper (requires _imagingft service)"""
font: Font
def __init__( def __init__(
self, self,
font: StrOrBytesPath | BinaryIO | None = None, font: StrOrBytesPath | BinaryIO | None = None,
@ -261,7 +266,7 @@ class FreeTypeFont:
path, size, index, encoding, layout_engine = state path, size, index, encoding, layout_engine = state
self.__init__(path, size, index, encoding, layout_engine) self.__init__(path, size, index, encoding, layout_engine)
def getname(self) -> tuple[str, str]: def getname(self) -> tuple[str | None, str | None]:
""" """
:return: A tuple of the font family (e.g. Helvetica) and the font style :return: A tuple of the font family (e.g. Helvetica) and the font style
(e.g. Bold) (e.g. Bold)
@ -876,6 +881,7 @@ def load_path(filename: str | bytes) -> ImageFont:
""" """
for directory in sys.path: for directory in sys.path:
if is_directory(directory): if is_directory(directory):
assert isinstance(directory, str)
if not isinstance(filename, str): if not isinstance(filename, str):
filename = filename.decode("utf-8") filename = filename.decode("utf-8")
try: try:
@ -900,6 +906,7 @@ def load_default(size: float | None = None) -> FreeTypeFont | ImageFont:
:return: A font object. :return: A font object.
""" """
f: FreeTypeFont | ImageFont
if core.__class__.__name__ == "module" or size is not None: if core.__class__.__name__ == "module" or size is not None:
f = truetype( f = truetype(
BytesIO( BytesIO(

View File

@ -14,7 +14,7 @@
# #
from __future__ import annotations from __future__ import annotations
from typing import Sequence from typing import Any, Sequence
from . import Image from . import Image
@ -34,7 +34,7 @@ class Transform(Image.ImageTransformHandler):
self, self,
size: tuple[int, int], size: tuple[int, int],
image: Image.Image, image: Image.Image,
**options: dict[str, str | int | tuple[int, ...] | list[int]], **options: Any,
) -> Image.Image: ) -> Image.Image:
"""Perform the transform. Called from :py:meth:`.Image.transform`.""" """Perform the transform. Called from :py:meth:`.Image.transform`."""
# can be overridden # can be overridden

View File

@ -1,3 +1,18 @@
from typing import Any from typing import Any
from typing_extensions import Buffer
class ImagingCore:
def __getattr__(self, name: str) -> Any: ...
class ImagingFont:
def __getattr__(self, name: str) -> Any: ...
class ImagingDraw:
def __getattr__(self, name: str) -> Any: ...
class PixelAccess:
def __getattr__(self, name: str) -> Any: ...
def font(image, glyphdata: Buffer) -> ImagingFont: ...
def __getattr__(name: str) -> Any: ... def __getattr__(name: str) -> Any: ...