From 2c9a9b35293382882dcd58cb208006910c2d7966 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 27 May 2021 06:21:28 +1000 Subject: [PATCH 1/2] Added ICO saving in BMP format --- Tests/test_file_ico.py | 29 ++++++++++++++++++++++++++++ docs/handbook/image-file-formats.rst | 6 ++++++ src/PIL/IcoImagePlugin.py | 21 +++++++++++++++++--- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/Tests/test_file_ico.py b/Tests/test_file_ico.py index 7b9e4e698..8060d1b76 100644 --- a/Tests/test_file_ico.py +++ b/Tests/test_file_ico.py @@ -56,6 +56,35 @@ def test_save_to_bytes(): assert_image_equal(reloaded, hopper().resize((32, 32), Image.LANCZOS)) +@pytest.mark.parametrize("mode", ("1", "L", "P", "RGB", "RGBA")) +def test_save_to_bytes_bmp(mode): + output = io.BytesIO() + im = hopper(mode) + im.save(output, "ico", bitmap_format="bmp", sizes=[(32, 32), (64, 64)]) + + # The default image + output.seek(0) + with Image.open(output) as reloaded: + assert reloaded.info["sizes"] == {(32, 32), (64, 64)} + + assert "RGBA" == reloaded.mode + assert (64, 64) == reloaded.size + assert reloaded.format == "ICO" + im = hopper(mode).resize((64, 64), Image.LANCZOS).convert("RGBA") + assert_image_equal(reloaded, im) + + # The other one + output.seek(0) + with Image.open(output) as reloaded: + reloaded.size = (32, 32) + + assert "RGBA" == reloaded.mode + assert (32, 32) == reloaded.size + assert reloaded.format == "ICO" + im = hopper(mode).resize((32, 32), Image.LANCZOS).convert("RGBA") + assert_image_equal(reloaded, im) + + def test_incorrect_size(): with Image.open(TEST_ICO_FILE) as im: with pytest.raises(ValueError): diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index ea40ef6d7..b990e9cc0 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -247,6 +247,12 @@ The :py:meth:`~PIL.Image.Image.save` method can take the following keyword argum .. versionadded:: 8.1.0 +**bitmap_format** + By default, the image data will be saved in PNG format. With a bitmap format of + "bmp", image data will be saved in BMP format instead. + + .. versionadded:: 8.3.0 + IM ^^ diff --git a/src/PIL/IcoImagePlugin.py b/src/PIL/IcoImagePlugin.py index dbc108d7b..ffb1e873d 100644 --- a/src/PIL/IcoImagePlugin.py +++ b/src/PIL/IcoImagePlugin.py @@ -30,6 +30,7 @@ from math import ceil, log from . import BmpImagePlugin, Image, ImageFile, PngImagePlugin from ._binary import i16le as i16 from ._binary import i32le as i32 +from ._binary import o32le as o32 # # -------------------------------------------------------------------- @@ -53,6 +54,7 @@ def _save(im, fp, filename): sizes = list(sizes) fp.write(struct.pack(" Date: Sun, 6 Jun 2021 21:34:01 +1000 Subject: [PATCH 2/2] Added release notes [ci skip] --- docs/releasenotes/8.3.0.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/releasenotes/8.3.0.rst b/docs/releasenotes/8.3.0.rst index a4b8cb88c..412f5a2e7 100644 --- a/docs/releasenotes/8.3.0.rst +++ b/docs/releasenotes/8.3.0.rst @@ -50,6 +50,14 @@ To compare it to other ImageOps methods: does not fill the extra space. Instead, the original aspect ratio is maintained. So unlike the other two methods, it is not guaranteed to return an image of ``size``. +ICO saving: bitmap_format argument +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +By default, Pillow saves ICO files in the PNG format. They can now also be saved in BMP +format, through the new ``bitmap_format`` argument:: + + im.save("out.ico", bitmap_format="bmp") + Security ========