Skip to content

Commit a891167

Browse files
authored
Merge branch 'main' into raqm-sheenbidi
2 parents a7fe4d9 + d7c8c4f commit a891167

8 files changed

Lines changed: 80 additions & 10 deletions

File tree

Tests/test_file_jpeg.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,8 @@ def test_restart_markers(self, blocks: int, rows: int, markers: int) -> None:
734734
def test_load_djpeg(self) -> None:
735735
with Image.open(TEST_FILE) as img:
736736
assert isinstance(img, JpegImagePlugin.JpegImageFile)
737-
img.load_djpeg()
737+
with pytest.warns(DeprecationWarning, match="load_djpeg"):
738+
img.load_djpeg()
738739
assert_image_similar_tofile(img, TEST_FILE, 5)
739740

740741
def test_no_duplicate_0x1001_tag(self) -> None:

Tests/test_file_png.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,34 @@ def test_truncated_chunks(
717717
monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
718718
png.call(cid, 0, 0)
719719

720+
@pytest.mark.parametrize("mode", ("1", "L", "I;16", "RGB"))
721+
def test_truncated_trns_chunk(
722+
self, mode: str, monkeypatch: pytest.MonkeyPatch
723+
) -> None:
724+
fp = BytesIO()
725+
with PngImagePlugin.PngStream(fp) as png:
726+
png.im_mode = mode
727+
with pytest.raises(ValueError, match="Truncated tRNS chunk"):
728+
png.call(b"tRNS", 0, 0)
729+
730+
monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
731+
png.call(b"tRNS", 0, 0)
732+
733+
def test_truncated_trns_chunk_in_file(
734+
self, monkeypatch: pytest.MonkeyPatch
735+
) -> None:
736+
# HEAD declares a truecolour image, so tRNS must carry 6 bytes
737+
data = HEAD + chunk(b"tRNS", bytes(4)) + TAIL
738+
739+
with pytest.raises(ValueError, match="Truncated tRNS chunk"):
740+
with Image.open(BytesIO(data)):
741+
pass
742+
743+
monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
744+
with Image.open(BytesIO(data)) as im:
745+
assert im.mode == "RGB"
746+
assert "transparency" not in im.info
747+
720748
@pytest.mark.parametrize("save_all", (True, False))
721749
def test_specify_bits(self, save_all: bool, tmp_path: Path) -> None:
722750
im = hopper("P")

Tests/test_shell_injection.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ def test_load_djpeg_filename(self, tmp_path: Path) -> None:
4444

4545
with Image.open(src_file) as im:
4646
assert isinstance(im, JpegImagePlugin.JpegImageFile)
47-
im.load_djpeg()
47+
with pytest.warns(DeprecationWarning, match="load_djpeg"):
48+
im.load_djpeg()
4849

4950
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
5051
def test_save_netpbm_filename_bmp_mode(self, tmp_path: Path) -> None:

docs/deprecations.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,17 @@ fribidi and harfbuzz features
3939
own bidirectional text and shaping libraries when it is built, so Pillow cannot
4040
reliably report which ones are in use.
4141

42+
JpegImageFile.load_djpeg
43+
~~~~~~~~~~~~~~~~~~~~~~~~
44+
45+
.. deprecated:: 13.0.0
46+
47+
``JpegImageFile.load_djpeg`` has been deprecated, and will be removed in Pillow 14
48+
(2027-10-15).
49+
50+
Use the built-in JPEG decoder instead, or call ``djpeg`` directly and decode the
51+
resulting image with Pillow.
52+
4253
Removed features
4354
----------------
4455

docs/releasenotes/13.0.0.rst

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ and so child images could only be retrieved from an :py:class:`PIL.ImageFile.Ima
5555
instance.
5656

5757
Image._show
58-
~~~~~~~~~~~
58+
^^^^^^^^^^^
5959

6060
``Image._show`` has been removed. Use :py:meth:`~PIL.ImageShow.show` instead.
6161

@@ -90,6 +90,15 @@ raqm=vendor and fribidi=vendor config settings
9090
The ``-C raqm=vendor`` and ``-C fribidi=vendor`` build config settings are deprecated
9191
and ignored. Raqm is no longer bundled, so there is nothing to vendor.
9292

93+
JpegImageFile.load_djpeg
94+
^^^^^^^^^^^^^^^^^^^^^^^^
95+
96+
``JpegImageFile.load_djpeg`` has been deprecated, and will be removed in Pillow 14
97+
(2027-10-15).
98+
99+
Use the built-in JPEG decoder instead, or call ``djpeg`` directly and decode the
100+
resulting image with Pillow.
101+
93102
API changes
94103
===========
95104

@@ -110,6 +119,13 @@ namespace URI prefix as before. If set to ``False``, each tag's full
110119
``{namespace-uri}local-name`` form is kept instead, avoiding collisions between tags
111120
that share a local name across different namespaces.
112121

122+
New resizing filters
123+
^^^^^^^^^^^^^^^^^^^^
124+
125+
Two new filters are available for :py:meth:`~PIL.Image.Image.resize` and
126+
:py:meth:`~PIL.Image.Image.thumbnail`: ``Image.Resampling.MKS2013`` and
127+
``Image.Resampling.MKS2021``. These are versions of the Magic Kernel Sharp filter.
128+
113129
Other changes
114130
=============
115131

src/PIL/ImageGrab.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def grab(
7979
scale = 1 if scale_down else 2
8080
im_cropped = im.resize(
8181
((right - left) * scale, (bottom - top) * scale),
82-
box=tuple(coord * 2 for coord in bbox),
82+
box=(left * 2, top * 2, right * 2, bottom * 2),
8383
)
8484
else:
8585
im_cropped = im.crop(bbox)

src/PIL/JpegImagePlugin.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
from ._binary import i32be as i32
4949
from ._binary import o8
5050
from ._binary import o16be as o16
51+
from ._deprecate import deprecate
5152
from .JpegPresets import presets
5253

5354
TYPE_CHECKING = False
@@ -467,6 +468,11 @@ def draft(
467468

468469
def load_djpeg(self) -> None:
469470
# ALTERNATIVE: handle JPEGs via the IJG command line utilities
471+
deprecate(
472+
"load_djpeg",
473+
14,
474+
action="Use the built-in JPEG decoder instead, or call djpeg yourself.",
475+
)
470476

471477
f, path = tempfile.mkstemp()
472478
os.close(f)

src/PIL/PngImagePlugin.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -509,12 +509,19 @@ def chunk_tRNS(self, pos: int, length: int) -> bytes:
509509
# otherwise, we have a byte string with one alpha value
510510
# for each palette entry
511511
self.im_info["transparency"] = s
512-
elif self.im_mode == "1":
513-
self.im_info["transparency"] = 255 if i16(s) else 0
514-
elif self.im_mode in ("L", "I;16"):
515-
self.im_info["transparency"] = i16(s)
516-
elif self.im_mode == "RGB":
517-
self.im_info["transparency"] = i16(s), i16(s, 2), i16(s, 4)
512+
elif self.im_mode in ("1", "L", "I;16", "RGB"):
513+
# 2 bytes for greyscale, 6 for truecolour
514+
if length < (6 if self.im_mode == "RGB" else 2):
515+
if ImageFile.LOAD_TRUNCATED_IMAGES:
516+
return s
517+
msg = "Truncated tRNS chunk"
518+
raise ValueError(msg)
519+
if self.im_mode == "1":
520+
self.im_info["transparency"] = 255 if i16(s) else 0
521+
elif self.im_mode in ("L", "I;16"):
522+
self.im_info["transparency"] = i16(s)
523+
elif self.im_mode == "RGB":
524+
self.im_info["transparency"] = i16(s), i16(s, 2), i16(s, 4)
518525
return s
519526

520527
def chunk_gAMA(self, pos: int, length: int) -> bytes:

0 commit comments

Comments
 (0)