mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-24 17:06:16 +03:00
Merge pull request #8268 from radarhere/type_hint
This commit is contained in:
commit
5833a8b18e
|
@ -2052,7 +2052,11 @@ class Image:
|
|||
msg = "illegal image mode"
|
||||
raise ValueError(msg)
|
||||
if isinstance(data, ImagePalette.ImagePalette):
|
||||
palette = ImagePalette.raw(data.rawmode, data.palette)
|
||||
if data.rawmode is not None:
|
||||
palette = ImagePalette.raw(data.rawmode, data.palette)
|
||||
else:
|
||||
palette = ImagePalette.ImagePalette(palette=data.palette)
|
||||
palette.dirty = 1
|
||||
else:
|
||||
if not isinstance(data, bytes):
|
||||
data = bytes(data)
|
||||
|
|
|
@ -167,7 +167,7 @@ class Draw:
|
|||
"""
|
||||
self.render("polygon", xy, *options)
|
||||
|
||||
def rectangle(self, xy: Coords, *options) -> None:
|
||||
def rectangle(self, xy: Coords, *options: Any) -> None:
|
||||
"""
|
||||
Draws a rectangle.
|
||||
|
||||
|
|
|
@ -269,12 +269,12 @@ class FreeTypeFont:
|
|||
else:
|
||||
load_from_bytes(font)
|
||||
|
||||
def __getstate__(self):
|
||||
def __getstate__(self) -> list[Any]:
|
||||
return [self.path, self.size, self.index, self.encoding, self.layout_engine]
|
||||
|
||||
def __setstate__(self, state):
|
||||
def __setstate__(self, state: list[Any]) -> None:
|
||||
path, size, index, encoding, layout_engine = state
|
||||
self.__init__(path, size, index, encoding, layout_engine)
|
||||
FreeTypeFont.__init__(self, path, size, index, encoding, layout_engine)
|
||||
|
||||
def getname(self) -> tuple[str | None, str | None]:
|
||||
"""
|
||||
|
|
|
@ -208,7 +208,7 @@ class ImagePalette:
|
|||
# Internal
|
||||
|
||||
|
||||
def raw(rawmode, data: Sequence[int] | bytes | bytearray) -> ImagePalette:
|
||||
def raw(rawmode: str, data: Sequence[int] | bytes | bytearray) -> ImagePalette:
|
||||
palette = ImagePalette()
|
||||
palette.rawmode = rawmode
|
||||
palette.palette = data
|
||||
|
|
|
@ -324,7 +324,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
|||
return self._reduce or super().reduce
|
||||
|
||||
@reduce.setter
|
||||
def reduce(self, value):
|
||||
def reduce(self, value: int) -> None:
|
||||
self._reduce = value
|
||||
|
||||
def load(self) -> Image.core.PixelAccess | None:
|
||||
|
|
|
@ -25,7 +25,7 @@ import io
|
|||
import math
|
||||
import os
|
||||
import time
|
||||
from typing import IO
|
||||
from typing import IO, Any
|
||||
|
||||
from . import Image, ImageFile, ImageSequence, PdfParser, __version__, features
|
||||
|
||||
|
@ -48,7 +48,12 @@ def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
|||
# (Internal) Image save plugin for the PDF format.
|
||||
|
||||
|
||||
def _write_image(im, filename, existing_pdf, image_refs):
|
||||
def _write_image(
|
||||
im: Image.Image,
|
||||
filename: str | bytes,
|
||||
existing_pdf: PdfParser.PdfParser,
|
||||
image_refs: list[PdfParser.IndirectReference],
|
||||
) -> tuple[PdfParser.IndirectReference, str]:
|
||||
# FIXME: Should replace ASCIIHexDecode with RunLengthDecode
|
||||
# (packbits) or LZWDecode (tiff/lzw compression). Note that
|
||||
# PDF 1.2 also supports Flatedecode (zip compression).
|
||||
|
@ -61,10 +66,10 @@ def _write_image(im, filename, existing_pdf, image_refs):
|
|||
|
||||
width, height = im.size
|
||||
|
||||
dict_obj = {"BitsPerComponent": 8}
|
||||
dict_obj: dict[str, Any] = {"BitsPerComponent": 8}
|
||||
if im.mode == "1":
|
||||
if features.check("libtiff"):
|
||||
filter = "CCITTFaxDecode"
|
||||
decode_filter = "CCITTFaxDecode"
|
||||
dict_obj["BitsPerComponent"] = 1
|
||||
params = PdfParser.PdfArray(
|
||||
[
|
||||
|
@ -79,22 +84,23 @@ def _write_image(im, filename, existing_pdf, image_refs):
|
|||
]
|
||||
)
|
||||
else:
|
||||
filter = "DCTDecode"
|
||||
decode_filter = "DCTDecode"
|
||||
dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceGray")
|
||||
procset = "ImageB" # grayscale
|
||||
elif im.mode == "L":
|
||||
filter = "DCTDecode"
|
||||
decode_filter = "DCTDecode"
|
||||
# params = f"<< /Predictor 15 /Columns {width-2} >>"
|
||||
dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceGray")
|
||||
procset = "ImageB" # grayscale
|
||||
elif im.mode == "LA":
|
||||
filter = "JPXDecode"
|
||||
decode_filter = "JPXDecode"
|
||||
# params = f"<< /Predictor 15 /Columns {width-2} >>"
|
||||
procset = "ImageB" # grayscale
|
||||
dict_obj["SMaskInData"] = 1
|
||||
elif im.mode == "P":
|
||||
filter = "ASCIIHexDecode"
|
||||
decode_filter = "ASCIIHexDecode"
|
||||
palette = im.getpalette()
|
||||
assert palette is not None
|
||||
dict_obj["ColorSpace"] = [
|
||||
PdfParser.PdfName("Indexed"),
|
||||
PdfParser.PdfName("DeviceRGB"),
|
||||
|
@ -110,15 +116,15 @@ def _write_image(im, filename, existing_pdf, image_refs):
|
|||
image_ref = _write_image(smask, filename, existing_pdf, image_refs)[0]
|
||||
dict_obj["SMask"] = image_ref
|
||||
elif im.mode == "RGB":
|
||||
filter = "DCTDecode"
|
||||
decode_filter = "DCTDecode"
|
||||
dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceRGB")
|
||||
procset = "ImageC" # color images
|
||||
elif im.mode == "RGBA":
|
||||
filter = "JPXDecode"
|
||||
decode_filter = "JPXDecode"
|
||||
procset = "ImageC" # color images
|
||||
dict_obj["SMaskInData"] = 1
|
||||
elif im.mode == "CMYK":
|
||||
filter = "DCTDecode"
|
||||
decode_filter = "DCTDecode"
|
||||
dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceCMYK")
|
||||
procset = "ImageC" # color images
|
||||
decode = [1, 0, 1, 0, 1, 0, 1, 0]
|
||||
|
@ -131,9 +137,9 @@ def _write_image(im, filename, existing_pdf, image_refs):
|
|||
|
||||
op = io.BytesIO()
|
||||
|
||||
if filter == "ASCIIHexDecode":
|
||||
if decode_filter == "ASCIIHexDecode":
|
||||
ImageFile._save(im, op, [("hex", (0, 0) + im.size, 0, im.mode)])
|
||||
elif filter == "CCITTFaxDecode":
|
||||
elif decode_filter == "CCITTFaxDecode":
|
||||
im.save(
|
||||
op,
|
||||
"TIFF",
|
||||
|
@ -141,21 +147,22 @@ def _write_image(im, filename, existing_pdf, image_refs):
|
|||
# use a single strip
|
||||
strip_size=math.ceil(width / 8) * height,
|
||||
)
|
||||
elif filter == "DCTDecode":
|
||||
elif decode_filter == "DCTDecode":
|
||||
Image.SAVE["JPEG"](im, op, filename)
|
||||
elif filter == "JPXDecode":
|
||||
elif decode_filter == "JPXDecode":
|
||||
del dict_obj["BitsPerComponent"]
|
||||
Image.SAVE["JPEG2000"](im, op, filename)
|
||||
else:
|
||||
msg = f"unsupported PDF filter ({filter})"
|
||||
msg = f"unsupported PDF filter ({decode_filter})"
|
||||
raise ValueError(msg)
|
||||
|
||||
stream = op.getvalue()
|
||||
if filter == "CCITTFaxDecode":
|
||||
filter: PdfParser.PdfArray | PdfParser.PdfName
|
||||
if decode_filter == "CCITTFaxDecode":
|
||||
stream = stream[8:]
|
||||
filter = PdfParser.PdfArray([PdfParser.PdfName(filter)])
|
||||
filter = PdfParser.PdfArray([PdfParser.PdfName(decode_filter)])
|
||||
else:
|
||||
filter = PdfParser.PdfName(filter)
|
||||
filter = PdfParser.PdfName(decode_filter)
|
||||
|
||||
image_ref = image_refs.pop(0)
|
||||
existing_pdf.write_obj(
|
||||
|
|
|
@ -47,16 +47,18 @@ import math
|
|||
import os
|
||||
import struct
|
||||
import warnings
|
||||
from collections.abc import MutableMapping
|
||||
from collections.abc import Iterator, MutableMapping
|
||||
from fractions import Fraction
|
||||
from numbers import Number, Rational
|
||||
from typing import IO, TYPE_CHECKING, Any, Callable, NoReturn
|
||||
from typing import IO, TYPE_CHECKING, Any, Callable, NoReturn, cast
|
||||
|
||||
from . import ExifTags, Image, ImageFile, ImageOps, ImagePalette, TiffTags
|
||||
from ._binary import i16be as i16
|
||||
from ._binary import i32be as i32
|
||||
from ._binary import o8
|
||||
from ._deprecate import deprecate
|
||||
from ._typing import StrOrBytesPath
|
||||
from ._util import is_path
|
||||
from .TiffTags import TYPES
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -313,7 +315,7 @@ _load_dispatch = {}
|
|||
_write_dispatch = {}
|
||||
|
||||
|
||||
def _delegate(op):
|
||||
def _delegate(op: str):
|
||||
def delegate(self, *args):
|
||||
return getattr(self._val, op)(*args)
|
||||
|
||||
|
@ -334,7 +336,9 @@ class IFDRational(Rational):
|
|||
|
||||
__slots__ = ("_numerator", "_denominator", "_val")
|
||||
|
||||
def __init__(self, value, denominator: int = 1) -> None:
|
||||
def __init__(
|
||||
self, value: float | Fraction | IFDRational, denominator: int = 1
|
||||
) -> None:
|
||||
"""
|
||||
:param value: either an integer numerator, a
|
||||
float/rational/other number, or an IFDRational
|
||||
|
@ -358,18 +362,20 @@ class IFDRational(Rational):
|
|||
self._val = float("nan")
|
||||
elif denominator == 1:
|
||||
self._val = Fraction(value)
|
||||
elif int(value) == value:
|
||||
self._val = Fraction(int(value), denominator)
|
||||
else:
|
||||
self._val = Fraction(value, denominator)
|
||||
self._val = Fraction(value / denominator)
|
||||
|
||||
@property
|
||||
def numerator(self):
|
||||
return self._numerator
|
||||
|
||||
@property
|
||||
def denominator(self):
|
||||
def denominator(self) -> int:
|
||||
return self._denominator
|
||||
|
||||
def limit_rational(self, max_denominator):
|
||||
def limit_rational(self, max_denominator: int) -> tuple[float, int]:
|
||||
"""
|
||||
|
||||
:param max_denominator: Integer, the maximum denominator value
|
||||
|
@ -379,6 +385,7 @@ class IFDRational(Rational):
|
|||
if self.denominator == 0:
|
||||
return self.numerator, self.denominator
|
||||
|
||||
assert isinstance(self._val, Fraction)
|
||||
f = self._val.limit_denominator(max_denominator)
|
||||
return f.numerator, f.denominator
|
||||
|
||||
|
@ -396,14 +403,15 @@ class IFDRational(Rational):
|
|||
val = float(val)
|
||||
return val == other
|
||||
|
||||
def __getstate__(self):
|
||||
def __getstate__(self) -> list[float | Fraction]:
|
||||
return [self._val, self._numerator, self._denominator]
|
||||
|
||||
def __setstate__(self, state):
|
||||
def __setstate__(self, state: list[float | Fraction]) -> None:
|
||||
IFDRational.__init__(self, 0)
|
||||
_val, _numerator, _denominator = state
|
||||
self._val = _val
|
||||
self._numerator = _numerator
|
||||
assert isinstance(_denominator, int)
|
||||
self._denominator = _denominator
|
||||
|
||||
""" a = ['add','radd', 'sub', 'rsub', 'mul', 'rmul',
|
||||
|
@ -730,13 +738,13 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
|||
self._tags_v1.pop(tag, None)
|
||||
self._tagdata.pop(tag, None)
|
||||
|
||||
def __iter__(self):
|
||||
def __iter__(self) -> Iterator[int]:
|
||||
return iter(set(self._tagdata) | set(self._tags_v2))
|
||||
|
||||
def _unpack(self, fmt: str, data: bytes):
|
||||
return struct.unpack(self._endian + fmt, data)
|
||||
|
||||
def _pack(self, fmt: str, *values):
|
||||
def _pack(self, fmt: str, *values) -> bytes:
|
||||
return struct.pack(self._endian + fmt, *values)
|
||||
|
||||
list(
|
||||
|
@ -787,7 +795,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
|||
def load_rational(self, data, legacy_api: bool = True):
|
||||
vals = self._unpack(f"{len(data) // 4}L", data)
|
||||
|
||||
def combine(a, b):
|
||||
def combine(a: int, b: int) -> tuple[int, int] | IFDRational:
|
||||
return (a, b) if legacy_api else IFDRational(a, b)
|
||||
|
||||
return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2]))
|
||||
|
@ -814,7 +822,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
|||
def load_signed_rational(self, data: bytes, legacy_api: bool = True):
|
||||
vals = self._unpack(f"{len(data) // 4}l", data)
|
||||
|
||||
def combine(a, b):
|
||||
def combine(a: int, b: int) -> tuple[int, int] | IFDRational:
|
||||
return (a, b) if legacy_api else IFDRational(a, b)
|
||||
|
||||
return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2]))
|
||||
|
@ -903,11 +911,11 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
|||
warnings.warn(str(msg))
|
||||
return
|
||||
|
||||
def tobytes(self, offset=0):
|
||||
def tobytes(self, offset: int = 0) -> bytes:
|
||||
# FIXME What about tagdata?
|
||||
result = self._pack("H", len(self._tags_v2))
|
||||
|
||||
entries = []
|
||||
entries: list[tuple[int, int, int, bytes, bytes]] = []
|
||||
offset = offset + len(result) + len(self._tags_v2) * 12 + 4
|
||||
stripoffsets = None
|
||||
|
||||
|
@ -916,7 +924,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
|||
for tag, value in sorted(self._tags_v2.items()):
|
||||
if tag == STRIPOFFSETS:
|
||||
stripoffsets = len(entries)
|
||||
typ = self.tagtype.get(tag)
|
||||
typ = self.tagtype[tag]
|
||||
logger.debug("Tag %s, Type: %s, Value: %s", tag, typ, repr(value))
|
||||
is_ifd = typ == TiffTags.LONG and isinstance(value, dict)
|
||||
if is_ifd:
|
||||
|
@ -1072,7 +1080,7 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2):
|
|||
def __len__(self) -> int:
|
||||
return len(set(self._tagdata) | set(self._tags_v1))
|
||||
|
||||
def __iter__(self):
|
||||
def __iter__(self) -> Iterator[int]:
|
||||
return iter(set(self._tagdata) | set(self._tags_v1))
|
||||
|
||||
def __setitem__(self, tag: int, value) -> None:
|
||||
|
@ -1943,17 +1951,18 @@ class AppendingTiffWriter:
|
|||
521, # JPEGACTables
|
||||
}
|
||||
|
||||
def __init__(self, fn, new: bool = False) -> None:
|
||||
if hasattr(fn, "read"):
|
||||
self.f = fn
|
||||
self.close_fp = False
|
||||
else:
|
||||
def __init__(self, fn: StrOrBytesPath | IO[bytes], new: bool = False) -> None:
|
||||
self.f: IO[bytes]
|
||||
if is_path(fn):
|
||||
self.name = fn
|
||||
self.close_fp = True
|
||||
try:
|
||||
self.f = open(fn, "w+b" if new else "r+b")
|
||||
except OSError:
|
||||
self.f = open(fn, "w+b")
|
||||
else:
|
||||
self.f = cast(IO[bytes], fn)
|
||||
self.close_fp = False
|
||||
self.beginning = self.f.tell()
|
||||
self.setup()
|
||||
|
||||
|
@ -1961,7 +1970,7 @@ class AppendingTiffWriter:
|
|||
# Reset everything.
|
||||
self.f.seek(self.beginning, os.SEEK_SET)
|
||||
|
||||
self.whereToWriteNewIFDOffset = None
|
||||
self.whereToWriteNewIFDOffset: int | None = None
|
||||
self.offsetOfNewPage = 0
|
||||
|
||||
self.IIMM = iimm = self.f.read(4)
|
||||
|
@ -2000,6 +2009,7 @@ class AppendingTiffWriter:
|
|||
|
||||
ifd_offset = self.readLong()
|
||||
ifd_offset += self.offsetOfNewPage
|
||||
assert self.whereToWriteNewIFDOffset is not None
|
||||
self.f.seek(self.whereToWriteNewIFDOffset)
|
||||
self.writeLong(ifd_offset)
|
||||
self.f.seek(ifd_offset)
|
||||
|
@ -2020,7 +2030,7 @@ class AppendingTiffWriter:
|
|||
def tell(self) -> int:
|
||||
return self.f.tell() - self.offsetOfNewPage
|
||||
|
||||
def seek(self, offset: int, whence=io.SEEK_SET) -> int:
|
||||
def seek(self, offset: int, whence: int = io.SEEK_SET) -> int:
|
||||
if whence == os.SEEK_SET:
|
||||
offset += self.offsetOfNewPage
|
||||
|
||||
|
@ -2111,7 +2121,6 @@ class AppendingTiffWriter:
|
|||
field_size = self.fieldSizes[field_type]
|
||||
total_size = field_size * count
|
||||
is_local = total_size <= 4
|
||||
offset: int | None
|
||||
if not is_local:
|
||||
offset = self.readLong() + self.offsetOfNewPage
|
||||
self.rewriteLastLong(offset)
|
||||
|
@ -2131,8 +2140,6 @@ class AppendingTiffWriter:
|
|||
)
|
||||
self.f.seek(cur_pos)
|
||||
|
||||
offset = cur_pos = None
|
||||
|
||||
elif is_local:
|
||||
# skip the locally stored value that is not an offset
|
||||
self.f.seek(4, os.SEEK_CUR)
|
||||
|
|
Loading…
Reference in New Issue
Block a user