Skip to content

Commit ce42cf7

Browse files
committed
correct EXIF handling + tests #92
Signed-off-by: bigcat88 <[email protected]>
1 parent 51df01d commit ce42cf7

File tree

3 files changed

+36
-7
lines changed

3 files changed

+36
-7
lines changed

pillow_heif/misc.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ def set_orientation(info: dict) -> Optional[int]:
6363
if info.get("exif", None):
6464
try:
6565
tif_tag = info["exif"]
66+
skipped_exif00 = False
6667
if tif_tag.startswith(b"Exif\x00\x00"):
68+
skipped_exif00 = True
6769
tif_tag = tif_tag[6:]
6870
endian_mark = "<" if tif_tag[0:2] == b"\x49\x49" else ">"
6971
pointer = unpack(endian_mark + "L", tif_tag[4:8])[0]
@@ -77,7 +79,9 @@ def set_orientation(info: dict) -> Optional[int]:
7779
_original_orientation = unpack(endian_mark + "H", value[0:2])[0]
7880
if _original_orientation != 1:
7981
original_orientation = _original_orientation
80-
p_value = 6 + pointer + 8
82+
p_value = pointer + 8
83+
if skipped_exif00:
84+
p_value += 6
8185
new_orientation = pack(endian_mark + "H", 1)
8286
info["exif"] = info["exif"][:p_value] + new_orientation + info["exif"][p_value + 2 :]
8387
break
@@ -154,10 +158,11 @@ def _retrieve_exif(metadata: List[dict]) -> Optional[bytes]:
154158
for i, md_block in enumerate(metadata):
155159
if md_block["type"] == "Exif":
156160
_purge.append(i)
157-
if md_block["data"][:4] == b"\x00\x00\x00\n": # Xiaomi EXIF start
158-
_data = md_block["data"][8:] # skip `\0\0\0\n` + TIFF header -> total 8 bytes
159-
else:
160-
_data = md_block["data"][4:] # skip TIFF header, first 4 bytes
161+
skip_size = int.from_bytes(md_block["data"][:4], byteorder="big", signed=False)
162+
skip_size += 4 # size of skip offset itself
163+
if len(md_block["data"]) - skip_size <= 4: # bad EXIF data, skip first 4 bytes
164+
skip_size = 4
165+
_data = md_block["data"][skip_size:]
161166
if not _result and _data:
162167
_result = _data
163168
for i in reversed(_purge):

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ profile = "black"
3030
master.py-version = "3.7"
3131
master.extension-pkg-allow-list = ["_pillow_heif"]
3232
design.max-attributes = 8
33-
design.max-branches = 14
34-
design.max-locals = 16
33+
design.max-branches = 16
34+
design.max-locals = 18
3535
design.max-returns = 8
3636
basic.good-names = [
3737
"a", "b", "c", "d", "e", "f", "i", "j", "k", "v",

tests/metadata_exif_test.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,3 +196,27 @@ def test_xiaomi_exif():
196196
im = Image.open("images/heif_special/xiaomi.heic")
197197
im.getexif()
198198
im.load()
199+
200+
201+
@pytest.mark.skipif(not helpers.hevc_enc(), reason="Requires HEVC encoder.")
202+
def test_data_before_exif():
203+
exif = Image.Exif()
204+
exif_bytes = exif.tobytes()
205+
exif_full_data = b"hidden data " + exif_bytes
206+
out_im = BytesIO()
207+
helpers.gradient_rgb().save(out_im, format="HEIF", exif=exif_full_data)
208+
im = Image.open(out_im)
209+
out_im.seek(0)
210+
assert out_im.read().find(b"hidden data ") != -1 # checking that this was saved
211+
assert im.getexif() == exif
212+
assert im.info["exif"] == exif_bytes[6:] # skipping b`Exif\x00\x00` that `exif.tobytes()` returns.
213+
214+
215+
@pytest.mark.skipif(not helpers.hevc_enc(), reason="Requires HEVC encoder.")
216+
def test_empty_exif():
217+
exif = Image.Exif()
218+
out_im = BytesIO()
219+
helpers.gradient_rgb().save(out_im, format="HEIF", exif=exif.tobytes())
220+
im = Image.open(out_im)
221+
assert im.getexif() == exif
222+
assert im.info["exif"] == exif.tobytes()[6:]

0 commit comments

Comments
 (0)