diff --git a/exif-read-tool/main.go b/exif-read-tool/main.go index dbd53e3..7e6e812 100644 --- a/exif-read-tool/main.go +++ b/exif-read-tool/main.go @@ -42,7 +42,7 @@ func main() { foundAt := -1 for i := 0; i < len(data); i++ { - if e.IsExif(data[i:i + 6]) == true { + if exif.IsExif(data[i:i + 6]) == true { foundAt = i break } diff --git a/exif.go b/exif.go index f2be0e1..bdd3fea 100644 --- a/exif.go +++ b/exif.go @@ -12,9 +12,12 @@ import ( ) const ( - // DefaultRootIfdExifOffset is the offset of the first IFD in the block of - // EXIF data. - DefaultRootIfdExifOffset = uint32(0x0008) + // RootIfdExifOffset is the offset of the first IFD in the block of EXIF + // data. + RootIfdExifOffset = uint32(0x0008) + + // ExifAddressableAreaStart is the start of where all offsets are relative to. + ExifAddressableAreaStart = uint32(0x6) ) var ( @@ -26,6 +29,16 @@ var ( ErrExifHeaderError = errors.New("exif header error") ) + +func IsExif(data []byte) (ok bool) { + if bytes.Compare(data[:6], []byte("Exif\000\000")) == 0 { + return true + } + + return false +} + + type Exif struct { } @@ -34,14 +47,6 @@ func NewExif() *Exif { return new(Exif) } -func (e *Exif) IsExif(data []byte) (ok bool) { - if bytes.Compare(data[:6], []byte("Exif\000\000")) == 0 { - return true - } - - return false -} - func (e *Exif) SearchAndExtractExif(filepath string) (rawExif []byte, err error) { defer func() { if state := recover(); state != nil { @@ -65,7 +70,7 @@ func (e *Exif) SearchAndExtractExif(filepath string) (rawExif []byte, err error) foundAt := -1 for i := 0; i < len(data); i++ { - if e.IsExif(data[i:i + 6]) == true { + if IsExif(data[i:i + 6]) == true { foundAt = i break } @@ -91,7 +96,7 @@ func (e *Exif) ParseExifHeader(data []byte) (eh ExifHeader, err error) { } }() - if e.IsExif(data) == false { + if IsExif(data) == false { log.Panic(ErrNotExif) } @@ -130,17 +135,17 @@ func (e *Exif) ParseExifHeader(data []byte) (eh ExifHeader, err error) { return eh, nil } -func (e *Exif) Visit(data []byte, visitor TagVisitor) (err error) { +func (e *Exif) Visit(exifData []byte, visitor TagVisitor) (err error) { defer func() { if state := recover(); state != nil { err = log.Wrap(state.(error)) } }() - eh, err := e.ParseExifHeader(data) + eh, err := e.ParseExifHeader(exifData) log.PanicIf(err) - ie := NewIfdEnumerate(data, eh.ByteOrder) + ie := NewIfdEnumerate(exifData, eh.ByteOrder) err = ie.Scan(IfdStandard, eh.FirstIfdOffset, visitor) log.PanicIf(err) @@ -148,17 +153,17 @@ func (e *Exif) Visit(data []byte, visitor TagVisitor) (err error) { return nil } -func (e *Exif) Collect(data []byte) (index IfdIndex, err error) { +func (e *Exif) Collect(exifData []byte) (index IfdIndex, err error) { defer func() { if state := recover(); state != nil { err = log.Wrap(state.(error)) } }() - eh, err := e.ParseExifHeader(data) + eh, err := e.ParseExifHeader(exifData) log.PanicIf(err) - ie := NewIfdEnumerate(data, eh.ByteOrder) + ie := NewIfdEnumerate(exifData, eh.ByteOrder) index, err = ie.Collect(eh.FirstIfdOffset) log.PanicIf(err) diff --git a/exif_test.go b/exif_test.go index a33203f..d53fb6c 100644 --- a/exif_test.go +++ b/exif_test.go @@ -18,17 +18,13 @@ var ( func TestIsExif_True(t *testing.T) { - e := NewExif() - - if ok := e.IsExif([]byte("Exif\000\000")); ok != true { + if ok := IsExif([]byte("Exif\000\000")); ok != true { t.Fatalf("expected true") } } func TestIsExif_False(t *testing.T) { - e := NewExif() - - if ok := e.IsExif([]byte("something unexpected")); ok != false { + if ok := IsExif([]byte("something unexpected")); ok != false { t.Fatalf("expected false") } } @@ -59,7 +55,7 @@ func TestVisit(t *testing.T) { foundAt := -1 for i := 0; i < len(data); i++ { - if e.IsExif(data[i:i + 6]) == true { + if IsExif(data[i:i + 6]) == true { foundAt = i break } @@ -210,7 +206,7 @@ func TestCollect(t *testing.T) { tree := index.Tree lookup := index.Lookup - if rootIfd.Offset != DefaultRootIfdExifOffset { + if rootIfd.Offset != RootIfdExifOffset { t.Fatalf("Root-IFD not correct: (0x%04d).", rootIfd) } else if rootIfd.Id != 0 { t.Fatalf("Root-IFD does not have the right ID: (%d)", rootIfd.Id) diff --git a/ifd_builder.go b/ifd_builder.go index a8dac89..ffe5ad8 100644 --- a/ifd_builder.go +++ b/ifd_builder.go @@ -206,7 +206,7 @@ func (ib *IfdBuilder) BuildExif() (new []byte, err error) { b := bytes.Buffer{} ioi := &ifdOffsetIterator{ - offset: DefaultRootIfdExifOffset, + offset: RootIfdExifOffset, } ptr := ib @@ -498,6 +498,10 @@ func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, includeTagIds []uint16, excl // an IfdEnumerator and then be read and re-read (like an IEnumerable vs IList). +// TODO(dustin): !! Finish. + // itevr := NewIfdTagEntryValueResolver(rawExif []byte, ib.byteOrder) + // itevr.ValueBytes(ite *IfdTagEntry) (value []byte, err error) + for _, tag := range ifd.Entries { // If we want to add an IFD tag, we'll have to build it first and *then* diff --git a/ifd_enumerate.go b/ifd_enumerate.go index f49b0c2..e7a2bc3 100644 --- a/ifd_enumerate.go +++ b/ifd_enumerate.go @@ -22,16 +22,16 @@ var ( // are fairly simple to enumerate. type IfdTagEnumerator struct { byteOrder binary.ByteOrder - rawExif []byte + addressableData []byte ifdOffset uint32 buffer *bytes.Buffer } -func NewIfdTagEnumerator(rawExif []byte, byteOrder binary.ByteOrder, ifdOffset uint32) (ite *IfdTagEnumerator) { +func NewIfdTagEnumerator(addressableData []byte, byteOrder binary.ByteOrder, ifdOffset uint32) (ite *IfdTagEnumerator) { ite = &IfdTagEnumerator{ - rawExif: rawExif, + addressableData: addressableData, byteOrder: byteOrder, - buffer: bytes.NewBuffer(rawExif[ifdOffset:]), + buffer: bytes.NewBuffer(addressableData[ifdOffset:]), } return ite @@ -87,19 +87,22 @@ func (ife *IfdTagEnumerator) getUint32() (value uint32, raw []byte, err error) { type IfdEnumerate struct { - data []byte + exifData []byte buffer *bytes.Buffer byteOrder binary.ByteOrder currentOffset uint32 - ifdTopOffset uint32 } -func NewIfdEnumerate(data []byte, byteOrder binary.ByteOrder) *IfdEnumerate { +func NewIfdEnumerate(exifData []byte, byteOrder binary.ByteOrder) *IfdEnumerate { + // Make it obvious what data we expect and when we don't get it. + if IsExif(exifData) == false { + log.Panicf("not exif data") + } + return &IfdEnumerate{ - data: data, - buffer: bytes.NewBuffer(data), + exifData: exifData, + buffer: bytes.NewBuffer(exifData), byteOrder: byteOrder, - ifdTopOffset: 6, } } @@ -109,12 +112,12 @@ type ValueContext struct { UnitCount uint32 ValueOffset uint32 RawValueOffset []byte - RawExif []byte + AddressableData []byte } func (ie *IfdEnumerate) getTagEnumerator(ifdOffset uint32) (ite *IfdTagEnumerator) { ite = NewIfdTagEnumerator( - ie.data[ie.ifdTopOffset:], + ie.exifData[ExifAddressableAreaStart:], ie.byteOrder, ifdOffset) @@ -122,8 +125,8 @@ func (ie *IfdEnumerate) getTagEnumerator(ifdOffset uint32) (ite *IfdTagEnumerato } // TagVisitor is an optional callback that can get hit for every tag we parse -// through. `rawExif` is the byte array startign after the EXIF header (where -// the offsets of all IFDs and values are calculated from). +// through. `addressableData` is the byte array startign after the EXIF header +// (where the offsets of all IFDs and values are calculated from). type TagVisitor func(indexedIfdName string, tagId uint16, tagType TagType, valueContext ValueContext) (err error) @@ -137,6 +140,64 @@ type IfdTagEntry struct { IfdName string } +func (ite IfdTagEntry) ValueBytes(addressableData []byte, byteOrder binary.ByteOrder) (value []byte, err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() + + originalType := NewTagType(ite.TagType, byteOrder) + byteCount := uint32(originalType.Size()) * ite.UnitCount + + tt := NewTagType(TypeByte, byteOrder) + + if tt.ValueIsEmbedded(byteCount) == true { + typeDecodeLogger.Debugf(nil, "Reading BYTE value (ITE; embedded).") + + // In this case, the bytes normally used for the offset are actually + // data. + value, err = tt.ParseBytes(ite.RawValueOffset, byteCount) + log.PanicIf(err) + } else { + typeDecodeLogger.Debugf(nil, "Reading BYTE value (ITE; at offset).") + + value, err = tt.ParseBytes(addressableData[ite.ValueOffset:], byteCount) + log.PanicIf(err) + } + + return value, nil +} + + +type IfdTagEntryValueResolver struct { + addressableData []byte + byteOrder binary.ByteOrder +} + +func NewIfdTagEntryValueResolver(exifData []byte, byteOrder binary.ByteOrder) (itevr *IfdTagEntryValueResolver) { + // Make it obvious what data we expect and when we don't get it. + if IsExif(exifData) == false { + log.Panicf("not exif data") + } + + return &IfdTagEntryValueResolver{ + addressableData: exifData[ExifAddressableAreaStart:], + byteOrder: byteOrder, + } +} + +func (itevr *IfdTagEntryValueResolver) ValueBytes(ite *IfdTagEntry) (value []byte, err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() + + value, err = ite.ValueBytes(itevr.addressableData, itevr.byteOrder) + return value, err +} + // ParseIfd decodes the IFD block that we're currently sitting on the first // byte of. @@ -191,7 +252,7 @@ func (ie *IfdEnumerate) ParseIfd(ifdName string, ifdIndex int, ifdOffset uint32, UnitCount: unitCount, ValueOffset: valueOffset, RawValueOffset: rawValueOffset, - RawExif: ie.data[ie.ifdTopOffset:], + AddressableData: ie.exifData[ExifAddressableAreaStart:], } err := visitor(indexedIfdName, tagId, tt, vc) diff --git a/type_decode.go b/type_decode.go index 103ba9f..0208f9d 100644 --- a/type_decode.go +++ b/type_decode.go @@ -320,12 +320,14 @@ func (tt TagType) ReadByteValues(valueContext ValueContext) (value []byte, err e if tt.ValueIsEmbedded(valueContext.UnitCount) == true { typeDecodeLogger.Debugf(nil, "Reading BYTE value (embedded).") + // In this case, the bytes normally used for the offset are actually + // data. value, err = tt.ParseBytes(valueContext.RawValueOffset, valueContext.UnitCount) log.PanicIf(err) } else { typeDecodeLogger.Debugf(nil, "Reading BYTE value (at offset).") - value, err = tt.ParseBytes(valueContext.RawExif[valueContext.ValueOffset:], valueContext.UnitCount) + value, err = tt.ParseBytes(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) log.PanicIf(err) } @@ -347,7 +349,7 @@ func (tt TagType) ReadAsciiValue(valueContext ValueContext) (value string, err e } else { typeDecodeLogger.Debugf(nil, "Reading ASCII value (no-nul; at offset).") - value, err = tt.ParseAscii(valueContext.RawExif[valueContext.ValueOffset:], valueContext.UnitCount) + value, err = tt.ParseAscii(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) log.PanicIf(err) } @@ -369,7 +371,7 @@ func (tt TagType) ReadAsciiNoNulValue(valueContext ValueContext) (value string, } else { typeDecodeLogger.Debugf(nil, "Reading ASCII value (no-nul; at offset).") - value, err = tt.ParseAsciiNoNul(valueContext.RawExif[valueContext.ValueOffset:], valueContext.UnitCount) + value, err = tt.ParseAsciiNoNul(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) log.PanicIf(err) } @@ -391,7 +393,7 @@ func (tt TagType) ReadShortValues(valueContext ValueContext) (value []uint16, er } else { typeDecodeLogger.Debugf(nil, "Reading SHORT value (at offset).") - value, err = tt.ParseShorts(valueContext.RawExif[valueContext.ValueOffset:], valueContext.UnitCount) + value, err = tt.ParseShorts(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) log.PanicIf(err) } @@ -413,7 +415,7 @@ func (tt TagType) ReadLongValues(valueContext ValueContext) (value []uint32, err } else { typeDecodeLogger.Debugf(nil, "Reading LONG value (at offset).") - value, err = tt.ParseLongs(valueContext.RawExif[valueContext.ValueOffset:], valueContext.UnitCount) + value, err = tt.ParseLongs(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) log.PanicIf(err) } @@ -435,7 +437,7 @@ func (tt TagType) ReadRationalValues(valueContext ValueContext) (value []Rationa } else { typeDecodeLogger.Debugf(nil, "Reading RATIONAL value (at offset).") - value, err = tt.ParseRationals(valueContext.RawExif[valueContext.ValueOffset:], valueContext.UnitCount) + value, err = tt.ParseRationals(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) log.PanicIf(err) } @@ -457,7 +459,7 @@ func (tt TagType) ReadSignedLongValues(valueContext ValueContext) (value []int32 } else { typeDecodeLogger.Debugf(nil, "Reading SLONG value (at offset).") - value, err = tt.ParseSignedLongs(valueContext.RawExif[valueContext.ValueOffset:], valueContext.UnitCount) + value, err = tt.ParseSignedLongs(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) log.PanicIf(err) } @@ -479,7 +481,7 @@ func (tt TagType) ReadSignedRationalValues(valueContext ValueContext) (value []S } else { typeDecodeLogger.Debugf(nil, "Reading SRATIONAL value (at offset).") - value, err = tt.ParseSignedRationals(valueContext.RawExif[valueContext.ValueOffset:], valueContext.UnitCount) + value, err = tt.ParseSignedRationals(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) log.PanicIf(err) } @@ -664,6 +666,7 @@ func UndefinedValue(indexedIfdName string, tagId uint16, valueContext ValueConte log.PanicIf(err) +// TODO(dustin): Doesn't work, but here as an example. // ie := NewIfdEnumerate(valueBytes, byteOrder) // // TODO(dustin): !! Validate types (might have proprietary types, but it might be worth splitting the list between valid and not validate; maybe fail if a certain proportion are invalid, or maybe aren't less then a certain small integer)?