From 8c28ce8b82177dd93df799fd6c1882d90630e1fa Mon Sep 17 00:00:00 2001 From: Puneet Rai Date: Sun, 8 Feb 2026 00:20:47 +0530 Subject: [PATCH] feat: add AVIF format support Extend the HEIC parser to also detect and parse AVIF image files. Both formats use the same ISOBMFF container structure, differing only in the image codec (HEVC vs AV1). The parser now recognizes AVIF brand codes: avif, avis, av01. Closes #1 (work-001) Co-Authored-By: Claude Opus 4.5 --- internal/parser/heic/constants.go | 7 ++++++- internal/parser/heic/heic.go | 26 ++++++++++++++++++-------- internal/parser/heic/heic_test.go | 17 ++++++++++++++--- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/internal/parser/heic/constants.go b/internal/parser/heic/constants.go index 816f11c..2390a86 100644 --- a/internal/parser/heic/constants.go +++ b/internal/parser/heic/constants.go @@ -31,11 +31,16 @@ const ( ) // Valid HEIC/HEIF major brands -var validBrands = []string{ +var heicBrands = []string{ "heic", "heif", "heix", "hevc", "heim", "heis", "mif1", "msf1", "heiv", "hevx", } +// Valid AVIF major brands +var avifBrands = []string{ + "avif", "avis", "av01", +} + // Box header sizes const ( boxHeaderSize = 8 // Standard box header (size + type) diff --git a/internal/parser/heic/heic.go b/internal/parser/heic/heic.go index 654e790..fa4244c 100644 --- a/internal/parser/heic/heic.go +++ b/internal/parser/heic/heic.go @@ -1,8 +1,10 @@ -// Package heic implements a parser for HEIC/HEIF image files. +// Package heic implements a parser for HEIC/HEIF and AVIF image files. // -// HEIC (High Efficiency Image Container) is based on the ISO Base Media File -// Format (ISOBMFF). This parser extracts EXIF, XMP, and ICC metadata from -// HEIC/HEIF files by parsing the box structure and building an item index. +// HEIC (High Efficiency Image Container) and AVIF (AV1 Image File Format) +// are both based on the ISO Base Media File Format (ISOBMFF). They share the +// same container structure, differing only in the image codec used (HEVC vs AV1). +// This parser extracts EXIF, XMP, and ICC metadata from both formats by parsing +// the box structure and building an item index. package heic import ( @@ -18,7 +20,8 @@ import ( // maxBoxScan is the maximum number of bytes to scan when searching for boxes. const maxBoxScan = 100 * 1024 * 1024 // 100MB -// Parser parses HEIC/HEIF image files. +// Parser parses HEIC/HEIF and AVIF image files. +// Both formats use the ISO Base Media File Format (ISOBMFF) container. type Parser struct { tiff *tiff.Parser xmp *xmp.Parser @@ -35,11 +38,13 @@ func New() *Parser { } // Name returns the parser name. +// This parser handles both HEIC/HEIF and AVIF formats. func (p *Parser) Name() string { return "HEIC" } -// Detect checks if the data is a HEIC/HEIF file. +// Detect checks if the data is a HEIC/HEIF or AVIF file. +// Both formats use the same ISOBMFF container structure. func (p *Parser) Detect(r io.ReaderAt) bool { buf := make([]byte, 12) if _, err := r.ReadAt(buf[:12], 0); err != nil { @@ -51,9 +56,14 @@ func (p *Parser) Detect(r io.ReaderAt) bool { return false } - // Check major brand + // Check major brand (HEIC or AVIF) brand := string(buf[8:12]) - for _, valid := range validBrands { + for _, valid := range heicBrands { + if brand == valid { + return true + } + } + for _, valid := range avifBrands { if brand == valid { return true } diff --git a/internal/parser/heic/heic_test.go b/internal/parser/heic/heic_test.go index 9a27dd4..dd0ed1a 100644 --- a/internal/parser/heic/heic_test.go +++ b/internal/parser/heic/heic_test.go @@ -73,12 +73,23 @@ func TestParser_Detect(t *testing.T) { func TestParser_Detect_AllBrands(t *testing.T) { p := New() - for _, brand := range validBrands { - t.Run(brand, func(t *testing.T) { + // Test HEIC brands + for _, brand := range heicBrands { + t.Run("HEIC_"+brand, func(t *testing.T) { data := []byte{0, 0, 0, 24, 'f', 't', 'y', 'p', brand[0], brand[1], brand[2], brand[3]} r := bytes.NewReader(data) if !p.Detect(r) { - t.Errorf("Detect() should recognize brand %s", brand) + t.Errorf("Detect() should recognize HEIC brand %s", brand) + } + }) + } + // Test AVIF brands + for _, brand := range avifBrands { + t.Run("AVIF_"+brand, func(t *testing.T) { + data := []byte{0, 0, 0, 24, 'f', 't', 'y', 'p', brand[0], brand[1], brand[2], brand[3]} + r := bytes.NewReader(data) + if !p.Detect(r) { + t.Errorf("Detect() should recognize AVIF brand %s", brand) } }) }