mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-25 17:36:18 +03:00
Merge branch 'main' into fix-alpha-for-overlapping-glyphs
This commit is contained in:
commit
a6a612c6c5
|
@ -28,7 +28,8 @@ fi
|
|||
|
||||
python3 -m pip install --upgrade pip
|
||||
python3 -m pip install --upgrade wheel
|
||||
PYTHONOPTIMIZE=0 python3 -m pip install cffi
|
||||
# TODO Update condition when cffi supports 3.13
|
||||
if ! [[ "$GHA_PYTHON_VERSION" == "3.13" ]]; then PYTHONOPTIMIZE=0 python3 -m pip install cffi ; fi
|
||||
python3 -m pip install coverage
|
||||
python3 -m pip install defusedxml
|
||||
python3 -m pip install olefile
|
||||
|
@ -38,7 +39,8 @@ python3 -m pip install -U pytest-timeout
|
|||
python3 -m pip install pyroma
|
||||
|
||||
if [[ $(uname) != CYGWIN* ]]; then
|
||||
python3 -m pip install numpy
|
||||
# TODO Update condition when NumPy supports 3.13
|
||||
if ! [[ "$GHA_PYTHON_VERSION" == "3.13" ]]; then python3 -m pip install numpy ; fi
|
||||
|
||||
# PyQt6 doesn't support PyPy3
|
||||
if [[ $GHA_PYTHON_VERSION == 3.* ]]; then
|
||||
|
|
1
.ci/requirements-cibw.txt
Normal file
1
.ci/requirements-cibw.txt
Normal file
|
@ -0,0 +1 @@
|
|||
cibuildwheel==2.16.2
|
7
.github/workflows/macos-install.sh
vendored
7
.github/workflows/macos-install.sh
vendored
|
@ -5,7 +5,9 @@ set -e
|
|||
brew install libtiff libjpeg openjpeg libimagequant webp little-cms2 freetype libraqm
|
||||
export PKG_CONFIG_PATH="/usr/local/opt/openblas/lib/pkgconfig"
|
||||
|
||||
PYTHONOPTIMIZE=0 python3 -m pip install cffi
|
||||
# TODO Update condition when cffi supports 3.13
|
||||
if ! [[ "$GHA_PYTHON_VERSION" == "3.13" ]]; then PYTHONOPTIMIZE=0 python3 -m pip install cffi ; fi
|
||||
|
||||
python3 -m pip install coverage
|
||||
python3 -m pip install defusedxml
|
||||
python3 -m pip install olefile
|
||||
|
@ -14,7 +16,8 @@ python3 -m pip install -U pytest-cov
|
|||
python3 -m pip install -U pytest-timeout
|
||||
python3 -m pip install pyroma
|
||||
|
||||
python3 -m pip install numpy
|
||||
# TODO Update condition when NumPy supports 3.13
|
||||
if ! [[ "$GHA_PYTHON_VERSION" == "3.13" ]]; then python3 -m pip install numpy ; fi
|
||||
|
||||
# extra test images
|
||||
pushd depends && ./install_extra_test_images.sh && popd
|
||||
|
|
1
.github/workflows/test-docker.yml
vendored
1
.github/workflows/test-docker.yml
vendored
|
@ -52,6 +52,7 @@ jobs:
|
|||
debian-12-bookworm-x86,
|
||||
debian-12-bookworm-amd64,
|
||||
fedora-38-amd64,
|
||||
fedora-39-amd64,
|
||||
gentoo,
|
||||
ubuntu-20.04-focal-amd64,
|
||||
ubuntu-22.04-jammy-amd64,
|
||||
|
|
51
.github/workflows/test-windows.yml
vendored
51
.github/workflows/test-windows.yml
vendored
|
@ -32,7 +32,7 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["pypy3.10", "pypy3.9", "3.8", "3.9", "3.10", "3.11", "3.12"]
|
||||
python-version: ["pypy3.10", "pypy3.9", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
|
||||
|
||||
timeout-minutes: 30
|
||||
|
||||
|
@ -59,6 +59,7 @@ jobs:
|
|||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
allow-prereleases: true
|
||||
cache: pip
|
||||
cache-dependency-path: ".github/workflows/test-windows.yml"
|
||||
|
||||
|
@ -71,10 +72,10 @@ jobs:
|
|||
- name: Install dependencies
|
||||
id: install
|
||||
run: |
|
||||
7z x winbuild\depends\nasm-2.16.01-win64.zip "-o$env:RUNNER_WORKSPACE\"
|
||||
echo "$env:RUNNER_WORKSPACE\nasm-2.16.01" >> $env:GITHUB_PATH
|
||||
choco install nasm --no-progress
|
||||
echo "C:\Program Files\NASM" >> $env:GITHUB_PATH
|
||||
|
||||
choco install ghostscript --version=10.0.0.20230317
|
||||
choco install ghostscript --version=10.0.0.20230317 --no-progress
|
||||
echo "C:\Program Files\gs\gs10.00.0\bin" >> $env:GITHUB_PATH
|
||||
|
||||
# Install extra test images
|
||||
|
@ -166,7 +167,6 @@ jobs:
|
|||
- name: Build Pillow
|
||||
run: |
|
||||
$FLAGS="-C raqm=vendor -C fribidi=vendor"
|
||||
if ('${{ github.event_name }}' -ne 'pull_request') { $FLAGS+=" -C imagequant=disable" }
|
||||
cmd /c "winbuild\build\build_env.cmd && $env:pythonLocation\python.exe -m pip install -v $FLAGS ."
|
||||
& $env:pythonLocation\python.exe selftest.py --installed
|
||||
shell: pwsh
|
||||
|
@ -208,47 +208,6 @@ jobs:
|
|||
flags: GHA_Windows
|
||||
name: ${{ runner.os }} Python ${{ matrix.python-version }}
|
||||
|
||||
- name: Build wheel
|
||||
id: wheel
|
||||
if: "github.event_name != 'pull_request'"
|
||||
run: |
|
||||
mkdir fribidi
|
||||
copy winbuild\build\bin\fribidi* fribidi
|
||||
setlocal EnableDelayedExpansion
|
||||
for %%f in (winbuild\build\license\*) do (
|
||||
set x=%%~nf
|
||||
rem Skip FriBiDi license, it is not included in the wheel.
|
||||
set fribidi=!x:~0,7!
|
||||
if NOT !fribidi!==fribidi (
|
||||
rem Skip imagequant license, it is not included in the wheel.
|
||||
set libimagequant=!x:~0,13!
|
||||
if NOT !libimagequant!==libimagequant (
|
||||
echo. >> LICENSE
|
||||
echo ===== %%~nf ===== >> LICENSE
|
||||
echo. >> LICENSE
|
||||
type %%f >> LICENSE
|
||||
)
|
||||
)
|
||||
)
|
||||
for /f "tokens=3 delims=/" %%a in ("${{ github.ref }}") do echo dist=dist-%%a >> %GITHUB_OUTPUT%
|
||||
call winbuild\\build\\build_env.cmd
|
||||
%pythonLocation%\python.exe -m pip wheel -v -C raqm=vendor -C fribidi=vendor -C imagequant=disable .
|
||||
shell: cmd
|
||||
|
||||
- name: Upload wheel
|
||||
uses: actions/upload-artifact@v3
|
||||
if: "github.event_name != 'pull_request'"
|
||||
with:
|
||||
name: ${{ steps.wheel.outputs.dist }}
|
||||
path: "*.whl"
|
||||
|
||||
- name: Upload fribidi.dll
|
||||
if: "github.event_name != 'pull_request' && matrix.python-version == 3.11"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: fribidi
|
||||
path: fribidi\*
|
||||
|
||||
success:
|
||||
permissions:
|
||||
contents: none
|
||||
|
|
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
|
@ -41,6 +41,7 @@ jobs:
|
|||
python-version: [
|
||||
"pypy3.10",
|
||||
"pypy3.9",
|
||||
"3.13",
|
||||
"3.12",
|
||||
"3.11",
|
||||
"3.10",
|
||||
|
@ -64,6 +65,7 @@ jobs:
|
|||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
allow-prereleases: true
|
||||
cache: pip
|
||||
cache-dependency-path: ".ci/*.sh"
|
||||
|
||||
|
|
40
.github/workflows/wheels-build.sh
vendored
40
.github/workflows/wheels-build.sh
vendored
|
@ -1,40 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
||||
# webp, zstd, xz, libtiff, libxcb cause a conflict with building webp, libtiff, libxcb
|
||||
# libxdmcp causes an issue on macOS < 11
|
||||
# curl from brew requires zstd, use system curl
|
||||
# if php is installed, brew tries to reinstall these after installing openblas
|
||||
# remove lcms2 and libpng to fix building openjpeg on arm64
|
||||
brew remove --ignore-dependencies webp zstd xz libpng libtiff libxcb libxdmcp curl php lcms2 ghostscript
|
||||
|
||||
brew install pkg-config
|
||||
|
||||
if [[ "$PLAT" == "arm64" ]]; then
|
||||
export MACOSX_DEPLOYMENT_TARGET="11.0"
|
||||
else
|
||||
export MACOSX_DEPLOYMENT_TARGET="10.10"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$MB_PYTHON_VERSION" == pypy3* ]]; then
|
||||
MB_PYTHON_OSX_VER="10.9"
|
||||
fi
|
||||
|
||||
echo "::group::Install a virtualenv"
|
||||
source wheels/multibuild/common_utils.sh
|
||||
source wheels/multibuild/travis_steps.sh
|
||||
python3 -m pip install virtualenv
|
||||
before_install
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Build wheel"
|
||||
build_wheel
|
||||
ls -l "${GITHUB_WORKSPACE}/${WHEEL_SDIR}/"
|
||||
echo "::endgroup::"
|
||||
|
||||
if [[ $MACOSX_DEPLOYMENT_TARGET != "11.0" ]]; then
|
||||
echo "::group::Test wheel"
|
||||
install_run
|
||||
echo "::endgroup::"
|
||||
fi
|
151
.github/workflows/wheels-dependencies.sh
vendored
Executable file
151
.github/workflows/wheels-dependencies.sh
vendored
Executable file
|
@ -0,0 +1,151 @@
|
|||
#!/bin/bash
|
||||
# Define custom utilities
|
||||
# Test for macOS with [ -n "$IS_MACOS" ]
|
||||
if [ -z "$IS_MACOS" ]; then
|
||||
export MB_ML_LIBC=${AUDITWHEEL_POLICY::9}
|
||||
export MB_ML_VER=${AUDITWHEEL_POLICY:9}
|
||||
fi
|
||||
export PLAT=$CIBW_ARCHS
|
||||
source wheels/multibuild/common_utils.sh
|
||||
source wheels/multibuild/library_builders.sh
|
||||
if [ -z "$IS_MACOS" ]; then
|
||||
source wheels/multibuild/manylinux_utils.sh
|
||||
fi
|
||||
|
||||
ARCHIVE_SDIR=pillow-depends-main
|
||||
|
||||
# Package versions for fresh source builds
|
||||
FREETYPE_VERSION=2.13.2
|
||||
HARFBUZZ_VERSION=8.3.0
|
||||
LIBPNG_VERSION=1.6.40
|
||||
JPEGTURBO_VERSION=3.0.1
|
||||
OPENJPEG_VERSION=2.5.0
|
||||
XZ_VERSION=5.4.5
|
||||
TIFF_VERSION=4.6.0
|
||||
LCMS2_VERSION=2.15
|
||||
if [[ -n "$IS_MACOS" ]]; then
|
||||
GIFLIB_VERSION=5.1.4
|
||||
else
|
||||
GIFLIB_VERSION=5.2.1
|
||||
fi
|
||||
if [[ -n "$IS_MACOS" ]] || [[ "$MB_ML_VER" != 2014 ]]; then
|
||||
ZLIB_VERSION=1.3
|
||||
else
|
||||
ZLIB_VERSION=1.2.8
|
||||
fi
|
||||
LIBWEBP_VERSION=1.3.2
|
||||
BZIP2_VERSION=1.0.8
|
||||
LIBXCB_VERSION=1.16
|
||||
BROTLI_VERSION=1.1.0
|
||||
|
||||
if [[ -n "$IS_MACOS" ]] && [[ "$CIBW_ARCHS" == "x86_64" ]]; then
|
||||
function build_openjpeg {
|
||||
local out_dir=$(fetch_unpack https://github.com/uclouvain/openjpeg/archive/v${OPENJPEG_VERSION}.tar.gz openjpeg-2.5.0.tar.gz)
|
||||
(cd $out_dir \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib . \
|
||||
&& make install)
|
||||
touch openjpeg-stamp
|
||||
}
|
||||
fi
|
||||
|
||||
function build_brotli {
|
||||
local cmake=$(get_modern_cmake)
|
||||
local out_dir=$(fetch_unpack https://github.com/google/brotli/archive/v$BROTLI_VERSION.tar.gz brotli-1.1.0.tar.gz)
|
||||
(cd $out_dir \
|
||||
&& $cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib . \
|
||||
&& make install)
|
||||
if [[ "$MB_ML_LIBC" == "manylinux" ]]; then
|
||||
cp /usr/local/lib64/libbrotli* /usr/local/lib
|
||||
cp /usr/local/lib64/pkgconfig/libbrotli* /usr/local/lib/pkgconfig
|
||||
fi
|
||||
}
|
||||
|
||||
function build {
|
||||
if [[ -n "$IS_MACOS" ]] && [[ "$CIBW_ARCHS" == "arm64" ]]; then
|
||||
export BUILD_PREFIX="/usr/local"
|
||||
fi
|
||||
build_xz
|
||||
if [ -z "$IS_ALPINE" ] && [ -z "$IS_MACOS" ]; then
|
||||
yum remove -y zlib-devel
|
||||
fi
|
||||
build_new_zlib
|
||||
|
||||
build_simple xcb-proto 1.16.0 https://xorg.freedesktop.org/archive/individual/proto
|
||||
if [ -n "$IS_MACOS" ]; then
|
||||
if [[ "$CIBW_ARCHS" == "arm64" ]]; then
|
||||
build_simple xorgproto 2023.2 https://www.x.org/pub/individual/proto
|
||||
build_simple libXau 1.0.11 https://www.x.org/pub/individual/lib
|
||||
build_simple libpthread-stubs 0.5 https://xcb.freedesktop.org/dist
|
||||
if [ -f /Library/Frameworks/Python.framework/Versions/Current/share/pkgconfig/xcb-proto.pc ]; then
|
||||
cp /Library/Frameworks/Python.framework/Versions/Current/share/pkgconfig/xcb-proto.pc /Library/Frameworks/Python.framework/Versions/Current/lib/pkgconfig/xcb-proto.pc
|
||||
fi
|
||||
fi
|
||||
else
|
||||
sed s/\${pc_sysrootdir\}// /usr/local/share/pkgconfig/xcb-proto.pc > /usr/local/lib/pkgconfig/xcb-proto.pc
|
||||
fi
|
||||
build_simple libxcb $LIBXCB_VERSION https://www.x.org/releases/individual/lib
|
||||
|
||||
build_libjpeg_turbo
|
||||
build_tiff
|
||||
build_libpng
|
||||
build_lcms2
|
||||
if [[ -n "$IS_MACOS" ]] && [[ "$CIBW_ARCHS" == "arm64" ]]; then
|
||||
for dylib in libjpeg.dylib libtiff.dylib liblcms2.dylib; do
|
||||
cp $BUILD_PREFIX/lib/$dylib /opt/arm64-builds/lib
|
||||
done
|
||||
fi
|
||||
build_openjpeg
|
||||
|
||||
ORIGINAL_CFLAGS=$CFLAGS
|
||||
CFLAGS="$CFLAGS -O3 -DNDEBUG"
|
||||
if [[ -n "$IS_MACOS" ]]; then
|
||||
CFLAGS="$CFLAGS -Wl,-headerpad_max_install_names"
|
||||
fi
|
||||
build_libwebp
|
||||
CFLAGS=$ORIGINAL_CFLAGS
|
||||
|
||||
build_brotli
|
||||
|
||||
if [ -n "$IS_MACOS" ]; then
|
||||
# Custom freetype build
|
||||
build_simple freetype $FREETYPE_VERSION https://download.savannah.gnu.org/releases/freetype tar.gz --with-harfbuzz=no
|
||||
else
|
||||
build_freetype
|
||||
fi
|
||||
|
||||
if [ -z "$IS_MACOS" ]; then
|
||||
export FREETYPE_LIBS=-lfreetype
|
||||
export FREETYPE_CFLAGS=-I/usr/local/include/freetype2/
|
||||
fi
|
||||
build_simple harfbuzz $HARFBUZZ_VERSION https://github.com/harfbuzz/harfbuzz/releases/download/$HARFBUZZ_VERSION tar.xz --with-freetype=yes --with-glib=no
|
||||
if [ -z "$IS_MACOS" ]; then
|
||||
export FREETYPE_LIBS=""
|
||||
export FREETYPE_CFLAGS=""
|
||||
fi
|
||||
}
|
||||
|
||||
# Any stuff that you need to do before you start building the wheels
|
||||
# Runs in the root directory of this repository.
|
||||
curl -fsSL -o pillow-depends-main.zip https://github.com/python-pillow/pillow-depends/archive/main.zip
|
||||
untar pillow-depends-main.zip
|
||||
|
||||
if [[ -n "$IS_MACOS" ]]; then
|
||||
# webp, libtiff, libxcb cause a conflict with building webp, libtiff, libxcb
|
||||
# libxdmcp causes an issue on macOS < 11
|
||||
# if php is installed, brew tries to reinstall these after installing openblas
|
||||
# remove cairo to fix building harfbuzz on arm64
|
||||
# remove lcms2 and libpng to fix building openjpeg on arm64
|
||||
# remove zstd to avoid inclusion on x86_64
|
||||
# curl from brew requires zstd, use system curl
|
||||
brew remove --ignore-dependencies webp libpng libtiff libxcb libxdmcp curl php cairo lcms2 ghostscript zstd
|
||||
|
||||
brew install pkg-config
|
||||
fi
|
||||
|
||||
wrap_wheel_builder build
|
||||
|
||||
# Append licenses
|
||||
for filename in wheels/dependency_licenses/*; do
|
||||
echo -e "\n\n----\n\n$(basename $filename | cut -f 1 -d '.')\n" | cat >> LICENSE
|
||||
cat $filename >> LICENSE
|
||||
done
|
69
.github/workflows/wheels-linux.yml
vendored
69
.github/workflows/wheels-linux.yml
vendored
|
@ -1,69 +0,0 @@
|
|||
name: Build Linux wheels
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
artifacts-name:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
CONFIG_PATH: "wheels/config.sh"
|
||||
REPO_DIR: "."
|
||||
TEST_DEPENDS: "pytest pytest-timeout"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.python }} ${{ matrix.mb-ml-libc }}${{ matrix.mb-ml-ver }}
|
||||
runs-on: "ubuntu-latest"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python: [
|
||||
"pypy3.9-7.3.13",
|
||||
"pypy3.10-7.3.13",
|
||||
"3.8",
|
||||
"3.9",
|
||||
"3.10",
|
||||
"3.11",
|
||||
"3.12",
|
||||
]
|
||||
mb-ml-libc: [ "manylinux" ]
|
||||
mb-ml-ver: [ 2014, "_2_28" ]
|
||||
include:
|
||||
- python: "3.8"
|
||||
mb-ml-libc: "musllinux"
|
||||
mb-ml-ver: "_1_1"
|
||||
- python: "3.9"
|
||||
mb-ml-libc: "musllinux"
|
||||
mb-ml-ver: "_1_1"
|
||||
- python: "3.10"
|
||||
mb-ml-libc: "musllinux"
|
||||
mb-ml-ver: "_1_1"
|
||||
- python: "3.11"
|
||||
mb-ml-libc: "musllinux"
|
||||
mb-ml-ver: "_1_1"
|
||||
- python: "3.12"
|
||||
mb-ml-libc: "musllinux"
|
||||
mb-ml-ver: "_1_1"
|
||||
env:
|
||||
MB_PYTHON_VERSION: ${{ matrix.python }}
|
||||
MB_ML_LIBC: ${{ matrix.mb-ml-libc }}
|
||||
MB_ML_VER: ${{ matrix.mb-ml-ver }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Build Wheel
|
||||
run: .github/workflows/wheels-build.sh
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ inputs.artifacts-name }}
|
||||
path: wheelhouse/*.whl
|
||||
# Uncomment to get SSH access for testing
|
||||
# - name: Setup tmate session
|
||||
# if: failure()
|
||||
# uses: mxschmitt/action-tmate@v3
|
57
.github/workflows/wheels-macos.yml
vendored
57
.github/workflows/wheels-macos.yml
vendored
|
@ -1,57 +0,0 @@
|
|||
name: Build macOS wheels
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
artifacts-name:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
CONFIG_PATH: "wheels/config.sh"
|
||||
REPO_DIR: "."
|
||||
TEST_DEPENDS: "pytest pytest-timeout"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.python }} ${{ matrix.platform }}
|
||||
runs-on: "macos-latest"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python: [
|
||||
"pypy3.9-7.3.13",
|
||||
"pypy3.10-7.3.13",
|
||||
"3.8",
|
||||
"3.9",
|
||||
"3.10",
|
||||
"3.11",
|
||||
"3.12",
|
||||
]
|
||||
platform: [ "x86_64", "arm64" ]
|
||||
exclude:
|
||||
- python: "pypy3.9-7.3.13"
|
||||
platform: "arm64"
|
||||
- python: "pypy3.10-7.3.13"
|
||||
platform: "arm64"
|
||||
env:
|
||||
PLAT: ${{ matrix.platform }}
|
||||
MB_PYTHON_VERSION: ${{ matrix.python }}
|
||||
TRAVIS_OS_NAME: "osx"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Build Wheel
|
||||
run: .github/workflows/wheels-build.sh
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ inputs.artifacts-name }}
|
||||
path: wheelhouse/*.whl
|
||||
# Uncomment to get SSH access for testing
|
||||
# - name: Setup tmate session
|
||||
# if: failure()
|
||||
# uses: mxschmitt/action-tmate@v3
|
22
.github/workflows/wheels-test.ps1
vendored
Normal file
22
.github/workflows/wheels-test.ps1
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
param ([string]$venv, [string]$pillow="C:\pillow")
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
Set-PSDebug -Trace 1
|
||||
if ("$venv" -like "*\cibw-run-*\pp*-win_amd64\*") {
|
||||
# unlike CPython, PyPy requires Visual C++ Redistributable to be installed
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
Invoke-WebRequest -Uri 'https://aka.ms/vs/15/release/vc_redist.x64.exe' -OutFile 'vc_redist.x64.exe'
|
||||
C:\vc_redist.x64.exe /install /quiet /norestart | Out-Null
|
||||
}
|
||||
$env:path += ";$pillow\winbuild\build\bin\"
|
||||
& "$venv\Scripts\activate.ps1"
|
||||
& reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\python.exe" /v "GlobalFlag" /t REG_SZ /d "0x02000000" /f
|
||||
cd $pillow
|
||||
& python -VV
|
||||
if (!$?) { exit $LASTEXITCODE }
|
||||
& python selftest.py
|
||||
if (!$?) { exit $LASTEXITCODE }
|
||||
& python -m pytest -vx Tests\check_wheel.py
|
||||
if (!$?) { exit $LASTEXITCODE }
|
||||
& python -m pytest -vx Tests
|
||||
if (!$?) { exit $LASTEXITCODE }
|
25
.github/workflows/wheels-test.sh
vendored
Executable file
25
.github/workflows/wheels-test.sh
vendored
Executable file
|
@ -0,0 +1,25 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
brew install fribidi
|
||||
export PKG_CONFIG_PATH="/usr/local/opt/openblas/lib/pkgconfig"
|
||||
elif [ "${AUDITWHEEL_POLICY::9}" == "musllinux" ]; then
|
||||
apk add curl fribidi
|
||||
else
|
||||
yum install -y fribidi
|
||||
fi
|
||||
if [ "${AUDITWHEEL_POLICY::9}" != "musllinux" ]; then
|
||||
python3 -m pip install numpy
|
||||
fi
|
||||
|
||||
if [ ! -d "test-images-main" ]; then
|
||||
curl -fsSL -o pillow-test-images.zip https://github.com/python-pillow/test-images/archive/main.zip
|
||||
unzip pillow-test-images.zip
|
||||
mv test-images-main/* Tests/images
|
||||
fi
|
||||
|
||||
# Runs tests
|
||||
python3 selftest.py
|
||||
python3 -m pytest Tests/check_wheel.py
|
||||
python3 -m pytest
|
188
.github/workflows/wheels.yml
vendored
188
.github/workflows/wheels.yml
vendored
|
@ -3,14 +3,20 @@ name: Wheels
|
|||
on:
|
||||
push:
|
||||
paths:
|
||||
- ".github/workflows/wheels*.yml"
|
||||
- ".ci/requirements-cibw.txt"
|
||||
- ".github/workflows/wheel*"
|
||||
- "wheels/*"
|
||||
- "winbuild/build_prepare.py"
|
||||
- "winbuild/fribidi.cmake"
|
||||
tags:
|
||||
- "*"
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/wheels*.yml"
|
||||
- ".ci/requirements-cibw.txt"
|
||||
- ".github/workflows/wheel*"
|
||||
- "wheels/*"
|
||||
- "winbuild/build_prepare.py"
|
||||
- "winbuild/fribidi.cmake"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
|
@ -20,21 +26,179 @@ concurrency:
|
|||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
macos:
|
||||
uses: ./.github/workflows/wheels-macos.yml
|
||||
with:
|
||||
artifacts-name: "wheels"
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
linux:
|
||||
uses: ./.github/workflows/wheels-linux.yml
|
||||
with:
|
||||
artifacts-name: "wheels"
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: "macOS x86_64"
|
||||
os: macos-latest
|
||||
archs: x86_64
|
||||
macosx_deployment_target: "10.10"
|
||||
- name: "macOS arm64"
|
||||
os: macos-latest
|
||||
archs: arm64
|
||||
macosx_deployment_target: "11.0"
|
||||
- name: "manylinux2014 and musllinux x86_64"
|
||||
os: ubuntu-latest
|
||||
archs: x86_64
|
||||
- name: "manylinux_2_28 x86_64"
|
||||
os: ubuntu-latest
|
||||
archs: x86_64
|
||||
build: "*manylinux*"
|
||||
manylinux: "manylinux_2_28"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
|
||||
- name: Build wheels
|
||||
run: |
|
||||
python3 -m pip install -r .ci/requirements-cibw.txt
|
||||
python3 -m cibuildwheel --output-dir wheelhouse
|
||||
env:
|
||||
CIBW_ARCHS: ${{ matrix.archs }}
|
||||
CIBW_BUILD: ${{ matrix.build }}
|
||||
CIBW_MANYLINUX_PYPY_X86_64_IMAGE: ${{ matrix.manylinux }}
|
||||
CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux }}
|
||||
CIBW_SKIP: pp38-*
|
||||
CIBW_TEST_SKIP: "*-macosx_arm64"
|
||||
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx_deployment_target }}
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: dist
|
||||
path: ./wheelhouse/*.whl
|
||||
|
||||
windows:
|
||||
name: Windows ${{ matrix.arch }}
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- arch: x86
|
||||
cibw_arch: x86
|
||||
- arch: x64
|
||||
cibw_arch: AMD64
|
||||
- arch: ARM64
|
||||
cibw_arch: ARM64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Checkout extra test images
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: python-pillow/test-images
|
||||
path: Tests\test-images
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
|
||||
- name: Prepare for build
|
||||
run: |
|
||||
choco install nasm --no-progress
|
||||
echo "C:\Program Files\NASM" >> $env:GITHUB_PATH
|
||||
|
||||
# Install extra test images
|
||||
xcopy /S /Y Tests\test-images\* Tests\images
|
||||
|
||||
& python.exe -m pip install -r .ci/requirements-cibw.txt
|
||||
|
||||
# Cannot cross-compile FriBiDi (only used for tests)
|
||||
$FLAGS = ("--no-imagequant", "--architecture=${{ matrix.arch }}")
|
||||
if ('${{ matrix.arch }}' -eq 'ARM64') { $FLAGS += "--no-fribidi" }
|
||||
& python.exe winbuild\build_prepare.py -v @FLAGS
|
||||
shell: pwsh
|
||||
|
||||
- name: Build wheels
|
||||
run: |
|
||||
setlocal EnableDelayedExpansion
|
||||
for %%f in (winbuild\build\license\*) do (
|
||||
set x=%%~nf
|
||||
rem Skip FriBiDi license, it is not included in the wheel.
|
||||
set fribidi=!x:~0,7!
|
||||
if NOT !fribidi!==fribidi (
|
||||
rem Skip imagequant license, it is not included in the wheel.
|
||||
set libimagequant=!x:~0,13!
|
||||
if NOT !libimagequant!==libimagequant (
|
||||
echo. >> LICENSE
|
||||
echo ===== %%~nf ===== >> LICENSE
|
||||
echo. >> LICENSE
|
||||
type %%f >> LICENSE
|
||||
)
|
||||
)
|
||||
)
|
||||
call winbuild\\build\\build_env.cmd
|
||||
%pythonLocation%\python.exe -m cibuildwheel . --output-dir wheelhouse
|
||||
env:
|
||||
CIBW_ARCHS: ${{ matrix.cibw_arch }}
|
||||
CIBW_BEFORE_ALL: "{package}\\winbuild\\build\\build_dep_all.cmd"
|
||||
CIBW_CACHE_PATH: "C:\\cibw"
|
||||
CIBW_TEST_SKIP: "*-win_arm64"
|
||||
CIBW_TEST_COMMAND: 'docker run --rm
|
||||
-v {project}:C:\pillow
|
||||
-v C:\cibw:C:\cibw
|
||||
-v %CD%\..\venv-test:%CD%\..\venv-test
|
||||
-e CI -e GITHUB_ACTIONS
|
||||
mcr.microsoft.com/windows/servercore:ltsc2022
|
||||
powershell C:\pillow\.github\workflows\wheels-test.ps1 %CD%\..\venv-test'
|
||||
shell: cmd
|
||||
|
||||
- name: Upload wheels
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: dist
|
||||
path: ./wheelhouse/*.whl
|
||||
|
||||
- name: Prepare to upload FriBiDi
|
||||
if: "matrix.arch != 'ARM64'"
|
||||
run: |
|
||||
mkdir fribidi\${{ matrix.arch }}
|
||||
copy winbuild\build\bin\fribidi* fribidi\${{ matrix.arch }}
|
||||
shell: cmd
|
||||
|
||||
- name: Upload fribidi.dll
|
||||
if: "matrix.arch != 'ARM64'"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: fribidi
|
||||
path: fribidi\*
|
||||
|
||||
sdist:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
cache: pip
|
||||
cache-dependency-path: "Makefile"
|
||||
|
||||
- run: make sdist
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: dist
|
||||
path: dist/*.tar.gz
|
||||
|
||||
success:
|
||||
permissions:
|
||||
contents: none
|
||||
needs: [macos, linux]
|
||||
needs: [build, windows, sdist]
|
||||
runs-on: ubuntu-latest
|
||||
name: Wheels Successful
|
||||
steps:
|
||||
|
|
|
@ -9,7 +9,6 @@ repos:
|
|||
rev: 23.10.1
|
||||
hooks:
|
||||
- id: black
|
||||
args: [--target-version=py38]
|
||||
|
||||
- repo: https://github.com/PyCQA/bandit
|
||||
rev: 1.7.5
|
||||
|
|
115
.travis.yml
115
.travis.yml
|
@ -2,10 +2,8 @@ if: tag IS present OR type = api
|
|||
|
||||
env:
|
||||
global:
|
||||
- CONFIG_PATH=wheels/config.sh
|
||||
- REPO_DIR=.
|
||||
- PLAT=aarch64
|
||||
- TEST_DEPENDS=pytest-timeout
|
||||
- CIBW_ARCHS=aarch64
|
||||
- CIBW_SKIP=pp38-*
|
||||
|
||||
language: python
|
||||
# Default Python version is usually 3.6
|
||||
|
@ -15,120 +13,39 @@ services: docker
|
|||
|
||||
jobs:
|
||||
include:
|
||||
- name: "3.8 Focal manylinux2014 aarch64"
|
||||
- name: "manylinux2014 aarch64"
|
||||
os: linux
|
||||
arch: arm64
|
||||
env:
|
||||
- MB_ML_VER=2014
|
||||
- MB_PYTHON_VERSION=3.8
|
||||
- name: "3.8 Focal manylinux_2_28 aarch64"
|
||||
- CIBW_BUILD="*manylinux*"
|
||||
- CIBW_MANYLINUX_AARCH64_IMAGE=manylinux2014
|
||||
- CIBW_MANYLINUX_PYPY_AARCH64_IMAGE=manylinux2014
|
||||
- name: "manylinux_2_28 aarch64"
|
||||
os: linux
|
||||
arch: arm64
|
||||
env:
|
||||
- MB_ML_VER="_2_28"
|
||||
- MB_PYTHON_VERSION=3.8
|
||||
- name: "3.8 musllinux_1_1 aarch64"
|
||||
- CIBW_BUILD="*manylinux*"
|
||||
- CIBW_MANYLINUX_AARCH64_IMAGE=manylinux_2_28
|
||||
- CIBW_MANYLINUX_PYPY_AARCH64_IMAGE=manylinux_2_28
|
||||
- name: "musllinux aarch64"
|
||||
os: linux
|
||||
arch: arm64
|
||||
env:
|
||||
- MB_ML_VER="_1_1"
|
||||
- MB_ML_LIBC="musllinux"
|
||||
- MB_PYTHON_VERSION=3.8
|
||||
- name: "3.9 Focal manylinux2014 aarch64"
|
||||
os: linux
|
||||
arch: arm64
|
||||
env:
|
||||
- MB_ML_VER=2014
|
||||
- MB_PYTHON_VERSION=3.9
|
||||
- name: "3.9 Focal manylinux_2_28 aarch64"
|
||||
os: linux
|
||||
arch: arm64
|
||||
env:
|
||||
- MB_ML_VER="_2_28"
|
||||
- MB_PYTHON_VERSION=3.9
|
||||
- name: "3.9 musllinux_1_1 aarch64"
|
||||
os: linux
|
||||
arch: arm64
|
||||
env:
|
||||
- MB_ML_VER="_1_1"
|
||||
- MB_ML_LIBC="musllinux"
|
||||
- MB_PYTHON_VERSION=3.9
|
||||
- name: "3.10 Focal manylinux2014 aarch64"
|
||||
os: linux
|
||||
arch: arm64
|
||||
env:
|
||||
- MB_ML_VER=2014
|
||||
- MB_PYTHON_VERSION=3.10
|
||||
- name: "3.10 Focal manylinux_2_28 aarch64"
|
||||
os: linux
|
||||
arch: arm64
|
||||
env:
|
||||
- MB_ML_VER="_2_28"
|
||||
- MB_PYTHON_VERSION=3.10
|
||||
- name: "3.10 musllinux_1_1 aarch64"
|
||||
os: linux
|
||||
arch: arm64
|
||||
env:
|
||||
- MB_ML_VER="_1_1"
|
||||
- MB_ML_LIBC="musllinux"
|
||||
- MB_PYTHON_VERSION=3.10
|
||||
- name: "3.11 Focal manylinux_2_28 aarch64"
|
||||
os: linux
|
||||
arch: arm64
|
||||
env:
|
||||
- MB_ML_VER=2014
|
||||
- MB_PYTHON_VERSION=3.11
|
||||
- name: "3.11 Focal manylinux_2_28 aarch64"
|
||||
os: linux
|
||||
arch: arm64
|
||||
env:
|
||||
- MB_ML_VER="_2_28"
|
||||
- MB_PYTHON_VERSION=3.11
|
||||
- name: "3.11 musllinux_1_1 aarch64"
|
||||
os: linux
|
||||
arch: arm64
|
||||
env:
|
||||
- MB_ML_VER="_1_1"
|
||||
- MB_ML_LIBC="musllinux"
|
||||
- MB_PYTHON_VERSION=3.11
|
||||
- name: "3.12 Focal manylinux_2_28 aarch64"
|
||||
os: linux
|
||||
arch: arm64
|
||||
env:
|
||||
- MB_ML_VER=2014
|
||||
- MB_PYTHON_VERSION=3.12
|
||||
- name: "3.12 Focal manylinux_2_28 aarch64"
|
||||
os: linux
|
||||
arch: arm64
|
||||
env:
|
||||
- MB_ML_VER="_2_28"
|
||||
- MB_PYTHON_VERSION=3.12
|
||||
- name: "3.12 musllinux_1_1 aarch64"
|
||||
os: linux
|
||||
arch: arm64
|
||||
env:
|
||||
- MB_ML_VER="_1_1"
|
||||
- MB_ML_LIBC="musllinux"
|
||||
- MB_PYTHON_VERSION=3.12
|
||||
|
||||
before_install:
|
||||
- source wheels/multibuild/common_utils.sh
|
||||
- source wheels/multibuild/travis_steps.sh
|
||||
- before_install
|
||||
- CIBW_BUILD="*musllinux*"
|
||||
|
||||
install:
|
||||
- build_multilinux aarch64 build_wheel
|
||||
- ls -l "${TRAVIS_BUILD_DIR}/${WHEEL_SDIR}/"
|
||||
- python3 -m pip install -r .ci/requirements-cibw.txt
|
||||
|
||||
script:
|
||||
- install_run
|
||||
- python3 -m cibuildwheel --output-dir wheelhouse
|
||||
- ls -l "${TRAVIS_BUILD_DIR}/wheelhouse/"
|
||||
|
||||
# Upload wheels to GitHub Releases
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key: $GITHUB_RELEASE_TOKEN
|
||||
file_glob: true
|
||||
file: "${TRAVIS_BUILD_DIR}/${WHEEL_SDIR}/*.whl"
|
||||
file: "${TRAVIS_BUILD_DIR}/wheelhouse/*.whl"
|
||||
on:
|
||||
repo: python-pillow/Pillow
|
||||
tags: true
|
||||
|
|
18
CHANGES.rst
18
CHANGES.rst
|
@ -5,6 +5,24 @@ Changelog (Pillow)
|
|||
10.2.0 (unreleased)
|
||||
-------------------
|
||||
|
||||
- Raise ValueError when TrueType font size is not greater than zero #7584
|
||||
[akx, radarhere]
|
||||
|
||||
- If absent, do not try to close fp when closing image #7557
|
||||
[RaphaelVRossi, radarhere]
|
||||
|
||||
- Allow configuring JPEG restart marker interval on save #7488
|
||||
[bgilbert, radarhere]
|
||||
|
||||
- Decrement reference count for PyObject #7549
|
||||
[radarhere]
|
||||
|
||||
- Implement ``streamtype=1`` option for tables-only JPEG encoding #7491
|
||||
[bgilbert, radarhere]
|
||||
|
||||
- If save_all PNG only has one frame, do not create animated image #7522
|
||||
[radarhere]
|
||||
|
||||
- Fixed frombytes() for images with a zero dimension #7493
|
||||
[radarhere]
|
||||
|
||||
|
|
2
Makefile
2
Makefile
|
@ -118,6 +118,6 @@ lint:
|
|||
.PHONY: lint-fix
|
||||
lint-fix:
|
||||
python3 -c "import black" > /dev/null 2>&1 || python3 -m pip install black
|
||||
python3 -m black --target-version py38 .
|
||||
python3 -m black .
|
||||
python3 -c "import ruff" > /dev/null 2>&1 || python3 -m pip install ruff
|
||||
python3 -m ruff --fix .
|
||||
|
|
20
RELEASING.md
20
RELEASING.md
|
@ -20,12 +20,8 @@ Released quarterly on January 2nd, April 1st, July 1st and October 15th.
|
|||
git tag 5.2.0
|
||||
git push --tags
|
||||
```
|
||||
* [ ] Create and check source distribution:
|
||||
```bash
|
||||
make sdist
|
||||
```
|
||||
* [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/main/RELEASING.md#binary-distributions)
|
||||
* [ ] Check and upload all binaries and source distributions e.g.:
|
||||
* [ ] Create [source and binary distributions](https://github.com/python-pillow/Pillow/blob/main/RELEASING.md#source-and-binary-distributions)
|
||||
* [ ] Check and upload all source and binary distributions e.g.:
|
||||
```bash
|
||||
python3 -m twine check --strict dist/*
|
||||
python3 -m twine upload dist/Pillow-5.2.0*
|
||||
|
@ -59,8 +55,8 @@ Released as needed for security, installation or critical bug fixes.
|
|||
```bash
|
||||
make sdist
|
||||
```
|
||||
* [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/main/RELEASING.md#binary-distributions)
|
||||
* [ ] Check and upload all binaries and source distributions e.g.:
|
||||
* [ ] Create [source and binary distributions](https://github.com/python-pillow/Pillow/blob/main/RELEASING.md#source-and-binary-distributions)
|
||||
* [ ] Check and upload all source and binary distributions e.g.:
|
||||
```bash
|
||||
python3 -m twine check --strict dist/*
|
||||
python3 -m twine upload dist/Pillow-5.2.1*
|
||||
|
@ -90,20 +86,20 @@ Released as needed privately to individual vendors for critical security-related
|
|||
```bash
|
||||
make sdist
|
||||
```
|
||||
* [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/main/RELEASING.md#binary-distributions)
|
||||
* [ ] Create [source and binary distributions](https://github.com/python-pillow/Pillow/blob/main/RELEASING.md#source-and-binary-distributions)
|
||||
* [ ] Publish the [release on GitHub](https://github.com/python-pillow/Pillow/releases) and then:
|
||||
```bash
|
||||
git push origin 2.5.x
|
||||
```
|
||||
|
||||
## Binary Distributions
|
||||
## Source and Binary Distributions
|
||||
|
||||
### macOS and Linux
|
||||
* [ ] Download wheels from the [GitHub Actions "Wheels" workflow](https://github.com/python-pillow/Pillow/actions/workflows/wheels.yml)
|
||||
* [ ] Download sdist and wheels from the [GitHub Actions "Wheels" workflow](https://github.com/python-pillow/Pillow/actions/workflows/wheels.yml)
|
||||
and copy into `dist/`. For example using [GitHub CLI](https://github.com/cli/cli):
|
||||
```bash
|
||||
gh run download --dir dist
|
||||
# select wheels
|
||||
# select dist
|
||||
```
|
||||
* [ ] Download the Linux aarch64 wheels created by Travis CI from [GitHub releases](https://github.com/python-pillow/Pillow/releases)
|
||||
and copy into `dist`.
|
||||
|
|
|
@ -45,7 +45,7 @@ def test_direct():
|
|||
|
||||
assert caccess[(0, 0)] == access[(0, 0)]
|
||||
|
||||
print("Size: %sx%s" % im.size) # noqa: UP031
|
||||
print(f"Size: {im.width}x{im.height}")
|
||||
timer(iterate_get, "PyAccess - get", im.size, access)
|
||||
timer(iterate_set, "PyAccess - set", im.size, access)
|
||||
timer(iterate_get, "C-api - get", im.size, caccess)
|
||||
|
|
41
Tests/check_wheel.py
Normal file
41
Tests/check_wheel.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
import sys
|
||||
|
||||
from PIL import features
|
||||
|
||||
|
||||
def test_wheel_modules():
|
||||
expected_modules = {"pil", "tkinter", "freetype2", "littlecms2", "webp"}
|
||||
|
||||
# tkinter is not available in cibuildwheel installed CPython on Windows
|
||||
try:
|
||||
import tkinter
|
||||
|
||||
assert tkinter
|
||||
except ImportError:
|
||||
expected_modules.remove("tkinter")
|
||||
|
||||
assert set(features.get_supported_modules()) == expected_modules
|
||||
|
||||
|
||||
def test_wheel_codecs():
|
||||
expected_codecs = {"jpg", "jpg_2000", "zlib", "libtiff"}
|
||||
|
||||
assert set(features.get_supported_codecs()) == expected_codecs
|
||||
|
||||
|
||||
def test_wheel_features():
|
||||
expected_features = {
|
||||
"webp_anim",
|
||||
"webp_mux",
|
||||
"transp_webp",
|
||||
"raqm",
|
||||
"fribidi",
|
||||
"harfbuzz",
|
||||
"libjpeg_turbo",
|
||||
"xcb",
|
||||
}
|
||||
|
||||
if sys.platform == "win32":
|
||||
expected_features.remove("xcb")
|
||||
|
||||
assert set(features.get_supported_features()) == expected_features
|
|
@ -5,6 +5,7 @@ Helper functions.
|
|||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import sysconfig
|
||||
import tempfile
|
||||
|
@ -95,7 +96,7 @@ def assert_image_equal(a, b, msg=None):
|
|||
except Exception:
|
||||
pass
|
||||
|
||||
assert False, msg or "got different content"
|
||||
pytest.fail(msg or "got different content")
|
||||
|
||||
|
||||
def assert_image_equal_tofile(a, filename, msg=None, mode=None):
|
||||
|
@ -258,11 +259,21 @@ def hopper(mode=None, cache={}):
|
|||
|
||||
|
||||
def djpeg_available():
|
||||
return bool(shutil.which("djpeg"))
|
||||
if shutil.which("djpeg"):
|
||||
try:
|
||||
subprocess.check_call(["djpeg", "-version"])
|
||||
return True
|
||||
except subprocess.CalledProcessError: # pragma: no cover
|
||||
return False
|
||||
|
||||
|
||||
def cjpeg_available():
|
||||
return bool(shutil.which("cjpeg"))
|
||||
if shutil.which("cjpeg"):
|
||||
try:
|
||||
subprocess.check_call(["cjpeg", "-version"])
|
||||
return True
|
||||
except subprocess.CalledProcessError: # pragma: no cover
|
||||
return False
|
||||
|
||||
|
||||
def netpbm_available():
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import sys
|
||||
from io import BytesIO, StringIO
|
||||
|
||||
import pytest
|
||||
|
||||
from PIL import Image, IptcImagePlugin
|
||||
|
||||
from .helper import hopper
|
||||
|
@ -44,7 +46,7 @@ def test_getiptcinfo_fotostation():
|
|||
for tag in iptc.keys():
|
||||
if tag[0] == 240:
|
||||
return
|
||||
assert False, "FotoStation tag not found"
|
||||
pytest.fail("FotoStation tag not found")
|
||||
|
||||
|
||||
def test_getiptcinfo_zero_padding():
|
||||
|
|
|
@ -643,6 +643,23 @@ class TestFileJpeg:
|
|||
assert max(im2.quantization[0]) <= 255
|
||||
assert max(im2.quantization[1]) <= 255
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"blocks, rows, markers",
|
||||
((0, 0, 0), (1, 0, 15), (3, 0, 5), (8, 0, 1), (0, 1, 3), (0, 2, 1)),
|
||||
)
|
||||
def test_restart_markers(self, blocks, rows, markers):
|
||||
im = Image.new("RGB", (32, 32)) # 16 MCUs
|
||||
out = BytesIO()
|
||||
im.save(
|
||||
out,
|
||||
format="JPEG",
|
||||
restart_marker_blocks=blocks,
|
||||
restart_marker_rows=rows,
|
||||
# force 8x8 pixel MCUs
|
||||
subsampling=0,
|
||||
)
|
||||
assert len(re.findall(b"\xff[\xd0-\xd7]", out.getvalue())) == markers
|
||||
|
||||
@pytest.mark.skipif(not djpeg_available(), reason="djpeg not available")
|
||||
def test_load_djpeg(self):
|
||||
with Image.open(TEST_FILE) as img:
|
||||
|
@ -961,6 +978,28 @@ class TestFileJpeg:
|
|||
im.load()
|
||||
ImageFile.LOAD_TRUNCATED_IMAGES = False
|
||||
|
||||
def test_separate_tables(self):
|
||||
im = hopper()
|
||||
data = [] # [interchange, tables-only, image-only]
|
||||
for streamtype in range(3):
|
||||
out = BytesIO()
|
||||
im.save(out, format="JPEG", streamtype=streamtype)
|
||||
data.append(out.getvalue())
|
||||
|
||||
# SOI, EOI
|
||||
for marker in b"\xff\xd8", b"\xff\xd9":
|
||||
assert marker in data[1] and marker in data[2]
|
||||
# DHT, DQT
|
||||
for marker in b"\xff\xc4", b"\xff\xdb":
|
||||
assert marker in data[1] and marker not in data[2]
|
||||
# SOF0, SOS, APP0 (JFIF header)
|
||||
for marker in b"\xff\xc0", b"\xff\xda", b"\xff\xe0":
|
||||
assert marker not in data[1] and marker in data[2]
|
||||
|
||||
with Image.open(BytesIO(data[0])) as interchange_im:
|
||||
with Image.open(BytesIO(data[1] + data[2])) as combined_im:
|
||||
assert_image_equal(interchange_im, combined_im)
|
||||
|
||||
def test_repr_jpeg(self):
|
||||
im = hopper()
|
||||
|
||||
|
|
|
@ -416,7 +416,7 @@ def test_plt_marker():
|
|||
while True:
|
||||
marker = out.read(2)
|
||||
if not marker:
|
||||
assert False, "End of stream without PLT"
|
||||
pytest.fail("End of stream without PLT")
|
||||
|
||||
jp2_boxid = _binary.i16be(marker)
|
||||
if jp2_boxid == 0xFF4F:
|
||||
|
@ -426,7 +426,7 @@ def test_plt_marker():
|
|||
# PLT
|
||||
return
|
||||
elif jp2_boxid == 0xFF93:
|
||||
assert False, "SOD without finding PLT first"
|
||||
pytest.fail("SOD without finding PLT first")
|
||||
|
||||
hdr = out.read(2)
|
||||
length = _binary.i16be(hdr)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import io
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
@ -999,7 +1000,7 @@ class TestImage:
|
|||
with Image.open(os.path.join("Tests/images", path)) as im:
|
||||
try:
|
||||
im.load()
|
||||
assert False
|
||||
pytest.fail()
|
||||
except OSError as e:
|
||||
buffer_overrun = str(e) == "buffer overrun when reading image file"
|
||||
truncated = "image file is truncated" in str(e)
|
||||
|
@ -1010,10 +1011,19 @@ class TestImage:
|
|||
with Image.open("Tests/images/fli_overrun2.bin") as im:
|
||||
try:
|
||||
im.seek(1)
|
||||
assert False
|
||||
pytest.fail()
|
||||
except OSError as e:
|
||||
assert str(e) == "buffer overrun when reading image file"
|
||||
|
||||
def test_close_graceful(self, caplog):
|
||||
with Image.open("Tests/images/hopper.jpg") as im:
|
||||
copy = im.copy()
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
im.close()
|
||||
copy.close()
|
||||
assert len(caplog.records) == 0
|
||||
assert im.fp is None
|
||||
|
||||
|
||||
class MockEncoder:
|
||||
pass
|
||||
|
|
|
@ -67,7 +67,7 @@ def test_quantize_no_dither():
|
|||
|
||||
def test_quantize_no_dither2():
|
||||
im = Image.new("RGB", (9, 1))
|
||||
im.putdata(list((p,) * 3 for p in range(0, 36, 4)))
|
||||
im.putdata([(p,) * 3 for p in range(0, 36, 4)])
|
||||
|
||||
palette = Image.new("P", (1, 1))
|
||||
data = (0, 0, 0, 32, 32, 32)
|
||||
|
|
|
@ -195,7 +195,7 @@ class TestReducingGapResize:
|
|||
(52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=1.0
|
||||
)
|
||||
|
||||
with pytest.raises(AssertionError):
|
||||
with pytest.raises(pytest.fail.Exception):
|
||||
assert_image_equal(ref, im)
|
||||
|
||||
assert_image_similar(ref, im, epsilon)
|
||||
|
@ -210,7 +210,7 @@ class TestReducingGapResize:
|
|||
(52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=2.0
|
||||
)
|
||||
|
||||
with pytest.raises(AssertionError):
|
||||
with pytest.raises(pytest.fail.Exception):
|
||||
assert_image_equal(ref, im)
|
||||
|
||||
assert_image_similar(ref, im, epsilon)
|
||||
|
@ -225,7 +225,7 @@ class TestReducingGapResize:
|
|||
(52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=3.0
|
||||
)
|
||||
|
||||
with pytest.raises(AssertionError):
|
||||
with pytest.raises(pytest.fail.Exception):
|
||||
assert_image_equal(ref, im)
|
||||
|
||||
assert_image_similar(ref, im, epsilon)
|
||||
|
|
|
@ -147,7 +147,7 @@ def test_reducing_gap_values():
|
|||
|
||||
ref = hopper()
|
||||
ref.thumbnail((18, 18), Image.Resampling.BICUBIC, reducing_gap=None)
|
||||
with pytest.raises(AssertionError):
|
||||
with pytest.raises(pytest.fail.Exception):
|
||||
assert_image_equal(ref, im)
|
||||
|
||||
assert_image_similar(ref, im, 3.5)
|
||||
|
|
|
@ -1082,3 +1082,9 @@ def test_raqm_missing_warning(monkeypatch):
|
|||
"Raqm layout was requested, but Raqm is not available. "
|
||||
"Falling back to basic layout."
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("size", [-1, 0])
|
||||
def test_invalid_truetype_sizes_raise_valueerror(layout_engine, size):
|
||||
with pytest.raises(ValueError):
|
||||
ImageFont.truetype(FONT_PATH, size, layout_engine=layout_engine)
|
||||
|
|
|
@ -11,6 +11,10 @@ from .helper import assert_image_equal_tofile, skip_unless_feature
|
|||
|
||||
|
||||
class TestImageGrab:
|
||||
@pytest.mark.skipif(
|
||||
os.environ.get("USERNAME") == "ContainerAdministrator",
|
||||
reason="can't grab screen when running in Docker",
|
||||
)
|
||||
@pytest.mark.skipif(
|
||||
sys.platform not in ("win32", "darwin"), reason="requires Windows or macOS"
|
||||
)
|
||||
|
|
|
@ -85,7 +85,7 @@ def test_ipythonviewer():
|
|||
test_viewer = viewer
|
||||
break
|
||||
else:
|
||||
assert False
|
||||
pytest.fail()
|
||||
|
||||
im = hopper()
|
||||
assert test_viewer.show(im) == 1
|
||||
|
|
10
docs/conf.py
10
docs/conf.py
|
@ -166,6 +166,12 @@ html_static_path = ["resources"]
|
|||
# directly to the root of the documentation.
|
||||
# html_extra_path = []
|
||||
|
||||
html_css_files = ["css/dark.css"]
|
||||
|
||||
html_js_files = [
|
||||
"js/activate_tab.js",
|
||||
]
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
# html_last_updated_fmt = '%b %d, %Y'
|
||||
|
@ -313,10 +319,6 @@ texinfo_documents = [
|
|||
# texinfo_no_detailmenu = False
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_css_file("css/dark.css")
|
||||
|
||||
|
||||
linkcheck_allowed_redirects = {
|
||||
r"https://www.bestpractices.dev/projects/6331": r"https://www.bestpractices.dev/en/.*",
|
||||
r"https://badges.gitter.im/python-pillow/Pillow.svg": r"https://badges.gitter.im/repo.svg",
|
||||
|
|
|
@ -266,9 +266,12 @@ following options are available::
|
|||
:py:class:`PIL.ImagePalette.ImagePalette` object.
|
||||
|
||||
**optimize**
|
||||
If present and true, attempt to compress the palette by
|
||||
eliminating unused colors. This is only useful if the palette can
|
||||
be compressed to the next smaller power of 2 elements.
|
||||
Whether to attempt to compress the palette by eliminating unused colors.
|
||||
This is attempted by default, unless a palette is specified as an option or
|
||||
as part of the first image's :py:attr:`~PIL.Image.Image.info` dictionary.
|
||||
|
||||
This is only useful if the palette can be compressed to the next smaller
|
||||
power of 2 elements.
|
||||
|
||||
Note that if the image you are saving comes from an existing GIF, it may have
|
||||
the following properties in its :py:attr:`~PIL.Image.Image.info` dictionary.
|
||||
|
@ -494,6 +497,18 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
|
|||
|
||||
If absent, the setting will be determined by libjpeg or libjpeg-turbo.
|
||||
|
||||
**restart_marker_blocks**
|
||||
If present, emit a restart marker whenever the specified number of MCU
|
||||
blocks has been produced.
|
||||
|
||||
.. versionadded:: 10.2.0
|
||||
|
||||
**restart_marker_rows**
|
||||
If present, emit a restart marker whenever the specified number of MCU
|
||||
rows has been produced.
|
||||
|
||||
.. versionadded:: 10.2.0
|
||||
|
||||
**qtables**
|
||||
If present, sets the qtables for the encoder. This is listed as an
|
||||
advanced option for wizards in the JPEG documentation. Use with
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
Installation
|
||||
============
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
activateTab(getOS());
|
||||
});
|
||||
</script>
|
||||
|
||||
Warnings
|
||||
--------
|
||||
|
||||
|
@ -151,13 +159,13 @@ Many of Pillow's features require external libraries:
|
|||
|
||||
* Pillow has been tested with libjpeg versions **6b**, **8**, **9-9d** and
|
||||
libjpeg-turbo version **8**.
|
||||
* Starting with Pillow 3.0.0, libjpeg is required by default, but
|
||||
may be disabled with the ``--disable-jpeg`` flag.
|
||||
* Starting with Pillow 3.0.0, libjpeg is required by default. It can be
|
||||
disabled with the ``-C jpeg=disable`` flag.
|
||||
|
||||
* **zlib** provides access to compressed PNGs
|
||||
|
||||
* Starting with Pillow 3.0.0, zlib is required by default, but may
|
||||
be disabled with the ``--disable-zlib`` flag.
|
||||
* Starting with Pillow 3.0.0, zlib is required by default. It can be
|
||||
disabled with the ``-C zlib=disable`` flag.
|
||||
|
||||
* **libtiff** provides compressed TIFF functionality
|
||||
|
||||
|
@ -176,8 +184,6 @@ Many of Pillow's features require external libraries:
|
|||
transparent WebP files. Versions **0.3.0** and above support
|
||||
transparency.
|
||||
|
||||
* **tcl/tk** provides support for tkinter bitmap and photo images.
|
||||
|
||||
* **openjpeg** provides JPEG 2000 functionality.
|
||||
|
||||
* Pillow has been tested with openjpeg **2.0.0**, **2.1.0**, **2.3.1**,
|
||||
|
@ -463,6 +469,8 @@ These platforms are built and tested for every change.
|
|||
+----------------------------------+----------------------------+---------------------+
|
||||
| Fedora 38 | 3.11 | x86-64 |
|
||||
+----------------------------------+----------------------------+---------------------+
|
||||
| Fedora 39 | 3.12 | x86-64 |
|
||||
+----------------------------------+----------------------------+---------------------+
|
||||
| Gentoo | 3.9 | x86-64 |
|
||||
+----------------------------------+----------------------------+---------------------+
|
||||
| macOS 12 Monterey | 3.8, 3.9, 3.10, 3.11, | x86-64 |
|
||||
|
@ -574,6 +582,10 @@ These platforms have been reported to work at the versions mentioned.
|
|||
+----------------------------------+----------------------------+------------------+--------------+
|
||||
| FreeBSD 10.2 | 2.7, 3.4 | 3.1.0 |x86-64 |
|
||||
+----------------------------------+----------------------------+------------------+--------------+
|
||||
| Windows 11 | 3.9, 3.10, 3.11, 3.12 | 10.1.0 |arm64 |
|
||||
+----------------------------------+----------------------------+------------------+--------------+
|
||||
| Windows 11 Pro | 3.11, 3.12 | 10.1.0 |x86-64 |
|
||||
+----------------------------------+----------------------------+------------------+--------------+
|
||||
| Windows 10 | 3.7 | 7.1.0 |x86-64 |
|
||||
+----------------------------------+----------------------------+------------------+--------------+
|
||||
| Windows 10/Cygwin 3.3 | 3.6, 3.7, 3.8, 3.9 | 8.4.0 |x86-64 |
|
||||
|
|
36
docs/resources/js/activate_tab.js
Normal file
36
docs/resources/js/activate_tab.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Based on https://stackoverflow.com/a/38241481/724176
|
||||
function getOS() {
|
||||
const userAgent = window.navigator.userAgent,
|
||||
platform = window.navigator.userAgentData?.platform || window.navigator.platform,
|
||||
macosPlatforms = ["macOS", "Macintosh", "MacIntel", "MacPPC", "Mac68K"],
|
||||
windowsPlatforms = ["Win32", "Win64", "Windows", "WinCE"];
|
||||
|
||||
if (macosPlatforms.includes(platform)) {
|
||||
return "macOS";
|
||||
} else if (windowsPlatforms.includes(platform)) {
|
||||
return "Windows";
|
||||
} else if (/Android/.test(userAgent)) {
|
||||
return "Android";
|
||||
} else if (/Linux/.test(platform)) {
|
||||
return "Linux";
|
||||
}
|
||||
}
|
||||
|
||||
function activateTab(tabName) {
|
||||
// Find all label elements with the specified tab name
|
||||
const labels = document.querySelectorAll(".tab-label");
|
||||
|
||||
labels.forEach((label) => {
|
||||
if (label.textContent == tabName) {
|
||||
// Find the associated input element using the "for" attribute
|
||||
const tabInputId = label.getAttribute("for");
|
||||
const tabInput = document.getElementById(tabInputId);
|
||||
|
||||
// Check if the input element exists before attempting to set the "checked" attribute
|
||||
if (tabInput) {
|
||||
// Activate the tab by setting its "checked" attribute to true
|
||||
tabInput.checked = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
|
@ -47,6 +47,12 @@ docs = [
|
|||
"sphinx-removed-in",
|
||||
"sphinxext-opengraph",
|
||||
]
|
||||
fpx = [
|
||||
"olefile",
|
||||
]
|
||||
mic = [
|
||||
"olefile",
|
||||
]
|
||||
tests = [
|
||||
"check-manifest",
|
||||
"coverage",
|
||||
|
@ -59,6 +65,9 @@ tests = [
|
|||
"pytest-cov",
|
||||
"pytest-timeout",
|
||||
]
|
||||
xmp = [
|
||||
"defusedxml",
|
||||
]
|
||||
[project.urls]
|
||||
Changelog = "https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst"
|
||||
Documentation = "https://pillow.readthedocs.io"
|
||||
|
@ -77,10 +86,17 @@ package-dir = {"" = "src"}
|
|||
[tool.setuptools.dynamic]
|
||||
version = {attr = "PIL.__version__"}
|
||||
|
||||
[tool.cibuildwheel]
|
||||
before-all = ".github/workflows/wheels-dependencies.sh"
|
||||
build-verbosity = 1
|
||||
config-settings = "raqm=enable raqm=vendor fribidi=vendor imagequant=disable"
|
||||
test-command = "cd {project} && .github/workflows/wheels-test.sh"
|
||||
test-extras = "tests"
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py38"
|
||||
line-length = 88
|
||||
select = [
|
||||
"C4", # flake8-comprehensions
|
||||
"E", # pycodestyle errors
|
||||
"EM", # flake8-errmsg
|
||||
"F", # pyflakes errors
|
||||
|
|
22
setup.py
22
setup.py
|
@ -440,17 +440,17 @@ class pil_build_ext(build_ext):
|
|||
|
||||
#
|
||||
# add configured kits
|
||||
for root_name, lib_name in dict(
|
||||
JPEG_ROOT="libjpeg",
|
||||
JPEG2K_ROOT="libopenjp2",
|
||||
TIFF_ROOT=("libtiff-5", "libtiff-4"),
|
||||
ZLIB_ROOT="zlib",
|
||||
FREETYPE_ROOT="freetype2",
|
||||
HARFBUZZ_ROOT="harfbuzz",
|
||||
FRIBIDI_ROOT="fribidi",
|
||||
LCMS_ROOT="lcms2",
|
||||
IMAGEQUANT_ROOT="libimagequant",
|
||||
).items():
|
||||
for root_name, lib_name in {
|
||||
"JPEG_ROOT": "libjpeg",
|
||||
"JPEG2K_ROOT": "libopenjp2",
|
||||
"TIFF_ROOT": ("libtiff-5", "libtiff-4"),
|
||||
"ZLIB_ROOT": "zlib",
|
||||
"FREETYPE_ROOT": "freetype2",
|
||||
"HARFBUZZ_ROOT": "harfbuzz",
|
||||
"FRIBIDI_ROOT": "fribidi",
|
||||
"LCMS_ROOT": "lcms2",
|
||||
"IMAGEQUANT_ROOT": "libimagequant",
|
||||
}.items():
|
||||
root = globals()[root_name]
|
||||
|
||||
if root is None and root_name in os.environ:
|
||||
|
|
|
@ -396,7 +396,7 @@ def _save(im, fp, filename, bitmap_header=True):
|
|||
dpi = info.get("dpi", (96, 96))
|
||||
|
||||
# 1 meter == 39.3701 inches
|
||||
ppm = tuple(map(lambda x: int(x * 39.3701 + 0.5), dpi))
|
||||
ppm = tuple(int(x * 39.3701 + 0.5) for x in dpi)
|
||||
|
||||
stride = ((im.size[0] * bits + 7) // 8 + 3) & (~3)
|
||||
header = 40 # or 64 for OS/2 version 2
|
||||
|
|
|
@ -64,8 +64,6 @@ class CurImageFile(BmpImagePlugin.BmpImageFile):
|
|||
d, e, o, a = self.tile[0]
|
||||
self.tile[0] = d, (0, 0) + self.size, o, a
|
||||
|
||||
return
|
||||
|
||||
|
||||
#
|
||||
# --------------------------------------------------------------------
|
||||
|
|
|
@ -77,14 +77,11 @@ def Ghostscript(tile, size, fp, scale=1, transparency=False):
|
|||
|
||||
# Hack to support hi-res rendering
|
||||
scale = int(scale) or 1
|
||||
# orig_size = size
|
||||
# orig_bbox = bbox
|
||||
size = (size[0] * scale, size[1] * scale)
|
||||
width = size[0] * scale
|
||||
height = size[1] * scale
|
||||
# resolution is dependent on bbox and size
|
||||
res = (
|
||||
72.0 * size[0] / (bbox[2] - bbox[0]),
|
||||
72.0 * size[1] / (bbox[3] - bbox[1]),
|
||||
)
|
||||
res_x = 72.0 * width / (bbox[2] - bbox[0])
|
||||
res_y = 72.0 * height / (bbox[3] - bbox[1])
|
||||
|
||||
out_fd, outfile = tempfile.mkstemp()
|
||||
os.close(out_fd)
|
||||
|
@ -121,8 +118,8 @@ def Ghostscript(tile, size, fp, scale=1, transparency=False):
|
|||
command = [
|
||||
gs_binary,
|
||||
"-q", # quiet mode
|
||||
"-g%dx%d" % size, # set output geometry (pixels)
|
||||
"-r%fx%f" % res, # set input DPI (dots per inch) # noqa: UP031
|
||||
f"-g{width:d}x{height:d}", # set output geometry (pixels)
|
||||
f"-r{res_x:f}x{res_y:f}", # set input DPI (dots per inch)
|
||||
"-dBATCH", # exit after processing
|
||||
"-dNOPAUSE", # don't pause between pages
|
||||
"-dSAFER", # safe mode
|
||||
|
|
|
@ -54,12 +54,10 @@ class FitsImageFile(ImageFile.ImageFile):
|
|||
self._mode = "L"
|
||||
elif number_of_bits == 16:
|
||||
self._mode = "I"
|
||||
# rawmode = "I;16S"
|
||||
elif number_of_bits == 32:
|
||||
self._mode = "I"
|
||||
elif number_of_bits in (-32, -64):
|
||||
self._mode = "F"
|
||||
# rawmode = "F" if number_of_bits == -32 else "F;64F"
|
||||
|
||||
offset = math.ceil(self.fp.tell() / 2880) * 2880
|
||||
self.tile = [("raw", (0, 0) + self.size, offset, (self.mode, 0, -1))]
|
||||
|
|
|
@ -78,7 +78,6 @@ class FontFile:
|
|||
if glyph:
|
||||
d, dst, src, im = glyph
|
||||
xx = src[2] - src[0]
|
||||
# yy = src[3] - src[1]
|
||||
x0, y0 = x, y
|
||||
x = x + xx
|
||||
if x > WIDTH:
|
||||
|
|
|
@ -227,6 +227,7 @@ class FpxImageFile(ImageFile.ImageFile):
|
|||
break # isn't really required
|
||||
|
||||
self.stream = stream
|
||||
self._fp = self.fp
|
||||
self.fp = None
|
||||
|
||||
def load(self):
|
||||
|
|
|
@ -281,14 +281,9 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
bits = self.fp.read(1)[0]
|
||||
self.__offset = self.fp.tell()
|
||||
break
|
||||
|
||||
else:
|
||||
pass
|
||||
# raise OSError, "illegal GIF tag `%x`" % s[0]
|
||||
s = None
|
||||
|
||||
if interlace is None:
|
||||
# self._fp = None
|
||||
msg = "image not found in GIF frame"
|
||||
raise EOFError(msg)
|
||||
|
||||
|
@ -661,7 +656,7 @@ def _save(im, fp, filename, save_all=False):
|
|||
palette = im.encoderinfo.get("palette", im.info.get("palette"))
|
||||
else:
|
||||
palette = None
|
||||
im.encoderinfo["optimize"] = im.encoderinfo.get("optimize", True)
|
||||
im.encoderinfo.setdefault("optimize", True)
|
||||
|
||||
if not save_all or not _write_multiple_frames(im, fp, palette):
|
||||
_write_single_frame(im, fp, palette)
|
||||
|
|
|
@ -391,8 +391,8 @@ if __name__ == "__main__":
|
|||
with open(sys.argv[1], "rb") as fp:
|
||||
imf = IcnsImageFile(fp)
|
||||
for size in imf.info["sizes"]:
|
||||
imf.size = size
|
||||
imf.save("out-%s-%s-%s.png" % size) # noqa: UP031
|
||||
width, height, scale = imf.size = size
|
||||
imf.save(f"out-{width}-{height}-{scale}.png")
|
||||
with Image.open(sys.argv[1]) as im:
|
||||
im.save("out.png")
|
||||
if sys.platform == "windows":
|
||||
|
|
|
@ -174,9 +174,7 @@ class IcoFile:
|
|||
|
||||
self.entry = sorted(self.entry, key=lambda x: x["color_depth"])
|
||||
# ICO images are usually squares
|
||||
# self.entry = sorted(self.entry, key=lambda x: x['width'])
|
||||
self.entry = sorted(self.entry, key=lambda x: x["square"])
|
||||
self.entry.reverse()
|
||||
self.entry = sorted(self.entry, key=lambda x: x["square"], reverse=True)
|
||||
|
||||
def sizes(self):
|
||||
"""
|
||||
|
|
|
@ -40,7 +40,7 @@ from enum import IntEnum
|
|||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import defusedxml.ElementTree as ElementTree
|
||||
from defusedxml import ElementTree
|
||||
except ImportError:
|
||||
ElementTree = None
|
||||
|
||||
|
@ -549,16 +549,17 @@ class Image:
|
|||
:py:meth:`~PIL.Image.Image.load` method. See :ref:`file-handling` for
|
||||
more information.
|
||||
"""
|
||||
try:
|
||||
if getattr(self, "_fp", False):
|
||||
if self._fp != self.fp:
|
||||
self._fp.close()
|
||||
self._fp = DeferredError(ValueError("Operation on closed image"))
|
||||
if self.fp:
|
||||
self.fp.close()
|
||||
self.fp = None
|
||||
except Exception as msg:
|
||||
logger.debug("Error closing: %s", msg)
|
||||
if hasattr(self, "fp"):
|
||||
try:
|
||||
if getattr(self, "_fp", False):
|
||||
if self._fp != self.fp:
|
||||
self._fp.close()
|
||||
self._fp = DeferredError(ValueError("Operation on closed image"))
|
||||
if self.fp:
|
||||
self.fp.close()
|
||||
self.fp = None
|
||||
except Exception as msg:
|
||||
logger.debug("Error closing: %s", msg)
|
||||
|
||||
if getattr(self, "map", None):
|
||||
self.map = None
|
||||
|
@ -1159,7 +1160,7 @@ class Image:
|
|||
if palette.mode != "P":
|
||||
msg = "bad mode for palette image"
|
||||
raise ValueError(msg)
|
||||
if self.mode != "RGB" and self.mode != "L":
|
||||
if self.mode not in {"RGB", "L"}:
|
||||
msg = "only RGB or L mode images can be quantized to a palette"
|
||||
raise ValueError(msg)
|
||||
im = self.im.convert("P", dither, palette.im)
|
||||
|
@ -3100,7 +3101,8 @@ def fromarray(obj, mode=None):
|
|||
try:
|
||||
mode, rawmode = _fromarray_typemap[typekey]
|
||||
except KeyError as e:
|
||||
msg = "Cannot handle this data type: %s, %s" % typekey # noqa: UP031
|
||||
typekey_shape, typestr = typekey
|
||||
msg = f"Cannot handle this data type: {typekey_shape}, {typestr}"
|
||||
raise TypeError(msg) from e
|
||||
else:
|
||||
rawmode = mode
|
||||
|
|
|
@ -923,7 +923,7 @@ def floodfill(image, xy, value, border=None, thresh=0):
|
|||
if border is None:
|
||||
fill = _color_diff(p, background) <= thresh
|
||||
else:
|
||||
fill = p != value and p != border
|
||||
fill = p not in (value, border)
|
||||
if fill:
|
||||
pixel[s, t] = value
|
||||
new_edge.add((s, t))
|
||||
|
|
|
@ -430,7 +430,6 @@ class Parser:
|
|||
with io.BytesIO(self.data) as fp:
|
||||
im = Image.open(fp)
|
||||
except OSError:
|
||||
# traceback.print_exc()
|
||||
pass # not enough data
|
||||
else:
|
||||
flag = hasattr(im, "load_seek") or hasattr(im, "load_read")
|
||||
|
|
|
@ -788,8 +788,13 @@ def truetype(font=None, size=10, index=0, encoding="", layout_engine=None):
|
|||
.. versionadded:: 4.2.0
|
||||
:return: A font object.
|
||||
:exception OSError: If the file could not be read.
|
||||
:exception ValueError: If the font size is not greater than zero.
|
||||
"""
|
||||
|
||||
if size <= 0:
|
||||
msg = "font size must be greater than 0"
|
||||
raise ValueError(msg)
|
||||
|
||||
def freetype(font):
|
||||
return FreeTypeFont(font, size, index, encoding, layout_engine)
|
||||
|
||||
|
|
|
@ -257,8 +257,6 @@ def load(filename):
|
|||
if lut:
|
||||
break
|
||||
except (SyntaxError, ValueError):
|
||||
# import traceback
|
||||
# traceback.print_exc()
|
||||
pass
|
||||
else:
|
||||
msg = "cannot load palette"
|
||||
|
|
|
@ -83,16 +83,6 @@ def fromqimage(im):
|
|||
|
||||
def fromqpixmap(im):
|
||||
return fromqimage(im)
|
||||
# buffer = QBuffer()
|
||||
# buffer.open(QIODevice.ReadWrite)
|
||||
# # im.save(buffer)
|
||||
# # What if png doesn't support some image features like animation?
|
||||
# im.save(buffer, 'ppm')
|
||||
# bytes_io = BytesIO()
|
||||
# bytes_io.write(buffer.data())
|
||||
# buffer.close()
|
||||
# bytes_io.seek(0)
|
||||
# return Image.open(bytes_io)
|
||||
|
||||
|
||||
def align8to32(bytes, width, mode):
|
||||
|
@ -208,9 +198,5 @@ def toqimage(im):
|
|||
|
||||
|
||||
def toqpixmap(im):
|
||||
# # This doesn't work. For now using a dumb approach.
|
||||
# im_data = _toqclass_helper(im)
|
||||
# result = QPixmap(im_data["size"][0], im_data["size"][1])
|
||||
# result.loadFromData(im_data["data"])
|
||||
qimage = toqimage(im)
|
||||
return QPixmap.fromImage(qimage)
|
||||
|
|
|
@ -334,10 +334,7 @@ def _save(im, fp, filename):
|
|||
if quality_layers is not None and not (
|
||||
isinstance(quality_layers, (list, tuple))
|
||||
and all(
|
||||
[
|
||||
isinstance(quality_layer, (int, float))
|
||||
for quality_layer in quality_layers
|
||||
]
|
||||
isinstance(quality_layer, (int, float)) for quality_layer in quality_layers
|
||||
)
|
||||
):
|
||||
msg = "quality_layers must be a sequence of numbers"
|
||||
|
|
|
@ -397,7 +397,7 @@ class JpegImageFile(ImageFile.ImageFile):
|
|||
# self.__offset = self.fp.tell()
|
||||
break
|
||||
s = self.fp.read(1)
|
||||
elif i == 0 or i == 0xFFFF:
|
||||
elif i in {0, 0xFFFF}:
|
||||
# padded marker or junk; move on
|
||||
s = b"\xff"
|
||||
elif i == 0xFF00: # Skip extraneous data (escaped 0xFF)
|
||||
|
@ -787,6 +787,8 @@ def _save(im, fp, filename):
|
|||
dpi[0],
|
||||
dpi[1],
|
||||
subsampling,
|
||||
info.get("restart_marker_blocks", 0),
|
||||
info.get("restart_marker_rows", 0),
|
||||
qtables,
|
||||
comment,
|
||||
extra,
|
||||
|
|
|
@ -66,6 +66,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
|
|||
self._n_frames = len(self.images)
|
||||
self.is_animated = self._n_frames > 1
|
||||
|
||||
self.__fp = self.fp
|
||||
self.seek(0)
|
||||
|
||||
def seek(self, frame):
|
||||
|
@ -87,10 +88,12 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
|
|||
return self.frame
|
||||
|
||||
def close(self):
|
||||
self.__fp.close()
|
||||
self.ole.close()
|
||||
super().close()
|
||||
|
||||
def __exit__(self, *args):
|
||||
self.__fp.close()
|
||||
self.ole.close()
|
||||
super().__exit__()
|
||||
|
||||
|
|
|
@ -33,9 +33,6 @@ from . import (
|
|||
from ._binary import i16be as i16
|
||||
from ._binary import o32le
|
||||
|
||||
# def _accept(prefix):
|
||||
# return JpegImagePlugin._accept(prefix)
|
||||
|
||||
|
||||
def _save(im, fp, filename):
|
||||
JpegImagePlugin._save(im, fp, filename)
|
||||
|
|
|
@ -82,7 +82,7 @@ class IndirectReference(
|
|||
collections.namedtuple("IndirectReferenceTuple", ["object_id", "generation"])
|
||||
):
|
||||
def __str__(self):
|
||||
return "%s %s R" % self # noqa: UP031
|
||||
return f"{self.object_id} {self.generation} R"
|
||||
|
||||
def __bytes__(self):
|
||||
return self.__str__().encode("us-ascii")
|
||||
|
@ -103,7 +103,7 @@ class IndirectReference(
|
|||
|
||||
class IndirectObjectDef(IndirectReference):
|
||||
def __str__(self):
|
||||
return "%s %s obj" % self # noqa: UP031
|
||||
return f"{self.object_id} {self.generation} obj"
|
||||
|
||||
|
||||
class XrefTable:
|
||||
|
|
|
@ -328,9 +328,6 @@ def _save(im, fp, filename):
|
|||
fp.write(b"65535\n")
|
||||
ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, 1))])
|
||||
|
||||
# ALTERNATIVE: save via builtin debug function
|
||||
# im._dump(filename)
|
||||
|
||||
|
||||
#
|
||||
# --------------------------------------------------------------------
|
||||
|
|
|
@ -123,7 +123,7 @@ class SgiImageFile(ImageFile.ImageFile):
|
|||
|
||||
|
||||
def _save(im, fp, filename):
|
||||
if im.mode != "RGB" and im.mode != "RGBA" and im.mode != "L":
|
||||
if im.mode not in {"RGB", "RGBA", "L"}:
|
||||
msg = "Unsupported SGI image mode"
|
||||
raise ValueError(msg)
|
||||
|
||||
|
@ -155,7 +155,7 @@ def _save(im, fp, filename):
|
|||
# Z Dimension: Number of channels
|
||||
z = len(im.mode)
|
||||
|
||||
if dim == 1 or dim == 2:
|
||||
if dim in {1, 2}:
|
||||
z = 1
|
||||
|
||||
# assert we've got the right number of bands.
|
||||
|
|
|
@ -1885,13 +1885,14 @@ class AppendingTiffWriter:
|
|||
8, # long8
|
||||
]
|
||||
|
||||
# StripOffsets = 273
|
||||
# FreeOffsets = 288
|
||||
# TileOffsets = 324
|
||||
# JPEGQTables = 519
|
||||
# JPEGDCTables = 520
|
||||
# JPEGACTables = 521
|
||||
Tags = {273, 288, 324, 519, 520, 521}
|
||||
Tags = {
|
||||
273, # StripOffsets
|
||||
288, # FreeOffsets
|
||||
324, # TileOffsets
|
||||
519, # JPEGQTables
|
||||
520, # JPEGDCTables
|
||||
521, # JPEGACTables
|
||||
}
|
||||
|
||||
def __init__(self, fn, new=False):
|
||||
if hasattr(fn, "read"):
|
||||
|
@ -1941,8 +1942,6 @@ class AppendingTiffWriter:
|
|||
|
||||
iimm = self.f.read(4)
|
||||
if not iimm:
|
||||
# msg = "nothing written into new page"
|
||||
# raise RuntimeError(msg)
|
||||
# Make it easy to finish a frame without committing to a new one.
|
||||
return
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ def lookup(tag, group=None):
|
|||
##
|
||||
# Map tag numbers to tag info.
|
||||
#
|
||||
# id: (Name, Type, Length, enum_values)
|
||||
# id: (Name, Type, Length[, enum_values])
|
||||
#
|
||||
# The length here differs from the length in the tiff spec. For
|
||||
# numbers, the tiff spec is for the number of fields returned. We
|
||||
|
@ -427,7 +427,7 @@ def _populate():
|
|||
|
||||
TAGS_V2[k] = TagInfo(k, *v)
|
||||
|
||||
for group, tags in TAGS_V2_GROUPS.items():
|
||||
for tags in TAGS_V2_GROUPS.values():
|
||||
for k, v in tags.items():
|
||||
tags[k] = TagInfo(k, *v)
|
||||
|
||||
|
@ -438,22 +438,6 @@ _populate()
|
|||
|
||||
TYPES = {}
|
||||
|
||||
# was:
|
||||
# TYPES = {
|
||||
# 1: "byte",
|
||||
# 2: "ascii",
|
||||
# 3: "short",
|
||||
# 4: "long",
|
||||
# 5: "rational",
|
||||
# 6: "signed byte",
|
||||
# 7: "undefined",
|
||||
# 8: "signed short",
|
||||
# 9: "signed long",
|
||||
# 10: "signed rational",
|
||||
# 11: "float",
|
||||
# 12: "double",
|
||||
# }
|
||||
|
||||
#
|
||||
# These tags are handled by default in libtiff, without
|
||||
# adding to the custom dictionary. From tif_dir.c, searching for
|
||||
|
|
|
@ -885,7 +885,9 @@ font_render(FontObject *self, PyObject *args) {
|
|||
PyMem_Del(glyph_info);
|
||||
return NULL;
|
||||
}
|
||||
id = PyLong_AsSsize_t(PyObject_GetAttrString(image, "id"));
|
||||
PyObject *imageId = PyObject_GetAttrString(image, "id");
|
||||
id = PyLong_AsSsize_t(imageId);
|
||||
Py_XDECREF(imageId);
|
||||
im = (Imaging)id;
|
||||
|
||||
x_offset -= stroke_width;
|
||||
|
|
|
@ -1045,6 +1045,8 @@ PyImaging_JpegEncoderNew(PyObject *self, PyObject *args) {
|
|||
Py_ssize_t streamtype = 0; /* 0=interchange, 1=tables only, 2=image only */
|
||||
Py_ssize_t xdpi = 0, ydpi = 0;
|
||||
Py_ssize_t subsampling = -1; /* -1=default, 0=none, 1=medium, 2=high */
|
||||
Py_ssize_t restart_marker_blocks = 0;
|
||||
Py_ssize_t restart_marker_rows = 0;
|
||||
PyObject *qtables = NULL;
|
||||
unsigned int *qarrays = NULL;
|
||||
int qtablesLen = 0;
|
||||
|
@ -1057,7 +1059,7 @@ PyImaging_JpegEncoderNew(PyObject *self, PyObject *args) {
|
|||
|
||||
if (!PyArg_ParseTuple(
|
||||
args,
|
||||
"ss|nnnnnnnnOz#y#y#",
|
||||
"ss|nnnnnnnnnnOz#y#y#",
|
||||
&mode,
|
||||
&rawmode,
|
||||
&quality,
|
||||
|
@ -1068,6 +1070,8 @@ PyImaging_JpegEncoderNew(PyObject *self, PyObject *args) {
|
|||
&xdpi,
|
||||
&ydpi,
|
||||
&subsampling,
|
||||
&restart_marker_blocks,
|
||||
&restart_marker_rows,
|
||||
&qtables,
|
||||
&comment,
|
||||
&comment_size,
|
||||
|
@ -1156,6 +1160,8 @@ PyImaging_JpegEncoderNew(PyObject *self, PyObject *args) {
|
|||
((JPEGENCODERSTATE *)encoder->state.context)->streamtype = streamtype;
|
||||
((JPEGENCODERSTATE *)encoder->state.context)->xdpi = xdpi;
|
||||
((JPEGENCODERSTATE *)encoder->state.context)->ydpi = ydpi;
|
||||
((JPEGENCODERSTATE *)encoder->state.context)->restart_marker_blocks = restart_marker_blocks;
|
||||
((JPEGENCODERSTATE *)encoder->state.context)->restart_marker_rows = restart_marker_rows;
|
||||
((JPEGENCODERSTATE *)encoder->state.context)->comment = comment;
|
||||
((JPEGENCODERSTATE *)encoder->state.context)->comment_size = comment_size;
|
||||
((JPEGENCODERSTATE *)encoder->state.context)->extra = extra;
|
||||
|
|
|
@ -83,6 +83,10 @@ typedef struct {
|
|||
/* Chroma Subsampling (-1=default, 0=none, 1=medium, 2=high) */
|
||||
int subsampling;
|
||||
|
||||
/* Restart marker interval, in MCU blocks or MCU rows, or 0 for none */
|
||||
unsigned int restart_marker_blocks;
|
||||
unsigned int restart_marker_rows;
|
||||
|
||||
/* Converter input mode (input to the shuffler) */
|
||||
char rawmode[8 + 1];
|
||||
|
||||
|
|
|
@ -210,6 +210,8 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
|
|||
}
|
||||
context->cinfo.smoothing_factor = context->smooth;
|
||||
context->cinfo.optimize_coding = (boolean)context->optimize;
|
||||
context->cinfo.restart_interval = context->restart_marker_blocks;
|
||||
context->cinfo.restart_in_rows = context->restart_marker_rows;
|
||||
if (context->xdpi > 0 && context->ydpi > 0) {
|
||||
context->cinfo.write_JFIF_header = TRUE;
|
||||
context->cinfo.density_unit = 1; /* dots per inch */
|
||||
|
@ -218,9 +220,9 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
|
|||
}
|
||||
switch (context->streamtype) {
|
||||
case 1:
|
||||
/* tables only -- not yet implemented */
|
||||
state->errcode = IMAGING_CODEC_CONFIG;
|
||||
return -1;
|
||||
/* tables only */
|
||||
jpeg_write_tables(&context->cinfo);
|
||||
goto cleanup;
|
||||
case 2:
|
||||
/* image only */
|
||||
jpeg_suppress_tables(&context->cinfo, TRUE);
|
||||
|
@ -316,6 +318,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
|
|||
}
|
||||
jpeg_finish_compress(&context->cinfo);
|
||||
|
||||
cleanup:
|
||||
/* Clean up */
|
||||
if (context->comment) {
|
||||
free(context->comment);
|
||||
|
|
|
@ -14,6 +14,10 @@
|
|||
|
||||
#ifdef HAVE_LIBTIFF
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h> /* lseek */
|
||||
#endif
|
||||
|
||||
#ifndef uint
|
||||
#define uint uint32
|
||||
#endif
|
||||
|
|
|
@ -13,12 +13,6 @@
|
|||
#include <tiff.h>
|
||||
#endif
|
||||
|
||||
/* UNDONE -- what are we using from this? */
|
||||
/*#ifndef _UNISTD_H
|
||||
# include <unistd.h>
|
||||
# endif
|
||||
*/
|
||||
|
||||
#ifndef min
|
||||
#define min(x, y) ((x > y) ? y : x)
|
||||
#define max(x, y) ((x < y) ? y : x)
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
README
|
||||
------
|
||||
|
||||
This directory creates wheels for tagged versions of Pillow.
|
||||
[cibuildwheel](https://github.com/pypa/cibuildwheel) is used to build macOS and Linux
|
||||
wheels for tagged versions of Pillow.
|
||||
|
||||
This directory contains [multibuild](https://github.com/multi-build/multibuild) to
|
||||
build dependencies for the wheels, and dependency licenses to be included.
|
||||
|
||||
Archives
|
||||
--------
|
||||
|
@ -16,8 +20,8 @@ But, the build will look in that repository before downloading from the
|
|||
URL, so if there is a library that often fails to download, or you think might
|
||||
fail to download, then download it and add it to the Git repository.
|
||||
|
||||
See the `pre_build` in `config.sh` and the `fetch_unpack` routine in
|
||||
`multibuild/common_utils.sh` for the logic, and the build recipes in
|
||||
See `build` in `.github/workflows/wheels-dependencies.sh` and the `fetch_unpack`
|
||||
routine in `multibuild/common_utils.sh` for the logic, and the build recipes in
|
||||
`multibuild/library_builders.sh` for the filename to give to the downloaded
|
||||
archive.
|
||||
|
||||
|
@ -27,5 +31,5 @@ Wheels
|
|||
Wheels are
|
||||
[GitHub Actions artifacts created for tags, relevant changes or manual builds](https://github.com/python-pillow/Pillow/actions/workflows/wheels.yml).
|
||||
|
||||
Windows wheels are not created here. Instead, they are
|
||||
Windows wheels are created separately. They are
|
||||
[GitHub Actions artifacts created on each run of the Windows workflow](https://github.com/python-pillow/Pillow/actions/workflows/test-windows.yml?query=branch%3Amain).
|
||||
|
|
187
wheels/config.sh
187
wheels/config.sh
|
@ -1,187 +0,0 @@
|
|||
# Define custom utilities
|
||||
# Test for macOS with [ -n "$IS_MACOS" ]
|
||||
|
||||
ARCHIVE_SDIR=pillow-depends-main
|
||||
|
||||
# Package versions for fresh source builds
|
||||
FREETYPE_VERSION=2.13.2
|
||||
HARFBUZZ_VERSION=8.2.1
|
||||
LIBPNG_VERSION=1.6.40
|
||||
JPEGTURBO_VERSION=3.0.1
|
||||
OPENJPEG_VERSION=2.5.0
|
||||
XZ_VERSION=5.4.4
|
||||
TIFF_VERSION=4.6.0
|
||||
LCMS2_VERSION=2.15
|
||||
if [[ -n "$IS_MACOS" ]]; then
|
||||
GIFLIB_VERSION=5.1.4
|
||||
else
|
||||
GIFLIB_VERSION=5.2.1
|
||||
fi
|
||||
if [[ -n "$IS_MACOS" ]] || [[ "$MB_ML_VER" != 2014 ]]; then
|
||||
ZLIB_VERSION=1.3
|
||||
else
|
||||
ZLIB_VERSION=1.2.8
|
||||
fi
|
||||
LIBWEBP_VERSION=1.3.2
|
||||
BZIP2_VERSION=1.0.8
|
||||
LIBXCB_VERSION=1.16
|
||||
BROTLI_VERSION=1.1.0
|
||||
|
||||
if [[ -n "$IS_MACOS" ]] && [[ "$PLAT" == "x86_64" ]]; then
|
||||
function build_openjpeg {
|
||||
local out_dir=$(fetch_unpack https://github.com/uclouvain/openjpeg/archive/v${OPENJPEG_VERSION}.tar.gz)
|
||||
(cd $out_dir \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib . \
|
||||
&& make install)
|
||||
touch openjpeg-stamp
|
||||
}
|
||||
fi
|
||||
|
||||
function build_brotli {
|
||||
local cmake=$(get_modern_cmake)
|
||||
local out_dir=$(fetch_unpack https://github.com/google/brotli/archive/v$BROTLI_VERSION.tar.gz)
|
||||
(cd $out_dir \
|
||||
&& $cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib . \
|
||||
&& make install)
|
||||
if [[ "$MB_ML_LIBC" == "manylinux" ]]; then
|
||||
cp /usr/local/lib64/libbrotli* /usr/local/lib
|
||||
cp /usr/local/lib64/pkgconfig/libbrotli* /usr/local/lib/pkgconfig
|
||||
fi
|
||||
}
|
||||
|
||||
function pre_build {
|
||||
# Any stuff that you need to do before you start building the wheels
|
||||
# Runs in the root directory of this repository.
|
||||
curl -fsSL -o pillow-depends-main.zip https://github.com/python-pillow/pillow-depends/archive/main.zip
|
||||
untar pillow-depends-main.zip
|
||||
|
||||
build_xz
|
||||
if [ -z "$IS_ALPINE" ] && [ -z "$IS_MACOS" ]; then
|
||||
yum remove -y zlib-devel
|
||||
fi
|
||||
build_new_zlib
|
||||
|
||||
if [ -n "$IS_MACOS" ]; then
|
||||
ORIGINAL_BUILD_PREFIX=$BUILD_PREFIX
|
||||
ORIGINAL_PKG_CONFIG_PATH=$PKG_CONFIG_PATH
|
||||
BUILD_PREFIX=`dirname $(dirname $(which python))`
|
||||
PKG_CONFIG_PATH="$BUILD_PREFIX/lib/pkgconfig"
|
||||
fi
|
||||
build_simple xcb-proto 1.16.0 https://xorg.freedesktop.org/archive/individual/proto
|
||||
if [ -n "$IS_MACOS" ]; then
|
||||
build_simple xorgproto 2023.2 https://www.x.org/pub/individual/proto
|
||||
build_simple libXau 1.0.11 https://www.x.org/pub/individual/lib
|
||||
build_simple libpthread-stubs 0.5 https://xcb.freedesktop.org/dist
|
||||
cp venv/share/pkgconfig/xcb-proto.pc venv/lib/pkgconfig/xcb-proto.pc
|
||||
else
|
||||
sed s/\${pc_sysrootdir\}// /usr/local/share/pkgconfig/xcb-proto.pc > /usr/local/lib/pkgconfig/xcb-proto.pc
|
||||
fi
|
||||
build_simple libxcb $LIBXCB_VERSION https://www.x.org/releases/individual/lib
|
||||
if [ -n "$IS_MACOS" ]; then
|
||||
BUILD_PREFIX=$ORIGINAL_BUILD_PREFIX
|
||||
PKG_CONFIG_PATH=$ORIGINAL_PKG_CONFIG_PATH
|
||||
fi
|
||||
|
||||
build_libjpeg_turbo
|
||||
if [[ -n "$IS_MACOS" ]]; then
|
||||
rm /usr/local/lib/libjpeg.dylib
|
||||
fi
|
||||
build_tiff
|
||||
build_libpng
|
||||
build_lcms2
|
||||
build_openjpeg
|
||||
|
||||
ORIGINAL_CFLAGS=$CFLAGS
|
||||
CFLAGS="$CFLAGS -O3 -DNDEBUG"
|
||||
if [[ -n "$IS_MACOS" ]]; then
|
||||
CFLAGS="$CFLAGS -Wl,-headerpad_max_install_names"
|
||||
fi
|
||||
build_libwebp
|
||||
CFLAGS=$ORIGINAL_CFLAGS
|
||||
|
||||
build_brotli
|
||||
|
||||
if [ -n "$IS_MACOS" ]; then
|
||||
# Custom freetype build
|
||||
build_simple freetype $FREETYPE_VERSION https://download.savannah.gnu.org/releases/freetype tar.gz --with-harfbuzz=no
|
||||
else
|
||||
build_freetype
|
||||
fi
|
||||
|
||||
if [ -z "$IS_MACOS" ]; then
|
||||
export FREETYPE_LIBS=-lfreetype
|
||||
export FREETYPE_CFLAGS=-I/usr/local/include/freetype2/
|
||||
fi
|
||||
build_simple harfbuzz $HARFBUZZ_VERSION https://github.com/harfbuzz/harfbuzz/releases/download/$HARFBUZZ_VERSION tar.xz --with-freetype=yes --with-glib=no
|
||||
if [ -z "$IS_MACOS" ]; then
|
||||
export FREETYPE_LIBS=''
|
||||
export FREETYPE_CFLAGS=''
|
||||
fi
|
||||
|
||||
# Append licenses
|
||||
for filename in wheels/dependency_licenses/*; do
|
||||
echo -e "\n\n----\n\n$(basename $filename | cut -f 1 -d '.')\n" | cat >> LICENSE
|
||||
cat $filename >> LICENSE
|
||||
done
|
||||
}
|
||||
|
||||
function pip_wheel_cmd {
|
||||
local abs_wheelhouse=$1
|
||||
if [ -z "$IS_MACOS" ]; then
|
||||
CFLAGS="$CFLAGS --std=c99" # for Raqm
|
||||
fi
|
||||
python3 -m pip wheel $(pip_opts) \
|
||||
-C raqm=enable -C raqm=vendor -C fribidi=vendor \
|
||||
-w $abs_wheelhouse --no-deps .
|
||||
}
|
||||
|
||||
function run_tests_in_repo {
|
||||
# Run Pillow tests from within source repo
|
||||
python3 selftest.py
|
||||
python3 -m pytest
|
||||
}
|
||||
|
||||
EXP_CODECS="jpg jpg_2000 libtiff zlib"
|
||||
EXP_MODULES="freetype2 littlecms2 pil tkinter webp"
|
||||
EXP_FEATURES="fribidi harfbuzz libjpeg_turbo raqm transp_webp webp_anim webp_mux xcb"
|
||||
|
||||
function run_tests {
|
||||
if [ -n "$IS_MACOS" ]; then
|
||||
brew install fribidi
|
||||
export PKG_CONFIG_PATH="/usr/local/opt/openblas/lib/pkgconfig"
|
||||
elif [ -n "$IS_ALPINE" ]; then
|
||||
apk add curl fribidi
|
||||
else
|
||||
apt-get update
|
||||
apt-get install -y curl libfribidi0 libopenblas-dev pkg-config unzip
|
||||
fi
|
||||
if [ -z "$IS_ALPINE" ]; then
|
||||
python3 -m pip install numpy
|
||||
fi
|
||||
python3 -m pip install defusedxml olefile pyroma
|
||||
|
||||
curl -fsSL -o pillow-test-images.zip https://github.com/python-pillow/test-images/archive/main.zip
|
||||
untar pillow-test-images.zip
|
||||
mv test-images-main/* ../Tests/images
|
||||
|
||||
# Runs tests on installed distribution from an empty directory
|
||||
(cd .. && run_tests_in_repo)
|
||||
# Test against expected codecs, modules and features
|
||||
local ret=0
|
||||
local codecs=$(python3 -c 'from PIL.features import *; print(" ".join(sorted(get_supported_codecs())))')
|
||||
if [ "$codecs" != "$EXP_CODECS" ]; then
|
||||
echo "Codecs should be: '$EXP_CODECS'; but are '$codecs'"
|
||||
ret=1
|
||||
fi
|
||||
local modules=$(python3 -c 'from PIL.features import *; print(" ".join(sorted(get_supported_modules())))')
|
||||
if [ "$modules" != "$EXP_MODULES" ]; then
|
||||
echo "Modules should be: '$EXP_MODULES'; but are '$modules'"
|
||||
ret=1
|
||||
fi
|
||||
local features=$(python3 -c 'from PIL.features import *; print(" ".join(sorted(get_supported_features())))')
|
||||
if [ "$features" != "$EXP_FEATURES" ]; then
|
||||
echo "Features should be: '$EXP_FEATURES'; but are '$features'"
|
||||
ret=1
|
||||
fi
|
||||
return $ret
|
||||
}
|
|
@ -38,3 +38,615 @@ the 'Old MIT' license, compatible to the above two licenses.
|
|||
|
||||
The MD5 checksum support (only used for debugging in development
|
||||
builds) is in the public domain.
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
The FreeType Project LICENSE
|
||||
----------------------------
|
||||
|
||||
2006-Jan-27
|
||||
|
||||
Copyright 1996-2002, 2006 by
|
||||
David Turner, Robert Wilhelm, and Werner Lemberg
|
||||
|
||||
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
The FreeType Project is distributed in several archive packages;
|
||||
some of them may contain, in addition to the FreeType font engine,
|
||||
various tools and contributions which rely on, or relate to, the
|
||||
FreeType Project.
|
||||
|
||||
This license applies to all files found in such packages, and
|
||||
which do not fall under their own explicit license. The license
|
||||
affects thus the FreeType font engine, the test programs,
|
||||
documentation and makefiles, at the very least.
|
||||
|
||||
This license was inspired by the BSD, Artistic, and IJG
|
||||
(Independent JPEG Group) licenses, which all encourage inclusion
|
||||
and use of free software in commercial and freeware products
|
||||
alike. As a consequence, its main points are that:
|
||||
|
||||
o We don't promise that this software works. However, we will be
|
||||
interested in any kind of bug reports. (`as is' distribution)
|
||||
|
||||
o You can use this software for whatever you want, in parts or
|
||||
full form, without having to pay us. (`royalty-free' usage)
|
||||
|
||||
o You may not pretend that you wrote this software. If you use
|
||||
it, or only parts of it, in a program, you must acknowledge
|
||||
somewhere in your documentation that you have used the
|
||||
FreeType code. (`credits')
|
||||
|
||||
We specifically permit and encourage the inclusion of this
|
||||
software, with or without modifications, in commercial products.
|
||||
We disclaim all warranties covering The FreeType Project and
|
||||
assume no liability related to The FreeType Project.
|
||||
|
||||
|
||||
Finally, many people asked us for a preferred form for a
|
||||
credit/disclaimer to use in compliance with this license. We thus
|
||||
encourage you to use the following text:
|
||||
|
||||
"""
|
||||
Portions of this software are copyright © <year> The FreeType
|
||||
Project (www.freetype.org). All rights reserved.
|
||||
"""
|
||||
|
||||
Please replace <year> with the value from the FreeType version you
|
||||
actually use.
|
||||
|
||||
|
||||
Legal Terms
|
||||
===========
|
||||
|
||||
0. Definitions
|
||||
--------------
|
||||
|
||||
Throughout this license, the terms `package', `FreeType Project',
|
||||
and `FreeType archive' refer to the set of files originally
|
||||
distributed by the authors (David Turner, Robert Wilhelm, and
|
||||
Werner Lemberg) as the `FreeType Project', be they named as alpha,
|
||||
beta or final release.
|
||||
|
||||
`You' refers to the licensee, or person using the project, where
|
||||
`using' is a generic term including compiling the project's source
|
||||
code as well as linking it to form a `program' or `executable'.
|
||||
This program is referred to as `a program using the FreeType
|
||||
engine'.
|
||||
|
||||
This license applies to all files distributed in the original
|
||||
FreeType Project, including all source code, binaries and
|
||||
documentation, unless otherwise stated in the file in its
|
||||
original, unmodified form as distributed in the original archive.
|
||||
If you are unsure whether or not a particular file is covered by
|
||||
this license, you must contact us to verify this.
|
||||
|
||||
The FreeType Project is copyright (C) 1996-2000 by David Turner,
|
||||
Robert Wilhelm, and Werner Lemberg. All rights reserved except as
|
||||
specified below.
|
||||
|
||||
1. No Warranty
|
||||
--------------
|
||||
|
||||
THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO
|
||||
USE, OF THE FREETYPE PROJECT.
|
||||
|
||||
2. Redistribution
|
||||
-----------------
|
||||
|
||||
This license grants a worldwide, royalty-free, perpetual and
|
||||
irrevocable right and license to use, execute, perform, compile,
|
||||
display, copy, create derivative works of, distribute and
|
||||
sublicense the FreeType Project (in both source and object code
|
||||
forms) and derivative works thereof for any purpose; and to
|
||||
authorize others to exercise some or all of the rights granted
|
||||
herein, subject to the following conditions:
|
||||
|
||||
o Redistribution of source code must retain this license file
|
||||
(`FTL.TXT') unaltered; any additions, deletions or changes to
|
||||
the original files must be clearly indicated in accompanying
|
||||
documentation. The copyright notices of the unaltered,
|
||||
original files must be preserved in all copies of source
|
||||
files.
|
||||
|
||||
o Redistribution in binary form must provide a disclaimer that
|
||||
states that the software is based in part of the work of the
|
||||
FreeType Team, in the distribution documentation. We also
|
||||
encourage you to put an URL to the FreeType web page in your
|
||||
documentation, though this isn't mandatory.
|
||||
|
||||
These conditions apply to any software derived from or based on
|
||||
the FreeType Project, not just the unmodified files. If you use
|
||||
our work, you must acknowledge us. However, no fee need be paid
|
||||
to us.
|
||||
|
||||
3. Advertising
|
||||
--------------
|
||||
|
||||
Neither the FreeType authors and contributors nor you shall use
|
||||
the name of the other for commercial, advertising, or promotional
|
||||
purposes without specific prior written permission.
|
||||
|
||||
We suggest, but do not require, that you use one or more of the
|
||||
following phrases to refer to this software in your documentation
|
||||
or advertising materials: `FreeType Project', `FreeType Engine',
|
||||
`FreeType library', or `FreeType Distribution'.
|
||||
|
||||
As you have not signed this license, you are not required to
|
||||
accept it. However, as the FreeType Project is copyrighted
|
||||
material, only this license, or another one contracted with the
|
||||
authors, grants you the right to use, distribute, and modify it.
|
||||
Therefore, by using, distributing, or modifying the FreeType
|
||||
Project, you indicate that you understand and accept all the terms
|
||||
of this license.
|
||||
|
||||
4. Contacts
|
||||
-----------
|
||||
|
||||
There are two mailing lists related to FreeType:
|
||||
|
||||
o freetype@nongnu.org
|
||||
|
||||
Discusses general use and applications of FreeType, as well as
|
||||
future and wanted additions to the library and distribution.
|
||||
If you are looking for support, start in this list if you
|
||||
haven't found anything to help you in the documentation.
|
||||
|
||||
o freetype-devel@nongnu.org
|
||||
|
||||
Discusses bugs, as well as engine internals, design issues,
|
||||
specific licenses, porting, etc.
|
||||
|
||||
Our home page can be found at
|
||||
|
||||
https://www.freetype.org
|
||||
|
||||
|
||||
--- end of FTL.TXT ---
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
The following license details are part of `src/bdf/README`:
|
||||
|
||||
```
|
||||
License
|
||||
*******
|
||||
|
||||
Copyright (C) 2001-2002 by Francesco Zappa Nardelli
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*** Portions of the driver (that is, bdflib.c and bdf.h):
|
||||
|
||||
Copyright 2000 Computing Research Labs, New Mexico State University
|
||||
Copyright 2001-2002, 2011 Francesco Zappa Nardelli
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
Credits
|
||||
*******
|
||||
|
||||
This driver is based on excellent Mark Leisher's bdf library. If you
|
||||
find something good in this driver you should probably thank him, not
|
||||
me.
|
||||
```
|
||||
|
||||
The following license details are part of `src/pcf/README`:
|
||||
|
||||
```
|
||||
License
|
||||
*******
|
||||
|
||||
Copyright (C) 2000 by Francesco Zappa Nardelli
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
Credits
|
||||
*******
|
||||
|
||||
Keith Packard wrote the pcf driver found in XFree86. His work is at
|
||||
the same time the specification and the sample implementation of the
|
||||
PCF format. Undoubtedly, this driver is inspired from his work.
|
||||
```
|
||||
|
|
|
@ -118,6 +118,12 @@ DEPS = {
|
|||
"(LEGAL ISSUES\n============\n\n.+?)\n\nREFERENCES\n=========="
|
||||
".+(libjpeg-turbo Licenses\n======================\n\n.+)$"
|
||||
),
|
||||
"patch": {
|
||||
r"CMakeLists.txt": {
|
||||
# libjpeg-turbo does not detect MSVC x86_arm64 cross-compiler correctly
|
||||
'if(MSVC_IDE AND CMAKE_GENERATOR_PLATFORM MATCHES "arm64")': "if({architecture} STREQUAL ARM64)", # noqa: E501
|
||||
},
|
||||
},
|
||||
"build": [
|
||||
*cmds_cmake(
|
||||
("jpeg-static", "cjpeg-static", "djpeg-static"),
|
||||
|
@ -148,9 +154,9 @@ DEPS = {
|
|||
"libs": [r"*.lib"],
|
||||
},
|
||||
"xz": {
|
||||
"url": SF_PROJECTS + "/lzmautils/files/xz-5.4.4.tar.gz/download",
|
||||
"filename": "xz-5.4.4.tar.gz",
|
||||
"dir": "xz-5.4.4",
|
||||
"url": SF_PROJECTS + "/lzmautils/files/xz-5.4.5.tar.gz/download",
|
||||
"filename": "xz-5.4.5.tar.gz",
|
||||
"dir": "xz-5.4.5",
|
||||
"license": "COPYING",
|
||||
"build": [
|
||||
*cmds_cmake("liblzma", "-DBUILD_SHARED_LIBS:BOOL=OFF"),
|
||||
|
@ -213,7 +219,6 @@ DEPS = {
|
|||
],
|
||||
"headers": [r"libtiff\tiff*.h"],
|
||||
"libs": [r"libtiff\*.lib"],
|
||||
# "bins": [r"libtiff\*.dll"],
|
||||
},
|
||||
"libpng": {
|
||||
"url": SF_PROJECTS + "/libpng/files/libpng16/1.6.39/lpng1639.zip/download",
|
||||
|
@ -272,7 +277,6 @@ DEPS = {
|
|||
cmd_xcopy("include", "{inc_dir}"),
|
||||
],
|
||||
"libs": [r"objs\{msbuild_arch}\Release Static\freetype.lib"],
|
||||
# "bins": [r"objs\{msbuild_arch}\Release\freetype.dll"],
|
||||
},
|
||||
"lcms2": {
|
||||
"url": SF_PROJECTS + "/lcms/files/lcms/2.15/lcms2-2.15.tar.gz/download",
|
||||
|
@ -329,6 +333,8 @@ DEPS = {
|
|||
"CMakeLists.txt": {
|
||||
"if(OPENMP_FOUND)": "if(false)",
|
||||
"install": "#install",
|
||||
# libimagequant does not detect MSVC x86_arm64 cross-compiler correctly
|
||||
"if(${{CMAKE_SYSTEM_PROCESSOR}} STREQUAL ARM64)": "if({architecture} STREQUAL ARM64)", # noqa: E501
|
||||
}
|
||||
},
|
||||
"build": [
|
||||
|
@ -339,9 +345,9 @@ DEPS = {
|
|||
"libs": [r"imagequant.lib"],
|
||||
},
|
||||
"harfbuzz": {
|
||||
"url": "https://github.com/harfbuzz/harfbuzz/archive/8.2.1.zip",
|
||||
"filename": "harfbuzz-8.2.1.zip",
|
||||
"dir": "harfbuzz-8.2.1",
|
||||
"url": "https://github.com/harfbuzz/harfbuzz/archive/8.3.0.zip",
|
||||
"filename": "harfbuzz-8.3.0.zip",
|
||||
"dir": "harfbuzz-8.3.0",
|
||||
"license": "COPYING",
|
||||
"build": [
|
||||
*cmds_cmake(
|
||||
|
@ -369,12 +375,17 @@ DEPS = {
|
|||
|
||||
|
||||
# based on distutils._msvccompiler from CPython 3.7.4
|
||||
def find_msvs() -> dict[str, str] | None:
|
||||
def find_msvs(architecture: str) -> dict[str, str] | None:
|
||||
root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles")
|
||||
if not root:
|
||||
print("Program Files not found")
|
||||
return None
|
||||
|
||||
if architecture == "ARM64":
|
||||
tools = "Microsoft.VisualStudio.Component.VC.Tools.ARM64"
|
||||
else:
|
||||
tools = "Microsoft.VisualStudio.Component.VC.Tools.x86.x64"
|
||||
|
||||
try:
|
||||
vspath = (
|
||||
subprocess.check_output(
|
||||
|
@ -385,7 +396,7 @@ def find_msvs() -> dict[str, str] | None:
|
|||
"-latest",
|
||||
"-prerelease",
|
||||
"-requires",
|
||||
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
|
||||
tools,
|
||||
"-property",
|
||||
"installationPath",
|
||||
"-products",
|
||||
|
@ -471,7 +482,7 @@ def extract_dep(url: str, filename: str) -> None:
|
|||
msg = "Attempted Path Traversal in Zip File"
|
||||
raise RuntimeError(msg)
|
||||
zf.extractall(sources_dir)
|
||||
elif filename.endswith(".tar.gz") or filename.endswith(".tgz"):
|
||||
elif filename.endswith((".tar.gz", ".tgz")):
|
||||
with tarfile.open(file, "r:gz") as tgz:
|
||||
for member in tgz.getnames():
|
||||
member_abspath = os.path.abspath(os.path.join(sources_dir, member))
|
||||
|
@ -575,14 +586,19 @@ def build_dep(name: str) -> str:
|
|||
|
||||
def build_dep_all() -> None:
|
||||
lines = [r'call "{build_dir}\build_env.cmd"']
|
||||
gha_groups = "GITHUB_ACTIONS" in os.environ
|
||||
for dep_name in DEPS:
|
||||
print()
|
||||
if dep_name in disabled:
|
||||
print(f"Skipping disabled dependency {dep_name}")
|
||||
continue
|
||||
script = build_dep(dep_name)
|
||||
if gha_groups:
|
||||
lines.append(f"@echo ::group::Running {script}")
|
||||
lines.append(rf'cmd.exe /c "{{build_dir}}\{script}"')
|
||||
lines.append("if errorlevel 1 echo Build failed! && exit /B 1")
|
||||
if gha_groups:
|
||||
lines.append("@echo ::endgroup::")
|
||||
print()
|
||||
lines.append("@echo All Pillow dependencies built successfully!")
|
||||
write_script("build_dep_all.cmd", lines)
|
||||
|
@ -656,7 +672,7 @@ if __name__ == "__main__":
|
|||
arch_prefs = ARCHITECTURES[args.architecture]
|
||||
print("Target architecture:", args.architecture)
|
||||
|
||||
msvs = find_msvs()
|
||||
msvs = find_msvs(args.architecture)
|
||||
if msvs is None:
|
||||
msg = "Visual Studio not found. Please install Visual Studio 2017 or newer."
|
||||
raise RuntimeError(msg)
|
||||
|
@ -691,6 +707,11 @@ if __name__ == "__main__":
|
|||
disabled += ["libimagequant"]
|
||||
if args.no_fribidi:
|
||||
disabled += ["fribidi"]
|
||||
elif args.architecture == "ARM64" and platform.machine() != "ARM64":
|
||||
import warnings
|
||||
|
||||
warnings.warn("Cross-compiling FriBiDi is currently not supported, disabling")
|
||||
disabled += ["fribidi"]
|
||||
|
||||
prefs = {
|
||||
"architecture": args.architecture,
|
||||
|
|
Loading…
Reference in New Issue
Block a user