PythonでICO作成

ひさびさのPythonネタです。PILハンドブックによるとではICOは読み取り専用らしいので、BMPで書き出した後ヘッダを編集することでICOが作成できるようにしてみました。
PILはよく知らないので24ビットのBMPが作成されるものと決め打ちでやっています。
また、pack関数の仕様上、sizeof(long)が4でないLP64やILP64の処理系では正しいデータが生成できないと思われます。
いまどきsizeof(int) == 2 はないだろうということで32ビット符号無し整数のフォーマット文字をLからIに変更しました。

ソース:

#!/usr/bin/env python
"""Windows icon (.ico) maker using The Python Imaging Library
"""
from PIL import Image
from cStringIO import StringIO
from struct import pack


def makeicon(infile, outfile=None, dimensions=(32, 32)):
    """Convert an image to Windows icon format
    """
    # check image dimensions
    width, height = dimensions
    if width < 1 or width > 256 or height < 1 or height > 256:
        raise ValueError('Invalid image dimensions')

    # open the source image and resize
    im = Image.open(infile)
    im.thumbnail(dimensions, Image.BICUBIC)
    width, height = im.size

    # crate BMP
    bmp = StringIO()
    im.save(bmp, 'BMP')

    # get resource size
    image_size = bmp.tell() - 14
    mask_size = ((width + 31) / 32) * 4 * height

    # write ICONDIR
    ico = StringIO()
    ico.write(pack('<3H', 0, 1, 1))

    # write ICONDIRENTRY
    ico.write(pack('<BBBBHHII', width & 255, height & 255,
                    0, 0, 1, 24, image_size + mask_size, 6 + 16))

    # write ICONIMAGE
    bmp.seek(14)
    ico.write(bmp.read(8))
    ico.write(pack('<I', height * 2))
    bmp.seek(4, 1)
    ico.write(bmp.read())
    ico.write('\0' * mask_size)
    ico.reset()

    # output or return the icon data
    if (outfile):
        open(outfile, 'w').write(ico.read())
    else:
        return ico.read()


if __name__ == '__main__':
    import sys, os
    infile = sys.argv[1]
    name, ext = os.path.splitext(os.path.basename(infile))
    outfile = name + '.ico'
    makeicon(infile, outfile, (48, 48))
    #print Image.open(outfile).size