Microsoft Windows icon file: Java parsing library

Microsoft Windows uses specific file format to store applications icons - ICO. This is a container that contains one or more image files (effectively, DIB parts of BMP files or full PNG files are contained inside).

File extension

ico

KS implementation details

License: CC0-1.0

This page hosts a formal specification of Microsoft Windows icon file using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Parse a local file and get structure in memory:

Ico data = Ico.fromFile("path/to/local/file.ico");

Or parse structure from a byte array:

byte[] someArray = new byte[] { ... };
Ico data = new Ico(new KaitaiStream(someArray));

After that, one can get various attributes from the structure by invoking getter methods like:

data.numImages() // => Number of images contained in this file

Java source code to parse Microsoft Windows icon file

Ico.java

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

import io.kaitai.struct.ByteBufferKaitaiStream;
import io.kaitai.struct.KaitaiStruct;
import io.kaitai.struct.KaitaiStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;


/**
 * Microsoft Windows uses specific file format to store applications
 * icons - ICO. This is a container that contains one or more image
 * files (effectively, DIB parts of BMP files or full PNG files are
 * contained inside).
 * @see <a href="https://msdn.microsoft.com/en-us/library/ms997538.aspx">Source</a>
 */
public class Ico extends KaitaiStruct {
    public static Ico fromFile(String fileName) throws IOException {
        return new Ico(new ByteBufferKaitaiStream(fileName));
    }

    public Ico(KaitaiStream _io) {
        this(_io, null, null);
    }

    public Ico(KaitaiStream _io, KaitaiStruct _parent) {
        this(_io, _parent, null);
    }

    public Ico(KaitaiStream _io, KaitaiStruct _parent, Ico _root) {
        super(_io);
        this._parent = _parent;
        this._root = _root == null ? this : _root;
        _read();
    }
    private void _read() {
        this.magic = this._io.ensureFixedContents(new byte[] { 0, 0, 1, 0 });
        this.numImages = this._io.readU2le();
        images = new ArrayList<IconDirEntry>((int) (numImages()));
        for (int i = 0; i < numImages(); i++) {
            this.images.add(new IconDirEntry(this._io, this, _root));
        }
    }
    public static class IconDirEntry extends KaitaiStruct {
        public static IconDirEntry fromFile(String fileName) throws IOException {
            return new IconDirEntry(new ByteBufferKaitaiStream(fileName));
        }

        public IconDirEntry(KaitaiStream _io) {
            this(_io, null, null);
        }

        public IconDirEntry(KaitaiStream _io, Ico _parent) {
            this(_io, _parent, null);
        }

        public IconDirEntry(KaitaiStream _io, Ico _parent, Ico _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
            _read();
        }
        private void _read() {
            this.width = this._io.readU1();
            this.height = this._io.readU1();
            this.numColors = this._io.readU1();
            this.reserved = this._io.ensureFixedContents(new byte[] { 0 });
            this.numPlanes = this._io.readU2le();
            this.bpp = this._io.readU2le();
            this.lenImg = this._io.readU4le();
            this.ofsImg = this._io.readU4le();
        }
        private byte[] img;

        /**
         * Raw image data. Use `is_png` to determine whether this is an
         * embedded PNG file (true) or a DIB bitmap (false) and call a
         * relevant parser, if needed to parse image data further.
         */
        public byte[] img() {
            if (this.img != null)
                return this.img;
            long _pos = this._io.pos();
            this._io.seek(ofsImg());
            this.img = this._io.readBytes(lenImg());
            this._io.seek(_pos);
            return this.img;
        }
        private byte[] pngHeader;

        /**
         * Pre-reads first 8 bytes of the image to determine if it's an
         * embedded PNG file.
         */
        public byte[] pngHeader() {
            if (this.pngHeader != null)
                return this.pngHeader;
            long _pos = this._io.pos();
            this._io.seek(ofsImg());
            this.pngHeader = this._io.readBytes(8);
            this._io.seek(_pos);
            return this.pngHeader;
        }
        private Boolean isPng;

        /**
         * True if this image is in PNG format.
         */
        public Boolean isPng() {
            if (this.isPng != null)
                return this.isPng;
            boolean _tmp = (boolean) (Arrays.equals(pngHeader(), new byte[] { -119, 80, 78, 71, 13, 10, 26, 10 }));
            this.isPng = _tmp;
            return this.isPng;
        }
        private int width;
        private int height;
        private int numColors;
        private byte[] reserved;
        private int numPlanes;
        private int bpp;
        private long lenImg;
        private long ofsImg;
        private Ico _root;
        private Ico _parent;

        /**
         * Width of image, px
         */
        public int width() { return width; }

        /**
         * Height of image, px
         */
        public int height() { return height; }

        /**
         * Number of colors in palette of the image or 0 if image has
         * no palette (i.e. RGB, RGBA, etc)
         */
        public int numColors() { return numColors; }
        public byte[] reserved() { return reserved; }

        /**
         * Number of color planes
         */
        public int numPlanes() { return numPlanes; }

        /**
         * Bits per pixel in the image
         */
        public int bpp() { return bpp; }

        /**
         * Size of the image data
         */
        public long lenImg() { return lenImg; }

        /**
         * Absolute offset of the image data start in the file
         */
        public long ofsImg() { return ofsImg; }
        public Ico _root() { return _root; }
        public Ico _parent() { return _parent; }
    }
    private byte[] magic;
    private int numImages;
    private ArrayList<IconDirEntry> images;
    private Ico _root;
    private KaitaiStruct _parent;
    public byte[] magic() { return magic; }

    /**
     * Number of images contained in this file
     */
    public int numImages() { return numImages; }
    public ArrayList<IconDirEntry> images() { return images; }
    public Ico _root() { return _root; }
    public KaitaiStruct _parent() { return _parent; }
}