Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 42 additions & 27 deletions internal/meta/exif/exif.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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) {
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down
Loading