diff --git a/.ci/install.sh b/.ci/install.sh index d5cbd8248..4748feb3d 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -23,7 +23,7 @@ if [[ $(uname) != CYGWIN* ]]; then sudo apt-get -qq install libfreetype6-dev liblcms2-dev python3-tk\ ghostscript libffi-dev libjpeg-turbo-progs libopenjp2-7-dev\ cmake meson imagemagick libharfbuzz-dev libfribidi-dev\ - sway wl-clipboard + sway wl-clipboard libopenblas-dev fi python3 -m pip install --upgrade pip @@ -38,8 +38,7 @@ python3 -m pip install -U pytest-timeout python3 -m pip install pyroma if [[ $(uname) != CYGWIN* ]]; then - # TODO Remove condition when NumPy supports 3.12 - if ! [ "$GHA_PYTHON_VERSION" == "3.12-dev" ]; then python3 -m pip install numpy ; fi + python3 -m pip install numpy # PyQt6 doesn't support PyPy3 if [[ $GHA_PYTHON_VERSION == 3.* ]]; then diff --git a/.github/workflows/macos-install.sh b/.github/workflows/macos-install.sh index 1fc6262f4..a20838a15 100755 --- a/.github/workflows/macos-install.sh +++ b/.github/workflows/macos-install.sh @@ -3,6 +3,7 @@ set -e brew install libtiff libjpeg openjpeg libimagequant webp little-cms2 freetype libraqm +export PKG_CONFIG_PATH="/usr/local/opt/openblas/lib/pkgconfig" PYTHONOPTIMIZE=0 python3 -m pip install cffi python3 -m pip install coverage @@ -13,8 +14,7 @@ python3 -m pip install -U pytest-cov python3 -m pip install -U pytest-timeout python3 -m pip install pyroma -# TODO Remove condition when NumPy supports 3.12 -if ! [ "$GHA_PYTHON_VERSION" == "3.12-dev" ]; then python3 -m pip install numpy ; fi +python3 -m pip install numpy # extra test images pushd depends && ./install_extra_test_images.sh && popd diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 949da6360..5071e5bd4 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -102,10 +102,10 @@ jobs: run: | bash.exe .ci/install.sh - - name: Install latest NumPy + - name: Upgrade NumPy shell: dash.exe -l "{0}" run: | - python3 -m pip install -U numpy + python3 -m pip install -U "numpy<1.26" - name: Build shell: bash.exe -eo pipefail -o igncr "{0}" diff --git a/CHANGES.rst b/CHANGES.rst index 1555c130f..d1dce8e0d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 10.1.0 (unreleased) ------------------- +- Fixed bug when reading BC5S DDS images #7401 + [radarhere] + +- Prevent TIFF orientation from being applied more than once #7383 + [radarhere] + - Use previous pixel alpha for QOI_OP_RGB #7357 [radarhere] @@ -50,6 +56,15 @@ Changelog (Pillow) - Fix missing symbols when libtiff depends on libjpeg #7270 [heitbaum] +10.0.1 (2023-09-15) +------------------- + +- Updated libwebp to 1.3.2 #7395 + [radarhere] + +- Updated zlib to 1.3 #7344 + [radarhere] + 10.0.0 (2023-07-01) ------------------- diff --git a/Tests/images/bc5s.png b/Tests/images/bc5s.png index 39d7811bf..657d72305 100644 Binary files a/Tests/images/bc5s.png and b/Tests/images/bc5s.png differ diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index ac78b0869..a7394f1bf 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -8,7 +8,7 @@ from collections import namedtuple import pytest -from PIL import Image, ImageFilter, TiffImagePlugin, TiffTags, features +from PIL import Image, ImageFilter, ImageOps, TiffImagePlugin, TiffTags, features from PIL.TiffImagePlugin import SAMPLEFORMAT, STRIPOFFSETS, SUBIFD from .helper import ( @@ -1035,7 +1035,18 @@ class TestFileLibTiff(LibTiffTestCase): with Image.open("Tests/images/g4_orientation_1.tif") as base_im: for i in range(2, 9): with Image.open("Tests/images/g4_orientation_" + str(i) + ".tif") as im: + assert 274 in im.tag_v2 + im.load() + assert 274 not in im.tag_v2 + + assert_image_similar(base_im, im, 0.7) + + def test_exif_transpose(self): + with Image.open("Tests/images/g4_orientation_1.tif") as base_im: + for i in range(2, 9): + with Image.open("Tests/images/g4_orientation_" + str(i) + ".tif") as im: + im = ImageOps.exif_transpose(im) assert_image_similar(base_im, im, 0.7) diff --git a/depends/install_imagequant.sh b/depends/install_imagequant.sh index fd6000ee1..ab94875d8 100755 --- a/depends/install_imagequant.sh +++ b/depends/install_imagequant.sh @@ -1,7 +1,7 @@ #!/bin/bash # install libimagequant -archive=libimagequant-4.2.0 +archive=libimagequant-4.2.1 ./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz diff --git a/depends/install_webp.sh b/depends/install_webp.sh index 4636aab43..6f867ab37 100755 --- a/depends/install_webp.sh +++ b/depends/install_webp.sh @@ -1,7 +1,7 @@ #!/bin/bash # install webp -archive=libwebp-1.3.1 +archive=libwebp-1.3.2 ./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz diff --git a/docs/installation.rst b/docs/installation.rst index 37739c8c0..ca78b2998 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -180,7 +180,7 @@ Many of Pillow's features require external libraries: * **libimagequant** provides improved color quantization - * Pillow has been tested with libimagequant **2.6-4.2** + * Pillow has been tested with libimagequant **2.6-4.2.1** * Libimagequant is licensed GPLv3, which is more restrictive than the Pillow license, therefore we will not be distributing binaries with libimagequant support enabled. @@ -498,7 +498,7 @@ These platforms have been reported to work at the versions mentioned. | Operating system | | Tested Python | | Latest tested | | Tested | | | | versions | | Pillow version | | processors | +==================================+===========================+==================+==============+ -| macOS 13 Ventura | 3.8, 3.9, 3.10, 3.11 | 10.0.0 |arm | +| macOS 13 Ventura | 3.8, 3.9, 3.10, 3.11 | 10.0.1 |arm | | +---------------------------+------------------+ | | | 3.7 | 9.5.0 | | +----------------------------------+---------------------------+------------------+--------------+ diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index 41d3b8fce..66e6b2a0c 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -93,10 +93,14 @@ Generating images Registering plugins ^^^^^^^^^^^^^^^^^^^ +.. autofunction:: preinit +.. autofunction:: init + .. note:: - These functions are for use by plugin authors. Application authors can - ignore them. + These functions are for use by plugin authors. They are called when a + plugin is loaded as part of :py:meth:`~preinit()` or :py:meth:`~init()`. + Application authors can ignore them. .. autofunction:: register_open .. autofunction:: register_mime diff --git a/docs/releasenotes/10.0.1.rst b/docs/releasenotes/10.0.1.rst new file mode 100644 index 000000000..6ac30e7fc --- /dev/null +++ b/docs/releasenotes/10.0.1.rst @@ -0,0 +1,14 @@ +10.0.1 +------ + +Security +======== + +This release addresses :cve:`2023-4863`, by providing an updated install script and +updated wheels to include libwebp 1.3.2, preventing a potential heap buffer overflow +in WebP. + +Updated tests to pass with latest zlib version +============================================== + +The release of zlib 1.3 caused one of the tests in the Pillow test suite to fail. diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index a843ddd72..1b1c353fd 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -15,6 +15,7 @@ expected to be backported to earlier versions. :maxdepth: 2 10.1.0 + 10.0.1 10.0.0 9.5.0 9.4.0 diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 9bf293d13..2a6b4646b 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -298,7 +298,11 @@ _initialized = 0 def preinit(): - """Explicitly load standard file format drivers.""" + """ + Explicitly loads BMP, GIF, JPEG, PPM and PPM file format drivers. + + It is called when opening or saving images. + """ global _initialized if _initialized >= 1: @@ -334,11 +338,6 @@ def preinit(): assert PngImagePlugin except ImportError: pass - # try: - # import TiffImagePlugin - # assert TiffImagePlugin - # except ImportError: - # pass _initialized = 1 @@ -347,6 +346,9 @@ def init(): """ Explicitly initializes the Python Imaging Library. This function loads all available file format drivers. + + It is called when opening or saving images if :py:meth:`~preinit()` is + insufficient, and by :py:meth:`~PIL.features.pilinfo`. """ global _initialized @@ -3407,8 +3409,12 @@ def register_open(id, factory, accept=None): def register_mime(id, mimetype): """ - Registers an image MIME type. This function should not be used - in application code. + Registers an image MIME type by populating ``Image.MIME``. This function + should not be used in application code. + + ``Image.MIME`` provides a mapping from image format identifiers to mime + formats, but :py:meth:`~PIL.ImageFile.ImageFile.get_format_mimetype` can + provide a different result for specific images. :param id: An image format identifier. :param mimetype: The image MIME type for this format. diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 17702778c..1231ad6eb 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -588,6 +588,7 @@ def exif_transpose(image, *, in_place=False): with the transposition applied. If there is no transposition, a copy of the image will be returned. """ + image.load() image_exif = image.getexif() orientation = image_exif.get(ExifTags.Base.Orientation) method = { diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 99c23ad4b..5d3bc4f83 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1203,20 +1203,6 @@ class TiffImageFile(ImageFile.ImageFile): return super().load() def load_end(self): - if self._tile_orientation: - method = { - 2: Image.Transpose.FLIP_LEFT_RIGHT, - 3: Image.Transpose.ROTATE_180, - 4: Image.Transpose.FLIP_TOP_BOTTOM, - 5: Image.Transpose.TRANSPOSE, - 6: Image.Transpose.ROTATE_270, - 7: Image.Transpose.TRANSVERSE, - 8: Image.Transpose.ROTATE_90, - }.get(self._tile_orientation) - if method is not None: - self.im = self.im.transpose(method) - self._size = self.im.size - # allow closing if we're on the first frame, there's no next # This is the ImageFile.load path only, libtiff specific below. if not self.is_animated: @@ -1233,6 +1219,10 @@ class TiffImageFile(ImageFile.ImageFile): continue exif.get_ifd(key) + ImageOps.exif_transpose(self, in_place=True) + if ExifTags.Base.Orientation in self.tag_v2: + del self.tag_v2[ExifTags.Base.Orientation] + def _load_libtiff(self): """Overload method triggered when we detect a compressed tiff Calls out to libtiff""" @@ -1542,8 +1532,6 @@ class TiffImageFile(ImageFile.ImageFile): palette = [o8(b // 256) for b in self.tag_v2[COLORMAP]] self.palette = ImagePalette.raw("RGB;L", b"".join(palette)) - self._tile_orientation = self.tag_v2.get(ExifTags.Base.Orientation) - # # -------------------------------------------------------------------- diff --git a/src/libImaging/BcnDecode.c b/src/libImaging/BcnDecode.c index a57b74b61..dfd4b3e07 100644 --- a/src/libImaging/BcnDecode.c +++ b/src/libImaging/BcnDecode.c @@ -118,8 +118,8 @@ decode_bc3_alpha(char *dst, const UINT8 *src, int stride, int o, int sign) { if (sign == 1) { bc5s_alpha b; memcpy(&b, src, sizeof(bc5s_alpha)); - a0 = (b.a0 + 255) / 2; - a1 = (b.a1 + 255) / 2; + a0 = b.a0 + 128; + a1 = b.a1 + 128; lut1 = b.lut[0] | (b.lut[1] << 8) | (b.lut[2] << 16); lut2 = b.lut[3] | (b.lut[4] << 8) | (b.lut[5] << 16); } else { diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 812799ac2..d9122e680 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -157,9 +157,9 @@ deps = { "libs": [r"liblzma.lib"], }, "libwebp": { - "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.3.1.tar.gz", - "filename": "libwebp-1.3.1.tar.gz", - "dir": "libwebp-1.3.1", + "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.3.2.tar.gz", + "filename": "libwebp-1.3.2.tar.gz", + "dir": "libwebp-1.3.2", "license": "COPYING", "build": [ cmd_rmdir(r"output\release-static"), # clean @@ -335,9 +335,9 @@ deps = { "libs": [r"imagequant.lib"], }, "harfbuzz": { - "url": "https://github.com/harfbuzz/harfbuzz/archive/8.1.1.zip", - "filename": "harfbuzz-8.1.1.zip", - "dir": "harfbuzz-8.1.1", + "url": "https://github.com/harfbuzz/harfbuzz/archive/8.2.0.zip", + "filename": "harfbuzz-8.2.0.zip", + "dir": "harfbuzz-8.2.0", "license": "COPYING", "build": [ *cmds_cmake(