Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 31 additions & 22 deletions Tests/test_file_palm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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"))
Expand Down
14 changes: 14 additions & 0 deletions docs/handbook/image-file-formats.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
^^^

Expand Down
4 changes: 1 addition & 3 deletions src/PIL/PalmImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Expand Down
Loading