I have a folder of icon files (ico) that I want to convert to PNG. Each icon has two sizes, each size is a different image. To be clear, the two sizes look different and are not just the same icon at different resolutions. I want to save the smaller version of each icon.
The PIL documentation states:
ICO is used to store icons on Windows. The largest available icon is read.
How do I get PIL to access the smaller version of the icon and not the largest?
I've searched and I can find lots of ways to save the different sizes of an icon but not how to read / open the smaller versions of an icon. I did find one similar question but the answers did not work for me.
Edit:
Here is an example icon. The larger version has a folder in the background and the smaller version is just the bolt. I want to export only the small version.
I had a try at this and wrote some code to:
Code is as follows - there's a link describing the ICO format on Wikipedia here:
#!/usr/bin/env python3
# Mark Setchell
# https://en.wikipedia.org/wiki/ICO_(file_format)
import io
import struct
from PIL import Image
ICONDIRSIZE, ICONDIRENTRYSIZE = 6, 16
def extractIcons(filename):
"""Extract all icons from .ICO file, returning a list of PIL Images"""
# List of PIL Images to return
res = []
# Open and slurp entire file
d = open(filename, 'rb').read()
# Extract ICONDIR, and unpack
ICONDIR = d[:ICONDIRSIZE]
hdrReserved, hdrImageType, hdrNumImages = struct.unpack('<HHH', ICONDIR)
print(f'DEBUG: hdrReserved={hdrReserved}, hdrImageType={hdrImageType}, hdrNumImages={hdrNumImages}')
for i in range(hdrNumImages):
start = ICONDIRSIZE + i*ICONDIRENTRYSIZE
ICONDIRENTRY = d[start:start+ICONDIRENTRYSIZE]
width, height, palColours, reserved, planes, bpp, nBytes, offset = struct.unpack('<BBBBHHII', ICONDIRENTRY)
print(f'DEBUG: Entry:{i}, width={width}, height={height}, palColours={palColours}, reserved={reserved}, planes={planes}, bpp={bpp}, nBytes={nBytes}, offset={offset}')
# Make a new, in-memory ICO file with one single icon in it
hdr = struct.pack('<HHH', hdrReserved, hdrImageType, 1)
dirent = struct.pack('<BBBBHHII', width, height, palColours, reserved, planes, bpp, nBytes, ICONDIRSIZE + ICONDIRENTRYSIZE)
pxData = d[offset:offset+nBytes]
inMemoryICO = io.BytesIO(hdr + dirent +pxData)
im = Image.open(inMemoryICO)
res.append(im)
return res
for i, icon in enumerate(extractIcons('example.ico')):
icon.save(f'icon-{i}.png')
For your file it prints:
DEBUG: hdrReserved=0, hdrImageType=1, hdrNumImages=2
DEBUG: Entry:0, width=60, height=45, palColours=0, reserved=0, planes=1, bpp=32, nBytes=11200, offset=38
DEBUG: Entry:1, width=16, height=16, palColours=0, reserved=0, planes=1, bpp=32, nBytes=1128, offset=11238
And extracts:
-rw-r--r-- 1 mark staff 675 8 Nov 11:00 icon-0.png
-rw-r--r-- 1 mark staff 366 8 Nov 11:00 icon-1.png