diff --git a/Tests/test_file_palm.py b/Tests/test_file_palm.py index ffac42e889d..f43a9ee2cff 100644 --- a/Tests/test_file_palm.py +++ b/Tests/test_file_palm.py @@ -7,11 +7,12 @@ from PIL import Image -from .helper import assert_image_equal, hopper, magick_command +from .helper import assert_image_equal, assert_image_similar, hopper, magick_command TYPE_CHECKING = False if TYPE_CHECKING: from pathlib import Path + from typing import Any def helper_save_as_palm(tmp_path: Path, mode: str) -> None: @@ -36,40 +37,48 @@ def open_with_magick(magick: list[str], tmp_path: Path, f: str) -> Image.Image: return Image.open(outfile) -def roundtrip(tmp_path: Path, mode: str) -> None: +def roundtrip(tmp_path: Path, im: Image.Image, **options: Any) -> Image.Image: magick = magick_command() if not magick: pytest.skip("ImageMagick not available") - im = hopper(mode) outfile = str(tmp_path / "temp.palm") - im.save(outfile) - converted = open_with_magick(magick, tmp_path, outfile) - if mode == "P": - assert converted.mode == "P" - - im = im.convert("RGB") - converted = converted.convert("RGB") - assert_image_equal(converted, im) + im.save(outfile, **options) + return open_with_magick(magick, tmp_path, outfile) def test_monochrome(tmp_path: Path) -> None: - # Arrange - mode = "1" + helper_save_as_palm(tmp_path, "1") - # Act / Assert - helper_save_as_palm(tmp_path, mode) - roundtrip(tmp_path, mode) + im = hopper("1") + assert_image_equal(roundtrip(tmp_path, im), im) def test_p_mode(tmp_path: Path) -> None: - # Arrange - mode = "P" - - # Act / Assert - helper_save_as_palm(tmp_path, mode) - roundtrip(tmp_path, mode) + helper_save_as_palm(tmp_path, "P") + + im = hopper("P") + converted = roundtrip(tmp_path, im) + assert converted.mode == "P" + assert_image_equal(converted.convert("RGB"), im.convert("RGB")) + + +@pytest.mark.parametrize("bpp", (1, 2, 4)) +@pytest.mark.parametrize("info", (True, False)) +def test_l_mode(tmp_path: Path, bpp: int, info: bool) -> None: + im = hopper("L") + if info: + reloaded = roundtrip(tmp_path, im, bpp=bpp) + else: + im.info["bpp"] = bpp + reloaded = roundtrip(tmp_path, im) + if bpp == 1: + assert reloaded.mode == "1" + else: + assert reloaded.mode == "L" + if info: + assert_image_similar(reloaded, im, 23 if bpp == 2 else 6) @pytest.mark.parametrize("mode", ("L", "RGB")) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 1cc01838909..518bb4faa8e 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -1788,6 +1788,20 @@ Pillow provides write-only support for PALM pixmap files. The format code is ``Palm``, the extension is ``.palm``. +``1``, ``L`` and ``P`` images can be saved. + +Saving +~~~~~~ + +The :py:meth:`~PIL.Image.Image.save` method can take the following keyword arguments: + +**bpp** + For ``L`` images, the number of bits per pixel to write, either 1, 2 or 4. Shades + in the image are adjusted to fit within this restriction. If omitted, the ``bpp`` + value from :py:attr:`~PIL.Image.Image.info` can be used instead, and only the + lowest bits of each pixel are kept. If there is no ``bpp`` setting at all, ``L`` + mode images cannot be saved. + PDF ^^^ diff --git a/src/PIL/PalmImagePlugin.py b/src/PIL/PalmImagePlugin.py index bf8e81a5f5e..8b4473892f0 100644 --- a/src/PIL/PalmImagePlugin.py +++ b/src/PIL/PalmImagePlugin.py @@ -143,9 +143,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: msg = f"cannot write mode {im.mode} as Palm" raise OSError(msg) - # we ignore the palette here - im._mode = "P" - rawmode = f"P;{bpp}" + rawmode = f"L;{bpp}" version = 1 elif im.mode == "1":