Repository: python-pillow/Pillow

Issue:
MPO format loading error when EXIF data is present
Someone sent me a MPO file that I was not able to open with Pillow
It looks like the second image of the file has EXIF data that might conflict with the loading process.
In particular, the reported size does not correspond to the image, but I was not able to figure out if it is the root cause of the bug.

I created a similar image that you can use for your tests and publish if you need. I copied the EXIF of the original file with some subtractions such as the embedded thumbnail. It exhibits the same behavior and is correctly loaded by some other players, such as geeqie or sView.

In this ZIP ([pillow_mpo_exif_error.zip](https://github.com/python-pillow/Pillow/files/5757456/pillow_mpo_exif_error.zip)), you will find the sample image and a script that show the error with this output: [visible with 8.0.1]

> Image 0 size: (64, 64)
> Image 0 loaded
> Image 1 size: (5472, 3648)
> Image 1 error: image file is truncated (0 bytes not processed)

As you can see, the second image is reported to be 5472×3648 but is actually 64×64 like the first one.



Discussion:

Resolving patch:
diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py
--- a/src/PIL/MpoImagePlugin.py
+++ b/src/PIL/MpoImagePlugin.py
@@ -82,9 +82,11 @@ def seek(self, frame):
             n = i16(self.fp.read(2)) - 2
             self.info["exif"] = ImageFile._safe_read(self.fp, n)
 
-            exif = self.getexif()
-            if 40962 in exif and 40963 in exif:
-                self._size = (exif[40962], exif[40963])
+            mptype = self.mpinfo[0xB002][frame]["Attribute"]["MPType"]
+            if mptype.startswith("Large Thumbnail"):
+                exif = self.getexif()
+                if 40962 in exif and 40963 in exif:
+                    self._size = (exif[40962], exif[40963])
         elif "exif" in self.info:
             del self.info["exif"]
 


Test patch:
diff --git a/Tests/images/ignore_frame_size.mpo b/Tests/images/ignore_frame_size.mpo
new file mode 100644
Binary files /dev/null and b/Tests/images/ignore_frame_size.mpo differ
diff --git a/Tests/images/sugarshack_frame_size.mpo b/Tests/images/sugarshack_frame_size.mpo
Binary files a/Tests/images/sugarshack_frame_size.mpo and b/Tests/images/sugarshack_frame_size.mpo differ
diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py
--- a/Tests/test_file_mpo.py
+++ b/Tests/test_file_mpo.py
@@ -89,6 +89,20 @@ def test_frame_size():
         assert im.size == (680, 480)
 
 
+def test_ignore_frame_size():
+    # Ignore the different size of the second frame
+    # since this is not a "Large Thumbnail" image
+    with Image.open("Tests/images/ignore_frame_size.mpo") as im:
+        assert im.size == (64, 64)
+
+        im.seek(1)
+        assert (
+            im.mpinfo[0xB002][1]["Attribute"]["MPType"]
+            == "Multi-Frame Image: (Disparity)"
+        )
+        assert im.size == (64, 64)
+
+
 def test_parallax():
     # Nintendo
     with Image.open("Tests/images/sugarshack.mpo") as im:
@@ -132,7 +146,7 @@ def test_mp_attribute():
         with Image.open(test_file) as im:
             mpinfo = im._getmp()
         frameNumber = 0
-        for mpentry in mpinfo[45058]:
+        for mpentry in mpinfo[0xB002]:
             mpattr = mpentry["Attribute"]
             if frameNumber:
                 assert not mpattr["RepresentativeImageFlag"]