icns

Easily create .icns files (Mac Icons) with this Go library or the included CLI.
Log | Files | Refs | LICENSE

commit 5dbc5dd2b9cbe3f623c758462978197d3a8235f4
parent 6d8994a7f3fa99bfa83d24d9455802673f00f08a
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date:   Fri, 18 Sep 2026 16:09:31 -0400

icns: let one icon occupy more than one element

The writer assumed an icon is a single element, which the colour and mask
types break: their alpha lives in an element of its own. Building a list of
elements per icon also retires the cached header each type carried.

Diffstat:
Mwriter.go | 91++++++++++++++++++++++++++-----------------------------------------------------
1 file changed, 30 insertions(+), 61 deletions(-)

diff --git a/writer.go b/writer.go @@ -13,39 +13,35 @@ type Icon struct { Type OsType Image image.Image - header [8]byte - headerSet bool - data []byte + elements []element } // WriteTo encodes the icon into wr. func (i *Icon) WriteTo(wr io.Writer) (int64, error) { - var written int64 - if err := i.encodeImage(); err != nil { - return written, err + if err := i.encode(); err != nil { + return 0, err } - size, err := i.writeHeader(wr) - written += size - if err != nil { - return written, err - } - size, err = i.writeData(wr) - written += size - if err != nil { - return written, err + var written int64 + for _, el := range i.elements { + n, err := writeElement(wr, el) + written += n + if err != nil { + return written, err + } } return written, nil } -func (i *Icon) encodeImage() error { - if len(i.data) > 0 { +// encode builds the elements this icon occupies in the file. +func (i *Icon) encode() error { + if len(i.elements) > 0 { return nil } data, err := encodeImage(i.Image) if err != nil { return err } - i.data = data + i.elements = []element{{id: i.Type.ID, payload: data}} return nil } @@ -57,47 +53,35 @@ func encodeImage(img image.Image) ([]byte, error) { return buf.Bytes(), nil } -func (i *Icon) writeHeader(wr io.Writer) (int64, error) { - if !i.headerSet { - defer func() { i.headerSet = true }() - copy(i.header[:4], i.Type.ID) - binary.BigEndian.PutUint32(i.header[4:8], uint32(len(i.data)+elementHeaderSize)) +// writeElement writes one element: its 4-byte type, its length counting the +// header, then its payload. +func writeElement(wr io.Writer, el element) (int64, error) { + var header [elementHeaderSize]byte + copy(header[:4], el.id) + binary.BigEndian.PutUint32(header[4:], uint32(elementHeaderSize+len(el.payload))) + n, err := wr.Write(header[:]) + written := int64(n) + if err != nil { + return written, err } - written, err := wr.Write(i.header[:8]) - return int64(written), err -} - -func (i *Icon) writeData(wr io.Writer) (int64, error) { - written, err := wr.Write(i.data) - return int64(written), err + n, err = wr.Write(el.payload) + return written + int64(n), err } // IconSet encodes a set of icons into an ICNS file. type IconSet struct { Icons []*Icon - header [8]byte - headerSet bool - data []byte + data []byte } // WriteTo writes the ICNS file to wr. func (s *IconSet) WriteTo(wr io.Writer) (int64, error) { - var written int64 if err := s.encodeIcons(); err != nil { - return written, err - } - size, err := s.writeHeader(wr) - written += size - if err != nil { - return written, err - } - size, err = s.writeData(wr) - written += size - if err != nil { - return written, err + return 0, err } - return written, nil + // The file is itself an element enclosing all the others. + return writeElement(wr, element{id: "icns", payload: s.data}) } func (s *IconSet) encodeIcons() error { @@ -116,18 +100,3 @@ func (s *IconSet) encodeIcons() error { s.data = buf.Bytes() return nil } - -func (s *IconSet) writeHeader(wr io.Writer) (int64, error) { - if !s.headerSet { - defer func() { s.headerSet = true }() - copy(s.header[:4], "icns") - binary.BigEndian.PutUint32(s.header[4:8], uint32(len(s.data)+elementHeaderSize)) - } - written, err := wr.Write(s.header[:8]) - return int64(written), err -} - -func (s *IconSet) writeData(wr io.Writer) (int64, error) { - written, err := wr.Write(s.data) - return int64(written), err -}