mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-25 17:36:18 +03:00
Merge pull request #8270 from radarhere/type_hint
This commit is contained in:
commit
4df4df2a18
|
@ -154,7 +154,7 @@ class TestFileJpeg:
|
||||||
assert k > 0.9
|
assert k > 0.9
|
||||||
|
|
||||||
def test_rgb(self) -> None:
|
def test_rgb(self) -> None:
|
||||||
def getchannels(im: JpegImagePlugin.JpegImageFile) -> tuple[int, int, int]:
|
def getchannels(im: JpegImagePlugin.JpegImageFile) -> tuple[int, ...]:
|
||||||
return tuple(v[0] for v in im.layer)
|
return tuple(v[0] for v in im.layer)
|
||||||
|
|
||||||
im = hopper()
|
im = hopper()
|
||||||
|
|
|
@ -99,7 +99,7 @@ class TestImage:
|
||||||
im = Image.new("L", (100, 100))
|
im = Image.new("L", (100, 100))
|
||||||
|
|
||||||
p = Pretty()
|
p = Pretty()
|
||||||
im._repr_pretty_(p, None)
|
im._repr_pretty_(p, False)
|
||||||
assert p.pretty_output == "<PIL.Image.Image image mode=L size=100x100>"
|
assert p.pretty_output == "<PIL.Image.Image image mode=L size=100x100>"
|
||||||
|
|
||||||
def test_open_formats(self) -> None:
|
def test_open_formats(self) -> None:
|
||||||
|
|
|
@ -185,6 +185,14 @@ Plugin reference
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
:mod:`~PIL.MpoImagePlugin` Module
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
.. automodule:: PIL.MpoImagePlugin
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
:mod:`~PIL.MspImagePlugin` Module
|
:mod:`~PIL.MspImagePlugin` Module
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
|
|
|
@ -65,16 +65,24 @@ def has_ghostscript() -> bool:
|
||||||
return gs_binary is not False
|
return gs_binary is not False
|
||||||
|
|
||||||
|
|
||||||
def Ghostscript(tile, size, fp, scale=1, transparency: bool = False) -> Image.Image:
|
def Ghostscript(
|
||||||
|
tile: list[ImageFile._Tile],
|
||||||
|
size: tuple[int, int],
|
||||||
|
fp: IO[bytes],
|
||||||
|
scale: int = 1,
|
||||||
|
transparency: bool = False,
|
||||||
|
) -> Image.Image:
|
||||||
"""Render an image using Ghostscript"""
|
"""Render an image using Ghostscript"""
|
||||||
global gs_binary
|
global gs_binary
|
||||||
if not has_ghostscript():
|
if not has_ghostscript():
|
||||||
msg = "Unable to locate Ghostscript on paths"
|
msg = "Unable to locate Ghostscript on paths"
|
||||||
raise OSError(msg)
|
raise OSError(msg)
|
||||||
|
assert isinstance(gs_binary, str)
|
||||||
|
|
||||||
# Unpack decoder tile
|
# Unpack decoder tile
|
||||||
decoder, tile, offset, data = tile[0]
|
args = tile[0].args
|
||||||
length, bbox = data
|
assert isinstance(args, tuple)
|
||||||
|
length, bbox = args
|
||||||
|
|
||||||
# Hack to support hi-res rendering
|
# Hack to support hi-res rendering
|
||||||
scale = int(scale) or 1
|
scale = int(scale) or 1
|
||||||
|
@ -227,7 +235,11 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
# put floating point values there anyway.
|
# put floating point values there anyway.
|
||||||
box = [int(float(i)) for i in v.split()]
|
box = [int(float(i)) for i in v.split()]
|
||||||
self._size = box[2] - box[0], box[3] - box[1]
|
self._size = box[2] - box[0], box[3] - box[1]
|
||||||
self.tile = [("eps", (0, 0) + self.size, offset, (length, box))]
|
self.tile = [
|
||||||
|
ImageFile._Tile(
|
||||||
|
"eps", (0, 0) + self.size, offset, (length, box)
|
||||||
|
)
|
||||||
|
]
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -220,7 +220,7 @@ if hasattr(core, "DEFAULT_STRATEGY"):
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from xml.etree.ElementTree import Element
|
from xml.etree.ElementTree import Element
|
||||||
|
|
||||||
from . import ImageFile, ImagePalette
|
from . import ImageFile, ImagePalette, TiffImagePlugin
|
||||||
from ._typing import NumpyArray, StrOrBytesPath, TypeGuard
|
from ._typing import NumpyArray, StrOrBytesPath, TypeGuard
|
||||||
ID: list[str] = []
|
ID: list[str] = []
|
||||||
OPEN: dict[
|
OPEN: dict[
|
||||||
|
@ -676,7 +676,7 @@ class Image:
|
||||||
id(self),
|
id(self),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _repr_pretty_(self, p, cycle) -> None:
|
def _repr_pretty_(self, p, cycle: bool) -> None:
|
||||||
"""IPython plain text display support"""
|
"""IPython plain text display support"""
|
||||||
|
|
||||||
# Same as __repr__ but without unpredictable id(self),
|
# Same as __repr__ but without unpredictable id(self),
|
||||||
|
@ -1551,6 +1551,7 @@ class Image:
|
||||||
ifds.append((exif._get_ifd_dict(subifd_offset), subifd_offset))
|
ifds.append((exif._get_ifd_dict(subifd_offset), subifd_offset))
|
||||||
ifd1 = exif.get_ifd(ExifTags.IFD.IFD1)
|
ifd1 = exif.get_ifd(ExifTags.IFD.IFD1)
|
||||||
if ifd1 and ifd1.get(513):
|
if ifd1 and ifd1.get(513):
|
||||||
|
assert exif._info is not None
|
||||||
ifds.append((ifd1, exif._info.next))
|
ifds.append((ifd1, exif._info.next))
|
||||||
|
|
||||||
offset = None
|
offset = None
|
||||||
|
@ -1560,12 +1561,13 @@ class Image:
|
||||||
offset = current_offset
|
offset = current_offset
|
||||||
|
|
||||||
fp = self.fp
|
fp = self.fp
|
||||||
thumbnail_offset = ifd.get(513)
|
if ifd is not None:
|
||||||
if thumbnail_offset is not None:
|
thumbnail_offset = ifd.get(513)
|
||||||
thumbnail_offset += getattr(self, "_exif_offset", 0)
|
if thumbnail_offset is not None:
|
||||||
self.fp.seek(thumbnail_offset)
|
thumbnail_offset += getattr(self, "_exif_offset", 0)
|
||||||
data = self.fp.read(ifd.get(514))
|
self.fp.seek(thumbnail_offset)
|
||||||
fp = io.BytesIO(data)
|
data = self.fp.read(ifd.get(514))
|
||||||
|
fp = io.BytesIO(data)
|
||||||
|
|
||||||
with open(fp) as im:
|
with open(fp) as im:
|
||||||
from . import TiffImagePlugin
|
from . import TiffImagePlugin
|
||||||
|
@ -3869,14 +3871,14 @@ class Exif(_ExifBase):
|
||||||
bigtiff = False
|
bigtiff = False
|
||||||
_loaded = False
|
_loaded = False
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
self._data = {}
|
self._data: dict[int, Any] = {}
|
||||||
self._hidden_data = {}
|
self._hidden_data: dict[int, Any] = {}
|
||||||
self._ifds = {}
|
self._ifds: dict[int, dict[int, Any]] = {}
|
||||||
self._info = None
|
self._info: TiffImagePlugin.ImageFileDirectory_v2 | None = None
|
||||||
self._loaded_exif = None
|
self._loaded_exif: bytes | None = None
|
||||||
|
|
||||||
def _fixup(self, value):
|
def _fixup(self, value: Any) -> Any:
|
||||||
try:
|
try:
|
||||||
if len(value) == 1 and isinstance(value, tuple):
|
if len(value) == 1 and isinstance(value, tuple):
|
||||||
return value[0]
|
return value[0]
|
||||||
|
@ -3884,24 +3886,26 @@ class Exif(_ExifBase):
|
||||||
pass
|
pass
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def _fixup_dict(self, src_dict):
|
def _fixup_dict(self, src_dict: dict[int, Any]) -> dict[int, Any]:
|
||||||
# Helper function
|
# Helper function
|
||||||
# returns a dict with any single item tuples/lists as individual values
|
# returns a dict with any single item tuples/lists as individual values
|
||||||
return {k: self._fixup(v) for k, v in src_dict.items()}
|
return {k: self._fixup(v) for k, v in src_dict.items()}
|
||||||
|
|
||||||
def _get_ifd_dict(self, offset: int, group: int | None = None):
|
def _get_ifd_dict(
|
||||||
|
self, offset: int, group: int | None = None
|
||||||
|
) -> dict[int, Any] | None:
|
||||||
try:
|
try:
|
||||||
# an offset pointer to the location of the nested embedded IFD.
|
# an offset pointer to the location of the nested embedded IFD.
|
||||||
# It should be a long, but may be corrupted.
|
# It should be a long, but may be corrupted.
|
||||||
self.fp.seek(offset)
|
self.fp.seek(offset)
|
||||||
except (KeyError, TypeError):
|
except (KeyError, TypeError):
|
||||||
pass
|
return None
|
||||||
else:
|
else:
|
||||||
from . import TiffImagePlugin
|
from . import TiffImagePlugin
|
||||||
|
|
||||||
info = TiffImagePlugin.ImageFileDirectory_v2(self.head, group=group)
|
info = TiffImagePlugin.ImageFileDirectory_v2(self.head, group=group)
|
||||||
info.load(self.fp)
|
info.load(self.fp)
|
||||||
return self._fixup_dict(info)
|
return self._fixup_dict(dict(info))
|
||||||
|
|
||||||
def _get_head(self) -> bytes:
|
def _get_head(self) -> bytes:
|
||||||
version = b"\x2B" if self.bigtiff else b"\x2A"
|
version = b"\x2B" if self.bigtiff else b"\x2A"
|
||||||
|
@ -3966,7 +3970,7 @@ class Exif(_ExifBase):
|
||||||
self.fp.seek(offset)
|
self.fp.seek(offset)
|
||||||
self._info.load(self.fp)
|
self._info.load(self.fp)
|
||||||
|
|
||||||
def _get_merged_dict(self):
|
def _get_merged_dict(self) -> dict[int, Any]:
|
||||||
merged_dict = dict(self)
|
merged_dict = dict(self)
|
||||||
|
|
||||||
# get EXIF extension
|
# get EXIF extension
|
||||||
|
@ -4124,7 +4128,7 @@ class Exif(_ExifBase):
|
||||||
keys.update(self._info)
|
keys.update(self._info)
|
||||||
return len(keys)
|
return len(keys)
|
||||||
|
|
||||||
def __getitem__(self, tag: int):
|
def __getitem__(self, tag: int) -> Any:
|
||||||
if self._info is not None and tag not in self._data and tag in self._info:
|
if self._info is not None and tag not in self._data and tag in self._info:
|
||||||
self._data[tag] = self._fixup(self._info[tag])
|
self._data[tag] = self._fixup(self._info[tag])
|
||||||
del self._info[tag]
|
del self._info[tag]
|
||||||
|
@ -4133,7 +4137,7 @@ class Exif(_ExifBase):
|
||||||
def __contains__(self, tag: object) -> bool:
|
def __contains__(self, tag: object) -> bool:
|
||||||
return tag in self._data or (self._info is not None and tag in self._info)
|
return tag in self._data or (self._info is not None and tag in self._info)
|
||||||
|
|
||||||
def __setitem__(self, tag: int, value) -> None:
|
def __setitem__(self, tag: int, value: Any) -> None:
|
||||||
if self._info is not None and tag in self._info:
|
if self._info is not None and tag in self._info:
|
||||||
del self._info[tag]
|
del self._info[tag]
|
||||||
self._data[tag] = value
|
self._data[tag] = value
|
||||||
|
|
|
@ -86,7 +86,7 @@ def raise_oserror(error: int) -> OSError:
|
||||||
raise _get_oserror(error, encoder=False)
|
raise _get_oserror(error, encoder=False)
|
||||||
|
|
||||||
|
|
||||||
def _tilesort(t) -> int:
|
def _tilesort(t: _Tile) -> int:
|
||||||
# sort on offset
|
# sort on offset
|
||||||
return t[2]
|
return t[2]
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ class ImageFile(Image.Image):
|
||||||
return Image.MIME.get(self.format.upper())
|
return Image.MIME.get(self.format.upper())
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def __setstate__(self, state) -> None:
|
def __setstate__(self, state: list[Any]) -> None:
|
||||||
self.tile = []
|
self.tile = []
|
||||||
super().__setstate__(state)
|
super().__setstate__(state)
|
||||||
|
|
||||||
|
@ -525,7 +525,7 @@ class Parser:
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def _save(im, fp, tile, bufsize: int = 0) -> None:
|
def _save(im: Image.Image, fp: IO[bytes], tile, bufsize: int = 0) -> None:
|
||||||
"""Helper to save image based on tile list
|
"""Helper to save image based on tile list
|
||||||
|
|
||||||
:param im: Image object.
|
:param im: Image object.
|
||||||
|
@ -554,7 +554,12 @@ def _save(im, fp, tile, bufsize: int = 0) -> None:
|
||||||
|
|
||||||
|
|
||||||
def _encode_tile(
|
def _encode_tile(
|
||||||
im, fp: IO[bytes], tile: list[_Tile], bufsize: int, fh, exc=None
|
im: Image.Image,
|
||||||
|
fp: IO[bytes],
|
||||||
|
tile: list[_Tile],
|
||||||
|
bufsize: int,
|
||||||
|
fh,
|
||||||
|
exc: BaseException | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
for encoder_name, extents, offset, args in tile:
|
for encoder_name, extents, offset, args in tile:
|
||||||
if offset > 0:
|
if offset > 0:
|
||||||
|
@ -664,7 +669,11 @@ class PyCodec:
|
||||||
"""
|
"""
|
||||||
self.fd = fd
|
self.fd = fd
|
||||||
|
|
||||||
def setimage(self, im, extents=None):
|
def setimage(
|
||||||
|
self,
|
||||||
|
im: Image.core.ImagingCore,
|
||||||
|
extents: tuple[int, int, int, int] | None = None,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Called from ImageFile to set the core output image for the codec
|
Called from ImageFile to set the core output image for the codec
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import warnings
|
import warnings
|
||||||
from typing import IO, Any
|
from typing import IO, TYPE_CHECKING, Any
|
||||||
|
|
||||||
from . import Image, ImageFile
|
from . import Image, ImageFile
|
||||||
from ._binary import i16be as i16
|
from ._binary import i16be as i16
|
||||||
|
@ -51,6 +51,9 @@ from ._binary import o8
|
||||||
from ._binary import o16be as o16
|
from ._binary import o16be as o16
|
||||||
from .JpegPresets import presets
|
from .JpegPresets import presets
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .MpoImagePlugin import MpoImageFile
|
||||||
|
|
||||||
#
|
#
|
||||||
# Parser
|
# Parser
|
||||||
|
|
||||||
|
@ -329,7 +332,7 @@ class JpegImageFile(ImageFile.ImageFile):
|
||||||
format = "JPEG"
|
format = "JPEG"
|
||||||
format_description = "JPEG (ISO 10918)"
|
format_description = "JPEG (ISO 10918)"
|
||||||
|
|
||||||
def _open(self):
|
def _open(self) -> None:
|
||||||
s = self.fp.read(3)
|
s = self.fp.read(3)
|
||||||
|
|
||||||
if not _accept(s):
|
if not _accept(s):
|
||||||
|
@ -342,13 +345,13 @@ class JpegImageFile(ImageFile.ImageFile):
|
||||||
self._exif_offset = 0
|
self._exif_offset = 0
|
||||||
|
|
||||||
# JPEG specifics (internal)
|
# JPEG specifics (internal)
|
||||||
self.layer = []
|
self.layer: list[tuple[int, int, int, int]] = []
|
||||||
self.huffman_dc = {}
|
self.huffman_dc: dict[Any, Any] = {}
|
||||||
self.huffman_ac = {}
|
self.huffman_ac: dict[Any, Any] = {}
|
||||||
self.quantization = {}
|
self.quantization: dict[int, list[int]] = {}
|
||||||
self.app = {} # compatibility
|
self.app: dict[str, bytes] = {} # compatibility
|
||||||
self.applist = []
|
self.applist: list[tuple[str, bytes]] = []
|
||||||
self.icclist = []
|
self.icclist: list[bytes] = []
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
i = s[0]
|
i = s[0]
|
||||||
|
@ -831,7 +834,9 @@ def _save_cjpeg(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||||
|
|
||||||
##
|
##
|
||||||
# Factory for making JPEG and MPO instances
|
# Factory for making JPEG and MPO instances
|
||||||
def jpeg_factory(fp: IO[bytes] | None = None, filename: str | bytes | None = None):
|
def jpeg_factory(
|
||||||
|
fp: IO[bytes] | None = None, filename: str | bytes | None = None
|
||||||
|
) -> JpegImageFile | MpoImageFile:
|
||||||
im = JpegImageFile(fp, filename)
|
im = JpegImageFile(fp, filename)
|
||||||
try:
|
try:
|
||||||
mpheader = im._getmp()
|
mpheader = im._getmp()
|
||||||
|
|
|
@ -40,7 +40,7 @@ import warnings
|
||||||
import zlib
|
import zlib
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
from typing import IO, TYPE_CHECKING, Any, NamedTuple, NoReturn
|
from typing import IO, TYPE_CHECKING, Any, NamedTuple, NoReturn, cast
|
||||||
|
|
||||||
from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence
|
from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence
|
||||||
from ._binary import i16be as i16
|
from ._binary import i16be as i16
|
||||||
|
@ -1223,7 +1223,11 @@ def _write_multiple_frames(
|
||||||
if default_image:
|
if default_image:
|
||||||
if im.mode != mode:
|
if im.mode != mode:
|
||||||
im = im.convert(mode)
|
im = im.convert(mode)
|
||||||
ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)])
|
ImageFile._save(
|
||||||
|
im,
|
||||||
|
cast(IO[bytes], _idat(fp, chunk)),
|
||||||
|
[("zip", (0, 0) + im.size, 0, rawmode)],
|
||||||
|
)
|
||||||
|
|
||||||
seq_num = 0
|
seq_num = 0
|
||||||
for frame, frame_data in enumerate(im_frames):
|
for frame, frame_data in enumerate(im_frames):
|
||||||
|
@ -1258,14 +1262,14 @@ def _write_multiple_frames(
|
||||||
# first frame must be in IDAT chunks for backwards compatibility
|
# first frame must be in IDAT chunks for backwards compatibility
|
||||||
ImageFile._save(
|
ImageFile._save(
|
||||||
im_frame,
|
im_frame,
|
||||||
_idat(fp, chunk),
|
cast(IO[bytes], _idat(fp, chunk)),
|
||||||
[("zip", (0, 0) + im_frame.size, 0, rawmode)],
|
[("zip", (0, 0) + im_frame.size, 0, rawmode)],
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
fdat_chunks = _fdat(fp, chunk, seq_num)
|
fdat_chunks = _fdat(fp, chunk, seq_num)
|
||||||
ImageFile._save(
|
ImageFile._save(
|
||||||
im_frame,
|
im_frame,
|
||||||
fdat_chunks,
|
cast(IO[bytes], fdat_chunks),
|
||||||
[("zip", (0, 0) + im_frame.size, 0, rawmode)],
|
[("zip", (0, 0) + im_frame.size, 0, rawmode)],
|
||||||
)
|
)
|
||||||
seq_num = fdat_chunks.seq_num
|
seq_num = fdat_chunks.seq_num
|
||||||
|
@ -1465,7 +1469,9 @@ def _save(
|
||||||
)
|
)
|
||||||
if single_im:
|
if single_im:
|
||||||
ImageFile._save(
|
ImageFile._save(
|
||||||
single_im, _idat(fp, chunk), [("zip", (0, 0) + single_im.size, 0, rawmode)]
|
single_im,
|
||||||
|
cast(IO[bytes], _idat(fp, chunk)),
|
||||||
|
[("zip", (0, 0) + single_im.size, 0, rawmode)],
|
||||||
)
|
)
|
||||||
|
|
||||||
if info:
|
if info:
|
||||||
|
|
|
@ -288,8 +288,10 @@ def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:4] in PREFIXES
|
return prefix[:4] in PREFIXES
|
||||||
|
|
||||||
|
|
||||||
def _limit_rational(val, max_val):
|
def _limit_rational(
|
||||||
inv = abs(val) > 1
|
val: float | Fraction | IFDRational, max_val: int
|
||||||
|
) -> tuple[float, float]:
|
||||||
|
inv = abs(float(val)) > 1
|
||||||
n_d = IFDRational(1 / val if inv else val).limit_rational(max_val)
|
n_d = IFDRational(1 / val if inv else val).limit_rational(max_val)
|
||||||
return n_d[::-1] if inv else n_d
|
return n_d[::-1] if inv else n_d
|
||||||
|
|
||||||
|
@ -792,7 +794,9 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
||||||
return value + b"\0"
|
return value + b"\0"
|
||||||
|
|
||||||
@_register_loader(5, 8)
|
@_register_loader(5, 8)
|
||||||
def load_rational(self, data, legacy_api: bool = True):
|
def load_rational(
|
||||||
|
self, data: bytes, legacy_api: bool = True
|
||||||
|
) -> tuple[tuple[int, int] | IFDRational, ...]:
|
||||||
vals = self._unpack(f"{len(data) // 4}L", data)
|
vals = self._unpack(f"{len(data) // 4}L", data)
|
||||||
|
|
||||||
def combine(a: int, b: int) -> tuple[int, int] | IFDRational:
|
def combine(a: int, b: int) -> tuple[int, int] | IFDRational:
|
||||||
|
@ -801,7 +805,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
||||||
return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2]))
|
return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2]))
|
||||||
|
|
||||||
@_register_writer(5)
|
@_register_writer(5)
|
||||||
def write_rational(self, *values) -> bytes:
|
def write_rational(self, *values: IFDRational) -> bytes:
|
||||||
return b"".join(
|
return b"".join(
|
||||||
self._pack("2L", *_limit_rational(frac, 2**32 - 1)) for frac in values
|
self._pack("2L", *_limit_rational(frac, 2**32 - 1)) for frac in values
|
||||||
)
|
)
|
||||||
|
@ -828,7 +832,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
||||||
return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2]))
|
return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2]))
|
||||||
|
|
||||||
@_register_writer(10)
|
@_register_writer(10)
|
||||||
def write_signed_rational(self, *values) -> bytes:
|
def write_signed_rational(self, *values: IFDRational) -> bytes:
|
||||||
return b"".join(
|
return b"".join(
|
||||||
self._pack("2l", *_limit_signed_rational(frac, 2**31 - 1, -(2**31)))
|
self._pack("2l", *_limit_signed_rational(frac, 2**31 - 1, -(2**31)))
|
||||||
for frac in values
|
for frac in values
|
||||||
|
|
Loading…
Reference in New Issue
Block a user