Merge branch 'main' into enum

This commit is contained in:
Andrew Murray 2022-02-10 09:52:24 +11:00 committed by GitHub
commit b38a67fa12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 1131 additions and 455 deletions

View File

@ -12,7 +12,7 @@ environment:
matrix:
- PYTHON: C:/Python310
ARCHITECTURE: x86
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
- PYTHON: C:/Python37-x64
ARCHITECTURE: x64
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017

View File

@ -1,7 +1,7 @@
#!/bin/bash
# gather the coverage data
pip3 install codecov
python3 -m pip install codecov
if [[ $MATRIX_DOCKER ]]; then
coverage xml --ignore-errors
else

View File

@ -19,7 +19,7 @@ set -e
sudo apt-get -qq install libfreetype6-dev liblcms2-dev python3-tk\
ghostscript libffi-dev libjpeg-turbo-progs libopenjp2-7-dev\
cmake imagemagick libharfbuzz-dev libfribidi-dev
cmake meson imagemagick libharfbuzz-dev libfribidi-dev
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade wheel

View File

@ -19,9 +19,10 @@ jobs:
amazon-2-amd64,
arch,
centos-7-amd64,
centos-8-amd64,
centos-stream-8-amd64,
centos-stream-9-amd64,
debian-10-buster-x86,
debian-11-bullseye-x86,
fedora-34-amd64,
fedora-35-amd64,
ubuntu-18.04-bionic-amd64,

View File

@ -4,7 +4,7 @@ on: [push, pull_request, workflow_dispatch]
jobs:
build:
runs-on: windows-2019
runs-on: windows-latest
strategy:
fail-fast: false
matrix:

View File

@ -4,7 +4,7 @@ on: [push, pull_request, workflow_dispatch]
jobs:
build:
runs-on: windows-2019
runs-on: windows-latest
strategy:
fail-fast: false
matrix:

View File

@ -5,6 +5,24 @@ Changelog (Pillow)
9.1.0 (unreleased)
------------------
- Added unpacker from RGBA;15 to RGB #6031
[radarhere]
- Enable arm64 for MSVC on Windows #5811
[gaborkertesz-linaro, gaborkertesz]
- Keep IPython/Jupyter text/plain output stable #5891
[shamrin, radarhere]
- Raise an error when performing a negative crop #5972
[radarhere, hugovk]
- Deprecated show_file "file" argument in favour of "path" #5959
[radarhere]
- Fixed SPIDER images for use with Bio-formats library #5956
[radarhere]
- Ensure duplicated file pointer is closed #5946
[radarhere]
@ -17,6 +35,15 @@ Changelog (Pillow)
- Remove readonly from Image.__eq__ #5930
[hugovk]
9.0.1 (2022-02-03)
------------------
- In show_file, use os.remove to remove temporary images. CVE-2022-24303 #6010
[radarhere, hugovk]
- Restrict builtins within lambdas for ImageMath.eval. CVE-2022-22817 #6009
[radarhere]
9.0.0 (2022-01-02)
------------------

View File

@ -105,7 +105,7 @@ test:
.PHONY: valgrind
valgrind:
python3 -c "import pytest_valgrind" || pip3 install pytest-valgrind
python3 -c "import pytest_valgrind" || python3 -m pip install pytest-valgrind
PYTHONMALLOC=malloc valgrind --suppressions=Tests/oss-fuzz/python.supp --leak-check=no \
--log-file=/tmp/valgrind-output \
python3 -m pytest --no-memcheck -vv --valgrind --valgrind-log=/tmp/valgrind-output

View File

@ -24,16 +24,19 @@ As of 2019, Pillow development is
<tr>
<th>tests</th>
<td>
<a href="https://github.com/python-pillow/Pillow/actions?query=workflow%3ALint"><img
<a href="https://github.com/python-pillow/Pillow/actions/workflows/lint.yml"><img
alt="GitHub Actions build status (Lint)"
src="https://github.com/python-pillow/Pillow/workflows/Lint/badge.svg"></a>
<a href="https://github.com/python-pillow/Pillow/actions?query=workflow%3ATest"><img
<a href="https://github.com/python-pillow/Pillow/actions/workflows/test.yml"><img
alt="GitHub Actions build status (Test Linux and macOS)"
src="https://github.com/python-pillow/Pillow/workflows/Test/badge.svg"></a>
<a href="https://github.com/python-pillow/Pillow/actions?query=workflow%3A%22Test+Windows%22"><img
<a href="https://github.com/python-pillow/Pillow/actions/workflows/test-windows.yml"><img
alt="GitHub Actions build status (Test Windows)"
src="https://github.com/python-pillow/Pillow/workflows/Test%20Windows/badge.svg"></a>
<a href="https://github.com/python-pillow/Pillow/actions?query=workflow%3A%22Test+Docker%22"><img
<a href="https://github.com/python-pillow/Pillow/actions/workflows/test-mingw.yml"><img
alt="GitHub Actions build status (Test MinGW)"
src="https://github.com/python-pillow/Pillow/workflows/Test%20MinGW/badge.svg"></a>
<a href="https://github.com/python-pillow/Pillow/actions/workflows/test-docker.yml"><img
alt="GitHub Actions build status (Test Docker)"
src="https://github.com/python-pillow/Pillow/workflows/Test%20Docker/badge.svg"></a>
<a href="https://ci.appveyor.com/project/python-pillow/Pillow"><img
@ -42,10 +45,10 @@ As of 2019, Pillow development is
<a href="https://github.com/python-pillow/pillow-wheels/actions"><img
alt="GitHub Actions wheels build status (Wheels)"
src="https://github.com/python-pillow/pillow-wheels/workflows/Wheels/badge.svg"></a>
<a href="https://travis-ci.com/github/python-pillow/pillow-wheels"><img
<a href="https://app.travis-ci.com/github/python-pillow/pillow-wheels"><img
alt="Travis CI wheels build status (aarch64)"
src="https://img.shields.io/travis/com/python-pillow/pillow-wheels/main.svg?label=aarch64%20wheels"></a>
<a href="https://codecov.io/gh/python-pillow/Pillow"><img
<a href="https://app.codecov.io/gh/python-pillow/Pillow"><img
alt="Code coverage"
src="https://codecov.io/gh/python-pillow/Pillow/branch/main/graph/badge.svg"></a>
<a href="https://github.com/python-pillow/Pillow/actions/workflows/tidelift.yml"><img

View File

@ -86,21 +86,12 @@ class TestDecompressionCrop:
pytest.warns(Image.DecompressionBombWarning, src.crop, box)
def test_crop_decompression_checks(self):
im = Image.new("RGB", (100, 100))
good_values = ((-9999, -9999, -9990, -9990), (-999, -999, -990, -990))
warning_values = ((-160, -160, 99, 99), (160, 160, -99, -99))
error_values = ((-99909, -99990, 99999, 99999), (99909, 99990, -99999, -99999))
for value in good_values:
for value in ((-9999, -9999, -9990, -9990), (-999, -999, -990, -990)):
assert im.crop(value).size == (9, 9)
for value in warning_values:
pytest.warns(Image.DecompressionBombWarning, im.crop, value)
pytest.warns(Image.DecompressionBombWarning, im.crop, (-160, -160, 99, 99))
for value in error_values:
with pytest.raises(Image.DecompressionBombError):
im.crop(value)
with pytest.raises(Image.DecompressionBombError):
im.crop((-99909, -99990, 99999, 99999))

View File

@ -58,6 +58,15 @@ def test_sanity():
assert image2_scale2.format == "EPS"
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
def test_load():
with Image.open(FILE1) as im:
assert im.load()[0, 0] == (255, 255, 255)
# Test again now that it has already been loaded once
assert im.load()[0, 0] == (255, 255, 255)
def test_invalid_file():
invalid_file = "Tests/images/flower.jpg"

View File

@ -5,20 +5,28 @@ from PIL import GbrImagePlugin, Image
from .helper import assert_image_equal_tofile
def test_invalid_file():
invalid_file = "Tests/images/flower.jpg"
with pytest.raises(SyntaxError):
GbrImagePlugin.GbrImageFile(invalid_file)
def test_gbr_file():
with Image.open("Tests/images/gbr.gbr") as im:
assert_image_equal_tofile(im, "Tests/images/gbr.png")
def test_load():
with Image.open("Tests/images/gbr.gbr") as im:
assert im.load()[0, 0] == (0, 0, 0, 0)
# Test again now that it has already been loaded once
assert im.load()[0, 0] == (0, 0, 0, 0)
def test_multiple_load_operations():
with Image.open("Tests/images/gbr.gbr") as im:
im.load()
im.load()
assert_image_equal_tofile(im, "Tests/images/gbr.png")
def test_invalid_file():
invalid_file = "Tests/images/flower.jpg"
with pytest.raises(SyntaxError):
GbrImagePlugin.GbrImageFile(invalid_file)

View File

@ -28,6 +28,14 @@ def test_sanity():
assert im.format == "ICNS"
def test_load():
with Image.open(TEST_FILE) as im:
assert im.load()[0, 0] == (0, 0, 0, 0)
# Test again now that it has already been loaded once
assert im.load()[0, 0] == (0, 0, 0, 0)
def test_save(tmp_path):
temp_file = str(tmp_path / "temp.icns")

View File

@ -18,6 +18,11 @@ def test_sanity():
assert im.get_format_mimetype() == "image/x-icon"
def test_load():
with Image.open(TEST_ICO_FILE) as im:
assert im.load()[0, 0] == (1, 1, 9, 255)
def test_mask():
with Image.open("Tests/images/hopper_mask.ico") as im:
assert_image_equal_tofile(im, "Tests/images/hopper_mask.png")

View File

@ -123,7 +123,7 @@ def test_no_icc_profile():
def test_combined_larger_than_size():
# The 'combined' sizes of the individual parts is larger than the
# The combined size of the individual parts is larger than the
# declared 'size' of the extra data field, resulting in a backwards seek.
# If we instead take the 'size' of the extra data field as the source of truth,

View File

@ -2,15 +2,11 @@ from PIL import WalImageFile
from .helper import assert_image_equal_tofile
TEST_FILE = "Tests/images/hopper.wal"
def test_open():
# Arrange
TEST_FILE = "Tests/images/hopper.wal"
# Act
with WalImageFile.open(TEST_FILE) as im:
# Assert
assert im.format == "WAL"
assert im.format_description == "Quake2 Texture"
assert im.mode == "P"
@ -19,3 +15,11 @@ def test_open():
assert isinstance(im, WalImageFile.WalImageFile)
assert_image_equal_tofile(im, "Tests/images/hopper_wal.png")
def test_load():
with WalImageFile.open(TEST_FILE) as im:
assert im.load()[0, 0] == 122
# Test again now that it has already been loaded once
assert im.load()[0, 0] == 122

View File

@ -24,6 +24,12 @@ def test_load_raw():
assert_image_similar_tofile(im, "Tests/images/drawing_wmf_ref.png", 2.0)
def test_load():
with Image.open("Tests/images/drawing.emf") as im:
if hasattr(Image.core, "drawwmf"):
assert im.load()[0, 0] == (255, 255, 255)
def test_register_handler(tmp_path):
class TestHandler:
methodCalled = False

View File

@ -89,6 +89,17 @@ class TestImage:
# with pytest.raises(MemoryError):
# Image.new("L", (1000000, 1000000))
def test_repr_pretty(self):
class Pretty:
def text(self, text):
self.pretty_output = text
im = Image.new("L", (100, 100))
p = Pretty()
im._repr_pretty_(p, None)
assert p.pretty_output == "<PIL.Image.Image image mode=L size=100x100>"
def test_open_formats(self):
PNGFILE = "Tests/images/hopper.png"
JPGFILE = "Tests/images/hopper.jpg"

View File

@ -47,16 +47,12 @@ def test_wide_crop():
assert crop(-25, 75, 25, 125) == (1875, 625)
def test_negative_crop():
# Check negative crop size (@PIL171)
@pytest.mark.parametrize("box", ((8, 2, 2, 8), (2, 8, 8, 2), (8, 8, 2, 2)))
def test_negative_crop(box):
im = Image.new("RGB", (10, 10))
im = Image.new("L", (512, 512))
im = im.crop((400, 400, 200, 200))
assert im.size == (0, 0)
assert len(im.getdata()) == 0
with pytest.raises(IndexError):
im.getdata()[0]
with pytest.raises(ValueError):
im.crop(box)
def test_crop_float():

View File

@ -1024,6 +1024,19 @@ def test_oom(test_file):
font.getmask("Test Text")
def test_raqm_missing_warning(monkeypatch):
monkeypatch.setattr(ImageFont.core, "HAVE_RAQM", False)
with pytest.warns(UserWarning) as record:
font = ImageFont.truetype(
FONT_PATH, FONT_SIZE, layout_engine=ImageFont.Layout.RAQM
)
assert font.layout_engine == ImageFont.Layout.BASIC
assert str(record[-1].message) == (
"Raqm layout was requested, but Raqm is not available. "
"Falling back to basic layout."
)
def test_constants_deprecation():
for enum, prefix in {
ImageFont.Layout: "LAYOUT_",

View File

@ -52,9 +52,17 @@ def test_ops():
assert pixel(ImageMath.eval("float(B)**33", images)) == "F 8589934592.0"
def test_prevent_exec():
@pytest.mark.parametrize(
"expression",
(
"exec('pass')",
"(lambda: exec('pass'))()",
"(lambda: (lambda: exec('pass'))())()",
),
)
def test_prevent_exec(expression):
with pytest.raises(ValueError):
ImageMath.eval("exec('pass')")
ImageMath.eval(expression)
def test_logical():

View File

@ -79,3 +79,20 @@ def test_ipythonviewer():
im = hopper()
assert test_viewer.show(im) == 1
@pytest.mark.skipif(
not on_ci() or is_win32(),
reason="Only run on CIs; hangs on Windows CIs",
)
def test_file_deprecated(tmp_path):
f = str(tmp_path / "temp.jpg")
for viewer in ImageShow._viewers:
hopper().save(f)
with pytest.warns(DeprecationWarning):
try:
viewer.show_file(file=f)
except NotImplementedError:
pass
with pytest.raises(TypeError):
viewer.show_file()

View File

@ -189,8 +189,9 @@ def test_putdata():
assert len(im.getdata()) == len(arr)
def test_roundtrip_eye():
for dtype in (
@pytest.mark.parametrize(
"dtype",
(
bool,
numpy.bool8,
numpy.int8,
@ -202,9 +203,11 @@ def test_roundtrip_eye():
float,
numpy.float32,
numpy.float64,
):
arr = numpy.eye(10, dtype=dtype)
numpy.testing.assert_array_equal(arr, numpy.array(Image.fromarray(arr)))
),
)
def test_roundtrip_eye(dtype):
arr = numpy.eye(10, dtype=dtype)
numpy.testing.assert_array_equal(arr, numpy.array(Image.fromarray(arr)))
def test_zero_size():

View File

@ -1,14 +1,15 @@
#!/bin/bash
# install libimagequant
archive=libimagequant-2.17.0
archive=libimagequant-4.0.0
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz
pushd $archive
pushd $archive/imagequant-sys
make shared
sudo cp libimagequant.so* /usr/lib/
sudo cp libimagequant.h /usr/include/
cargo install cargo-c
cargo cinstall --prefix=/usr --destdir=.
sudo cp usr/lib/libimagequant.so* /usr/lib/
sudo cp usr/include/libimagequant.h /usr/include/
popd

View File

@ -2,13 +2,13 @@
# install raqm
archive=raqm-0.7.1
archive=libraqm-0.9.0
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz
pushd $archive
./configure --prefix=/usr && make -j4 && sudo make -j4 install
meson build --prefix=/usr && sudo ninja -C build install
popd

View File

@ -1,7 +1,7 @@
#!/bin/bash
# install webp
archive=libwebp-1.2.1
archive=libwebp-1.2.2
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz

View File

@ -12,7 +12,7 @@ The fork author's goal is to foster and support active development of PIL throug
.. _GitHub Actions: https://github.com/python-pillow/Pillow/actions
.. _AppVeyor: https://ci.appveyor.com/project/Python-pillow/pillow
.. _Travis CI: https://travis-ci.com/github/python-pillow/pillow-wheels
.. _Travis CI: https://app.travis-ci.com/github/python-pillow/pillow-wheels
.. _GitHub: https://github.com/python-pillow/Pillow
.. _Python Package Index: https://pypi.org/project/Pillow/

View File

@ -53,6 +53,19 @@ Before Pillow 8.3.0, ``ImagePalette`` required palette data of particular length
default, and the size parameter could be used to override that. Pillow 8.3.0 removed
the default required length, also removing the need for the size parameter.
ImageShow.Viewer.show_file file argument
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 9.1.0
The ``file`` argument in :py:meth:`~PIL.ImageShow.Viewer.show_file()` has been
deprecated and will be removed in Pillow 10.0.0 (2023-07-01). It has been replaced by
``path``.
In effect, ``viewer.show_file("test.jpg")`` will continue to work unchanged.
``viewer.show_file(file="test.jpg")`` will raise a deprecation warning, and suggest
``viewer.show_file(path="test.jpg")`` instead.
Removed features
----------------

View File

@ -10,21 +10,25 @@ Pillow for enterprise is available via the Tidelift Subscription. `Learn more <h
:alt: Documentation Status
.. image:: https://github.com/python-pillow/Pillow/workflows/Lint/badge.svg
:target: https://github.com/python-pillow/Pillow/actions?query=workflow%3ALint
:target: https://github.com/python-pillow/Pillow/actions/workflows/lint.yml
:alt: GitHub Actions build status (Lint)
.. image:: https://github.com/python-pillow/Pillow/workflows/Test%20Docker/badge.svg
:target: https://github.com/python-pillow/Pillow/actions?query=workflow%3A%22Test+Docker%22
:target: https://github.com/python-pillow/Pillow/actions/workflows/test-docker.yml
:alt: GitHub Actions build status (Test Docker)
.. image:: https://github.com/python-pillow/Pillow/workflows/Test/badge.svg
:target: https://github.com/python-pillow/Pillow/actions?query=workflow%3ATest
:target: https://github.com/python-pillow/Pillow/actions/workflows/test.yml
:alt: GitHub Actions build status (Test Linux and macOS)
.. image:: https://github.com/python-pillow/Pillow/workflows/Test%20Windows/badge.svg
:target: https://github.com/python-pillow/Pillow/actions?query=workflow%3A%22Test+Windows%22
:target: https://github.com/python-pillow/Pillow/actions/workflows/test-windows.yml
:alt: GitHub Actions build status (Test Windows)
.. image:: https://github.com/python-pillow/Pillow/workflows/Test%20MinGW/badge.svg
:target: https://github.com/python-pillow/Pillow/actions/workflows/test-mingw.yml
:alt: GitHub Actions build status (Test MinGW)
.. image:: https://img.shields.io/appveyor/build/python-pillow/Pillow/main.svg?label=Windows%20build
:target: https://ci.appveyor.com/project/python-pillow/Pillow
:alt: AppVeyor CI build status (Windows)
@ -34,11 +38,11 @@ Pillow for enterprise is available via the Tidelift Subscription. `Learn more <h
:alt: GitHub Actions wheels build status (Wheels)
.. image:: https://img.shields.io/travis/com/python-pillow/pillow-wheels/main.svg?label=aarch64%20wheels
:target: https://travis-ci.com/github/python-pillow/pillow-wheels
:target: https://app.travis-ci.com/github/python-pillow/pillow-wheels
:alt: Travis CI wheels build status (aarch64)
.. image:: https://codecov.io/gh/python-pillow/Pillow/branch/main/graph/badge.svg
:target: https://codecov.io/gh/python-pillow/Pillow
:target: https://app.codecov.io/gh/python-pillow/Pillow
:alt: Code coverage
.. image:: https://zenodo.org/badge/17549/python-pillow/Pillow.svg

View File

@ -169,7 +169,7 @@ Many of Pillow's features require external libraries:
* **littlecms** provides color management
* 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.12**.
above uses liblcms2. Tested with **1.19** and **2.7-2.13.1**.
* **libwebp** provides the WebP format.
@ -187,7 +187,7 @@ Many of Pillow's features require external libraries:
* **libimagequant** provides improved color quantization
* Pillow has been tested with libimagequant **2.6-2.17.0**
* Pillow has been tested with libimagequant **2.6-4.0**
* Libimagequant is licensed GPLv3, which is more restrictive than
the Pillow license, therefore we will not be distributing binaries
with libimagequant support enabled.
@ -394,7 +394,8 @@ Prerequisites for **Ubuntu 16.04 LTS - 20.04 LTS** are installed with::
libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python3-tk \
libharfbuzz-dev libfribidi-dev libxcb1-dev
Then see ``depends/install_raqm.sh`` to install libraqm.
To install libraqm, ``sudo apt-get install meson`` and then see
``depends/install_raqm.sh``.
Prerequisites are installed on recent **Red Hat**, **CentOS** or **Fedora** with::
@ -452,12 +453,14 @@ These platforms are built and tested for every change.
+----------------------------------+----------------------------+---------------------+
| CentOS 7 | 3.9 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| CentOS 8 | 3.9 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| CentOS Stream 8 | 3.9 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| CentOS Stream 9 | 3.9 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| Debian 10 Buster | 3.7 | x86 |
+----------------------------------+----------------------------+---------------------+
| Debian 11 Bullseye | 3.9 | x86 |
+----------------------------------+----------------------------+---------------------+
| Fedora 34 | 3.9 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| Fedora 35 | 3.10 | x86-64 |
@ -493,9 +496,13 @@ These platforms have been reported to work at the versions mentioned.
| Operating system | | Tested Python | | Latest tested | | Tested |
| | | versions | | Pillow version | | processors |
+==================================+===========================+==================+==============+
| macOS 11.0 Big Sur | 3.7, 3.8, 3.9, 3.10 | 8.4.0 |arm |
| macOS 12 Big Sur | 3.7, 3.8, 3.9, 3.10 | 9.0.1 |arm |
+----------------------------------+---------------------------+------------------+--------------+
| macOS 11 Big Sur | 3.7, 3.8, 3.9, 3.10 | 8.4.0 |arm |
| +---------------------------+------------------+--------------+
| | 3.6, 3.7, 3.8, 3.9, 3.10 | 8.4.0 |x86-64 |
| | 3.7, 3.8, 3.9, 3.10 | 9.0.1 |x86-64 |
| +---------------------------+------------------+--------------+
| | 3.6 | 8.4.0 |x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| macOS 10.15 Catalina | 3.6, 3.7, 3.8, 3.9 | 8.3.2 |x86-64 |
| +---------------------------+------------------+ |
@ -523,6 +530,8 @@ These platforms have been reported to work at the versions mentioned.
+----------------------------------+---------------------------+------------------+--------------+
| CentOS 6.3 | 2.7, 3.3 | |x86 |
+----------------------------------+---------------------------+------------------+--------------+
| CentOS 8 | 3.9 | 9.0.0 |x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| Fedora 23 | 2.7, 3.4 | 3.1.0 |x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| Ubuntu Linux 12.04 LTS (Precise) | | 2.6, 3.2, 3.3, 3.4, 3.5 | 3.4.1 |x86,x86-64 |

View File

@ -0,0 +1,23 @@
9.0.1
-----
Security
========
This release addresses several security problems.
:cve:`CVE-2022-24303`: If the path to the temporary directory on Linux or macOS
contained a space, this would break removal of the temporary image file after
``im.show()`` (and related actions), and potentially remove an unrelated file. This
has been present since PIL.
:cve:`CVE-2022-22817`: While Pillow 9.0 restricted top-level builtins available to
:py:meth:`PIL.ImageMath.eval`, it did not prevent builtins available to lambda
expressions. These are now also restricted.
Other Changes
=============
Pillow 9.0 added support for ``xdg-open`` as an image viewer, but there have been
reports that the temporary image file was removed too quickly to be loaded into the
final application. A delay has been added.

View File

@ -14,6 +14,7 @@ expected to be backported to earlier versions.
.. toctree::
:maxdepth: 2
9.0.1
9.0.0
8.4.0
8.3.2

View File

@ -329,12 +329,12 @@ class EpsImageFile(ImageFile.ImageFile):
def load(self, scale=1, transparency=False):
# Load EPS via Ghostscript
if not self.tile:
return
self.im = Ghostscript(self.tile, self.size, self.fp, scale, transparency)
self.mode = self.im.mode
self._size = self.im.size
self.tile = []
if self.tile:
self.im = Ghostscript(self.tile, self.size, self.fp, scale, transparency)
self.mode = self.im.mode
self._size = self.im.size
self.tile = []
return Image.Image.load(self)
def load_seek(self, *args, **kwargs):
# we can't incrementally load, so force ImageFile.parser to

View File

@ -84,12 +84,10 @@ class GbrImageFile(ImageFile.ImageFile):
self._data_size = width * height * color_depth
def load(self):
if self.im:
# Already loaded
return
self.im = Image.core.new(self.mode, self.size)
self.frombytes(self.fp.read(self._data_size))
if not self.im:
self.im = Image.core.new(self.mode, self.size)
self.frombytes(self.fp.read(self._data_size))
return Image.Image.load(self)
#

View File

@ -286,21 +286,22 @@ class IcnsImageFile(ImageFile.ImageFile):
self.best_size[1] * self.best_size[2],
)
Image.Image.load(self)
px = Image.Image.load(self)
if self.im and self.im.size == self.size:
# Already loaded
return
return px
self.load_prepare()
# This is likely NOT the best way to do it, but whatever.
im = self.icns.getimage(self.best_size)
# If this is a PNG or JPEG 2000, it won't be loaded yet
im.load()
px = im.load()
self.im = im.im
self.mode = im.mode
self.size = im.size
self.load_end()
return px
def _save(im, fp, filename):

View File

@ -306,7 +306,7 @@ class IcoImageFile(ImageFile.ImageFile):
def load(self):
if self.im and self.im.size == self.size:
# Already loaded
return
return Image.Image.load(self)
im = self.ico.getimage(self.size)
# if tile is PNG, it won't really be loaded yet
im.load()

View File

@ -702,6 +702,22 @@ class Image:
id(self),
)
def _repr_pretty_(self, p, cycle):
"""IPython plain text display support"""
# Same as __repr__ but without unpredicatable id(self),
# to keep Jupyter notebook `text/plain` output stable.
p.text(
"<%s.%s image mode=%s size=%dx%d>"
% (
self.__class__.__module__,
self.__class__.__name__,
self.mode,
self.size[0],
self.size[1],
)
)
def _repr_png_(self):
"""iPython display hook support
@ -1213,6 +1229,11 @@ class Image:
if box is None:
return self.copy()
if box[2] < box[0]:
raise ValueError("Coordinate 'right' is less than 'left'")
elif box[3] < box[1]:
raise ValueError("Coordinate 'lower' is less than 'upper'")
self.load()
return self._new(self._crop(self.im, box))

View File

@ -328,6 +328,7 @@ class StubImageFile(ImageFile):
# become the other object (!)
self.__class__ = image.__class__
self.__dict__ = image.__dict__
return image.load()
def _load(self):
"""(Hook) Find actual image loader."""

View File

@ -196,6 +196,12 @@ class FreeTypeFont:
if core.HAVE_RAQM:
layout_engine = Layout.RAQM
elif layout_engine == Layout.RAQM and not core.HAVE_RAQM:
import warnings
warnings.warn(
"Raqm layout was requested, but Raqm is not available. "
"Falling back to basic layout."
)
layout_engine = Layout.BASIC
self.layout_engine = layout_engine

View File

@ -240,11 +240,18 @@ def eval(expression, _dict={}, **kw):
if hasattr(v, "im"):
args[k] = _Operand(v)
code = compile(expression, "<string>", "eval")
for name in code.co_names:
if name not in args and name != "abs":
raise ValueError(f"'{name}' not allowed")
compiled_code = compile(expression, "<string>", "eval")
def scan(code):
for const in code.co_consts:
if type(const) == type(compiled_code):
scan(const)
for name in code.co_names:
if name not in args and name != "abs":
raise ValueError(f"'{name}' not allowed")
scan(compiled_code)
out = builtins.eval(expression, {"__builtins": {"abs": abs}}, args)
try:
return out.im

View File

@ -15,7 +15,7 @@ import os
import shutil
import subprocess
import sys
import tempfile
import warnings
from shlex import quote
from PIL import Image
@ -105,11 +105,37 @@ class Viewer:
"""Display the given image."""
return self.show_file(self.save_image(image), **options)
def show_file(self, file, **options):
"""Display the given file."""
os.system(self.get_command(file, **options))
def show_file(self, path=None, **options):
"""
Display given file.
Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
and will be removed in Pillow 10.0.0 (2023-07-01). ``path`` should be used
instead.
"""
if path is None:
if "file" in options:
warnings.warn(
"The 'file' argument is deprecated and will be removed in Pillow "
"10 (2023-07-01). Use 'path' instead.",
DeprecationWarning,
)
path = options.pop("file")
else:
raise TypeError("Missing required argument: 'path'")
os.system(self.get_command(path, **options))
return 1
def _remove_path_after_delay(self, path):
subprocess.Popen(
[
sys.executable,
"-c",
"import os, sys, time; time.sleep(20); os.remove(sys.argv[1])",
path,
]
)
# --------------------------------------------------------------------
@ -145,18 +171,26 @@ class MacViewer(Viewer):
command = f"({command} {quote(file)}; sleep 20; rm -f {quote(file)})&"
return command
def show_file(self, file, **options):
"""Display given file"""
fd, path = tempfile.mkstemp()
with os.fdopen(fd, "w") as f:
f.write(file)
with open(path) as f:
subprocess.Popen(
["im=$(cat); open -a Preview.app $im; sleep 20; rm -f $im"],
shell=True,
stdin=f,
)
os.remove(path)
def show_file(self, path=None, **options):
"""
Display given file.
Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
and will be removed in Pillow 10.0.0 (2023-07-01). ``path`` should be used
instead.
"""
if path is None:
if "file" in options:
warnings.warn(
"The 'file' argument is deprecated and will be removed in Pillow "
"10 (2023-07-01). Use 'path' instead.",
DeprecationWarning,
)
path = options.pop("file")
else:
raise TypeError("Missing required argument: 'path'")
subprocess.call(["open", "-a", "Preview.app", path])
self._remove_path_after_delay(path)
return 1
@ -172,19 +206,6 @@ class UnixViewer(Viewer):
command = self.get_command_ex(file, **options)[0]
return f"({command} {quote(file)}; rm -f {quote(file)})&"
def show_file(self, file, **options):
"""Display given file"""
fd, path = tempfile.mkstemp()
with os.fdopen(fd, "w") as f:
f.write(file)
with open(path) as f:
command = self.get_command_ex(file, **options)[0]
subprocess.Popen(
["im=$(cat);" + command + " $im; rm -f $im"], shell=True, stdin=f
)
os.remove(path)
return 1
class XDGViewer(UnixViewer):
"""
@ -195,6 +216,28 @@ class XDGViewer(UnixViewer):
command = executable = "xdg-open"
return command, executable
def show_file(self, path=None, **options):
"""
Display given file.
Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
and will be removed in Pillow 10.0.0 (2023-07-01). ``path`` should be used
instead.
"""
if path is None:
if "file" in options:
warnings.warn(
"The 'file' argument is deprecated and will be removed in Pillow "
"10 (2023-07-01). Use 'path' instead.",
DeprecationWarning,
)
path = options.pop("file")
else:
raise TypeError("Missing required argument: 'path'")
subprocess.Popen(["xdg-open", path])
self._remove_path_after_delay(path)
return 1
class DisplayViewer(UnixViewer):
"""
@ -208,6 +251,32 @@ class DisplayViewer(UnixViewer):
command += f" -name {quote(title)}"
return command, executable
def show_file(self, path=None, **options):
"""
Display given file.
Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
and ``path`` should be used instead.
"""
if path is None:
if "file" in options:
warnings.warn(
"The 'file' argument is deprecated and will be removed in Pillow "
"10 (2023-07-01). Use 'path' instead.",
DeprecationWarning,
)
path = options.pop("file")
else:
raise TypeError("Missing required argument: 'path'")
args = ["display"]
if "title" in options:
args += ["-name", options["title"]]
args.append(path)
subprocess.Popen(args)
os.remove(path)
return 1
class GmDisplayViewer(UnixViewer):
"""The GraphicsMagick ``gm display`` command."""
@ -217,6 +286,27 @@ class GmDisplayViewer(UnixViewer):
command = "gm display"
return command, executable
def show_file(self, path=None, **options):
"""
Display given file.
Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
and ``path`` should be used instead.
"""
if path is None:
if "file" in options:
warnings.warn(
"The 'file' argument is deprecated and will be removed in Pillow "
"10 (2023-07-01). Use 'path' instead.",
DeprecationWarning,
)
path = options.pop("file")
else:
raise TypeError("Missing required argument: 'path'")
subprocess.Popen(["gm", "display", path])
os.remove(path)
return 1
class EogViewer(UnixViewer):
"""The GNOME Image Viewer ``eog`` command."""
@ -226,6 +316,27 @@ class EogViewer(UnixViewer):
command = "eog -n"
return command, executable
def show_file(self, path=None, **options):
"""
Display given file.
Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
and ``path`` should be used instead.
"""
if path is None:
if "file" in options:
warnings.warn(
"The 'file' argument is deprecated and will be removed in Pillow "
"10 (2023-07-01). Use 'path' instead.",
DeprecationWarning,
)
path = options.pop("file")
else:
raise TypeError("Missing required argument: 'path'")
subprocess.Popen(["eog", "-n", path])
os.remove(path)
return 1
class XVViewer(UnixViewer):
"""
@ -241,6 +352,32 @@ class XVViewer(UnixViewer):
command += f" -name {quote(title)}"
return command, executable
def show_file(self, path=None, **options):
"""
Display given file.
Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
and ``path`` should be used instead.
"""
if path is None:
if "file" in options:
warnings.warn(
"The 'file' argument is deprecated and will be removed in Pillow "
"10 (2023-07-01). Use 'path' instead.",
DeprecationWarning,
)
path = options.pop("file")
else:
raise TypeError("Missing required argument: 'path'")
args = ["xv"]
if "title" in options:
args += ["-name", options["title"]]
args.append(path)
subprocess.Popen(args)
os.remove(path)
return 1
if sys.platform not in ("win32", "darwin"): # unixoids
if shutil.which("xdg-open"):

View File

@ -482,6 +482,7 @@ class JpegImageFile(ImageFile.ImageFile):
"""
Returns a dictionary containing the XMP tags.
Requires defusedxml to be installed.
:returns: XMP tags in a dictionary.
"""

View File

@ -1009,6 +1009,7 @@ class PngImageFile(ImageFile.ImageFile):
"""
Returns a dictionary containing the XMP tags.
Requires defusedxml to be installed.
:returns: XMP tags in a dictionary.
"""
return (

View File

@ -195,7 +195,6 @@ def _layerinfo(fp, ct_bytes):
x1 = i32(read(4))
# image info
info = []
mode = []
ct_types = i16(read(2))
types = list(range(ct_types))
@ -211,8 +210,7 @@ def _layerinfo(fp, ct_bytes):
m = "RGBA"[type]
mode.append(m)
size = i32(read(4))
info.append((m, size))
read(4) # size
# figure out the image mode
mode.sort()
@ -229,26 +227,22 @@ def _layerinfo(fp, ct_bytes):
read(12) # filler
name = ""
size = i32(read(4)) # length of the extra data field
combined = 0
if size:
data_end = fp.tell() + size
length = i32(read(4))
if length:
fp.seek(length - 16, io.SEEK_CUR)
combined += length + 4
length = i32(read(4))
if length:
fp.seek(length, io.SEEK_CUR)
combined += length + 4
length = i8(read(1))
if length:
# Don't know the proper encoding,
# Latin-1 should be a good guess
name = read(length).decode("latin-1", "replace")
combined += length + 1
fp.seek(data_end)
layers.append((name, mode, (x0, y0, x1, y1)))

View File

@ -238,17 +238,18 @@ def makeSpiderHeader(im):
if 1024 % lenbyt != 0:
labrec += 1
labbyt = labrec * lenbyt
hdr = []
nvalues = int(labbyt / 4)
if nvalues < 23:
return []
hdr = []
for i in range(nvalues):
hdr.append(0.0)
if len(hdr) < 23:
return []
# NB these are Fortran indices
hdr[1] = 1.0 # nslice (=1 for an image)
hdr[2] = float(nrow) # number of rows per slice
hdr[3] = float(nrow) # number of records in the image
hdr[5] = 1.0 # iform for 2D image
hdr[12] = float(nsam) # number of pixels per line
hdr[13] = float(labrec) # number of records in file header
@ -259,10 +260,7 @@ def makeSpiderHeader(im):
hdr = hdr[1:]
hdr.append(0.0)
# pack binary data into a string
hdrstr = []
for v in hdr:
hdrstr.append(struct.pack("f", v))
return hdrstr
return [struct.pack("f", v) for v in hdr]
def _save(im, fp, filename):

View File

@ -1124,6 +1124,7 @@ class TiffImageFile(ImageFile.ImageFile):
"""
Returns a dictionary containing the XMP tags.
Requires defusedxml to be installed.
:returns: XMP tags in a dictionary.
"""
return self._getxmp(self.tag_v2[700]) if 700 in self.tag_v2 else {}

View File

@ -51,14 +51,11 @@ class WalImageFile(ImageFile.ImageFile):
self.info["next_name"] = next_name
def load(self):
if self.im:
# Already loaded
return
self.im = Image.core.new(self.mode, self.size)
self.frombytes(self.fp.read(self.size[0] * self.size[1]))
self.putpalette(quake2palette)
Image.Image.load(self)
if not self.im:
self.im = Image.core.new(self.mode, self.size)
self.frombytes(self.fp.read(self.size[0] * self.size[1]))
self.putpalette(quake2palette)
return Image.Image.load(self)
def open(filename):

View File

@ -158,7 +158,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
(x1 - x0) * self.info["dpi"] // self._inch,
(y1 - y0) * self.info["dpi"] // self._inch,
)
super().load()
return super().load()
def _save(im, fp, filename):

View File

@ -29,7 +29,7 @@
* 1995-09-12 fl Created
* 1996-04-08 fl Ready for release
* 1997-05-09 fl Use command instead of image type
* 2001-03-18 fl Initialize alpha layer pointer (struct changed in 8.3)
* 2001-03-18 fl Initialize alpha layer pointer (struct changed in Tk 8.3)
* 2003-04-23 fl Fixed building for Tk 8.4.1 and later (Jack Jansen)
* 2004-06-24 fl Fixed building for Tk 8.4.6 and later.
*
@ -116,7 +116,7 @@ PyImagingPhotoPut(
block.offset[1] = 1;
block.offset[2] = 2;
if (strcmp(im->mode, "RGBA") == 0) {
block.offset[3] = 3; /* alpha (or reserved, under 8.2) */
block.offset[3] = 3; /* alpha (or reserved, under Tk 8.2) */
} else {
block.offset[3] = 0; /* no alpha */
}

View File

@ -1529,6 +1529,7 @@ static struct {
{"RGB", "RGBX", 32, copy4},
{"RGB", "RGBX;L", 32, unpackRGBAL},
{"RGB", "RGBA;L", 32, unpackRGBAL},
{"RGB", "RGBA;15", 16, ImagingUnpackRGBA15},
{"RGB", "BGRX", 32, ImagingUnpackBGRX},
{"RGB", "XRGB", 32, ImagingUnpackXRGB},
{"RGB", "XBGR", 32, ImagingUnpackXBGR},

View File

@ -1,7 +1,7 @@
The MIT License (MIT)
Copyright © 2015 Information Technology Authority (ITA) <foss@ita.gov.om>
Copyright © 2016 Khaled Hosny <khaledhosny@eglug.org>
Copyright © 2016-2022 Khaled Hosny <khaled@aliftype.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,4 +1,18 @@
Overview of changes leading to 0.7.1
Overview of changes leading to 0.8.0
Monday, December 13, 2021
====================================
Remove autotools build.
Support using SheenBiDi instead of FriBiDi for Unicode BiDi support.
Fix running tests with Python <= 3.6.
New API:
* raqm_get_par_resolved_direction
* raqm_get_direction_at_index
Overview of changes leading to 0.7.2
Monday, September 27, 2021
====================================

View File

@ -6,26 +6,26 @@ Raqm
Raqm is a small library that encapsulates the logic for complex text layout and
provides a convenient API.
It currently provides bidirectional text support (using [FriBiDi][1]), shaping
(using [HarfBuzz][2]), and proper script itemization. As a result,
Raqm can support most writing systems covered by Unicode.
It currently provides bidirectional text support (using [FriBiDi][1] or
[SheenBidi][2]), shaping (using [HarfBuzz][3]), and proper script itemization.
As a result, Raqm can support most writing systems covered by Unicode.
The documentation can be accessed on the web at:
> http://host-oman.github.io/libraqm/
Raqm (Arabic: رَقْم) is writing, also number or digit and the Arabic word for
digital (رَقَمِيّ) shares the same root, so it is a play on “digital writing”.
digital (رَقَمِيّ) shares the same root, so it is a play on “digital writing”.
Building
--------
Raqm depends on the following libraries:
* [FreeType][3]
* [HarfBuzz][2]
* [FriBiDi][1]
* [FreeType][4]
* [HarfBuzz][3]
* [FriBiDi][1] or [SheenBidi][2]
To build the documentation you will also need:
* [GTK-Doc][4]
* [GTK-Doc][5]
To install dependencies on Fedora:
@ -48,11 +48,11 @@ directory:
$ ninja -C build
$ ninja -C build install
To build the documentation, pass `-Ddocs=enable` to the `meson`.
To build the documentation, pass `-Ddocs=true` to the `meson`.
To run the tests:
$ ninja -C test
$ ninja -C build test
Contributing
------------
@ -68,6 +68,7 @@ Projects using Raqm
3. [FontView](https://github.com/googlei18n/fontview)
4. [Pillow](https://github.com/python-pillow)
5. [mplcairo](https://github.com/anntzer/mplcairo)
6. [CEGUI](https://github.com/cegui/cegui)
The following projects have patches to support complex text layout using Raqm:
@ -77,7 +78,8 @@ The following projects have patches to support complex text layout using Raqm:
[1]: http://fribidi.org
[2]: http://harfbuzz.org
[3]: https://www.freetype.org
[4]: https://www.gtk.org/gtk-doc
[1]: https://github.com/fribidi/fribidi
[2]: https://github.com/Tehreer/SheenBidi
[3]: https://github.com/harfbuzz/harfbuzz
[4]: https://www.freetype.org
[5]: https://www.gtk.org/gtk-doc

View File

@ -32,10 +32,10 @@
#define _RAQM_VERSION_H_
#define RAQM_VERSION_MAJOR 0
#define RAQM_VERSION_MINOR 7
#define RAQM_VERSION_MICRO 2
#define RAQM_VERSION_MINOR 9
#define RAQM_VERSION_MICRO 0
#define RAQM_VERSION_STRING "0.7.2"
#define RAQM_VERSION_STRING "0.9.0"
#define RAQM_VERSION_ATLEAST(major,minor,micro) \
((major)*10000+(minor)*100+(micro) <= \

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/*
* Copyright © 2015 Information Technology Authority (ITA) <foss@ita.gov.om>
* Copyright © 2016 Khaled Hosny <khaledhosny@eglug.org>
* Copyright © 2016-2022 Khaled Hosny <khaled@aliftype.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@ -106,6 +106,9 @@ raqm_reference (raqm_t *rq);
RAQM_API void
raqm_destroy (raqm_t *rq);
RAQM_API void
raqm_clear_contents (raqm_t *rq);
RAQM_API bool
raqm_set_text (raqm_t *rq,
const uint32_t *text,
@ -145,6 +148,12 @@ RAQM_API bool
raqm_set_freetype_load_flags (raqm_t *rq,
int flags);
RAQM_API bool
raqm_set_freetype_load_flags_range (raqm_t *rq,
int flags,
size_t start,
size_t len);
RAQM_API bool
raqm_set_invisible_glyph (raqm_t *rq,
int gid);
@ -156,6 +165,13 @@ RAQM_API raqm_glyph_t *
raqm_get_glyphs (raqm_t *rq,
size_t *length);
RAQM_API raqm_direction_t
raqm_get_par_resolved_direction (raqm_t *rq);
RAQM_API raqm_direction_t
raqm_get_direction_at_index (raqm_t *rq,
size_t index);
RAQM_API bool
raqm_index_to_position (raqm_t *rq,
size_t *index,

View File

@ -24,7 +24,7 @@ Download and install:
* `CMake 3.12 or newer <https://cmake.org/download/>`_
(also available as Visual Studio component C++ CMake tools for Windows)
* `NASM <https://www.nasm.us/pub/nasm/releasebuilds/?C=M;O=D>`_
* x86/x64: `NASM <https://www.nasm.us/pub/nasm/releasebuilds/?C=M;O=D>`_
Any version of Visual Studio 2017 or newer should be supported,
including Visual Studio 2017 Community, or Build Tools for Visual Studio 2019.
@ -42,8 +42,8 @@ behaviour of ``build_prepare.py``:
If ``PYTHON`` is unset, the version of Python used to run
``build_prepare.py`` will be used. If only ``PYTHON`` is set,
``EXECUTABLE`` defaults to ``python.exe``.
* ``ARCHITECTURE`` is used to select a ``x86`` or ``x64`` build. By default,
uses same architecture as the version of Python used to run ``build_prepare.py``.
* ``ARCHITECTURE`` is used to select a ``x86``, ``x64`` or ``ARM64``build.
By default, uses same architecture as the version of Python used to run ``build_prepare.py``.
is used.
* ``PILLOW_BUILD`` can be used to override the ``winbuild\build`` directory
path, used to store generated build scripts and compiled libraries.

View File

@ -1,4 +1,5 @@
import os
import platform
import shutil
import struct
import subprocess
@ -93,6 +94,7 @@ SF_MIRROR = "http://iweb.dl.sourceforge.net"
architectures = {
"x86": {"vcvars_arch": "x86", "msbuild_arch": "Win32"},
"x64": {"vcvars_arch": "x86_amd64", "msbuild_arch": "x64"},
"ARM64": {"vcvars_arch": "x86_arm64", "msbuild_arch": "ARM64"},
}
header = [
@ -154,9 +156,9 @@ deps = {
# "bins": [r"libtiff\*.dll"],
},
"libwebp": {
"url": "http://downloads.webmproject.org/releases/webp/libwebp-1.2.1.tar.gz",
"filename": "libwebp-1.2.1.tar.gz",
"dir": "libwebp-1.2.1",
"url": "http://downloads.webmproject.org/releases/webp/libwebp-1.2.2.tar.gz",
"filename": "libwebp-1.2.2.tar.gz",
"dir": "libwebp-1.2.2",
"build": [
cmd_rmdir(r"output\release-static"), # clean
cmd_nmake(
@ -219,25 +221,25 @@ deps = {
# "bins": [r"objs\{msbuild_arch}\Release\freetype.dll"],
},
"lcms2": {
"url": SF_MIRROR + "/project/lcms/lcms/2.12/lcms2-2.12.tar.gz",
"filename": "lcms2-2.12.tar.gz",
"dir": "lcms2-2.12",
"url": SF_MIRROR + "/project/lcms/lcms/2.13/lcms2-2.13.1.tar.gz",
"filename": "lcms2-2.13.1.tar.gz",
"dir": "lcms2-2.13.1",
"patch": {
r"Projects\VC2017\lcms2_static\lcms2_static.vcxproj": {
r"Projects\VC2019\lcms2_static\lcms2_static.vcxproj": {
# default is /MD for x86 and /MT for x64, we need /MD always
"<RuntimeLibrary>MultiThreaded</RuntimeLibrary>": "<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>", # noqa: E501
# retarget to default toolset (selected by vcvarsall.bat)
"<PlatformToolset>v141</PlatformToolset>": "<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>", # noqa: E501
"<PlatformToolset>v142</PlatformToolset>": "<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>", # noqa: E501
# retarget to latest (selected by vcvarsall.bat)
"<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>": "<WindowsTargetPlatformVersion>$(WindowsSDKVersion)</WindowsTargetPlatformVersion>", # noqa: E501
"<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>": "<WindowsTargetPlatformVersion>$(WindowsSDKVersion)</WindowsTargetPlatformVersion>", # noqa: E501
}
},
"build": [
cmd_rmdir("Lib"),
cmd_rmdir(r"Projects\VC2017\Release"),
cmd_msbuild(r"Projects\VC2017\lcms2.sln", "Release", "Clean"),
cmd_rmdir(r"Projects\VC2019\Release"),
cmd_msbuild(r"Projects\VC2019\lcms2.sln", "Release", "Clean"),
cmd_msbuild(
r"Projects\VC2017\lcms2.sln", "Release", "lcms2_static:Rebuild"
r"Projects\VC2019\lcms2.sln", "Release", "lcms2_static:Rebuild"
),
cmd_xcopy("include", "{inc_dir}"),
],
@ -278,9 +280,9 @@ deps = {
"libs": [r"imagequant.lib"],
},
"harfbuzz": {
"url": "https://github.com/harfbuzz/harfbuzz/archive/3.2.0.zip",
"filename": "harfbuzz-3.2.0.zip",
"dir": "harfbuzz-3.2.0",
"url": "https://github.com/harfbuzz/harfbuzz/archive/3.3.2.zip",
"filename": "harfbuzz-3.3.2.zip",
"dir": "harfbuzz-3.3.2",
"build": [
cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"),
cmd_nmake(target="clean"),
@ -490,7 +492,10 @@ if __name__ == "__main__":
python_dir = os.environ.get("PYTHON")
python_exe = os.environ.get("EXECUTABLE", "python.exe")
architecture = os.environ.get(
"ARCHITECTURE", "x86" if struct.calcsize("P") == 4 else "x64"
"ARCHITECTURE",
"ARM64"
if platform.machine() == "ARM64"
else ("x86" if struct.calcsize("P") == 4 else "x64"),
)
build_dir = os.environ.get("PILLOW_BUILD", os.path.join(winbuild_dir, "build"))
sources_dir = ""