Merge branch 'main' into version_bump

This commit is contained in:
Andrew Murray 2023-04-11 07:18:13 +10:00
commit 54fddf0181
67 changed files with 408 additions and 1297 deletions

View File

@ -13,7 +13,7 @@ environment:
- PYTHON: C:/Python311
ARCHITECTURE: x86
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
- PYTHON: C:/Python37-x64
- PYTHON: C:/Python38-x64
ARCHITECTURE: x64
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
@ -31,7 +31,7 @@ install:
- path c:\nasm-2.15.05;C:\Program Files\gs\gs10.00.0\bin;%PATH%
- cd c:\pillow\winbuild\
- ps: |
c:\python37\python.exe c:\pillow\winbuild\build_prepare.py -v --depends=C:\pillow-depends\
c:\python38\python.exe c:\pillow\winbuild\build_prepare.py -v --depends=C:\pillow-depends\
c:\pillow\winbuild\build\build_dep_all.cmd
$host.SetShouldExit(0)
- path C:\pillow\winbuild\build\bin;%PATH%

View File

@ -43,7 +43,7 @@ if [[ $(uname) != CYGWIN* ]]; then
# PyQt6 doesn't support PyPy3
if [[ $GHA_PYTHON_VERSION == 3.* ]]; then
sudo apt-get -qq install libegl1 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxkbcommon-x11-0
python3 -m pip install pyqt6
python3 -m pip install pyqt6 PyQt6-Qt6!=6.5.0
fi
# webp

View File

@ -67,7 +67,6 @@ jobs:
python3${{ matrix.python-minor-version }}-numpy
python3${{ matrix.python-minor-version }}-sip
python3${{ matrix.python-minor-version }}-tkinter
qt5-devel-tools
wget
xorg-server-extra
zlib-devel

View File

@ -24,7 +24,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12-dev"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12-dev"]
architecture: ["x86", "x64"]
include:
# PyPy 7.3.4+ only ships 64-bit binaries for Windows

View File

@ -36,10 +36,9 @@ jobs:
"3.10",
"3.9",
"3.8",
"3.7",
]
include:
- python-version: "3.7"
- python-version: "3.9"
PYTHONOPTIMIZE: 1
REVERSE: "--reverse"
- python-version: "3.8"

View File

@ -1,9 +1,9 @@
repos:
- repo: https://github.com/psf/black
rev: 23.1.0
rev: 23.3.0
hooks:
- id: black
args: [--target-version=py37]
args: [--target-version=py38]
# Only .py files, until https://github.com/psf/black/issues/402 resolved
files: \.py$
types: []
@ -14,7 +14,7 @@ repos:
- id: isort
- repo: https://github.com/PyCQA/bandit
rev: 1.7.4
rev: 1.7.5
hooks:
- id: bandit
args: [--severity-level=high]
@ -26,10 +26,10 @@ repos:
- id: yesqa
- repo: https://github.com/Lucas-C/pre-commit-hooks
rev: v1.4.2
rev: v1.5.1
hooks:
- id: remove-tabs
exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.opt$)
exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.gd$|\.opt$)
- repo: https://github.com/PyCQA/flake8
rev: 6.0.0
@ -57,7 +57,7 @@ repos:
- id: sphinx-lint
- repo: https://github.com/tox-dev/tox-ini-fmt
rev: 0.6.1
rev: 1.0.0
hooks:
- id: tox-ini-fmt

View File

@ -1,5 +1,10 @@
version: 2
build:
os: ubuntu-22.04
tools:
python: "3.11"
python:
install:
- method: pip

View File

@ -2,6 +2,15 @@
Changelog (Pillow)
==================
10.0.0 (unreleased)
-------------------
- Remove deprecations for Pillow 10.0.0 #7059
[hugovk, radarhere]
- Drop support for soon-EOL Python 3.7 #7058
[hugovk, radarhere]
9.5.0 (2023-04-01)
------------------

View File

@ -78,6 +78,7 @@ debug:
.PHONY: release-test
release-test:
python3 Tests/check_release_notes.py
python3 -m pip install -e .[tests]
python3 selftest.py
python3 -m pytest Tests
@ -123,5 +124,5 @@ lint:
lint-fix:
python3 -c "import black" > /dev/null 2>&1 || python3 -m pip install black
python3 -c "import isort" > /dev/null 2>&1 || python3 -m pip install isort
python3 -m black --target-version py37 .
python3 -m black --target-version py38 .
python3 -m isort .

View File

@ -0,0 +1,6 @@
import sys
from pathlib import Path
for rst in Path("docs/releasenotes").glob("[1-9]*.rst"):
if "TODO" in open(rst).read():
sys.exit(f"Error: remove TODO from {rst}")

View File

@ -6,11 +6,6 @@ from PIL import _deprecate
@pytest.mark.parametrize(
"version, expected",
[
(
10,
"Old thing is deprecated and will be removed in Pillow 10 "
r"\(2023-07-01\)\. Use new thing instead\.",
),
(
11,
"Old thing is deprecated and will be removed in Pillow 11 "
@ -57,18 +52,18 @@ def test_old_version(deprecated, plural, expected):
def test_plural():
expected = (
r"Old things are deprecated and will be removed in Pillow 10 \(2023-07-01\)\. "
r"Old things are deprecated and will be removed in Pillow 11 \(2024-10-15\)\. "
r"Use new thing instead\."
)
with pytest.warns(DeprecationWarning, match=expected):
_deprecate.deprecate("Old things", 10, "new thing", plural=True)
_deprecate.deprecate("Old things", 11, "new thing", plural=True)
def test_replacement_and_action():
expected = "Use only one of 'replacement' and 'action'"
with pytest.raises(ValueError, match=expected):
_deprecate.deprecate(
"Old thing", 10, replacement="new thing", action="Upgrade to new thing"
"Old thing", 11, replacement="new thing", action="Upgrade to new thing"
)
@ -81,16 +76,16 @@ def test_replacement_and_action():
)
def test_action(action):
expected = (
r"Old thing is deprecated and will be removed in Pillow 10 \(2023-07-01\)\. "
r"Old thing is deprecated and will be removed in Pillow 11 \(2024-10-15\)\. "
r"Upgrade to new thing\."
)
with pytest.warns(DeprecationWarning, match=expected):
_deprecate.deprecate("Old thing", 10, action=action)
_deprecate.deprecate("Old thing", 11, action=action)
def test_no_replacement_or_action():
expected = (
r"Old thing is deprecated and will be removed in Pillow 10 \(2023-07-01\)"
r"Old thing is deprecated and will be removed in Pillow 11 \(2024-10-15\)"
)
with pytest.warns(DeprecationWarning, match=expected):
_deprecate.deprecate("Old thing", 10)
_deprecate.deprecate("Old thing", 11)

View File

@ -1,18 +0,0 @@
import warnings
with warnings.catch_warnings(record=True) as w:
# Arrange: cause all warnings to always be triggered
warnings.simplefilter("always")
# Act: trigger a warning with Qt5
from PIL import ImageQt
def test_deprecated():
# Assert
if ImageQt.qt_version in ("5", "side2"):
assert len(w) == 1
assert issubclass(w[0].category, DeprecationWarning)
assert "deprecated" in str(w[0].message)
else:
assert len(w) == 0

View File

@ -655,13 +655,3 @@ def test_different_modes_in_later_frames(mode, tmp_path):
im.save(test_file, save_all=True, append_images=[Image.new(mode, (1, 1))])
with Image.open(test_file) as reloaded:
assert reloaded.mode == mode
def test_constants_deprecation():
for enum, prefix in {
PngImagePlugin.Disposal: "APNG_DISPOSE_",
PngImagePlugin.Blend: "APNG_BLEND_",
}.items():
for name in enum.__members__:
with pytest.warns(DeprecationWarning):
assert getattr(PngImagePlugin, prefix + name) == enum[name]

View File

@ -1,6 +1,6 @@
import pytest
from PIL import BlpImagePlugin, Image
from PIL import Image
from .helper import (
assert_image_equal,
@ -72,14 +72,3 @@ def test_crashes(test_file):
with Image.open(f) as im:
with pytest.raises(OSError):
im.load()
def test_constants_deprecation():
for enum, prefix in {
BlpImagePlugin.Format: "BLP_FORMAT_",
BlpImagePlugin.Encoding: "BLP_ENCODING_",
BlpImagePlugin.AlphaEncoding: "BLP_ALPHA_ENCODING_",
}.items():
for name in enum.__members__:
with pytest.warns(DeprecationWarning):
assert getattr(BlpImagePlugin, prefix + name) == enum[name]

View File

@ -2,7 +2,7 @@ from io import BytesIO
import pytest
from PIL import FitsImagePlugin, FitsStubImagePlugin, Image
from PIL import FitsImagePlugin, Image
from .helper import assert_image_equal, hopper
@ -48,39 +48,3 @@ def test_comment():
image_data = b"SIMPLE = T / comment string"
with pytest.raises(OSError):
FitsImagePlugin.FitsImageFile(BytesIO(image_data))
def test_stub_deprecated():
class Handler:
opened = False
loaded = False
def open(self, im):
self.opened = True
def load(self, im):
self.loaded = True
im.fp.close()
return Image.new("RGB", (1, 1))
handler = Handler()
with pytest.warns(DeprecationWarning):
FitsStubImagePlugin.register_handler(handler)
with Image.open(TEST_FILE) as im:
assert im.format == "FITS"
assert im.size == (128, 128)
assert im.mode == "L"
assert handler.opened
assert not handler.loaded
im.load()
assert handler.loaded
FitsStubImagePlugin._handler = None
Image.register_open(
FitsImagePlugin.FitsImageFile.format,
FitsImagePlugin.FitsImageFile,
FitsImagePlugin._accept,
)

View File

@ -21,12 +21,3 @@ def test_invalid_file():
with pytest.raises(SyntaxError):
FtexImagePlugin.FtexImageFile(invalid_file)
def test_constants_deprecation():
for enum, prefix in {
FtexImagePlugin.Format: "FORMAT_",
}.items():
for name in enum.__members__:
with pytest.warns(DeprecationWarning):
assert getattr(FtexImagePlugin, prefix + name) == enum[name]

View File

@ -636,12 +636,6 @@ class TestFileJpeg:
assert max(im2.quantization[0]) <= 255
assert max(im2.quantization[1]) <= 255
def test_convert_dict_qtables_deprecation(self):
with pytest.warns(DeprecationWarning):
qtable = {0: [1, 2, 3, 4]}
qtable2 = JpegImagePlugin.convert_dict_qtables(qtable)
assert qtable == qtable2
@pytest.mark.skipif(not djpeg_available(), reason="djpeg not available")
def test_load_djpeg(self):
with Image.open(TEST_FILE) as img:

View File

@ -82,9 +82,6 @@ def test_textsize(request, tmp_path):
assert dy == 20
assert dx in (0, 10)
assert font.getlength(chr(i)) == dx
with pytest.warns(DeprecationWarning) as log:
assert font.getsize(chr(i)) == (dx, dy)
assert len(log) == 1
for i in range(len(message)):
msg = message[: i + 1]
assert font.getlength(msg) == len(msg) * 10

View File

@ -929,25 +929,7 @@ class TestImage:
im.apply_transparency()
assert im.palette.colors[(27, 35, 6, 214)] == 24
def test_categories_deprecation(self):
with pytest.warns(DeprecationWarning):
assert hopper().category == 0
with pytest.warns(DeprecationWarning):
assert Image.NORMAL == 0
with pytest.warns(DeprecationWarning):
assert Image.SEQUENCE == 1
with pytest.warns(DeprecationWarning):
assert Image.CONTAINER == 2
def test_constants(self):
with pytest.warns(DeprecationWarning):
assert Image.LINEAR == Image.Resampling.BILINEAR
with pytest.warns(DeprecationWarning):
assert Image.CUBIC == Image.Resampling.BICUBIC
with pytest.warns(DeprecationWarning):
assert Image.ANTIALIAS == Image.Resampling.LANCZOS
for enum in (
Image.Transpose,
Image.Transform,

View File

@ -1,7 +1,5 @@
import pytest
from PIL import Image
from .helper import assert_image_equal, hopper
@ -62,8 +60,3 @@ def test_f_mode():
im = hopper("F")
with pytest.raises(ValueError):
im.point(None)
def test_coerce_e_deprecation():
with pytest.warns(DeprecationWarning):
assert Image.coerce_e(2).data == 2

View File

@ -617,16 +617,6 @@ def test_auxiliary_channels_isolated():
assert_image_equal(test_image.convert(dst_format[2]), reference_image)
def test_constants_deprecation():
for enum, prefix in {
ImageCms.Intent: "INTENT_",
ImageCms.Direction: "DIRECTION_",
}.items():
for name in enum.__members__:
with pytest.warns(DeprecationWarning):
assert getattr(ImageCms, prefix + name) == enum[name]
@pytest.mark.parametrize("mode", ("RGB", "RGBA", "RGBX"))
def test_rgb_lab(mode):
im = Image.new(mode, (1, 1))

View File

@ -1224,21 +1224,6 @@ def test_textbbox_stroke():
assert draw.textbbox((2, 2), "ABC\nAaaa", font, stroke_width=4) == (-2, 2, 54, 50)
def test_textsize_deprecation():
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
with pytest.warns(DeprecationWarning) as log:
draw.textsize("Hello")
assert len(log) == 1
with pytest.warns(DeprecationWarning) as log:
draw.textsize("Hello\nWorld")
assert len(log) == 1
with pytest.warns(DeprecationWarning) as log:
draw.multiline_textsize("Hello\nWorld")
assert len(log) == 1
@skip_unless_feature("freetype2")
def test_stroke():
for suffix, stroke_fill in {"same": None, "different": "#0f0"}.items():

View File

@ -2,7 +2,7 @@ import os.path
import pytest
from PIL import Image, ImageDraw, ImageDraw2
from PIL import Image, ImageDraw, ImageDraw2, features
from .helper import (
assert_image_equal,
@ -171,19 +171,18 @@ def test_text():
@skip_unless_feature("freetype2")
def test_textsize():
def test_textbbox():
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw2.Draw(im)
font = ImageDraw2.Font("white", FONT_PATH)
# Act
with pytest.warns(DeprecationWarning) as log:
size = draw.textsize("ImageDraw2", font)
assert len(log) == 1
bbox = draw.textbbox((0, 0), "ImageDraw2", font)
# Assert
assert size[1] == 12
right = 72 if features.check_feature("raqm") else 70
assert bbox == (0, 2, right, 12)
@skip_unless_feature("freetype2")

View File

@ -251,27 +251,6 @@ def test_draw_align(font):
draw.text((100, 40), line, (0, 0, 0), font=font, align="left")
def test_multiline_size(font):
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
with pytest.warns(DeprecationWarning) as log:
# Test that textsize() correctly connects to multiline_textsize()
assert draw.textsize(TEST_TEXT, font=font) == draw.multiline_textsize(
TEST_TEXT, font=font
)
# Test that multiline_textsize corresponds to ImageFont.textsize()
# for single line text
assert font.getsize("A") == draw.multiline_textsize("A", font=font)
# Test that textsize() can pass on additional arguments
# to multiline_textsize()
draw.textsize(TEST_TEXT, font=font, spacing=4)
draw.textsize(TEST_TEXT, font, 4)
assert len(log) == 6
def test_multiline_bbox(font):
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
@ -298,12 +277,6 @@ def test_multiline_width(font):
draw.textbbox((0, 0), "longest line", font=font)[2]
== draw.multiline_textbbox((0, 0), "longest line\nline", font=font)[2]
)
with pytest.warns(DeprecationWarning) as log:
assert (
draw.textsize("longest line", font=font)[0]
== draw.multiline_textsize("longest line\nline", font=font)[0]
)
assert len(log) == 2
def test_multiline_spacing(font):
@ -326,29 +299,23 @@ def test_rotated_transposed_font(font, orientation):
# Original font
draw.font = font
with pytest.warns(DeprecationWarning) as log:
box_size_a = draw.textsize(word)
assert box_size_a == font.getsize(word)
assert len(log) == 2
bbox_a = draw.textbbox((10, 10), word)
# Rotated font
draw.font = transposed_font
with pytest.warns(DeprecationWarning) as log:
box_size_b = draw.textsize(word)
assert box_size_b == transposed_font.getsize(word)
assert len(log) == 2
bbox_b = draw.textbbox((20, 20), word)
# Check (w,h) of box a is (h,w) of box b
assert box_size_a[0] == box_size_b[1]
assert box_size_a[1] == box_size_b[0]
# Check (w, h) of box a is (h, w) of box b
assert (
bbox_a[2] - bbox_a[0],
bbox_a[3] - bbox_a[1],
) == (
bbox_b[3] - bbox_b[1],
bbox_b[2] - bbox_b[0],
)
# Check bbox b is (20, 20, 20 + h, 20 + w)
assert bbox_b[0] == 20
assert bbox_b[1] == 20
assert bbox_b[2] == 20 + bbox_a[3] - bbox_a[1]
assert bbox_b[3] == 20 + bbox_a[2] - bbox_a[0]
# Check top left co-ordinates are correct
assert bbox_b[:2] == (20, 20)
# text length is undefined for vertical text
with pytest.raises(ValueError):
@ -373,28 +340,25 @@ def test_unrotated_transposed_font(font, orientation):
# Original font
draw.font = font
with pytest.warns(DeprecationWarning) as log:
box_size_a = draw.textsize(word)
assert len(log) == 1
bbox_a = draw.textbbox((10, 10), word)
length_a = draw.textlength(word)
# Rotated font
draw.font = transposed_font
with pytest.warns(DeprecationWarning) as log:
box_size_b = draw.textsize(word)
assert len(log) == 1
bbox_b = draw.textbbox((20, 20), word)
length_b = draw.textlength(word)
# Check boxes a and b are same size
assert box_size_a == box_size_b
assert (
bbox_a[2] - bbox_a[0],
bbox_a[3] - bbox_a[1],
) == (
bbox_b[2] - bbox_b[0],
bbox_b[3] - bbox_b[1],
)
# Check bbox b is (20, 20, 20 + w, 20 + h)
assert bbox_b[0] == 20
assert bbox_b[1] == 20
assert bbox_b[2] == 20 + bbox_a[2] - bbox_a[0]
assert bbox_b[3] == 20 + bbox_a[3] - bbox_a[1]
# Check top left co-ordinates are correct
assert bbox_b[:2] == (20, 20)
assert length_a == length_b
@ -447,19 +411,6 @@ def test_free_type_font_get_metrics(font):
assert (ascent, descent) == (16, 4)
def test_free_type_font_get_offset(font):
# Arrange
text = "offset this"
# Act
with pytest.warns(DeprecationWarning) as log:
offset = font.getoffset(text)
# Assert
assert len(log) == 1
assert offset == (0, 3)
def test_free_type_font_get_mask(font):
# Arrange
text = "mask this"
@ -618,19 +569,6 @@ def test_imagefont_getters(font):
assert font.getlength("M") == 12
assert font.getlength("y") == 12
assert font.getlength("a") == 12
with pytest.warns(DeprecationWarning) as log:
assert font.getsize("A") == (12, 16)
assert font.getsize("AB") == (24, 16)
assert font.getsize("M") == (12, 16)
assert font.getsize("y") == (12, 20)
assert font.getsize("a") == (12, 16)
assert font.getsize_multiline("A") == (12, 16)
assert font.getsize_multiline("AB") == (24, 16)
assert font.getsize_multiline("a") == (12, 16)
assert font.getsize_multiline("ABC\n") == (36, 36)
assert font.getsize_multiline("ABC\nA") == (36, 36)
assert font.getsize_multiline("ABC\nAaaa") == (48, 36)
assert len(log) == 11
@pytest.mark.parametrize("stroke_width", (0, 2))
@ -641,16 +579,6 @@ def test_getsize_stroke(font, stroke_width):
12 + stroke_width,
16 + stroke_width,
)
with pytest.warns(DeprecationWarning) as log:
assert font.getsize("A", stroke_width=stroke_width) == (
12 + stroke_width * 2,
16 + stroke_width * 2,
)
assert font.getsize_multiline("ABC\nAaaa", stroke_width=stroke_width) == (
48 + stroke_width * 2,
36 + stroke_width * 4,
)
assert len(log) == 2
def test_complex_font_settings():
@ -781,11 +709,8 @@ def test_textbbox_non_freetypefont():
im = Image.new("RGB", (200, 200))
d = ImageDraw.Draw(im)
default_font = ImageFont.load_default()
with pytest.warns(DeprecationWarning) as log:
width, height = d.textsize("test", font=default_font)
assert len(log) == 1
assert d.textlength("test", font=default_font) == width
assert d.textbbox((0, 0), "test", font=default_font) == (0, 0, width, height)
assert d.textlength("test", font=default_font) == 24
assert d.textbbox((0, 0), "test", font=default_font) == (0, 0, 24, 11)
@pytest.mark.parametrize(
@ -1083,14 +1008,6 @@ def test_woff2(layout_engine):
assert_image_similar_tofile(im, "Tests/images/test_woff2.png", 5)
def test_fill_deprecation(font):
with pytest.warns(DeprecationWarning):
font.getmask2("Hello world", fill=Image.core.fill)
with pytest.warns(DeprecationWarning):
with pytest.raises(TypeError):
font.getmask2("Hello world", fill=None)
def test_render_mono_size():
# issue 4177
@ -1130,12 +1047,3 @@ def test_raqm_missing_warning(monkeypatch):
"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_",
}.items():
for name in enum.__members__:
with pytest.warns(DeprecationWarning):
assert getattr(ImageFont, prefix + name) == enum[name]

View File

@ -9,10 +9,6 @@ def test_sanity():
palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3)
assert len(palette.colors) == 256
with pytest.warns(DeprecationWarning):
with pytest.raises(ValueError):
ImagePalette.ImagePalette("RGB", list(range(256)) * 3, 10)
def test_reload():
with Image.open("Tests/images/hopper.gif") as im:

View File

@ -2,13 +2,10 @@ import warnings
import pytest
from PIL import ImageQt
from .helper import assert_image_similar, hopper
with warnings.catch_warnings() as w:
warnings.simplefilter("ignore", category=DeprecationWarning)
from PIL import ImageQt
pytestmark = pytest.mark.skipif(
not ImageQt.qt_is_installed, reason="Qt bindings are not installed"
)
@ -26,10 +23,6 @@ def test_rgb():
from PyQt6.QtGui import qRgb
elif ImageQt.qt_version == "side6":
from PySide6.QtGui import qRgb
elif ImageQt.qt_version == "5":
from PyQt5.QtGui import qRgb
elif ImageQt.qt_version == "side2":
from PySide2.QtGui import qRgb
assert qRgb(0, 0, 0) == qRgba(0, 0, 0, 255)

View File

@ -89,20 +89,3 @@ 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",
)
@pytest.mark.parametrize("viewer", ImageShow._viewers)
def test_file_deprecated(tmp_path, viewer):
f = str(tmp_path / "temp.jpg")
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

@ -89,13 +89,6 @@ def test_photoimage_blank(mode):
assert_image_equal(reloaded.convert(mode), im)
def test_box_deprecation():
im = hopper()
im_tk = ImageTk.PhotoImage(im)
with pytest.warns(DeprecationWarning):
im_tk.paste(im, (0, 0, 128, 128))
def test_bitmapimage():
im = hopper("1")

View File

@ -1,10 +1,6 @@
import warnings
import pytest
with warnings.catch_warnings():
warnings.simplefilter("ignore", category=DeprecationWarning)
from PIL import ImageQt
from PIL import ImageQt
from .helper import assert_image_equal_tofile, assert_image_similar, hopper
@ -19,14 +15,6 @@ if ImageQt.qt_is_installed:
from PySide6.QtCore import QPoint
from PySide6.QtGui import QImage, QPainter, QRegion
from PySide6.QtWidgets import QApplication, QHBoxLayout, QLabel, QWidget
elif ImageQt.qt_version == "5":
from PyQt5.QtCore import QPoint
from PyQt5.QtGui import QImage, QPainter, QRegion
from PyQt5.QtWidgets import QApplication, QHBoxLayout, QLabel, QWidget
elif ImageQt.qt_version == "side2":
from PySide2.QtCore import QPoint
from PySide2.QtGui import QImage, QPainter, QRegion
from PySide2.QtWidgets import QApplication, QHBoxLayout, QLabel, QWidget
class Example(QWidget):
def __init__(self):

View File

@ -1,10 +1,6 @@
import warnings
import pytest
with warnings.catch_warnings():
warnings.simplefilter("ignore", category=DeprecationWarning)
from PIL import ImageQt
from PIL import ImageQt
from .helper import assert_image_equal, assert_image_equal_tofile, hopper
@ -32,7 +28,7 @@ def test_sanity(mode, tmp_path):
assert_image_equal(rt, src)
if mode == "1":
# BW appears to not save correctly on QT5
# BW appears to not save correctly on Qt
# kicks out errors on console:
# libpng warning: Invalid color type/bit depth combination
# in IHDR

View File

@ -12,22 +12,38 @@ Deprecated features
Below are features which are considered deprecated. Where appropriate,
a ``DeprecationWarning`` is issued.
PSFile
~~~~~~
.. deprecated:: 9.5.0
The :py:class:`~PIL.EpsImagePlugin.PSFile` class has been deprecated and will
be removed in Pillow 11 (2024-10-15). This class was only made as a helper to
be used internally, so there is no replacement. If you need this functionality
though, it is a very short class that can easily be recreated in your own code.
Removed features
----------------
Deprecated features are only removed in major releases after an appropriate
period of deprecation has passed.
Tk/Tcl 8.4
~~~~~~~~~~
.. deprecated:: 8.2.0
.. versionremoved:: 10.0.0
Support for Tk/Tcl 8.4 is deprecated and will be removed in Pillow 10.0.0 (2023-07-01),
when Tk/Tcl 8.5 will be the minimum supported.
Support for Tk/Tcl 8.4 was removed in Pillow 10.0.0 (2023-07-01).
Categories
~~~~~~~~~~
.. deprecated:: 8.2.0
.. versionremoved:: 10.0.0
``im.category`` is deprecated and will be removed in Pillow 10.0.0 (2023-07-01),
along with the related ``Image.NORMAL``, ``Image.SEQUENCE`` and
``Image.CONTAINER`` attributes.
``im.category`` was removed along with the related ``Image.NORMAL``,
``Image.SEQUENCE`` and ``Image.CONTAINER`` attributes.
To determine if an image has multiple frames or not,
``getattr(im, "is_animated", False)`` can be used instead.
@ -36,43 +52,40 @@ JpegImagePlugin.convert_dict_qtables
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 8.3.0
.. versionremoved:: 10.0.0
JPEG ``quantization`` is now automatically converted, but still returned as a
dictionary. The :py:attr:`~PIL.JpegImagePlugin.convert_dict_qtables` method no longer
performs any operations on the data given to it, has been deprecated and will be
removed in Pillow 10.0.0 (2023-07-01).
Since deprecation in Pillow 8.3.0, the ``convert_dict_qtables`` method no longer
performed any operations on the data given to it, and has been removed.
ImagePalette size parameter
~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 8.4.0
The ``size`` parameter will be removed in Pillow 10.0.0 (2023-07-01).
.. versionremoved:: 10.0.0
Before Pillow 8.3.0, ``ImagePalette`` required palette data of particular lengths by
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.
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
.. versionremoved:: 10.0.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``.
removed and 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.
Constants
~~~~~~~~~
.. deprecated:: 9.1.0
.. versionremoved:: 10.0.0
A number of constants have been deprecated and will be removed in Pillow 10.0.0
(2023-07-01). Instead, ``enum.IntEnum`` classes have been added.
A number of constants have been removed.
Instead, ``enum.IntEnum`` classes have been added.
.. note::
@ -81,7 +94,7 @@ A number of constants have been deprecated and will be removed in Pillow 10.0.0
See :ref:`restored-image-constants`
===================================================== ============================================================
Deprecated Use instead
Removed Use instead
===================================================== ============================================================
``Image.LINEAR`` ``Image.BILINEAR`` or ``Image.Resampling.BILINEAR``
``Image.CUBIC`` ``Image.BICUBIC`` or ``Image.Resampling.BICUBIC``
@ -115,67 +128,29 @@ FitsStubImagePlugin
~~~~~~~~~~~~~~~~~~~
.. deprecated:: 9.1.0
.. versionremoved:: 10.0.0
The stub image plugin ``FitsStubImagePlugin`` has been deprecated and will be removed in
Pillow 10.0.0 (2023-07-01). FITS images can be read without a handler through
:mod:`~PIL.FitsImagePlugin` instead.
FreeTypeFont.getmask2 fill parameter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 9.2.0
The undocumented ``fill`` parameter of :py:meth:`.FreeTypeFont.getmask2` has been
deprecated and will be removed in Pillow 10 (2023-07-01).
PhotoImage.paste box parameter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 9.2.0
The ``box`` parameter is unused. It will be removed in Pillow 10.0.0 (2023-07-01).
PyQt5 and PySide2
~~~~~~~~~~~~~~~~~
.. deprecated:: 9.2.0
`Qt 5 reached end-of-life <https://www.qt.io/blog/qt-5.15-released>`_ on 2020-12-08 for
open-source users (and will reach EOL on 2023-12-08 for commercial licence holders).
Support for PyQt5 and PySide2 has been deprecated from ``ImageQt`` and will be removed
in Pillow 10 (2023-07-01). Upgrade to
`PyQt6 <https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ or
`PySide6 <https://doc.qt.io/qtforpython/>`_ instead.
Image.coerce_e
~~~~~~~~~~~~~~
.. deprecated:: 9.2.0
This undocumented method has been deprecated and will be removed in Pillow 10
(2023-07-01).
.. _Font size and offset methods:
The stub image plugin ``FitsStubImagePlugin`` has been removed.
FITS images can be read without a handler through :mod:`~PIL.FitsImagePlugin` instead.
Font size and offset methods
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 9.2.0
.. versionremoved:: 10.0.0
Several functions for computing the size and offset of rendered text
have been deprecated and will be removed in Pillow 10 (2023-07-01):
Several functions for computing the size and offset of rendered text have been removed:
=========================================================================== =============================================================================================================
Deprecated Use instead
=========================================================================== =============================================================================================================
:py:meth:`.FreeTypeFont.getsize` and :py:meth:`.FreeTypeFont.getoffset` :py:meth:`.FreeTypeFont.getbbox` and :py:meth:`.FreeTypeFont.getlength`
:py:meth:`.FreeTypeFont.getsize_multiline` :py:meth:`.ImageDraw.multiline_textbbox`
:py:meth:`.ImageFont.getsize` :py:meth:`.ImageFont.getbbox` and :py:meth:`.ImageFont.getlength`
:py:meth:`.TransposedFont.getsize` :py:meth:`.TransposedFont.getbbox` and :py:meth:`.TransposedFont.getlength`
:py:meth:`.ImageDraw.textsize` and :py:meth:`.ImageDraw.multiline_textsize` :py:meth:`.ImageDraw.textbbox`, :py:meth:`.ImageDraw.textlength` and :py:meth:`.ImageDraw.multiline_textbbox`
:py:meth:`.ImageDraw2.Draw.textsize` :py:meth:`.ImageDraw2.Draw.textbbox` and :py:meth:`.ImageDraw2.Draw.textlength`
=========================================================================== =============================================================================================================
=============================================================== =============================================================================================================
Removed Use instead
=============================================================== =============================================================================================================
``FreeTypeFont.getsize()`` and ``FreeTypeFont.getoffset()`` :py:meth:`.FreeTypeFont.getbbox` and :py:meth:`.FreeTypeFont.getlength`
``FreeTypeFont.getsize_multiline()`` :py:meth:`.ImageDraw.multiline_textbbox`
``ImageFont.getsize()`` :py:meth:`.ImageFont.getbbox` and :py:meth:`.ImageFont.getlength`
``TransposedFont.getsize()`` :py:meth:`.TransposedFont.getbbox` and :py:meth:`.TransposedFont.getlength`
``ImageDraw.textsize()`` and ``ImageDraw.multiline_textsize()`` :py:meth:`.ImageDraw.textbbox`, :py:meth:`.ImageDraw.textlength` and :py:meth:`.ImageDraw.multiline_textbbox`
``ImageDraw2.Draw.textsize()`` :py:meth:`.ImageDraw2.Draw.textbbox` and :py:meth:`.ImageDraw2.Draw.textlength`
=============================================================== =============================================================================================================
Previous code::
@ -207,21 +182,43 @@ Use instead::
left, top, right, bottom = draw.multiline_textbbox((0, 0), "Hello\nworld")
width, height = right - left, bottom - top
PSFile
~~~~~~
FreeTypeFont.getmask2 fill parameter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 9.5.0
.. deprecated:: 9.2.0
.. versionremoved:: 10.0.0
The :py:class:`~PIL.EpsImagePlugin.PSFile` class has been deprecated and will
be removed in Pillow 11 (2024-10-15). This class was only made as a helper to
be used internally, so there is no replacement. If you need this functionality
though, it is a very short class that can easily be recreated in your own code.
The undocumented ``fill`` parameter of :py:meth:`.FreeTypeFont.getmask2` has been
removed.
Removed features
----------------
PhotoImage.paste box parameter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Deprecated features are only removed in major releases after an appropriate
period of deprecation has passed.
.. deprecated:: 9.2.0
.. versionremoved:: 10.0.0
The ``box`` parameter was unused and has been removed.
PyQt5 and PySide2
~~~~~~~~~~~~~~~~~
.. deprecated:: 9.2.0
.. versionremoved:: 10.0.0
`Qt 5 reached end-of-life <https://www.qt.io/blog/qt-5.15-released>`_ on 2020-12-08 for
open-source users (and will reach EOL on 2023-12-08 for commercial licence holders).
Support for PyQt5 and PySide2 has been removed from ``ImageQt``. Upgrade to
`PyQt6 <https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ or
`PySide6 <https://doc.qt.io/qtforpython/>`_ instead.
Image.coerce_e
~~~~~~~~~~~~~~
.. deprecated:: 9.2.0
.. versionremoved:: 10.0.0
This undocumented method has been removed.
PILLOW_VERSION constant
~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -434,7 +434,7 @@ These platforms are built and tested for every change.
+==================================+============================+=====================+
| Alpine | 3.9 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| Amazon Linux 2 | 3.7 | x86-64 |
| Amazon Linux 2 | 3.9 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| Amazon Linux 2023 | 3.9 | x86-64 |
+----------------------------------+----------------------------+---------------------+
@ -454,22 +454,22 @@ These platforms are built and tested for every change.
+----------------------------------+----------------------------+---------------------+
| Gentoo | 3.9 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| macOS 12 Monterey | 3.7, 3.8, 3.9, 3.10, 3.11, | x86-64 |
| macOS 12 Monterey | 3.8, 3.9, 3.10, 3.11, | x86-64 |
| | 3.12, PyPy3 | |
+----------------------------------+----------------------------+---------------------+
| Ubuntu Linux 18.04 LTS (Bionic) | 3.9 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| Ubuntu Linux 20.04 LTS (Focal) | 3.8 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| Ubuntu Linux 22.04 LTS (Jammy) | 3.7, 3.8, 3.9, 3.10, 3.11, | x86-64 |
| Ubuntu Linux 22.04 LTS (Jammy) | 3.8, 3.9, 3.10, 3.11, | x86-64 |
| | 3.12, PyPy3 | |
| +----------------------------+---------------------+
| | 3.10 | arm64v8, ppc64le, |
| | | s390x |
+----------------------------------+----------------------------+---------------------+
| Windows Server 2016 | 3.7 | x86-64 |
| Windows Server 2016 | 3.8 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| Windows Server 2022 | 3.7, 3.8, 3.9, 3.10, 3.11, | x86, x86-64 |
| Windows Server 2022 | 3.8, 3.9, 3.10, 3.11, | x86, x86-64 |
| | 3.12, PyPy3 | |
| +----------------------------+---------------------+
| | 3.9 (MinGW) | x86, x86-64 |

View File

@ -1,5 +1,6 @@
Python,3.11,3.10,3.9,3.8,3.7,3.6,3.5
Pillow >= 9.3,Yes,Yes,Yes,Yes,Yes,,
Pillow >= 10,Yes,Yes,Yes,Yes,,,
Pillow 9.3 - 9.5,Yes,Yes,Yes,Yes,Yes,,
Pillow 9.0 - 9.2,,Yes,Yes,Yes,Yes,,
Pillow 8.3.2 - 8.4,,Yes,Yes,Yes,Yes,Yes,
Pillow 8.0 - 8.3.1,,,Yes,Yes,Yes,Yes,

1 Python 3.11 3.10 3.9 3.8 3.7 3.6 3.5
2 Pillow >= 9.3 Pillow >= 10 Yes Yes Yes Yes Yes
3 Pillow 9.3 - 9.5 Yes Yes Yes Yes Yes
4 Pillow 9.0 - 9.2 Yes Yes Yes Yes
5 Pillow 8.3.2 - 8.4 Yes Yes Yes Yes Yes
6 Pillow 8.0 - 8.3.1 Yes Yes Yes Yes

View File

@ -412,18 +412,6 @@ See :ref:`concept-filters` for details.
:undoc-members:
:noindex:
Some deprecated filters are also available under the following names:
.. data:: NONE
:noindex:
:value: Resampling.NEAREST
.. data:: LINEAR
:value: Resampling.BILINEAR
.. data:: CUBIC
:value: Resampling.BICUBIC
.. data:: ANTIALIAS
:value: Resampling.LANCZOS
Dither modes
^^^^^^^^^^^^

View File

@ -474,116 +474,6 @@ Methods
.. versionadded:: 8.0.0
.. py:method:: ImageDraw.textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0)
.. deprecated:: 9.2.0
See :ref:`deprecations <Font size and offset methods>` for more information.
Use :py:meth:`textlength()` to measure the offset of following text with
1/64 pixel precision.
Use :py:meth:`textbbox()` to get the exact bounding box based on an anchor.
Return the size of the given string, in pixels.
.. note:: For historical reasons this function measures text height from
the ascender line instead of the top, see :ref:`text-anchors`.
If you wish to measure text height from the top, it is recommended
to use :meth:`textbbox` with ``anchor='lt'`` instead.
:param text: Text to be measured. If it contains any newline characters,
the text is passed on to :py:meth:`~PIL.ImageDraw.ImageDraw.multiline_textsize`.
:param font: An :py:class:`~PIL.ImageFont.ImageFont` instance.
:param spacing: If the text is passed on to
:py:meth:`~PIL.ImageDraw.ImageDraw.multiline_textsize`,
the number of pixels between lines.
:param direction: Direction of the text. It can be ``"rtl"`` (right to
left), ``"ltr"`` (left to right) or ``"ttb"`` (top to bottom).
Requires libraqm.
.. versionadded:: 4.2.0
:param features: A list of OpenType font features to be used during text
layout. This is usually used to turn on optional
font features that are not enabled by default,
for example ``"dlig"`` or ``"ss01"``, but can be also
used to turn off default font features, for
example ``"-liga"`` to disable ligatures or ``"-kern"``
to disable kerning. To get all supported
features, see `OpenType docs`_.
Requires libraqm.
.. versionadded:: 4.2.0
:param language: Language of the text. Different languages may use
different glyph shapes or ligatures. This parameter tells
the font which language the text is in, and to apply the
correct substitutions as appropriate, if available.
It should be a `BCP 47 language code`_.
Requires libraqm.
.. versionadded:: 6.0.0
:param stroke_width: The width of the text stroke.
.. versionadded:: 6.2.0
:return: (width, height)
.. py:method:: ImageDraw.multiline_textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0)
.. deprecated:: 9.2.0
See :ref:`deprecations <Font size and offset methods>` for more information.
Use :py:meth:`.multiline_textbbox` instead.
Return the size of the given string, in pixels.
Use :py:meth:`textlength()` to measure the offset of following text with
1/64 pixel precision.
Use :py:meth:`textbbox()` to get the exact bounding box based on an anchor.
.. note:: For historical reasons this function measures text height as the
distance between the top ascender line and bottom descender line,
not the top and bottom of the text, see :ref:`text-anchors`.
If you wish to measure text height from the top to the bottom of text,
it is recommended to use :meth:`multiline_textbbox` instead.
:param text: Text to be measured.
:param font: An :py:class:`~PIL.ImageFont.ImageFont` instance.
:param spacing: The number of pixels between lines.
:param direction: Direction of the text. It can be ``"rtl"`` (right to
left), ``"ltr"`` (left to right) or ``"ttb"`` (top to bottom).
Requires libraqm.
.. versionadded:: 4.2.0
:param features: A list of OpenType font features to be used during text
layout. This is usually used to turn on optional
font features that are not enabled by default,
for example ``"dlig"`` or ``"ss01"``, but can be also
used to turn off default font features, for
example ``"-liga"`` to disable ligatures or ``"-kern"``
to disable kerning. To get all supported
features, see `OpenType docs`_.
Requires libraqm.
.. versionadded:: 4.2.0
:param language: Language of the text. Different languages may use
different glyph shapes or ligatures. This parameter tells
the font which language the text is in, and to apply the
correct substitutions as appropriate, if available.
It should be a `BCP 47 language code`_.
Requires libraqm.
.. versionadded:: 6.0.0
:param stroke_width: The width of the text stroke.
.. versionadded:: 6.2.0
:return: (width, height)
.. py:method:: ImageDraw.textlength(text, font=None, direction=None, features=None, language=None, embedded_color=False)
Returns length (in pixels with 1/64 precision) of given text when rendered

View File

@ -29,6 +29,8 @@ Classes
All enhancement classes implement a common interface, containing a single
method:
.. _enhancement-factor:
.. py:class:: _Enhance
.. py:method:: enhance(factor)
@ -45,31 +47,35 @@ method:
Adjust image color balance.
This class can be used to adjust the colour balance of an image, in
a manner similar to the controls on a colour TV set. An enhancement
factor of 0.0 gives a black and white image. A factor of 1.0 gives
the original image.
This class can be used to adjust the colour balance of an image, in a
manner similar to the controls on a colour TV set. An
:ref:`enhancement factor <enhancement-factor>` of 0.0 gives a black and
white image. A factor of 1.0 gives the original image.
.. py:class:: Contrast(image)
Adjust image contrast.
This class can be used to control the contrast of an image, similar
to the contrast control on a TV set. An enhancement factor of 0.0
gives a solid grey image. A factor of 1.0 gives the original image.
This class can be used to control the contrast of an image, similar to the
contrast control on a TV set. An
:ref:`enhancement factor <enhancement-factor>` of 0.0 gives a solid grey
image, a factor of 1.0 gives the original image, and greater values
increase the contrast of the image.
.. py:class:: Brightness(image)
Adjust image brightness.
This class can be used to control the brightness of an image. An
enhancement factor of 0.0 gives a black image. A factor of 1.0 gives the
original image.
This class can be used to control the brightness of an image. An
:ref:`enhancement factor <enhancement-factor>` of 0.0 gives a black image,
a factor of 1.0 gives the original image, and greater values increase the
brightness of the image.
.. py:class:: Sharpness(image)
Adjust image sharpness.
This class can be used to adjust the sharpness of an image. An
enhancement factor of 0.0 gives a blurred image, a factor of 1.0 gives the
original image, and a factor of 2.0 gives a sharpened image.
:ref:`enhancement factor <enhancement-factor>` of 0.0 gives a blurred
image, a factor of 1.0 gives the original image, and a factor of 2.0 gives
a sharpened image.

View File

@ -4,16 +4,8 @@
:py:mod:`~PIL.ImageQt` Module
=============================
The :py:mod:`~PIL.ImageQt` module contains support for creating PyQt6, PySide6, PyQt5
or PySide2 QImage objects from PIL images.
`Qt 5 reached end-of-life <https://www.qt.io/blog/qt-5.15-released>`_ on 2020-12-08 for
open-source users (and will reach EOL on 2023-12-08 for commercial licence holders).
Support for PyQt5 and PySide2 has been deprecated from ``ImageQt`` and will be removed
in Pillow 10 (2023-07-01). Upgrade to
`PyQt6 <https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ or
`PySide6 <https://doc.qt.io/qtforpython/>`_ instead.
The :py:mod:`~PIL.ImageQt` module contains support for creating PyQt6 or PySide6
QImage objects from PIL images.
.. versionadded:: 1.1.6
@ -22,7 +14,7 @@ in Pillow 10 (2023-07-01). Upgrade to
Creates an :py:class:`~PIL.ImageQt.ImageQt` object from a PIL
:py:class:`~PIL.Image.Image` object. This class is a subclass of
QtGui.QImage, which means that you can pass the resulting objects directly
to PyQt6/PySide6/PyQt5/PySide2 API functions and methods.
to PyQt6/PySide6 API functions and methods.
This operation is currently supported for mode 1, L, P, RGB, and RGBA
images. To handle other modes, you need to convert the image first.

View File

@ -0,0 +1,165 @@
10.0.0
------
Backwards Incompatible Changes
==============================
Categories
^^^^^^^^^^
``im.category`` has been removed, along with the related ``Image.NORMAL``,
``Image.SEQUENCE`` and ``Image.CONTAINER`` attributes.
To determine if an image has multiple frames or not,
``getattr(im, "is_animated", False)`` can be used instead.
Tk/Tcl 8.4
^^^^^^^^^^
Support for Tk/Tcl 8.4 has been removed.
JpegImagePlugin.convert_dict_qtables
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Since deprecation in Pillow 8.3.0, the ``convert_dict_qtables`` method no longer
performed any operations on the data given to it, and has been removed.
ImagePalette size parameter
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Before Pillow 8.3.0, ``ImagePalette`` required palette data of particular lengths by
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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``file`` argument in :py:meth:`~PIL.ImageShow.Viewer.show_file()` has been
removed and replaced by ``path``.
In effect, ``viewer.show_file("test.jpg")`` will continue to work unchanged.
Constants
^^^^^^^^^
A number of constants have been removed.
Instead, ``enum.IntEnum`` classes have been added.
===================================================== ============================================================
Removed Use instead
===================================================== ============================================================
``Image.LINEAR`` ``Image.BILINEAR`` or ``Image.Resampling.BILINEAR``
``Image.CUBIC`` ``Image.BICUBIC`` or ``Image.Resampling.BICUBIC``
``Image.ANTIALIAS`` ``Image.LANCZOS`` or ``Image.Resampling.LANCZOS``
``ImageCms.INTENT_PERCEPTUAL`` ``ImageCms.Intent.PERCEPTUAL``
``ImageCms.INTENT_RELATIVE_COLORMETRIC`` ``ImageCms.Intent.RELATIVE_COLORMETRIC``
``ImageCms.INTENT_SATURATION`` ``ImageCms.Intent.SATURATION``
``ImageCms.INTENT_ABSOLUTE_COLORIMETRIC`` ``ImageCms.Intent.ABSOLUTE_COLORIMETRIC``
``ImageCms.DIRECTION_INPUT`` ``ImageCms.Direction.INPUT``
``ImageCms.DIRECTION_OUTPUT`` ``ImageCms.Direction.OUTPUT``
``ImageCms.DIRECTION_PROOF`` ``ImageCms.Direction.PROOF``
``ImageFont.LAYOUT_BASIC`` ``ImageFont.Layout.BASIC``
``ImageFont.LAYOUT_RAQM`` ``ImageFont.Layout.RAQM``
``BlpImagePlugin.BLP_FORMAT_JPEG`` ``BlpImagePlugin.Format.JPEG``
``BlpImagePlugin.BLP_ENCODING_UNCOMPRESSED`` ``BlpImagePlugin.Encoding.UNCOMPRESSED``
``BlpImagePlugin.BLP_ENCODING_DXT`` ``BlpImagePlugin.Encoding.DXT``
``BlpImagePlugin.BLP_ENCODING_UNCOMPRESSED_RAW_RGBA`` ``BlpImagePlugin.Encoding.UNCOMPRESSED_RAW_RGBA``
``BlpImagePlugin.BLP_ALPHA_ENCODING_DXT1`` ``BlpImagePlugin.AlphaEncoding.DXT1``
``BlpImagePlugin.BLP_ALPHA_ENCODING_DXT3`` ``BlpImagePlugin.AlphaEncoding.DXT3``
``BlpImagePlugin.BLP_ALPHA_ENCODING_DXT5`` ``BlpImagePlugin.AlphaEncoding.DXT5``
``FtexImagePlugin.FORMAT_DXT1`` ``FtexImagePlugin.Format.DXT1``
``FtexImagePlugin.FORMAT_UNCOMPRESSED`` ``FtexImagePlugin.Format.UNCOMPRESSED``
``PngImagePlugin.APNG_DISPOSE_OP_NONE`` ``PngImagePlugin.Disposal.OP_NONE``
``PngImagePlugin.APNG_DISPOSE_OP_BACKGROUND`` ``PngImagePlugin.Disposal.OP_BACKGROUND``
``PngImagePlugin.APNG_DISPOSE_OP_PREVIOUS`` ``PngImagePlugin.Disposal.OP_PREVIOUS``
``PngImagePlugin.APNG_BLEND_OP_SOURCE`` ``PngImagePlugin.Blend.OP_SOURCE``
``PngImagePlugin.APNG_BLEND_OP_OVER`` ``PngImagePlugin.Blend.OP_OVER``
===================================================== ============================================================
FitsStubImagePlugin
^^^^^^^^^^^^^^^^^^^
The stub image plugin ``FitsStubImagePlugin`` has been removed.
FITS images can be read without a handler through :mod:`~PIL.FitsImagePlugin` instead.
Font size and offset methods
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Several functions for computing the size and offset of rendered text have been removed:
=============================================================== =============================================================================================================
Removed Use instead
=============================================================== =============================================================================================================
``FreeTypeFont.getsize()`` and ``FreeTypeFont.getoffset()`` :py:meth:`.FreeTypeFont.getbbox` and :py:meth:`.FreeTypeFont.getlength`
``FreeTypeFont.getsize_multiline()`` :py:meth:`.ImageDraw.multiline_textbbox`
``ImageFont.getsize()`` :py:meth:`.ImageFont.getbbox` and :py:meth:`.ImageFont.getlength`
``TransposedFont.getsize()`` :py:meth:`.TransposedFont.getbbox` and :py:meth:`.TransposedFont.getlength`
``ImageDraw.textsize()`` and ``ImageDraw.multiline_textsize()`` :py:meth:`.ImageDraw.textbbox`, :py:meth:`.ImageDraw.textlength` and :py:meth:`.ImageDraw.multiline_textbbox`
``ImageDraw2.Draw.textsize()`` :py:meth:`.ImageDraw2.Draw.textbbox` and :py:meth:`.ImageDraw2.Draw.textlength`
=============================================================== =============================================================================================================
FreeTypeFont.getmask2 fill parameter
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The undocumented ``fill`` parameter of :py:meth:`.FreeTypeFont.getmask2` has been
removed.
PhotoImage.paste box parameter
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``box`` parameter was unused and has been removed.
PyQt5 and PySide2
^^^^^^^^^^^^^^^^^
`Qt 5 reached end-of-life <https://www.qt.io/blog/qt-5.15-released>`_ on 2020-12-08 for
open-source users (and will reach EOL on 2023-12-08 for commercial licence holders).
Support for PyQt5 and PySide2 has been removed from ``ImageQt``. Upgrade to
`PyQt6 <https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ or
`PySide6 <https://doc.qt.io/qtforpython/>`_ instead.
Image.coerce_e
^^^^^^^^^^^^^^
This undocumented method has been removed.
Deprecations
============
TODO
^^^^
TODO
API Changes
===========
TODO
^^^^
TODO
API Additions
=============
TODO
^^^^
TODO
Security
========
TODO
^^^^
TODO
Other Changes
=============
TODO
^^^^
TODO

View File

@ -8,7 +8,7 @@ JpegImagePlugin.convert_dict_qtables
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
JPEG ``quantization`` is now automatically converted, but still returned as a
dictionary. The :py:attr:`~PIL.JpegImagePlugin.convert_dict_qtables` method no longer
dictionary. The ``convert_dict_qtables`` method no longer
performs any operations on the data given to it, has been deprecated and will be
removed in Pillow 10.0.0 (2023-07-01).

View File

@ -48,16 +48,16 @@ Font size and offset methods
Several functions for computing the size and offset of rendered text
have been deprecated and will be removed in Pillow 10 (2023-07-01):
=========================================================================== =============================================================================================================
Deprecated Use instead
=========================================================================== =============================================================================================================
:py:meth:`.FreeTypeFont.getsize` and :py:meth:`.FreeTypeFont.getoffset` :py:meth:`.FreeTypeFont.getbbox` and :py:meth:`.FreeTypeFont.getlength`
:py:meth:`.FreeTypeFont.getsize_multiline` :py:meth:`.ImageDraw.multiline_textbbox`
:py:meth:`.ImageFont.getsize` :py:meth:`.ImageFont.getbbox` and :py:meth:`.ImageFont.getlength`
:py:meth:`.TransposedFont.getsize` :py:meth:`.TransposedFont.getbbox` and :py:meth:`.TransposedFont.getlength`
:py:meth:`.ImageDraw.textsize` and :py:meth:`.ImageDraw.multiline_textsize` :py:meth:`.ImageDraw.textbbox`, :py:meth:`.ImageDraw.textlength` and :py:meth:`.ImageDraw.multiline_textbbox`
:py:meth:`.ImageDraw2.Draw.textsize` :py:meth:`.ImageDraw2.Draw.textbbox` and :py:meth:`.ImageDraw2.Draw.textlength`
=========================================================================== =============================================================================================================
=============================================================== =============================================================================================================
Deprecated Use instead
=============================================================== =============================================================================================================
``FreeTypeFont.getsize()`` and ``FreeTypeFont.getoffset()`` :py:meth:`.FreeTypeFont.getbbox` and :py:meth:`.FreeTypeFont.getlength`
``FreeTypeFont.getsize_multiline()`` :py:meth:`.ImageDraw.multiline_textbbox`
``ImageFont.getsize()`` :py:meth:`.ImageFont.getbbox` and :py:meth:`.ImageFont.getlength`
``TransposedFont.getsize()`` :py:meth:`.TransposedFont.getbbox` and :py:meth:`.TransposedFont.getlength`
``ImageDraw.textsize()`` and ``ImageDraw.multiline_textsize()`` :py:meth:`.ImageDraw.textbbox`, :py:meth:`.ImageDraw.textlength` and :py:meth:`.ImageDraw.multiline_textbbox`
``ImageDraw2.Draw.textsize()`` :py:meth:`.ImageDraw2.Draw.textbbox` and :py:meth:`.ImageDraw2.Draw.textlength`
=============================================================== =============================================================================================================
Previous code::

View File

@ -14,6 +14,7 @@ expected to be backported to earlier versions.
.. toctree::
:maxdepth: 2
10.0.0
9.5.0
9.4.0
9.3.0

View File

@ -1,5 +1,5 @@
x.y.z
-----
xx.y.z
------
Backwards Incompatible Changes
==============================

View File

@ -12,7 +12,6 @@ classifiers =
License :: OSI Approved :: Historical Permission Notice and Disclaimer (HPND)
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
@ -36,7 +35,7 @@ project_urls =
[options]
packages = PIL
python_requires = >=3.7
python_requires = >=3.8
include_package_data = True
package_dir =
= src

View File

@ -681,10 +681,6 @@ class pil_build_ext(build_ext):
# Add the directory to the include path so we can include
# <openjpeg.h> rather than having to cope with the versioned
# include path
# FIXME (melvyn-sopacua):
# At this point it's possible that best_path is already in
# self.compiler.include_dirs. Should investigate how that is
# possible.
_add_directory(self.compiler.include_dirs, best_path, 0)
feature.jpeg2000 = "openjp2"
feature.openjpeg_version = ".".join(str(x) for x in best_version)

View File

@ -35,7 +35,6 @@ from enum import IntEnum
from io import BytesIO
from . import Image, ImageFile
from ._deprecate import deprecate
class Format(IntEnum):
@ -54,21 +53,6 @@ class AlphaEncoding(IntEnum):
DXT5 = 7
def __getattr__(name):
for enum, prefix in {
Format: "BLP_FORMAT_",
Encoding: "BLP_ENCODING_",
AlphaEncoding: "BLP_ALPHA_ENCODING_",
}.items():
if name.startswith(prefix):
name = name[len(prefix) :]
if name in enum.__members__:
deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}")
return enum[name]
msg = f"module '{__name__}' has no attribute '{name}'"
raise AttributeError(msg)
def unpack_565(i):
return ((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3

View File

@ -1,76 +0,0 @@
#
# The Python Imaging Library
# $Id$
#
# FITS stub adapter
#
# Copyright (c) 1998-2003 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
from . import FitsImagePlugin, Image, ImageFile
from ._deprecate import deprecate
_handler = None
def register_handler(handler):
"""
Install application-specific FITS image handler.
:param handler: Handler object.
"""
global _handler
_handler = handler
deprecate(
"FitsStubImagePlugin",
10,
action="FITS images can now be read without "
"a handler through FitsImagePlugin instead",
)
# Override FitsImagePlugin with this handler
# for backwards compatibility
try:
Image.ID.remove(FITSStubImageFile.format)
except ValueError:
pass
Image.register_open(
FITSStubImageFile.format, FITSStubImageFile, FitsImagePlugin._accept
)
class FITSStubImageFile(ImageFile.StubImageFile):
format = FitsImagePlugin.FitsImageFile.format
format_description = FitsImagePlugin.FitsImageFile.format_description
def _open(self):
offset = self.fp.tell()
im = FitsImagePlugin.FitsImageFile(self.fp)
self._size = im.size
self.mode = im.mode
self.tile = []
self.fp.seek(offset)
loader = self._load()
if loader:
loader.open(self)
def _load(self):
return _handler
def _save(im, fp, filename):
msg = "FITS save handler not installed"
raise OSError(msg)
# --------------------------------------------------------------------
# Registry
Image.register_save(FITSStubImageFile.format, _save)

View File

@ -56,7 +56,6 @@ from enum import IntEnum
from io import BytesIO
from . import Image, ImageFile
from ._deprecate import deprecate
MAGIC = b"FTEX"
@ -66,17 +65,6 @@ class Format(IntEnum):
UNCOMPRESSED = 1
def __getattr__(name):
for enum, prefix in {Format: "FORMAT_"}.items():
if name.startswith(prefix):
name = name[len(prefix) :]
if name in enum.__members__:
deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}")
return enum[name]
msg = f"module '{__name__}' has no attribute '{name}'"
raise AttributeError(msg)
class FtexImageFile(ImageFile.ImageFile):
format = "FTEX"
format_description = "Texture File Format (IW2:EOC)"

View File

@ -56,29 +56,8 @@ from . import (
_plugins,
)
from ._binary import i32le, o32be, o32le
from ._deprecate import deprecate
from ._util import DeferredError, is_path
def __getattr__(name):
categories = {"NORMAL": 0, "SEQUENCE": 1, "CONTAINER": 2}
if name in categories:
deprecate("Image categories", 10, "is_animated", plural=True)
return categories[name]
old_resampling = {
"LINEAR": "BILINEAR",
"CUBIC": "BICUBIC",
"ANTIALIAS": "LANCZOS",
}
if name in old_resampling:
deprecate(
name, 10, f"{old_resampling[name]} or Resampling.{old_resampling[name]}"
)
return Resampling[old_resampling[name]]
msg = f"module '{__name__}' has no attribute '{name}'"
raise AttributeError(msg)
logger = logging.getLogger(__name__)
@ -441,26 +420,18 @@ def _getencoder(mode, encoder_name, args, extra=()):
# Simple expression analyzer
def coerce_e(value):
deprecate("coerce_e", 10)
return value if isinstance(value, _E) else _E(1, value)
# _E(scale, offset) represents the affine transformation scale * x + offset.
# The "data" field is named for compatibility with the old implementation,
# and should be renamed once coerce_e is removed.
class _E:
def __init__(self, scale, data):
def __init__(self, scale, offset):
self.scale = scale
self.data = data
self.offset = offset
def __neg__(self):
return _E(-self.scale, -self.data)
return _E(-self.scale, -self.offset)
def __add__(self, other):
if isinstance(other, _E):
return _E(self.scale + other.scale, self.data + other.data)
return _E(self.scale, self.data + other)
return _E(self.scale + other.scale, self.offset + other.offset)
return _E(self.scale, self.offset + other)
__radd__ = __add__
@ -473,19 +444,19 @@ class _E:
def __mul__(self, other):
if isinstance(other, _E):
return NotImplemented
return _E(self.scale * other, self.data * other)
return _E(self.scale * other, self.offset * other)
__rmul__ = __mul__
def __truediv__(self, other):
if isinstance(other, _E):
return NotImplemented
return _E(self.scale / other, self.data / other)
return _E(self.scale / other, self.offset / other)
def _getscaleoffset(expr):
a = expr(_E(1, 0))
return (a.scale, a.data) if isinstance(a, _E) else (0, a)
return (a.scale, a.offset) if isinstance(a, _E) else (0, a)
# --------------------------------------------------------------------
@ -516,17 +487,10 @@ class Image:
self._size = (0, 0)
self.palette = None
self.info = {}
self._category = 0
self.readonly = 0
self.pyaccess = None
self._exif = None
def __getattr__(self, name):
if name == "category":
deprecate("Image categories", 10, "is_animated", plural=True)
return self._category
raise AttributeError(name)
@property
def width(self):
return self.size[0]
@ -639,7 +603,6 @@ class Image:
and self.mode == other.mode
and self.size == other.size
and self.info == other.info
and self._category == other._category
and self.getpalette() == other.getpalette()
and self.tobytes() == other.tobytes()
)

View File

@ -20,8 +20,6 @@ from enum import IntEnum
from PIL import Image
from ._deprecate import deprecate
try:
from PIL import _imagingcms
except ImportError as ex:
@ -117,17 +115,6 @@ class Direction(IntEnum):
PROOF = 2
def __getattr__(name):
for enum, prefix in {Intent: "INTENT_", Direction: "DIRECTION_"}.items():
if name.startswith(prefix):
name = name[len(prefix) :]
if name in enum.__members__:
deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}")
return enum[name]
msg = f"module '{__name__}' has no attribute '{name}'"
raise AttributeError(msg)
#
# flags

View File

@ -32,10 +32,8 @@
import math
import numbers
import warnings
from . import Image, ImageColor
from ._deprecate import deprecate
"""
A simple 2D drawing interface for PIL images.
@ -433,17 +431,11 @@ class ImageDraw:
return text.split(split_character)
def _multiline_spacing(self, font, spacing, stroke_width):
# this can be replaced with self.textbbox(...)[3] when textsize is removed
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
return (
self.textsize(
"A",
font=font,
stroke_width=stroke_width,
)[1]
+ spacing
)
return (
self.textbbox((0, 0), "A", font, stroke_width=stroke_width)[3]
+ stroke_width
+ spacing
)
def text(
self,
@ -645,72 +637,6 @@ class ImageDraw:
)
top += line_spacing
def textsize(
self,
text,
font=None,
spacing=4,
direction=None,
features=None,
language=None,
stroke_width=0,
):
"""Get the size of a given string, in pixels."""
deprecate("textsize", 10, "textbbox or textlength")
if self._multiline_check(text):
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
return self.multiline_textsize(
text,
font,
spacing,
direction,
features,
language,
stroke_width,
)
if font is None:
font = self.getfont()
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
return font.getsize(
text,
direction,
features,
language,
stroke_width,
)
def multiline_textsize(
self,
text,
font=None,
spacing=4,
direction=None,
features=None,
language=None,
stroke_width=0,
):
deprecate("multiline_textsize", 10, "multiline_textbbox")
max_width = 0
lines = self._multiline_split(text)
line_spacing = self._multiline_spacing(font, spacing, stroke_width)
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
for line in lines:
line_width, line_height = self.textsize(
line,
font,
spacing,
direction,
features,
language,
stroke_width,
)
max_width = max(max_width, line_width)
return max_width, len(lines) * line_spacing - spacing
def textlength(
self,
text,
@ -731,22 +657,7 @@ class ImageDraw:
if font is None:
font = self.getfont()
mode = "RGBA" if embedded_color else self.fontmode
try:
return font.getlength(text, mode, direction, features, language)
except AttributeError:
deprecate("textlength support for fonts without getlength", 10)
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
size = self.textsize(
text,
font,
direction=direction,
features=features,
language=language,
)
if direction == "ttb":
return size[1]
return size[0]
return font.getlength(text, mode, direction, features, language)
def textbbox(
self,

View File

@ -24,10 +24,7 @@
"""
import warnings
from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath
from ._deprecate import deprecate
class Pen:
@ -173,19 +170,6 @@ class Draw:
xy.transform(self.transform)
self.draw.text(xy, text, font=font.font, fill=font.color)
def textsize(self, text, font):
"""
.. deprecated:: 9.2.0
Return the size of the given string, in pixels.
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textsize`
"""
deprecate("textsize", 10, "textbbox or textlength")
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
return self.draw.textsize(text, font=font.font)
def textbbox(self, xy, text, font):
"""
Returns bounding box (in pixels) of given text.

View File

@ -34,7 +34,6 @@ from enum import IntEnum
from io import BytesIO
from . import Image
from ._deprecate import deprecate
from ._util import is_directory, is_path
@ -43,17 +42,6 @@ class Layout(IntEnum):
RAQM = 1
def __getattr__(name):
for enum, prefix in {Layout: "LAYOUT_"}.items():
if name.startswith(prefix):
name = name[len(prefix) :]
if name in enum.__members__:
deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}")
return enum[name]
msg = f"module '{__name__}' has no attribute '{name}'"
raise AttributeError(msg)
try:
from . import _imagingft as core
except ImportError as ex:
@ -62,9 +50,6 @@ except ImportError as ex:
core = DeferredError(ex)
_UNSPECIFIED = object()
# FIXME: add support for pilfont2 format (see FontFile.py)
# --------------------------------------------------------------------
@ -134,23 +119,6 @@ class ImageFont:
self.font = Image.core.font(image.im, data)
def getsize(self, text, *args, **kwargs):
"""
.. deprecated:: 9.2.0
Use :py:meth:`.getbbox` or :py:meth:`.getlength` instead.
See :ref:`deprecations <Font size and offset methods>` for more information.
Returns width and height (in pixels) of given text.
:param text: Text to measure.
:return: (width, height)
"""
deprecate("getsize", 10, "getbbox or getlength")
return self.font.getsize(text)
def getmask(self, text, mode="", *args, **kwargs):
"""
Create a bitmap for the text.
@ -412,165 +380,6 @@ class FreeTypeFont:
width, height = size[0] + 2 * stroke_width, size[1] + 2 * stroke_width
return left, top, left + width, top + height
def getsize(
self,
text,
direction=None,
features=None,
language=None,
stroke_width=0,
):
"""
.. deprecated:: 9.2.0
Use :py:meth:`getlength()` to measure the offset of following text with
1/64 pixel precision.
Use :py:meth:`getbbox()` to get the exact bounding box based on an anchor.
See :ref:`deprecations <Font size and offset methods>` for more information.
Returns width and height (in pixels) of given text if rendered in font with
provided direction, features, and language.
.. note:: For historical reasons this function measures text height from
the ascender line instead of the top, see :ref:`text-anchors`.
If you wish to measure text height from the top, it is recommended
to use the bottom value of :meth:`getbbox` with ``anchor='lt'`` instead.
:param text: Text to measure.
:param direction: Direction of the text. It can be 'rtl' (right to
left), 'ltr' (left to right) or 'ttb' (top to bottom).
Requires libraqm.
.. versionadded:: 4.2.0
:param features: A list of OpenType font features to be used during text
layout. This is usually used to turn on optional
font features that are not enabled by default,
for example 'dlig' or 'ss01', but can be also
used to turn off default font features for
example '-liga' to disable ligatures or '-kern'
to disable kerning. To get all supported
features, see
https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist
Requires libraqm.
.. versionadded:: 4.2.0
:param language: Language of the text. Different languages may use
different glyph shapes or ligatures. This parameter tells
the font which language the text is in, and to apply the
correct substitutions as appropriate, if available.
It should be a `BCP 47 language code
<https://www.w3.org/International/articles/language-tags/>`_
Requires libraqm.
.. versionadded:: 6.0.0
:param stroke_width: The width of the text stroke.
.. versionadded:: 6.2.0
:return: (width, height)
"""
deprecate("getsize", 10, "getbbox or getlength")
# vertical offset is added for historical reasons
# see https://github.com/python-pillow/Pillow/pull/4910#discussion_r486682929
size, offset = self.font.getsize(text, "L", direction, features, language)
return (
size[0] + stroke_width * 2,
size[1] + stroke_width * 2 + offset[1],
)
def getsize_multiline(
self,
text,
direction=None,
spacing=4,
features=None,
language=None,
stroke_width=0,
):
"""
.. deprecated:: 9.2.0
Use :py:meth:`.ImageDraw.multiline_textbbox` instead.
See :ref:`deprecations <Font size and offset methods>` for more information.
Returns width and height (in pixels) of given text if rendered in font
with provided direction, features, and language, while respecting
newline characters.
:param text: Text to measure.
:param direction: Direction of the text. It can be 'rtl' (right to
left), 'ltr' (left to right) or 'ttb' (top to bottom).
Requires libraqm.
:param spacing: The vertical gap between lines, defaulting to 4 pixels.
:param features: A list of OpenType font features to be used during text
layout. This is usually used to turn on optional
font features that are not enabled by default,
for example 'dlig' or 'ss01', but can be also
used to turn off default font features for
example '-liga' to disable ligatures or '-kern'
to disable kerning. To get all supported
features, see
https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist
Requires libraqm.
:param language: Language of the text. Different languages may use
different glyph shapes or ligatures. This parameter tells
the font which language the text is in, and to apply the
correct substitutions as appropriate, if available.
It should be a `BCP 47 language code
<https://www.w3.org/International/articles/language-tags/>`_
Requires libraqm.
.. versionadded:: 6.0.0
:param stroke_width: The width of the text stroke.
.. versionadded:: 6.2.0
:return: (width, height)
"""
deprecate("getsize_multiline", 10, "ImageDraw.multiline_textbbox")
max_width = 0
lines = self._multiline_split(text)
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
line_spacing = self.getsize("A", stroke_width=stroke_width)[1] + spacing
for line in lines:
line_width, line_height = self.getsize(
line, direction, features, language, stroke_width
)
max_width = max(max_width, line_width)
return max_width, len(lines) * line_spacing - spacing
def getoffset(self, text):
"""
.. deprecated:: 9.2.0
Use :py:meth:`.getbbox` instead.
See :ref:`deprecations <Font size and offset methods>` for more information.
Returns the offset of given text. This is the gap between the
starting coordinate and the first marking. Note that this gap is
included in the result of :py:func:`~PIL.ImageFont.FreeTypeFont.getsize`.
:param text: Text to measure.
:return: A tuple of the x and y offset
"""
deprecate("getoffset", 10, "getbbox")
return self.font.getsize(text)[1]
def getmask(
self,
text,
@ -665,7 +474,6 @@ class FreeTypeFont:
self,
text,
mode="",
fill=_UNSPECIFIED,
direction=None,
features=None,
language=None,
@ -691,12 +499,6 @@ class FreeTypeFont:
.. versionadded:: 1.1.5
:param fill: Optional fill function. By default, an internal Pillow function
will be used.
Deprecated. This parameter will be removed in Pillow 10
(2023-07-01).
:param direction: Direction of the text. It can be 'rtl' (right to
left), 'ltr' (left to right) or 'ttb' (top to bottom).
Requires libraqm.
@ -749,10 +551,6 @@ class FreeTypeFont:
:py:mod:`PIL.Image.core` interface module, and the text offset, the
gap between the starting coordinate and the first marking
"""
if fill is _UNSPECIFIED:
fill = Image.core.fill
else:
deprecate("fill", 10)
size, offset = self.font.getsize(
text, mode, direction, features, language, anchor
)
@ -761,7 +559,7 @@ class FreeTypeFont:
size = tuple(math.ceil(size[i] + stroke_width * 2 + start[i]) for i in range(2))
offset = offset[0] - stroke_width, offset[1] - stroke_width
Image._decompression_bomb_check(size)
im = fill("RGBA" if mode == "RGBA" else "L", size, 0)
im = Image.core.fill("RGBA" if mode == "RGBA" else "L", size, 0)
if min(size):
self.font.render(
text,
@ -876,22 +674,6 @@ class TransposedFont:
self.font = font
self.orientation = orientation # any 'transpose' argument, or None
def getsize(self, text, *args, **kwargs):
"""
.. deprecated:: 9.2.0
Use :py:meth:`.getbbox` or :py:meth:`.getlength` instead.
See :ref:`deprecations <Font size and offset methods>` for more information.
"""
deprecate("getsize", 10, "getbbox or getlength")
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
w, h = self.font.getsize(text)
if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270):
return h, w
return w, h
def getmask(self, text, mode="", *args, **kwargs):
im = self.font.getmask(text, mode, *args, **kwargs)
if self.orientation is not None:

View File

@ -19,7 +19,6 @@
import array
from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile
from ._deprecate import deprecate
class ImagePalette:
@ -34,16 +33,11 @@ class ImagePalette:
Defaults to an empty palette.
"""
def __init__(self, mode="RGB", palette=None, size=0):
def __init__(self, mode="RGB", palette=None):
self.mode = mode
self.rawmode = None # if set, palette contains raw data
self.palette = palette or bytearray()
self.dirty = None
if size != 0:
deprecate("The size parameter", 10, None)
if size != len(self.palette):
msg = "wrong palette size"
raise ValueError(msg)
@property
def palette(self):

View File

@ -20,14 +20,11 @@ import sys
from io import BytesIO
from . import Image
from ._deprecate import deprecate
from ._util import is_path
qt_versions = [
["6", "PyQt6"],
["side6", "PySide6"],
["5", "PyQt5"],
["side2", "PySide2"],
]
# If a version has already been imported, attempt it first
@ -40,16 +37,6 @@ for qt_version, qt_module in qt_versions:
elif qt_module == "PySide6":
from PySide6.QtCore import QBuffer, QIODevice
from PySide6.QtGui import QImage, QPixmap, qRgba
elif qt_module == "PyQt5":
from PyQt5.QtCore import QBuffer, QIODevice
from PyQt5.QtGui import QImage, QPixmap, qRgba
deprecate("Support for PyQt5", 10, "PyQt6 or PySide6")
elif qt_module == "PySide2":
from PySide2.QtCore import QBuffer, QIODevice
from PySide2.QtGui import QImage, QPixmap, qRgba
deprecate("Support for PySide2", 10, "PyQt6 or PySide6")
except (ImportError, RuntimeError):
continue
qt_is_installed = True

View File

@ -19,8 +19,6 @@ from shlex import quote
from PIL import Image
from ._deprecate import deprecate
_viewers = []
@ -111,21 +109,10 @@ class Viewer:
"""Display the given image."""
return self.show_file(self.save_image(image), **options)
def show_file(self, path=None, **options):
def show_file(self, path, **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:
deprecate("The 'file' argument", 10, "'path'")
path = options.pop("file")
else:
msg = "Missing required argument: 'path'"
raise TypeError(msg)
os.system(self.get_command(path, **options)) # nosec
return 1
@ -164,21 +151,10 @@ class MacViewer(Viewer):
command = f"({command} {quote(file)}; sleep 20; rm -f {quote(file)})&"
return command
def show_file(self, path=None, **options):
def show_file(self, path, **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:
deprecate("The 'file' argument", 10, "'path'")
path = options.pop("file")
else:
msg = "Missing required argument: 'path'"
raise TypeError(msg)
subprocess.call(["open", "-a", "Preview.app", path])
executable = sys.executable or shutil.which("python3")
if executable:
@ -215,21 +191,10 @@ class XDGViewer(UnixViewer):
command = executable = "xdg-open"
return command, executable
def show_file(self, path=None, **options):
def show_file(self, path, **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:
deprecate("The 'file' argument", 10, "'path'")
path = options.pop("file")
else:
msg = "Missing required argument: 'path'"
raise TypeError(msg)
subprocess.Popen(["xdg-open", path])
return 1
@ -246,20 +211,10 @@ class DisplayViewer(UnixViewer):
command += f" -title {quote(title)}"
return command, executable
def show_file(self, path=None, **options):
def show_file(self, path, **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:
deprecate("The 'file' argument", 10, "'path'")
path = options.pop("file")
else:
msg = "Missing required argument: 'path'"
raise TypeError(msg)
args = ["display"]
title = options.get("title")
if title:
@ -278,20 +233,10 @@ class GmDisplayViewer(UnixViewer):
command = "gm display"
return command, executable
def show_file(self, path=None, **options):
def show_file(self, path, **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:
deprecate("The 'file' argument", 10, "'path'")
path = options.pop("file")
else:
msg = "Missing required argument: 'path'"
raise TypeError(msg)
subprocess.Popen(["gm", "display", path])
return 1
@ -304,20 +249,10 @@ class EogViewer(UnixViewer):
command = "eog -n"
return command, executable
def show_file(self, path=None, **options):
def show_file(self, path, **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:
deprecate("The 'file' argument", 10, "'path'")
path = options.pop("file")
else:
msg = "Missing required argument: 'path'"
raise TypeError(msg)
subprocess.Popen(["eog", "-n", path])
return 1
@ -336,20 +271,10 @@ class XVViewer(UnixViewer):
command += f" -name {quote(title)}"
return command, executable
def show_file(self, path=None, **options):
def show_file(self, path, **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:
deprecate("The 'file' argument", 10, "'path'")
path = options.pop("file")
else:
msg = "Missing required argument: 'path'"
raise TypeError(msg)
args = ["xv"]
title = options.get("title")
if title:

View File

@ -29,7 +29,6 @@ import tkinter
from io import BytesIO
from . import Image
from ._deprecate import deprecate
# --------------------------------------------------------------------
# Check for Tkinter interface hooks
@ -162,7 +161,7 @@ class PhotoImage:
"""
return self.__size[1]
def paste(self, im, box=None):
def paste(self, im):
"""
Paste a PIL image into the photo image. Note that this can
be very slow if the photo image is displayed.
@ -170,13 +169,7 @@ class PhotoImage:
:param im: A PIL image. The size must match the target region. If the
mode does not match, the image is converted to the mode of
the bitmap image.
:param box: Deprecated. This parameter will be removed in Pillow 10
(2023-07-01).
"""
if box is not None:
deprecate("The box parameter", 10, None)
# convert to blittable
im.load()
image = im.im

View File

@ -46,7 +46,6 @@ from ._binary import i16be as i16
from ._binary import i32be as i32
from ._binary import o8
from ._binary import o16be as o16
from ._deprecate import deprecate
from .JpegPresets import presets
#
@ -612,11 +611,6 @@ samplings = {
# fmt: on
def convert_dict_qtables(qtables):
deprecate("convert_dict_qtables", 10, action="Conversion is no longer needed")
return qtables
def get_sampling(im):
# There's no subsampling when images have only 1 layer
# (grayscale images) or when they are CMYK (4 layers),

View File

@ -66,9 +66,6 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
self._n_frames = len(self.images)
self.is_animated = self._n_frames > 1
if len(self.images) > 1:
self._category = Image.CONTAINER
self.seek(0)
def seek(self, frame):

View File

@ -45,7 +45,6 @@ from ._binary import i32be as i32
from ._binary import o8
from ._binary import o16be as o16
from ._binary import o32be as o32
from ._deprecate import deprecate
logger = logging.getLogger(__name__)
@ -131,17 +130,6 @@ class Blend(IntEnum):
"""
def __getattr__(name):
for enum, prefix in {Disposal: "APNG_DISPOSE_", Blend: "APNG_BLEND_"}.items():
if name.startswith(prefix):
name = name[len(prefix) :]
if name in enum.__members__:
deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}")
return enum[name]
msg = f"module '{__name__}' has no attribute '{name}'"
raise AttributeError(msg)
def _safe_zlib_decompress(s):
dobj = zlib.decompressobj()
plaintext = dobj.decompress(s, MAX_TEXT_CHUNK)

View File

@ -31,7 +31,6 @@ _plugins = [
"DdsImagePlugin",
"EpsImagePlugin",
"FitsImagePlugin",
"FitsStubImagePlugin",
"FliImagePlugin",
"FpxImagePlugin",
"FtexImagePlugin",

View File

@ -45,8 +45,6 @@ def deprecate(
elif when <= int(__version__.split(".")[0]):
msg = f"{deprecated} {is_} deprecated and should be removed."
raise RuntimeError(msg)
elif when == 10:
removed = "Pillow 10 (2023-07-01)"
elif when == 11:
removed = "Pillow 11 (2024-10-15)"
else:

View File

@ -4,8 +4,6 @@ import sys
import tkinter
from tkinter import _tkinter as tk
from ._deprecate import deprecate
try:
if hasattr(sys, "pypy_find_executable"):
TKINTER_LIB = tk.tklib_cffi.__file__
@ -17,7 +15,3 @@ except AttributeError:
TKINTER_LIB = None
tk_version = str(tkinter.TkVersion)
if tk_version == "8.4":
deprecate(
"Support for Tk/Tcl 8.4", 10, action="Please upgrade to Tk/Tcl 8.5 or newer"
)

View File

@ -119,17 +119,7 @@ typedef struct Tk_PhotoImageBlock {
} Tk_PhotoImageBlock;
/* Typedefs derived from function signatures in Tk header */
/* Tk_PhotoPutBlock for Tk <= 8.4 */
typedef void (*Tk_PhotoPutBlock_84_t)(
Tk_PhotoHandle handle,
Tk_PhotoImageBlock *blockPtr,
int x,
int y,
int width,
int height,
int compRule);
/* Tk_PhotoPutBlock for Tk >= 8.5 */
typedef int (*Tk_PhotoPutBlock_85_t)(
typedef int (*Tk_PhotoPutBlock_t)(
Tcl_Interp *interp,
Tk_PhotoHandle handle,
Tk_PhotoImageBlock *blockPtr,
@ -138,8 +128,6 @@ typedef int (*Tk_PhotoPutBlock_85_t)(
int width,
int height,
int compRule);
/* Tk_PhotoSetSize for Tk <= 8.4 */
typedef void (*Tk_PhotoSetSize_84_t)(Tk_PhotoHandle handle, int width, int height);
/* Tk_FindPhoto */
typedef Tk_PhotoHandle (*Tk_FindPhoto_t)(Tcl_Interp *interp, const char *imageName);
/* Tk_PhotoGetImage */

View File

@ -48,14 +48,11 @@
* Global vars for Tcl / Tk functions. We load these symbols from the tkinter
* extension module or loaded Tcl / Tk libraries at run-time.
*/
static int TK_LT_85 = 0;
static Tcl_CreateCommand_t TCL_CREATE_COMMAND;
static Tcl_AppendResult_t TCL_APPEND_RESULT;
static Tk_FindPhoto_t TK_FIND_PHOTO;
static Tk_PhotoGetImage_t TK_PHOTO_GET_IMAGE;
static Tk_PhotoPutBlock_84_t TK_PHOTO_PUT_BLOCK_84;
static Tk_PhotoSetSize_84_t TK_PHOTO_SET_SIZE_84;
static Tk_PhotoPutBlock_85_t TK_PHOTO_PUT_BLOCK_85;
static Tk_PhotoPutBlock_t TK_PHOTO_PUT_BLOCK;
static Imaging
ImagingFind(const char *name) {
@ -130,26 +127,15 @@ PyImagingPhotoPut(
block.pitch = im->linesize;
block.pixelPtr = (unsigned char *)im->block;
if (TK_LT_85) { /* Tk 8.4 */
TK_PHOTO_PUT_BLOCK_84(
photo, &block, 0, 0, block.width, block.height, TK_PHOTO_COMPOSITE_SET);
if (strcmp(im->mode, "RGBA") == 0) {
/* Tk workaround: we need apply ToggleComplexAlphaIfNeeded */
/* (fixed in Tk 8.5a3) */
TK_PHOTO_SET_SIZE_84(photo, block.width, block.height);
}
} else {
/* Tk >=8.5 */
TK_PHOTO_PUT_BLOCK_85(
interp,
photo,
&block,
0,
0,
block.width,
block.height,
TK_PHOTO_COMPOSITE_SET);
}
TK_PHOTO_PUT_BLOCK(
interp,
photo,
&block,
0,
0,
block.width,
block.height,
TK_PHOTO_COMPOSITE_SET);
return TCL_OK;
}
@ -290,16 +276,7 @@ get_tk(HMODULE hMod) {
if ((TK_FIND_PHOTO = (Tk_FindPhoto_t)_dfunc(hMod, "Tk_FindPhoto")) == NULL) {
return -1;
};
TK_LT_85 = GetProcAddress(hMod, "Tk_PhotoPutBlock_Panic") == NULL;
/* Tk_PhotoPutBlock_Panic defined as of 8.5.0 */
if (TK_LT_85) {
TK_PHOTO_PUT_BLOCK_84 = (Tk_PhotoPutBlock_84_t)func;
return ((TK_PHOTO_SET_SIZE_84 =
(Tk_PhotoSetSize_84_t)_dfunc(hMod, "Tk_PhotoSetSize")) == NULL)
? -1
: 1;
}
TK_PHOTO_PUT_BLOCK_85 = (Tk_PhotoPutBlock_85_t)func;
TK_PHOTO_PUT_BLOCK = (Tk_PhotoPutBlock_t)func;
return 1;
}
@ -422,18 +399,9 @@ _func_loader(void *lib) {
if ((TK_FIND_PHOTO = (Tk_FindPhoto_t)_dfunc(lib, "Tk_FindPhoto")) == NULL) {
return 1;
}
/* Tk_PhotoPutBlock_Panic defined as of 8.5.0 */
TK_LT_85 = (dlsym(lib, "Tk_PhotoPutBlock_Panic") == NULL);
if (TK_LT_85) {
return (
((TK_PHOTO_PUT_BLOCK_84 =
(Tk_PhotoPutBlock_84_t)_dfunc(lib, "Tk_PhotoPutBlock")) == NULL) ||
((TK_PHOTO_SET_SIZE_84 =
(Tk_PhotoSetSize_84_t)_dfunc(lib, "Tk_PhotoSetSize")) == NULL));
}
return (
(TK_PHOTO_PUT_BLOCK_85 =
(Tk_PhotoPutBlock_85_t)_dfunc(lib, "Tk_PhotoPutBlock")) == NULL);
(TK_PHOTO_PUT_BLOCK =
(Tk_PhotoPutBlock_t)_dfunc(lib, "Tk_PhotoPutBlock")) == NULL);
}
int

11
tox.ini
View File

@ -1,8 +1,8 @@
[tox]
minversion = 1.9
envlist =
lint
py{py3, 311, 310, 39, 38, 37}
minversion = 1.9
py{py3, 311, 310, 39, 38}
[testenv]
deps =
@ -15,15 +15,16 @@ commands =
{envpython} -m pip install --global-option="build_ext" --global-option="--inplace" .
{envpython} selftest.py
{envpython} -m pytest -W always {posargs}
allowlist_externals = make
allowlist_externals =
make
[testenv:lint]
passenv =
PRE_COMMIT_COLOR
skip_install = true
deps =
check-manifest
pre-commit
passenv =
PRE_COMMIT_COLOR
commands =
pre-commit run --all-files --show-diff-on-failure
check-manifest

View File

@ -18,7 +18,7 @@ The following is a simplified version of the script used on AppVeyor:
```
set PYTHON=C:\Python38\bin
cd /D C:\Pillow\winbuild
C:\Python37\bin\python.exe build_prepare.py -v --depends=C:\pillow-depends
C:\Python39\bin\python.exe build_prepare.py -v --depends=C:\pillow-depends
build\build_dep_all.cmd
build\build_pillow.cmd install
cd ..

View File

@ -112,7 +112,7 @@ The following is a simplified version of the script used on AppVeyor::
set PYTHON=C:\Python38\bin
cd /D C:\Pillow\winbuild
C:\Python37\bin\python.exe build_prepare.py -v --depends C:\pillow-depends
C:\Python39\bin\python.exe build_prepare.py -v --depends C:\pillow-depends
build\build_dep_all.cmd
build\build_pillow.cmd install
cd ..