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.