diff --git a/Tests/test_image.py b/Tests/test_image.py index 69a66b85a71..a37c90296eb 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -851,6 +851,31 @@ def test_exif_load_from_fp(self): 34665: 196, } + def test_exif_hide_offsets(self): + with Image.open("Tests/images/flower.jpg") as im: + exif = im.getexif() + + # Check offsets are present initially + assert 0x8769 in exif + for tag in (0xA005, 0x927C): + assert tag in exif.get_ifd(0x8769) + assert exif.get_ifd(0xA005) + loaded_exif = exif + + with Image.open("Tests/images/flower.jpg") as im: + new_exif = im.getexif() + + for exif in (loaded_exif, new_exif): + exif.hide_offsets() + + # Assert they are hidden afterwards, + # but that the IFDs are still available + assert 0x8769 not in exif + assert exif.get_ifd(0x8769) + for tag in (0xA005, 0x927C): + assert tag not in exif.get_ifd(0x8769) + assert exif.get_ifd(0xA005) + @pytest.mark.parametrize("size", ((1, 0), (0, 1), (0, 0))) def test_zero_tobytes(self, size): im = Image.new("RGB", size) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 18a83550123..a760de575a6 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -3551,6 +3551,7 @@ class Exif(MutableMapping): def __init__(self): self._data = {} + self._hidden_data = {} self._ifds = {} self._info = None self._loaded_exif = None @@ -3604,6 +3605,7 @@ def load(self, data): return self._loaded_exif = data self._data.clear() + self._hidden_data.clear() self._ifds.clear() if data and data.startswith(b"Exif\x00\x00"): data = data[6:] @@ -3624,6 +3626,7 @@ def load(self, data): def load_from_fp(self, fp, offset=None): self._loaded_exif = None self._data.clear() + self._hidden_data.clear() self._ifds.clear() # process dictionary @@ -3686,8 +3689,9 @@ def get_ifd(self, tag): if self._info is not None: self._ifds[tag] = self._get_ifd_dict(self._info.next) elif tag in [ExifTags.IFD.Exif, ExifTags.IFD.GPSInfo]: - if tag in self: - self._ifds[tag] = self._get_ifd_dict(self[tag]) + offset = self._hidden_data.get(tag, self.get(tag)) + if offset is not None: + self._ifds[tag] = self._get_ifd_dict(offset) elif tag in [ExifTags.IFD.Interop, ExifTags.IFD.Makernote]: if ExifTags.IFD.Exif not in self._ifds: self.get_ifd(ExifTags.IFD.Exif) @@ -3770,7 +3774,20 @@ def get_ifd(self, tag): else: # Interop self._ifds[tag] = self._get_ifd_dict(tag_data) - return self._ifds.get(tag, {}) + ifd = self._ifds.get(tag, {}) + if tag == ExifTags.IFD.Exif and self._hidden_data: + ifd = { + k: v + for (k, v) in ifd.items() + if k not in (ExifTags.IFD.Interop, ExifTags.IFD.Makernote) + } + return ifd + + def hide_offsets(self): + for tag in (ExifTags.IFD.Exif, ExifTags.IFD.GPSInfo): + if tag in self: + self._hidden_data[tag] = self[tag] + del self[tag] def __str__(self): if self._info is not None: