Skip to content

Commit

Permalink
Wait until all markers are read to process EXIF
Browse files Browse the repository at this point in the history
  • Loading branch information
radarhere committed Jun 10, 2024
1 parent 53e82e4 commit b84c970
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 33 deletions.
Binary file modified Tests/images/multiple_exif.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion Tests/test_file_jpeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -870,7 +870,7 @@ def test_ifd_offset_exif(self) -> None:

def test_multiple_exif(self) -> None:
with Image.open("Tests/images/multiple_exif.jpg") as im:
assert im.info["exif"] == b"Exif\x00\x00firstsecond"
assert im.getexif()[270] == "firstsecond"

@mark_if_feature_version(
pytest.mark.valgrind_known_error, "libjpeg_turbo", "2.0", reason="Known Failing"
Expand Down
68 changes: 36 additions & 32 deletions src/PIL/JpegImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,38 +159,6 @@ def APP(self, marker):
# plus constant header size
self.info["mpoffset"] = self.fp.tell() - n + 4

# If DPI isn't in JPEG header, fetch from EXIF
if "dpi" not in self.info and "exif" in self.info:
try:
exif = self.getexif()
resolution_unit = exif[0x0128]
x_resolution = exif[0x011A]
try:
dpi = float(x_resolution[0]) / x_resolution[1]
except TypeError:
dpi = x_resolution
if math.isnan(dpi):
msg = "DPI is not a number"
raise ValueError(msg)
if resolution_unit == 3: # cm
# 1 dpcm = 2.54 dpi
dpi *= 2.54
self.info["dpi"] = dpi, dpi
except (
struct.error,
KeyError,
SyntaxError,
TypeError,
ValueError,
ZeroDivisionError,
):
# struct.error for truncated EXIF
# KeyError for dpi not included
# SyntaxError for invalid/unreadable EXIF
# ValueError or TypeError for dpi being an invalid float
# ZeroDivisionError for invalid dpi rational value
self.info["dpi"] = 72, 72


def COM(self: JpegImageFile, marker: int) -> None:
#
Expand Down Expand Up @@ -409,6 +377,8 @@ def _open(self):
msg = "no marker found"
raise SyntaxError(msg)

self._read_dpi_from_exif()

def load_read(self, read_bytes: int) -> bytes:
"""
internal: read more image data
Expand Down Expand Up @@ -497,6 +467,40 @@ def load_djpeg(self) -> None:
def _getexif(self) -> dict[str, Any] | None:
return _getexif(self)

def _read_dpi_from_exif(self) -> None:
# If DPI isn't in JPEG header, fetch from EXIF
if "dpi" in self.info or "exif" not in self.info:
return
try:
exif = self.getexif()
resolution_unit = exif[0x0128]
x_resolution = exif[0x011A]
try:
dpi = float(x_resolution[0]) / x_resolution[1]
except TypeError:
dpi = x_resolution
if math.isnan(dpi):
msg = "DPI is not a number"
raise ValueError(msg)

Check warning on line 484 in src/PIL/JpegImagePlugin.py

View check run for this annotation

Codecov / codecov/patch

src/PIL/JpegImagePlugin.py#L483-L484

Added lines #L483 - L484 were not covered by tests
if resolution_unit == 3: # cm
# 1 dpcm = 2.54 dpi
dpi *= 2.54
self.info["dpi"] = dpi, dpi
except (
struct.error,
KeyError,
SyntaxError,
TypeError,
ValueError,
ZeroDivisionError,
):
# struct.error for truncated EXIF
# KeyError for dpi not included
# SyntaxError for invalid/unreadable EXIF
# ValueError or TypeError for dpi being an invalid float
# ZeroDivisionError for invalid dpi rational value
self.info["dpi"] = 72, 72

def _getmp(self):
return _getmp(self)

Expand Down

0 comments on commit b84c970

Please sign in to comment.