From 49e5731f20fb511e7d98f2a88b8148db9dafac7a Mon Sep 17 00:00:00 2001 From: ANSHUL SINGH <72524975+ekanshul@users.noreply.github.com> Date: Tue, 29 Sep 2026 17:38:27 +0000 Subject: [PATCH] Keep ImageOps.contain from rounding thin images to zero contain() uses round() when scaling the shorter side. For a 100x1 image into a 10x10 box that becomes 0, and resize() raises ValueError. Clamp the calculated dimension to at least one pixel so contain() and pad() succeed. Fixes #9997 --- Tests/test_imageops.py | 16 ++++++++++++++++ docs/releasenotes/13.0.0.rst | 7 +++++++ src/PIL/ImageOps.py | 4 ++-- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 4a4826d22e3..8dc1854c983 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -142,6 +142,22 @@ def test_contain_round() -> None: assert new_im.height == 5 +def test_contain_narrow_image() -> None: + im = Image.new("RGB", (100, 1), "red") + new_im = ImageOps.contain(im, (10, 10)) + assert new_im.size == (10, 1) + + im = Image.new("RGB", (1, 100), "red") + new_im = ImageOps.contain(im, (10, 10)) + assert new_im.size == (1, 10) + + +def test_pad_narrow_image() -> None: + im = Image.new("RGB", (100, 1), "red") + new_im = ImageOps.pad(im, (10, 10)) + assert new_im.size == (10, 10) + + @pytest.mark.parametrize( "image_name, expected_size", ( diff --git a/docs/releasenotes/13.0.0.rst b/docs/releasenotes/13.0.0.rst index b9906b7f19d..687b8f41bbd 100644 --- a/docs/releasenotes/13.0.0.rst +++ b/docs/releasenotes/13.0.0.rst @@ -228,3 +228,10 @@ position or contents. :py:meth:`~PIL.Image.Image.tobytes` also now explicitly cleans up its encoder on both success and failure, rather than relying on object destruction. + +Fixed ImageOps.contain() for very thin images +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:py:func:`~PIL.ImageOps.contain` and :py:func:`~PIL.ImageOps.pad` no longer raise +``ValueError`` when a very thin image rounds the shorter side to zero pixels. The +calculated dimension is now kept at least one pixel. diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 593f801b031..7d74a188289 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -300,11 +300,11 @@ def contain( if im_ratio != dest_ratio: if im_ratio > dest_ratio: - new_height = round(image.height / image.width * size[0]) + new_height = max(1, round(image.height / image.width * size[0])) if new_height != size[1]: size = (size[0], new_height) else: - new_width = round(image.width / image.height * size[1]) + new_width = max(1, round(image.width / image.height * size[1])) if new_width != size[0]: size = (new_width, size[1]) return image.resize(size, resample=method)