diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index ca7d3c039..1a2602957 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -557,6 +557,28 @@ class TestFilePng(PillowTestCase): chunks = PngImagePlugin.getchunks(im) self.assertEqual(len(chunks), 3) + def test_textual_chunks_after_idat(self): + im = Image.open("Tests/images/hopper.png") + self.assertIn('comment', im.text.keys()) + for k, v in { + 'date:create': '2014-09-04T09:37:08+03:00', + 'date:modify': '2014-09-04T09:37:08+03:00', + }.items(): + self.assertEqual(im.text[k], v) + + # Raises a SyntaxError in load_end + im = Image.open("Tests/images/broken_data_stream.png") + with self.assertRaises(IOError): + self.assertIsInstance(im.text, dict) + + # Raises a UnicodeDecodeError in load_end + im = Image.open("Tests/images/truncated_image.png") + # The file is truncated + self.assertRaises(IOError, lambda: im.text) + ImageFile.LOAD_TRUNCATED_IMAGES = True + self.assertIsInstance(im.text, dict) + ImageFile.LOAD_TRUNCATED_IMAGES = False + @unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or macOS") class TestTruncatedPngPLeaks(PillowLeakTestCase): diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index f780f811a..dcd4b437f 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -579,7 +579,7 @@ class PngImageFile(ImageFile.ImageFile): self.mode = self.png.im_mode self._size = self.png.im_size self.info = self.png.im_info - self.text = self.png.im_text # experimental + self._text = None self.tile = self.png.im_tile if self.png.im_palette: @@ -588,6 +588,15 @@ class PngImageFile(ImageFile.ImageFile): self.__idat = length # used by load_read() + @property + def text(self): + # experimental + if self._text is None: + # iTxt, tEXt and zTXt chunks may appear at the end of the file + # So load the file to ensure that they are read + self.load() + return self._text + def verify(self): "Verify PNG file" @@ -640,7 +649,22 @@ class PngImageFile(ImageFile.ImageFile): def load_end(self): "internal: finished reading image data" + while True: + self.fp.read(4) # CRC + try: + cid, pos, length = self.png.read() + except (struct.error, SyntaxError): + break + + if cid == b"IEND": + break + + try: + self.png.call(cid, pos, length) + except UnicodeDecodeError: + break + self._text = self.png.im_text self.png.close() self.png = None