mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-04-25 03:23:41 +03:00
Merge branch 'main' into font
This commit is contained in:
commit
9a6c47a9d2
1
.ci/requirements-cibw.txt
Normal file
1
.ci/requirements-cibw.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
cibuildwheel==2.16.2
|
48
.github/workflows/test-windows.yml
vendored
48
.github/workflows/test-windows.yml
vendored
|
@ -72,10 +72,10 @@ jobs:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
id: install
|
id: install
|
||||||
run: |
|
run: |
|
||||||
7z x winbuild\depends\nasm-2.16.01-win64.zip "-o$env:RUNNER_WORKSPACE\"
|
choco install nasm --no-progress
|
||||||
echo "$env:RUNNER_WORKSPACE\nasm-2.16.01" >> $env:GITHUB_PATH
|
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
|
echo "C:\Program Files\gs\gs10.00.0\bin" >> $env:GITHUB_PATH
|
||||||
|
|
||||||
# Install extra test images
|
# Install extra test images
|
||||||
|
@ -167,7 +167,6 @@ jobs:
|
||||||
- name: Build Pillow
|
- name: Build Pillow
|
||||||
run: |
|
run: |
|
||||||
$FLAGS="-C raqm=vendor -C fribidi=vendor"
|
$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 ."
|
cmd /c "winbuild\build\build_env.cmd && $env:pythonLocation\python.exe -m pip install -v $FLAGS ."
|
||||||
& $env:pythonLocation\python.exe selftest.py --installed
|
& $env:pythonLocation\python.exe selftest.py --installed
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
@ -209,47 +208,6 @@ jobs:
|
||||||
flags: GHA_Windows
|
flags: GHA_Windows
|
||||||
name: ${{ runner.os }} Python ${{ matrix.python-version }}
|
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:
|
success:
|
||||||
permissions:
|
permissions:
|
||||||
contents: none
|
contents: none
|
||||||
|
|
4
.github/workflows/wheels-dependencies.sh
vendored
4
.github/workflows/wheels-dependencies.sh
vendored
|
@ -16,13 +16,13 @@ ARCHIVE_SDIR=pillow-depends-main
|
||||||
|
|
||||||
# Package versions for fresh source builds
|
# Package versions for fresh source builds
|
||||||
FREETYPE_VERSION=2.13.2
|
FREETYPE_VERSION=2.13.2
|
||||||
HARFBUZZ_VERSION=8.2.1
|
HARFBUZZ_VERSION=8.3.0
|
||||||
LIBPNG_VERSION=1.6.40
|
LIBPNG_VERSION=1.6.40
|
||||||
JPEGTURBO_VERSION=3.0.1
|
JPEGTURBO_VERSION=3.0.1
|
||||||
OPENJPEG_VERSION=2.5.0
|
OPENJPEG_VERSION=2.5.0
|
||||||
XZ_VERSION=5.4.5
|
XZ_VERSION=5.4.5
|
||||||
TIFF_VERSION=4.6.0
|
TIFF_VERSION=4.6.0
|
||||||
LCMS2_VERSION=2.15
|
LCMS2_VERSION=2.16
|
||||||
if [[ -n "$IS_MACOS" ]]; then
|
if [[ -n "$IS_MACOS" ]]; then
|
||||||
GIFLIB_VERSION=5.1.4
|
GIFLIB_VERSION=5.1.4
|
||||||
else
|
else
|
||||||
|
|
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 }
|
22
.github/workflows/wheels-test.sh
vendored
22
.github/workflows/wheels-test.sh
vendored
|
@ -1,10 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
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"
|
|
||||||
|
|
||||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
brew install fribidi
|
brew install fribidi
|
||||||
export PKG_CONFIG_PATH="/usr/local/opt/openblas/lib/pkgconfig"
|
export PKG_CONFIG_PATH="/usr/local/opt/openblas/lib/pkgconfig"
|
||||||
|
@ -25,21 +21,5 @@ fi
|
||||||
|
|
||||||
# Runs tests
|
# Runs tests
|
||||||
python3 selftest.py
|
python3 selftest.py
|
||||||
|
python3 -m pytest Tests/check_wheel.py
|
||||||
python3 -m pytest
|
python3 -m pytest
|
||||||
|
|
||||||
# Test against expected codecs, modules and features
|
|
||||||
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'"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
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'"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
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'"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
119
.github/workflows/wheels.yml
vendored
119
.github/workflows/wheels.yml
vendored
|
@ -3,14 +3,20 @@ name: Wheels
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- ".github/workflows/wheels*.yml"
|
- ".ci/requirements-cibw.txt"
|
||||||
|
- ".github/workflows/wheel*"
|
||||||
- "wheels/*"
|
- "wheels/*"
|
||||||
|
- "winbuild/build_prepare.py"
|
||||||
|
- "winbuild/fribidi.cmake"
|
||||||
tags:
|
tags:
|
||||||
- "*"
|
- "*"
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- ".github/workflows/wheels*.yml"
|
- ".ci/requirements-cibw.txt"
|
||||||
|
- ".github/workflows/wheel*"
|
||||||
- "wheels/*"
|
- "wheels/*"
|
||||||
|
- "winbuild/build_prepare.py"
|
||||||
|
- "winbuild/fribidi.cmake"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
@ -52,14 +58,17 @@ jobs:
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- name: Build wheels
|
- uses: actions/setup-python@v4
|
||||||
uses: pypa/cibuildwheel@v2.16.2
|
|
||||||
with:
|
with:
|
||||||
output-dir: wheelhouse
|
python-version: "3.x"
|
||||||
|
|
||||||
|
- name: Build wheels
|
||||||
|
run: |
|
||||||
|
python3 -m pip install -r .ci/requirements-cibw.txt
|
||||||
|
python3 -m cibuildwheel --output-dir wheelhouse
|
||||||
env:
|
env:
|
||||||
CIBW_ARCHS: ${{ matrix.archs }}
|
CIBW_ARCHS: ${{ matrix.archs }}
|
||||||
CIBW_BUILD: ${{ matrix.build }}
|
CIBW_BUILD: ${{ matrix.build }}
|
||||||
CIBW_CONFIG_SETTINGS: raqm=enable raqm=vendor fribidi=vendor
|
|
||||||
CIBW_MANYLINUX_PYPY_X86_64_IMAGE: ${{ matrix.manylinux }}
|
CIBW_MANYLINUX_PYPY_X86_64_IMAGE: ${{ matrix.manylinux }}
|
||||||
CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux }}
|
CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux }}
|
||||||
CIBW_SKIP: pp38-*
|
CIBW_SKIP: pp38-*
|
||||||
|
@ -71,6 +80,102 @@ jobs:
|
||||||
name: dist
|
name: dist
|
||||||
path: ./wheelhouse/*.whl
|
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:
|
sdist:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
@ -93,7 +198,7 @@ jobs:
|
||||||
success:
|
success:
|
||||||
permissions:
|
permissions:
|
||||||
contents: none
|
contents: none
|
||||||
needs: [build, sdist]
|
needs: [build, windows, sdist]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Wheels Successful
|
name: Wheels Successful
|
||||||
steps:
|
steps:
|
||||||
|
|
|
@ -3,7 +3,6 @@ if: tag IS present OR type = api
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- CIBW_ARCHS=aarch64
|
- CIBW_ARCHS=aarch64
|
||||||
- CIBW_CONFIG_SETTINGS="raqm=enable raqm=vendor fribidi=vendor"
|
|
||||||
- CIBW_SKIP=pp38-*
|
- CIBW_SKIP=pp38-*
|
||||||
|
|
||||||
language: python
|
language: python
|
||||||
|
@ -35,7 +34,7 @@ jobs:
|
||||||
- CIBW_BUILD="*musllinux*"
|
- CIBW_BUILD="*musllinux*"
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- python3 -m pip install cibuildwheel==2.16.2
|
- python3 -m pip install -r .ci/requirements-cibw.txt
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- python3 -m cibuildwheel --output-dir wheelhouse
|
- python3 -m cibuildwheel --output-dir wheelhouse
|
||||||
|
|
|
@ -5,6 +5,15 @@ Changelog (Pillow)
|
||||||
10.2.0 (unreleased)
|
10.2.0 (unreleased)
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
- Correct PDF palette size when saving #7555
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Fixed closing file pointer with olefile 0.47 #7594
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Raise ValueError when TrueType font size is not greater than zero #7584, #7587
|
||||||
|
[akx, radarhere]
|
||||||
|
|
||||||
- If absent, do not try to close fp when closing image #7557
|
- If absent, do not try to close fp when closing image #7557
|
||||||
[RaphaelVRossi, radarhere]
|
[RaphaelVRossi, radarhere]
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,6 @@ Released as needed privately to individual vendors for critical security-related
|
||||||
|
|
||||||
## Source and Binary Distributions
|
## Source and Binary Distributions
|
||||||
|
|
||||||
### macOS and Linux
|
|
||||||
* [ ] Download sdist and 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):
|
and copy into `dist/`. For example using [GitHub CLI](https://github.com/cli/cli):
|
||||||
```bash
|
```bash
|
||||||
|
@ -104,14 +103,6 @@ Released as needed privately to individual vendors for critical security-related
|
||||||
* [ ] Download the Linux aarch64 wheels created by Travis CI from [GitHub releases](https://github.com/python-pillow/Pillow/releases)
|
* [ ] Download the Linux aarch64 wheels created by Travis CI from [GitHub releases](https://github.com/python-pillow/Pillow/releases)
|
||||||
and copy into `dist`.
|
and copy into `dist`.
|
||||||
|
|
||||||
### Windows
|
|
||||||
* [ ] Download the artifacts from the [GitHub Actions "Test Windows" workflow](https://github.com/python-pillow/Pillow/actions/workflows/test-windows.yml)
|
|
||||||
and copy into `dist/`. For example using [GitHub CLI](https://github.com/cli/cli):
|
|
||||||
```bash
|
|
||||||
gh run download --dir dist
|
|
||||||
# select dist-x.y.z
|
|
||||||
```
|
|
||||||
|
|
||||||
## Publicize Release
|
## Publicize Release
|
||||||
|
|
||||||
* [ ] Announce release availability via [Twitter](https://twitter.com/pythonpillow) and [Mastodon](https://fosstodon.org/@pillow) e.g. https://twitter.com/PythonPillow/status/1013789184354603010
|
* [ ] Announce release availability via [Twitter](https://twitter.com/pythonpillow) and [Mastodon](https://fosstodon.org/@pillow) e.g. https://twitter.com/PythonPillow/status/1013789184354603010
|
||||||
|
|
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 logging
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import sysconfig
|
import sysconfig
|
||||||
import tempfile
|
import tempfile
|
||||||
|
@ -258,11 +259,21 @@ def hopper(mode=None, cache={}):
|
||||||
|
|
||||||
|
|
||||||
def djpeg_available():
|
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():
|
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():
|
def netpbm_available():
|
||||||
|
|
|
@ -67,7 +67,7 @@ def test_quantize_no_dither():
|
||||||
|
|
||||||
def test_quantize_no_dither2():
|
def test_quantize_no_dither2():
|
||||||
im = Image.new("RGB", (9, 1))
|
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))
|
palette = Image.new("P", (1, 1))
|
||||||
data = (0, 0, 0, 32, 32, 32)
|
data = (0, 0, 0, 32, 32, 32)
|
||||||
|
|
|
@ -1073,3 +1073,9 @@ def test_raqm_missing_warning(monkeypatch):
|
||||||
"Raqm layout was requested, but Raqm is not available. "
|
"Raqm layout was requested, but Raqm is not available. "
|
||||||
"Falling back to basic layout."
|
"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:
|
class TestImageGrab:
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
os.environ.get("USERNAME") == "ContainerAdministrator",
|
||||||
|
reason="can't grab screen when running in Docker",
|
||||||
|
)
|
||||||
@pytest.mark.skipif(
|
@pytest.mark.skipif(
|
||||||
sys.platform not in ("win32", "darwin"), reason="requires Windows or macOS"
|
sys.platform not in ("win32", "darwin"), reason="requires Windows or macOS"
|
||||||
)
|
)
|
||||||
|
|
10
docs/conf.py
10
docs/conf.py
|
@ -166,6 +166,12 @@ html_static_path = ["resources"]
|
||||||
# directly to the root of the documentation.
|
# directly to the root of the documentation.
|
||||||
# html_extra_path = []
|
# 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,
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||||
# using the given strftime format.
|
# using the given strftime format.
|
||||||
# html_last_updated_fmt = '%b %d, %Y'
|
# html_last_updated_fmt = '%b %d, %Y'
|
||||||
|
@ -313,10 +319,6 @@ texinfo_documents = [
|
||||||
# texinfo_no_detailmenu = False
|
# texinfo_no_detailmenu = False
|
||||||
|
|
||||||
|
|
||||||
def setup(app):
|
|
||||||
app.add_css_file("css/dark.css")
|
|
||||||
|
|
||||||
|
|
||||||
linkcheck_allowed_redirects = {
|
linkcheck_allowed_redirects = {
|
||||||
r"https://www.bestpractices.dev/projects/6331": r"https://www.bestpractices.dev/en/.*",
|
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",
|
r"https://badges.gitter.im/python-pillow/Pillow.svg": r"https://badges.gitter.im/repo.svg",
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
Installation
|
Installation
|
||||||
============
|
============
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
activateTab(getOS());
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
Warnings
|
Warnings
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
@ -87,11 +95,10 @@ and :pypi:`olefile` for Pillow to read FPX and MIC images::
|
||||||
|
|
||||||
.. tab:: Windows
|
.. tab:: Windows
|
||||||
|
|
||||||
.. warning:: Pillow > 9.5.0 no longer includes 32-bit wheels.
|
We provide Pillow binaries for Windows compiled for the matrix of supported
|
||||||
|
Pythons in the wheel format. These include x86, x86-64 and arm64 versions
|
||||||
We provide Pillow binaries for Windows compiled for the matrix of
|
(with the exception of Python 3.8 on arm64). These binaries include support
|
||||||
supported Pythons in 64-bit versions in the wheel format. These binaries include
|
for all optional libraries except libimagequant and libxcb. Raqm support
|
||||||
support for all optional libraries except libimagequant and libxcb. Raqm support
|
|
||||||
requires FriBiDi to be installed separately::
|
requires FriBiDi to be installed separately::
|
||||||
|
|
||||||
python3 -m pip install --upgrade pip
|
python3 -m pip install --upgrade pip
|
||||||
|
@ -168,7 +175,7 @@ Many of Pillow's features require external libraries:
|
||||||
* **littlecms** provides color management
|
* **littlecms** provides color management
|
||||||
|
|
||||||
* Pillow version 2.2.1 and below uses liblcms1, Pillow 2.3.0 and
|
* Pillow version 2.2.1 and below uses liblcms1, Pillow 2.3.0 and
|
||||||
above uses liblcms2. Tested with **1.19** and **2.7-2.15**.
|
above uses liblcms2. Tested with **1.19** and **2.7-2.16**.
|
||||||
|
|
||||||
* **libwebp** provides the WebP format.
|
* **libwebp** provides the WebP format.
|
||||||
|
|
||||||
|
|
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",
|
"sphinx-removed-in",
|
||||||
"sphinxext-opengraph",
|
"sphinxext-opengraph",
|
||||||
]
|
]
|
||||||
|
fpx = [
|
||||||
|
"olefile",
|
||||||
|
]
|
||||||
|
mic = [
|
||||||
|
"olefile",
|
||||||
|
]
|
||||||
tests = [
|
tests = [
|
||||||
"check-manifest",
|
"check-manifest",
|
||||||
"coverage",
|
"coverage",
|
||||||
|
@ -59,6 +65,9 @@ tests = [
|
||||||
"pytest-cov",
|
"pytest-cov",
|
||||||
"pytest-timeout",
|
"pytest-timeout",
|
||||||
]
|
]
|
||||||
|
xmp = [
|
||||||
|
"defusedxml",
|
||||||
|
]
|
||||||
[project.urls]
|
[project.urls]
|
||||||
Changelog = "https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst"
|
Changelog = "https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst"
|
||||||
Documentation = "https://pillow.readthedocs.io"
|
Documentation = "https://pillow.readthedocs.io"
|
||||||
|
@ -79,12 +88,15 @@ version = {attr = "PIL.__version__"}
|
||||||
|
|
||||||
[tool.cibuildwheel]
|
[tool.cibuildwheel]
|
||||||
before-all = ".github/workflows/wheels-dependencies.sh"
|
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-command = "cd {project} && .github/workflows/wheels-test.sh"
|
||||||
test-extras = "tests"
|
test-extras = "tests"
|
||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
line-length = 88
|
line-length = 88
|
||||||
select = [
|
select = [
|
||||||
|
"C4", # flake8-comprehensions
|
||||||
"E", # pycodestyle errors
|
"E", # pycodestyle errors
|
||||||
"EM", # flake8-errmsg
|
"EM", # flake8-errmsg
|
||||||
"F", # pyflakes errors
|
"F", # pyflakes errors
|
||||||
|
|
22
setup.py
22
setup.py
|
@ -440,17 +440,17 @@ class pil_build_ext(build_ext):
|
||||||
|
|
||||||
#
|
#
|
||||||
# add configured kits
|
# add configured kits
|
||||||
for root_name, lib_name in dict(
|
for root_name, lib_name in {
|
||||||
JPEG_ROOT="libjpeg",
|
"JPEG_ROOT": "libjpeg",
|
||||||
JPEG2K_ROOT="libopenjp2",
|
"JPEG2K_ROOT": "libopenjp2",
|
||||||
TIFF_ROOT=("libtiff-5", "libtiff-4"),
|
"TIFF_ROOT": ("libtiff-5", "libtiff-4"),
|
||||||
ZLIB_ROOT="zlib",
|
"ZLIB_ROOT": "zlib",
|
||||||
FREETYPE_ROOT="freetype2",
|
"FREETYPE_ROOT": "freetype2",
|
||||||
HARFBUZZ_ROOT="harfbuzz",
|
"HARFBUZZ_ROOT": "harfbuzz",
|
||||||
FRIBIDI_ROOT="fribidi",
|
"FRIBIDI_ROOT": "fribidi",
|
||||||
LCMS_ROOT="lcms2",
|
"LCMS_ROOT": "lcms2",
|
||||||
IMAGEQUANT_ROOT="libimagequant",
|
"IMAGEQUANT_ROOT": "libimagequant",
|
||||||
).items():
|
}.items():
|
||||||
root = globals()[root_name]
|
root = globals()[root_name]
|
||||||
|
|
||||||
if root is None and root_name in os.environ:
|
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))
|
dpi = info.get("dpi", (96, 96))
|
||||||
|
|
||||||
# 1 meter == 39.3701 inches
|
# 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)
|
stride = ((im.size[0] * bits + 7) // 8 + 3) & (~3)
|
||||||
header = 40 # or 64 for OS/2 version 2
|
header = 40 # or 64 for OS/2 version 2
|
||||||
|
|
|
@ -64,8 +64,6 @@ class CurImageFile(BmpImagePlugin.BmpImageFile):
|
||||||
d, e, o, a = self.tile[0]
|
d, e, o, a = self.tile[0]
|
||||||
self.tile[0] = d, (0, 0) + self.size, o, a
|
self.tile[0] = d, (0, 0) + self.size, o, a
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
|
|
|
@ -227,6 +227,7 @@ class FpxImageFile(ImageFile.ImageFile):
|
||||||
break # isn't really required
|
break # isn't really required
|
||||||
|
|
||||||
self.stream = stream
|
self.stream = stream
|
||||||
|
self._fp = self.fp
|
||||||
self.fp = None
|
self.fp = None
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
|
|
|
@ -40,7 +40,7 @@ from enum import IntEnum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import defusedxml.ElementTree as ElementTree
|
from defusedxml import ElementTree
|
||||||
except ImportError:
|
except ImportError:
|
||||||
ElementTree = None
|
ElementTree = None
|
||||||
|
|
||||||
|
@ -1160,7 +1160,7 @@ class Image:
|
||||||
if palette.mode != "P":
|
if palette.mode != "P":
|
||||||
msg = "bad mode for palette image"
|
msg = "bad mode for palette image"
|
||||||
raise ValueError(msg)
|
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"
|
msg = "only RGB or L mode images can be quantized to a palette"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
im = self.im.convert("P", dither, palette.im)
|
im = self.im.convert("P", dither, palette.im)
|
||||||
|
|
|
@ -921,7 +921,7 @@ def floodfill(image, xy, value, border=None, thresh=0):
|
||||||
if border is None:
|
if border is None:
|
||||||
fill = _color_diff(p, background) <= thresh
|
fill = _color_diff(p, background) <= thresh
|
||||||
else:
|
else:
|
||||||
fill = p != value and p != border
|
fill = p not in (value, border)
|
||||||
if fill:
|
if fill:
|
||||||
pixel[s, t] = value
|
pixel[s, t] = value
|
||||||
new_edge.add((s, t))
|
new_edge.add((s, t))
|
||||||
|
|
|
@ -198,6 +198,10 @@ class FreeTypeFont:
|
||||||
) -> None:
|
) -> None:
|
||||||
# FIXME: use service provider instead
|
# FIXME: use service provider instead
|
||||||
|
|
||||||
|
if size <= 0:
|
||||||
|
msg = "font size must be greater than 0"
|
||||||
|
raise ValueError(msg)
|
||||||
|
|
||||||
self.path = font
|
self.path = font
|
||||||
self.size = size
|
self.size = size
|
||||||
self.index = index
|
self.index = index
|
||||||
|
@ -800,6 +804,7 @@ def truetype(font=None, size=10, index=0, encoding="", layout_engine=None):
|
||||||
.. versionadded:: 4.2.0
|
.. versionadded:: 4.2.0
|
||||||
:return: A font object.
|
:return: A font object.
|
||||||
:exception OSError: If the file could not be read.
|
:exception OSError: If the file could not be read.
|
||||||
|
:exception ValueError: If the font size is not greater than zero.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def freetype(font):
|
def freetype(font):
|
||||||
|
|
|
@ -334,10 +334,7 @@ def _save(im, fp, filename):
|
||||||
if quality_layers is not None and not (
|
if quality_layers is not None and not (
|
||||||
isinstance(quality_layers, (list, tuple))
|
isinstance(quality_layers, (list, tuple))
|
||||||
and all(
|
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"
|
msg = "quality_layers must be a sequence of numbers"
|
||||||
|
|
|
@ -397,7 +397,7 @@ class JpegImageFile(ImageFile.ImageFile):
|
||||||
# self.__offset = self.fp.tell()
|
# self.__offset = self.fp.tell()
|
||||||
break
|
break
|
||||||
s = self.fp.read(1)
|
s = self.fp.read(1)
|
||||||
elif i == 0 or i == 0xFFFF:
|
elif i in {0, 0xFFFF}:
|
||||||
# padded marker or junk; move on
|
# padded marker or junk; move on
|
||||||
s = b"\xff"
|
s = b"\xff"
|
||||||
elif i == 0xFF00: # Skip extraneous data (escaped 0xFF)
|
elif i == 0xFF00: # Skip extraneous data (escaped 0xFF)
|
||||||
|
|
|
@ -66,6 +66,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
|
||||||
self._n_frames = len(self.images)
|
self._n_frames = len(self.images)
|
||||||
self.is_animated = self._n_frames > 1
|
self.is_animated = self._n_frames > 1
|
||||||
|
|
||||||
|
self.__fp = self.fp
|
||||||
self.seek(0)
|
self.seek(0)
|
||||||
|
|
||||||
def seek(self, frame):
|
def seek(self, frame):
|
||||||
|
@ -87,10 +88,12 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
|
||||||
return self.frame
|
return self.frame
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
self.__fp.close()
|
||||||
self.ole.close()
|
self.ole.close()
|
||||||
super().close()
|
super().close()
|
||||||
|
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
|
self.__fp.close()
|
||||||
self.ole.close()
|
self.ole.close()
|
||||||
super().__exit__()
|
super().__exit__()
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ def _write_image(im, filename, existing_pdf, image_refs):
|
||||||
dict_obj["ColorSpace"] = [
|
dict_obj["ColorSpace"] = [
|
||||||
PdfParser.PdfName("Indexed"),
|
PdfParser.PdfName("Indexed"),
|
||||||
PdfParser.PdfName("DeviceRGB"),
|
PdfParser.PdfName("DeviceRGB"),
|
||||||
255,
|
len(palette) // 3 - 1,
|
||||||
PdfParser.PdfBinary(palette),
|
PdfParser.PdfBinary(palette),
|
||||||
]
|
]
|
||||||
procset = "ImageI" # indexed color
|
procset = "ImageI" # indexed color
|
||||||
|
|
|
@ -123,7 +123,7 @@ class SgiImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
|
|
||||||
def _save(im, fp, filename):
|
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"
|
msg = "Unsupported SGI image mode"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
||||||
|
@ -155,7 +155,7 @@ def _save(im, fp, filename):
|
||||||
# Z Dimension: Number of channels
|
# Z Dimension: Number of channels
|
||||||
z = len(im.mode)
|
z = len(im.mode)
|
||||||
|
|
||||||
if dim == 1 or dim == 2:
|
if dim in {1, 2}:
|
||||||
z = 1
|
z = 1
|
||||||
|
|
||||||
# assert we've got the right number of bands.
|
# assert we've got the right number of bands.
|
||||||
|
|
|
@ -427,7 +427,7 @@ def _populate():
|
||||||
|
|
||||||
TAGS_V2[k] = TagInfo(k, *v)
|
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():
|
for k, v in tags.items():
|
||||||
tags[k] = TagInfo(k, *v)
|
tags[k] = TagInfo(k, *v)
|
||||||
|
|
||||||
|
|
|
@ -279,10 +279,10 @@ DEPS = {
|
||||||
"libs": [r"objs\{msbuild_arch}\Release Static\freetype.lib"],
|
"libs": [r"objs\{msbuild_arch}\Release Static\freetype.lib"],
|
||||||
},
|
},
|
||||||
"lcms2": {
|
"lcms2": {
|
||||||
"url": SF_PROJECTS + "/lcms/files/lcms/2.15/lcms2-2.15.tar.gz/download",
|
"url": SF_PROJECTS + "/lcms/files/lcms/2.16/lcms2-2.16.tar.gz/download",
|
||||||
"filename": "lcms2-2.15.tar.gz",
|
"filename": "lcms2-2.16.tar.gz",
|
||||||
"dir": "lcms2-2.15",
|
"dir": "lcms2-2.16",
|
||||||
"license": "COPYING",
|
"license": "LICENSE",
|
||||||
"patch": {
|
"patch": {
|
||||||
r"Projects\VC2022\lcms2_static\lcms2_static.vcxproj": {
|
r"Projects\VC2022\lcms2_static\lcms2_static.vcxproj": {
|
||||||
# default is /MD for x86 and /MT for x64, we need /MD always
|
# default is /MD for x86 and /MT for x64, we need /MD always
|
||||||
|
@ -345,9 +345,9 @@ DEPS = {
|
||||||
"libs": [r"imagequant.lib"],
|
"libs": [r"imagequant.lib"],
|
||||||
},
|
},
|
||||||
"harfbuzz": {
|
"harfbuzz": {
|
||||||
"url": "https://github.com/harfbuzz/harfbuzz/archive/8.2.1.zip",
|
"url": "https://github.com/harfbuzz/harfbuzz/archive/8.3.0.zip",
|
||||||
"filename": "harfbuzz-8.2.1.zip",
|
"filename": "harfbuzz-8.3.0.zip",
|
||||||
"dir": "harfbuzz-8.2.1",
|
"dir": "harfbuzz-8.3.0",
|
||||||
"license": "COPYING",
|
"license": "COPYING",
|
||||||
"build": [
|
"build": [
|
||||||
*cmds_cmake(
|
*cmds_cmake(
|
||||||
|
@ -482,7 +482,7 @@ def extract_dep(url: str, filename: str) -> None:
|
||||||
msg = "Attempted Path Traversal in Zip File"
|
msg = "Attempted Path Traversal in Zip File"
|
||||||
raise RuntimeError(msg)
|
raise RuntimeError(msg)
|
||||||
zf.extractall(sources_dir)
|
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:
|
with tarfile.open(file, "r:gz") as tgz:
|
||||||
for member in tgz.getnames():
|
for member in tgz.getnames():
|
||||||
member_abspath = os.path.abspath(os.path.join(sources_dir, member))
|
member_abspath = os.path.abspath(os.path.join(sources_dir, member))
|
||||||
|
@ -586,14 +586,19 @@ def build_dep(name: str) -> str:
|
||||||
|
|
||||||
def build_dep_all() -> None:
|
def build_dep_all() -> None:
|
||||||
lines = [r'call "{build_dir}\build_env.cmd"']
|
lines = [r'call "{build_dir}\build_env.cmd"']
|
||||||
|
gha_groups = "GITHUB_ACTIONS" in os.environ
|
||||||
for dep_name in DEPS:
|
for dep_name in DEPS:
|
||||||
print()
|
print()
|
||||||
if dep_name in disabled:
|
if dep_name in disabled:
|
||||||
print(f"Skipping disabled dependency {dep_name}")
|
print(f"Skipping disabled dependency {dep_name}")
|
||||||
continue
|
continue
|
||||||
script = build_dep(dep_name)
|
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(rf'cmd.exe /c "{{build_dir}}\{script}"')
|
||||||
lines.append("if errorlevel 1 echo Build failed! && exit /B 1")
|
lines.append("if errorlevel 1 echo Build failed! && exit /B 1")
|
||||||
|
if gha_groups:
|
||||||
|
lines.append("@echo ::endgroup::")
|
||||||
print()
|
print()
|
||||||
lines.append("@echo All Pillow dependencies built successfully!")
|
lines.append("@echo All Pillow dependencies built successfully!")
|
||||||
write_script("build_dep_all.cmd", lines)
|
write_script("build_dep_all.cmd", lines)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user