Repository: python-pillow/Pillow

Issue:
Error when loading some PNG images
After update PIL to 5.4.0 I cannot load an image
![mastercard](https://user-images.githubusercontent.com/9046065/50598772-71f08100-0eb4-11e9-99cd-126e65673626.png)
[_image duplicate on Google drive_](https://drive.google.com/open?id=19pwEEGBh7h1iYRn3uSC19z8tkiHokPJQ)

The example code shown bellow raises `EOFError`. But it worked correctly in previous versions.

The error was reproduced in Ubuntu 14.04 + Python 3.6.7 and macOS Sierra 10.12.6 + Python 3.6.3. In both cases Pillow 5.4.0 was uses. it seems the error was caused by the commit [22837c37e279a5fb3fb7b482d81e2c4d44c8cdcc](https://github.com/python-pillow/Pillow/commit/22837c37e279a5fb3fb7b482d81e2c4d44c8cdcc).

```python
from io import BytesIO

from PIL import Image

with open('mastercard.png', mode='rb') as fp:
    im_bytes = fp.read()

image = Image.open(BytesIO(im_bytes))
image.show()
```

Error log:
```
EOFError                                  Traceback (most recent call last)
<ipython-input-11-d946298b95dd> in <module>
----> 1 image.show()

~/py/ocean/ocean_ai/env/lib/python3.6/site-packages/PIL/Image.py in show(self, title, command)
   2039         """
   2040
-> 2041         _show(self, title=title, command=command)
   2042
   2043     def split(self):

~/py/ocean/ocean_ai/env/lib/python3.6/site-packages/PIL/Image.py in _show(image, **options)
   2904 def _show(image, **options):
   2905     # override me, as necessary
-> 2906     _showxv(image, **options)
   2907
   2908

~/py/ocean/ocean_ai/env/lib/python3.6/site-packages/PIL/Image.py in _showxv(image, title, **options)
   2909 def _showxv(image, title=None, **options):
   2910     from . import ImageShow
-> 2911     ImageShow.show(image, title, **options)
   2912
   2913

~/py/ocean/ocean_ai/env/lib/python3.6/site-packages/PIL/ImageShow.py in show(image, title, **options)
     51     """
     52     for viewer in _viewers:
---> 53         if viewer.show(image, title=title, **options):
     54             return 1
     55     return 0

~/py/ocean/ocean_ai/env/lib/python3.6/site-packages/PIL/ImageShow.py in show(self, image, **options)
     75             image = image.convert(base)
     76
---> 77         return self.show_image(image, **options)
     78
     79     # hook methods

~/py/ocean/ocean_ai/env/lib/python3.6/site-packages/PIL/ImageShow.py in show_image(self, image, **options)
     95     def show_image(self, image, **options):
     96         """Display given image"""
---> 97         return self.show_file(self.save_image(image), **options)
     98
     99     def show_file(self, file, **options):

~/py/ocean/ocean_ai/env/lib/python3.6/site-packages/PIL/ImageShow.py in save_image(self, image)
     91     def save_image(self, image):
     92         """Save to temporary file, and return filename"""
---> 93         return image._dump(format=self.get_format(image), **self.options)
     94
     95     def show_image(self, image, **options):

~/py/ocean/ocean_ai/env/lib/python3.6/site-packages/PIL/Image.py in _dump(self, file, format, **options)
    646                 filename = filename + suffix
    647
--> 648         self.load()
    649
    650         if not format or format == "PPM":

~/py/ocean/ocean_ai/env/lib/python3.6/site-packages/PIL/ImageFile.py in load(self)
    248         self.readonly = readonly
    249
--> 250         self.load_end()
    251
    252         if self._exclusive_fp and self._close_exclusive_fp_after_loading:

~/py/ocean/ocean_ai/env/lib/python3.6/site-packages/PIL/PngImagePlugin.py in load_end(self)
    675
    676             try:
--> 677                 self.png.call(cid, pos, length)
    678             except UnicodeDecodeError:
    679                 break

~/py/ocean/ocean_ai/env/lib/python3.6/site-packages/PIL/PngImagePlugin.py in call(self, cid, pos, length)
    138
    139         logger.debug("STREAM %r %s %s", cid, pos, length)
--> 140         return getattr(self, "chunk_" + cid.decode('ascii'))(pos, length)
    141
    142     def crc(self, cid, data):

~/py/ocean/ocean_ai/env/lib/python3.6/site-packages/PIL/PngImagePlugin.py in chunk_IDAT(self, pos, length)
    354         self.im_tile = [("zip", (0, 0)+self.im_size, pos, self.im_rawmode)]
    355         self.im_idat = length
--> 356         raise EOFError
    357
    358     def chunk_IEND(self, pos, length):

EOFError:
```



Discussion:
Reproducible with 5.4.0 with:
```python
from PIL import Image
im = Image.open("mastercard.png")
```
Not reproducible with 5.3.0.


@hugovk I find your comment surprising, given that the traceback involves `load()`. Would you be able to post your traceback?
Sorry, missed the last line: 
```python
from PIL import Image
im = Image.open("mastercard.png")
im.show()
```
```python
Python 3.7.1 (default, Nov  6 2018, 18:45:35)
[Clang 10.0.0 (clang-1000.11.45.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from PIL import Image
>>> im = Image.open("mastercard.png")
>>> im.show()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/hugo/github/Pillow/src/PIL/Image.py", line 2041, in show
    _show(self, title=title, command=command)
  File "/Users/hugo/github/Pillow/src/PIL/Image.py", line 2906, in _show
    _showxv(image, **options)
  File "/Users/hugo/github/Pillow/src/PIL/Image.py", line 2911, in _showxv
    ImageShow.show(image, title, **options)
  File "/Users/hugo/github/Pillow/src/PIL/ImageShow.py", line 53, in show
    if viewer.show(image, title=title, **options):
  File "/Users/hugo/github/Pillow/src/PIL/ImageShow.py", line 77, in show
    return self.show_image(image, **options)
  File "/Users/hugo/github/Pillow/src/PIL/ImageShow.py", line 97, in show_image
    return self.show_file(self.save_image(image), **options)
  File "/Users/hugo/github/Pillow/src/PIL/ImageShow.py", line 93, in save_image
    return image._dump(format=self.get_format(image), **self.options)
  File "/Users/hugo/github/Pillow/src/PIL/Image.py", line 648, in _dump
    self.load()
  File "/Users/hugo/github/Pillow/src/PIL/ImageFile.py", line 250, in load
    self.load_end()
  File "/Users/hugo/github/Pillow/src/PIL/PngImagePlugin.py", line 677, in load_end
    self.png.call(cid, pos, length)
  File "/Users/hugo/github/Pillow/src/PIL/PngImagePlugin.py", line 140, in call
    return getattr(self, "chunk_" + cid.decode('ascii'))(pos, length)
  File "/Users/hugo/github/Pillow/src/PIL/PngImagePlugin.py", line 356, in chunk_IDAT
    raise EOFError
EOFError
>>>
```
It looks like this only happen if the chunk types 'IDAT' and 'IEND' are in the file.

Resolving patch:
diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py
--- a/src/PIL/PngImagePlugin.py
+++ b/src/PIL/PngImagePlugin.py
@@ -677,6 +677,8 @@ def load_end(self):
                 self.png.call(cid, pos, length)
             except UnicodeDecodeError:
                 break
+            except EOFError:
+                ImageFile._safe_read(self.fp, length)
         self._text = self.png.im_text
         self.png.close()
         self.png = None


Test patch:
diff --git a/Tests/images/hopper_idat_after_image_end.png b/Tests/images/hopper_idat_after_image_end.png
new file mode 100644
Binary files /dev/null and b/Tests/images/hopper_idat_after_image_end.png differ
diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py
--- a/Tests/test_file_png.py
+++ b/Tests/test_file_png.py
@@ -585,6 +585,10 @@ def test_textual_chunks_after_idat(self):
         self.assertIsInstance(im.text, dict)
         ImageFile.LOAD_TRUNCATED_IMAGES = False
 
+        # Raises an EOFError in load_end
+        im = Image.open("Tests/images/hopper_idat_after_image_end.png")
+        self.assertEqual(im.text, {'TXT': 'VALUE', 'ZIP': 'VALUE'})
+
     @unittest.skipUnless(HAVE_WEBP and _webp.HAVE_WEBPANIM,
                          "WebP support not installed with animation")
     def test_apng(self):