Merge branch 'master' into jp2-decode-subsample

This commit is contained in:
Andrew Murray 2021-01-04 23:25:09 +11:00 committed by GitHub
commit 2341c6b933
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
226 changed files with 15644 additions and 14927 deletions

View File

@ -1,5 +1,4 @@
version: '{build}'
image: Visual Studio 2017
clone_folder: c:\pillow
init:
- ECHO %PYTHON%
@ -8,21 +7,22 @@ init:
environment:
EXECUTABLE: python.exe
PIP_DIR: Scripts
TEST_OPTIONS:
DEPLOY: YES
matrix:
- PYTHON: C:/Python38
- PYTHON: C:/Python39
ARCHITECTURE: x86
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
- PYTHON: C:/Python36-x64
ARCHITECTURE: x64
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
install:
- curl -fsSL -o pillow-depends.zip https://github.com/python-pillow/pillow-depends/archive/master.zip
- 7z x pillow-depends.zip -oc:\
- mv c:\pillow-depends-master c:\pillow-depends
- xcopy /s c:\pillow-depends\test_images\* c:\pillow\tests\images
- xcopy /S /Y c:\pillow-depends\test_images\* c:\pillow\tests\images
- 7z x ..\pillow-depends\nasm-2.14.02-win64.zip -oc:\
- ..\pillow-depends\gs9533w32.exe /S
- path c:\nasm-2.14.02;C:\Program Files (x86)\gs\gs9.53.3\bin;%PATH%
@ -32,6 +32,7 @@ install:
c:\pillow\winbuild\build\build_dep_all.cmd
$host.SetShouldExit(0)
- path C:\pillow\winbuild\build\bin;%PATH%
- '%PYTHON%\%EXECUTABLE% -m pip install -U "setuptools>=49.3.2"'
build_script:
- ps: |
@ -42,13 +43,13 @@ build_script:
test_script:
- cd c:\pillow
- '%PYTHON%\%PIP_DIR%\pip.exe install pytest pytest-cov'
- '%PYTHON%\%EXECUTABLE% -m pip install pytest pytest-cov'
- c:\"Program Files (x86)"\"Windows Kits"\10\Debuggers\x86\gflags.exe /p /enable %PYTHON%\%EXECUTABLE%
- '%PYTHON%\%EXECUTABLE% -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests'
#- '%PYTHON%\%EXECUTABLE% test-installed.py -v -s %TEST_OPTIONS%' TODO TEST_OPTIONS with pytest?
after_test:
- pip install codecov
- python -m pip install codecov
- codecov --file coverage.xml --name %PYTHON% --flags AppVeyor
matrix:
@ -65,7 +66,7 @@ artifacts:
before_deploy:
- cd c:\pillow
- '%PYTHON%\%PIP_DIR%\pip.exe install wheel'
- '%PYTHON%\%EXECUTABLE% -m pip install wheel'
- cd c:\pillow\winbuild\
- c:\pillow\winbuild\build\build_pillow.cmd bdist_wheel
- cd c:\pillow

View File

@ -7,13 +7,3 @@ if [[ $MATRIX_DOCKER ]]; then
else
coverage xml
fi
if [[ $TRAVIS ]]; then
codecov --flags TravisCI
fi
if [ "$TRAVIS_PYTHON_VERSION" == "3.8" ]; then
# Coverage and quality reports on just the latest diff.
depends/diffcover-install.sh
depends/diffcover-run.sh
fi

View File

@ -21,35 +21,30 @@ sudo apt-get -qq install libfreetype6-dev liblcms2-dev python3-tk\
ghostscript libffi-dev libjpeg-turbo-progs libopenjp2-7-dev\
cmake imagemagick libharfbuzz-dev libfribidi-dev
if [[ $TRAVIS_CPU_ARCH == "s390x" ]]; then sudo chown $USER ~/.cache/pip/wheels ; fi
python3 -m pip install --upgrade pip
PYTHONOPTIMIZE=0 python3 -m pip install cffi
python3 -m pip install coverage
python3 -m pip install olefile
python3 -m pip install -U pytest
python3 -m pip install -U pytest-cov
python3 -m pip install pyroma
python3 -m pip install test-image-results
# TODO Remove condition when numpy supports 3.10
if ! [ "$GHA_PYTHON_VERSION" == "3.10-dev" ]; then python3 -m pip install numpy ; fi
pip install --upgrade pip
PYTHONOPTIMIZE=0 pip install cffi
pip install coverage
pip install olefile
pip install -U pytest
pip install -U pytest-cov
pip install pyroma
pip install test-image-results
pip install numpy
# TODO Remove when 3.8 / 3.9 includes setuptools 49.3.2+:
if [ "$GHA_PYTHON_VERSION" == "3.8" ]; then python3 -m pip install -U "setuptools>=49.3.2" ; fi
if [ "$GHA_PYTHON_VERSION" == "3.9" ]; then python3 -m pip install -U "setuptools>=49.3.2" ; fi
# TODO Remove when 3.8 / 3.9 / PyPy3 includes setuptools 49.3.2+:
if [ "$GHA_PYTHON_VERSION" == "3.8" ]; then pip install -U "setuptools>=49.3.2" ; fi
if [ "$GHA_PYTHON_VERSION" == "3.9" ]; then pip install -U "setuptools>=49.3.2" ; fi
if [ "$TRAVIS_PYTHON_VERSION" == "pypy3.6-7.3.1" ]; then pip install -U "setuptools>=49.3.2" ; fi
if [[ $TRAVIS_PYTHON_VERSION == 3.* ]]; then
# PyQt5 doesn't support PyPy3
# Wheel doesn't yet support 3.10
if [[ $GHA_PYTHON_VERSION == 3.* && $GHA_PYTHON_VERSION != "3.10-dev" ]]; then
# arm64, ppc64le, s390x CPUs:
# "ERROR: Could not find a version that satisfies the requirement pyqt5"
if [[ $TRAVIS_CPU_ARCH == "amd64" ]]; then
sudo apt-get -qq install libxcb-xinerama0 pyqt5-dev-tools
pip install pyqt5
fi
python3 -m pip install pyqt5
fi
# docs only on Python 3.8
if [ "$TRAVIS_PYTHON_VERSION" == "3.8" ]; then pip install -r requirements.txt ; fi
# webp
pushd depends && ./install_webp.sh && popd

View File

@ -3,8 +3,3 @@
set -e
python -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests
# Docs
if [ "$TRAVIS_PYTHON_VERSION" == "3.8" ] && [ "$TRAVIS_CPU_ARCH" == "amd64" ]; then
make doccheck
fi

20
.clang-format Normal file
View File

@ -0,0 +1,20 @@
# A clang-format style that approximates Python's PEP 7
# Useful for IDE integration
BasedOnStyle: Google
AlwaysBreakAfterReturnType: All
AllowShortIfStatementsOnASingleLine: false
AlignAfterOpenBracket: AlwaysBreak
BinPackArguments: false
BinPackParameters: false
BreakBeforeBraces: Attach
ColumnLimit: 88
DerivePointerAlignment: false
IndentWidth: 4
Language: Cpp
PointerAlignment: Right
ReflowComments: true
SortIncludes: false
SpaceBeforeParens: ControlStatements
SpacesInParentheses: false
TabWidth: 4
UseTab: Never

View File

@ -9,7 +9,7 @@ Please send a pull request to the master branch. Please include [documentation](
- Fork the Pillow repository.
- Create a branch from master.
- Develop bug fixes, features, tests, etc.
- Run the test suite. You can enable [Travis CI](https://travis-ci.org/profile/) and [AppVeyor](https://ci.appveyor.com/projects/new) on your repo to catch test failures prior to the pull request, and [Codecov](https://codecov.io/gh) to see if the changed code is covered by tests.
- Run the test suite. You can enable GitHub Actions (https://github.com/MY-USERNAME/Pillow/actions) and [AppVeyor](https://ci.appveyor.com/projects/new) on your repo to catch test failures prior to the pull request, and [Codecov](https://codecov.io/gh) to see if the changed code is covered by tests.
- Create a pull request to pull the changes from your branch to the Pillow master.
### Guidelines
@ -17,7 +17,7 @@ Please send a pull request to the master branch. Please include [documentation](
- Separate code commits from reformatting commits.
- Provide tests for any newly added code.
- Follow PEP 8.
- When committing only documentation changes please include [ci skip] in the commit message to avoid running tests on Travis-CI and AppVeyor.
- When committing only documentation changes please include `[ci skip]` in the commit message to avoid running tests on AppVeyor.
## Reporting Issues

1
.github/mergify.yml vendored
View File

@ -8,7 +8,6 @@ pull_request_rules:
- status-success=Docker Test Successful
- status-success=Windows Test Successful
- status-success=continuous-integration/appveyor/pr
- status-success=continuous-integration/travis-ci/pr
actions:
merge:
method: merge

26
.github/release-drafter.yml vendored Normal file
View File

@ -0,0 +1,26 @@
name-template: "$NEXT_MINOR_VERSION"
tag-template: "$NEXT_MINOR_VERSION"
change-template: '- $TITLE #$NUMBER [@$AUTHOR]'
categories:
- title: "Dependencies"
label: "Dependency"
- title: "Deprecations"
label: "Deprecation"
- title: "Documentation"
label: "Documentation"
- title: "Removals"
label: "Removal"
- title: "Testing"
label: "Testing"
exclude-labels:
- "changelog: skip"
template: |
https://pillow.readthedocs.io/en/stable/releasenotes/$NEXT_MINOR_VERSION.html
## Changes
$CHANGES

View File

@ -2,22 +2,23 @@
set -e
brew install libtiff libjpeg openjpeg libimagequant webp little-cms2 freetype openblas
brew install libtiff libjpeg openjpeg libimagequant webp little-cms2 freetype openblas libraqm
PYTHONOPTIMIZE=0 pip install cffi
pip install coverage
pip install olefile
pip install -U pytest
pip install -U pytest-cov
pip install pyroma
pip install test-image-results
PYTHONOPTIMIZE=0 python3 -m pip install cffi
python3 -m pip install coverage
python3 -m pip install olefile
python3 -m pip install -U pytest
python3 -m pip install -U pytest-cov
python3 -m pip install pyroma
python3 -m pip install test-image-results
echo -e "[openblas]\nlibraries = openblas\nlibrary_dirs = /usr/local/opt/openblas/lib" >> ~/.numpy-site.cfg
pip install numpy
# TODO Remove condition when numpy supports 3.10
if ! [ "$GHA_PYTHON_VERSION" == "3.10-dev" ]; then python3 -m pip install numpy ; fi
# TODO Remove when 3.8 / 3.9 includes setuptools 49.3.2+:
if [ "$GHA_PYTHON_VERSION" == "3.8" ]; then pip install -U "setuptools>=49.3.2" ; fi
if [ "$GHA_PYTHON_VERSION" == "3.9" ]; then pip install -U "setuptools>=49.3.2" ; fi
if [ "$GHA_PYTHON_VERSION" == "3.8" ]; then python3 -m pip install -U "setuptools>=49.3.2" ; fi
if [ "$GHA_PYTHON_VERSION" == "3.9" ]; then python3 -m pip install -U "setuptools>=49.3.2" ; fi
# extra test images
pushd depends && ./install_extra_test_images.sh && popd

17
.github/workflows/release-drafter.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: Release drafter
on:
push:
# branches to consider in the event; optional, defaults to all
branches:
- master
jobs:
update_release_draft:
if: github.repository == 'python-pillow/Pillow'
runs-on: ubuntu-latest
steps:
# Drafts your next release notes as pull requests are merged into "master"
- uses: release-drafter/release-drafter@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -10,19 +10,30 @@ jobs:
fail-fast: false
matrix:
docker: [
# Run slower jobs first to give them a headstart and reduce waiting time
ubuntu-20.04-focal-arm64v8,
ubuntu-20.04-focal-ppc64le,
ubuntu-20.04-focal-s390x,
# Then run the remainder
alpine,
amazon-2-amd64,
arch,
ubuntu-18.04-bionic-amd64,
ubuntu-20.04-focal-amd64,
debian-10-buster-x86,
centos-6-amd64,
centos-7-amd64,
centos-8-amd64,
amazon-1-amd64,
amazon-2-amd64,
debian-10-buster-x86,
fedora-32-amd64,
fedora-33-amd64,
ubuntu-18.04-bionic-amd64,
ubuntu-20.04-focal-amd64,
]
dockerTag: [master]
include:
- docker: "ubuntu-20.04-focal-arm64v8"
qemu-arch: "aarch64"
- docker: "ubuntu-20.04-focal-ppc64le"
qemu-arch: "ppc64le"
- docker: "ubuntu-20.04-focal-s390x"
qemu-arch: "s390x"
name: ${{ matrix.docker }}
@ -32,6 +43,11 @@ jobs:
- name: Build system information
run: python .github/workflows/system-info.py
- name: Set up QEMU
if: "matrix.qemu-arch"
run: |
docker run --rm --privileged aptman/qus -s -- -p ${{ matrix.qemu-arch }}
- name: Docker pull
run: |
docker pull pythonpillow/${{ matrix.docker }}:${{ matrix.dockerTag }}

View File

@ -8,7 +8,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.6", "3.7", "3.8", "3.9", "pypy3"]
python-version: ["pypy-3.6", "pypy-3.7", "3.6", "3.7", "3.8", "3.9", "3.10-dev"]
architecture: ["x86", "x64"]
include:
- architecture: "x86"
@ -19,7 +19,9 @@ jobs:
platform-msbuild: "x64"
exclude:
# PyPy does not support 64-bit on Windows
- python-version: "pypy3"
- python-version: "pypy-3.6"
architecture: "x64"
- python-version: "pypy-3.7"
architecture: "x64"
timeout-minutes: 30
@ -55,7 +57,7 @@ jobs:
- name: Print build system information
run: python .github/workflows/system-info.py
- name: pip install wheel pytest pytest-cov
- name: python -m pip install wheel pytest pytest-cov
run: python -m pip install wheel pytest pytest-cov
# TODO Remove when 3.8 / 3.9 includes setuptools 49.3.2+:
@ -64,6 +66,7 @@ jobs:
run: python -m pip install -U "setuptools>=49.3.2"
- name: Install dependencies
id: install
run: |
7z x winbuild\depends\nasm-2.14.02-win64.zip "-o$env:RUNNER_WORKSPACE\"
echo "$env:RUNNER_WORKSPACE\nasm-2.14.02" >> $env:GITHUB_PATH
@ -71,7 +74,10 @@ jobs:
winbuild\depends\gs9533w32.exe /S
echo "C:\Program Files (x86)\gs\gs9.53.3\bin" >> $env:GITHUB_PATH
xcopy /s winbuild\depends\test_images\* Tests\images\
xcopy /S /Y winbuild\depends\test_images\* Tests\images\
# make cache key depend on VS version
& "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" | find """catalog_buildVersion""" | ForEach-Object { $a = $_.split(" ")[1]; echo "::set-output name=vs::$a" }
shell: pwsh
- name: Cache build
@ -80,7 +86,7 @@ jobs:
with:
path: winbuild\build
key:
${{ hashFiles('winbuild\build_prepare.py') }}-${{ hashFiles('.github\workflows\test-windows.yml') }}-${{ env.pythonLocation }}
${{ hashFiles('winbuild\build_prepare.py') }}-${{ hashFiles('.github\workflows\test-windows.yml') }}-${{ env.pythonLocation }}-${{ steps.install.outputs.vs }}
- name: Prepare build
if: steps.build-cache.outputs.cache-hit != 'true'
@ -91,25 +97,32 @@ jobs:
- name: Build dependencies / libjpeg-turbo
if: steps.build-cache.outputs.cache-hit != 'true'
run: "& winbuild\\build\\build_dep_libjpeg.cmd"
- name: Build dependencies / zlib
if: steps.build-cache.outputs.cache-hit != 'true'
run: "& winbuild\\build\\build_dep_zlib.cmd"
- name: Build dependencies / LibTiff
if: steps.build-cache.outputs.cache-hit != 'true'
run: "& winbuild\\build\\build_dep_libtiff.cmd"
- name: Build dependencies / WebP
if: steps.build-cache.outputs.cache-hit != 'true'
run: "& winbuild\\build\\build_dep_libwebp.cmd"
# for FreeType CBDT font support
- name: Build dependencies / libpng
if: steps.build-cache.outputs.cache-hit != 'true'
run: "& winbuild\\build\\build_dep_libpng.cmd"
- name: Build dependencies / FreeType
if: steps.build-cache.outputs.cache-hit != 'true'
run: "& winbuild\\build\\build_dep_freetype.cmd"
- name: Build dependencies / LCMS2
if: steps.build-cache.outputs.cache-hit != 'true'
run: "& winbuild\\build\\build_dep_lcms2.cmd"
- name: Build dependencies / OpenJPEG
if: steps.build-cache.outputs.cache-hit != 'true'
run: "& winbuild\\build\\build_dep_openjpeg.cmd"
@ -123,9 +136,11 @@ jobs:
- name: Build dependencies / HarfBuzz
if: steps.build-cache.outputs.cache-hit != 'true'
run: "& winbuild\\build\\build_dep_harfbuzz.cmd"
- name: Build dependencies / FriBidi
if: steps.build-cache.outputs.cache-hit != 'true'
run: "& winbuild\\build\\build_dep_fribidi.cmd"
- name: Build dependencies / Raqm
if: steps.build-cache.outputs.cache-hit != 'true'
run: "& winbuild\\build\\build_dep_libraqm.cmd"

View File

@ -13,7 +13,9 @@ jobs:
"macOS-latest",
]
python-version: [
"pypy3",
"pypy-3.7",
"pypy-3.6",
"3.10-dev",
"3.9",
"3.8",
"3.7",
@ -21,9 +23,9 @@ jobs:
]
include:
- python-version: "3.6"
env: PYTHONOPTIMIZE=1
PYTHONOPTIMIZE: 1
- python-version: "3.7"
env: PYTHONOPTIMIZE=2
PYTHONOPTIMIZE: 2
# Include new variables for Codecov
- os: ubuntu-latest
codecov-flag: GHA_Ubuntu
@ -44,7 +46,7 @@ jobs:
- name: Get pip cache dir
id: pip-cache
run: |
echo "::set-output name=dir::$(pip cache dir)"
echo "::set-output name=dir::$(python3 -m pip cache dir)"
- name: pip cache
uses: actions/cache@v2
@ -78,7 +80,13 @@ jobs:
- name: Test
run: |
.ci/test.sh
if [ "${{ matrix.os }}" = "ubuntu-latest" ]; then
xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh
else
.ci/test.sh
fi
env:
PYTHONOPTIMIZE: ${{ matrix.PYTHONOPTIMIZE }}
- name: Prepare to upload errors
if: failure()
@ -94,9 +102,9 @@ jobs:
path: Tests/errors
- name: Docs
if: startsWith(matrix.os, 'ubuntu') && matrix.python-version == 3.8
if: startsWith(matrix.os, 'ubuntu') && matrix.python-version == 3.9
run: |
pip install sphinx-removed-in sphinx-rtd-theme
python3 -m pip install sphinx-issues sphinx-removed-in sphinx-rtd-theme
make doccheck
- name: After success

4
.gitignore vendored
View File

@ -81,6 +81,10 @@ docs/_build/
# Extra test images installed from pillow-depends/test_images
Tests/images/README.md
Tests/images/crash_1.tif
Tests/images/crash_2.tif
Tests/images/string_dimension.tiff
Tests/images/jpeg2000
Tests/images/msp
Tests/images/picins
Tests/images/sunraster

View File

@ -8,7 +8,7 @@ repos:
files: \.py$
types: []
- repo: https://github.com/timothycrosley/isort
- repo: https://github.com/PyCQA/isort
rev: 377d260ffa6f746693f97b46d95025afc4bd8275 # frozen: 5.4.2
hooks:
- id: isort

View File

@ -1,70 +0,0 @@
dist: xenial
language: python
cache:
pip: true
directories:
- $HOME/.cache/pre-commit
notifications:
irc: "chat.freenode.net#pil"
# Run fast lint first to get fast feedback.
# Run slower CPUs next, to give them a headstart and reduce waiting time.
# Then run the remainder.
matrix:
fast_finish: true
include:
- python: "3.6"
name: "Lint"
env: LINT="true"
- python: "3.6"
arch: arm64
- python: "3.7"
arch: ppc64le
- python: "3.8"
arch: s390x
- python: "pypy3.6-7.3.1"
name: "PyPy3 Xenial"
- python: "3.9-dev"
name: "3.9-dev Xenial"
services: xvfb
- python: "3.8"
name: "3.8 Xenial"
services: xvfb
- python: '3.7'
name: "3.7 Xenial PYTHONOPTIMIZE=2"
env: PYTHONOPTIMIZE=2
services: xvfb
- python: '3.6'
name: "3.6 Xenial PYTHONOPTIMIZE=1"
env: PYTHONOPTIMIZE=1
services: xvfb
allow_failures:
- python: "3.9-dev"
install:
- |
if [ "$LINT" == "true" ]; then
pip install tox
else
.ci/install.sh;
fi
script:
- |
if [ "$LINT" == "true" ]; then
tox -e lint
else
.ci/build.sh
.ci/test.sh
fi
after_success:
- |
if [ "$LINT" == "" ]; then
.ci/after_success.sh
fi

View File

@ -2,12 +2,78 @@
Changelog (Pillow)
==================
8.1.0 (unreleased)
8.1.0 (2020-01-02)
------------------
- Fix TIFF OOB Write error. CVE-2020-35654 #5175
[wiredfool]
- Fix for Read Overflow in PCX Decoding. CVE-2020-35653 #5174
[wiredfool, radarhere]
- Fix for SGI Decode buffer overrun. CVE-2020-35655 #5173
[wiredfool, radarhere]
- Fix OOB Read when saving GIF of xsize=1 #5149
[wiredfool]
- Makefile updates #5159
[wiredfool, radarhere]
- Add support for PySide6 #5161
[hugovk]
- Use disposal settings from previous frame in APNG #5126
[radarhere]
- Added exception explaining that _repr_png_ saves to PNG #5139
[radarhere]
- Use previous disposal method in GIF load_end #5125
[radarhere]
- Allow putpalette to accept 1024 integers to include alpha values #5089
[radarhere]
- Fix OOB Read when writing TIFF with custom Metadata #5148
[wiredfool]
- Added append_images support for ICO #4568
[ziplantil, radarhere]
- Block TIFFTAG_SUBIFD #5120
[radarhere]
- Fixed dereferencing potential null pointers #5108, #5111
[cgohlke, radarhere]
- Deprecate FreeType 2.7 #5098
[hugovk, radarhere]
- Moved warning to end of execution #4965
[radarhere]
- Removed unused fromstring and tostring C methods #5026
[radarhere]
- init() if one of the formats is unrecognised #5037
[radarhere]
- Moved string_dimension CVE image to pillow-depends #4993
[radarhere]
- Support raw rgba8888 for DDS #4760
[qiankanglai]
8.0.1 (2020-10-22)
------------------
- Update FreeType used in binary wheels to 2.10.4 to fix CVE-2020-15999.
[radarhere]
- Moved string_dimension image to pillow-depends #4993
[radarhere]
8.0.0 (2020-10-15)
------------------
@ -4030,8 +4096,8 @@ Changelog (Pillow)
1.0 (07/30/2010)
----------------
- Remove support for ``import Image``, etc. from the standard namespace. ``from PIL import Image`` etc. now required.
- Forked PIL based on `Hanno Schlichting's re-packaging <https://dist.plone.org/thirdparty/PIL-1.1.7.tar.gz>`_
- Remove support for ``import Image``. ``from PIL import Image`` now required.
- Forked PIL based on `Chris McDonough and Hanno Schlichting's setuptools compatible re-packaging <https://dist.plone.org/thirdparty/PIL-1.1.7.tar.gz>`_
[aclark4life]
Pre-fork

View File

@ -5,9 +5,9 @@ The Python Imaging Library (PIL) is
Pillow is the friendly PIL fork. It is
Copyright © 2010-2020 by Alex Clark and contributors
Copyright © 2010-2021 by Alex Clark and contributors
Like PIL, Pillow is licensed under the open source PIL Software License:
Like PIL, Pillow is licensed under the open source HPND License:
By obtaining, using, and/or copying this software and/or its associated
documentation, you agree that you have read, understood, and will comply

View File

@ -18,6 +18,7 @@ graft docs
# build/src control detritus
exclude .appveyor.yml
exclude .clang-format
exclude .coveragerc
exclude .editorconfig
exclude .readthedocs.yml

View File

@ -1,4 +1,4 @@
.DEFAULT_GOAL := release-test
.DEFAULT_GOAL := help
.PHONY: clean
clean:
@ -7,13 +7,6 @@ clean:
rm -r build || true
find . -name __pycache__ | xargs rm -r || true
BRANCHES=`git branch -a | grep -v HEAD | grep -v master | grep remote`
.PHONY: co
co:
-for i in $(BRANCHES) ; do \
git checkout -t $$i ; \
done
.PHONY: coverage
coverage:
pytest -qq
@ -33,7 +26,7 @@ doccheck:
.PHONY: docserve
docserve:
cd docs/_build/html && python3 -mSimpleHTTPServer 2> /dev/null&
cd docs/_build/html && python3 -m http.server 2> /dev/null&
.PHONY: help
help:
@ -47,7 +40,9 @@ help:
@echo " install make and install"
@echo " install-coverage make and install with C coverage"
@echo " install-req install documentation and test dependencies"
@echo " install-venv install in virtualenv"
@echo " install-venv (deprecated) install in virtualenv"
@echo " lint run the lint checks"
@echo " lint-fix run black and isort to (mostly) fix lint issues."
@echo " release-test run code and package tests before release"
@echo " test run tests on installed pillow"
@echo " upload build and upload sdists to PyPI"
@ -81,6 +76,7 @@ install-req:
.PHONY: install-venv
install-venv:
echo "'install-venv' is deprecated and will be removed in a future Pillow release"
virtualenv .
bin/pip install -r requirements.txt
@ -96,7 +92,7 @@ release-test:
python3 -m pytest -qq
check-manifest
pyroma .
viewdoc
$(MAKE) readme
.PHONY: sdist
sdist:
@ -108,4 +104,15 @@ test:
.PHONY: readme
readme:
viewdoc
python3 setup.py --long-description | markdown2 > .long-description.html && open .long-description.html
.PHONY: lint
lint:
tox --help > /dev/null || python3 -m pip install tox
tox -e lint
.PHONY: lint-fix
lint-fix:
black --target-version py36 .
isort .

View File

@ -24,15 +24,6 @@ As of 2019, Pillow development is
<tr>
<th>tests</th>
<td>
<a href="https://travis-ci.org/python-pillow/Pillow"><img
alt="Travis CI build status (Linux)"
src="https://img.shields.io/travis/python-pillow/Pillow/master.svg?label=Linux%20build"></a>
<a href="https://travis-ci.org/python-pillow/pillow-wheels"><img
alt="Travis CI build status (macOS)"
src="https://img.shields.io/travis/python-pillow/pillow-wheels/master.svg?label=macOS%20build"></a>
<a href="https://ci.appveyor.com/project/python-pillow/Pillow"><img
alt="AppVeyor CI build status (Windows)"
src="https://img.shields.io/appveyor/build/python-pillow/Pillow/master.svg?label=Windows%20build"></a>
<a href="https://github.com/python-pillow/Pillow/actions?query=workflow%3ALint"><img
alt="GitHub Actions build status (Lint)"
src="https://github.com/python-pillow/Pillow/workflows/Lint/badge.svg"></a>
@ -45,6 +36,12 @@ As of 2019, Pillow development is
<a href="https://github.com/python-pillow/Pillow/actions?query=workflow%3A%22Test+Docker%22"><img
alt="GitHub Actions build status (Test Docker)"
src="https://github.com/python-pillow/Pillow/workflows/Test%20Docker/badge.svg"></a>
<a href="https://ci.appveyor.com/project/python-pillow/Pillow"><img
alt="AppVeyor CI build status (Windows)"
src="https://img.shields.io/appveyor/build/python-pillow/Pillow/master.svg?label=Windows%20build"></a>
<a href="https://travis-ci.com/github/python-pillow/pillow-wheels"><img
alt="Travis CI build status (macOS)"
src="https://img.shields.io/travis/com/python-pillow/pillow-wheels/master.svg?label=macOS%20build"></a>
<a href="https://codecov.io/gh/python-pillow/Pillow"><img
alt="Code coverage"
src="https://codecov.io/gh/python-pillow/Pillow/branch/master/graph/badge.svg"></a>
@ -96,6 +93,7 @@ The core image library is designed for fast access to data stored in a few basic
- [Contribute](https://github.com/python-pillow/Pillow/blob/master/.github/CONTRIBUTING.md)
- [Issues](https://github.com/python-pillow/Pillow/issues)
- [Pull requests](https://github.com/python-pillow/Pillow/pulls)
- [Release notes](https://pillow.readthedocs.io/en/stable/releasenotes/index.html)
- [Changelog](https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst)
- [Pre-fork](https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst#pre-fork)

View File

@ -1,16 +1,16 @@
# Release Checklist
See https://pillow.readthedocs.io/en/stable/releasenotes/versioning.html for
information about how the version numbers line up with releases.
## Main Release
Released quarterly on January 2nd, April 1st, July 1st and October 15th.
* [ ] Open a release ticket e.g. https://github.com/python-pillow/Pillow/issues/3154
* [ ] Develop and prepare release in `master` branch.
* [ ] Check [GitHub Actions](https://github.com/python-pillow/Pillow/actions),
[Travis CI](https://travis-ci.org/github/python-pillow/Pillow) and
[AppVeyor](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm
passing tests in `master` branch.
* [ ] Check that all of the wheel builds [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels) pass the tests in Travis CI.
* [ ] Check [GitHub Actions](https://github.com/python-pillow/Pillow/actions) and [AppVeyor](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm passing tests in `master` branch.
* [ ] Check that all of the wheel builds [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels) pass the tests in Travis CI and GitHub Actions.
* [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), update version identifier in `src/PIL/_version.py`
* [ ] Update `CHANGES.rst`.
* [ ] Run pre-release check via `make release-test` in a freshly cloned repo.
@ -21,13 +21,18 @@ Released quarterly on January 2nd, April 1st, July 1st and October 15th.
git push --all
git push --tags
```
* [ ] Create source distributions e.g.:
* [ ] Create and check source distribution:
```bash
make sdist
twine check dist/*
```
* [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/master/RELEASING.md#binary-distributions)
* [ ] Upload all binaries and source distributions e.g. `twine upload dist/Pillow-5.2.0*`
* [ ] Create a [new release on GitHub](https://github.com/python-pillow/Pillow/releases/new)
* [ ] Check and upload all binaries and source distributions e.g.:
```bash
twine check dist/*
twine upload dist/Pillow-5.2.0*
```
* [ ] Publish the [release on GitHub](https://github.com/python-pillow/Pillow/releases)
* [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), increment and append `.dev0` to version identifier in `src/PIL/_version.py`
## Point Release
@ -40,14 +45,11 @@ Released as needed for security, installation or critical bug fixes.
```bash
git checkout -t remotes/origin/5.2.x
```
* [ ] Cherry pick individual commits from `master` branch to release branch e.g. `5.2.x`.
* [ ] Cherry pick individual commits from `master` branch to release branch e.g. `5.2.x`, then `git push`.
* [ ] Check [GitHub Actions](https://github.com/python-pillow/Pillow/actions),
[Travis CI](https://travis-ci.org/github/python-pillow/Pillow) and
[AppVeyor](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm
passing tests in release branch e.g. `5.2.x`.
* [ ] Check [GitHub Actions](https://github.com/python-pillow/Pillow/actions) and [AppVeyor](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm passing tests in release branch e.g. `5.2.x`.
* [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), update version identifier in `src/PIL/_version.py`
* [ ] Run pre-release check via `make release-test`.
* [ ] Create tag for release e.g.:
@ -56,13 +58,18 @@ Released as needed for security, installation or critical bug fixes.
git push
git push --tags
```
* [ ] Create source distributions e.g.:
* [ ] Create and check source distribution:
```bash
make sdist
twine check dist/*
```
* [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/master/RELEASING.md#binary-distributions)
* [ ] Upload all binaries and source distributions e.g. `twine upload dist/Pillow-5.2.1*`
* [ ] Create a [new release on GitHub](https://github.com/python-pillow/Pillow/releases/new)
* [ ] Check and upload all binaries and source distributions e.g.:
```bash
twine check dist/*
twine upload dist/Pillow-5.2.1*
```
* [ ] Publish the [release on GitHub](https://github.com/python-pillow/Pillow/releases)
## Embargoed Release
@ -81,18 +88,19 @@ Released as needed privately to individual vendors for critical security-related
git push origin 2.5.x
git push origin --tags
```
* [ ] Create source distributions e.g.:
* [ ] Create and check source distribution:
```bash
make sdist
twine check dist/*
```
* [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/master/RELEASING.md#binary-distributions)
* [ ] Create a [new release on GitHub](https://github.com/python-pillow/Pillow/releases/new)
* [ ] Publish the [release on GitHub](https://github.com/python-pillow/Pillow/releases)
## Binary Distributions
### Windows
* [ ] Contact `@cgohlke` for Windows binaries via release ticket e.g. https://github.com/python-pillow/Pillow/issues/1174.
* [ ] Download and extract tarball from `@cgohlke` and `twine upload *`.
* [ ] Download and extract tarball from `@cgohlke` and copy into `dist/`
### Mac and Linux
* [ ] Use the [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels):
@ -101,7 +109,8 @@ Released as needed privately to individual vendors for critical security-related
cd pillow-wheels
./update-pillow-tag.sh [[release tag]]
```
* [ ] Download wheels from the [Pillow Wheel Builder release](https://github.com/python-pillow/pillow-wheels/releases).
* [ ] Download wheels from the [Pillow Wheel Builder release](https://github.com/python-pillow/pillow-wheels/releases)
and copy into `dist/`
## Publicize Release

View File

@ -4,7 +4,7 @@ from PIL import PyAccess
from .helper import hopper
# Not running this test by default. No DOS against Travis CI.
# Not running this test by default. No DOS against CI.
def iterate_get(size, access):

View File

@ -11,6 +11,8 @@ BungeeColor-Regular_colr_Windows.ttf, from https://github.com/djrrb/bungee
All of the above fonts are published under the SIL Open Font License (OFL) v1.1 (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL), which allows you to copy, modify, and redistribute them if you need to.
FreeMono.ttf is licensed under GPLv3, with the GPL font exception.
OpenSansCondensed-LightItalic.tt, from https://fonts.google.com/specimen/Open+Sans, under Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
DejaVuSans-24-{1,2,4,8}-stripped.ttf are based on DejaVuSans.ttf converted using FontForge to add bitmap strikes and keep only the ASCII range.

View File

@ -270,7 +270,7 @@ def on_github_actions():
def on_ci():
# GitHub Actions, Travis and AppVeyor have "CI"
# GitHub Actions and AppVeyor have "CI"
return "CI" in os.environ
@ -278,6 +278,12 @@ def is_big_endian():
return sys.byteorder == "big"
def is_ppc64le():
import platform
return platform.machine() == "ppc64le"
def is_win32():
return sys.platform.startswith("win32")

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Binary file not shown.

View File

@ -11,7 +11,7 @@ ORIGINAL_LIMIT = Image.MAX_IMAGE_PIXELS
class TestDecompressionBomb:
@classmethod
def teardown_class(self):
def teardown_class(cls):
Image.MAX_IMAGE_PIXELS = ORIGINAL_LIMIT
def test_no_warning_small_file(self):

View File

@ -105,6 +105,31 @@ def test_apng_dispose_region():
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
def test_apng_dispose_op_previous_frame():
# Test that the dispose settings being used are from the previous frame
#
# Image created with:
# red = Image.new("RGBA", (128, 64), (255, 0, 0, 255))
# green = red.copy()
# green.paste(Image.new("RGBA", (64, 32), (0, 255, 0, 255)))
# blue = red.copy()
# blue.paste(Image.new("RGBA", (64, 32), (0, 255, 0, 255)), (64, 32))
#
# red.save(
# "Tests/images/apng/dispose_op_previous_frame.png",
# save_all=True,
# append_images=[green, blue],
# disposal=[
# PngImagePlugin.APNG_DISPOSE_OP_NONE,
# PngImagePlugin.APNG_DISPOSE_OP_PREVIOUS,
# PngImagePlugin.APNG_DISPOSE_OP_PREVIOUS
# ],
# )
with Image.open("Tests/images/apng/dispose_op_previous_frame.png") as im:
im.seek(im.n_frames - 1)
assert im.getpixel((0, 0)) == (255, 0, 0, 255)
def test_apng_dispose_op_background_p_mode():
with Image.open("Tests/images/apng/dispose_op_background_p_mode.png") as im:
im.seek(1)

View File

@ -74,10 +74,10 @@ def test_optimize():
im.save(test_file, "GIF", optimize=optimize)
return len(test_file.getvalue())
assert test_grayscale(0) == 800
assert test_grayscale(1) == 44
assert test_bilevel(0) == 800
assert test_bilevel(1) == 800
assert test_grayscale(0) == 799
assert test_grayscale(1) == 43
assert test_bilevel(0) == 799
assert test_bilevel(1) == 799
def test_optimize_correctness():
@ -307,6 +307,20 @@ def test_dispose_none():
pass
def test_dispose_none_load_end():
# Test image created with:
#
# im = Image.open("transparent.gif")
# im_rotated = im.rotate(180)
# im.save("dispose_none_load_end.gif",
# save_all=True, append_images=[im_rotated], disposal=[1,2])
with Image.open("Tests/images/dispose_none_load_end.gif") as img:
img.seek(1)
with Image.open("Tests/images/dispose_none_load_end_second.gif") as expected:
assert_image_equal(img, expected)
def test_dispose_background():
with Image.open("Tests/images/dispose_bgnd.gif") as img:
try:

View File

@ -86,6 +86,20 @@ def test_only_save_relevant_sizes(tmp_path):
assert im_saved.info["sizes"] == {(16, 16), (24, 24), (32, 32), (48, 48)}
def test_save_append_images(tmp_path):
# append_images should be used for scaled down versions of the image
im = hopper("RGBA")
provided_im = Image.new("RGBA", (32, 32), (255, 0, 0))
outfile = str(tmp_path / "temp_saved_multi_icon.ico")
im.save(outfile, sizes=[(32, 32), (128, 128)], append_images=[provided_im])
with Image.open(outfile) as reread:
assert_image_equal(reread, hopper("RGBA"))
reread.size = (32, 32)
assert_image_equal(reread, provided_im)
def test_unexpected_size():
# This image has been manually hexedited to state that it is 16x32
# while the image within is still 16x16

View File

@ -16,6 +16,7 @@ from .helper import (
assert_image_similar,
assert_image_similar_tofile,
hopper,
is_big_endian,
skip_unless_feature,
)
@ -813,11 +814,13 @@ class TestFileLibTiff(LibTiffTestCase):
with Image.open(infile) as im:
assert_image_similar_tofile(im, "Tests/images/pil_sample_cmyk.jpg", 0.5)
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
def test_strip_ycbcr_jpeg_2x2_sampling(self):
infile = "Tests/images/tiff_strip_ycbcr_jpeg_2x2_sampling.tif"
with Image.open(infile) as im:
assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5)
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
def test_strip_ycbcr_jpeg_1x1_sampling(self):
infile = "Tests/images/tiff_strip_ycbcr_jpeg_1x1_sampling.tif"
with Image.open(infile) as im:
@ -828,16 +831,19 @@ class TestFileLibTiff(LibTiffTestCase):
with Image.open(infile) as im:
assert_image_similar_tofile(im, "Tests/images/pil_sample_cmyk.jpg", 0.5)
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
def test_tiled_ycbcr_jpeg_1x1_sampling(self):
infile = "Tests/images/tiff_tiled_ycbcr_jpeg_1x1_sampling.tif"
with Image.open(infile) as im:
assert_image_equal_tofile(im, "Tests/images/flower2.jpg")
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
def test_tiled_ycbcr_jpeg_2x2_sampling(self):
infile = "Tests/images/tiff_tiled_ycbcr_jpeg_2x2_sampling.tif"
with Image.open(infile) as im:
assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5)
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
def test_old_style_jpeg(self):
infile = "Tests/images/old-style-jpeg-compression.tif"
with Image.open(infile) as im:

View File

@ -537,6 +537,12 @@ class TestFilePng:
assert repr_png.format == "PNG"
assert_image_equal(im, repr_png)
def test_repr_png_error(self):
im = hopper("F")
with pytest.raises(ValueError):
im._repr_png_()
def test_chunk_order(self, tmp_path):
with Image.open("Tests/images/icc_profile.png") as im:
test_file = str(tmp_path / "temp.png")

View File

@ -4,7 +4,7 @@ from io import BytesIO
import pytest
from PIL import Image, TiffImagePlugin
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
from PIL.TiffImagePlugin import RESOLUTION_UNIT, SUBIFD, X_RESOLUTION, Y_RESOLUTION
from .helper import (
assert_image_equal,
@ -161,6 +161,14 @@ class TestFileTiff:
reloaded.load()
assert (round(dpi), round(dpi)) == reloaded.info["dpi"]
def test_subifd(self, tmp_path):
outfile = str(tmp_path / "temp.tif")
with Image.open("Tests/images/g4_orientation_6.tif") as im:
im.tag_v2[SUBIFD] = 10000
# Should not segfault
im.save(outfile)
def test_save_setting_missing_resolution(self):
b = BytesIO()
Image.open("Tests/images/10ct_32bit_128.tiff").save(

View File

@ -775,26 +775,34 @@ class TestImage:
with pytest.warns(DeprecationWarning):
assert test_module.PILLOW_VERSION > "7.0.0"
def test_overrun(self):
"""For overrun completeness, test as:
valgrind pytest -qq Tests/test_image.py::TestImage::test_overrun | grep decode.c
"""
for file in [
@pytest.mark.parametrize(
"path",
[
"fli_overrun.bin",
"sgi_overrun.bin",
"sgi_overrun_expandrow.bin",
"sgi_overrun_expandrow2.bin",
"pcx_overrun.bin",
"pcx_overrun2.bin",
"ossfuzz-4836216264589312.pcx",
"01r_00.pcx",
]:
with Image.open(os.path.join("Tests/images", file)) as im:
try:
im.load()
assert False
except OSError as e:
assert str(e) == "buffer overrun when reading image file"
],
)
def test_overrun(self, path):
"""For overrun completeness, test as:
valgrind pytest -qq Tests/test_image.py::TestImage::test_overrun | grep decode.c
"""
with Image.open(os.path.join("Tests/images", path)) as im:
try:
im.load()
assert False
except OSError as e:
buffer_overrun = str(e) == "buffer overrun when reading image file"
truncated = "image file is truncated" in str(e)
assert buffer_overrun or truncated
def test_fli_overrun2(self):
with Image.open("Tests/images/fli_overrun2.bin") as im:
try:
im.seek(1)

View File

@ -1,8 +1,8 @@
import pytest
from PIL import ImagePalette
from PIL import Image, ImagePalette
from .helper import hopper
from .helper import assert_image_equal, hopper
def test_putpalette():
@ -39,3 +39,20 @@ def test_imagepalette():
im.putpalette(ImagePalette.random())
im.putpalette(ImagePalette.sepia())
im.putpalette(ImagePalette.wedge())
def test_putpalette_with_alpha_values():
with Image.open("Tests/images/transparent.gif") as im:
expected = im.convert("RGBA")
palette = im.getpalette()
transparency = im.info.pop("transparency")
palette_with_alpha_values = []
for i in range(256):
color = palette[i * 3 : i * 3 + 3]
alpha = 0 if i == transparency else 255
palette_with_alpha_values += color + [alpha]
im.putpalette(palette_with_alpha_values, "RGBA")
assert_image_equal(im.convert("RGBA"), expected)

View File

@ -2,7 +2,7 @@ import pytest
from PIL import Image
from .helper import assert_image, assert_image_similar, hopper
from .helper import assert_image, assert_image_similar, hopper, is_ppc64le
def test_sanity():
@ -17,11 +17,12 @@ def test_sanity():
assert_image_similar(converted.convert("RGB"), image, 60)
@pytest.mark.xfail(is_ppc64le(), reason="failing on ppc64le on GHA")
def test_libimagequant_quantize():
image = hopper()
try:
converted = image.quantize(100, Image.LIBIMAGEQUANT)
except ValueError as ex:
except ValueError as ex: # pragma: no cover
if "dependency" in str(ex).lower():
pytest.skip("libimagequant support not available")
else:

View File

@ -32,58 +32,6 @@ pytestmark = skip_unless_feature("freetype2")
class TestImageFont:
LAYOUT_ENGINE = ImageFont.LAYOUT_BASIC
# Freetype has different metrics depending on the version.
# (and, other things, but first things first)
METRICS = {
(">=2.3", "<2.4"): {
"multiline": 30,
"textsize": 12,
"getters": (13, 16),
"mask": (107, 13),
"multiline-anchor": 6,
"getlength": (36, 27, 27, 33),
},
(">=2.7",): {
"multiline": 6.2,
"textsize": 2.5,
"getters": (12, 16),
"mask": (108, 13),
"multiline-anchor": 4,
"getlength": (36, 21, 24, 33),
},
"Default": {
"multiline": 0.5,
"textsize": 0.5,
"getters": (12, 16),
"mask": (108, 13),
"multiline-anchor": 4,
"getlength": (36, 24, 24, 33),
},
}
@classmethod
def setup_class(self):
freetype = parse_version(features.version_module("freetype2"))
self.metrics = self.METRICS["Default"]
for conditions, metrics in self.METRICS.items():
if not isinstance(conditions, tuple):
continue
for condition in conditions:
version = parse_version(re.sub("[<=>]", "", condition))
if (condition.startswith(">=") and freetype >= version) or (
condition.startswith("<") and freetype < version
):
# Condition was met
continue
# Condition failed
break
else:
# All conditions were met
self.metrics = metrics
def get_font(self):
return ImageFont.truetype(
FONT_PATH, FONT_SIZE, layout_engine=self.LAYOUT_ENGINE
@ -199,24 +147,24 @@ class TestImageFont:
with Image.open(target) as target_img:
# Epsilon ~.5 fails with FreeType 2.7
assert_image_similar(im, target_img, self.metrics["textsize"])
assert_image_similar(im, target_img, 2.5)
@pytest.mark.parametrize(
"text, mode, font, size, length_basic_index, length_raqm",
"text, mode, font, size, length_basic, length_raqm",
(
# basic test
("text", "L", "FreeMono.ttf", 15, 0, 36),
("text", "1", "FreeMono.ttf", 15, 0, 36),
("text", "L", "FreeMono.ttf", 15, 36, 36),
("text", "1", "FreeMono.ttf", 15, 36, 36),
# issue 4177
("rrr", "L", "DejaVuSans.ttf", 18, 1, 22.21875),
("rrr", "1", "DejaVuSans.ttf", 18, 2, 22.21875),
("rrr", "L", "DejaVuSans.ttf", 18, 21, 22.21875),
("rrr", "1", "DejaVuSans.ttf", 18, 24, 22.21875),
# test 'l' not including extra margin
# using exact value 2047 / 64 for raqm, checked with debugger
("ill", "L", "OpenSansCondensed-LightItalic.ttf", 63, 3, 31.984375),
("ill", "1", "OpenSansCondensed-LightItalic.ttf", 63, 3, 31.984375),
("ill", "L", "OpenSansCondensed-LightItalic.ttf", 63, 33, 31.984375),
("ill", "1", "OpenSansCondensed-LightItalic.ttf", 63, 33, 31.984375),
),
)
def test_getlength(self, text, mode, font, size, length_basic_index, length_raqm):
def test_getlength(self, text, mode, font, size, length_basic, length_raqm):
f = ImageFont.truetype(
"Tests/fonts/" + font, size, layout_engine=self.LAYOUT_ENGINE
)
@ -226,7 +174,7 @@ class TestImageFont:
if self.LAYOUT_ENGINE == ImageFont.LAYOUT_BASIC:
length = d.textlength(text, f)
assert length == self.metrics["getlength"][length_basic_index]
assert length == length_basic
else:
# disable kerning, kerning metrics changed
length = d.textlength(text, f, features=["-kern"])
@ -249,7 +197,7 @@ class TestImageFont:
# some versions of freetype have different horizontal spacing.
# setting a tight epsilon, I'm showing the original test failure
# at epsilon = ~38.
assert_image_similar(im, target_img, self.metrics["multiline"])
assert_image_similar(im, target_img, 6.2)
def test_render_multiline_text(self):
ttf = self.get_font()
@ -264,7 +212,7 @@ class TestImageFont:
with Image.open(target) as target_img:
# Epsilon ~.5 fails with FreeType 2.7
assert_image_similar(im, target_img, self.metrics["multiline"])
assert_image_similar(im, target_img, 6.2)
# Test that text() can pass on additional arguments
# to multiline_text()
@ -283,7 +231,7 @@ class TestImageFont:
with Image.open(target) as target_img:
# Epsilon ~.5 fails with FreeType 2.7
assert_image_similar(im, target_img, self.metrics["multiline"])
assert_image_similar(im, target_img, 6.2)
def test_unknown_align(self):
im = Image.new(mode="RGB", size=(300, 100))
@ -341,7 +289,7 @@ class TestImageFont:
with Image.open(target) as target_img:
# Epsilon ~.5 fails with FreeType 2.7
assert_image_similar(im, target_img, self.metrics["multiline"])
assert_image_similar(im, target_img, 6.2)
def test_rotated_transposed_font(self):
img_grey = Image.new("L", (100, 100))
@ -395,7 +343,7 @@ class TestImageFont:
mask = transposed_font.getmask(text)
# Assert
assert mask.size == self.metrics["mask"][::-1]
assert mask.size == (13, 108)
def test_unrotated_transposed_font_get_mask(self):
# Arrange
@ -408,7 +356,7 @@ class TestImageFont:
mask = transposed_font.getmask(text)
# Assert
assert mask.size == self.metrics["mask"]
assert mask.size == (108, 13)
def test_free_type_font_get_name(self):
# Arrange
@ -452,7 +400,7 @@ class TestImageFont:
mask = font.getmask(text)
# Assert
assert mask.size == self.metrics["mask"]
assert mask.size == (108, 13)
def test_load_path_not_found(self):
# Arrange
@ -633,7 +581,7 @@ class TestImageFont:
assert t.font.glyphs == 4177
assert t.getsize("A") == (12, 16)
assert t.getsize("AB") == (24, 16)
assert t.getsize("M") == self.metrics["getters"]
assert t.getsize("M") == (12, 16)
assert t.getsize("y") == (12, 20)
assert t.getsize("a") == (12, 16)
assert t.getsize_multiline("A") == (12, 16)
@ -869,7 +817,7 @@ class TestImageFont:
)
with Image.open(target) as expected:
assert_image_similar(im, expected, self.metrics["multiline-anchor"])
assert_image_similar(im, expected, 4)
def test_anchor_invalid(self):
font = self.get_font()
@ -928,7 +876,7 @@ class TestImageFont:
d.text((10, 10), txt, font=ttf, fill="#fa6", embedded_color=True)
with Image.open("Tests/images/standard_embedded.png") as expected:
assert_image_similar(im, expected, max(self.metrics["multiline"], 3))
assert_image_similar(im, expected, 6.2)
@skip_unless_feature_version("freetype2", "2.5.0")
def test_cbdt(self):
@ -945,7 +893,7 @@ class TestImageFont:
d.text((10, 10), "\U0001f469", embedded_color=True, font=font)
with Image.open("Tests/images/cbdt_notocoloremoji.png") as expected:
assert_image_similar(im, expected, self.metrics["multiline"])
assert_image_similar(im, expected, 6.2)
except IOError as e:
assert str(e) in ("unimplemented feature", "unknown file format")
pytest.skip("freetype compiled without libpng or unsupported")
@ -965,7 +913,7 @@ class TestImageFont:
d.text((10, 10), "\U0001f469", "black", font=font)
with Image.open("Tests/images/cbdt_notocoloremoji_mask.png") as expected:
assert_image_similar(im, expected, self.metrics["multiline"])
assert_image_similar(im, expected, 6.2)
except IOError as e:
assert str(e) in ("unimplemented feature", "unknown file format")
pytest.skip("freetype compiled without libpng or unsupported")
@ -1020,3 +968,15 @@ def test_render_mono_size():
draw.text((10, 10), "r" * 10, "black", ttf)
assert_image_equal_tofile(im, "Tests/images/text_mono.gif")
def test_freetype_deprecation(monkeypatch):
# Arrange: mock features.version_module to return fake FreeType version
def fake_version_module(module):
return "2.7"
monkeypatch.setattr(features, "version_module", fake_version_module)
# Act / Assert
with pytest.warns(DeprecationWarning):
ImageFont.truetype(FONT_PATH, FONT_SIZE)

View File

@ -1,4 +1,5 @@
import array
import math
import struct
import pytest
@ -6,75 +7,175 @@ import pytest
from PIL import Image, ImagePath
class TestImagePath:
def test_path(self):
def test_path():
p = ImagePath.Path(list(range(10)))
p = ImagePath.Path(list(range(10)))
# sequence interface
assert len(p) == 5
assert p[0] == (0.0, 1.0)
assert p[-1] == (8.0, 9.0)
assert list(p[:1]) == [(0.0, 1.0)]
with pytest.raises(TypeError) as cm:
p["foo"]
assert str(cm.value) == "Path indices must be integers, not str"
assert list(p) == [(0.0, 1.0), (2.0, 3.0), (4.0, 5.0), (6.0, 7.0), (8.0, 9.0)]
# sequence interface
assert len(p) == 5
assert p[0] == (0.0, 1.0)
assert p[-1] == (8.0, 9.0)
assert list(p[:1]) == [(0.0, 1.0)]
with pytest.raises(TypeError) as cm:
p["foo"]
assert str(cm.value) == "Path indices must be integers, not str"
assert list(p) == [(0.0, 1.0), (2.0, 3.0), (4.0, 5.0), (6.0, 7.0), (8.0, 9.0)]
# method sanity check
assert p.tolist() == [
(0.0, 1.0),
(2.0, 3.0),
(4.0, 5.0),
(6.0, 7.0),
(8.0, 9.0),
]
assert p.tolist(1) == [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
# method sanity check
assert p.tolist() == [
(0.0, 1.0),
(2.0, 3.0),
(4.0, 5.0),
(6.0, 7.0),
(8.0, 9.0),
]
assert p.tolist(1) == [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
assert p.getbbox() == (0.0, 1.0, 8.0, 9.0)
assert p.getbbox() == (0.0, 1.0, 8.0, 9.0)
assert p.compact(5) == 2
assert list(p) == [(0.0, 1.0), (4.0, 5.0), (8.0, 9.0)]
assert p.compact(5) == 2
assert list(p) == [(0.0, 1.0), (4.0, 5.0), (8.0, 9.0)]
p.transform((1, 0, 1, 0, 1, 1))
assert list(p) == [(1.0, 2.0), (5.0, 6.0), (9.0, 10.0)]
p.transform((1, 0, 1, 0, 1, 1))
assert list(p) == [(1.0, 2.0), (5.0, 6.0), (9.0, 10.0)]
# alternative constructors
p = ImagePath.Path([0, 1])
assert list(p) == [(0.0, 1.0)]
p = ImagePath.Path([0.0, 1.0])
assert list(p) == [(0.0, 1.0)]
p = ImagePath.Path([0, 1])
assert list(p) == [(0.0, 1.0)]
p = ImagePath.Path([(0, 1)])
assert list(p) == [(0.0, 1.0)]
p = ImagePath.Path(p)
assert list(p) == [(0.0, 1.0)]
p = ImagePath.Path(p.tolist(0))
assert list(p) == [(0.0, 1.0)]
p = ImagePath.Path(p.tolist(1))
assert list(p) == [(0.0, 1.0)]
p = ImagePath.Path(array.array("f", [0, 1]))
assert list(p) == [(0.0, 1.0)]
# alternative constructors
p = ImagePath.Path([0, 1])
assert list(p) == [(0.0, 1.0)]
p = ImagePath.Path([0.0, 1.0])
assert list(p) == [(0.0, 1.0)]
p = ImagePath.Path([0, 1])
assert list(p) == [(0.0, 1.0)]
p = ImagePath.Path([(0, 1)])
assert list(p) == [(0.0, 1.0)]
p = ImagePath.Path(p)
assert list(p) == [(0.0, 1.0)]
p = ImagePath.Path(p.tolist(0))
assert list(p) == [(0.0, 1.0)]
p = ImagePath.Path(p.tolist(1))
assert list(p) == [(0.0, 1.0)]
p = ImagePath.Path(array.array("f", [0, 1]))
assert list(p) == [(0.0, 1.0)]
arr = array.array("f", [0, 1])
if hasattr(arr, "tobytes"):
p = ImagePath.Path(arr.tobytes())
else:
p = ImagePath.Path(arr.tostring())
assert list(p) == [(0.0, 1.0)]
arr = array.array("f", [0, 1])
if hasattr(arr, "tobytes"):
p = ImagePath.Path(arr.tobytes())
else:
p = ImagePath.Path(arr.tostring())
assert list(p) == [(0.0, 1.0)]
def test_overflow_segfault(self):
# Some Pythons fail getting the argument as an integer, and it falls
# through to the sequence. Seeing this on 32-bit Windows.
with pytest.raises((TypeError, MemoryError)):
# post patch, this fails with a memory error
x = evil()
# This fails due to the invalid malloc above,
# and segfaults
for i in range(200000):
x[i] = b"0" * 16
def test_invalid_coords():
# Arrange
coords = ["a", "b"]
# Act / Assert
with pytest.raises(SystemError):
ImagePath.Path(coords)
def test_path_odd_number_of_coordinates():
# Arrange
coords = [0]
# Act / Assert
with pytest.raises(ValueError) as e:
ImagePath.Path(coords)
assert str(e.value) == "wrong number of coordinates"
@pytest.mark.parametrize(
"coords, expected",
[
([0, 1, 2, 3], (0.0, 1.0, 2.0, 3.0)),
([3, 2, 1, 0], (1.0, 0.0, 3.0, 2.0)),
],
)
def test_getbbox(coords, expected):
# Arrange
p = ImagePath.Path(coords)
# Act / Assert
assert p.getbbox() == expected
def test_getbbox_no_args():
# Arrange
p = ImagePath.Path([0, 1, 2, 3])
# Act / Assert
with pytest.raises(TypeError):
p.getbbox(1)
@pytest.mark.parametrize(
"coords, expected",
[
(0, []),
(list(range(6)), [(0.0, 3.0), (4.0, 9.0), (8.0, 15.0)]),
],
)
def test_map(coords, expected):
# Arrange
p = ImagePath.Path(coords)
# Act
# Modifies the path in-place
p.map(lambda x, y: (x * 2, y * 3))
# Assert
assert list(p) == expected
def test_transform():
# Arrange
p = ImagePath.Path([0, 1, 2, 3])
theta = math.pi / 15
# Act
# Affine transform, in-place
p.transform(
(math.cos(theta), math.sin(theta), 20, -math.sin(theta), math.cos(theta), 20),
)
# Assert
assert p.tolist() == [
(20.20791169081776, 20.978147600733806),
(22.58003027392089, 22.518619420565898),
]
def test_transform_with_wrap():
# Arrange
p = ImagePath.Path([0, 1, 2, 3])
theta = math.pi / 15
# Act
# Affine transform, in-place, with wrap parameter
p.transform(
(math.cos(theta), math.sin(theta), 20, -math.sin(theta), math.cos(theta), 20),
1.0,
)
# Assert
assert p.tolist() == [
(0.20791169081775962, 20.978147600733806),
(0.5800302739208902, 22.518619420565898),
]
def test_overflow_segfault():
# Some Pythons fail getting the argument as an integer, and it falls
# through to the sequence. Seeing this on 32-bit Windows.
with pytest.raises((TypeError, MemoryError)):
# post patch, this fails with a memory error
x = evil()
# This fails due to the invalid malloc above,
# and segfaults
for i in range(200000):
x[i] = b"0" * 16
class evil:

View File

@ -8,34 +8,15 @@ if ImageQt.qt_is_installed:
from PIL.ImageQt import qRgba
@pytest.mark.skipif(not ImageQt.qt_is_installed, reason="Qt bindings are not installed")
class PillowQPixmapTestCase:
@classmethod
def setup_class(self):
try:
if ImageQt.qt_version == "5":
from PyQt5.QtGui import QGuiApplication
elif ImageQt.qt_version == "side2":
from PySide2.QtGui import QGuiApplication
except ImportError:
pytest.skip("QGuiApplication not installed")
return
self.app = QGuiApplication([])
@classmethod
def teardown_class(self):
self.app.quit()
self.app = None
@pytest.mark.skipif(not ImageQt.qt_is_installed, reason="Qt bindings are not installed")
def test_rgb():
# from https://doc.qt.io/archives/qt-4.8/qcolor.html
# typedef QRgb
# An ARGB quadruplet on the format #AARRGGBB,
# equivalent to an unsigned int.
if ImageQt.qt_version == "5":
if ImageQt.qt_version == "side6":
from PySide6.QtGui import qRgb
elif ImageQt.qt_version == "5":
from PyQt5.QtGui import qRgb
elif ImageQt.qt_version == "side2":
from PySide2.QtGui import qRgb

View File

@ -1,15 +0,0 @@
from PIL import ImageQt
from .helper import assert_image_equal, hopper
from .test_imageqt import PillowQPixmapTestCase
class TestFromQPixmap(PillowQPixmapTestCase):
def roundtrip(self, expected):
result = ImageQt.fromqpixmap(ImageQt.toqpixmap(expected))
# Qt saves all pixmaps as rgb
assert_image_equal(result, expected.convert("RGB"))
def test_sanity(self):
for mode in ("1", "RGB", "RGBA", "L", "P"):
self.roundtrip(hopper(mode))

View File

@ -0,0 +1,63 @@
import pytest
from PIL import ImageQt
from .helper import assert_image_equal, hopper
if ImageQt.qt_is_installed:
from PIL.ImageQt import QPixmap
if ImageQt.qt_version == "side6":
from PySide6.QtWidgets import QApplication, QHBoxLayout, QLabel, QWidget
elif ImageQt.qt_version == "5":
from PyQt5.QtWidgets import QApplication, QHBoxLayout, QLabel, QWidget
elif ImageQt.qt_version == "side2":
from PySide2.QtWidgets import QApplication, QHBoxLayout, QLabel, QWidget
class Example(QWidget):
def __init__(self):
super().__init__()
img = hopper().resize((1000, 1000))
qimage = ImageQt.ImageQt(img)
pixmap1 = ImageQt.QPixmap.fromImage(qimage)
QHBoxLayout(self) # hbox
lbl = QLabel(self)
# Segfault in the problem
lbl.setPixmap(pixmap1.copy())
def roundtrip(expected):
result = ImageQt.fromqpixmap(ImageQt.toqpixmap(expected))
# Qt saves all pixmaps as rgb
assert_image_equal(result, expected.convert("RGB"))
@pytest.mark.skipif(not ImageQt.qt_is_installed, reason="Qt bindings are not installed")
def test_sanity(tmp_path):
# Segfault test
app = QApplication([])
ex = Example()
assert app # Silence warning
assert ex # Silence warning
for mode in ("1", "RGB", "RGBA", "L", "P"):
# to QPixmap
data = ImageQt.toqpixmap(hopper(mode))
assert isinstance(data, QPixmap)
assert not data.isNull()
# Test saving the file
tempfile = str(tmp_path / f"temp_{mode}.png")
data.save(tempfile)
# from QPixmap
roundtrip(hopper(mode))
app.quit()
app = None

View File

@ -11,13 +11,6 @@ pytestmark = pytest.mark.skipif(
if ImageQt.qt_is_installed:
from PIL.ImageQt import QImage
try:
from PyQt5 import QtGui
from PyQt5.QtWidgets import QApplication, QHBoxLayout, QLabel, QWidget
except (ImportError, RuntimeError):
from PySide2 import QtGui
from PySide2.QtWidgets import QApplication, QHBoxLayout, QLabel, QWidget
def test_sanity(tmp_path):
for mode in ("RGB", "RGBA", "L", "P", "1"):
@ -49,29 +42,3 @@ def test_sanity(tmp_path):
# Check that it actually worked.
with Image.open(tempfile) as reloaded:
assert_image_equal(reloaded, src)
def test_segfault():
app = QApplication([])
ex = Example()
assert app # Silence warning
assert ex # Silence warning
if ImageQt.qt_is_installed:
class Example(QWidget):
def __init__(self):
super().__init__()
img = hopper().resize((1000, 1000))
qimage = ImageQt.ImageQt(img)
pixmap1 = QtGui.QPixmap.fromImage(qimage)
QHBoxLayout(self) # hbox
lbl = QLabel(self)
# Segfault in the problem
lbl.setPixmap(pixmap1.copy())

View File

@ -1,20 +0,0 @@
from PIL import ImageQt
from .helper import hopper
from .test_imageqt import PillowQPixmapTestCase
if ImageQt.qt_is_installed:
from PIL.ImageQt import QPixmap
class TestToQPixmap(PillowQPixmapTestCase):
def test_sanity(self, tmp_path):
for mode in ("1", "RGB", "RGBA", "L", "P"):
data = ImageQt.toqpixmap(hopper(mode))
assert isinstance(data, QPixmap)
assert not data.isNull()
# Test saving the file
tempfile = str(tmp_path / f"temp_{mode}.png")
data.save(tempfile)

View File

@ -6,7 +6,12 @@ from PIL import Image
@pytest.mark.parametrize(
"test_file",
["Tests/images/sgi_overrun_expandrowF04.bin", "Tests/images/sgi_crash.bin"],
[
"Tests/images/sgi_overrun_expandrowF04.bin",
"Tests/images/sgi_crash.bin",
"Tests/images/crash-6b7f2244da6d0ae297ee0754a424213444e92778.sgi",
"Tests/images/ossfuzz-5730089102868480.sgi",
],
)
def test_crashes(test_file):
with open(test_file, "rb") as f:

View File

@ -19,7 +19,12 @@ from .helper import on_ci
@pytest.mark.parametrize(
"test_file", ["Tests/images/crash_1.tif", "Tests/images/crash_2.tif"]
"test_file",
[
"Tests/images/crash_1.tif",
"Tests/images/crash_2.tif",
"Tests/images/crash-2020-10-test.tif",
],
)
@pytest.mark.filterwarnings("ignore:Possibly corrupt EXIF data")
@pytest.mark.filterwarnings("ignore:Metadata warning")

View File

@ -3,7 +3,7 @@ Depends
``install_openjpeg.sh``, ``install_webp.sh``, ``install_imagequant.sh``,
``install_raqm.sh`` and ``install_raqm_cmake.sh`` can be used to download,
build & install non-packaged dependencies; useful for testing with Travis CI.
build & install non-packaged dependencies; useful for testing on CI.
``install_extra_test_images.sh`` can be used to install additional test images
that are used for Travis CI and AppVeyor.
that are used by CI.

View File

@ -1,8 +0,0 @@
#!/usr/bin/env bash
# Fetch the remote master branch before running diff-cover on Travis CI.
# https://github.com/Bachmann1234/diff-cover#troubleshooting
git fetch origin master:refs/remotes/origin/master
# CFLAGS=-O0 means build with no optimisation.
# Makes build much quicker for lxml and other dependencies.
time CFLAGS=-O0 pip install diff_cover

View File

@ -1,5 +0,0 @@
#!/usr/bin/env bash
coverage xml
diff-cover coverage.xml
diff-quality --violation=pyflakes
diff-quality --violation=pycodestyle

View File

@ -1,7 +1,7 @@
#!/bin/bash
# install libimagequant
archive=libimagequant-2.13.0
archive=libimagequant-2.13.1
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/master/$archive.tar.gz

View File

@ -1,7 +1,7 @@
#!/bin/bash
# install openjpeg
archive=openjpeg-2.3.1
archive=openjpeg-2.4.0
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/master/$archive.tar.gz

View File

@ -2,7 +2,7 @@
# install raqm
archive=raqm-0.7.0
archive=raqm-0.7.1
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/master/$archive.tar.gz

View File

@ -5,7 +5,7 @@ The Python Imaging Library (PIL) is
Pillow is the friendly PIL fork. It is
Copyright © 2010-2020 by Alex Clark and contributors
Copyright © 2010-2021 by Alex Clark and contributors
Like PIL, Pillow is licensed under the open source PIL
Software License:

View File

@ -156,4 +156,4 @@ livehtml: html
livereload $(BUILDDIR)/html -p 33233
serve:
cd $(BUILDDIR)/html; python -m SimpleHTTPServer
cd $(BUILDDIR)/html; python3 -m http.server

View File

@ -6,20 +6,20 @@ Goals
The fork author's goal is to foster and support active development of PIL through:
- Continuous integration testing via `Travis CI`_, `AppVeyor`_ and `GitHub Actions`_
- Continuous integration testing via `GitHub Actions`_, `AppVeyor`_ and `Travis CI`_
- Publicized development activity on `GitHub`_
- Regular releases to the `Python Package Index`_
.. _Travis CI: https://travis-ci.org/python-pillow/Pillow
.. _AppVeyor: https://ci.appveyor.com/project/Python-pillow/pillow
.. _GitHub Actions: https://github.com/python-pillow/Pillow/actions
.. _AppVeyor: https://ci.appveyor.com/project/Python-pillow/pillow
.. _Travis CI: https://travis-ci.com/github/python-pillow/pillow-wheels
.. _GitHub: https://github.com/python-pillow/Pillow
.. _Python Package Index: https://pypi.org/project/Pillow/
License
-------
Like PIL, Pillow is `licensed under the open source PIL Software License <https://raw.githubusercontent.com/python-pillow/Pillow/master/LICENSE>`_
Like PIL, Pillow is `licensed under the open source HPND License <https://raw.githubusercontent.com/python-pillow/Pillow/master/LICENSE>`_
Why a fork?
-----------

View File

@ -32,6 +32,7 @@ extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.intersphinx",
"sphinx.ext.viewcode",
"sphinx_issues",
"sphinx_removed_in",
]
@ -50,7 +51,7 @@ master_doc = "index"
# General information about the project.
project = "Pillow (PIL Fork)"
copyright = "1995-2011 Fredrik Lundh, 2010-2020 Alex Clark and Contributors"
copyright = "1995-2011 Fredrik Lundh, 2010-2021 Alex Clark and Contributors"
author = "Fredrik Lundh, Alex Clark and Contributors"
# The version info for the project you're documenting, acts as replacement for
@ -143,7 +144,7 @@ html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
# html_logo = None
html_logo = "resources/pillow-logo.png"
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
@ -310,3 +311,4 @@ texinfo_documents = [
def setup(app):
app.add_js_file("js/script.js")
app.add_css_file("css/dark.css")
app.add_css_file("css/light.css")

View File

@ -12,12 +12,25 @@ Deprecated features
Below are features which are considered deprecated. Where appropriate,
a ``DeprecationWarning`` is issued.
FreeType 2.7
~~~~~~~~~~~~
.. deprecated:: 8.1.0
Support for FreeType 2.7 is deprecated and will be removed in Pillow 9.0.0 (2022-01-02),
when FreeType 2.8 will be the minimum supported.
We recommend upgrading to at least FreeType `2.10.4`_, which fixed a severe
vulnerability introduced in FreeType 2.6 (:cve:`CVE-2020-15999`).
.. _2.10.4: https://sourceforge.net/projects/freetype/files/freetype2/2.10.4/
Image.show command parameter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.2.0
The ``command`` parameter was deprecated and will be removed in a future release.
The ``command`` parameter will be removed in Pillow 9.0.0 (2022-01-02).
Use a subclass of :py:class:`.ImageShow.Viewer` instead.
Image._showxv
@ -25,26 +38,26 @@ Image._showxv
.. deprecated:: 7.2.0
``Image._showxv`` has been deprecated. Use :py:meth:`.Image.Image.show`
instead. If custom behaviour is required, use :py:func:`.ImageShow.register` to add
a custom :py:class:`.ImageShow.Viewer` class.
``Image._showxv`` will be removed in Pillow 9.0.0 (2022-01-02).
Use :py:meth:`.Image.Image.show` instead. If custom behaviour is required, use
:py:func:`.ImageShow.register` to add a custom :py:class:`.ImageShow.Viewer` class.
ImageFile.raise_ioerror
~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.2.0
``IOError`` was merged into ``OSError`` in Python 3.3. So, ``ImageFile.raise_ioerror``
is now deprecated and will be removed in a future release. Use
``ImageFile.raise_oserror`` instead.
``IOError`` was merged into ``OSError`` in Python 3.3.
So, ``ImageFile.raise_ioerror`` will be removed in Pillow 9.0.0 (2022-01-02).
Use ``ImageFile.raise_oserror`` instead.
PILLOW_VERSION constant
~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 5.2.0
``PILLOW_VERSION`` has been deprecated and will be removed in a future release. Use
``__version__`` instead.
``PILLOW_VERSION`` will be removed in Pillow 9.0.0 (2022-01-02).
Use ``__version__`` instead.
It was initially removed in Pillow 7.0.0, but brought back in 7.1.0 to give projects
more time to upgrade.

View File

@ -125,10 +125,10 @@ following options are available::
**append_images**
A list of images to append as additional frames. Each of the
images in the list can be single or multiframe images.
This is currently supported for GIF, PDF, TIFF, and WebP.
This is currently supported for GIF, PDF, PNG, TIFF, and WebP.
It is also supported for ICNS. If images are passed in of relevant sizes,
they will be used instead of scaling down the main image.
It is also supported for ICO and ICNS. If images are passed in of relevant
sizes, they will be used instead of scaling down the main image.
**include_color_table**
Whether or not to include local color table.
@ -238,6 +238,15 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
(64, 64), (128, 128), (256, 256)]``. Any sizes bigger than the original
size or 256 will be ignored.
The :py:meth:`~PIL.Image.Image.save` method can take the following keyword arguments:
**append_images**
A list of images to replace the scaled down versions of the image.
The order of the images does not matter, as their use is determined by
the size of each image.
.. versionadded:: 8.1.0
IM
^^
@ -947,9 +956,10 @@ Saving sequences
library is v0.5.0 or later. You can check webp animation support at
runtime by calling ``features.check("webp_anim")``.
When calling :py:meth:`~PIL.Image.Image.save` to write a WebP file, the
following options are available when the ``save_all`` argument is present and
true.
When calling :py:meth:`~PIL.Image.Image.save` to write a WebP file, by default
only the first frame of a multiframe image will be saved. If the ``save_all``
argument is present and true, then all frames will be saved, and the following
options will also be available.
**append_images**
A list of images to append as additional frames. Each of the

View File

@ -157,7 +157,7 @@ The raw decoder
The ``raw`` decoder is used to read uncompressed data from an image file. It
can be used with most uncompressed file formats, such as PPM, BMP, uncompressed
TIFF, and many others. To use the raw decoder with the
:py:func:`PIL.Image.frombytes` function, use the following syntax::
:py:func:`PIL.Image.frombytes` function, use the following syntax:
.. code-block:: python

View File

@ -9,18 +9,6 @@ Pillow for enterprise is available via the Tidelift Subscription. `Learn more <h
:target: https://pillow.readthedocs.io/?badge=latest
:alt: Documentation Status
.. image:: https://img.shields.io/travis/python-pillow/Pillow/master.svg?label=Linux%20build
:target: https://travis-ci.org/python-pillow/Pillow
:alt: Travis CI build status (Linux)
.. image:: https://img.shields.io/travis/python-pillow/pillow-wheels/master.svg?label=macOS%20build
:target: https://travis-ci.org/python-pillow/pillow-wheels
:alt: Travis CI build status (macOS)
.. image:: https://img.shields.io/appveyor/build/python-pillow/Pillow/master.svg?label=Windows%20build
:target: https://ci.appveyor.com/project/python-pillow/Pillow
:alt: AppVeyor CI build status (Windows)
.. image:: https://github.com/python-pillow/Pillow/workflows/Lint/badge.svg
:target: https://github.com/python-pillow/Pillow/actions?query=workflow%3ALint
:alt: GitHub Actions build status (Lint)
@ -37,6 +25,14 @@ Pillow for enterprise is available via the Tidelift Subscription. `Learn more <h
:target: https://github.com/python-pillow/Pillow/actions?query=workflow%3A%22Test+Windows%22
:alt: GitHub Actions build status (Test Windows)
.. image:: https://img.shields.io/appveyor/build/python-pillow/Pillow/master.svg?label=Windows%20build
:target: https://ci.appveyor.com/project/python-pillow/Pillow
:alt: AppVeyor CI build status (Windows)
.. image:: https://img.shields.io/travis/com/python-pillow/pillow-wheels/master.svg?label=macOS%20build
:target: https://travis-ci.com/github/python-pillow/pillow-wheels
:alt: Travis CI build status (macOS)
.. image:: https://codecov.io/gh/python-pillow/Pillow/branch/master/graph/badge.svg
:target: https://codecov.io/gh/python-pillow/Pillow
:alt: Code coverage

View File

@ -171,13 +171,13 @@ Many of Pillow's features require external libraries:
* **openjpeg** provides JPEG 2000 functionality.
* Pillow has been tested with openjpeg **2.0.0**, **2.1.0** and **2.3.1**.
* Pillow has been tested with openjpeg **2.0.0**, **2.1.0**, **2.3.1** and **2.4.0**.
* Pillow does **not** support the earlier **1.5** series which ships
with Debian Jessie.
* **libimagequant** provides improved color quantization
* Pillow has been tested with libimagequant **2.6-2.13.0**
* Pillow has been tested with libimagequant **2.6-2.13.1**
* Libimagequant is licensed GPLv3, which is more restrictive than
the Pillow license, therefore we will not be distributing binaries
with libimagequant support enabled.
@ -422,12 +422,8 @@ These platforms are built and tested for every change.
+----------------------------------+--------------------------+-----------------------+
| Arch | 3.8 |x86-64 |
+----------------------------------+--------------------------+-----------------------+
| Amazon Linux 1 | 3.6 |x86-64 |
+----------------------------------+--------------------------+-----------------------+
| Amazon Linux 2 | 3.7 |x86-64 |
+----------------------------------+--------------------------+-----------------------+
| CentOS 6 | 3.6 |x86-64 |
+----------------------------------+--------------------------+-----------------------+
| CentOS 7 | 3.6 |x86-64 |
+----------------------------------+--------------------------+-----------------------+
| CentOS 8 | 3.6 |x86-64 |
@ -436,17 +432,17 @@ These platforms are built and tested for every change.
+----------------------------------+--------------------------+-----------------------+
| Fedora 32 | 3.8 |x86-64 |
+----------------------------------+--------------------------+-----------------------+
| Fedora 33 | 3.9 |x86-64 |
+----------------------------------+--------------------------+-----------------------+
| macOS 10.15 Catalina | 3.6, 3.7, 3.8, 3.9, PyPy3|x86-64 |
+----------------------------------+--------------------------+-----------------------+
| Ubuntu Linux 16.04 LTS (Xenial) | 3.6, 3.7, 3.8, PyPy3 |x86-64 |
| Ubuntu Linux 16.04 LTS (Xenial) | 3.6, 3.7, 3.8, 3.9, PyPy3|x86-64 |
+----------------------------------+--------------------------+-----------------------+
| Ubuntu Linux 18.04 LTS (Bionic) | 3.6, 3.7, 3.8, 3.9, PyPy3|x86-64 |
+----------------------------------+--------------------------+-----------------------+
| Ubuntu Linux 20.04 LTS (Focal) | 3.8 |x86-64 |
+----------------------------------+--------------------------+-----------------------+
| Windows Server 2016 | 3.8 |x86 |
| +--------------------------+-----------------------+
| | 3.6 |x86-64 |
| Windows Server 2016 | 3.6 |x86-64 |
+----------------------------------+--------------------------+-----------------------+
| Windows Server 2019 | 3.6, 3.7, 3.8, 3.9 |x86, x86-64 |
| +--------------------------+-----------------------+
@ -469,7 +465,13 @@ These platforms have been reported to work at the versions mentioned.
+----------------------------------+------------------------------+--------------------------------+-----------------------+
|**Operating system** |**Tested Python versions** |**Latest tested Pillow version**|**Tested processors** |
+----------------------------------+------------------------------+--------------------------------+-----------------------+
| macOS 10.15 Catalina | 3.5, 3.6, 3.7, 3.8 | 7.2.0 |x86-64 |
| macOS 11.0 Big Sur | 3.8, 3.9 | 8.0.1 |arm |
| +------------------------------+--------------------------------+-----------------------+
| | 3.6, 3.7, 3.8, 3.9 | 8.0.1 |x86-64 |
+----------------------------------+------------------------------+--------------------------------+-----------------------+
| macOS 10.15 Catalina | 3.6, 3.7, 3.8, 3.9 | 8.0.1 |x86-64 |
| +------------------------------+--------------------------------+ +
| | 3.5 | 7.2.0 | |
+----------------------------------+------------------------------+--------------------------------+-----------------------+
| macOS 10.14 Mojave | 3.5, 3.6, 3.7, 3.8 | 7.2.0 |x86-64 |
| +------------------------------+--------------------------------+ +

View File

@ -200,7 +200,7 @@ can be easily displayed in a chromaticity diagram, for example).
The chromatic adaption matrix converts a color measured using the
actual illumination conditions and relative to the actual adopted
white, to an color relative to the PCS adopted white, with
white, to a color relative to the PCS adopted white, with
complete adaptation from the actual adopted white chromaticity to
the PCS adopted white chromaticity (see 9.2.15 of ICC.1:2010).

View File

@ -296,7 +296,7 @@ Methods
Draws the string at the given position.
:param xy: The anchor coordinates of the text.
:param text: Text to be drawn. If it contains any newline characters,
:param text: String to be drawn. If it contains any newline characters,
the text is passed on to
:py:meth:`~PIL.ImageDraw.ImageDraw.multiline_text`.
:param fill: Color to use for the text.
@ -362,7 +362,7 @@ Methods
Draws the string at the given position.
:param xy: The anchor coordinates of the text.
:param text: Text to be drawn.
:param text: String to be drawn.
:param fill: Color to use for the text.
:param font: An :py:class:`~PIL.ImageFont.ImageFont` instance.

View File

@ -8,10 +8,10 @@ The :py:mod:`~PIL.ImageFont` module defines a class with the same name. Instance
this class store bitmap fonts, and are used with the
:py:meth:`PIL.ImageDraw.ImageDraw.text` method.
PIL uses its own font file format to store bitmap fonts. You can use the
:command:`pilfont` utility from
`pillow-scripts <https://pypi.org/project/pillow-scripts/>`_
to convert BDF and PCF font descriptors (X window font formats) to this format.
PIL uses its own font file format to store bitmap fonts, limited to 256 characters. You can use
`pilfont.py <https://github.com/python-pillow/pillow-scripts/blob/master/Scripts/pilfont.py>`_
from `pillow-scripts <https://pypi.org/project/pillow-scripts/>`_ to convert BDF and
PCF font descriptors (X window font formats) to this format.
Starting with version 1.1.4, PIL can be configured to support TrueType and
OpenType fonts (as well as other font formats supported by the FreeType

View File

@ -6,7 +6,7 @@ CVE-2016-0740 -- Buffer overflow in TiffDecode.c
------------------------------------------------
Pillow 3.1.0 and earlier when linked against libtiff >= 4.0.0 on x64
may overflow a buffer when reading a specially crafted tiff file.
may overflow a buffer when reading a specially crafted tiff file (:cve:`CVE-2016-0740`).
Specifically, libtiff >= 4.0.0 changed the return type of
``TIFFScanlineSize`` from ``int32`` to machine dependent
@ -24,9 +24,11 @@ CVE-2016-0775 -- Buffer overflow in FliDecode.c
-----------------------------------------------
In all versions of Pillow, dating back at least to the last PIL 1.1.7
release, FliDecode.c has a buffer overflow error.
release, FliDecode.c has a buffer overflow error (:cve:`CVE-2016-0775`).
Around line 192::
Around line 192:
.. code-block:: c
case 16:
/* COPY chunk */
@ -45,13 +47,13 @@ is a set of row pointers to segments of memory that are the size of
the row. At the max ``y``, this will write the contents of the line
off the end of the memory buffer, causing a segfault.
This issue was found by Alyssa Besseling at Atlassian
This issue was found by Alyssa Besseling at Atlassian.
CVE-2016-2533 -- Buffer overflow in PcdDecode.c
-----------------------------------------------
In all versions of Pillow, dating back at least to the last PIL 1.1.7
release, ``PcdDecode.c`` has a buffer overflow error.
release, ``PcdDecode.c`` has a buffer overflow error (:cve:`CVE-2016-2533`).
The ``state.buffer`` for ``PcdDecode.c`` is allocated based on a 3
bytes per pixel sizing, where ``PcdDecode.c`` wrote into the buffer
@ -63,14 +65,16 @@ Integer overflow in Resample.c
------------------------------
If a large value was passed into the new size for an image, it is
possible to overflow an int32 value passed into malloc.
possible to overflow an ``int32`` value passed into malloc.
kk = malloc(xsize * kmax * sizeof(float));
...
xbounds = malloc(xsize * 2 * sizeof(int));
.. code-block:: c
kk = malloc(xsize * kmax * sizeof(float));
...
xbounds = malloc(xsize * 2 * sizeof(int));
``xsize`` is trusted user input. These multiplications can overflow,
leading the malloc'd buffer to be undersized. These allocations are
leading the ``malloc``'d buffer to be undersized. These allocations are
followed by a loop that writes out of bounds. This can lead to
corruption on the heap of the Python process with attacker controlled
float data.

View File

@ -7,9 +7,11 @@ CVE-2016-3076 -- Buffer overflow in Jpeg2KEncode.c
Pillow between 2.5.0 and 3.1.1 may overflow a buffer when writing
large Jpeg2000 files, allowing for code execution or other memory
corruption.
corruption (:cve:`CVE-2016-3076`).
This occurs specifically in the function ``j2k_encode_entry``, at the line::
This occurs specifically in the function ``j2k_encode_entry``, at the line:
.. code-block:: c
state->buffer = malloc (tile_width * tile_height * components * prec / 8);

View File

@ -17,7 +17,7 @@ Removed deprecated PIL.OleFileIO
PIL.OleFileIO was removed as a vendored file and in Pillow 4.0.0 (2017-01) in favour of
the upstream olefile Python package, and replaced with an ``ImportError``. The
deprecated file has now been removed from Pillow. If needed, install from PyPI (eg.
``pip install olefile``).
``python3 -m pip install olefile``).
Removed deprecated ImageOps functions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -73,7 +73,7 @@ Security
========
This release catches several buffer overruns, as well as addressing
CVE-2019-16865. The CVE is regarding DOS problems, such as consuming large
:cve:`CVE-2019-16865`. The CVE is regarding DOS problems, such as consuming large
amounts of memory, or taking a large amount of time to process an image.
In RawDecode.c, an error is now thrown if skip is calculated to be less than
@ -96,14 +96,14 @@ Other Changes
Removed bdist_wininst .exe installers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.exe installers fell out of favour with PEP 527, and will be deprecated in
.exe installers fell out of favour with :pep:`527`, and will be deprecated in
Python 3.8. Pillow will no longer be distributing them. Wheels should be used
instead.
Flags for libwebp in wheels
^^^^^^^^^^^^^^^^^^^^^^^^^^^
When building libwebp for inclusion in wheels, Pillow now adds the -O3 and
-DNDEBUG CFLAGS. These flags would be used by default if building libwebp
When building libwebp for inclusion in wheels, Pillow now adds the ``-O3`` and
``-DNDEBUG`` CFLAGS. These flags would be used by default if building libwebp
without debugging, and using them fixes a significant decrease in speed when
a wheel-installed copy of Pillow performs libwebp operations.

View File

@ -6,12 +6,13 @@ Security
This release addresses several security problems.
CVE-2019-19911 is regarding FPX images. If an image reports that it has a large number
of bands, a large amount of resources will be used when trying to process the
:cve:`CVE-2019-19911` is regarding FPX images. If an image reports that it has a large
number of bands, a large amount of resources will be used when trying to process the
image. This is fixed by limiting the number of bands to those usable by Pillow.
Buffer overruns were found when processing an SGI (CVE-2020-5311), PCX (CVE-2020-5312)
or FLI image (CVE-2020-5313). Checks have been added to prevent this.
Buffer overruns were found when processing an SGI (:cve:`CVE-2020-5311`),
PCX (:cve:`CVE-2020-5312`) or FLI image (:cve:`CVE-2020-5313`). Checks have been added
to prevent this.
CVE-2020-5310: Overflow checks have been added when calculating the size of a memory
block to be reallocated in the processing of a TIFF image.
:cve:`CVE-2020-5310`: Overflow checks have been added when calculating the size of a
memory block to be reallocated in the processing of a TIFF image.

View File

@ -74,11 +74,11 @@ Security
This release includes security fixes.
* CVE-2020-10177 Fix multiple OOB reads in FLI decoding
* CVE-2020-10378 Fix bounds overflow in PCX decoding
* CVE-2020-10379 Fix two buffer overflows in TIFF decoding
* CVE-2020-10994 Fix bounds overflow in JPEG 2000 decoding
* CVE-2020-11538 Fix buffer overflow in SGI-RLE decoding
* :cve:`CVE-2020-10177` Fix multiple OOB reads in FLI decoding
* :cve:`CVE-2020-10378` Fix bounds overflow in PCX decoding
* :cve:`CVE-2020-10379` Fix two buffer overflows in TIFF decoding
* :cve:`CVE-2020-10994` Fix bounds overflow in JPEG 2000 decoding
* :cve:`CVE-2020-11538` Fix buffer overflow in SGI-RLE decoding
Other Changes
=============

View File

@ -7,7 +7,7 @@ Fix regression seeking PNG files
This fixes a regression introduced in 7.1.0 when adding support for APNG files when calling
``seek`` and ``tell``:
.. code-block:: python
.. code-block:: pycon
>>> from PIL import Image
>>> with Image.open("Tests/images/hopper.png") as im:

View File

@ -9,7 +9,7 @@ This fixes a regression introduced in 7.1.0 when adding support for APNG files.
When calling ``seek(n)`` on a regular PNG where ``n > 0``, it failed to raise an
``EOFError`` as it should have done, resulting in:
.. code-block:: python
.. code-block:: pycon
AttributeError: 'NoneType' object has no attribute 'read'

View File

@ -0,0 +1,22 @@
8.0.1
-----
Security
========
Update FreeType used in binary wheels to `2.10.4`_ to fix :cve:`CVE-2020-15999`:
- A heap buffer overflow has been found in the handling of embedded PNG bitmaps,
introduced in FreeType version 2.6.
If you use option ``FT_CONFIG_OPTION_USE_PNG`` you should upgrade immediately.
We strongly recommend updating to Pillow 8.0.1 if you are using Pillow 8.0.0, which improved support for bitmap fonts.
In Pillow 7.2.0 and earlier bitmap fonts were disabled with ``FT_LOAD_NO_BITMAP``, but it is not
clear if this prevents the exploit and we recommend updating to Pillow 8.0.1.
Pillow 8.0.0 and earlier are potentially vulnerable releases, including the last release
to support Python 2.7, namely Pillow 6.2.2.
.. _2.10.4: https://sourceforge.net/projects/freetype/files/freetype2/2.10.4/

View File

@ -0,0 +1,89 @@
8.1.0
-----
Deprecations
============
FreeType 2.7
^^^^^^^^^^^^
Support for FreeType 2.7 is deprecated and will be removed in Pillow 9.0.0 (2022-01-02),
when FreeType 2.8 will be the minimum supported.
We recommend upgrading to at least FreeType `2.10.4`_, which fixed a severe
vulnerability introduced in FreeType 2.6 (:cve:`CVE-2020-15999`).
.. _2.10.4: https://sourceforge.net/projects/freetype/files/freetype2/2.10.4/
Makefile
^^^^^^^^
The 'install-venv' target has been deprecated.
API Additions
=============
Append images to ICO
^^^^^^^^^^^^^^^^^^^^
When saving an ICO image, the file may contain versions of the image at different
sizes. By default, Pillow will scale down the main image to create these copies.
With this release, a list of images can be provided to the ``append_images`` parameter
when saving, to replace the scaled down versions. This is the same functionality that
already exists for the ICNS format.
Security
========
This release includes security fixes.
* An out-of-bounds read when saving TIFFs with custom metadata through LibTIFF
* An out-of-bounds read when saving a GIF of 1px width
* :cve:`CVE-2020-35653` Buffer read overrun in PCX decoding
The PCX image decoder used the reported image stride to calculate the row buffer,
rather than calculating it from the image size. This issue dates back to the PIL fork.
Thanks to Google's `OSS-Fuzz`_ project for finding this.
* :cve:`CVE-2020-35654` Fix TIFF OOB Write error
OOB Write in TiffDecode.c when reading corrupt YCbCr files in some LibTIFF versions
(4.1.0/Ubuntu 20.04, but not 4.0.9/Ubuntu 18.04). In some cases LibTIFF's
interpretation of the file is different when reading in RGBA mode, leading to an Out of
bounds write in TiffDecode.c. This potentially affects Pillow versions from 6.0.0 to
8.0.1, depending on the version of LibTIFF. This was reported through `Tidelift`_.
* :cve:`CVE-2020-35655` Fix for SGI Decode buffer overrun
4 byte read overflow in SGIRleDecode.c, where the code was not correctly checking the
offsets and length tables. Independently reported through `Tidelift`_ and Google's
`OSS-Fuzz`_. This vulnerability covers Pillow versions 4.3.0->8.0.1.
.. _Tidelift: https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pillow&utm_medium=referral&utm_campaign=docs
.. _OSS-Fuzz: https://github.com/google/oss-fuzz
Dependencies
^^^^^^^^^^^^
OpenJPEG in the macOS and Linux wheels has been updated from 2.3.1 to 2.4.0, including
security fixes.
Other Changes
=============
Makefile
^^^^^^^^
The 'co' target has been removed.
PyPy wheels
^^^^^^^^^^^
Wheels have been added for PyPy 3.7.
PySide6
^^^^^^^
Support has been added for PySide6. If it is installed, it will be used instead of
PyQt5 or PySide2, since it is based on a newer Qt.

View File

@ -3,7 +3,8 @@ Release Notes
Pillow is released quarterly on January 2nd, April 1st, July 1st and October 15th.
Patch releases are created if the latest release contains severe bugs, or if security
fixes are put together before a scheduled release.
fixes are put together before a scheduled release. See :ref:`versioning` for more
information.
Please use the latest version of Pillow. Functionality and security fixes should not be
expected to be backported to earlier versions.
@ -13,6 +14,8 @@ expected to be backported to earlier versions.
.. toctree::
:maxdepth: 2
8.1.0
8.0.1
8.0.0
7.2.0
7.1.2
@ -46,3 +49,4 @@ expected to be backported to earlier versions.
3.0.0
2.8.0
2.7.0
versioning

View File

@ -0,0 +1,30 @@
.. _versioning:
Versioning
==========
Pillow follows [Semantic Versioning](https://semver.org/):
Given a version number MAJOR.MINOR.PATCH, increment the:
1. MAJOR version when you make incompatible API changes,
2. MINOR version when you add functionality in a backwards compatible manner, and
3. PATCH version when you make backwards compatible bug fixes.
Quarterly releases ("`Main Release <https://github.com/python-pillow/Pillow/blob/master/RELEASING.md#main-release>`_")
bump at least the MINOR version, as new functionality has likely been added in the
prior three months.
A quarterly release bumps the MAJOR version when incompatible API changes are
made, such as removing deprecated APIs or dropping an EOL Python version. In practice,
these occur every 12-18 months, guided by
`Python's EOL schedule <https://devguide.python.org/#status-of-python-branches>`_, and
any APIs that have been deprecated for at least a year are removed at the same time.
PATCH versions ("`Point Release <https://github.com/python-pillow/Pillow/blob/master/RELEASING.md#point-release>`_"
or "`Embargoed Release <https://github.com/python-pillow/Pillow/blob/master/RELEASING.md#embargoed-release>`_")
are for security, installation or critical bug fixes. These are less common as it is
preferred to stick to quarterly releases.
Between quarterly releases, ".dev0" is appended to the "master" branch, indicating that
this is not a formally released copy.

View File

@ -1196,6 +1196,11 @@
color: rgb(166, 158, 146);
}
.wy-menu-vertical li.toctree-l2.current a,
.wy-menu-vertical li.toctree-l3.current a {
background-color: #363636;
}
.wy-menu-vertical li ul li a {
color: rgb(208, 204, 198);
}

View File

@ -0,0 +1,8 @@
@media (prefers-color-scheme: light) {
.wy-menu-vertical li.toctree-l2.current a,
.wy-menu-vertical li.toctree-l3.current a {
background-color: #c9c9c9;
}
}

View File

@ -24,13 +24,11 @@ jQuery(document).ready(function ($) {
var $upperA = $sidebarItem.parent().children('a');
var $upperAParent = $upperA.parent();
if ($upperAParent.hasClass('toctree-l2')) {
$a.css('background-color', '#c9c9c9');
$a.css('padding-left', '4em');
} else if ($upperAParent.hasClass('toctree-l3')) {
if (!$upperA.find('.toctree-expand').length) {
$upperA.prepend($('<span />').addClass('toctree-expand'));
}
$a.css('background-color', '#c9c9c9');
$a.css('padding-left', '5em');
} else {
$a.css('background-color', '#bdbdbd');

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -2,13 +2,13 @@
black
check-manifest
coverage
jarn.viewdoc
markdown2
olefile
pycodestyle
pyflakes
packaging
pyroma
pytest
pytest-cov
sphinx>=2.4
sphinx-issues
sphinx-removed-in
sphinx-rtd-theme

View File

@ -38,12 +38,16 @@ ZLIB_ROOT = None
if sys.platform == "win32" and sys.version_info >= (3, 10):
warnings.warn(
f"Pillow {PILLOW_VERSION} does not support Python "
f"{sys.version_info.major}.{sys.version_info.minor} and does not provide "
"prebuilt Windows binaries. We do not recommend building from source on "
"Windows.",
RuntimeWarning,
import atexit
atexit.register(
lambda: warnings.warn(
f"Pillow {PILLOW_VERSION} does not support Python "
f"{sys.version_info.major}.{sys.version_info.minor} and does not provide "
"prebuilt Windows binaries. We do not recommend building from source on "
"Windows.",
RuntimeWarning,
)
)
@ -873,6 +877,10 @@ try:
"Source": "https://github.com/python-pillow/Pillow",
"Funding": "https://tidelift.com/subscription/pkg/pypi-pillow?"
"utm_source=pypi-pillow&utm_medium=pypi",
"Release notes": "https://pillow.readthedocs.io/en/stable/releasenotes/"
"index.html",
"Changelog": "https://github.com/python-pillow/Pillow/blob/master/"
"CHANGES.rst",
},
classifiers=[
"Development Status :: 6 - Mature",

View File

@ -25,7 +25,6 @@
from . import Image, ImageFile, ImagePalette
from ._binary import i8
from ._binary import i16le as i16
from ._binary import i32le as i32
from ._binary import o8
@ -52,7 +51,7 @@ def _accept(prefix):
def _dib_accept(prefix):
return i32(prefix[:4]) in [12, 40, 64, 108, 124]
return i32(prefix) in [12, 40, 64, 108, 124]
# =============================================================================
@ -87,34 +86,34 @@ class BmpImageFile(ImageFile.ImageFile):
# -------------------------------------------------- IBM OS/2 Bitmap v1
# ----- This format has different offsets because of width/height types
if file_info["header_size"] == 12:
file_info["width"] = i16(header_data[0:2])
file_info["height"] = i16(header_data[2:4])
file_info["planes"] = i16(header_data[4:6])
file_info["bits"] = i16(header_data[6:8])
file_info["width"] = i16(header_data, 0)
file_info["height"] = i16(header_data, 2)
file_info["planes"] = i16(header_data, 4)
file_info["bits"] = i16(header_data, 6)
file_info["compression"] = self.RAW
file_info["palette_padding"] = 3
# --------------------------------------------- Windows Bitmap v2 to v5
# v3, OS/2 v2, v4, v5
elif file_info["header_size"] in (40, 64, 108, 124):
file_info["y_flip"] = i8(header_data[7]) == 0xFF
file_info["y_flip"] = header_data[7] == 0xFF
file_info["direction"] = 1 if file_info["y_flip"] else -1
file_info["width"] = i32(header_data[0:4])
file_info["width"] = i32(header_data, 0)
file_info["height"] = (
i32(header_data[4:8])
i32(header_data, 4)
if not file_info["y_flip"]
else 2 ** 32 - i32(header_data[4:8])
else 2 ** 32 - i32(header_data, 4)
)
file_info["planes"] = i16(header_data[8:10])
file_info["bits"] = i16(header_data[10:12])
file_info["compression"] = i32(header_data[12:16])
file_info["planes"] = i16(header_data, 8)
file_info["bits"] = i16(header_data, 10)
file_info["compression"] = i32(header_data, 12)
# byte size of pixel data
file_info["data_size"] = i32(header_data[16:20])
file_info["data_size"] = i32(header_data, 16)
file_info["pixels_per_meter"] = (
i32(header_data[20:24]),
i32(header_data[24:28]),
i32(header_data, 20),
i32(header_data, 24),
)
file_info["colors"] = i32(header_data[28:32])
file_info["colors"] = i32(header_data, 28)
file_info["palette_padding"] = 4
self.info["dpi"] = tuple(
int(x / 39.3701 + 0.5) for x in file_info["pixels_per_meter"]
@ -124,7 +123,7 @@ class BmpImageFile(ImageFile.ImageFile):
for idx, mask in enumerate(
["r_mask", "g_mask", "b_mask", "a_mask"]
):
file_info[mask] = i32(header_data[36 + idx * 4 : 40 + idx * 4])
file_info[mask] = i32(header_data, 36 + idx * 4)
else:
# 40 byte headers only have the three components in the
# bitfields masks, ref:
@ -267,7 +266,7 @@ class BmpImageFile(ImageFile.ImageFile):
if not _accept(head_data):
raise SyntaxError("Not a BMP file")
# read the start position of the BMP image data (u32)
offset = i32(head_data[10:14])
offset = i32(head_data, 10)
# load bitmap information (offset=raster info)
self._bitmap(offset=offset)

View File

@ -16,7 +16,6 @@
# See the README file for information on usage and redistribution.
#
from . import BmpImagePlugin, Image
from ._binary import i8
from ._binary import i16le as i16
from ._binary import i32le as i32
@ -48,17 +47,17 @@ class CurImageFile(BmpImagePlugin.BmpImageFile):
# pick the largest cursor in the file
m = b""
for i in range(i16(s[4:])):
for i in range(i16(s, 4)):
s = self.fp.read(16)
if not m:
m = s
elif i8(s[0]) > i8(m[0]) and i8(s[1]) > i8(m[1]):
elif s[0] > m[0] and s[1] > m[1]:
m = s
if not m:
raise TypeError("No cursors were found")
# load as bitmap
self._bitmap(i32(m[12:]) + offset)
self._bitmap(i32(m, 12) + offset)
# patch up the bitmap height
self._size = self.size[0], self.size[1] // 2

View File

@ -312,14 +312,14 @@ class EpsImageFile(ImageFile.ImageFile):
fp.seek(0, io.SEEK_END)
length = fp.tell()
offset = 0
elif i32(s[0:4]) == 0xC6D3D0C5:
elif i32(s, 0) == 0xC6D3D0C5:
# FIX for: Some EPS file not handled correctly / issue #302
# EPS can contain binary data
# or start directly with latin coding
# more info see:
# https://web.archive.org/web/20160528181353/http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf
offset = i32(s[4:8])
length = i32(s[8:12])
offset = i32(s, 4)
length = i32(s, 8)
else:
raise SyntaxError("not an EPS file")

View File

@ -17,7 +17,6 @@
from . import Image, ImageFile, ImagePalette
from ._binary import i8
from ._binary import i16le as i16
from ._binary import i32le as i32
from ._binary import o8
@ -27,7 +26,7 @@ from ._binary import o8
def _accept(prefix):
return len(prefix) >= 6 and i16(prefix[4:6]) in [0xAF11, 0xAF12]
return len(prefix) >= 6 and i16(prefix, 4) in [0xAF11, 0xAF12]
##
@ -47,22 +46,22 @@ class FliImageFile(ImageFile.ImageFile):
s = self.fp.read(128)
if not (
_accept(s)
and i16(s[14:16]) in [0, 3] # flags
and i16(s, 14) in [0, 3] # flags
and s[20:22] == b"\x00\x00" # reserved
):
raise SyntaxError("not an FLI/FLC file")
# frames
self.n_frames = i16(s[6:8])
self.n_frames = i16(s, 6)
self.is_animated = self.n_frames > 1
# image characteristics
self.mode = "P"
self._size = i16(s[8:10]), i16(s[10:12])
self._size = i16(s, 8), i16(s, 10)
# animation speed
duration = i32(s[16:20])
magic = i16(s[4:6])
duration = i32(s, 16)
magic = i16(s, 4)
if magic == 0xAF11:
duration = (duration * 1000) // 70
self.info["duration"] = duration
@ -74,17 +73,17 @@ class FliImageFile(ImageFile.ImageFile):
self.__offset = 128
if i16(s[4:6]) == 0xF100:
if i16(s, 4) == 0xF100:
# prefix chunk; ignore it
self.__offset = self.__offset + i32(s)
s = self.fp.read(16)
if i16(s[4:6]) == 0xF1FA:
if i16(s, 4) == 0xF1FA:
# look for palette chunk
s = self.fp.read(6)
if i16(s[4:6]) == 11:
if i16(s, 4) == 11:
self._palette(palette, 2)
elif i16(s[4:6]) == 4:
elif i16(s, 4) == 4:
self._palette(palette, 0)
palette = [o8(r) + o8(g) + o8(b) for (r, g, b) in palette]
@ -102,15 +101,15 @@ class FliImageFile(ImageFile.ImageFile):
i = 0
for e in range(i16(self.fp.read(2))):
s = self.fp.read(2)
i = i + i8(s[0])
n = i8(s[1])
i = i + s[0]
n = s[1]
if n == 0:
n = 256
s = self.fp.read(n * 3)
for n in range(0, len(s), 3):
r = i8(s[n]) << shift
g = i8(s[n + 1]) << shift
b = i8(s[n + 2]) << shift
r = s[n] << shift
g = s[n + 1] << shift
b = s[n + 2] << shift
palette[i] = (r, g, b)
i += 1

View File

@ -17,7 +17,6 @@
import olefile
from . import Image, ImageFile
from ._binary import i8
from ._binary import i32le as i32
# we map from colour field tuples to (mode, rawmode) descriptors
@ -181,8 +180,8 @@ class FpxImageFile(ImageFile.ImageFile):
elif compression == 2:
internal_color_conversion = i8(s[14])
jpeg_tables = i8(s[15])
internal_color_conversion = s[14]
jpeg_tables = s[15]
rawmode = self.rawmode
if internal_color_conversion:

View File

@ -29,7 +29,7 @@ from ._binary import i32be as i32
def _accept(prefix):
return len(prefix) >= 8 and i32(prefix[:4]) >= 20 and i32(prefix[4:8]) in (1, 2)
return len(prefix) >= 8 and i32(prefix, 0) >= 20 and i32(prefix, 4) in (1, 2)
##

View File

@ -28,7 +28,6 @@
from . import ImageFile, ImagePalette, UnidentifiedImageError
from ._binary import i8
from ._binary import i16be as i16
from ._binary import i32be as i32
@ -49,17 +48,17 @@ class GdImageFile(ImageFile.ImageFile):
# Header
s = self.fp.read(1037)
if not i16(s[:2]) in [65534, 65535]:
if not i16(s) in [65534, 65535]:
raise SyntaxError("Not a valid GD 2.x .gd file")
self.mode = "L" # FIXME: "P"
self._size = i16(s[2:4]), i16(s[4:6])
self._size = i16(s, 2), i16(s, 4)
trueColor = i8(s[6])
trueColor = s[6]
trueColorOffset = 2 if trueColor else 0
# transparency index
tindex = i32(s[7 + trueColorOffset : 7 + trueColorOffset + 4])
tindex = i32(s, 7 + trueColorOffset)
if tindex < 256:
self.info["transparency"] = tindex

View File

@ -30,7 +30,6 @@ import os
import subprocess
from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence
from ._binary import i8
from ._binary import i16le as i16
from ._binary import o8
from ._binary import o16le as o16
@ -58,8 +57,8 @@ class GifImageFile(ImageFile.ImageFile):
def data(self):
s = self.fp.read(1)
if s and i8(s):
return self.fp.read(i8(s))
if s and s[0]:
return self.fp.read(s[0])
return None
def _open(self):
@ -70,18 +69,18 @@ class GifImageFile(ImageFile.ImageFile):
raise SyntaxError("not a GIF file")
self.info["version"] = s[:6]
self._size = i16(s[6:]), i16(s[8:])
self._size = i16(s, 6), i16(s, 8)
self.tile = []
flags = i8(s[10])
flags = s[10]
bits = (flags & 7) + 1
if flags & 128:
# get global palette
self.info["background"] = i8(s[11])
self.info["background"] = s[11]
# check if palette contains colour indices
p = self.fp.read(3 << bits)
for i in range(0, len(p), 3):
if not (i // 3 == i8(p[i]) == i8(p[i + 1]) == i8(p[i + 2])):
if not (i // 3 == p[i] == p[i + 1] == p[i + 2]):
p = ImagePalette.raw("RGB", p)
self.global_palette = self.palette = p
break
@ -187,14 +186,14 @@ class GifImageFile(ImageFile.ImageFile):
#
s = self.fp.read(1)
block = self.data()
if i8(s) == 249:
if s[0] == 249:
#
# graphic control extension
#
flags = i8(block[0])
flags = block[0]
if flags & 1:
info["transparency"] = i8(block[3])
info["duration"] = i16(block[1:3]) * 10
info["transparency"] = block[3]
info["duration"] = i16(block, 1) * 10
# disposal method - find the value of bits 4 - 6
dispose_bits = 0b00011100 & flags
@ -205,7 +204,7 @@ class GifImageFile(ImageFile.ImageFile):
# correct, but it seems to prevent the last
# frame from looking odd for some animations
self.disposal_method = dispose_bits
elif i8(s) == 254:
elif s[0] == 254:
#
# comment extension
#
@ -216,15 +215,15 @@ class GifImageFile(ImageFile.ImageFile):
info["comment"] = block
block = self.data()
continue
elif i8(s) == 255:
elif s[0] == 255:
#
# application extension
#
info["extension"] = block, self.fp.tell()
if block[:11] == b"NETSCAPE2.0":
block = self.data()
if len(block) >= 3 and i8(block[0]) == 1:
info["loop"] = i16(block[1:3])
if len(block) >= 3 and block[0] == 1:
info["loop"] = i16(block, 1)
while self.data():
pass
@ -235,12 +234,12 @@ class GifImageFile(ImageFile.ImageFile):
s = self.fp.read(9)
# extent
x0, y0 = i16(s[0:]), i16(s[2:])
x1, y1 = x0 + i16(s[4:]), y0 + i16(s[6:])
x0, y0 = i16(s, 0), i16(s, 2)
x1, y1 = x0 + i16(s, 4), y0 + i16(s, 6)
if x1 > self.size[0] or y1 > self.size[1]:
self._size = max(x1, self.size[0]), max(y1, self.size[1])
self.dispose_extent = x0, y0, x1, y1
flags = i8(s[8])
flags = s[8]
interlace = (flags & 64) != 0
@ -249,7 +248,7 @@ class GifImageFile(ImageFile.ImageFile):
self.palette = ImagePalette.raw("RGB", self.fp.read(3 << bits))
# image data
bits = i8(self.fp.read(1))
bits = self.fp.read(1)[0]
self.__offset = self.fp.tell()
self.tile = [
("gif", (x0, y0, x1, y1), self.__offset, (bits, interlace))
@ -258,7 +257,7 @@ class GifImageFile(ImageFile.ImageFile):
else:
pass
# raise OSError, "illegal GIF tag `%x`" % i8(s)
# raise OSError, "illegal GIF tag `%x`" % s[0]
try:
if self.disposal_method < 2:
@ -301,13 +300,14 @@ class GifImageFile(ImageFile.ImageFile):
# if the disposal method is 'do not dispose', transparent
# pixels should show the content of the previous frame
if self._prev_im and self.disposal_method == 1:
if self._prev_im and self._prev_disposal_method == 1:
# we do this by pasting the updated area onto the previous
# frame which we then use as the current image content
updated = self._crop(self.im, self.dispose_extent)
self._prev_im.paste(updated, self.dispose_extent, updated.convert("RGBA"))
self.im = self._prev_im
self._prev_im = self.im.copy()
self._prev_disposal_method = self.disposal_method
def _close__fp(self):
try:

View File

@ -10,7 +10,6 @@
#
from . import Image, ImageFile
from ._binary import i8
_handler = None
@ -30,7 +29,7 @@ def register_handler(handler):
def _accept(prefix):
return prefix[0:4] == b"GRIB" and i8(prefix[7]) == 1
return prefix[0:4] == b"GRIB" and prefix[7] == 1
class GribStubImageFile(ImageFile.StubImageFile):

View File

@ -24,7 +24,6 @@ import sys
import tempfile
from PIL import Image, ImageFile, PngImagePlugin, features
from PIL._binary import i8
enable_jpeg2k = features.check_codec("jpg_2000")
if enable_jpeg2k:
@ -70,7 +69,7 @@ def read_32(fobj, start_length, size):
byte = fobj.read(1)
if not byte:
break
byte = i8(byte)
byte = byte[0]
if byte & 0x80:
blocksize = byte - 125
byte = fobj.read(1)

View File

@ -28,7 +28,6 @@ from io import BytesIO
from math import ceil, log
from . import BmpImagePlugin, Image, ImageFile, PngImagePlugin
from ._binary import i8
from ._binary import i16le as i16
from ._binary import i32le as i32
@ -54,6 +53,7 @@ def _save(im, fp, filename):
sizes = list(sizes)
fp.write(struct.pack("<H", len(sizes))) # idCount(2)
offset = fp.tell() + len(sizes) * 16
provided_images = {im.size: im for im in im.encoderinfo.get("append_images", [])}
for size in sizes:
width, height = size
# 0 means 256
@ -65,9 +65,11 @@ def _save(im, fp, filename):
fp.write(struct.pack("<H", 32)) # wBitCount(2)
image_io = BytesIO()
# TODO: invent a more convenient method for proportional scalings
tmp = im.copy()
tmp.thumbnail(size, Image.LANCZOS, reducing_gap=None)
tmp = provided_images.get(size)
if not tmp:
# TODO: invent a more convenient method for proportional scalings
tmp = im.copy()
tmp.thumbnail(size, Image.LANCZOS, reducing_gap=None)
tmp.save(image_io, "png")
image_io.seek(0)
image_bytes = image_io.read()
@ -100,21 +102,21 @@ class IcoFile:
self.entry = []
# Number of items in file
self.nb_items = i16(s[4:])
self.nb_items = i16(s, 4)
# Get headers for each item
for i in range(self.nb_items):
s = buf.read(16)
icon_header = {
"width": i8(s[0]),
"height": i8(s[1]),
"nb_color": i8(s[2]), # No. of colors in image (0 if >=8bpp)
"reserved": i8(s[3]),
"planes": i16(s[4:]),
"bpp": i16(s[6:]),
"size": i32(s[8:]),
"offset": i32(s[12:]),
"width": s[0],
"height": s[1],
"nb_color": s[2], # No. of colors in image (0 if >=8bpp)
"reserved": s[3],
"planes": i16(s, 4),
"bpp": i16(s, 6),
"size": i32(s, 8),
"offset": i32(s, 12),
}
# See Wikipedia

View File

@ -30,7 +30,6 @@ import os
import re
from . import Image, ImageFile, ImagePalette
from ._binary import i8
# --------------------------------------------------------------------
# Standard tags
@ -223,14 +222,14 @@ class ImImageFile(ImageFile.ImageFile):
linear = 1 # linear greyscale palette
for i in range(256):
if palette[i] == palette[i + 256] == palette[i + 512]:
if i8(palette[i]) != i:
if palette[i] != i:
linear = 0
else:
greyscale = 0
if self.mode in ["L", "LA", "P", "PA"]:
if greyscale:
if not linear:
self.lut = [i8(c) for c in palette[:256]]
self.lut = list(palette[:256])
else:
if self.mode in ["L", "P"]:
self.mode = self.rawmode = "P"
@ -240,7 +239,7 @@ class ImImageFile(ImageFile.ImageFile):
self.palette = ImagePalette.raw("RGB;L", palette)
elif self.mode == "RGB":
if not greyscale or not linear:
self.lut = [i8(c) for c in palette]
self.lut = list(palette)
self.frame = 0

View File

@ -50,7 +50,7 @@ from . import (
_plugins,
_raise_version_warning,
)
from ._binary import i8, i32le
from ._binary import i32le
from ._util import deferred_error, isPath
if sys.version_info >= (3, 7):
@ -670,7 +670,10 @@ class Image:
:returns: png version of the image as bytes
"""
b = io.BytesIO()
self.save(b, "PNG")
try:
self.save(b, "PNG")
except Exception as e:
raise ValueError("Could not save to PNG for display") from e
return b.getvalue()
@property
@ -815,9 +818,15 @@ class Image:
"""
if self.im and self.palette and self.palette.dirty:
# realize palette
self.im.putpalette(*self.palette.getdata())
mode, arr = self.palette.getdata()
if mode == "RGBA":
mode = "RGB"
self.info["transparency"] = arr[3::4]
arr = bytes(
value for (index, value) in enumerate(arr) if index % 4 != 3
)
self.im.putpalette(mode, arr)
self.palette.dirty = 0
self.palette.mode = "RGB"
self.palette.rawmode = None
if "transparency" in self.info:
if isinstance(self.info["transparency"], int):
@ -825,6 +834,8 @@ class Image:
else:
self.im.putpalettealphas(self.info["transparency"])
self.palette.mode = "RGBA"
else:
self.palette.mode = "RGB"
if self.im:
if cffi and USE_CFFI_ACCESS:
@ -1367,7 +1378,7 @@ class Image:
self.load()
x, y = self.im.getprojection()
return [i8(c) for c in x], [i8(c) for c in y]
return list(x), list(y)
def histogram(self, mask=None, extrema=None):
"""
@ -1626,7 +1637,7 @@ class Image:
self.im = im
self.pyaccess = None
self.mode = self.im.mode
except (KeyError, ValueError) as e:
except KeyError as e:
raise ValueError("illegal image mode") from e
if self.mode in ("LA", "PA"):
@ -1672,12 +1683,14 @@ class Image:
def putpalette(self, data, rawmode="RGB"):
"""
Attaches a palette to this image. The image must be a "P",
"PA", "L" or "LA" image, and the palette sequence must contain
768 integer values, where each group of three values represent
the red, green, and blue values for the corresponding pixel
index. Instead of an integer sequence, you can use an 8-bit
string.
Attaches a palette to this image. The image must be a "P", "PA", "L"
or "LA" image.
The palette sequence must contain either 768 integer values, or 1024
integer values if alpha is included. Each group of values represents
the red, green, blue (and alpha if included) values for the
corresponding pixel index. Instead of an integer sequence, you can use
an 8-bit string.
:param data: A palette sequence (either a list or a string).
:param rawmode: The raw mode of the palette.
@ -2197,8 +2210,8 @@ class Image:
if command is not None:
warnings.warn(
"The command parameter is deprecated and will be removed in a future "
"release. Use a subclass of ImageShow.Viewer instead.",
"The command parameter is deprecated and will be removed in Pillow 9 "
"(2022-01-02). Use a subclass of ImageShow.Viewer instead.",
DeprecationWarning,
)
@ -2905,6 +2918,8 @@ def open(fp, mode="r", formats=None):
def _open_core(fp, filename, prefix, formats):
for i in formats:
if i not in OPEN:
init()
try:
factory, accept = OPEN[i]
result = not accept or accept(prefix)
@ -3174,7 +3189,7 @@ def _showxv(image, title=None, **options):
del options["_internal_pillow"]
else:
warnings.warn(
"_showxv is deprecated and will be removed in a future release. "
"_showxv is deprecated and will be removed in Pillow 9 (2022-01-02). "
"Use Image.show instead.",
DeprecationWarning,
)
@ -3359,7 +3374,7 @@ class Exif(MutableMapping):
if self[0x927C][:8] == b"FUJIFILM":
exif_data = self[0x927C]
ifd_offset = i32le(exif_data[8:12])
ifd_offset = i32le(exif_data, 8)
ifd_data = exif_data[ifd_offset:]
makernote = {}

Some files were not shown because too many files have changed in this diff Show More