Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
18 changes: 18 additions & 0 deletions Tests/test_file_ico.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,24 @@ def test_only_save_relevant_sizes(tmp_path):
assert im_saved.info["sizes"] == {(16, 16), (24, 24), (32, 32), (48, 48)}


def test_only_save_append_images(tmp_path):
"""append_images should work to provide alternative sizes"""
im = hopper()
provided_im = Image.new("RGBA", (32, 32), (255, 0, 0, 255))
outfile = str(tmp_path / "temp_saved_multi_icon.ico")
im.save(outfile, sizes=[(32, 32), (64, 64)], append_images=[provided_im])

with Image.open(outfile) as reread:
reread.size = (64, 64)
reread.load()
assert_image_equal(reread, hopper().resize((64, 64), Image.LANCZOS))

with Image.open(outfile) as reread:
reread.size = (32, 32)
reread.load()
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
10 changes: 7 additions & 3 deletions src/PIL/IcoImagePlugin.py
Original file line number Diff line number Diff line change
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
alt_images = {im.size: im for im in im.encoderinfo.get("append_images", [])}
for size in sizes:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like if someone were to add an unusual sized image to append_images (e.g. (20,20)), it would not be saved unless it is added to the sizes parameter as well. I feel like this could be confusing behaviour. Perhaps adding sizes.extend(alt_images.keys()) would be a good idea (sizes would have to be changed to a set to avoid duplicates).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is correct. I believe the ICNS saving currently suffers from the same issue. I don't know if it should be fixed for .ICOs; the documentation would need to mention the role of append_images either way, since if the behavior is changed as you suggest, it would then also save other sizes than the ones the user requested (via the sizes parameter).

width, height = size
# 0 means 256
Expand All @@ -63,9 +64,12 @@ 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)
if size in alt_images:
tmp = alt_images[size]
else:
# 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