Pillow
| Language | Python 3 |
|---|---|
| License | MIT-CMU (permissive) |
| Repository | github.com/python-pillow/Pillow |
| Documentation | ICO in the Pillow image file formats docs |
| Depends on | nothing to install separately (wheels include the native code) |
| Read / write | ICO yes / yes (PNG or BMP entries); CUR read only |
Pillow opens ICO files like any other image. Image.open loads the largest entry by default; im.info["sizes"] lists every size in the file and setting im.size before loading picks one. When saving as ICO, Pillow scales the image to each size you ask for and stores the entries as PNG (or BMP with bitmap_format="bmp").
CUR files open read-only (the largest cursor; animated .ani cursors aren’t supported). Pillow doesn’t return the cursor hotspot.
Installation#
pip install pillow
Basic usage: convert ICO to PNG#
from PIL import Image
with Image.open("app.ico") as im:
print(im.size, im.mode) # largest entry, e.g. (256, 256) RGBA
print(sorted(im.info["sizes"])) # every size in the file
im.save("app.png")
How to open a specific icon size#
from PIL import Image
with Image.open("app.ico") as im:
if (32, 32) in im.info["sizes"]:
im.size = (32, 32) # must be set before the image is loaded
im.load()
im.save("app-32.png")
im.ico.getimage((16, 16)) returns a new image for one size, and im.ico.frame(i) returns entry i (Pillow sorts entries from largest to smallest).
How to create a multi-size favicon.ico#
Save one large square image with the sizes you want. Pillow resizes it for each entry and skips sizes larger than the source:
from PIL import Image
with Image.open("logo.png") as logo:
logo = logo.convert("RGBA")
logo.save("favicon.ico", sizes=[(16, 16), (32, 32), (48, 48)])
logo.save("app.ico", sizes=[(16, 16), (24, 24), (32, 32), (48, 48), (64, 64), (256, 256)])
Without sizes, Pillow writes 16, 24, 32, 48, 64, 128 and 256 (up to the source size).
How to use hand-drawn small sizes#
append_images replaces the scaled-down versions with your own images of the same size:
from PIL import Image
big = Image.open("icon256.png")
small = [Image.open(f) for f in ("icon16.png", "icon32.png", "icon48.png")]
big.save("app.ico", sizes=[(16, 16), (32, 32), (48, 48), (256, 256)], append_images=small)
How to write BMP entries instead of PNG#
PNG entries are smaller but need Windows Vista or later. For the widest compatibility:
logo.save("app.ico", sizes=[(16, 16), (32, 32), (48, 48)], bitmap_format="bmp")
Tips#
- Convert to
RGBAbefore saving so the icon keeps transparency. - Entries are limited to 256x256; larger
sizesvalues are ignored.