Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f38f01bbe3 | ||
|
|
8189bb24fe | ||
|
|
876e7b36a1 | ||
|
|
35c7a554ce | ||
|
|
1ce66a22b8 | ||
|
|
bafb7e8587 | ||
|
|
eabc600690 | ||
|
|
24636d8a36 | ||
|
|
194f599f27 | ||
|
|
f073be52dc | ||
|
|
3fdd7002a9 | ||
|
|
2ad96b038c |
15
CHANGES.rst
15
CHANGES.rst
@ -2,6 +2,21 @@
|
||||
Changelog (Pillow)
|
||||
==================
|
||||
|
||||
5.4.1 (2019-01-06)
|
||||
------------------
|
||||
|
||||
- File closing: Only close __fp if not fp #3540
|
||||
[radarhere]
|
||||
|
||||
- Fix build for Termux #3529
|
||||
[pslacerda]
|
||||
|
||||
- PNG: Detect MIME types #3525
|
||||
[radarhere]
|
||||
|
||||
- PNG: Handle IDAT chunks after image end #3532
|
||||
[radarhere]
|
||||
|
||||
5.4.0 (2019-01-01)
|
||||
------------------
|
||||
|
||||
|
||||
BIN
Tests/images/hopper_idat_after_image_end.png
Normal file
BIN
Tests/images/hopper_idat_after_image_end.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
BIN
Tests/images/hopper_unknown_pixel_mode.tif
Normal file
BIN
Tests/images/hopper_unknown_pixel_mode.tif
Normal file
Binary file not shown.
@ -86,6 +86,7 @@ class TestFilePng(PillowTestCase):
|
||||
self.assertEqual(im.mode, "RGB")
|
||||
self.assertEqual(im.size, (128, 128))
|
||||
self.assertEqual(im.format, "PNG")
|
||||
self.assertEqual(im.get_format_mimetype(), 'image/png')
|
||||
|
||||
hopper("1").save(test_file)
|
||||
Image.open(test_file)
|
||||
@ -585,10 +586,16 @@ class TestFilePng(PillowTestCase):
|
||||
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):
|
||||
im = Image.open("Tests/images/iss634.apng")
|
||||
self.assertEqual(im.get_format_mimetype(), 'image/apng')
|
||||
|
||||
expected = Image.open("Tests/images/iss634.webp")
|
||||
self.assert_image_similar(im, expected, 0.23)
|
||||
|
||||
|
||||
@ -220,6 +220,10 @@ class TestFileTiff(PillowTestCase):
|
||||
self.assertEqual(
|
||||
im.getextrema(), (-3.140936851501465, 3.140684127807617))
|
||||
|
||||
def test_unknown_pixel_mode(self):
|
||||
self.assertRaises(
|
||||
IOError, Image.open, 'Tests/images/hopper_unknown_pixel_mode.tif')
|
||||
|
||||
def test_n_frames(self):
|
||||
for path, n_frames in [
|
||||
['Tests/images/multipage-lastframe.tif', 1],
|
||||
|
||||
36
docs/releasenotes/5.4.1.rst
Normal file
36
docs/releasenotes/5.4.1.rst
Normal file
@ -0,0 +1,36 @@
|
||||
5.4.1
|
||||
-----
|
||||
|
||||
This release fixes regressions in 5.4.0.
|
||||
|
||||
Installation on Termux
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
A change to the way Pillow detects libraries during installed prevented
|
||||
installation on Termux, which does not have ``/sbin/ldconfig``. This is now
|
||||
fixed.
|
||||
|
||||
PNG: Handle IDAT chunks after image end
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Some PNG images have multiple IDAT chunks. In some cases, Pillow will stop
|
||||
reading image data before the IDAT chunks finish. A regression caused an
|
||||
``EOFError`` exception when previously there was none. This is now fixed, and
|
||||
file reading continues in case there are subsequent text chunks.
|
||||
|
||||
PNG: MIME type
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
The addition of limited APNG support to the PNG plugin also overwrote the MIME
|
||||
type for PNG files, causing "image/apng" to be returned as the MIME type of
|
||||
both APNG and PNG files. This has been fixed so the MIME type of PNG files is
|
||||
"image/png".
|
||||
|
||||
File closing
|
||||
^^^^^^^^^^^^
|
||||
|
||||
A regression caused an unsupported image file to report a
|
||||
``ValueError: seek of closed file`` exception instead of an ``OSError``. This
|
||||
has been fixed by ensuring that image plugins only close their internal ``__fp``
|
||||
if they are not the same as ``ImageFile``'s ``fp``, allowing each to manage their own
|
||||
file pointers.
|
||||
@ -6,6 +6,7 @@ Release Notes
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
5.4.1
|
||||
5.4.0
|
||||
5.3.0
|
||||
5.2.0
|
||||
|
||||
2
setup.py
2
setup.py
@ -111,7 +111,7 @@ def _find_library_dirs_ldconfig():
|
||||
stdout=subprocess.PIPE,
|
||||
env=env)
|
||||
except OSError: # E.g. command not found
|
||||
return None
|
||||
return []
|
||||
[data, _] = p.communicate()
|
||||
if isinstance(data, bytes):
|
||||
data = data.decode()
|
||||
|
||||
@ -83,7 +83,8 @@ class DcxImageFile(PcxImageFile):
|
||||
|
||||
def _close__fp(self):
|
||||
try:
|
||||
self.__fp.close()
|
||||
if self.__fp != self.fp:
|
||||
self.__fp.close()
|
||||
except AttributeError:
|
||||
pass
|
||||
finally:
|
||||
|
||||
@ -159,7 +159,8 @@ class FliImageFile(ImageFile.ImageFile):
|
||||
|
||||
def _close__fp(self):
|
||||
try:
|
||||
self.__fp.close()
|
||||
if self.__fp != self.fp:
|
||||
self.__fp.close()
|
||||
except AttributeError:
|
||||
pass
|
||||
finally:
|
||||
|
||||
@ -304,7 +304,8 @@ class GifImageFile(ImageFile.ImageFile):
|
||||
|
||||
def _close__fp(self):
|
||||
try:
|
||||
self.__fp.close()
|
||||
if self.__fp != self.fp:
|
||||
self.__fp.close()
|
||||
except AttributeError:
|
||||
pass
|
||||
finally:
|
||||
|
||||
@ -292,7 +292,8 @@ class ImImageFile(ImageFile.ImageFile):
|
||||
|
||||
def _close__fp(self):
|
||||
try:
|
||||
self.__fp.close()
|
||||
if self.__fp != self.fp:
|
||||
self.__fp.close()
|
||||
except AttributeError:
|
||||
pass
|
||||
finally:
|
||||
|
||||
@ -594,10 +594,10 @@ class Image(object):
|
||||
:ref:`file-handling` for more information.
|
||||
"""
|
||||
try:
|
||||
self.fp.close()
|
||||
self.fp = None
|
||||
if hasattr(self, "_close__fp"):
|
||||
self._close__fp()
|
||||
self.fp.close()
|
||||
self.fp = None
|
||||
except Exception as msg:
|
||||
logger.debug("Error closing: %s", msg)
|
||||
|
||||
@ -611,12 +611,12 @@ class Image(object):
|
||||
|
||||
if sys.version_info.major >= 3:
|
||||
def __del__(self):
|
||||
if hasattr(self, "_close__fp"):
|
||||
self._close__fp()
|
||||
if (hasattr(self, 'fp') and hasattr(self, '_exclusive_fp')
|
||||
and self.fp and self._exclusive_fp):
|
||||
self.fp.close()
|
||||
self.fp = None
|
||||
if hasattr(self, "_close__fp"):
|
||||
self._close__fp()
|
||||
|
||||
def _copy(self):
|
||||
self.load()
|
||||
|
||||
@ -79,6 +79,8 @@ class ImageFile(Image.Image):
|
||||
|
||||
self._min_frame = 0
|
||||
|
||||
self.custom_mimetype = None
|
||||
|
||||
self.tile = None
|
||||
self.readonly = 1 # until we know better
|
||||
|
||||
@ -120,7 +122,7 @@ class ImageFile(Image.Image):
|
||||
def get_format_mimetype(self):
|
||||
if self.format is None:
|
||||
return
|
||||
return Image.MIME.get(self.format.upper())
|
||||
return self.custom_mimetype or Image.MIME.get(self.format.upper())
|
||||
|
||||
def verify(self):
|
||||
"Check file integrity"
|
||||
|
||||
@ -99,7 +99,8 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
|
||||
|
||||
def _close__fp(self):
|
||||
try:
|
||||
self.__fp.close()
|
||||
if self.__fp != self.fp:
|
||||
self.__fp.close()
|
||||
except AttributeError:
|
||||
pass
|
||||
finally:
|
||||
|
||||
@ -86,7 +86,8 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
|
||||
|
||||
def _close__fp(self):
|
||||
try:
|
||||
self.__fp.close()
|
||||
if self.__fp != self.fp:
|
||||
self.__fp.close()
|
||||
except AttributeError:
|
||||
pass
|
||||
finally:
|
||||
|
||||
@ -296,6 +296,7 @@ class PngStream(ChunkStream):
|
||||
self.im_mode = None
|
||||
self.im_tile = None
|
||||
self.im_palette = None
|
||||
self.im_custom_mimetype = None
|
||||
|
||||
self.text_memory = 0
|
||||
|
||||
@ -529,6 +530,7 @@ class PngStream(ChunkStream):
|
||||
# APNG chunks
|
||||
def chunk_acTL(self, pos, length):
|
||||
s = ImageFile._safe_read(self.fp, length)
|
||||
self.im_custom_mimetype = 'image/apng'
|
||||
return s
|
||||
|
||||
def chunk_fcTL(self, pos, length):
|
||||
@ -594,6 +596,7 @@ class PngImageFile(ImageFile.ImageFile):
|
||||
self.info = self.png.im_info
|
||||
self._text = None
|
||||
self.tile = self.png.im_tile
|
||||
self.custom_mimetype = self.png.im_custom_mimetype
|
||||
|
||||
if self.png.im_palette:
|
||||
rawmode, data = self.png.im_palette
|
||||
@ -677,6 +680,8 @@ class PngImageFile(ImageFile.ImageFile):
|
||||
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
|
||||
@ -908,4 +913,3 @@ Image.register_save(PngImageFile.format, _save)
|
||||
Image.register_extensions(PngImageFile.format, [".png", ".apng"])
|
||||
|
||||
Image.register_mime(PngImageFile.format, "image/png")
|
||||
Image.register_mime(PngImageFile.format, "image/apng")
|
||||
|
||||
@ -195,7 +195,8 @@ class SpiderImageFile(ImageFile.ImageFile):
|
||||
|
||||
def _close__fp(self):
|
||||
try:
|
||||
self.__fp.close()
|
||||
if self.__fp != self.fp:
|
||||
self.__fp.close()
|
||||
except AttributeError:
|
||||
pass
|
||||
finally:
|
||||
|
||||
@ -1361,7 +1361,8 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||
|
||||
def _close__fp(self):
|
||||
try:
|
||||
self.__fp.close()
|
||||
if self.__fp != self.fp:
|
||||
self.__fp.close()
|
||||
except AttributeError:
|
||||
pass
|
||||
finally:
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
# Master version for Pillow
|
||||
__version__ = '5.4.0'
|
||||
__version__ = '5.4.1'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user