Is there a Python library for generating .ico files?

Perhaps the following would work:

  • Generate your icon image using PIL
  • Convert the image to .ico format using the python interface to ImageMagick, PythonMagick

I have not tried this approach. The ImageMagick convert command line program was able to convert a .png file to .ico format, so at least ImageMagick supports the .ico format.


You can use Pillow:

from PIL import Image
filename = r'logo.png'
img = Image.open(filename)
img.save('logo.ico')

Optionally, you may specify the icon sizes you want:

icon_sizes = [(16,16), (32, 32), (48, 48), (64,64)]
img.save('logo.ico', sizes=icon_sizes)

The Pillow docs say that by default it will generate sizes [(16, 16), (24, 24), (32, 32), (48, 48), (64, 64), (128, 128), (255, 255)] and any size bigger than the original size or 255 will be ignored.

Yes, it is in the Read-only section of the docs, but it works to some extent.


Although this question is rather old, it's a prominent search result for using Python to convert PNG files to ICO, so I thought I'll add my two cents.

If all you want is a favicon, Douglas Leeder's answer seems perfectly fine to me. If you have one high-resolution PNG file of your logo and want to convert it to an ICO file, the answer of Ronan Paixão is probably the easiest way to go.

But an ICO file can contain multiple images, intended for different resolutions, and I often found myself in the situation of wanting to have fine-grained control over these different resolutions, to avoid unfortunate anti-aliasing effects, which means that I want to provide each image resolution individually. Which means that I want to convert not a single, but multiple PNG files into a single ICO file. As far as I can see, the Pillow package doesn't provide this capability. Fortunately, a modern ICO file can contain multiple PNG files inside, so the task boils down to the simple challenge of writing some header entries. Depending on the situation, I wrote two functions, the first one basically the solution of Ronan Paixão, while the second one provides the functionality to join several PNG files into one ICO file:

from pathlib import Path
from PIL import Image


def bake_one_big_png_to_ico(sourcefile, targetfile, sizes=None):
    """Converts one big PNG into one ICO file.

    args:
        sourcefile (str): Pathname of a PNG file.
        targetfile (str): Pathname of the resulting ICO file.
        sizes (list of int): Requested sizes of the resulting
            icon file, defaults to [16, 32, 48].

    Use this function if you have one big, square PNG file
    and don’t care about fine-tuning individual icon sizes.

    Example::

        sourcefile = "Path/to/high_resolution_logo_512x512.png"
        targetfile = "Path/to/logo.ico"
        sizes = [16, 24, 32, 48, 256]
        bake_one_big_png_to_ico(sourcefile, targetfile, sizes)
    """
    if sizes is None:
        sizes = [16, 32, 48]
    icon_sizes = [(x, x) for x in sizes]
    Image.open(sourcefile).save(targetfile, icon_sizes=icon_sizes)


def bake_several_pngs_to_ico(sourcefiles, targetfile):
    """Converts several PNG files into one ICO file.

    args:
        sourcefiles (list of str): A list of pathnames of PNG files.
        targetfile (str): Pathname of the resulting ICO file.

    Use this function if you want to have fine-grained control over
    the resulting icon file, providing each possible icon resolution
    individually.

    Example::

        sourcefiles = [
            "Path/to/logo_16x16.png",
            "Path/to/logo_32x32.png",
            "Path/to/logo_48x48.png"
        ]
        targetfile = "Path/to/logo.ico"
        bake_several_pngs_to_ico(sourcefiles, targetfile)
    """

    # Write the global header
    number_of_sources = len(sourcefiles)
    data = bytes((0, 0, 1, 0, number_of_sources, 0))
    offset = 6 + number_of_sources * 16

    # Write the header entries for each individual image
    for sourcefile in sourcefiles:
        img = Image.open(sourcefile)
        data += bytes((img.width, img.height, 0, 0, 1, 0, 32, 0, ))
        bytesize = Path(sourcefile).stat().st_size
        data += bytesize.to_bytes(4, byteorder="little")
        data += offset.to_bytes(4, byteorder="little")
        offset += bytesize

    # Write the individual image data
    for sourcefile in sourcefiles:
        data += Path(sourcefile).read_bytes()

    # Save the icon file
    Path(targetfile).write_bytes(data)

The code presupposes that your PNG files are 32-Bit-per-Pixel RGBA images. Otherwise, the number 32 in the above code would have to be changed and should be replaced with some Pillow-image-sniffing.


According to Wikipedia modern browsers can handle favicons in PNG format, so maybe you could just generate that?

Alternatively the ICO article describes the format...

Tags:

Python

Favicon