mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-13 18:56:17 +03:00
Added BigTIFF reading
This commit is contained in:
parent
37d28ce514
commit
fc7319318e
BIN
Tests/images/hopper_bigtiff.tif
Normal file
BIN
Tests/images/hopper_bigtiff.tif
Normal file
Binary file not shown.
|
@ -87,6 +87,10 @@ class TestFileTiff:
|
||||||
|
|
||||||
assert_image_similar_tofile(im, "Tests/images/pil136.png", 1)
|
assert_image_similar_tofile(im, "Tests/images/pil136.png", 1)
|
||||||
|
|
||||||
|
def test_bigtiff(self):
|
||||||
|
with Image.open("Tests/images/hopper_bigtiff.tif") as im:
|
||||||
|
assert_image_equal_tofile(im, "Tests/images/hopper.tif")
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"file_name,mode,size,offset",
|
"file_name,mode,size,offset",
|
||||||
[
|
[
|
||||||
|
|
|
@ -49,7 +49,7 @@ except ImportError:
|
||||||
# PILLOW_VERSION was removed in Pillow 9.0.0.
|
# PILLOW_VERSION was removed in Pillow 9.0.0.
|
||||||
# Use __version__ instead.
|
# Use __version__ instead.
|
||||||
from . import ImageMode, TiffTags, UnidentifiedImageError, __version__, _plugins
|
from . import ImageMode, TiffTags, UnidentifiedImageError, __version__, _plugins
|
||||||
from ._binary import i32le
|
from ._binary import i32le, o32be, o32le
|
||||||
from ._util import deferred_error, isPath
|
from ._util import deferred_error, isPath
|
||||||
|
|
||||||
|
|
||||||
|
@ -1416,6 +1416,7 @@ class Image:
|
||||||
"".join(self.info["Raw profile type exif"].split("\n")[3:])
|
"".join(self.info["Raw profile type exif"].split("\n")[3:])
|
||||||
)
|
)
|
||||||
elif hasattr(self, "tag_v2"):
|
elif hasattr(self, "tag_v2"):
|
||||||
|
self._exif.bigtiff = self.tag_v2._bigtiff
|
||||||
self._exif.endian = self.tag_v2._endian
|
self._exif.endian = self.tag_v2._endian
|
||||||
self._exif.load_from_fp(self.fp, self.tag_v2._offset)
|
self._exif.load_from_fp(self.fp, self.tag_v2._offset)
|
||||||
if exif_info is not None:
|
if exif_info is not None:
|
||||||
|
@ -3423,6 +3424,7 @@ atexit.register(core.clear_cache)
|
||||||
|
|
||||||
class Exif(MutableMapping):
|
class Exif(MutableMapping):
|
||||||
endian = None
|
endian = None
|
||||||
|
bigtiff = False
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._data = {}
|
self._data = {}
|
||||||
|
@ -3458,10 +3460,15 @@ class Exif(MutableMapping):
|
||||||
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"
|
||||||
if self.endian == "<":
|
if self.endian == "<":
|
||||||
return b"II\x2A\x00\x08\x00\x00\x00"
|
head = b"II" + version + b"\x00" + o32le(8)
|
||||||
else:
|
else:
|
||||||
return b"MM\x00\x2A\x00\x00\x00\x08"
|
head = b"MM\x00" + version + o32be(8)
|
||||||
|
if self.bigtiff:
|
||||||
|
head += o32le(8) if self.endian == "<" else o32be(8)
|
||||||
|
head += b"\x00\x00\x00\x00"
|
||||||
|
return head
|
||||||
|
|
||||||
def load(self, data):
|
def load(self, data):
|
||||||
# Extract EXIF information. This is highly experimental,
|
# Extract EXIF information. This is highly experimental,
|
||||||
|
|
|
@ -260,6 +260,8 @@ PREFIXES = [
|
||||||
b"II\x2A\x00", # Valid TIFF header with little-endian byte order
|
b"II\x2A\x00", # Valid TIFF header with little-endian byte order
|
||||||
b"MM\x2A\x00", # Invalid TIFF header, assume big-endian
|
b"MM\x2A\x00", # Invalid TIFF header, assume big-endian
|
||||||
b"II\x00\x2A", # Invalid TIFF header, assume little-endian
|
b"II\x00\x2A", # Invalid TIFF header, assume little-endian
|
||||||
|
b"MM\x00\x2B", # BigTIFF with big-endian byte order
|
||||||
|
b"II\x2B\x00", # BigTIFF with little-endian byte order
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -502,11 +504,14 @@ class ImageFileDirectory_v2(MutableMapping):
|
||||||
self._endian = "<"
|
self._endian = "<"
|
||||||
else:
|
else:
|
||||||
raise SyntaxError("not a TIFF IFD")
|
raise SyntaxError("not a TIFF IFD")
|
||||||
|
self._bigtiff = ifh[2] == 43
|
||||||
self.group = group
|
self.group = group
|
||||||
self.tagtype = {}
|
self.tagtype = {}
|
||||||
""" Dictionary of tag types """
|
""" Dictionary of tag types """
|
||||||
self.reset()
|
self.reset()
|
||||||
(self.next,) = self._unpack("L", ifh[4:])
|
(self.next,) = (
|
||||||
|
self._unpack("Q", ifh[8:]) if self._bigtiff else self._unpack("L", ifh[4:])
|
||||||
|
)
|
||||||
self._legacy_api = False
|
self._legacy_api = False
|
||||||
|
|
||||||
prefix = property(lambda self: self._prefix)
|
prefix = property(lambda self: self._prefix)
|
||||||
|
@ -699,6 +704,7 @@ class ImageFileDirectory_v2(MutableMapping):
|
||||||
(TiffTags.FLOAT, "f", "float"),
|
(TiffTags.FLOAT, "f", "float"),
|
||||||
(TiffTags.DOUBLE, "d", "double"),
|
(TiffTags.DOUBLE, "d", "double"),
|
||||||
(TiffTags.IFD, "L", "long"),
|
(TiffTags.IFD, "L", "long"),
|
||||||
|
(TiffTags.LONG8, "Q", "long8"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -776,8 +782,17 @@ class ImageFileDirectory_v2(MutableMapping):
|
||||||
self._offset = fp.tell()
|
self._offset = fp.tell()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for i in range(self._unpack("H", self._ensure_read(fp, 2))[0]):
|
tag_count = (
|
||||||
tag, typ, count, data = self._unpack("HHL4s", self._ensure_read(fp, 12))
|
self._unpack("Q", self._ensure_read(fp, 8))
|
||||||
|
if self._bigtiff
|
||||||
|
else self._unpack("H", self._ensure_read(fp, 2))
|
||||||
|
)[0]
|
||||||
|
for i in range(tag_count):
|
||||||
|
tag, typ, count, data = (
|
||||||
|
self._unpack("HHQ8s", self._ensure_read(fp, 20))
|
||||||
|
if self._bigtiff
|
||||||
|
else self._unpack("HHL4s", self._ensure_read(fp, 12))
|
||||||
|
)
|
||||||
|
|
||||||
tagname = TiffTags.lookup(tag, self.group).name
|
tagname = TiffTags.lookup(tag, self.group).name
|
||||||
typname = TYPES.get(typ, "unknown")
|
typname = TYPES.get(typ, "unknown")
|
||||||
|
@ -789,9 +804,9 @@ class ImageFileDirectory_v2(MutableMapping):
|
||||||
logger.debug(msg + f" - unsupported type {typ}")
|
logger.debug(msg + f" - unsupported type {typ}")
|
||||||
continue # ignore unsupported type
|
continue # ignore unsupported type
|
||||||
size = count * unit_size
|
size = count * unit_size
|
||||||
if size > 4:
|
if size > (8 if self._bigtiff else 4):
|
||||||
here = fp.tell()
|
here = fp.tell()
|
||||||
(offset,) = self._unpack("L", data)
|
(offset,) = self._unpack("Q" if self._bigtiff else "L", data)
|
||||||
msg += f" Tag Location: {here} - Data Location: {offset}"
|
msg += f" Tag Location: {here} - Data Location: {offset}"
|
||||||
fp.seek(offset)
|
fp.seek(offset)
|
||||||
data = ImageFile._safe_read(fp, size)
|
data = ImageFile._safe_read(fp, size)
|
||||||
|
@ -820,7 +835,11 @@ class ImageFileDirectory_v2(MutableMapping):
|
||||||
)
|
)
|
||||||
logger.debug(msg)
|
logger.debug(msg)
|
||||||
|
|
||||||
(self.next,) = self._unpack("L", self._ensure_read(fp, 4))
|
(self.next,) = (
|
||||||
|
self._unpack("Q", self._ensure_read(fp, 8))
|
||||||
|
if self._bigtiff
|
||||||
|
else self._unpack("L", self._ensure_read(fp, 4))
|
||||||
|
)
|
||||||
except OSError as msg:
|
except OSError as msg:
|
||||||
warnings.warn(str(msg))
|
warnings.warn(str(msg))
|
||||||
return
|
return
|
||||||
|
@ -1042,6 +1061,8 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
# Header
|
# Header
|
||||||
ifh = self.fp.read(8)
|
ifh = self.fp.read(8)
|
||||||
|
if ifh[2] == 43:
|
||||||
|
ifh += self.fp.read(8)
|
||||||
|
|
||||||
self.tag_v2 = ImageFileDirectory_v2(ifh)
|
self.tag_v2 = ImageFileDirectory_v2(ifh)
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,7 @@ SIGNED_RATIONAL = 10
|
||||||
FLOAT = 11
|
FLOAT = 11
|
||||||
DOUBLE = 12
|
DOUBLE = 12
|
||||||
IFD = 13
|
IFD = 13
|
||||||
|
LONG8 = 16
|
||||||
|
|
||||||
TAGS_V2 = {
|
TAGS_V2 = {
|
||||||
254: ("NewSubfileType", LONG, 1),
|
254: ("NewSubfileType", LONG, 1),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user