Merge branch 'master' into buffer-updates
|
@ -32,7 +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"'
|
||||
- '%PYTHON%\%EXECUTABLE% -m pip install -U setuptools'
|
||||
|
||||
build_script:
|
||||
- ps: |
|
||||
|
@ -45,6 +45,7 @@ test_script:
|
|||
- cd c:\pillow
|
||||
- '%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% -c "from PIL import Image"'
|
||||
- '%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?
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ 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 -U pytest-timeout
|
||||
python3 -m pip install pyroma
|
||||
python3 -m pip install test-image-results
|
||||
# TODO Remove condition when numpy supports 3.10
|
||||
|
|
|
@ -2,4 +2,6 @@
|
|||
|
||||
set -e
|
||||
|
||||
python -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests
|
||||
python3 -c "from PIL import Image"
|
||||
|
||||
python3 -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests
|
||||
|
|
1
.github/CONTRIBUTING.md
vendored
|
@ -18,6 +18,7 @@ Please send a pull request to the master branch. Please include [documentation](
|
|||
- 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 AppVeyor.
|
||||
- Include [release notes](https://github.com/python-pillow/Pillow/tree/master/docs/releasenotes) as needed or appropriate with your bug fixes, feature additions and tests.
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
|
|
47
.github/workflows/cifuzz.yml
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
name: CIFuzz
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "**.c"
|
||||
- "**.h"
|
||||
pull_request:
|
||||
paths:
|
||||
- "**.c"
|
||||
- "**.h"
|
||||
|
||||
jobs:
|
||||
Fuzzing:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Build Fuzzers
|
||||
id: build
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'pillow'
|
||||
language: python
|
||||
dry-run: false
|
||||
- name: Run Fuzzers
|
||||
id: run
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'pillow'
|
||||
fuzz-seconds: 600
|
||||
language: python
|
||||
dry-run: false
|
||||
- name: Upload New Crash
|
||||
uses: actions/upload-artifact@v2
|
||||
if: failure() && steps.build.outcome == 'success'
|
||||
with:
|
||||
name: artifacts
|
||||
path: ./out/artifacts
|
||||
- name: Upload Legacy Crash
|
||||
uses: actions/upload-artifact@v2
|
||||
if: steps.run.outcome == 'success'
|
||||
with:
|
||||
name: crash
|
||||
path: ./out/crash*
|
||||
- name: Fail on legacy crash
|
||||
if: success()
|
||||
run: |
|
||||
[ ! -e out/crash-* ]
|
||||
echo No legacy crash detected
|
6
.github/workflows/lint.yml
vendored
|
@ -34,12 +34,12 @@ jobs:
|
|||
python-version: 3.8
|
||||
|
||||
- name: Build system information
|
||||
run: python .github/workflows/system-info.py
|
||||
run: python3 .github/workflows/system-info.py
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install -U pip
|
||||
python -m pip install -U tox
|
||||
python3 -m pip install -U pip
|
||||
python3 -m pip install -U tox
|
||||
|
||||
- name: Lint
|
||||
run: tox -e lint
|
||||
|
|
1
.github/workflows/macos-install.sh
vendored
|
@ -9,6 +9,7 @@ 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 -U pytest-timeout
|
||||
python3 -m pip install pyroma
|
||||
python3 -m pip install test-image-results
|
||||
|
||||
|
|
2
.github/workflows/test-docker.yml
vendored
|
@ -41,7 +41,7 @@ jobs:
|
|||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Build system information
|
||||
run: python .github/workflows/system-info.py
|
||||
run: python3 .github/workflows/system-info.py
|
||||
|
||||
- name: Set up QEMU
|
||||
if: "matrix.qemu-arch"
|
||||
|
|
52
.github/workflows/test-valgrind.yml
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
name: Test Valgrind
|
||||
|
||||
# like the docker tests, but running valgrind only on *.c/*.h changes.
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "**.c"
|
||||
- "**.h"
|
||||
pull_request:
|
||||
paths:
|
||||
- "**.c"
|
||||
- "**.h"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
docker: [
|
||||
ubuntu-20.04-focal-amd64-valgrind,
|
||||
]
|
||||
dockerTag: [master]
|
||||
|
||||
name: ${{ matrix.docker }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Build system information
|
||||
run: python3 .github/workflows/system-info.py
|
||||
|
||||
- name: Docker pull
|
||||
run: |
|
||||
docker pull pythonpillow/${{ matrix.docker }}:${{ matrix.dockerTag }}
|
||||
|
||||
- name: Build and Run Valgrind
|
||||
run: |
|
||||
# The Pillow user in the docker container is UID 1000
|
||||
sudo chown -R 1000 $GITHUB_WORKSPACE
|
||||
docker run --name pillow_container -v $GITHUB_WORKSPACE:/Pillow pythonpillow/${{ matrix.docker }}:${{ matrix.dockerTag }}
|
||||
sudo chown -R runner $GITHUB_WORKSPACE
|
||||
|
||||
success:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
name: Valgrind Test Successful
|
||||
steps:
|
||||
- name: Success
|
||||
run: echo Valgrind Test Successful
|
18
.github/workflows/test-windows.yml
vendored
|
@ -57,8 +57,8 @@ jobs:
|
|||
- name: Print build system information
|
||||
run: python .github/workflows/system-info.py
|
||||
|
||||
- name: python -m pip install wheel pytest pytest-cov
|
||||
run: python -m pip install wheel pytest pytest-cov
|
||||
- name: python -m pip install wheel pytest pytest-cov pytest-timeout
|
||||
run: python -m pip install wheel pytest pytest-cov pytest-timeout
|
||||
|
||||
# TODO Remove when 3.8 / 3.9 includes setuptools 49.3.2+:
|
||||
- name: Upgrade setuptools
|
||||
|
@ -110,7 +110,7 @@ jobs:
|
|||
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||
run: "& winbuild\\build\\build_dep_libwebp.cmd"
|
||||
|
||||
# for FreeType CBDT font support
|
||||
# for FreeType CBDT/SBIX font support
|
||||
- name: Build dependencies / libpng
|
||||
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||
run: "& winbuild\\build\\build_dep_libpng.cmd"
|
||||
|
@ -137,14 +137,11 @@ jobs:
|
|||
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||
run: "& winbuild\\build\\build_dep_harfbuzz.cmd"
|
||||
|
||||
# Raqm dependencies
|
||||
- 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"
|
||||
|
||||
# trim ~150MB x 9
|
||||
- name: Optimize build cache
|
||||
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||
|
@ -174,7 +171,7 @@ jobs:
|
|||
if: failure()
|
||||
run: |
|
||||
mkdir -p Tests/errors
|
||||
shell: pwsh
|
||||
shell: bash
|
||||
|
||||
- name: Upload errors
|
||||
uses: actions/upload-artifact@v2
|
||||
|
@ -249,8 +246,6 @@ jobs:
|
|||
${{ matrix.package }}-python3-olefile \
|
||||
${{ matrix.package }}-python3-pip \
|
||||
${{ matrix.package }}-python3-pyqt5 \
|
||||
${{ matrix.package }}-python3-pytest \
|
||||
${{ matrix.package }}-python3-pytest-cov \
|
||||
${{ matrix.package }}-python3-setuptools \
|
||||
${{ matrix.package }}-freetype \
|
||||
${{ matrix.package }}-ghostscript \
|
||||
|
@ -263,7 +258,7 @@ jobs:
|
|||
${{ matrix.package }}-openjpeg2 \
|
||||
subversion
|
||||
|
||||
python3 -m pip install pyroma
|
||||
python3 -m pip install pyroma pytest pytest-cov
|
||||
|
||||
pushd depends && ./install_extra_test_images.sh && popd
|
||||
|
||||
|
@ -273,6 +268,7 @@ jobs:
|
|||
- name: Test Pillow
|
||||
run: |
|
||||
python3 selftest.py --installed
|
||||
python3 -c "from PIL import Image"
|
||||
python3 -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests
|
||||
|
||||
- name: Upload coverage
|
||||
|
|
3
.github/workflows/test.yml
vendored
|
@ -58,7 +58,7 @@ jobs:
|
|||
${{ matrix.os }}-${{ matrix.python-version }}-
|
||||
|
||||
- name: Build system information
|
||||
run: python .github/workflows/system-info.py
|
||||
run: python3 .github/workflows/system-info.py
|
||||
|
||||
- name: Install Linux dependencies
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
|
@ -92,7 +92,6 @@ jobs:
|
|||
if: failure()
|
||||
run: |
|
||||
mkdir -p Tests/errors
|
||||
shell: pwsh
|
||||
|
||||
- name: Upload errors
|
||||
uses: actions/upload-artifact@v2
|
||||
|
|
3
.gitignore
vendored
|
@ -88,3 +88,6 @@ Tests/images/jpeg2000
|
|||
Tests/images/msp
|
||||
Tests/images/picins
|
||||
Tests/images/sunraster
|
||||
|
||||
# pyinstaller
|
||||
*.spec
|
||||
|
|
107
CHANGES.rst
|
@ -2,7 +2,112 @@
|
|||
Changelog (Pillow)
|
||||
==================
|
||||
|
||||
8.1.0 (2020-01-02)
|
||||
8.2.0 (unreleased)
|
||||
------------------
|
||||
|
||||
- Fixed linear_gradient and radial_gradient I and F modes #5274
|
||||
[radarhere]
|
||||
|
||||
- Add support for reading TIFFs with PlanarConfiguration=2 #5364
|
||||
[kkopachev, wiredfool, nulano]
|
||||
|
||||
- Deprecated categories #5351
|
||||
[radarhere]
|
||||
|
||||
- Do not premultiply alpha when resizing with Image.NEAREST resampling #5304
|
||||
[nulano]
|
||||
|
||||
- Dynamically link FriBiDi instead of Raqm #5062
|
||||
[nulano]
|
||||
|
||||
- Allow fewer PNG palette entries than the bit depth maximum when saving #5330
|
||||
[radarhere]
|
||||
|
||||
- Use duration from info dictionary when saving WebP #5338
|
||||
[radarhere]
|
||||
|
||||
- Stop flattening EXIF IFD into getexif() #4947
|
||||
[radarhere, kkopachev]
|
||||
|
||||
- Replaced tiff_deflate with tiff_adobe_deflate compression when saving TIFF images #5343
|
||||
[radarhere]
|
||||
|
||||
- Save ICC profile from TIFF encoderinfo #5321
|
||||
[radarhere]
|
||||
|
||||
- Moved RGB fix inside ImageQt class #5268
|
||||
[radarhere]
|
||||
|
||||
- Allow alpha_composite destination to be negative #5313
|
||||
[radarhere]
|
||||
|
||||
- Ensure file is closed if it is opened by ImageQt.ImageQt #5260
|
||||
[radarhere]
|
||||
|
||||
- Added ImageDraw rounded_rectangle method #5208
|
||||
[radarhere]
|
||||
|
||||
- Added IPythonViewer #5289
|
||||
[radarhere, Kipkurui-mutai]
|
||||
|
||||
- Only draw each rectangle outline pixel once #5183
|
||||
[radarhere]
|
||||
|
||||
- Use mmap instead of built-in Win32 mapper #5224
|
||||
[radarhere, cgohlke]
|
||||
|
||||
- Handle PCX images with an odd stride #5214
|
||||
[radarhere]
|
||||
|
||||
- Only read different sizes for "Large Thumbnail" MPO frames #5168
|
||||
[radarhere]
|
||||
|
||||
- Added PyQt6 support #5258
|
||||
[radarhere]
|
||||
|
||||
- Changed Image.open formats parameter to be case-insensitive #5250
|
||||
[Piolie, radarhere]
|
||||
|
||||
- Deprecate Tk/Tcl 8.4, to be removed in Pillow 10 (2023-01-02) #5216
|
||||
[radarhere]
|
||||
|
||||
- Added tk version to pilinfo #5226
|
||||
[radarhere, nulano]
|
||||
|
||||
- Support for ignoring tests when running valgrind #5150
|
||||
[wiredfool, radarhere, hugovk]
|
||||
|
||||
- OSS-Fuzz support #5189
|
||||
[wiredfool, radarhere]
|
||||
|
||||
8.1.2 (2021-03-06)
|
||||
------------------
|
||||
|
||||
- Fix Memory DOS in BLP (CVE-2021-27921), ICNS (CVE-2021-27922) and ICO (CVE-2021-27923) Image Plugins
|
||||
[wiredfool]
|
||||
|
||||
8.1.1 (2021-03-01)
|
||||
------------------
|
||||
|
||||
- Use more specific regex chars to prevent ReDoS. CVE-2021-25292
|
||||
[hugovk]
|
||||
|
||||
- Fix OOB Read in TiffDecode.c, and check the tile validity before reading. CVE-2021-25291
|
||||
[wiredfool]
|
||||
|
||||
- Fix negative size read in TiffDecode.c. CVE-2021-25290
|
||||
[wiredfool]
|
||||
|
||||
- Fix OOB read in SgiRleDecode.c. CVE-2021-25293
|
||||
[wiredfool]
|
||||
|
||||
- Incorrect error code checking in TiffDecode.c. CVE-2021-25289
|
||||
[wiredfool]
|
||||
|
||||
- PyModule_AddObject fix for Python 3.10 #5194
|
||||
[radarhere]
|
||||
|
||||
8.1.0 (2021-01-02)
|
||||
------------------
|
||||
|
||||
- Fix TIFF OOB Write error. CVE-2020-35654 #5175
|
||||
|
|
|
@ -5,4 +5,5 @@ from io import BytesIO
|
|||
|
||||
from PIL import Image
|
||||
|
||||
Image.open(BytesIO(b"icns\x00\x00\x00\x10hang\x00\x00\x00\x00"))
|
||||
with Image.open(BytesIO(b"icns\x00\x00\x00\x10hang\x00\x00\x00\x00")):
|
||||
pass
|
||||
|
|
|
@ -5,4 +5,7 @@ from io import BytesIO
|
|||
|
||||
from PIL import Image
|
||||
|
||||
Image.open(BytesIO(b"\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang"))
|
||||
with Image.open(
|
||||
BytesIO(b"\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang")
|
||||
):
|
||||
pass
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import io
|
||||
import warnings
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def pytest_report_header(config):
|
||||
|
@ -10,3 +13,19 @@ def pytest_report_header(config):
|
|||
return out.getvalue()
|
||||
except Exception as e:
|
||||
return f"pytest_report_header failed: {e}"
|
||||
|
||||
|
||||
def pytest_configure(config):
|
||||
# We're marking some tests to ignore valgrind errors and XFAIL them.
|
||||
# Ensure that the mark is defined
|
||||
# even in cases where pytest-valgrind isn't installed
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error")
|
||||
try:
|
||||
getattr(pytest.mark, "valgrind_known_error")
|
||||
except Exception:
|
||||
config.addinivalue_line(
|
||||
"markers",
|
||||
"valgrind_known_error: Tests that have known issues with valgrind",
|
||||
)
|
||||
|
|
40
Tests/fonts/DejaVuSans/LICENSE.txt
Normal file
|
@ -0,0 +1,40 @@
|
|||
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.
|
||||
|
||||
DejaVu Fonts — License
|
||||
Fonts are © Bitstream (see below). DejaVu changes are in public domain. Explanation of copyright is on Gnome page on Bitstream Vera fonts. Glyphs imported from Arev fonts are © Tavmjung Bah (see below)
|
||||
|
||||
Bitstream Vera Fonts Copyright
|
||||
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces.
|
||||
|
||||
The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera".
|
||||
|
||||
This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names.
|
||||
|
||||
The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself.
|
||||
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.
|
||||
|
||||
Arev Fonts Copyright
|
||||
Original text
|
||||
|
||||
Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the modifications to the Bitstream Vera Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces.
|
||||
|
||||
The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Tavmjong Bah" or the word "Arev".
|
||||
|
||||
This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Tavmjong Bah Arev" names.
|
||||
|
||||
The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself.
|
||||
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of Tavmjong Bah shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from Tavmjong Bah. For further information, contact: tavmjong @ free . fr.
|
|
@ -15,8 +15,11 @@ 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.
|
||||
chromacheck-sbix.woff, from https://github.com/RoelN/ChromaCheck, under The MIT License (MIT), Copyright (c) 2018 Roel Nieskens, https://pixelambacht.nl Copyright (c) 2018 Google LLC
|
||||
|
||||
KhmerOSBattambang-Regular.ttf is licensed under LGPL-2.1 or later.
|
||||
|
||||
FreeMono.ttf is licensed under GPLv3.
|
||||
|
||||
10x20-ISO8859-1.pcf, from https://packages.ubuntu.com/xenial/xfonts-base
|
||||
|
||||
|
|
BIN
Tests/fonts/chromacheck-sbix.woff
Normal file
BIN
Tests/images/chromacheck-sbix.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Tests/images/chromacheck-sbix_mask.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Tests/images/crash-0c7e0e8e11ce787078f00b5b0ca409a167f070e0.tif
Normal file
BIN
Tests/images/crash-0e16d3bfb83be87356d026d66919deaefca44dac.tif
Normal file
BIN
Tests/images/crash-1152ec2d1a1a71395b6f2ce6721c38924d025bf3.tif
Normal file
BIN
Tests/images/crash-1185209cf7655b5aed8ae5e77784dfdd18ab59e9.tif
Normal file
BIN
Tests/images/crash-338516dbd2f0e83caddb8ce256c22db3bd6dc40f.tif
Normal file
BIN
Tests/images/crash-465703f71a0f0094873a3e0e82c9f798161171b8.sgi
Normal file
BIN
Tests/images/crash-4f085cc12ece8cde18758d42608bed6a2a2cfb1c.tif
Normal file
BIN
Tests/images/crash-63b1dffefc8c075ddc606c0a2f5fdc15ece78863.tif
Normal file
BIN
Tests/images/crash-64834657ee604b8797bf99eac6a194c124a9a8ba.sgi
Normal file
BIN
Tests/images/crash-754d9c7ec485ffb76a90eeaab191ef69a2a3a3cd.sgi
Normal file
BIN
Tests/images/crash-86214e58da443d2b80820cff9677a38a33dcbbca.tif
Normal file
BIN
Tests/images/crash-abcf1c97b8fe42a6c68f1fb0b978530c98d57ced.sgi
Normal file
BIN
Tests/images/crash-b82e64d4f3f76d7465b6af535283029eda211259.sgi
Normal file
BIN
Tests/images/crash-c1b2595b8b0b92cc5f38b6635e98e3a119ade807.sgi
Normal file
BIN
Tests/images/crash-db8bfa78b19721225425530c5946217720d7df4e.sgi
Normal file
BIN
Tests/images/crash-f46f5b2f43c370fe65706c11449f567ecc345e74.tif
Normal file
BIN
Tests/images/ignore_frame_size.mpo
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
Tests/images/imagedraw_rectangle_translucent_outline.png
Normal file
After Width: | Height: | Size: 235 B |
BIN
Tests/images/imagedraw_rounded_rectangle.png
Normal file
After Width: | Height: | Size: 934 B |
BIN
Tests/images/imagedraw_rounded_rectangle_both.png
Normal file
After Width: | Height: | Size: 530 B |
BIN
Tests/images/imagedraw_rounded_rectangle_x.png
Normal file
After Width: | Height: | Size: 567 B |
BIN
Tests/images/imagedraw_rounded_rectangle_y.png
Normal file
After Width: | Height: | Size: 528 B |
BIN
Tests/images/odd_stride.pcx
Normal file
BIN
Tests/images/oom-8ed3316a4109213ca96fb8a256a0bfefdece1461.icns
Normal file
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 117 KiB |
BIN
Tests/images/tiff_strip_planar_16bit_RGB.tiff
Normal file
BIN
Tests/images/tiff_strip_planar_16bit_RGBa.tiff
Normal file
BIN
Tests/images/tiff_strip_planar_lzw.tiff
Normal file
BIN
Tests/images/tiff_tiled_planar_16bit_RGB.tiff
Normal file
BIN
Tests/images/tiff_tiled_planar_16bit_RGBa.tiff
Normal file
BIN
Tests/images/tiff_tiled_planar_lzw.tiff
Normal file
48
Tests/oss-fuzz/build.sh
Executable file
|
@ -0,0 +1,48 @@
|
|||
#!/bin/bash -eu
|
||||
# Copyright 2020 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
python3 setup.py build --build-base=/tmp/build install
|
||||
|
||||
# Build fuzzers in $OUT.
|
||||
for fuzzer in $(find $SRC -name 'fuzz_*.py'); do
|
||||
fuzzer_basename=$(basename -s .py $fuzzer)
|
||||
fuzzer_package=${fuzzer_basename}.pkg
|
||||
pyinstaller \
|
||||
--add-binary /usr/local/lib/libjpeg.so.9:. \
|
||||
--add-binary /usr/local/lib/libfreetype.so.6:. \
|
||||
--add-binary /usr/local/lib/liblcms2.so.2:. \
|
||||
--add-binary /usr/local/lib/libopenjp2.so.7:. \
|
||||
--add-binary /usr/local/lib/libpng16.so.16:. \
|
||||
--add-binary /usr/local/lib/libtiff.so.5:. \
|
||||
--add-binary /usr/local/lib/libwebp.so.7:. \
|
||||
--add-binary /usr/local/lib/libwebpdemux.so.2:. \
|
||||
--add-binary /usr/local/lib/libwebpmux.so.3:. \
|
||||
--add-binary /usr/local/lib/libxcb.so.1:. \
|
||||
--distpath $OUT --onefile --name $fuzzer_package $fuzzer
|
||||
|
||||
# Create execution wrapper.
|
||||
echo "#!/bin/sh
|
||||
# LLVMFuzzerTestOneInput for fuzzer detection.
|
||||
this_dir=\$(dirname \"\$0\")
|
||||
LD_PRELOAD=\$this_dir/sanitizer_with_fuzzer.so \
|
||||
ASAN_OPTIONS=\$ASAN_OPTIONS:symbolize=1:external_symbolizer_path=\$this_dir/llvm-symbolizer:detect_leaks=0 \
|
||||
\$this_dir/$fuzzer_package \$@" > $OUT/$fuzzer_basename
|
||||
chmod u+x $OUT/$fuzzer_basename
|
||||
done
|
||||
|
||||
find Tests/images Tests/icc -print | zip -q $OUT/fuzz_pillow_seed_corpus.zip -@
|
||||
find Tests/fonts -print | zip -q $OUT/fuzz_font_seed_corpus.zip -@
|
33
Tests/oss-fuzz/build_dictionaries.sh
Executable file
|
@ -0,0 +1,33 @@
|
|||
#!/bin/bash -eu
|
||||
# Copyright 2020 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
# Generate image dictionaries here for each of the fuzzers and put them in the
|
||||
# $OUT directory, named for the fuzzer
|
||||
|
||||
git clone --depth 1 https://github.com/google/fuzzing
|
||||
cat fuzzing/dictionaries/bmp.dict \
|
||||
fuzzing/dictionaries/dds.dict \
|
||||
fuzzing/dictionaries/gif.dict \
|
||||
fuzzing/dictionaries/icns.dict \
|
||||
fuzzing/dictionaries/jpeg.dict \
|
||||
fuzzing/dictionaries/jpeg2000.dict \
|
||||
fuzzing/dictionaries/pbm.dict \
|
||||
fuzzing/dictionaries/png.dict \
|
||||
fuzzing/dictionaries/psd.dict \
|
||||
fuzzing/dictionaries/tiff.dict \
|
||||
fuzzing/dictionaries/webp.dict \
|
||||
> $OUT/fuzz_pillow.dict
|
40
Tests/oss-fuzz/fuzz_font.py
Executable file
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
# Copyright 2020 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import sys
|
||||
|
||||
import atheris_no_libfuzzer as atheris
|
||||
import fuzzers
|
||||
|
||||
|
||||
def TestOneInput(data):
|
||||
try:
|
||||
fuzzers.fuzz_font(data)
|
||||
except Exception:
|
||||
# We're catching all exceptions because Pillow's exceptions are
|
||||
# directly inheriting from Exception.
|
||||
return
|
||||
return
|
||||
|
||||
|
||||
def main():
|
||||
fuzzers.enable_decompressionbomb_error()
|
||||
atheris.Setup(sys.argv, TestOneInput, enable_python_coverage=True)
|
||||
atheris.Fuzz()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
40
Tests/oss-fuzz/fuzz_pillow.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
# Copyright 2020 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import sys
|
||||
|
||||
import atheris_no_libfuzzer as atheris
|
||||
import fuzzers
|
||||
|
||||
|
||||
def TestOneInput(data):
|
||||
try:
|
||||
fuzzers.fuzz_image(data)
|
||||
except Exception:
|
||||
# We're catching all exceptions because Pillow's exceptions are
|
||||
# directly inheriting from Exception.
|
||||
return
|
||||
return
|
||||
|
||||
|
||||
def main():
|
||||
fuzzers.enable_decompressionbomb_error()
|
||||
atheris.Setup(sys.argv, TestOneInput, enable_python_coverage=True)
|
||||
atheris.Fuzz()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
36
Tests/oss-fuzz/fuzzers.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
import io
|
||||
import warnings
|
||||
|
||||
from PIL import Image, ImageDraw, ImageFile, ImageFilter, ImageFont
|
||||
|
||||
|
||||
def enable_decompressionbomb_error():
|
||||
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||
warnings.filterwarnings("ignore")
|
||||
warnings.simplefilter("error", Image.DecompressionBombWarning)
|
||||
|
||||
|
||||
def fuzz_image(data):
|
||||
# This will fail on some images in the corpus, as we have many
|
||||
# invalid images in the test suite.
|
||||
with Image.open(io.BytesIO(data)) as im:
|
||||
im.rotate(45)
|
||||
im.filter(ImageFilter.DETAIL)
|
||||
im.save(io.BytesIO(), "BMP")
|
||||
|
||||
|
||||
def fuzz_font(data):
|
||||
wrapper = io.BytesIO(data)
|
||||
try:
|
||||
font = ImageFont.truetype(wrapper)
|
||||
except OSError:
|
||||
# Catch pcf/pilfonts/random garbage here. They return
|
||||
# different font objects.
|
||||
return
|
||||
|
||||
font.getsize_multiline("ABC\nAaaa")
|
||||
font.getmask("test text")
|
||||
with Image.new(mode="RGBA", size=(200, 200)) as im:
|
||||
draw = ImageDraw.Draw(im)
|
||||
draw.multiline_textsize("ABC\nAaaa", font, stroke_width=2)
|
||||
draw.text((10, 10), "Test Text", font=font, fill="#000")
|
53
Tests/oss-fuzz/test_fuzzers.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
import subprocess
|
||||
import sys
|
||||
|
||||
import fuzzers
|
||||
import pytest
|
||||
|
||||
from PIL import Image
|
||||
|
||||
if sys.platform.startswith("win32"):
|
||||
pytest.skip("Fuzzer is linux only", allow_module_level=True)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
subprocess.check_output("find Tests/images -type f", shell=True).split(b"\n"),
|
||||
)
|
||||
def test_fuzz_images(path):
|
||||
fuzzers.enable_decompressionbomb_error()
|
||||
try:
|
||||
with open(path, "rb") as f:
|
||||
fuzzers.fuzz_image(f.read())
|
||||
assert True
|
||||
except (
|
||||
OSError,
|
||||
SyntaxError,
|
||||
MemoryError,
|
||||
ValueError,
|
||||
NotImplementedError,
|
||||
OverflowError,
|
||||
):
|
||||
# Known exceptions that are through from Pillow
|
||||
assert True
|
||||
except (
|
||||
Image.DecompressionBombError,
|
||||
Image.DecompressionBombWarning,
|
||||
Image.UnidentifiedImageError,
|
||||
):
|
||||
# Known Image.* exceptions
|
||||
assert True
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"path", subprocess.check_output("find Tests/fonts -type f", shell=True).split(b"\n")
|
||||
)
|
||||
def test_fuzz_fonts(path):
|
||||
if not path:
|
||||
return
|
||||
with open(path, "rb") as f:
|
||||
try:
|
||||
fuzzers.fuzz_font(f.read())
|
||||
except (Image.DecompressionBombError, Image.DecompressionBombWarning):
|
||||
pass
|
||||
assert True
|
|
@ -20,7 +20,7 @@ def test_bad():
|
|||
either"""
|
||||
for f in get_files("b"):
|
||||
|
||||
def open(f):
|
||||
with pytest.warns(None) as record:
|
||||
try:
|
||||
with Image.open(f) as im:
|
||||
im.load()
|
||||
|
@ -28,7 +28,7 @@ def test_bad():
|
|||
pass
|
||||
|
||||
# Assert that there is no unclosed file warning
|
||||
pytest.warns(None, open, f)
|
||||
assert not record
|
||||
|
||||
|
||||
def test_questionable():
|
||||
|
|
|
@ -54,15 +54,18 @@ class TestDecompressionBomb:
|
|||
|
||||
def test_exception_ico(self):
|
||||
with pytest.raises(Image.DecompressionBombError):
|
||||
Image.open("Tests/images/decompression_bomb.ico")
|
||||
with Image.open("Tests/images/decompression_bomb.ico"):
|
||||
pass
|
||||
|
||||
def test_exception_gif(self):
|
||||
with pytest.raises(Image.DecompressionBombError):
|
||||
Image.open("Tests/images/decompression_bomb.gif")
|
||||
with Image.open("Tests/images/decompression_bomb.gif"):
|
||||
pass
|
||||
|
||||
def test_exception_bmp(self):
|
||||
with pytest.raises(Image.DecompressionBombError):
|
||||
Image.open("Tests/images/bmp/b/reallybig.bmp")
|
||||
with Image.open("Tests/images/bmp/b/reallybig.bmp"):
|
||||
pass
|
||||
|
||||
|
||||
class TestDecompressionCrop:
|
||||
|
|
|
@ -1,21 +1,18 @@
|
|||
from PIL import Image
|
||||
|
||||
from .helper import assert_image_equal
|
||||
from .helper import assert_image_equal_tofile
|
||||
|
||||
|
||||
def test_load_blp2_raw():
|
||||
with Image.open("Tests/images/blp/blp2_raw.blp") as im:
|
||||
with Image.open("Tests/images/blp/blp2_raw.png") as target:
|
||||
assert_image_equal(im, target)
|
||||
assert_image_equal_tofile(im, "Tests/images/blp/blp2_raw.png")
|
||||
|
||||
|
||||
def test_load_blp2_dxt1():
|
||||
with Image.open("Tests/images/blp/blp2_dxt1.blp") as im:
|
||||
with Image.open("Tests/images/blp/blp2_dxt1.png") as target:
|
||||
assert_image_equal(im, target)
|
||||
assert_image_equal_tofile(im, "Tests/images/blp/blp2_dxt1.png")
|
||||
|
||||
|
||||
def test_load_blp2_dxt1a():
|
||||
with Image.open("Tests/images/blp/blp2_dxt1a.blp") as im:
|
||||
with Image.open("Tests/images/blp/blp2_dxt1a.png") as target:
|
||||
assert_image_equal(im, target)
|
||||
assert_image_equal_tofile(im, "Tests/images/blp/blp2_dxt1a.png")
|
||||
|
|
|
@ -4,7 +4,7 @@ import pytest
|
|||
|
||||
from PIL import BmpImagePlugin, Image
|
||||
|
||||
from .helper import assert_image_equal, hopper
|
||||
from .helper import assert_image_equal, assert_image_equal_tofile, hopper
|
||||
|
||||
|
||||
def test_sanity(tmp_path):
|
||||
|
@ -111,8 +111,7 @@ def test_load_dib():
|
|||
assert im.format == "DIB"
|
||||
assert im.get_format_mimetype() == "image/bmp"
|
||||
|
||||
with Image.open("Tests/images/clipboard_target.png") as target:
|
||||
assert_image_equal(im, target)
|
||||
assert_image_equal_tofile(im, "Tests/images/clipboard_target.png")
|
||||
|
||||
|
||||
def test_save_dib(tmp_path):
|
||||
|
@ -136,5 +135,4 @@ def test_rgba_bitfields():
|
|||
b, g, r = im.split()[1:]
|
||||
im = Image.merge("RGB", (r, g, b))
|
||||
|
||||
with Image.open("Tests/images/bmp/q/rgb32bf-xbgr.bmp") as target:
|
||||
assert_image_equal(im, target)
|
||||
assert_image_equal_tofile(im, "Tests/images/bmp/q/rgb32bf-xbgr.bmp")
|
||||
|
|
|
@ -31,20 +31,20 @@ def test_unclosed_file():
|
|||
|
||||
|
||||
def test_closed_file():
|
||||
def open():
|
||||
with pytest.warns(None) as record:
|
||||
im = Image.open(TEST_FILE)
|
||||
im.load()
|
||||
im.close()
|
||||
|
||||
pytest.warns(None, open)
|
||||
assert not record
|
||||
|
||||
|
||||
def test_context_manager():
|
||||
def open():
|
||||
with pytest.warns(None) as record:
|
||||
with Image.open(TEST_FILE) as im:
|
||||
im.load()
|
||||
|
||||
pytest.warns(None, open)
|
||||
assert not record
|
||||
|
||||
|
||||
def test_invalid_file():
|
||||
|
|
|
@ -5,7 +5,7 @@ import pytest
|
|||
|
||||
from PIL import DdsImagePlugin, Image
|
||||
|
||||
from .helper import assert_image_equal
|
||||
from .helper import assert_image_equal, assert_image_equal_tofile
|
||||
|
||||
TEST_FILE_DXT1 = "Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.dds"
|
||||
TEST_FILE_DXT3 = "Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds"
|
||||
|
@ -41,22 +41,20 @@ def test_sanity_dxt5():
|
|||
assert im.mode == "RGBA"
|
||||
assert im.size == (256, 256)
|
||||
|
||||
with Image.open(TEST_FILE_DXT5.replace(".dds", ".png")) as target:
|
||||
assert_image_equal(target, im)
|
||||
assert_image_equal_tofile(im, TEST_FILE_DXT5.replace(".dds", ".png"))
|
||||
|
||||
|
||||
def test_sanity_dxt3():
|
||||
"""Check DXT3 images can be opened"""
|
||||
|
||||
with Image.open(TEST_FILE_DXT3.replace(".dds", ".png")) as target:
|
||||
with Image.open(TEST_FILE_DXT3) as im:
|
||||
im.load()
|
||||
with Image.open(TEST_FILE_DXT3) as im:
|
||||
im.load()
|
||||
|
||||
assert im.format == "DDS"
|
||||
assert im.mode == "RGBA"
|
||||
assert im.size == (256, 256)
|
||||
assert im.format == "DDS"
|
||||
assert im.mode == "RGBA"
|
||||
assert im.size == (256, 256)
|
||||
|
||||
assert_image_equal(target, im)
|
||||
assert_image_equal_tofile(im, TEST_FILE_DXT3.replace(".dds", ".png"))
|
||||
|
||||
|
||||
def test_dx10_bc7():
|
||||
|
@ -69,8 +67,7 @@ def test_dx10_bc7():
|
|||
assert im.mode == "RGBA"
|
||||
assert im.size == (256, 256)
|
||||
|
||||
with Image.open(TEST_FILE_DX10_BC7.replace(".dds", ".png")) as target:
|
||||
assert_image_equal(target, im)
|
||||
assert_image_equal_tofile(im, TEST_FILE_DX10_BC7.replace(".dds", ".png"))
|
||||
|
||||
|
||||
def test_dx10_bc7_unorm_srgb():
|
||||
|
@ -84,10 +81,9 @@ def test_dx10_bc7_unorm_srgb():
|
|||
assert im.size == (16, 16)
|
||||
assert im.info["gamma"] == 1 / 2.2
|
||||
|
||||
with Image.open(
|
||||
TEST_FILE_DX10_BC7_UNORM_SRGB.replace(".dds", ".png")
|
||||
) as target:
|
||||
assert_image_equal(target, im)
|
||||
assert_image_equal_tofile(
|
||||
im, TEST_FILE_DX10_BC7_UNORM_SRGB.replace(".dds", ".png")
|
||||
)
|
||||
|
||||
|
||||
def test_dx10_r8g8b8a8():
|
||||
|
@ -100,8 +96,7 @@ def test_dx10_r8g8b8a8():
|
|||
assert im.mode == "RGBA"
|
||||
assert im.size == (256, 256)
|
||||
|
||||
with Image.open(TEST_FILE_DX10_R8G8B8A8.replace(".dds", ".png")) as target:
|
||||
assert_image_equal(target, im)
|
||||
assert_image_equal_tofile(im, TEST_FILE_DX10_R8G8B8A8.replace(".dds", ".png"))
|
||||
|
||||
|
||||
def test_dx10_r8g8b8a8_unorm_srgb():
|
||||
|
@ -115,15 +110,15 @@ def test_dx10_r8g8b8a8_unorm_srgb():
|
|||
assert im.size == (16, 16)
|
||||
assert im.info["gamma"] == 1 / 2.2
|
||||
|
||||
with Image.open(
|
||||
TEST_FILE_DX10_R8G8B8A8_UNORM_SRGB.replace(".dds", ".png")
|
||||
) as target:
|
||||
assert_image_equal(target, im)
|
||||
assert_image_equal_tofile(
|
||||
im, TEST_FILE_DX10_R8G8B8A8_UNORM_SRGB.replace(".dds", ".png")
|
||||
)
|
||||
|
||||
|
||||
def test_unimplemented_dxgi_format():
|
||||
with pytest.raises(NotImplementedError):
|
||||
Image.open("Tests/images/unimplemented_dxgi_format.dds")
|
||||
with Image.open("Tests/images/unimplemented_dxgi_format.dds"):
|
||||
pass
|
||||
|
||||
|
||||
def test_uncompressed_rgb():
|
||||
|
@ -136,8 +131,9 @@ def test_uncompressed_rgb():
|
|||
assert im.mode == "RGBA"
|
||||
assert im.size == (800, 600)
|
||||
|
||||
with Image.open(TEST_FILE_UNCOMPRESSED_RGB.replace(".dds", ".png")) as target:
|
||||
assert_image_equal(target, im)
|
||||
assert_image_equal_tofile(
|
||||
im, TEST_FILE_UNCOMPRESSED_RGB.replace(".dds", ".png")
|
||||
)
|
||||
|
||||
|
||||
def test__validate_true():
|
||||
|
@ -170,7 +166,8 @@ def test_short_header():
|
|||
img_file = f.read()
|
||||
|
||||
def short_header():
|
||||
Image.open(BytesIO(img_file[:119]))
|
||||
with Image.open(BytesIO(img_file[:119])):
|
||||
pass # pragma: no cover
|
||||
|
||||
with pytest.raises(OSError):
|
||||
short_header()
|
||||
|
@ -192,4 +189,5 @@ def test_short_file():
|
|||
|
||||
def test_unimplemented_pixel_format():
|
||||
with pytest.raises(NotImplementedError):
|
||||
Image.open("Tests/images/unimplemented_pixel_format.dds")
|
||||
with Image.open("Tests/images/unimplemented_pixel_format.dds"):
|
||||
pass
|
||||
|
|
|
@ -4,7 +4,12 @@ import pytest
|
|||
|
||||
from PIL import EpsImagePlugin, Image, features
|
||||
|
||||
from .helper import assert_image_similar, hopper, skip_unless_feature
|
||||
from .helper import (
|
||||
assert_image_similar,
|
||||
assert_image_similar_tofile,
|
||||
hopper,
|
||||
skip_unless_feature,
|
||||
)
|
||||
|
||||
HAS_GHOSTSCRIPT = EpsImagePlugin.has_ghostscript()
|
||||
|
||||
|
@ -59,6 +64,7 @@ def test_invalid_file():
|
|||
EpsImagePlugin.EpsImageFile(invalid_file)
|
||||
|
||||
|
||||
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||
def test_cmyk():
|
||||
with Image.open("Tests/images/pil_sample_cmyk.eps") as cmyk_image:
|
||||
|
@ -71,8 +77,9 @@ def test_cmyk():
|
|||
assert cmyk_image.mode == "RGB"
|
||||
|
||||
if features.check("jpg"):
|
||||
with Image.open("Tests/images/pil_sample_rgb.jpg") as target:
|
||||
assert_image_similar(cmyk_image, target, 10)
|
||||
assert_image_similar_tofile(
|
||||
cmyk_image, "Tests/images/pil_sample_rgb.jpg", 10
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||
|
|
|
@ -2,7 +2,7 @@ import pytest
|
|||
|
||||
from PIL import FliImagePlugin, Image
|
||||
|
||||
from .helper import assert_image_equal, is_pypy
|
||||
from .helper import assert_image_equal_tofile, is_pypy
|
||||
|
||||
# created as an export of a palette image from Gimp2.6
|
||||
# save as...-> hopper.fli, default options.
|
||||
|
@ -38,20 +38,20 @@ def test_unclosed_file():
|
|||
|
||||
|
||||
def test_closed_file():
|
||||
def open():
|
||||
with pytest.warns(None) as record:
|
||||
im = Image.open(static_test_file)
|
||||
im.load()
|
||||
im.close()
|
||||
|
||||
pytest.warns(None, open)
|
||||
assert not record
|
||||
|
||||
|
||||
def test_context_manager():
|
||||
def open():
|
||||
with pytest.warns(None) as record:
|
||||
with Image.open(static_test_file) as im:
|
||||
im.load()
|
||||
|
||||
pytest.warns(None, open)
|
||||
assert not record
|
||||
|
||||
|
||||
def test_tell():
|
||||
|
@ -122,5 +122,4 @@ def test_seek():
|
|||
with Image.open(animated_test_file) as im:
|
||||
im.seek(50)
|
||||
|
||||
with Image.open("Tests/images/a_fli.png") as expected:
|
||||
assert_image_equal(im, expected)
|
||||
assert_image_equal_tofile(im, "Tests/images/a_fli.png")
|
||||
|
|
|
@ -21,4 +21,5 @@ def test_invalid_file():
|
|||
|
||||
def test_fpx_invalid_number_of_bands():
|
||||
with pytest.raises(OSError, match="Invalid number of bands"):
|
||||
Image.open("Tests/images/input_bw_five_bands.fpx")
|
||||
with Image.open("Tests/images/input_bw_five_bands.fpx"):
|
||||
pass
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
from PIL import Image
|
||||
|
||||
from .helper import assert_image_equal, assert_image_similar
|
||||
from .helper import assert_image_equal_tofile, assert_image_similar
|
||||
|
||||
|
||||
def test_load_raw():
|
||||
with Image.open("Tests/images/ftex_uncompressed.ftu") as im:
|
||||
with Image.open("Tests/images/ftex_uncompressed.png") as target:
|
||||
assert_image_equal(im, target)
|
||||
assert_image_equal_tofile(im, "Tests/images/ftex_uncompressed.png")
|
||||
|
||||
|
||||
def test_load_dxt1():
|
||||
|
|
|
@ -2,7 +2,7 @@ import pytest
|
|||
|
||||
from PIL import GbrImagePlugin, Image
|
||||
|
||||
from .helper import assert_image_equal
|
||||
from .helper import assert_image_equal_tofile
|
||||
|
||||
|
||||
def test_invalid_file():
|
||||
|
@ -14,13 +14,11 @@ def test_invalid_file():
|
|||
|
||||
def test_gbr_file():
|
||||
with Image.open("Tests/images/gbr.gbr") as im:
|
||||
with Image.open("Tests/images/gbr.png") as target:
|
||||
assert_image_equal(target, im)
|
||||
assert_image_equal_tofile(im, "Tests/images/gbr.png")
|
||||
|
||||
|
||||
def test_multiple_load_operations():
|
||||
with Image.open("Tests/images/gbr.gbr") as im:
|
||||
im.load()
|
||||
im.load()
|
||||
with Image.open("Tests/images/gbr.png") as target:
|
||||
assert_image_equal(target, im)
|
||||
assert_image_equal_tofile(im, "Tests/images/gbr.png")
|
||||
|
|
|
@ -6,6 +6,7 @@ from PIL import GifImagePlugin, Image, ImageDraw, ImagePalette, features
|
|||
|
||||
from .helper import (
|
||||
assert_image_equal,
|
||||
assert_image_equal_tofile,
|
||||
assert_image_similar,
|
||||
hopper,
|
||||
is_pypy,
|
||||
|
@ -38,20 +39,20 @@ def test_unclosed_file():
|
|||
|
||||
|
||||
def test_closed_file():
|
||||
def open():
|
||||
with pytest.warns(None) as record:
|
||||
im = Image.open(TEST_GIF)
|
||||
im.load()
|
||||
im.close()
|
||||
|
||||
pytest.warns(None, open)
|
||||
assert not record
|
||||
|
||||
|
||||
def test_context_manager():
|
||||
def open():
|
||||
with pytest.warns(None) as record:
|
||||
with Image.open(TEST_GIF) as im:
|
||||
im.load()
|
||||
|
||||
pytest.warns(None, open)
|
||||
assert not record
|
||||
|
||||
|
||||
def test_invalid_file():
|
||||
|
@ -317,8 +318,7 @@ def test_dispose_none_load_end():
|
|||
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)
|
||||
assert_image_equal_tofile(img, "Tests/images/dispose_none_load_end_second.gif")
|
||||
|
||||
|
||||
def test_dispose_background():
|
||||
|
@ -629,8 +629,7 @@ def test_comment_over_255(tmp_path):
|
|||
|
||||
def test_zero_comment_subblocks():
|
||||
with Image.open("Tests/images/hopper_zero_comment_subblocks.gif") as im:
|
||||
with Image.open(TEST_GIF) as expected:
|
||||
assert_image_equal(im, expected)
|
||||
assert_image_equal_tofile(im, TEST_GIF)
|
||||
|
||||
|
||||
def test_version(tmp_path):
|
||||
|
|
|
@ -5,7 +5,7 @@ import pytest
|
|||
|
||||
from PIL import IcnsImagePlugin, Image, features
|
||||
|
||||
from .helper import assert_image_equal, assert_image_similar
|
||||
from .helper import assert_image_equal, assert_image_similar_tofile
|
||||
|
||||
# sample icon file
|
||||
TEST_FILE = "Tests/images/pillow.icns"
|
||||
|
@ -19,7 +19,9 @@ def test_sanity():
|
|||
with Image.open(TEST_FILE) as im:
|
||||
|
||||
# Assert that there is no unclosed file warning
|
||||
pytest.warns(None, im.load)
|
||||
with pytest.warns(None) as record:
|
||||
im.load()
|
||||
assert not record
|
||||
|
||||
assert im.mode == "RGBA"
|
||||
assert im.size == (1024, 1024)
|
||||
|
@ -47,8 +49,7 @@ def test_save_append_images(tmp_path):
|
|||
with Image.open(TEST_FILE) as im:
|
||||
im.save(temp_file, append_images=[provided_im])
|
||||
|
||||
with Image.open(temp_file) as reread:
|
||||
assert_image_similar(reread, im, 1)
|
||||
assert_image_similar_tofile(im, temp_file, 1)
|
||||
|
||||
with Image.open(temp_file) as reread:
|
||||
reread.size = (16, 16, 2)
|
||||
|
@ -139,3 +140,11 @@ def test_not_an_icns_file():
|
|||
with io.BytesIO(b"invalid\n") as fp:
|
||||
with pytest.raises(SyntaxError):
|
||||
IcnsImagePlugin.IcnsFile(fp)
|
||||
|
||||
|
||||
def test_icns_decompression_bomb():
|
||||
with Image.open(
|
||||
"Tests/images/oom-8ed3316a4109213ca96fb8a256a0bfefdece1461.icns"
|
||||
) as im:
|
||||
with pytest.raises(Image.DecompressionBombError):
|
||||
im.load()
|
||||
|
|
|
@ -4,7 +4,7 @@ import pytest
|
|||
|
||||
from PIL import IcoImagePlugin, Image, ImageDraw
|
||||
|
||||
from .helper import assert_image_equal, hopper
|
||||
from .helper import assert_image_equal, assert_image_equal_tofile, hopper
|
||||
|
||||
TEST_ICO_FILE = "Tests/images/hopper.ico"
|
||||
|
||||
|
@ -120,5 +120,4 @@ def test_draw_reloaded(tmp_path):
|
|||
|
||||
with Image.open(outfile) as im:
|
||||
im.save("Tests/images/hopper_draw.ico")
|
||||
with Image.open("Tests/images/hopper_draw.ico") as reloaded:
|
||||
assert_image_equal(im, reloaded)
|
||||
assert_image_equal_tofile(im, "Tests/images/hopper_draw.ico")
|
||||
|
|
|
@ -4,7 +4,7 @@ import pytest
|
|||
|
||||
from PIL import Image, ImImagePlugin
|
||||
|
||||
from .helper import assert_image_equal, hopper, is_pypy
|
||||
from .helper import assert_image_equal_tofile, hopper, is_pypy
|
||||
|
||||
# sample im
|
||||
TEST_IM = "Tests/images/hopper.im"
|
||||
|
@ -35,20 +35,20 @@ def test_unclosed_file():
|
|||
|
||||
|
||||
def test_closed_file():
|
||||
def open():
|
||||
with pytest.warns(None) as record:
|
||||
im = Image.open(TEST_IM)
|
||||
im.load()
|
||||
im.close()
|
||||
|
||||
pytest.warns(None, open)
|
||||
assert not record
|
||||
|
||||
|
||||
def test_context_manager():
|
||||
def open():
|
||||
with pytest.warns(None) as record:
|
||||
with Image.open(TEST_IM) as im:
|
||||
im.load()
|
||||
|
||||
pytest.warns(None, open)
|
||||
assert not record
|
||||
|
||||
|
||||
def test_tell():
|
||||
|
@ -86,8 +86,7 @@ def test_roundtrip(tmp_path):
|
|||
out = str(tmp_path / "temp.im")
|
||||
im = hopper(mode)
|
||||
im.save(out)
|
||||
with Image.open(out) as reread:
|
||||
assert_image_equal(reread, im)
|
||||
assert_image_equal_tofile(im, out)
|
||||
|
||||
for mode in ["RGB", "P", "PA"]:
|
||||
roundtrip(mode)
|
||||
|
|
|
@ -17,7 +17,9 @@ from PIL import (
|
|||
from .helper import (
|
||||
assert_image,
|
||||
assert_image_equal,
|
||||
assert_image_equal_tofile,
|
||||
assert_image_similar,
|
||||
assert_image_similar_tofile,
|
||||
cjpeg_available,
|
||||
djpeg_available,
|
||||
hopper,
|
||||
|
@ -114,6 +116,7 @@ class TestFileJpeg:
|
|||
assert test(100, 200) == (100, 200)
|
||||
assert test(0) is None # square pixels
|
||||
|
||||
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
||||
def test_icc(self, tmp_path):
|
||||
# Test ICC support
|
||||
with Image.open("Tests/images/rgb.jpg") as im1:
|
||||
|
@ -153,6 +156,7 @@ class TestFileJpeg:
|
|||
test(ImageFile.MAXBLOCK + 1) # full buffer block plus one byte
|
||||
test(ImageFile.MAXBLOCK * 4 + 3) # large block
|
||||
|
||||
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
||||
def test_large_icc_meta(self, tmp_path):
|
||||
# https://github.com/python-pillow/Pillow/issues/148
|
||||
# Sometimes the meta data on the icc_profile block is bigger than
|
||||
|
@ -260,11 +264,11 @@ class TestFileJpeg:
|
|||
assert exif[0x0112] == Image.TRANSVERSE
|
||||
|
||||
# Assert that the GPS IFD is present and empty
|
||||
assert exif[0x8825] == {}
|
||||
assert exif.get_ifd(0x8825) == {}
|
||||
|
||||
transposed = ImageOps.exif_transpose(im)
|
||||
exif = transposed.getexif()
|
||||
assert exif[0x8825] == {}
|
||||
assert exif.get_ifd(0x8825) == {}
|
||||
|
||||
# Assert that it was transposed
|
||||
assert 0x0112 not in exif
|
||||
|
@ -419,6 +423,7 @@ class TestFileJpeg:
|
|||
with Image.open(filename):
|
||||
pass
|
||||
|
||||
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
||||
def test_truncated_jpeg_should_read_all_the_data(self):
|
||||
filename = "Tests/images/truncated_jpeg.jpg"
|
||||
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||
|
@ -437,6 +442,7 @@ class TestFileJpeg:
|
|||
with pytest.raises(OSError):
|
||||
im.load()
|
||||
|
||||
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
||||
def test_qtables(self, tmp_path):
|
||||
def _n_qtables_helper(n, test_file):
|
||||
with Image.open(test_file) as im:
|
||||
|
@ -573,7 +579,7 @@ class TestFileJpeg:
|
|||
def test_load_djpeg(self):
|
||||
with Image.open(TEST_FILE) as img:
|
||||
img.load_djpeg()
|
||||
assert_image_similar(img, Image.open(TEST_FILE), 5)
|
||||
assert_image_similar_tofile(img, TEST_FILE, 5)
|
||||
|
||||
@pytest.mark.skipif(not cjpeg_available(), reason="cjpeg not available")
|
||||
def test_save_cjpeg(self, tmp_path):
|
||||
|
@ -581,7 +587,7 @@ class TestFileJpeg:
|
|||
tempfile = str(tmp_path / "temp.jpg")
|
||||
JpegImagePlugin._save_cjpeg(img, 0, tempfile)
|
||||
# Default save quality is 75%, so a tiny bit of difference is alright
|
||||
assert_image_similar(img, Image.open(tempfile), 17)
|
||||
assert_image_similar_tofile(img, tempfile, 17)
|
||||
|
||||
def test_no_duplicate_0x1001_tag(self):
|
||||
# Arrange
|
||||
|
@ -720,6 +726,7 @@ class TestFileJpeg:
|
|||
# OSError for unidentified image.
|
||||
assert im.info.get("dpi") == (72, 72)
|
||||
|
||||
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
||||
def test_exif_x_resolution(self, tmp_path):
|
||||
with Image.open("Tests/images/flower.jpg") as im:
|
||||
exif = im.getexif()
|
||||
|
@ -728,7 +735,7 @@ class TestFileJpeg:
|
|||
out = str(tmp_path / "out.jpg")
|
||||
with pytest.warns(None) as record:
|
||||
im.save(out, exif=exif)
|
||||
assert len(record) == 0
|
||||
assert not record
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert reloaded.getexif()[282] == 180
|
||||
|
@ -750,6 +757,7 @@ class TestFileJpeg:
|
|||
# Act / Assert
|
||||
assert im._getexif()[306] == "2017:03:13 23:03:09"
|
||||
|
||||
@pytest.mark.valgrind_known_error(reason="Backtrace in Python Core")
|
||||
def test_photoshop(self):
|
||||
with Image.open("Tests/images/photoshop-200dpi.jpg") as im:
|
||||
assert im.info["photoshop"][0x03ED] == {
|
||||
|
@ -761,8 +769,7 @@ class TestFileJpeg:
|
|||
|
||||
# Test that the image can still load, even with broken Photoshop data
|
||||
# This image had the APP13 length hexedited to be smaller
|
||||
with Image.open("Tests/images/photoshop-200dpi-broken.jpg") as im_broken:
|
||||
assert_image_equal(im_broken, im)
|
||||
assert_image_equal_tofile(im, "Tests/images/photoshop-200dpi-broken.jpg")
|
||||
|
||||
# This image does not contain a Photoshop header string
|
||||
with Image.open("Tests/images/app13.jpg") as im:
|
||||
|
@ -792,7 +799,8 @@ class TestFileJpeg:
|
|||
|
||||
buffer.read = read
|
||||
with pytest.raises(UnidentifiedImageError):
|
||||
Image.open(buffer)
|
||||
with Image.open(buffer):
|
||||
pass
|
||||
|
||||
# Assert the entire file has not been read
|
||||
assert 0 < buffer.max_pos < size
|
||||
|
|
|
@ -8,6 +8,7 @@ from PIL import Image, ImageFile, Jpeg2KImagePlugin, features
|
|||
from .helper import (
|
||||
assert_image_equal,
|
||||
assert_image_similar,
|
||||
assert_image_similar_tofile,
|
||||
is_big_endian,
|
||||
skip_unless_feature,
|
||||
)
|
||||
|
@ -62,9 +63,7 @@ def test_invalid_file():
|
|||
def test_bytesio():
|
||||
with open("Tests/images/test-card-lossless.jp2", "rb") as f:
|
||||
data = BytesIO(f.read())
|
||||
with Image.open(data) as im:
|
||||
im.load()
|
||||
assert_image_similar(im, test_card, 1.0e-3)
|
||||
assert_image_similar_tofile(test_card, data, 1.0e-3)
|
||||
|
||||
|
||||
# These two test pre-written JPEG 2000 files that were not written with
|
||||
|
@ -80,9 +79,9 @@ def test_lossless(tmp_path):
|
|||
|
||||
|
||||
def test_lossy_tiled():
|
||||
with Image.open("Tests/images/test-card-lossy-tiled.jp2") as im:
|
||||
im.load()
|
||||
assert_image_similar(im, test_card, 2.0)
|
||||
assert_image_similar_tofile(
|
||||
test_card, "Tests/images/test-card-lossy-tiled.jp2", 2.0
|
||||
)
|
||||
|
||||
|
||||
def test_lossless_rt():
|
||||
|
@ -193,15 +192,13 @@ def test_16bit_monochrome_has_correct_mode():
|
|||
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
|
||||
def test_16bit_monochrome_jp2_like_tiff():
|
||||
with Image.open("Tests/images/16bit.cropped.tif") as tiff_16bit:
|
||||
with Image.open("Tests/images/16bit.cropped.jp2") as jp2:
|
||||
assert_image_similar(jp2, tiff_16bit, 1e-3)
|
||||
assert_image_similar_tofile(tiff_16bit, "Tests/images/16bit.cropped.jp2", 1e-3)
|
||||
|
||||
|
||||
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
|
||||
def test_16bit_monochrome_j2k_like_tiff():
|
||||
with Image.open("Tests/images/16bit.cropped.tif") as tiff_16bit:
|
||||
with Image.open("Tests/images/16bit.cropped.j2k") as j2k:
|
||||
assert_image_similar(j2k, tiff_16bit, 1e-3)
|
||||
assert_image_similar_tofile(tiff_16bit, "Tests/images/16bit.cropped.j2k", 1e-3)
|
||||
|
||||
|
||||
def test_16bit_j2k_roundtrips():
|
||||
|
@ -219,7 +216,8 @@ def test_16bit_jp2_roundtrips():
|
|||
def test_unbound_local():
|
||||
# prepatch, a malformed jp2 file could cause an UnboundLocalError exception.
|
||||
with pytest.raises(OSError):
|
||||
Image.open("Tests/images/unbound_variable.jp2")
|
||||
with Image.open("Tests/images/unbound_variable.jp2"):
|
||||
pass
|
||||
|
||||
|
||||
def test_parser_feed():
|
||||
|
|
|
@ -9,6 +9,7 @@ from ctypes import c_float
|
|||
import pytest
|
||||
|
||||
from PIL import Image, ImageFilter, TiffImagePlugin, TiffTags, features
|
||||
from PIL.TiffImagePlugin import SUBIFD
|
||||
|
||||
from .helper import (
|
||||
assert_image_equal,
|
||||
|
@ -16,7 +17,6 @@ from .helper import (
|
|||
assert_image_similar,
|
||||
assert_image_similar_tofile,
|
||||
hopper,
|
||||
is_big_endian,
|
||||
skip_unless_feature,
|
||||
)
|
||||
|
||||
|
@ -98,15 +98,13 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
def test_g4_eq_png(self):
|
||||
""" Checking that we're actually getting the data that we expect"""
|
||||
with Image.open("Tests/images/hopper_bw_500.png") as png:
|
||||
with Image.open("Tests/images/hopper_g4_500.tif") as g4:
|
||||
assert_image_equal(g4, png)
|
||||
assert_image_equal_tofile(png, "Tests/images/hopper_g4_500.tif")
|
||||
|
||||
# see https://github.com/python-pillow/Pillow/issues/279
|
||||
def test_g4_fillorder_eq_png(self):
|
||||
""" Checking that we're actually getting the data that we expect"""
|
||||
with Image.open("Tests/images/g4-fillorder-test.png") as png:
|
||||
with Image.open("Tests/images/g4-fillorder-test.tif") as g4:
|
||||
assert_image_equal(g4, png)
|
||||
with Image.open("Tests/images/g4-fillorder-test.tif") as g4:
|
||||
assert_image_equal_tofile(g4, "Tests/images/g4-fillorder-test.png")
|
||||
|
||||
def test_g4_write(self, tmp_path):
|
||||
"""Checking to see that the saved image is the same as what we wrote"""
|
||||
|
@ -185,6 +183,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
for field in requested_fields:
|
||||
assert field in reloaded, f"{field} not in metadata"
|
||||
|
||||
@pytest.mark.valgrind_known_error(reason="Known invalid metadata")
|
||||
def test_additional_metadata(self, tmp_path):
|
||||
# these should not crash. Seriously dummy data, most of it doesn't make
|
||||
# any sense, so we're running up against limits where we're asking
|
||||
|
@ -323,6 +322,14 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
)
|
||||
TiffImagePlugin.WRITE_LIBTIFF = False
|
||||
|
||||
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_xmlpacket_tag(self, tmp_path):
|
||||
TiffImagePlugin.WRITE_LIBTIFF = True
|
||||
|
||||
|
@ -427,10 +434,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
im = im.filter(ImageFilter.GaussianBlur(4))
|
||||
im.save(out, compression="tiff_adobe_deflate")
|
||||
|
||||
with Image.open(out) as im2:
|
||||
im2.load()
|
||||
|
||||
assert_image_equal(im, im2)
|
||||
assert_image_equal_tofile(im, out)
|
||||
|
||||
def test_compressions(self, tmp_path):
|
||||
# Test various tiff compressions and assert similar image content but reduced
|
||||
|
@ -443,8 +447,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
for compression in ("packbits", "tiff_lzw"):
|
||||
im.save(out, compression=compression)
|
||||
size_compressed = os.path.getsize(out)
|
||||
with Image.open(out) as im2:
|
||||
assert_image_equal(im, im2)
|
||||
assert_image_equal_tofile(im, out)
|
||||
|
||||
im.save(out, compression="jpeg")
|
||||
size_jpeg = os.path.getsize(out)
|
||||
|
@ -453,8 +456,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
|
||||
im.save(out, compression="jpeg", quality=30)
|
||||
size_jpeg_30 = os.path.getsize(out)
|
||||
with Image.open(out) as im3:
|
||||
assert_image_similar(im2, im3, 30)
|
||||
assert_image_similar_tofile(im2, out, 30)
|
||||
|
||||
assert size_raw > size_compressed
|
||||
assert size_compressed > size_jpeg
|
||||
|
@ -468,6 +470,14 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
with Image.open(out) as reloaded:
|
||||
assert reloaded.info["compression"] == "jpeg"
|
||||
|
||||
def test_tiff_deflate_compression(self, tmp_path):
|
||||
im = hopper("RGB")
|
||||
out = str(tmp_path / "temp.tif")
|
||||
im.save(out, compression="tiff_deflate")
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert reloaded.info["compression"] == "tiff_adobe_deflate"
|
||||
|
||||
def test_quality(self, tmp_path):
|
||||
im = hopper("RGB")
|
||||
out = str(tmp_path / "temp.tif")
|
||||
|
@ -488,8 +498,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
out = str(tmp_path / "temp.tif")
|
||||
|
||||
im.save(out, compression="tiff_adobe_deflate")
|
||||
with Image.open(out) as im2:
|
||||
assert_image_equal(im, im2)
|
||||
assert_image_equal_tofile(im, out)
|
||||
|
||||
def test_palette_save(self, tmp_path):
|
||||
im = hopper("P")
|
||||
|
@ -639,8 +648,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
pilim.save(buffer_io, format="tiff", compression=compression)
|
||||
buffer_io.seek(0)
|
||||
|
||||
with Image.open(buffer_io) as pilim_load:
|
||||
assert_image_similar(pilim, pilim_load, 0)
|
||||
assert_image_similar_tofile(pilim, buffer_io, 0)
|
||||
|
||||
save_bytesio()
|
||||
save_bytesio("raw")
|
||||
|
@ -814,13 +822,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")
|
||||
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
||||
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")
|
||||
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
||||
def test_strip_ycbcr_jpeg_1x1_sampling(self):
|
||||
infile = "Tests/images/tiff_strip_ycbcr_jpeg_1x1_sampling.tif"
|
||||
with Image.open(infile) as im:
|
||||
|
@ -831,19 +839,58 @@ 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")
|
||||
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
||||
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")
|
||||
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
||||
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_strip_planar_rgb(self):
|
||||
# gdal_translate -co TILED=no -co INTERLEAVE=BAND -co COMPRESS=LZW \
|
||||
# tiff_strip_raw.tif tiff_strip_planar_lzw.tiff
|
||||
infile = "Tests/images/tiff_strip_planar_lzw.tiff"
|
||||
with Image.open(infile) as im:
|
||||
assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
||||
|
||||
def test_tiled_planar_rgb(self):
|
||||
# gdal_translate -co TILED=yes -co INTERLEAVE=BAND -co COMPRESS=LZW \
|
||||
# tiff_tiled_raw.tif tiff_tiled_planar_lzw.tiff
|
||||
infile = "Tests/images/tiff_tiled_planar_lzw.tiff"
|
||||
with Image.open(infile) as im:
|
||||
assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
||||
|
||||
def test_tiled_planar_16bit_RGB(self):
|
||||
# gdal_translate -co TILED=yes -co INTERLEAVE=BAND -co COMPRESS=LZW \
|
||||
# tiff_16bit_RGB.tiff tiff_tiled_planar_16bit_RGB.tiff
|
||||
with Image.open("Tests/images/tiff_tiled_planar_16bit_RGB.tiff") as im:
|
||||
assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGB_target.png")
|
||||
|
||||
def test_strip_planar_16bit_RGB(self):
|
||||
# gdal_translate -co TILED=no -co INTERLEAVE=BAND -co COMPRESS=LZW \
|
||||
# tiff_16bit_RGB.tiff tiff_strip_planar_16bit_RGB.tiff
|
||||
with Image.open("Tests/images/tiff_strip_planar_16bit_RGB.tiff") as im:
|
||||
assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGB_target.png")
|
||||
|
||||
def test_tiled_planar_16bit_RGBa(self):
|
||||
# gdal_translate -co TILED=yes \
|
||||
# -co INTERLEAVE=BAND -co COMPRESS=LZW -co ALPHA=PREMULTIPLIED \
|
||||
# tiff_16bit_RGBa.tiff tiff_tiled_planar_16bit_RGBa.tiff
|
||||
with Image.open("Tests/images/tiff_tiled_planar_16bit_RGBa.tiff") as im:
|
||||
assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGBa_target.png")
|
||||
|
||||
def test_strip_planar_16bit_RGBa(self):
|
||||
# gdal_translate -co TILED=no \
|
||||
# -co INTERLEAVE=BAND -co COMPRESS=LZW -co ALPHA=PREMULTIPLIED \
|
||||
# tiff_16bit_RGBa.tiff tiff_strip_planar_16bit_RGBa.tiff
|
||||
with Image.open("Tests/images/tiff_strip_planar_16bit_RGBa.tiff") as im:
|
||||
assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGBa_target.png")
|
||||
|
||||
def test_old_style_jpeg(self):
|
||||
infile = "Tests/images/old-style-jpeg-compression.tif"
|
||||
with Image.open(infile) as im:
|
||||
|
@ -864,6 +911,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
|
||||
assert_image_similar(base_im, im, 0.7)
|
||||
|
||||
@pytest.mark.valgrind_known_error(reason="Backtrace in Python Core")
|
||||
def test_sampleformat_not_corrupted(self):
|
||||
# Assert that a TIFF image with SampleFormat=UINT tag is not corrupted
|
||||
# when saving to a new file.
|
||||
|
|
|
@ -2,7 +2,7 @@ import pytest
|
|||
|
||||
from PIL import Image, McIdasImagePlugin
|
||||
|
||||
from .helper import assert_image_equal
|
||||
from .helper import assert_image_equal_tofile
|
||||
|
||||
|
||||
def test_invalid_file():
|
||||
|
@ -27,5 +27,4 @@ def test_valid_file():
|
|||
assert im.format == "MCIDAS"
|
||||
assert im.mode == "I"
|
||||
assert im.size == (1800, 400)
|
||||
with Image.open(saved_file) as im2:
|
||||
assert_image_equal(im, im2)
|
||||
assert_image_equal_tofile(im, saved_file)
|
||||
|
|
|
@ -41,20 +41,20 @@ def test_unclosed_file():
|
|||
|
||||
|
||||
def test_closed_file():
|
||||
def open():
|
||||
with pytest.warns(None) as record:
|
||||
im = Image.open(test_files[0])
|
||||
im.load()
|
||||
im.close()
|
||||
|
||||
pytest.warns(None, open)
|
||||
assert not record
|
||||
|
||||
|
||||
def test_context_manager():
|
||||
def open():
|
||||
with pytest.warns(None) as record:
|
||||
with Image.open(test_files[0]) as im:
|
||||
im.load()
|
||||
|
||||
pytest.warns(None, open)
|
||||
assert not record
|
||||
|
||||
|
||||
def test_app():
|
||||
|
@ -89,6 +89,20 @@ def test_frame_size():
|
|||
assert im.size == (680, 480)
|
||||
|
||||
|
||||
def test_ignore_frame_size():
|
||||
# Ignore the different size of the second frame
|
||||
# since this is not a "Large Thumbnail" image
|
||||
with Image.open("Tests/images/ignore_frame_size.mpo") as im:
|
||||
assert im.size == (64, 64)
|
||||
|
||||
im.seek(1)
|
||||
assert (
|
||||
im.mpinfo[0xB002][1]["Attribute"]["MPType"]
|
||||
== "Multi-Frame Image: (Disparity)"
|
||||
)
|
||||
assert im.size == (64, 64)
|
||||
|
||||
|
||||
def test_parallax():
|
||||
# Nintendo
|
||||
with Image.open("Tests/images/sugarshack.mpo") as im:
|
||||
|
@ -132,7 +146,7 @@ def test_mp_attribute():
|
|||
with Image.open(test_file) as im:
|
||||
mpinfo = im._getmp()
|
||||
frameNumber = 0
|
||||
for mpentry in mpinfo[45058]:
|
||||
for mpentry in mpinfo[0xB002]:
|
||||
mpattr = mpentry["Attribute"]
|
||||
if frameNumber:
|
||||
assert not mpattr["RepresentativeImageFlag"]
|
||||
|
|
|
@ -4,7 +4,7 @@ import pytest
|
|||
|
||||
from PIL import Image, MspImagePlugin
|
||||
|
||||
from .helper import assert_image_equal, hopper
|
||||
from .helper import assert_image_equal, assert_image_equal_tofile, hopper
|
||||
|
||||
TEST_FILE = "Tests/images/hopper.msp"
|
||||
EXTRA_DIR = "Tests/images/picins"
|
||||
|
@ -52,8 +52,7 @@ def test_open_windows_v1():
|
|||
|
||||
def _assert_file_image_equal(source_path, target_path):
|
||||
with Image.open(source_path) as im:
|
||||
with Image.open(target_path) as target:
|
||||
assert_image_equal(im, target)
|
||||
assert_image_equal_tofile(im, target_path)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
|
|
|
@ -44,6 +44,14 @@ def test_odd(tmp_path):
|
|||
_roundtrip(tmp_path, hopper(mode).resize((511, 511)))
|
||||
|
||||
|
||||
def test_odd_read():
|
||||
# Reading an image with an odd stride, making it malformed
|
||||
with Image.open("Tests/images/odd_stride.pcx") as im:
|
||||
im.load()
|
||||
|
||||
assert im.size == (371, 150)
|
||||
|
||||
|
||||
def test_pil184():
|
||||
# Check reading of files where xmin/xmax is not zero.
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ def test_unsupported_mode(tmp_path):
|
|||
im.save(outfile)
|
||||
|
||||
|
||||
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
||||
def test_save_all(tmp_path):
|
||||
# Single frame image
|
||||
helper_save_as_pdf(tmp_path, "RGB", save_all=True)
|
||||
|
|
|
@ -106,7 +106,8 @@ class TestFilePng:
|
|||
|
||||
test_file = "Tests/images/broken.png"
|
||||
with pytest.raises(OSError):
|
||||
Image.open(test_file)
|
||||
with Image.open(test_file):
|
||||
pass
|
||||
|
||||
def test_bad_text(self):
|
||||
# Make sure PIL can read malformed tEXt chunks (@PIL152)
|
||||
|
@ -324,7 +325,9 @@ class TestFilePng:
|
|||
|
||||
with Image.open(TEST_PNG_FILE) as im:
|
||||
# Assert that there is no unclosed file warning
|
||||
pytest.warns(None, im.verify)
|
||||
with pytest.warns(None) as record:
|
||||
im.verify()
|
||||
assert not record
|
||||
|
||||
with Image.open(TEST_PNG_FILE) as im:
|
||||
im.load()
|
||||
|
@ -464,7 +467,8 @@ class TestFilePng:
|
|||
|
||||
pngfile = BytesIO(data)
|
||||
with pytest.raises(OSError):
|
||||
Image.open(pngfile)
|
||||
with Image.open(pngfile):
|
||||
pass
|
||||
|
||||
def test_trns_rgb(self):
|
||||
# Check writing and reading of tRNS chunks for RGB images.
|
||||
|
@ -513,6 +517,8 @@ class TestFilePng:
|
|||
|
||||
def test_discard_icc_profile(self):
|
||||
with Image.open("Tests/images/icc_profile.png") as im:
|
||||
assert "icc_profile" in im.info
|
||||
|
||||
im = roundtrip(im, icc_profile=None)
|
||||
assert "icc_profile" not in im.info
|
||||
|
||||
|
@ -571,8 +577,8 @@ class TestFilePng:
|
|||
assert len(chunks) == 3
|
||||
|
||||
def test_read_private_chunks(self):
|
||||
im = Image.open("Tests/images/exif.png")
|
||||
assert im.private_chunks == [(b"orNT", b"\x01")]
|
||||
with Image.open("Tests/images/exif.png") as im:
|
||||
assert im.private_chunks == [(b"orNT", b"\x01")]
|
||||
|
||||
def test_roundtrip_private_chunk(self):
|
||||
# Check private chunk roundtripping
|
||||
|
@ -619,6 +625,25 @@ class TestFilePng:
|
|||
with Image.open("Tests/images/hopper_idat_after_image_end.png") as im:
|
||||
assert im.text == {"TXT": "VALUE", "ZIP": "VALUE"}
|
||||
|
||||
def test_specify_bits(self, tmp_path):
|
||||
im = hopper("P")
|
||||
|
||||
out = str(tmp_path / "temp.png")
|
||||
im.save(out, bits=4)
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert len(reloaded.png.im_palette[1]) == 48
|
||||
|
||||
def test_plte_length(self, tmp_path):
|
||||
im = Image.new("P", (1, 1))
|
||||
im.putpalette((1, 1, 1))
|
||||
|
||||
out = str(tmp_path / "temp.png")
|
||||
im.save(str(tmp_path / "temp.png"))
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert len(reloaded.png.im_palette[1]) == 3
|
||||
|
||||
def test_exif(self):
|
||||
# With an EXIF chunk
|
||||
with Image.open("Tests/images/exif.png") as im:
|
||||
|
@ -654,6 +679,7 @@ class TestFilePng:
|
|||
exif = reloaded._getexif()
|
||||
assert exif[274] == 1
|
||||
|
||||
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
||||
def test_exif_from_jpg(self, tmp_path):
|
||||
with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
|
||||
test_file = str(tmp_path / "temp.png")
|
||||
|
|
|
@ -2,7 +2,7 @@ import pytest
|
|||
|
||||
from PIL import Image
|
||||
|
||||
from .helper import assert_image_equal, assert_image_similar, hopper
|
||||
from .helper import assert_image_equal_tofile, assert_image_similar, hopper
|
||||
|
||||
# sample ppm stream
|
||||
TEST_FILE = "Tests/images/hopper.ppm"
|
||||
|
@ -24,8 +24,7 @@ def test_16bit_pgm():
|
|||
assert im.size == (20, 100)
|
||||
assert im.get_format_mimetype() == "image/x-portable-graymap"
|
||||
|
||||
with Image.open("Tests/images/16_bit_binary_pgm.png") as tgt:
|
||||
assert_image_equal(im, tgt)
|
||||
assert_image_equal_tofile(im, "Tests/images/16_bit_binary_pgm.png")
|
||||
|
||||
|
||||
def test_16bit_pgm_write(tmp_path):
|
||||
|
@ -35,8 +34,7 @@ def test_16bit_pgm_write(tmp_path):
|
|||
f = str(tmp_path / "temp.pgm")
|
||||
im.save(f, "PPM")
|
||||
|
||||
with Image.open(f) as reloaded:
|
||||
assert_image_equal(im, reloaded)
|
||||
assert_image_equal_tofile(im, f)
|
||||
|
||||
|
||||
def test_pnm(tmp_path):
|
||||
|
@ -46,8 +44,7 @@ def test_pnm(tmp_path):
|
|||
f = str(tmp_path / "temp.pnm")
|
||||
im.save(f)
|
||||
|
||||
with Image.open(f) as reloaded:
|
||||
assert_image_equal(im, reloaded)
|
||||
assert_image_equal_tofile(im, f)
|
||||
|
||||
|
||||
def test_truncated_file(tmp_path):
|
||||
|
@ -56,7 +53,8 @@ def test_truncated_file(tmp_path):
|
|||
f.write("P6")
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
Image.open(path)
|
||||
with Image.open(path):
|
||||
pass
|
||||
|
||||
|
||||
def test_neg_ppm():
|
||||
|
@ -66,7 +64,8 @@ def test_neg_ppm():
|
|||
# sizes.
|
||||
|
||||
with pytest.raises(OSError):
|
||||
Image.open("Tests/images/negative_size.ppm")
|
||||
with Image.open("Tests/images/negative_size.ppm"):
|
||||
pass
|
||||
|
||||
|
||||
def test_mimetypes(tmp_path):
|
||||
|
|
|
@ -29,20 +29,20 @@ def test_unclosed_file():
|
|||
|
||||
|
||||
def test_closed_file():
|
||||
def open():
|
||||
with pytest.warns(None) as record:
|
||||
im = Image.open(test_file)
|
||||
im.load()
|
||||
im.close()
|
||||
|
||||
pytest.warns(None, open)
|
||||
assert not record
|
||||
|
||||
|
||||
def test_context_manager():
|
||||
def open():
|
||||
with pytest.warns(None) as record:
|
||||
with Image.open(test_file) as im:
|
||||
im.load()
|
||||
|
||||
pytest.warns(None, open)
|
||||
assert not record
|
||||
|
||||
|
||||
def test_invalid_file():
|
||||
|
@ -128,4 +128,5 @@ def test_combined_larger_than_size():
|
|||
# If we instead take the 'size' of the extra data field as the source of truth,
|
||||
# then the seek can't be negative
|
||||
with pytest.raises(OSError):
|
||||
Image.open("Tests/images/combined_larger_than_size.psd")
|
||||
with Image.open("Tests/images/combined_larger_than_size.psd"):
|
||||
pass
|
||||
|
|
|
@ -2,7 +2,12 @@ import pytest
|
|||
|
||||
from PIL import Image, SgiImagePlugin
|
||||
|
||||
from .helper import assert_image_equal, assert_image_similar, hopper
|
||||
from .helper import (
|
||||
assert_image_equal,
|
||||
assert_image_equal_tofile,
|
||||
assert_image_similar,
|
||||
hopper,
|
||||
)
|
||||
|
||||
|
||||
def test_rgb():
|
||||
|
@ -16,10 +21,7 @@ def test_rgb():
|
|||
|
||||
|
||||
def test_rgb16():
|
||||
test_file = "Tests/images/hopper16.rgb"
|
||||
|
||||
with Image.open(test_file) as im:
|
||||
assert_image_equal(im, hopper())
|
||||
assert_image_equal_tofile(hopper(), "Tests/images/hopper16.rgb")
|
||||
|
||||
|
||||
def test_l():
|
||||
|
@ -38,8 +40,7 @@ def test_rgba():
|
|||
test_file = "Tests/images/transparent.sgi"
|
||||
|
||||
with Image.open(test_file) as im:
|
||||
with Image.open("Tests/images/transparent.png") as target:
|
||||
assert_image_equal(im, target)
|
||||
assert_image_equal_tofile(im, "Tests/images/transparent.png")
|
||||
assert im.get_format_mimetype() == "image/sgi"
|
||||
|
||||
|
||||
|
@ -49,16 +50,14 @@ def test_rle():
|
|||
test_file = "Tests/images/hopper.sgi"
|
||||
|
||||
with Image.open(test_file) as im:
|
||||
with Image.open("Tests/images/hopper.rgb") as target:
|
||||
assert_image_equal(im, target)
|
||||
assert_image_equal_tofile(im, "Tests/images/hopper.rgb")
|
||||
|
||||
|
||||
def test_rle16():
|
||||
test_file = "Tests/images/tv16.sgi"
|
||||
|
||||
with Image.open(test_file) as im:
|
||||
with Image.open("Tests/images/tv.rgb") as target:
|
||||
assert_image_equal(im, target)
|
||||
assert_image_equal_tofile(im, "Tests/images/tv.rgb")
|
||||
|
||||
|
||||
def test_invalid_file():
|
||||
|
@ -72,8 +71,7 @@ def test_write(tmp_path):
|
|||
def roundtrip(img):
|
||||
out = str(tmp_path / "temp.sgi")
|
||||
img.save(out, format="sgi")
|
||||
with Image.open(out) as reloaded:
|
||||
assert_image_equal(img, reloaded)
|
||||
assert_image_equal_tofile(img, out)
|
||||
|
||||
for mode in ("L", "RGB", "RGBA"):
|
||||
roundtrip(hopper(mode))
|
||||
|
@ -89,8 +87,7 @@ def test_write16(tmp_path):
|
|||
out = str(tmp_path / "temp.sgi")
|
||||
im.save(out, format="sgi", bpc=2)
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert_image_equal(im, reloaded)
|
||||
assert_image_equal_tofile(im, out)
|
||||
|
||||
|
||||
def test_unsupported_mode(tmp_path):
|
||||
|
|
|
@ -5,7 +5,7 @@ import pytest
|
|||
|
||||
from PIL import Image, ImageSequence, SpiderImagePlugin
|
||||
|
||||
from .helper import assert_image_equal, hopper, is_pypy
|
||||
from .helper import assert_image_equal_tofile, hopper, is_pypy
|
||||
|
||||
TEST_FILE = "Tests/images/hopper.spider"
|
||||
|
||||
|
@ -28,20 +28,20 @@ def test_unclosed_file():
|
|||
|
||||
|
||||
def test_closed_file():
|
||||
def open():
|
||||
with pytest.warns(None) as record:
|
||||
im = Image.open(TEST_FILE)
|
||||
im.load()
|
||||
im.close()
|
||||
|
||||
pytest.warns(None, open)
|
||||
assert not record
|
||||
|
||||
|
||||
def test_context_manager():
|
||||
def open():
|
||||
with pytest.warns(None) as record:
|
||||
with Image.open(TEST_FILE) as im:
|
||||
im.load()
|
||||
|
||||
pytest.warns(None, open)
|
||||
assert not record
|
||||
|
||||
|
||||
def test_save(tmp_path):
|
||||
|
@ -136,7 +136,8 @@ def test_invalid_file():
|
|||
invalid_file = "Tests/images/invalid.spider"
|
||||
|
||||
with pytest.raises(OSError):
|
||||
Image.open(invalid_file)
|
||||
with Image.open(invalid_file):
|
||||
pass
|
||||
|
||||
|
||||
def test_nonstack_file():
|
||||
|
@ -159,5 +160,4 @@ def test_odd_size():
|
|||
im.save(data, format="SPIDER")
|
||||
|
||||
data.seek(0)
|
||||
with Image.open(data) as im2:
|
||||
assert_image_equal(im, im2)
|
||||
assert_image_equal_tofile(im, data)
|
||||
|
|
|
@ -4,7 +4,7 @@ import pytest
|
|||
|
||||
from PIL import Image, SunImagePlugin
|
||||
|
||||
from .helper import assert_image_equal, assert_image_similar, hopper
|
||||
from .helper import assert_image_equal_tofile, assert_image_similar, hopper
|
||||
|
||||
EXTRA_DIR = "Tests/images/sunraster"
|
||||
|
||||
|
@ -29,8 +29,7 @@ def test_sanity():
|
|||
|
||||
def test_im1():
|
||||
with Image.open("Tests/images/sunraster.im1") as im:
|
||||
with Image.open("Tests/images/sunraster.im1.png") as target:
|
||||
assert_image_equal(im, target)
|
||||
assert_image_equal_tofile(im, "Tests/images/sunraster.im1.png")
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
|
@ -46,7 +45,4 @@ def test_others():
|
|||
with Image.open(path) as im:
|
||||
im.load()
|
||||
assert isinstance(im, SunImagePlugin.SunImageFile)
|
||||
target_path = f"{os.path.splitext(path)[0]}.png"
|
||||
# im.save(target_file)
|
||||
with Image.open(target_path) as target:
|
||||
assert_image_equal(im, target)
|
||||
assert_image_equal_tofile(im, f"{os.path.splitext(path)[0]}.png")
|
||||
|
|
|
@ -31,16 +31,16 @@ def test_unclosed_file():
|
|||
|
||||
|
||||
def test_close():
|
||||
def open():
|
||||
with pytest.warns(None) as record:
|
||||
tar = TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg")
|
||||
tar.close()
|
||||
|
||||
pytest.warns(None, open)
|
||||
assert not record
|
||||
|
||||
|
||||
def test_contextmanager():
|
||||
def open():
|
||||
with pytest.warns(None) as record:
|
||||
with TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg"):
|
||||
pass
|
||||
|
||||
pytest.warns(None, open)
|
||||
assert not record
|
||||
|
|
|
@ -4,7 +4,7 @@ from io import BytesIO
|
|||
import pytest
|
||||
|
||||
from PIL import Image, TiffImagePlugin
|
||||
from PIL.TiffImagePlugin import RESOLUTION_UNIT, SUBIFD, X_RESOLUTION, Y_RESOLUTION
|
||||
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
|
||||
|
||||
from .helper import (
|
||||
assert_image_equal,
|
||||
|
@ -59,19 +59,19 @@ class TestFileTiff:
|
|||
pytest.warns(ResourceWarning, open)
|
||||
|
||||
def test_closed_file(self):
|
||||
def open():
|
||||
with pytest.warns(None) as record:
|
||||
im = Image.open("Tests/images/multipage.tiff")
|
||||
im.load()
|
||||
im.close()
|
||||
|
||||
pytest.warns(None, open)
|
||||
assert not record
|
||||
|
||||
def test_context_manager(self):
|
||||
def open():
|
||||
with pytest.warns(None) as record:
|
||||
with Image.open("Tests/images/multipage.tiff") as im:
|
||||
im.load()
|
||||
|
||||
pytest.warns(None, open)
|
||||
assert not record
|
||||
|
||||
def test_mac_tiff(self):
|
||||
# Read RGBa images from macOS [@PIL136]
|
||||
|
@ -161,19 +161,10 @@ 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(
|
||||
b, format="tiff", resolution=123.45
|
||||
)
|
||||
with Image.open("Tests/images/10ct_32bit_128.tiff") as im:
|
||||
im.save(b, format="tiff", resolution=123.45)
|
||||
with Image.open(b) as im:
|
||||
assert float(im.tag_v2[X_RESOLUTION]) == 123.45
|
||||
assert float(im.tag_v2[Y_RESOLUTION]) == 123.45
|
||||
|
@ -256,7 +247,8 @@ class TestFileTiff:
|
|||
|
||||
def test_unknown_pixel_mode(self):
|
||||
with pytest.raises(OSError):
|
||||
Image.open("Tests/images/hopper_unknown_pixel_mode.tif")
|
||||
with Image.open("Tests/images/hopper_unknown_pixel_mode.tif"):
|
||||
pass
|
||||
|
||||
def test_n_frames(self):
|
||||
for path, n_frames in [
|
||||
|
@ -491,8 +483,7 @@ class TestFileTiff:
|
|||
tmpfile = str(tmp_path / "temp.tif")
|
||||
im.save(tmpfile)
|
||||
|
||||
with Image.open(tmpfile) as reloaded:
|
||||
assert_image_equal(im, reloaded)
|
||||
assert_image_equal_tofile(im, tmpfile)
|
||||
|
||||
def test_strip_raw(self):
|
||||
infile = "Tests/images/tiff_strip_raw.tif"
|
||||
|
@ -577,6 +568,28 @@ class TestFileTiff:
|
|||
with Image.open(tmpfile) as reloaded:
|
||||
assert b"Dummy value" == reloaded.info["icc_profile"]
|
||||
|
||||
def test_save_icc_profile(self, tmp_path):
|
||||
im = hopper()
|
||||
assert "icc_profile" not in im.info
|
||||
|
||||
outfile = str(tmp_path / "temp.tif")
|
||||
icc_profile = b"Dummy value"
|
||||
im.save(outfile, icc_profile=icc_profile)
|
||||
|
||||
with Image.open(outfile) as reloaded:
|
||||
assert reloaded.info["icc_profile"] == icc_profile
|
||||
|
||||
def test_discard_icc_profile(self, tmp_path):
|
||||
outfile = str(tmp_path / "temp.tif")
|
||||
|
||||
with Image.open("Tests/images/icc_profile.png") as im:
|
||||
assert "icc_profile" in im.info
|
||||
|
||||
im.save(outfile, icc_profile=None)
|
||||
|
||||
with Image.open(outfile) as reloaded:
|
||||
assert "icc_profile" not in reloaded.info
|
||||
|
||||
def test_close_on_load_exclusive(self, tmp_path):
|
||||
# similar to test_fd_leak, but runs on unixlike os
|
||||
tmpfile = str(tmp_path / "temp.tif")
|
||||
|
@ -613,7 +626,8 @@ class TestFileTiff:
|
|||
def test_string_dimension(self):
|
||||
# Assert that an error is raised if one of the dimensions is a string
|
||||
with pytest.raises(ValueError):
|
||||
Image.open("Tests/images/string_dimension.tiff")
|
||||
with Image.open("Tests/images/string_dimension.tiff"):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.skipif(not is_win32(), reason="Windows only")
|
||||
|
|
|
@ -145,7 +145,9 @@ class TestFileWebp:
|
|||
file_path = "Tests/images/hopper.webp"
|
||||
with Image.open(file_path) as image:
|
||||
temp_file = str(tmp_path / "temp.webp")
|
||||
pytest.warns(None, image.save, temp_file)
|
||||
with pytest.warns(None) as record:
|
||||
image.save(temp_file)
|
||||
assert not record
|
||||
|
||||
def test_file_pointer_could_be_reused(self):
|
||||
file_path = "Tests/images/hopper.webp"
|
||||
|
@ -165,7 +167,8 @@ class TestFileWebp:
|
|||
|
||||
# Save as GIF
|
||||
out_gif = str(tmp_path / "temp.gif")
|
||||
Image.open(out_webp).save(out_gif)
|
||||
with Image.open(out_webp) as im:
|
||||
im.save(out_gif)
|
||||
|
||||
with Image.open(out_gif) as reread:
|
||||
reread_value = reread.convert("RGB").getpixel((1, 1))
|
||||
|
@ -173,3 +176,16 @@ class TestFileWebp:
|
|||
[abs(original_value[i] - reread_value[i]) for i in range(0, 3)]
|
||||
)
|
||||
assert difference < 5
|
||||
|
||||
@skip_unless_feature("webp")
|
||||
@skip_unless_feature("webp_anim")
|
||||
def test_duration(self, tmp_path):
|
||||
with Image.open("Tests/images/dispose_bgnd.gif") as im:
|
||||
assert im.info["duration"] == 1000
|
||||
|
||||
out_webp = str(tmp_path / "temp.webp")
|
||||
im.save(out_webp, save_all=True)
|
||||
|
||||
with Image.open(out_webp) as reloaded:
|
||||
reloaded.load()
|
||||
assert reloaded.info["duration"] == 1000
|
||||
|
|
|
@ -2,7 +2,12 @@ import pytest
|
|||
|
||||
from PIL import Image
|
||||
|
||||
from .helper import assert_image_equal, assert_image_similar, hopper
|
||||
from .helper import (
|
||||
assert_image_equal,
|
||||
assert_image_similar,
|
||||
assert_image_similar_tofile,
|
||||
hopper,
|
||||
)
|
||||
|
||||
_webp = pytest.importorskip("PIL._webp", reason="WebP support not installed")
|
||||
|
||||
|
@ -29,8 +34,7 @@ def test_read_rgba():
|
|||
|
||||
image.tobytes()
|
||||
|
||||
with Image.open("Tests/images/transparent.png") as target:
|
||||
assert_image_similar(image, target, 20.0)
|
||||
assert_image_similar_tofile(image, "Tests/images/transparent.png", 20.0)
|
||||
|
||||
|
||||
def test_write_lossless_rgb(tmp_path):
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
from io import BytesIO
|
||||
|
||||
import pytest
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from .helper import skip_unless_feature
|
||||
|
@ -39,6 +41,7 @@ def test_read_exif_metadata_without_prefix():
|
|||
assert exif[305] == "Adobe Photoshop CS6 (Macintosh)"
|
||||
|
||||
|
||||
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
||||
def test_write_exif_metadata():
|
||||
file_path = "Tests/images/flower.jpg"
|
||||
test_buffer = BytesIO()
|
||||
|
@ -71,6 +74,7 @@ def test_read_icc_profile():
|
|||
assert icc == expected_icc
|
||||
|
||||
|
||||
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
||||
def test_write_icc_metadata():
|
||||
file_path = "Tests/images/flower2.jpg"
|
||||
test_buffer = BytesIO()
|
||||
|
@ -88,6 +92,7 @@ def test_write_icc_metadata():
|
|||
assert webp_icc_profile == expected_icc_profile, "Webp ICC didn't match"
|
||||
|
||||
|
||||
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
||||
def test_read_no_exif():
|
||||
file_path = "Tests/images/flower.jpg"
|
||||
test_buffer = BytesIO()
|
||||
|
|
|
@ -2,7 +2,7 @@ import pytest
|
|||
|
||||
from PIL import Image, WmfImagePlugin
|
||||
|
||||
from .helper import assert_image_similar, hopper
|
||||
from .helper import assert_image_similar_tofile, hopper
|
||||
|
||||
|
||||
def test_load_raw():
|
||||
|
@ -13,9 +13,7 @@ def test_load_raw():
|
|||
# Currently, support for WMF/EMF is Windows-only
|
||||
im.load()
|
||||
# Compare to reference rendering
|
||||
with Image.open("Tests/images/drawing_emf_ref.png") as imref:
|
||||
imref.load()
|
||||
assert_image_similar(im, imref, 0)
|
||||
assert_image_similar_tofile(im, "Tests/images/drawing_emf_ref.png", 0)
|
||||
|
||||
# Test basic WMF open and rendering
|
||||
with Image.open("Tests/images/drawing.wmf") as im:
|
||||
|
@ -23,9 +21,7 @@ def test_load_raw():
|
|||
# Currently, support for WMF/EMF is Windows-only
|
||||
im.load()
|
||||
# Compare to reference rendering
|
||||
with Image.open("Tests/images/drawing_wmf_ref.png") as imref:
|
||||
imref.load()
|
||||
assert_image_similar(im, imref, 2.0)
|
||||
assert_image_similar_tofile(im, "Tests/images/drawing_wmf_ref.png", 2.0)
|
||||
|
||||
|
||||
def test_register_handler(tmp_path):
|
||||
|
@ -66,8 +62,7 @@ def test_load_set_dpi():
|
|||
im.load(144)
|
||||
assert im.size == (164, 164)
|
||||
|
||||
with Image.open("Tests/images/drawing_wmf_ref_144.png") as expected:
|
||||
assert_image_similar(im, expected, 2.1)
|
||||
assert_image_similar_tofile(im, "Tests/images/drawing_wmf_ref_144.png", 2.1)
|
||||
|
||||
|
||||
def test_save(tmp_path):
|
||||
|
|
|
@ -4,7 +4,11 @@ import pytest
|
|||
|
||||
from PIL import FontFile, Image, ImageDraw, ImageFont, PcfFontFile
|
||||
|
||||
from .helper import assert_image_equal, assert_image_similar, skip_unless_feature
|
||||
from .helper import (
|
||||
assert_image_equal_tofile,
|
||||
assert_image_similar_tofile,
|
||||
skip_unless_feature,
|
||||
)
|
||||
|
||||
fontname = "Tests/fonts/10x20-ISO8859-1.pcf"
|
||||
|
||||
|
@ -33,8 +37,7 @@ def save_font(request, tmp_path):
|
|||
font.save(tempname)
|
||||
|
||||
with Image.open(tempname.replace(".pil", ".pbm")) as loaded:
|
||||
with Image.open("Tests/fonts/10x20.pbm") as target:
|
||||
assert_image_equal(loaded, target)
|
||||
assert_image_equal_tofile(loaded, "Tests/fonts/10x20.pbm")
|
||||
|
||||
with open(tempname, "rb") as f_loaded:
|
||||
with open("Tests/fonts/10x20.pil", "rb") as f_target:
|
||||
|
@ -58,8 +61,7 @@ def test_draw(request, tmp_path):
|
|||
im = Image.new("L", (130, 30), "white")
|
||||
draw = ImageDraw.Draw(im)
|
||||
draw.text((0, 0), message, "black", font=font)
|
||||
with Image.open("Tests/images/test_draw_pbm_target.png") as target:
|
||||
assert_image_similar(im, target, 0)
|
||||
assert_image_similar_tofile(im, "Tests/images/test_draw_pbm_target.png", 0)
|
||||
|
||||
|
||||
def test_textsize(request, tmp_path):
|
||||
|
@ -80,8 +82,7 @@ def _test_high_characters(request, tmp_path, message):
|
|||
im = Image.new("L", (750, 30), "white")
|
||||
draw = ImageDraw.Draw(im)
|
||||
draw.text((0, 0), message, "black", font=font)
|
||||
with Image.open("Tests/images/high_ascii_chars.png") as target:
|
||||
assert_image_similar(im, target, 0)
|
||||
assert_image_similar_tofile(im, "Tests/images/high_ascii_chars.png", 0)
|
||||
|
||||
|
||||
def test_high_characters(request, tmp_path):
|
||||
|
|
|
@ -2,7 +2,11 @@ import os
|
|||
|
||||
from PIL import FontFile, Image, ImageDraw, ImageFont, PcfFontFile
|
||||
|
||||
from .helper import assert_image_equal, assert_image_similar, skip_unless_feature
|
||||
from .helper import (
|
||||
assert_image_equal_tofile,
|
||||
assert_image_similar_tofile,
|
||||
skip_unless_feature,
|
||||
)
|
||||
|
||||
fontname = "Tests/fonts/ter-x20b.pcf"
|
||||
|
||||
|
@ -47,8 +51,7 @@ def save_font(request, tmp_path, encoding):
|
|||
font.save(tempname)
|
||||
|
||||
with Image.open(tempname.replace(".pil", ".pbm")) as loaded:
|
||||
with Image.open(f"Tests/fonts/ter-x20b-{encoding}.pbm") as target:
|
||||
assert_image_equal(loaded, target)
|
||||
assert_image_equal_tofile(loaded, f"Tests/fonts/ter-x20b-{encoding}.pbm")
|
||||
|
||||
with open(tempname, "rb") as f_loaded:
|
||||
with open(f"Tests/fonts/ter-x20b-{encoding}.pil", "rb") as f_target:
|
||||
|
@ -79,8 +82,7 @@ def _test_draw(request, tmp_path, encoding):
|
|||
draw = ImageDraw.Draw(im)
|
||||
message = charsets[encoding]["message"].encode(encoding)
|
||||
draw.text((0, 0), message, "black", font=font)
|
||||
with Image.open(charsets[encoding]["image1"]) as target:
|
||||
assert_image_similar(im, target, 0)
|
||||
assert_image_similar_tofile(im, charsets[encoding]["image1"], 0)
|
||||
|
||||
|
||||
def test_draw_iso8859_1(request, tmp_path):
|
||||
|
|