mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-26 01:46:18 +03:00
Merge branch 'main' into enum
This commit is contained in:
commit
b38a67fa12
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
3
.github/workflows/test-docker.yml
vendored
3
.github/workflows/test-docker.yml
vendored
|
@ -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,
|
||||
|
|
2
.github/workflows/test-mingw.yml
vendored
2
.github/workflows/test-mingw.yml
vendored
|
@ -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:
|
||||
|
|
2
.github/workflows/test-windows.yml
vendored
2
.github/workflows/test-windows.yml
vendored
|
@ -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:
|
||||
|
|
27
CHANGES.rst
27
CHANGES.rst
|
@ -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)
|
||||
------------------
|
||||
|
||||
|
|
2
Makefile
2
Makefile
|
@ -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
|
||||
|
|
15
README.md
15
README.md
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -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_",
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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/
|
||||
|
||||
|
|
|
@ -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
|
||||
----------------
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 |
|
||||
|
|
23
docs/releasenotes/9.0.1.rst
Normal file
23
docs/releasenotes/9.0.1.rst
Normal 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.
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
#
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"):
|
||||
|
|
|
@ -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.
|
||||
"""
|
||||
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
|
|
@ -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},
|
||||
|
|
2
src/thirdparty/raqm/COPYING
vendored
2
src/thirdparty/raqm/COPYING
vendored
|
@ -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
|
||||
|
|
16
src/thirdparty/raqm/NEWS
vendored
16
src/thirdparty/raqm/NEWS
vendored
|
@ -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
|
||||
====================================
|
||||
|
||||
|
|
30
src/thirdparty/raqm/README.md
vendored
30
src/thirdparty/raqm/README.md
vendored
|
@ -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
|
||||
|
|
6
src/thirdparty/raqm/raqm-version.h
vendored
6
src/thirdparty/raqm/raqm-version.h
vendored
|
@ -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) <= \
|
||||
|
|
838
src/thirdparty/raqm/raqm.c
vendored
838
src/thirdparty/raqm/raqm.c
vendored
File diff suppressed because it is too large
Load Diff
18
src/thirdparty/raqm/raqm.h
vendored
18
src/thirdparty/raqm/raqm.h
vendored
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 = ""
|
||||
|
|
Loading…
Reference in New Issue
Block a user