Added get_ifd method to access embedded IFDs

This commit is contained in:
Andrew Murray 2019-03-12 10:39:12 +11:00
parent d5db62be7b
commit 3caec4344e
2 changed files with 46 additions and 24 deletions

View File

@ -284,7 +284,7 @@ class TestPyDecoder(PillowTestCase):
@unittest.skipIf(not HAVE_WEBP or not _webp.HAVE_WEBPANIM, @unittest.skipIf(not HAVE_WEBP or not _webp.HAVE_WEBPANIM,
"WebP support not installed with animation") "WebP support not installed with animation")
def test_exif_webp(self): def test_exif_webp(self):
im = Image.open("Tests/images/hopper.webp") # WebP im = Image.open("Tests/images/hopper.webp")
exif = im.getexif() exif = im.getexif()
self.assertEqual(exif, {}) self.assertEqual(exif, {})
@ -321,5 +321,15 @@ class TestPyDecoder(PillowTestCase):
self.assertEqual(reloaded_exif, { self.assertEqual(reloaded_exif, {
258: 8, 258: 8,
40963: 455, 40963: 455,
305: 'Pillow test' 305: 'Pillow test',
})
def test_exif_interop(self):
im = Image.open("Tests/images/flower.jpg")
exif = im.getexif()
self.assertEqual(exif.get_ifd(0xa005), {
1: 'R98',
2: b'0100',
4097: 2272,
4098: 1704,
}) })

View File

@ -689,6 +689,7 @@ class PyDecoder(object):
class Exif(MutableMapping): class Exif(MutableMapping):
_data = {} _data = {}
_ifds = {}
endian = "<" endian = "<"
def _fixup_dict(self, src_dict): def _fixup_dict(self, src_dict):
@ -704,6 +705,19 @@ class Exif(MutableMapping):
return {k: _fixup(v) for k, v in src_dict.items()} return {k: _fixup(v) for k, v in src_dict.items()}
def _get_ifd_dict(self, fp, head, tag):
try:
# an offset pointer to the location of the nested embedded ifd.
# It should be a long, but may be corrupted.
fp.seek(self._data[tag])
except (KeyError, TypeError):
pass
else:
from . import TiffImagePlugin
info = TiffImagePlugin.ImageFileDirectory_v1(head)
info.load(fp)
return self._fixup_dict(info)
def load(self, data): def load(self, data):
# Extract EXIF information. This is highly experimental, # Extract EXIF information. This is highly experimental,
# and is likely to be replaced with something better in a future # and is likely to be replaced with something better in a future
@ -720,30 +734,25 @@ class Exif(MutableMapping):
fp.seek(info.next) fp.seek(info.next)
info.load(fp) info.load(fp)
self._data = dict(self._fixup_dict(info)) self._data = dict(self._fixup_dict(info))
# get exif extension # get exif extension
try: ifd = self._get_ifd_dict(fp, head, 0x8769)
# exif field 0x8769 is an offset pointer to the location if ifd:
# of the nested embedded exif ifd. self._data.update(ifd)
# It should be a long, but may be corrupted. self._ifds[0x8769] = ifd
fp.seek(self._data[0x8769])
except (KeyError, TypeError):
pass
else:
info = TiffImagePlugin.ImageFileDirectory_v1(head)
info.load(fp)
self._data.update(self._fixup_dict(info))
# get gpsinfo extension # get gpsinfo extension
try: ifd = self._get_ifd_dict(fp, head, 0x8825)
# exif field 0x8825 is an offset pointer to the location if ifd:
# of the nested embedded gps exif ifd. self._data[0x8825] = ifd
# It should be a long, but may be corrupted. self._ifds[0x8825] = ifd
fp.seek(self._data[0x8825])
except (KeyError, TypeError): for tag in [
pass 0xa005, # interop
else: ]:
info = TiffImagePlugin.ImageFileDirectory_v1(head) ifd = self._get_ifd_dict(fp, head, tag)
info.load(fp) if ifd:
self._data[0x8825] = self._fixup_dict(info) self._ifds[tag] = ifd
def toBytes(self, offset=0): def toBytes(self, offset=0):
from . import TiffImagePlugin from . import TiffImagePlugin
@ -756,6 +765,9 @@ class Exif(MutableMapping):
ifd[tag] = value ifd[tag] = value
return b"Exif\x00\x00"+head+ifd.toBytes(offset) return b"Exif\x00\x00"+head+ifd.toBytes(offset)
def get_ifd(self, tag):
return self._ifds.get(tag, {})
def __str__(self): def __str__(self):
return str(self._data) return str(self._data)