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
Dustin Oprea 2018-04-21 20:54:56 -04:00
parent ff7cfacd60
commit f1f23dca82
6 changed files with 121 additions and 52 deletions

View File

@ -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
View File

@ -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)

View File

@ -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)

View File

@ -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*

View File

@ -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)

View File

@ -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)?