Skip to content

Commit

Permalink
Added Exif load_from_fp method to get TIFF tag_v2 data
Browse files Browse the repository at this point in the history
  • Loading branch information
radarhere committed Apr 19, 2021
1 parent fc08a72 commit 293a394
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 11 deletions.
44 changes: 44 additions & 0 deletions Tests/test_file_tiff.py
Expand Up @@ -389,6 +389,50 @@ def test_ifd_tag_type(self):
with Image.open("Tests/images/ifd_tag_type.tiff") as im:
assert 0x8825 in im.tag_v2

def test_exif(self):
with Image.open("Tests/images/ifd_tag_type.tiff") as im:
exif = im.getexif()

assert list(exif.keys()) == [
256,
257,
258,
259,
262,
271,
272,
273,
277,
278,
279,
282,
283,
284,
34853,
296,
297,
50735,
305,
700,
339,
34665,
]
assert exif[256] == 640
assert exif[271] == "FLIR"

gps = exif.get_ifd(0x8825)
assert list(gps.keys()) == [0, 1, 2, 3, 4, 5, 6, 18]
assert gps[0] == b"\x03\x02\x00\x00"
assert gps[18] == "WGS-84"

def test_exif_frames(self):
# Test that EXIF data can change across frames
with Image.open("Tests/images/g4-multi.tiff") as im:
assert im.getexif()[273] == (328, 815)

im.seek(1)
assert im.getexif()[273] == (1408, 1907)

def test_seek(self):
filename = "Tests/images/pil136.tiff"
with Image.open(filename) as im:
Expand Down
21 changes: 21 additions & 0 deletions Tests/test_image.py
Expand Up @@ -773,6 +773,27 @@ def test_exif_ifd(self):
reloaded_exif.load(exif.tobytes())
assert reloaded_exif.get_ifd(0x8769) == exif.get_ifd(0x8769)

def test_exif_load_from_fp(self):
with Image.open("Tests/images/flower.jpg") as im:
data = im.info["exif"]
if data.startswith(b"Exif\x00\x00"):
data = data[6:]
fp = io.BytesIO(data)

exif = Image.Exif()
exif.load_from_fp(fp)
assert exif == {
271: "Canon",
272: "Canon PowerShot S40",
274: 1,
282: 180.0,
283: 180.0,
296: 2,
306: "2003:12:14 12:01:44",
531: 1,
34665: 196,
}

@pytest.mark.skipif(
sys.version_info < (3, 7), reason="Python 3.7 or greater required"
)
Expand Down
51 changes: 40 additions & 11 deletions src/PIL/Image.py
Expand Up @@ -1317,11 +1317,16 @@ def getexif(self):
self._exif = Exif()

exif_info = self.info.get("exif")
if exif_info is None and "Raw profile type exif" in self.info:
exif_info = bytes.fromhex(
"".join(self.info["Raw profile type exif"].split("\n")[3:])
)
self._exif.load(exif_info)
if exif_info is None:
if "Raw profile type exif" in self.info:
exif_info = bytes.fromhex(
"".join(self.info["Raw profile type exif"].split("\n")[3:])
)
elif hasattr(self, "tag_v2"):
self._exif.endian = self.tag_v2._endian
self._exif.load_from_fp(self.fp, self.tag_v2._offset)
if exif_info is not None:
self._exif.load(exif_info)

# XMP tags
if 0x0112 not in self._exif:
Expand Down Expand Up @@ -3292,7 +3297,7 @@ def _apply_env_variables(env=None):


class Exif(MutableMapping):
endian = "<"
endian = None

def __init__(self):
self._data = {}
Expand Down Expand Up @@ -3327,6 +3332,12 @@ def _get_ifd_dict(self, offset):
info.load(self.fp)
return self._fixup_dict(info)

def _get_head(self):
if self.endian == "<":
return b"II\x2A\x00\x08\x00\x00\x00"
else:
return b"MM\x00\x2A\x00\x00\x00\x08"

def load(self, data):
# Extract EXIF information. This is highly experimental,
# and is likely to be replaced with something better in a future
Expand All @@ -3339,8 +3350,8 @@ def load(self, data):
self._loaded_exif = data
self._data.clear()
self._ifds.clear()
self._info = None
if not data:
self._info = None
return

if data.startswith(b"Exif\x00\x00"):
Expand All @@ -3355,6 +3366,27 @@ def load(self, data):
self.fp.seek(self._info.next)
self._info.load(self.fp)

def load_from_fp(self, fp, offset=None):
self._loaded_exif = None
self._data.clear()
self._ifds.clear()

# process dictionary
from . import TiffImagePlugin

self.fp = fp
if offset is not None:
self.head = self._get_head()
else:
self.head = self.fp.read(8)
self._info = TiffImagePlugin.ImageFileDirectory_v2(self.head)
if self.endian is None:
self.endian = self._info._endian
if offset is None:
offset = self._info.next
self.fp.seek(offset)
self._info.load(self.fp)

def _get_merged_dict(self):
merged_dict = dict(self)

Expand All @@ -3373,10 +3405,7 @@ def _get_merged_dict(self):
def tobytes(self, offset=8):
from . import TiffImagePlugin

if self.endian == "<":
head = b"II\x2A\x00\x08\x00\x00\x00"
else:
head = b"MM\x00\x2A\x00\x00\x00\x08"
head = self._get_head()
ifd = TiffImagePlugin.ImageFileDirectory_v2(ifh=head)
for tag, value in self.items():
if tag in [0x8769, 0x8225, 0x8825] and not isinstance(value, dict):
Expand Down

0 comments on commit 293a394

Please sign in to comment.