diff --git a/README.md b/README.md index f371605..3a2f809 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ visitor := func(indexedIfdName string, tagId uint16, tagType exif.TagType, value return nil } -err = e.Parse(data[foundAt:], visitor) +err = e.Visit(data[foundAt:], visitor) log.PanicIf(err) ``` diff --git a/exif-read-tool/main.go b/exif-read-tool/main.go index 3a34434..dbd53e3 100644 --- a/exif-read-tool/main.go +++ b/exif-read-tool/main.go @@ -93,6 +93,6 @@ func main() { return nil } - err = e.Parse(data[foundAt:], visitor) + err = e.Visit(data[foundAt:], visitor) log.PanicIf(err) } diff --git a/exif.go b/exif.go index 97a50db..5d55142 100644 --- a/exif.go +++ b/exif.go @@ -11,7 +11,11 @@ import ( var ( exifLogger = log.NewLogger("exif.exif") +) + +var ( ErrNotExif = errors.New("not exif data") + ErrExifHeaderError = errors.New("exif header error") ) type Exif struct { @@ -30,7 +34,13 @@ func (e *Exif) IsExif(data []byte) (ok bool) { return false } -func (e *Exif) Parse(data []byte, visitor TagVisitor) (err error) { + +type ExifHeader struct { + ByteOrder binary.ByteOrder + FirstIfdOffset uint32 +} + +func (e *Exif) ParseExifHeader(data []byte) (eh ExifHeader, err error) { defer func() { if state := recover(); state != nil { err = log.Wrap(state.(error)) @@ -57,8 +67,8 @@ func (e *Exif) Parse(data []byte, visitor TagVisitor) (err error) { fixedBytes := data[8:10] if fixedBytes[0] != 0x2a || fixedBytes[1] != 0x00 { - exifLogger.Warningf(nil, "EXIF app-data header fixed-bytes should be 0x002a but are: [%v]", fixedBytes) - return nil + exifLogger.Warningf(nil, "EXIF header fixed-bytes should be 0x002a but are: [%v]", fixedBytes) + log.Panic(ErrExifHeaderError) } firstIfdOffset := uint32(0) @@ -68,9 +78,27 @@ func (e *Exif) Parse(data []byte, visitor TagVisitor) (err error) { firstIfdOffset = binary.LittleEndian.Uint32(data[10:14]) } - ie := NewIfdEnumerate(data, byteOrder) + eh = ExifHeader{ + ByteOrder: byteOrder, + FirstIfdOffset: firstIfdOffset, + } - err = ie.Scan(IfdStandard, firstIfdOffset, visitor) + return eh, nil +} + +func (e *Exif) Visit(data []byte, visitor TagVisitor) (err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() + + eh, err := e.ParseExifHeader(data) + log.PanicIf(err) + + ie := NewIfdEnumerate(data, eh.ByteOrder) + + err = ie.Scan(IfdStandard, eh.FirstIfdOffset, visitor) log.PanicIf(err) return nil diff --git a/exif_test.go b/exif_test.go index ff0ee07..d5432f7 100644 --- a/exif_test.go +++ b/exif_test.go @@ -33,7 +33,7 @@ func TestIsExif_False(t *testing.T) { } } -func TestParse(t *testing.T) { +func TestVisit(t *testing.T) { defer func() { if state := recover(); state != nil { err := log.Wrap(state.(error)) @@ -113,7 +113,7 @@ func TestParse(t *testing.T) { return nil } - err = e.Parse(data[foundAt:], visitor) + err = e.Visit(data[foundAt:], visitor) log.PanicIf(err) // for _, line := range tags { diff --git a/ifd_enumerate.go b/ifd_enumerate.go index 760168c..d5d1d04 100644 --- a/ifd_enumerate.go +++ b/ifd_enumerate.go @@ -124,9 +124,21 @@ func (ie *IfdEnumerate) getTagEnumerator(ifdOffset uint32) (ite *IfdTagEnumerato // the offsets of all IFDs and values are calculated from). type TagVisitor func(indexedIfdName string, tagId uint16, tagType TagType, valueContext ValueContext) (err error) -// parseIfd decodes the IFD block that we're currently sitting on the first + +type IfdTagEntry struct { + TagId uint16 + TagIndex int + TagType uint16 + UnitCount uint32 + ValueOffset uint32 + RawValueOffset []byte + IsIfd bool +} + + +// ParseIfd decodes the IFD block that we're currently sitting on the first // byte of. -func (ie *IfdEnumerate) parseIfd(ifdName string, ifdIndex int, ifdOffset uint32, visitor TagVisitor) (nextIfdOffset uint32, err error) { +func (ie *IfdEnumerate) ParseIfd(ifdName string, ifdIndex int, ifdOffset uint32, visitor TagVisitor, doDescend bool) (nextIfdOffset uint32, entries []IfdTagEntry, err error) { defer func() { if state := recover(); state != nil { err = log.Wrap(state.(error)) @@ -155,6 +167,8 @@ func (ie *IfdEnumerate) parseIfd(ifdName string, ifdIndex int, ifdOffset uint32, ifdLogger.Debugf(nil, "Current IFD tag-count: (%d)", tagCount) + entries = make([]IfdTagEntry, tagCount) + for i := uint16(0); i < tagCount; i++ { tagId, _, err := ite.getUint16() log.PanicIf(err) @@ -182,13 +196,28 @@ func (ie *IfdEnumerate) parseIfd(ifdName string, ifdIndex int, ifdOffset uint32, log.PanicIf(err) } + tag := IfdTagEntry{ + TagId: tagId, + TagIndex: int(i), + TagType: tagType, + UnitCount: unitCount, + ValueOffset: valueOffset, + RawValueOffset: rawValueOffset, + } + childIfdName, isIfd := IsIfdTag(tagId) if isIfd == true { - ifdLogger.Debugf(nil, "Descending to IFD [%s].", childIfdName) + tag.IsIfd = true - err := ie.Scan(childIfdName, valueOffset, visitor) - log.PanicIf(err) + if doDescend == true { + ifdLogger.Debugf(nil, "Descending to IFD [%s].", childIfdName) + + err := ie.Scan(childIfdName, valueOffset, visitor) + log.PanicIf(err) + } } + + entries[i] = tag } nextIfdOffset, _, err = ite.getUint32() @@ -196,7 +225,7 @@ func (ie *IfdEnumerate) parseIfd(ifdName string, ifdIndex int, ifdOffset uint32, ifdLogger.Debugf(nil, "Next IFD at offset: (%08x)", nextIfdOffset) - return nextIfdOffset, nil + return nextIfdOffset, entries, nil } // Scan enumerates the different EXIF blocks (called IFDs). @@ -208,7 +237,7 @@ func (ie *IfdEnumerate) Scan(ifdName string, ifdOffset uint32, visitor TagVisito }() for ifdIndex := 0;; ifdIndex++ { - nextIfdOffset, err := ie.parseIfd(ifdName, ifdIndex, ifdOffset, visitor) + nextIfdOffset, _, err := ie.ParseIfd(ifdName, ifdIndex, ifdOffset, visitor, true) log.PanicIf(err) if nextIfdOffset == 0 {