Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add append_images support for ICO #4568

Merged
merged 9 commits into from Dec 24, 2020
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions Tests/test_file_ico.py
Expand Up @@ -85,6 +85,20 @@ def test_only_save_relevant_sizes(tmp_path):
assert im_saved.info["sizes"] == {(16, 16), (24, 24), (32, 32), (48, 48)}


def test_save_append_images(tmp_path):
# append_images should be used for scaled down versions of the image
im = hopper("RGBA")
provided_im = Image.new("RGBA", (32, 32), (255, 0, 0))
outfile = str(tmp_path / "temp_saved_multi_icon.ico")
im.save(outfile, sizes=[(32, 32), (128, 128)], append_images=[provided_im])

with Image.open(outfile) as reread:
assert_image_equal(reread, hopper("RGBA"))

reread.size = (32, 32)
assert_image_equal(reread, provided_im)


def test_unexpected_size():
# This image has been manually hexedited to state that it is 16x32
# while the image within is still 16x16
Expand Down
13 changes: 11 additions & 2 deletions docs/handbook/image-file-formats.rst
Expand Up @@ -127,8 +127,8 @@ following options are available::
images in the list can be single or multiframe images.
This is currently supported for GIF, PDF, TIFF, and WebP.

It is also supported for ICNS. If images are passed in of relevant sizes,
they will be used instead of scaling down the main image.
It is also supported for ICO and ICNS. If images are passed in of relevant
sizes, they will be used instead of scaling down the main image.

**include_color_table**
Whether or not to include local color table.
Expand Down Expand Up @@ -238,6 +238,15 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
(64, 64), (128, 128), (256, 256)]``. Any sizes bigger than the original
size or 256 will be ignored.

The :py:meth:`~PIL.Image.Image.save` method can take the following keyword arguments:

**append_images**
A list of images to replace the scaled down versions of the image.
The order of the images does not matter, as their use is determined by
the size of each image.

.. versionadded:: 8.1.0

IM
^^

Expand Down
9 changes: 6 additions & 3 deletions src/PIL/IcoImagePlugin.py
Expand Up @@ -52,6 +52,7 @@ def _save(im, fp, filename):
sizes = list(sizes)
fp.write(struct.pack("<H", len(sizes))) # idCount(2)
offset = fp.tell() + len(sizes) * 16
provided_images = {im.size: im for im in im.encoderinfo.get("append_images", [])}
for size in sizes:
width, height = size
# 0 means 256
Expand All @@ -63,9 +64,11 @@ def _save(im, fp, filename):
fp.write(struct.pack("<H", 32)) # wBitCount(2)

image_io = BytesIO()
# TODO: invent a more convenient method for proportional scalings
tmp = im.copy()
tmp.thumbnail(size, Image.LANCZOS, reducing_gap=None)
tmp = provided_images.get(size)
if not tmp:
# TODO: invent a more convenient method for proportional scalings
tmp = im.copy()
tmp.thumbnail(size, Image.LANCZOS, reducing_gap=None)
tmp.save(image_io, "png")
image_io.seek(0)
image_bytes = image_io.read()
Expand Down