diff --git a/internal/meta/exif/exif.go b/internal/meta/exif/exif.go index b2d086d..0e35b22 100644 --- a/internal/meta/exif/exif.go +++ b/internal/meta/exif/exif.go @@ -47,14 +47,10 @@ func (p *Parser) parseTIFF(data []byte) ([]common.Directory, error) { return nil, fmt.Errorf("TIFF header too short") } - // Read byte order (first 2 bytes) - var byteOrder binary.ByteOrder - if data[0] == 'I' && data[1] == 'I' { - byteOrder = binary.LittleEndian // Intel - } else if data[0] == 'M' && data[1] == 'M' { - byteOrder = binary.BigEndian // Motorola - } else { - return nil, fmt.Errorf("invalid TIFF byte order: %02X %02X", data[0], data[1]) + // Read byte order from TIFF header + byteOrder, err := parseByteOrder(data) + if err != nil { + return nil, err } // Verify TIFF magic number (should be 42) @@ -78,24 +74,12 @@ func (p *Parser) parseTIFF(data []byte) ([]common.Directory, error) { } dirs = append(dirs, ifd0) - // Check for EXIF sub-IFD pointer - if exifOffset, ok := ifd0.Tags["EXIF:ExifOffset"]; ok { - if offset, ok := exifOffset.Value.(int); ok && offset > 0 && offset < len(data) { - exifIFD, _, err := p.parseIFD(data, offset, byteOrder, "ExifIFD") - if err == nil { - dirs = append(dirs, exifIFD) - } - } + // Parse EXIF and GPS sub-IFDs if present + if subIFD, ok := p.parseSubIFD(ifd0, "EXIF:ExifOffset", "ExifIFD", data, byteOrder); ok { + dirs = append(dirs, subIFD) } - - // Check for GPS sub-IFD pointer - if gpsOffset, ok := ifd0.Tags["EXIF:GPSInfo"]; ok { - if offset, ok := gpsOffset.Value.(int); ok && offset > 0 && offset < len(data) { - gpsIFD, _, err := p.parseIFD(data, offset, byteOrder, "GPS") - if err == nil { - dirs = append(dirs, gpsIFD) - } - } + if subIFD, ok := p.parseSubIFD(ifd0, "EXIF:GPSInfo", "GPS", data, byteOrder); ok { + dirs = append(dirs, subIFD) } // Parse IFD1 (thumbnail) if present @@ -110,6 +94,37 @@ func (p *Parser) parseTIFF(data []byte) ([]common.Directory, error) { return dirs, nil } +// parseByteOrder reads and validates the TIFF byte order marker +func parseByteOrder(data []byte) (binary.ByteOrder, error) { + if data[0] == 'I' && data[1] == 'I' { + return binary.LittleEndian, nil // Intel + } + if data[0] == 'M' && data[1] == 'M' { + return binary.BigEndian, nil // Motorola + } + return nil, fmt.Errorf("invalid TIFF byte order: %02X %02X", data[0], data[1]) +} + +// parseSubIFD attempts to parse a sub-IFD referenced by a tag in the parent IFD +func (p *Parser) parseSubIFD(parentIFD common.Directory, tagID common.TagID, name string, data []byte, byteOrder binary.ByteOrder) (common.Directory, bool) { + tag, ok := parentIFD.Tags[tagID] + if !ok { + return common.Directory{}, false + } + + offset, ok := tag.Value.(int) + if !ok || offset <= 0 || offset >= len(data) { + return common.Directory{}, false + } + + subIFD, _, err := p.parseIFD(data, offset, byteOrder, name) + if err != nil { + return common.Directory{}, false + } + + return subIFD, true +} + // parseIFD parses a single IFD (Image File Directory) func (p *Parser) parseIFD(data []byte, offset int, byteOrder binary.ByteOrder, name string) (common.Directory, uint32, error) { if offset+2 > len(data) { @@ -124,7 +139,7 @@ func (p *Parser) parseIFD(data []byte, offset int, byteOrder binary.ByteOrder, n dir := common.Directory{ Spec: common.SpecEXIF, Name: name, - Tags: make(map[common.TagID]common.Tag), + Tags: make(map[common.TagID]common.Tag, entryCount), } // Parse each entry (12 bytes each) @@ -179,7 +194,7 @@ func (p *Parser) parseEntry(data []byte, offset int, byteOrder binary.ByteOrder, if !ok { tagName = fmt.Sprintf("Tag%04X", tagID) } - tag.ID = common.TagID(fmt.Sprintf("EXIF:%s", tagName)) + tag.ID = common.TagID("EXIF:" + tagName) tag.Name = tagName // Parse value based on type