diff --git a/Tests/images/multiple_exif.jpg b/Tests/images/multiple_exif.jpg index 32e0aa301..f9f343507 100644 Binary files a/Tests/images/multiple_exif.jpg and b/Tests/images/multiple_exif.jpg differ diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index dbd917517..68705094b 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -872,7 +872,7 @@ class TestFileJpeg: 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" diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index ada529153..b15bf06d2 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -161,38 +161,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: # @@ -411,6 +379,8 @@ class JpegImageFile(ImageFile.ImageFile): 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 @@ -499,6 +469,35 @@ class JpegImageFile(ImageFile.ImageFile): 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) + if resolution_unit == 3: # cm + # 1 dpcm = 2.54 dpi + dpi *= 2.54 + self.info["dpi"] = dpi, dpi + except ( + struct.error, # truncated EXIF + KeyError, # dpi not included + SyntaxError, # invalid/unreadable EXIF + TypeError, # dpi is an invalid float + ValueError, # dpi is an invalid float + ZeroDivisionError, # invalid dpi rational value + ): + self.info["dpi"] = 72, 72 + def _getmp(self): return _getmp(self)