Added BigTIFF reading

This commit is contained in:
Andrew Murray 2022-03-01 09:23:12 +11:00
parent 37d28ce514
commit fc7319318e
5 changed files with 42 additions and 9 deletions

Binary file not shown.

View File

@ -87,6 +87,10 @@ class TestFileTiff:
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(
"file_name,mode,size,offset",
[

View File

@ -49,7 +49,7 @@ except ImportError:
# PILLOW_VERSION was removed in Pillow 9.0.0.
# Use __version__ instead.
from . import ImageMode, TiffTags, UnidentifiedImageError, __version__, _plugins
from ._binary import i32le
from ._binary import i32le, o32be, o32le
from ._util import deferred_error, isPath
@ -1416,6 +1416,7 @@ class Image:
"".join(self.info["Raw profile type exif"].split("\n")[3:])
)
elif hasattr(self, "tag_v2"):
self._exif.bigtiff = self.tag_v2._bigtiff
self._exif.endian = self.tag_v2._endian
self._exif.load_from_fp(self.fp, self.tag_v2._offset)
if exif_info is not None:
@ -3423,6 +3424,7 @@ atexit.register(core.clear_cache)
class Exif(MutableMapping):
endian = None
bigtiff = False
def __init__(self):
self._data = {}
@ -3458,10 +3460,15 @@ class Exif(MutableMapping):
return self._fixup_dict(info)
def _get_head(self):
version = b"\x2B" if self.bigtiff else b"\x2A"
if self.endian == "<":
return b"II\x2A\x00\x08\x00\x00\x00"
head = b"II" + version + b"\x00" + o32le(8)
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):
# Extract EXIF information. This is highly experimental,

View File

@ -260,6 +260,8 @@ PREFIXES = [
b"II\x2A\x00", # Valid TIFF header with little-endian byte order
b"MM\x2A\x00", # Invalid TIFF header, assume big-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 = "<"
else:
raise SyntaxError("not a TIFF IFD")
self._bigtiff = ifh[2] == 43
self.group = group
self.tagtype = {}
""" Dictionary of tag types """
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
prefix = property(lambda self: self._prefix)
@ -699,6 +704,7 @@ class ImageFileDirectory_v2(MutableMapping):
(TiffTags.FLOAT, "f", "float"),
(TiffTags.DOUBLE, "d", "double"),
(TiffTags.IFD, "L", "long"),
(TiffTags.LONG8, "Q", "long8"),
],
)
)
@ -776,8 +782,17 @@ class ImageFileDirectory_v2(MutableMapping):
self._offset = fp.tell()
try:
for i in range(self._unpack("H", self._ensure_read(fp, 2))[0]):
tag, typ, count, data = self._unpack("HHL4s", self._ensure_read(fp, 12))
tag_count = (
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
typname = TYPES.get(typ, "unknown")
@ -789,9 +804,9 @@ class ImageFileDirectory_v2(MutableMapping):
logger.debug(msg + f" - unsupported type {typ}")
continue # ignore unsupported type
size = count * unit_size
if size > 4:
if size > (8 if self._bigtiff else 4):
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}"
fp.seek(offset)
data = ImageFile._safe_read(fp, size)
@ -820,7 +835,11 @@ class ImageFileDirectory_v2(MutableMapping):
)
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:
warnings.warn(str(msg))
return
@ -1042,6 +1061,8 @@ class TiffImageFile(ImageFile.ImageFile):
# Header
ifh = self.fp.read(8)
if ifh[2] == 43:
ifh += self.fp.read(8)
self.tag_v2 = ImageFileDirectory_v2(ifh)

View File

@ -74,6 +74,7 @@ SIGNED_RATIONAL = 10
FLOAT = 11
DOUBLE = 12
IFD = 13
LONG8 = 16
TAGS_V2 = {
254: ("NewSubfileType", LONG, 1),