Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added tag data for IFD groups #5554

Merged
merged 2 commits into from Jun 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions Tests/test_file_tiff_metadata.py
Expand Up @@ -378,6 +378,20 @@ def test_too_many_entries():
pytest.warns(UserWarning, lambda: ifd[277])


def test_tag_group_data():
base_ifd = TiffImagePlugin.ImageFileDirectory_v2()
interop_ifd = TiffImagePlugin.ImageFileDirectory_v2(group=40965)
for ifd in (base_ifd, interop_ifd):
ifd[2] = "test"
ifd[256] = 10

assert base_ifd.tagtype[256] == 4
assert interop_ifd.tagtype[256] != base_ifd.tagtype[256]

assert interop_ifd.tagtype[2] == 7
assert base_ifd.tagtype[2] != interop_ifd.tagtype[256]


def test_empty_subifd(tmp_path):
out = str(tmp_path / "temp.jpg")

Expand Down
16 changes: 10 additions & 6 deletions src/PIL/TiffImagePlugin.py
Expand Up @@ -465,7 +465,7 @@ class ImageFileDirectory_v2(MutableMapping):

"""

def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None):
def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None, group=None):
"""Initialize an ImageFileDirectory.

To construct an ImageFileDirectory from a real file, pass the 8-byte
Expand All @@ -485,6 +485,7 @@ def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None):
self._endian = "<"
else:
raise SyntaxError("not a TIFF IFD")
self.group = group
self.tagtype = {}
""" Dictionary of tag types """
self.reset()
Expand Down Expand Up @@ -516,7 +517,10 @@ def named(self):

Returns the complete tag dictionary, with named tags where possible.
"""
return {TiffTags.lookup(code).name: value for code, value in self.items()}
return {
TiffTags.lookup(code, self.group).name: value
for code, value in self.items()
}

def __len__(self):
return len(set(self._tagdata) | set(self._tags_v2))
Expand All @@ -541,7 +545,7 @@ def __setitem__(self, tag, value):
def _setitem(self, tag, value, legacy_api):
basetypes = (Number, bytes, str)

info = TiffTags.lookup(tag)
info = TiffTags.lookup(tag, self.group)
values = [value] if isinstance(value, basetypes) else value

if tag not in self.tagtype:
Expand Down Expand Up @@ -758,7 +762,7 @@ def load(self, fp):
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))

tagname = TiffTags.lookup(tag).name
tagname = TiffTags.lookup(tag, self.group).name
typname = TYPES.get(typ, "unknown")
msg = f"tag: {tagname} ({tag}) - type: {typname} ({typ})"

Expand Down Expand Up @@ -825,7 +829,7 @@ def tobytes(self, offset=0):
ifh = b"II\x2A\x00\x08\x00\x00\x00"
else:
ifh = b"MM\x00\x2A\x00\x00\x00\x08"
ifd = ImageFileDirectory_v2(ifh)
ifd = ImageFileDirectory_v2(ifh, group=tag)
values = self._tags_v2[tag]
for ifd_tag, ifd_value in values.items():
ifd[ifd_tag] = ifd_value
Expand All @@ -834,7 +838,7 @@ def tobytes(self, offset=0):
values = value if isinstance(value, tuple) else (value,)
data = self._write_dispatch[typ](self, *values)

tagname = TiffTags.lookup(tag).name
tagname = TiffTags.lookup(tag, self.group).name
typname = "ifd" if is_ifd else TYPES.get(typ, "unknown")
msg = f"save: {tagname} ({tag}) - type: {typname} ({typ})"
msg += " - value: " + (
Expand Down
25 changes: 23 additions & 2 deletions src/PIL/TiffTags.py
Expand Up @@ -33,7 +33,7 @@ def cvt_enum(self, value):
return self.enum.get(value, value) if self.enum else value


def lookup(tag):
def lookup(tag, group=None):
"""
:param tag: Integer tag number
:returns: Taginfo namedtuple, From the TAGS_V2 info if possible,
Expand All @@ -42,7 +42,11 @@ def lookup(tag):

"""

return TAGS_V2.get(tag, TagInfo(tag, TAGS.get(tag, "unknown")))
if group is not None:
info = TAGS_V2_GROUPS[group].get(tag) if group in TAGS_V2_GROUPS else None
else:
info = TAGS_V2.get(tag)
return info or TagInfo(tag, TAGS.get(tag, "unknown"))


##
Expand Down Expand Up @@ -213,6 +217,19 @@ def lookup(tag):
50838: ("ImageJMetaDataByteCounts", LONG, 0), # Can be more than one
50839: ("ImageJMetaData", UNDEFINED, 1), # see Issue #2006
}
TAGS_V2_GROUPS = {
# ExifIFD
34665: {
36864: ("ExifVersion", UNDEFINED, 1),
40960: ("FlashPixVersion", UNDEFINED, 1),
40965: ("InteroperabilityIFD", LONG, 1),
41730: ("CFAPattern", UNDEFINED, 1),
},
# GPSInfoIFD
34853: {},
# InteroperabilityIFD
40965: {1: ("InteropIndex", ASCII, 1), 2: ("InteropVersion", UNDEFINED, 1)},
}

# Legacy Tags structure
# these tags aren't included above, but were in the previous versions
Expand Down Expand Up @@ -371,6 +388,10 @@ def _populate():

TAGS_V2[k] = TagInfo(k, *v)

for group, tags in TAGS_V2_GROUPS.items():
for k, v in tags.items():
tags[k] = TagInfo(k, *v)


_populate()
##
Expand Down