mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-26 01:46:18 +03:00
Merge pull request #8263 from radarhere/type_hint
This commit is contained in:
commit
8f62fbdf44
|
@ -57,6 +57,7 @@ def test_getiptcinfo_fotostation() -> None:
|
|||
iptc = IptcImagePlugin.getiptcinfo(im)
|
||||
|
||||
# Assert
|
||||
assert iptc is not None
|
||||
for tag in iptc.keys():
|
||||
if tag[0] == 240:
|
||||
return
|
||||
|
|
|
@ -468,40 +468,40 @@ def _getencoder(
|
|||
|
||||
|
||||
class _E:
|
||||
def __init__(self, scale, offset) -> None:
|
||||
def __init__(self, scale: float, offset: float) -> None:
|
||||
self.scale = scale
|
||||
self.offset = offset
|
||||
|
||||
def __neg__(self) -> _E:
|
||||
return _E(-self.scale, -self.offset)
|
||||
|
||||
def __add__(self, other) -> _E:
|
||||
def __add__(self, other: _E | float) -> _E:
|
||||
if isinstance(other, _E):
|
||||
return _E(self.scale + other.scale, self.offset + other.offset)
|
||||
return _E(self.scale, self.offset + other)
|
||||
|
||||
__radd__ = __add__
|
||||
|
||||
def __sub__(self, other):
|
||||
def __sub__(self, other: _E | float) -> _E:
|
||||
return self + -other
|
||||
|
||||
def __rsub__(self, other):
|
||||
def __rsub__(self, other: _E | float) -> _E:
|
||||
return other + -self
|
||||
|
||||
def __mul__(self, other) -> _E:
|
||||
def __mul__(self, other: _E | float) -> _E:
|
||||
if isinstance(other, _E):
|
||||
return NotImplemented
|
||||
return _E(self.scale * other, self.offset * other)
|
||||
|
||||
__rmul__ = __mul__
|
||||
|
||||
def __truediv__(self, other) -> _E:
|
||||
def __truediv__(self, other: _E | float) -> _E:
|
||||
if isinstance(other, _E):
|
||||
return NotImplemented
|
||||
return _E(self.scale / other, self.offset / other)
|
||||
|
||||
|
||||
def _getscaleoffset(expr):
|
||||
def _getscaleoffset(expr) -> tuple[float, float]:
|
||||
a = expr(_E(1, 0))
|
||||
return (a.scale, a.offset) if isinstance(a, _E) else (0, a)
|
||||
|
||||
|
|
|
@ -24,10 +24,10 @@
|
|||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import BinaryIO
|
||||
from typing import AnyStr, BinaryIO
|
||||
|
||||
from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath
|
||||
from ._typing import StrOrBytesPath
|
||||
from ._typing import Coords, StrOrBytesPath
|
||||
|
||||
|
||||
class Pen:
|
||||
|
@ -74,12 +74,14 @@ class Draw:
|
|||
image = Image.new(image, size, color)
|
||||
self.draw = ImageDraw.Draw(image)
|
||||
self.image = image
|
||||
self.transform = None
|
||||
self.transform: tuple[float, float, float, float, float, float] | None = None
|
||||
|
||||
def flush(self) -> Image.Image:
|
||||
return self.image
|
||||
|
||||
def render(self, op, xy, pen, brush=None):
|
||||
def render(
|
||||
self, op: str, xy: Coords, pen: Pen | Brush, brush: Brush | Pen | None = None
|
||||
) -> None:
|
||||
# handle color arguments
|
||||
outline = fill = None
|
||||
width = 1
|
||||
|
@ -95,20 +97,21 @@ class Draw:
|
|||
fill = pen.color
|
||||
# handle transformation
|
||||
if self.transform:
|
||||
xy = ImagePath.Path(xy)
|
||||
xy.transform(self.transform)
|
||||
path = ImagePath.Path(xy)
|
||||
path.transform(self.transform)
|
||||
xy = path
|
||||
# render the item
|
||||
if op == "line":
|
||||
self.draw.line(xy, fill=outline, width=width)
|
||||
else:
|
||||
getattr(self.draw, op)(xy, fill=fill, outline=outline)
|
||||
|
||||
def settransform(self, offset):
|
||||
def settransform(self, offset: tuple[float, float]) -> None:
|
||||
"""Sets a transformation offset."""
|
||||
(xoffset, yoffset) = offset
|
||||
self.transform = (1, 0, xoffset, 0, 1, yoffset)
|
||||
|
||||
def arc(self, xy, start, end, *options):
|
||||
def arc(self, xy: Coords, start, end, *options) -> None:
|
||||
"""
|
||||
Draws an arc (a portion of a circle outline) between the start and end
|
||||
angles, inside the given bounding box.
|
||||
|
@ -117,7 +120,7 @@ class Draw:
|
|||
"""
|
||||
self.render("arc", xy, start, end, *options)
|
||||
|
||||
def chord(self, xy, start, end, *options):
|
||||
def chord(self, xy: Coords, start, end, *options) -> None:
|
||||
"""
|
||||
Same as :py:meth:`~PIL.ImageDraw2.Draw.arc`, but connects the end points
|
||||
with a straight line.
|
||||
|
@ -126,7 +129,7 @@ class Draw:
|
|||
"""
|
||||
self.render("chord", xy, start, end, *options)
|
||||
|
||||
def ellipse(self, xy, *options):
|
||||
def ellipse(self, xy: Coords, *options) -> None:
|
||||
"""
|
||||
Draws an ellipse inside the given bounding box.
|
||||
|
||||
|
@ -134,7 +137,7 @@ class Draw:
|
|||
"""
|
||||
self.render("ellipse", xy, *options)
|
||||
|
||||
def line(self, xy, *options):
|
||||
def line(self, xy: Coords, *options) -> None:
|
||||
"""
|
||||
Draws a line between the coordinates in the ``xy`` list.
|
||||
|
||||
|
@ -142,7 +145,7 @@ class Draw:
|
|||
"""
|
||||
self.render("line", xy, *options)
|
||||
|
||||
def pieslice(self, xy, start, end, *options):
|
||||
def pieslice(self, xy: Coords, start, end, *options) -> None:
|
||||
"""
|
||||
Same as arc, but also draws straight lines between the end points and the
|
||||
center of the bounding box.
|
||||
|
@ -151,7 +154,7 @@ class Draw:
|
|||
"""
|
||||
self.render("pieslice", xy, start, end, *options)
|
||||
|
||||
def polygon(self, xy, *options):
|
||||
def polygon(self, xy: Coords, *options) -> None:
|
||||
"""
|
||||
Draws a polygon.
|
||||
|
||||
|
@ -164,7 +167,7 @@ class Draw:
|
|||
"""
|
||||
self.render("polygon", xy, *options)
|
||||
|
||||
def rectangle(self, xy, *options):
|
||||
def rectangle(self, xy: Coords, *options) -> None:
|
||||
"""
|
||||
Draws a rectangle.
|
||||
|
||||
|
@ -172,18 +175,21 @@ class Draw:
|
|||
"""
|
||||
self.render("rectangle", xy, *options)
|
||||
|
||||
def text(self, xy, text, font):
|
||||
def text(self, xy: tuple[float, float], text: AnyStr, font: Font) -> None:
|
||||
"""
|
||||
Draws the string at the given position.
|
||||
|
||||
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.text`
|
||||
"""
|
||||
if self.transform:
|
||||
xy = ImagePath.Path(xy)
|
||||
xy.transform(self.transform)
|
||||
path = ImagePath.Path(xy)
|
||||
path.transform(self.transform)
|
||||
xy = path
|
||||
self.draw.text(xy, text, font=font.font, fill=font.color)
|
||||
|
||||
def textbbox(self, xy, text, font):
|
||||
def textbbox(
|
||||
self, xy: tuple[float, float], text: AnyStr, font: Font
|
||||
) -> tuple[float, float, float, float]:
|
||||
"""
|
||||
Returns bounding box (in pixels) of given text.
|
||||
|
||||
|
@ -192,11 +198,12 @@ class Draw:
|
|||
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textbbox`
|
||||
"""
|
||||
if self.transform:
|
||||
xy = ImagePath.Path(xy)
|
||||
xy.transform(self.transform)
|
||||
path = ImagePath.Path(xy)
|
||||
path.transform(self.transform)
|
||||
xy = path
|
||||
return self.draw.textbbox(xy, text, font=font.font)
|
||||
|
||||
def textlength(self, text, font):
|
||||
def textlength(self, text: AnyStr, font: Font) -> float:
|
||||
"""
|
||||
Returns length (in pixels) of given text.
|
||||
This is the amount by which following text should be offset.
|
||||
|
|
|
@ -58,7 +58,7 @@ else:
|
|||
qt_version = None
|
||||
|
||||
|
||||
def rgb(r, g, b, a=255):
|
||||
def rgb(r: int, g: int, b: int, a: int = 255) -> int:
|
||||
"""(Internal) Turns an RGB color into a Qt compatible color integer."""
|
||||
# use qRgb to pack the colors, and then turn the resulting long
|
||||
# into a negative integer with the same bitpattern.
|
||||
|
|
|
@ -185,7 +185,9 @@ Image.register_open(IptcImageFile.format, IptcImageFile)
|
|||
Image.register_extension(IptcImageFile.format, ".iim")
|
||||
|
||||
|
||||
def getiptcinfo(im: ImageFile.ImageFile):
|
||||
def getiptcinfo(
|
||||
im: ImageFile.ImageFile,
|
||||
) -> dict[tuple[int, int], bytes | list[bytes]] | None:
|
||||
"""
|
||||
Get IPTC information from TIFF, JPEG, or IPTC file.
|
||||
|
||||
|
|
|
@ -584,8 +584,10 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
|||
self.tagtype: dict[int, int] = {}
|
||||
""" Dictionary of tag types """
|
||||
self.reset()
|
||||
(self.next,) = (
|
||||
self._unpack("Q", ifh[8:]) if self._bigtiff else self._unpack("L", ifh[4:])
|
||||
self.next = (
|
||||
self._unpack("Q", ifh[8:])[0]
|
||||
if self._bigtiff
|
||||
else self._unpack("L", ifh[4:])[0]
|
||||
)
|
||||
self._legacy_api = False
|
||||
|
||||
|
@ -643,7 +645,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
|||
def __setitem__(self, tag: int, value) -> None:
|
||||
self._setitem(tag, value, self.legacy_api)
|
||||
|
||||
def _setitem(self, tag, value, legacy_api) -> None:
|
||||
def _setitem(self, tag: int, value, legacy_api: bool) -> None:
|
||||
basetypes = (Number, bytes, str)
|
||||
|
||||
info = TiffTags.lookup(tag, self.group)
|
||||
|
@ -731,7 +733,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
|||
def __iter__(self):
|
||||
return iter(set(self._tagdata) | set(self._tags_v2))
|
||||
|
||||
def _unpack(self, fmt: str, data):
|
||||
def _unpack(self, fmt: str, data: bytes):
|
||||
return struct.unpack(self._endian + fmt, data)
|
||||
|
||||
def _pack(self, fmt: str, *values):
|
||||
|
@ -755,11 +757,11 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
|||
)
|
||||
|
||||
@_register_loader(1, 1) # Basic type, except for the legacy API.
|
||||
def load_byte(self, data, legacy_api: bool = True):
|
||||
def load_byte(self, data: bytes, legacy_api: bool = True) -> bytes:
|
||||
return data
|
||||
|
||||
@_register_writer(1) # Basic type, except for the legacy API.
|
||||
def write_byte(self, data) -> bytes:
|
||||
def write_byte(self, data: bytes | int | IFDRational) -> bytes:
|
||||
if isinstance(data, IFDRational):
|
||||
data = int(data)
|
||||
if isinstance(data, int):
|
||||
|
@ -773,7 +775,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
|||
return data.decode("latin-1", "replace")
|
||||
|
||||
@_register_writer(2)
|
||||
def write_string(self, value) -> bytes:
|
||||
def write_string(self, value: str | bytes | int) -> bytes:
|
||||
# remerge of https://github.com/python-pillow/Pillow/pull/1416
|
||||
if isinstance(value, int):
|
||||
value = str(value)
|
||||
|
@ -782,7 +784,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
|||
return value + b"\0"
|
||||
|
||||
@_register_loader(5, 8)
|
||||
def load_rational(self, data, legacy_api=True):
|
||||
def load_rational(self, data, legacy_api: bool = True):
|
||||
vals = self._unpack(f"{len(data) // 4}L", data)
|
||||
|
||||
def combine(a, b):
|
||||
|
@ -797,11 +799,11 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
|||
)
|
||||
|
||||
@_register_loader(7, 1)
|
||||
def load_undefined(self, data, legacy_api: bool = True):
|
||||
def load_undefined(self, data: bytes, legacy_api: bool = True) -> bytes:
|
||||
return data
|
||||
|
||||
@_register_writer(7)
|
||||
def write_undefined(self, value) -> bytes:
|
||||
def write_undefined(self, value: bytes | int | IFDRational) -> bytes:
|
||||
if isinstance(value, IFDRational):
|
||||
value = int(value)
|
||||
if isinstance(value, int):
|
||||
|
@ -809,7 +811,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
|||
return value
|
||||
|
||||
@_register_loader(10, 8)
|
||||
def load_signed_rational(self, data, legacy_api: bool = True):
|
||||
def load_signed_rational(self, data: bytes, legacy_api: bool = True):
|
||||
vals = self._unpack(f"{len(data) // 4}l", data)
|
||||
|
||||
def combine(a, b):
|
||||
|
|
Loading…
Reference in New Issue
Block a user