mirror of https://github.com/dsoprea/go-exif.git
General intuitivity refactor. Imp'd (IfdTagEntry).ValueBytes().
- Added some assertions and constants. Renamed from variable so it'd be easy to tell if the byte-arrays, even though they come directly from the EXIF, which specific offsets they start at (so no remove for confusion during enumeration and building).pull/3/head
parent
ff7cfacd60
commit
f1f23dca82
|
@ -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
|
||||
}
|
||||
|
|
43
exif.go
43
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)
|
||||
|
|
12
exif_test.go
12
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)
|
||||
|
|
|
@ -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*
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)?
|
||||
|
|
Loading…
Reference in New Issue