diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index c7e63fbe548..5fce20ff1ba 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -132,6 +132,20 @@ def test_contain(new_size: tuple[int, int]) -> None: assert new_im.size == (256, 256) +@pytest.mark.parametrize("source_size", ((100, 1), (1, 100), (20, 1), (1, 20))) +def test_contain_minimum_dimension(source_size: tuple[int, int]) -> None: + im = Image.new("RGB", source_size, "red") + result = ImageOps.contain(im, (10, 10)) + expected_size = (10, 1) if source_size[0] > source_size[1] else (1, 10) + assert result.size == expected_size + assert result.getpixel((0, 0)) == (255, 0, 0) + + padded = ImageOps.pad(im, (10, 10), color="blue") + assert padded.size == (10, 10) + assert padded.getpixel((4, 4)) == (255, 0, 0) + assert padded.getpixel((0, 0)) == (0, 0, 255) + + def test_contain_round() -> None: im = Image.new("1", (43, 63), 1) new_im = ImageOps.contain(im, (5, 7)) diff --git a/docs/releasenotes/13.0.0.rst b/docs/releasenotes/13.0.0.rst index 46dbed3b58b..6e032ae65b0 100644 --- a/docs/releasenotes/13.0.0.rst +++ b/docs/releasenotes/13.0.0.rst @@ -126,6 +126,14 @@ Two new filters are available for :py:meth:`~PIL.Image.Image.resize` and Other changes ============= +Keep resized dimensions positive in ImageOps.contain() +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:py:func:`~PIL.ImageOps.contain` now keeps the calculated width or height at a +minimum of one pixel. Previously, resizing a very narrow image could round one +dimension to zero and raise ``ValueError``. This also fixes +:py:func:`~PIL.ImageOps.pad` for these images. + Python 3.15 ^^^^^^^^^^^ diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 94964e12156..b04cd65423c 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)