Skip to content

Generated icon file doesn't contain multiple icons #9340

@RolandKaechele

Description

@RolandKaechele

It seems there is no support for multiple icons in one icon file

import os
from optparse import OptionParser
from PIL import Image

def main():
    parser = OptionParser(usage="usage: %prog [options] INPUTFILE")
    parser.add_option("-f", "--format", dest="output_format", default="ico",
                      help="Output image format (default: ico)")
    parser.add_option("-o", "--output", dest="output_file", help="Output file path (optional)")
    parser.add_option("--debug", action="store_true", dest="debug", default=False,
                      help="Extract and save all icons from the generated .ico file for debugging.")

    (options, args) = parser.parse_args()

    if not args:
        parser.print_help()
        parser.error("Input file is required as the last argument.")

    input_file = args[-1]
    output_format = options.output_format.lower()
    output_file = options.output_file

    if not os.path.isfile(input_file):
        parser.error(f"Input file '{input_file}' does not exist.")

    if not output_file:
        base, _ = os.path.splitext(input_file)
        output_file = f"{base}.{output_format}"
    base, _ = os.path.splitext(output_file)

    if os.path.isfile(output_file):
        answer = input(f"Output file '{output_file}' already exists. Overwrite? [y/n]: ").strip().lower()
        if answer != "y" and answer != "yes":
            print("Aborted by user.")
            return

    try:
        with Image.open(input_file) as img:
            if output_format == "ico":
                sizes = [(16, 16), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)]
                img = img.convert("RGBA")
                # Create resized images for each size
                resized_imgs = [img.resize(size, Image.LANCZOS) for size in sizes]
                # Save a multi-icon .ico file containing all sizes
                multi_icon_path = f"{base}.ico"
                resized_imgs[0].save(multi_icon_path, format="ICO", sizes=sizes)
                print(f"Saved multi-size icon: {multi_icon_path}")
                # Save individual ICO files for each size
                for i, size in enumerate(sizes):
                    out_path = f"{base}_{size[0]}x{size[1]}.ico"
                    resized_imgs[i].save(out_path, format="ICO", sizes=[size])
                    print(f"Saved {out_path}")

                # Debug: extract all icons from the generated .ico file
                if options.debug:
                    with Image.open(multi_icon_path) as ico_img:
                        try:
                            i = 0
                            extracted = set()
                            while True:
                                try:
                                    ico_img.seek(i)
                                    size = ico_img.size
                                    out_path = f"{base}_extracted_{size[0]}x{size[1]}_{i}.png"
                                    ico_img.save(out_path, format="PNG")
                                    print(f"Extracted {out_path}")
                                    extracted.add(size)
                                    i += 1
                                except EOFError:
                                    break
                            if len(extracted) == 1:
                                print("Warning: Only one icon size was extracted from the .ico file. "
                                      "This may indicate a limitation in Pillow or the input image. "
                                      "Try updating Pillow or using a square input image of at least 256x256.")
                        except Exception as e:
                            print(f"Error extracting icons: {e}")
            else:
                img.save(output_file, format=output_format.upper())
                print(f"Converted '{input_file}' to '{output_file}' as format '{output_format}'.")
    except Exception as e:
        print(f"Error: {e}")

if __name__ == "__main__":
    main()
Warning: Only one icon size was extracted from the .ico file. This may indicate a limitation in Pillow or the input image. Try updating Pillow or using a square input image of at least 256x256.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions