From b84c970fbf3f08825cabf9c5a132e20f868907f1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 10 Jun 2024 19:19:06 +1000 Subject: [PATCH 1/2] Wait until all markers are read to process EXIF --- Tests/images/multiple_exif.jpg | Bin 364 -> 391 bytes Tests/test_file_jpeg.py | 2 +- src/PIL/JpegImagePlugin.py | 68 +++++++++++++++++---------------- 3 files changed, 37 insertions(+), 33 deletions(-) diff --git a/Tests/images/multiple_exif.jpg b/Tests/images/multiple_exif.jpg index 32e0aa301a94dfa13dc47faeb3071c25a5d8b573..f9f343507e73c5fe2df844deaa5d025f5cd013ec 100644 GIT binary patch delta 66 zcmaFE)XqFX++5wYA~TJF!Pl2Ti-CcGgMpEekAVru;sIhQAZAF*EGjPf|B!(nqOdqM NIX^FjVWY7nBLK0l4od(4 delta 39 qcmZo?e#10DT!z=RA~TJFAuY40xa9vs20jS4I5jyxFJ+^jB_jX@Ck-qB diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 18dc752d8bd..342c9e47bd4 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -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" diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 4d0b75e77ed..a9a89e58bd7 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -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: # @@ -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 @@ -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) + 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) From 88cd6d41eff702ac83300807b097f6044b665025 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Fri, 28 Jun 2024 17:28:42 +1000 Subject: [PATCH 2/2] Rearranged comments Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- src/PIL/JpegImagePlugin.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index a9a89e58bd7..59259b7194c 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -487,18 +487,13 @@ def _read_dpi_from_exif(self) -> None: dpi *= 2.54 self.info["dpi"] = dpi, dpi except ( - struct.error, - KeyError, - SyntaxError, - TypeError, - ValueError, - ZeroDivisionError, + 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 ): - # 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):