mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-12 10:16:17 +03:00
Merge pull request #8642 from radarhere/bigtiff
This commit is contained in:
commit
c7026d9bc8
|
@ -115,6 +115,13 @@ class TestFileTiff:
|
|||
outfile = str(tmp_path / "temp.tif")
|
||||
im.save(outfile, save_all=True, append_images=[im], tiffinfo=im.tag_v2)
|
||||
|
||||
def test_bigtiff_save(self, tmp_path: Path) -> None:
|
||||
outfile = str(tmp_path / "temp.tif")
|
||||
hopper().save(outfile, big_tiff=True)
|
||||
|
||||
with Image.open(outfile) as im:
|
||||
assert im.tag_v2._bigtiff is True
|
||||
|
||||
def test_seek_too_large(self) -> None:
|
||||
with pytest.raises(ValueError, match="Unable to seek to frame"):
|
||||
Image.open("Tests/images/seek_too_large.tif")
|
||||
|
|
|
@ -1208,6 +1208,11 @@ The :py:meth:`~PIL.Image.Image.save` method can take the following keyword argum
|
|||
|
||||
.. versionadded:: 8.4.0
|
||||
|
||||
**big_tiff**
|
||||
If true, the image will be saved as a BigTIFF.
|
||||
|
||||
.. versionadded:: 11.1.0
|
||||
|
||||
**compression**
|
||||
A string containing the desired compression method for the
|
||||
file. (valid only with libtiff installed) Valid compression
|
||||
|
|
|
@ -1,25 +1,6 @@
|
|||
11.1.0
|
||||
------
|
||||
|
||||
Security
|
||||
========
|
||||
|
||||
TODO
|
||||
^^^^
|
||||
|
||||
TODO
|
||||
|
||||
:cve:`YYYY-XXXXX`: TODO
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
TODO
|
||||
|
||||
Backwards Incompatible Changes
|
||||
==============================
|
||||
|
||||
TODO
|
||||
^^^^
|
||||
|
||||
Deprecations
|
||||
============
|
||||
|
||||
|
@ -66,6 +47,13 @@ zlib library, and what version of zlib-ng is being used::
|
|||
features.check_feature("zlib_ng") # True or False
|
||||
features.version_feature("zlib_ng") # "2.2.2" for example, or None
|
||||
|
||||
Saving TIFF as BigTIFF
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
TIFF images can now be saved as BigTIFF using a ``big_tiff`` argument::
|
||||
|
||||
im.save("out.tiff", big_tiff=True)
|
||||
|
||||
Other Changes
|
||||
=============
|
||||
|
||||
|
|
|
@ -582,7 +582,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
ifh: bytes = b"II\052\0\0\0\0\0",
|
||||
ifh: bytes = b"II\x2A\x00\x00\x00\x00\x00",
|
||||
prefix: bytes | None = None,
|
||||
group: int | None = None,
|
||||
) -> None:
|
||||
|
@ -949,16 +949,26 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
|||
warnings.warn(str(msg))
|
||||
return
|
||||
|
||||
def _get_ifh(self):
|
||||
ifh = self._prefix + self._pack("H", 43 if self._bigtiff else 42)
|
||||
if self._bigtiff:
|
||||
ifh += self._pack("HH", 8, 0)
|
||||
ifh += self._pack("Q", 16) if self._bigtiff else self._pack("L", 8)
|
||||
|
||||
return ifh
|
||||
|
||||
def tobytes(self, offset: int = 0) -> bytes:
|
||||
# FIXME What about tagdata?
|
||||
result = self._pack("H", len(self._tags_v2))
|
||||
result = self._pack("Q" if self._bigtiff else "H", len(self._tags_v2))
|
||||
|
||||
entries: list[tuple[int, int, int, bytes, bytes]] = []
|
||||
offset = offset + len(result) + len(self._tags_v2) * 12 + 4
|
||||
offset += len(result) + len(self._tags_v2) * (20 if self._bigtiff else 12) + 4
|
||||
stripoffsets = None
|
||||
|
||||
# pass 1: convert tags to binary format
|
||||
# always write tags in ascending order
|
||||
fmt = "Q" if self._bigtiff else "L"
|
||||
fmt_size = 8 if self._bigtiff else 4
|
||||
for tag, value in sorted(self._tags_v2.items()):
|
||||
if tag == STRIPOFFSETS:
|
||||
stripoffsets = len(entries)
|
||||
|
@ -966,11 +976,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
|||
logger.debug("Tag %s, Type: %s, Value: %s", tag, typ, repr(value))
|
||||
is_ifd = typ == TiffTags.LONG and isinstance(value, dict)
|
||||
if is_ifd:
|
||||
if self._endian == "<":
|
||||
ifh = b"II\x2A\x00\x08\x00\x00\x00"
|
||||
else:
|
||||
ifh = b"MM\x00\x2A\x00\x00\x00\x08"
|
||||
ifd = ImageFileDirectory_v2(ifh, group=tag)
|
||||
ifd = ImageFileDirectory_v2(self._get_ifh(), group=tag)
|
||||
values = self._tags_v2[tag]
|
||||
for ifd_tag, ifd_value in values.items():
|
||||
ifd[ifd_tag] = ifd_value
|
||||
|
@ -993,10 +999,10 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
|||
else:
|
||||
count = len(values)
|
||||
# figure out if data fits into the entry
|
||||
if len(data) <= 4:
|
||||
entries.append((tag, typ, count, data.ljust(4, b"\0"), b""))
|
||||
if len(data) <= fmt_size:
|
||||
entries.append((tag, typ, count, data.ljust(fmt_size, b"\0"), b""))
|
||||
else:
|
||||
entries.append((tag, typ, count, self._pack("L", offset), data))
|
||||
entries.append((tag, typ, count, self._pack(fmt, offset), data))
|
||||
offset += (len(data) + 1) // 2 * 2 # pad to word
|
||||
|
||||
# update strip offset data to point beyond auxiliary data
|
||||
|
@ -1007,13 +1013,15 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
|||
values = [val + offset for val in handler(self, data, self.legacy_api)]
|
||||
data = self._write_dispatch[typ](self, *values)
|
||||
else:
|
||||
value = self._pack("L", self._unpack("L", value)[0] + offset)
|
||||
value = self._pack(fmt, self._unpack(fmt, value)[0] + offset)
|
||||
entries[stripoffsets] = tag, typ, count, value, data
|
||||
|
||||
# pass 2: write entries to file
|
||||
for tag, typ, count, value, data in entries:
|
||||
logger.debug("%s %s %s %s %s", tag, typ, count, repr(value), repr(data))
|
||||
result += self._pack("HHL4s", tag, typ, count, value)
|
||||
result += self._pack(
|
||||
"HHQ8s" if self._bigtiff else "HHL4s", tag, typ, count, value
|
||||
)
|
||||
|
||||
# -- overwrite here for multi-page --
|
||||
result += b"\0\0\0\0" # end of entries
|
||||
|
@ -1028,8 +1036,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
|||
|
||||
def save(self, fp: IO[bytes]) -> int:
|
||||
if fp.tell() == 0: # skip TIFF header on subsequent pages
|
||||
# tiff header -- PIL always starts the first IFD at offset 8
|
||||
fp.write(self._prefix + self._pack("HL", 42, 8))
|
||||
fp.write(self._get_ifh())
|
||||
|
||||
offset = fp.tell()
|
||||
result = self.tobytes(offset)
|
||||
|
@ -1680,10 +1687,13 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
|||
msg = f"cannot write mode {im.mode} as TIFF"
|
||||
raise OSError(msg) from e
|
||||
|
||||
ifd = ImageFileDirectory_v2(prefix=prefix)
|
||||
|
||||
encoderinfo = im.encoderinfo
|
||||
encoderconfig = im.encoderconfig
|
||||
|
||||
ifd = ImageFileDirectory_v2(prefix=prefix)
|
||||
if encoderinfo.get("big_tiff"):
|
||||
ifd._bigtiff = True
|
||||
|
||||
try:
|
||||
compression = encoderinfo["compression"]
|
||||
except KeyError:
|
||||
|
|
Loading…
Reference in New Issue
Block a user