We now have to pass a specific tag-index in from the top.

- This will allow us to handle custom tags and, with a little more work,
  custom IFDs.
This commit is contained in:
Dustin Oprea 2018-07-26 22:45:43 -04:00
parent d8cdf29fa6
commit d3b45fae77
11 changed files with 3384 additions and 3350 deletions

View File

@ -14,15 +14,15 @@
package main package main
import ( import (
"os"
"fmt"
"flag" "flag"
"fmt"
"os"
"io/ioutil"
"encoding/json" "encoding/json"
"io/ioutil"
"github.com/dsoprea/go-logging"
"github.com/dsoprea/go-exif" "github.com/dsoprea/go-exif"
"github.com/dsoprea/go-logging"
) )
var ( var (
@ -30,7 +30,6 @@ var (
printAsJsonArg = false printAsJsonArg = false
) )
type IfdEntry struct { type IfdEntry struct {
IfdName string `json:"ifd_name"` IfdName string `json:"ifd_name"`
ParentIfdName string `json:"parent_ifd_name"` ParentIfdName string `json:"parent_ifd_name"`
@ -131,7 +130,7 @@ func main() {
return nil return nil
} }
_, err = exif.Visit(rawExif, visitor) _, err = exif.Visit(ti, rawExif, visitor)
log.PanicIf(err) log.PanicIf(err)
if printAsJsonArg == true { if printAsJsonArg == true {

View File

@ -168,7 +168,7 @@ func ParseExifHeader(data []byte) (eh ExifHeader, err error) {
} }
// Visit recursively invokes a callback for every tag. // Visit recursively invokes a callback for every tag.
func Visit(exifData []byte, visitor RawTagVisitor) (eh ExifHeader, err error) { func Visit(tagIndex *TagIndex, exifData []byte, visitor RawTagVisitor) (eh ExifHeader, err error) {
defer func() { defer func() {
if state := recover(); state != nil { if state := recover(); state != nil {
err = log.Wrap(state.(error)) err = log.Wrap(state.(error))
@ -178,7 +178,7 @@ func Visit(exifData []byte, visitor RawTagVisitor) (eh ExifHeader, err error) {
eh, err = ParseExifHeader(exifData) eh, err = ParseExifHeader(exifData)
log.PanicIf(err) log.PanicIf(err)
ie := NewIfdEnumerate(exifData, eh.ByteOrder) ie := NewIfdEnumerate(tagIndex, exifData, eh.ByteOrder)
err = ie.Scan(eh.FirstIfdOffset, visitor, true) err = ie.Scan(eh.FirstIfdOffset, visitor, true)
log.PanicIf(err) log.PanicIf(err)
@ -187,7 +187,7 @@ func Visit(exifData []byte, visitor RawTagVisitor) (eh ExifHeader, err error) {
} }
// Collect recursively builds a static structure of all IFDs and tags. // Collect recursively builds a static structure of all IFDs and tags.
func Collect(exifData []byte) (eh ExifHeader, index IfdIndex, err error) { func Collect(tagIndex *TagIndex, exifData []byte) (eh ExifHeader, index IfdIndex, err error) {
defer func() { defer func() {
if state := recover(); state != nil { if state := recover(); state != nil {
err = log.Wrap(state.(error)) err = log.Wrap(state.(error))
@ -197,7 +197,7 @@ func Collect(exifData []byte) (eh ExifHeader, index IfdIndex, err error) {
eh, err = ParseExifHeader(exifData) eh, err = ParseExifHeader(exifData)
log.PanicIf(err) log.PanicIf(err)
ie := NewIfdEnumerate(exifData, eh.ByteOrder) ie := NewIfdEnumerate(tagIndex, exifData, eh.ByteOrder)
index, err = ie.Collect(eh.FirstIfdOffset, true) index, err = ie.Collect(eh.FirstIfdOffset, true)
log.PanicIf(err) log.PanicIf(err)

View File

@ -1,20 +1,19 @@
package exif package exif
import ( import (
"testing" "bytes"
"fmt"
"os" "os"
"path" "path"
"fmt"
"reflect" "reflect"
"bytes" "testing"
"io/ioutil"
"encoding/binary" "encoding/binary"
"io/ioutil"
"github.com/dsoprea/go-logging" "github.com/dsoprea/go-logging"
) )
func TestVisit(t *testing.T) { func TestVisit(t *testing.T) {
defer func() { defer func() {
if state := recover(); state != nil { if state := recover(); state != nil {
@ -23,6 +22,8 @@ func TestVisit(t *testing.T) {
} }
}() }()
ti := NewTagIndex()
// Open the file. // Open the file.
filepath := path.Join(assetsPath, "NDM_8901.jpg") filepath := path.Join(assetsPath, "NDM_8901.jpg")
@ -63,7 +64,7 @@ func TestVisit(t *testing.T) {
} }
}() }()
it, err := tagIndex.Get(ii, tagId) it, err := ti.Get(ii, tagId)
if err != nil { if err != nil {
if log.Is(err, ErrTagNotFound) { if log.Is(err, ErrTagNotFound) {
fmt.Printf("Unknown tag: [%v] (%04x)\n", ii, tagId) fmt.Printf("Unknown tag: [%v] (%04x)\n", ii, tagId)
@ -94,7 +95,7 @@ func TestVisit(t *testing.T) {
return nil return nil
} }
_, err = Visit(data[foundAt:], visitor) _, err = Visit(ti, data[foundAt:], visitor)
log.PanicIf(err) log.PanicIf(err)
// for _, line := range tags { // for _, line := range tags {
@ -203,7 +204,6 @@ func TestSearchAndExtractExif(t *testing.T) {
imageData, err := ioutil.ReadFile(filepath) imageData, err := ioutil.ReadFile(filepath)
log.PanicIf(err) log.PanicIf(err)
rawExif, err := SearchAndExtractExif(imageData) rawExif, err := SearchAndExtractExif(imageData)
log.PanicIf(err) log.PanicIf(err)
@ -225,7 +225,9 @@ func TestCollect(t *testing.T) {
rawExif, err := SearchFileAndExtractExif(filepath) rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err) log.PanicIf(err)
_, index, err := Collect(rawExif) ti := NewTagIndex()
_, index, err := Collect(ti, rawExif)
log.PanicIf(err) log.PanicIf(err)
rootIfd := index.RootIfd rootIfd := index.RootIfd

View File

@ -154,48 +154,12 @@ func (bt *BuilderTag) SetValue(byteOrder binary.ByteOrder, value interface{}) (e
// NewStandardBuilderTag constructs a `BuilderTag` instance. The type is looked // NewStandardBuilderTag constructs a `BuilderTag` instance. The type is looked
// up. `ii` is the type of IFD that owns this tag. // up. `ii` is the type of IFD that owns this tag.
func NewStandardBuilderTag(ii IfdIdentity, tagId uint16, byteOrder binary.ByteOrder, value interface{}) *BuilderTag { func NewStandardBuilderTag(ii IfdIdentity, it *IndexedTag, byteOrder binary.ByteOrder, value interface{}) *BuilderTag {
it, err := tagIndex.Get(ii, tagId)
log.PanicIf(err)
typeId := it.Type typeId := it.Type
tt := NewTagType(typeId, byteOrder) tt := NewTagType(typeId, byteOrder)
ve := NewValueEncoder(byteOrder) ve := NewValueEncoder(byteOrder)
var ed EncodedData
if it.Type == TypeUndefined {
var err error
ed, err = EncodeUndefined(ii, tagId, value)
log.PanicIf(err)
} else {
var err error
ed, err = ve.EncodeWithType(tt, value)
log.PanicIf(err)
}
tagValue := NewIfdBuilderTagValueFromBytes(ed.Encoded)
return NewBuilderTag(
ii,
tagId,
typeId,
tagValue)
}
// NewStandardBuilderTagWithName allows us to easily generate solid, consistent
// tags for testing with. `ii` is the type of IFD that owns this tag. This can
// not be an IFD (IFDs are not associated with standardized, official names).
func NewStandardBuilderTagWithName(ii IfdIdentity, tagName string, byteOrder binary.ByteOrder, value interface{}) *BuilderTag {
it, err := tagIndex.GetWithName(ii, tagName)
log.PanicIf(err)
tt := NewTagType(it.Type, byteOrder)
ve := NewValueEncoder(byteOrder)
var ed EncodedData var ed EncodedData
if it.Type == TypeUndefined { if it.Type == TypeUndefined {
var err error var err error
@ -214,7 +178,7 @@ func NewStandardBuilderTagWithName(ii IfdIdentity, tagName string, byteOrder bin
return NewBuilderTag( return NewBuilderTag(
ii, ii,
it.Id, it.Id,
it.Type, typeId,
tagValue) tagValue)
} }
@ -242,9 +206,11 @@ type IfdBuilder struct {
// thumbnailData is populated with thumbnail data if there was thumbnail // thumbnailData is populated with thumbnail data if there was thumbnail
// data. Otherwise, it's nil. // data. Otherwise, it's nil.
thumbnailData []byte thumbnailData []byte
tagIndex *TagIndex
} }
func NewIfdBuilder(ii IfdIdentity, byteOrder binary.ByteOrder) (ib *IfdBuilder) { func NewIfdBuilder(tagIndex *TagIndex, ii IfdIdentity, byteOrder binary.ByteOrder) (ib *IfdBuilder) {
ifdTagId, _ := IfdTagIdWithIdentity(ii) ifdTagId, _ := IfdTagIdWithIdentity(ii)
ib = &IfdBuilder{ ib = &IfdBuilder{
@ -256,6 +222,8 @@ func NewIfdBuilder(ii IfdIdentity, byteOrder binary.ByteOrder) (ib *IfdBuilder)
byteOrder: byteOrder, byteOrder: byteOrder,
tags: make([]*BuilderTag, 0), tags: make([]*BuilderTag, 0),
tagIndex: tagIndex,
} }
return ib return ib
@ -278,6 +246,7 @@ func NewIfdBuilderWithExistingIfd(ifd *Ifd) (ib *IfdBuilder) {
ifdTagId: ifdTagId, ifdTagId: ifdTagId,
byteOrder: ifd.ByteOrder, byteOrder: ifd.ByteOrder,
existingOffset: ifd.Offset, existingOffset: ifd.Offset,
tagIndex: ifd.tagIndex,
} }
return ib return ib
@ -293,7 +262,7 @@ func NewIfdBuilderFromExistingChain(rootIfd *Ifd, itevr *IfdTagEntryValueResolve
for thisExistingIfd := rootIfd; thisExistingIfd != nil; thisExistingIfd = thisExistingIfd.NextIfd { for thisExistingIfd := rootIfd; thisExistingIfd != nil; thisExistingIfd = thisExistingIfd.NextIfd {
ii := thisExistingIfd.Identity() ii := thisExistingIfd.Identity()
newIb := NewIfdBuilder(ii, thisExistingIfd.ByteOrder) newIb := NewIfdBuilder(rootIfd.tagIndex, ii, thisExistingIfd.ByteOrder)
if firstIb == nil { if firstIb == nil {
firstIb = newIb firstIb = newIb
} else { } else {
@ -350,7 +319,7 @@ func GetOrCreateIbFromRootIb(rootIb *IfdBuilder, ifdDesignation string) (ib *Ifd
case "ifd1": case "ifd1":
if ib.nextIb == nil { if ib.nextIb == nil {
ib.nextIb = NewIfdBuilder(RootIi, ib.byteOrder) ib.nextIb = NewIfdBuilder(ib.tagIndex, RootIi, ib.byteOrder)
} }
return ib.nextIb, nil return ib.nextIb, nil
@ -359,7 +328,7 @@ func GetOrCreateIbFromRootIb(rootIb *IfdBuilder, ifdDesignation string) (ib *Ifd
exifIb, err := ib.ChildWithIfdIdentity(ExifIi) exifIb, err := ib.ChildWithIfdIdentity(ExifIi)
if err != nil { if err != nil {
if log.Is(err, ErrChildIbNotFound) == true { if log.Is(err, ErrChildIbNotFound) == true {
exifIb = NewIfdBuilder(ExifIi, ib.byteOrder) exifIb = NewIfdBuilder(exifIb.tagIndex, ExifIi, ib.byteOrder)
err := ib.AddChildIb(exifIb) err := ib.AddChildIb(exifIb)
log.PanicIf(err) log.PanicIf(err)
@ -374,7 +343,7 @@ func GetOrCreateIbFromRootIb(rootIb *IfdBuilder, ifdDesignation string) (ib *Ifd
exifIb, err := ib.ChildWithIfdIdentity(ExifIi) exifIb, err := ib.ChildWithIfdIdentity(ExifIi)
if err != nil { if err != nil {
if log.Is(err, ErrChildIbNotFound) == true { if log.Is(err, ErrChildIbNotFound) == true {
exifIb = NewIfdBuilder(ExifIi, ib.byteOrder) exifIb = NewIfdBuilder(exifIb.tagIndex, ExifIi, ib.byteOrder)
err := ib.AddChildIb(exifIb) err := ib.AddChildIb(exifIb)
log.PanicIf(err) log.PanicIf(err)
@ -391,7 +360,7 @@ func GetOrCreateIbFromRootIb(rootIb *IfdBuilder, ifdDesignation string) (ib *Ifd
iopIb, err := exifIb.ChildWithIfdIdentity(ExifIopIi) iopIb, err := exifIb.ChildWithIfdIdentity(ExifIopIi)
if err != nil { if err != nil {
if log.Is(err, ErrChildIbNotFound) == true { if log.Is(err, ErrChildIbNotFound) == true {
iopIb = NewIfdBuilder(ExifIopIi, ib.byteOrder) iopIb = NewIfdBuilder(iopIb.tagIndex, ExifIopIi, ib.byteOrder)
err := exifIb.AddChildIb(iopIb) err := exifIb.AddChildIb(iopIb)
log.PanicIf(err) log.PanicIf(err)
@ -406,7 +375,7 @@ func GetOrCreateIbFromRootIb(rootIb *IfdBuilder, ifdDesignation string) (ib *Ifd
gpsIb, err := ib.ChildWithIfdIdentity(GpsIi) gpsIb, err := ib.ChildWithIfdIdentity(GpsIi)
if err != nil { if err != nil {
if log.Is(err, ErrChildIbNotFound) == true { if log.Is(err, ErrChildIbNotFound) == true {
gpsIb = NewIfdBuilder(GpsIi, ib.byteOrder) gpsIb = NewIfdBuilder(gpsIb.tagIndex, GpsIi, ib.byteOrder)
err := ib.AddChildIb(gpsIb) err := ib.AddChildIb(gpsIb)
log.PanicIf(err) log.PanicIf(err)
@ -477,7 +446,10 @@ func (ib *IfdBuilder) SetThumbnail(data []byte) (err error) {
err = ib.Set(offsetBt) err = ib.Set(offsetBt)
log.PanicIf(err) log.PanicIf(err)
sizeBt := NewStandardBuilderTag(ib.ii, ThumbnailSizeTagId, ib.byteOrder, []uint32{uint32(len(ib.thumbnailData))}) thumbnailSizeIt, err := ib.tagIndex.Get(ib.ii, ThumbnailSizeTagId)
log.PanicIf(err)
sizeBt := NewStandardBuilderTag(ib.ii, thumbnailSizeIt, ib.byteOrder, []uint32{uint32(len(ib.thumbnailData))})
err = ib.Set(sizeBt) err = ib.Set(sizeBt)
log.PanicIf(err) log.PanicIf(err)
@ -517,7 +489,7 @@ func (ib *IfdBuilder) printTagTree(levels int) {
if isChildIb == true { if isChildIb == true {
tagName = "<Child IFD>" tagName = "<Child IFD>"
} else { } else {
it, err := tagIndex.Get(tag.ii, tag.tagId) it, err := ib.tagIndex.Get(tag.ii, tag.tagId)
if log.Is(err, ErrTagNotFound) == true { if log.Is(err, ErrTagNotFound) == true {
tagName = "<UNKNOWN>" tagName = "<UNKNOWN>"
} else if err != nil { } else if err != nil {
@ -817,7 +789,7 @@ func (ib *IfdBuilder) FindTagWithName(tagName string) (bt *BuilderTag, err error
} }
}() }()
it, err := tagIndex.GetWithName(ib.ii, tagName) it, err := ib.tagIndex.GetWithName(ib.ii, tagName)
log.PanicIf(err) log.PanicIf(err)
found, err := ib.FindN(it.Id, 1) found, err := ib.FindN(it.Id, 1)
@ -1025,10 +997,8 @@ func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, itevr *IfdTagEntryValueResol
} else { } else {
var err error var err error
// TODO(dustin): !! Not correct. If we're adding from existing and it's an unknown-type tag that we can't parse, we're just going to be seting the placeholder even though there's nothing stopping us from just taking the raw bytes (other than some design decisions that we'd have to make in order to do this). // TODO(dustin): !! Not correct. If we're adding from existing and it's an unknown-type tag that we can't parse, we're just going to be seting the placeholder even though there's nothing stopping us from just taking the raw bytes (other than some design decisions that we'd have to make in order to do this).
valueBytes, err := itevr.ValueBytes(ite) valueBytes, err := itevr.ValueBytes(ite)
if err != nil { if err != nil {
if log.Is(err, ErrUnhandledUnknownTypedTag) == true { if log.Is(err, ErrUnhandledUnknownTypedTag) == true {
@ -1060,7 +1030,10 @@ func (ib *IfdBuilder) AddStandard(tagId uint16, value interface{}) (err error) {
} }
}() }()
bt := NewStandardBuilderTag(ib.ii, tagId, ib.byteOrder, value) it, err := ib.tagIndex.Get(ib.ii, tagId)
log.PanicIf(err)
bt := NewStandardBuilderTag(ib.ii, it, ib.byteOrder, value)
err = ib.add(bt) err = ib.add(bt)
log.PanicIf(err) log.PanicIf(err)
@ -1078,7 +1051,10 @@ func (ib *IfdBuilder) AddStandardWithName(tagName string, value interface{}) (er
} }
}() }()
bt := NewStandardBuilderTagWithName(ib.ii, tagName, ib.byteOrder, value) it, err := ib.tagIndex.GetWithName(ib.ii, tagName)
log.PanicIf(err)
bt := NewStandardBuilderTag(ib.ii, it, ib.byteOrder, value)
err = ib.add(bt) err = ib.add(bt)
log.PanicIf(err) log.PanicIf(err)
@ -1097,7 +1073,10 @@ func (ib *IfdBuilder) SetStandard(tagId uint16, value interface{}) (err error) {
// TODO(dustin): !! Add test for this function. // TODO(dustin): !! Add test for this function.
bt := NewStandardBuilderTag(ib.ii, tagId, ib.byteOrder, value) it, err := ib.tagIndex.Get(ib.ii, tagId)
log.PanicIf(err)
bt := NewStandardBuilderTag(ib.ii, it, ib.byteOrder, value)
i, err := ib.Find(tagId) i, err := ib.Find(tagId)
if err != nil { if err != nil {
@ -1125,7 +1104,10 @@ func (ib *IfdBuilder) SetStandardWithName(tagName string, value interface{}) (er
// TODO(dustin): !! Add test for this function. // TODO(dustin): !! Add test for this function.
bt := NewStandardBuilderTagWithName(ib.ii, tagName, ib.byteOrder, value) it, err := ib.tagIndex.GetWithName(ib.ii, tagName)
log.PanicIf(err)
bt := NewStandardBuilderTag(ib.ii, it, ib.byteOrder, value)
i, err := ib.Find(bt.tagId) i, err := ib.Find(bt.tagId)
if err != nil { if err != nil {

View File

@ -1,11 +1,11 @@
package exif package exif
import ( import (
"testing"
"bytes" "bytes"
"reflect"
"fmt" "fmt"
"reflect"
"strings" "strings"
"testing"
"github.com/dsoprea/go-logging" "github.com/dsoprea/go-logging"
) )
@ -94,7 +94,6 @@ func Test_ByteWriter_WriteFourBytes_TooMany(t *testing.T) {
} }
} }
func Test_IfdDataAllocator_Allocate_InitialOffset1(t *testing.T) { func Test_IfdDataAllocator_Allocate_InitialOffset1(t *testing.T) {
addressableOffset := uint32(0) addressableOffset := uint32(0)
ida := newIfdDataAllocator(addressableOffset) ida := newIfdDataAllocator(addressableOffset)
@ -169,7 +168,6 @@ func Test_IfdDataAllocator_Allocate_InitialOffset2(t *testing.T) {
} }
} }
func Test_IfdByteEncoder__Arithmetic(t *testing.T) { func Test_IfdByteEncoder__Arithmetic(t *testing.T) {
ibe := NewIfdByteEncoder() ibe := NewIfdByteEncoder()
@ -190,9 +188,13 @@ func Test_IfdByteEncoder_encodeTagToBytes_bytes_embedded1(t *testing.T) {
ibe := NewIfdByteEncoder() ibe := NewIfdByteEncoder()
ib := NewIfdBuilder(GpsIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, GpsIi, TestDefaultByteOrder)
bt := NewStandardBuilderTag(GpsIi, uint16(0x0000), TestDefaultByteOrder, []uint8 { uint8(0x12) }) it, err := ti.Get(ib.ii, uint16(0x0000))
log.PanicIf(err)
bt := NewStandardBuilderTag(GpsIi, it, TestDefaultByteOrder, []uint8{uint8(0x12)})
b := new(bytes.Buffer) b := new(bytes.Buffer)
bw := NewByteWriter(b, TestDefaultByteOrder) bw := NewByteWriter(b, TestDefaultByteOrder)
@ -215,9 +217,13 @@ func Test_IfdByteEncoder_encodeTagToBytes_bytes_embedded1(t *testing.T) {
func Test_IfdByteEncoder_encodeTagToBytes_bytes_embedded2(t *testing.T) { func Test_IfdByteEncoder_encodeTagToBytes_bytes_embedded2(t *testing.T) {
ibe := NewIfdByteEncoder() ibe := NewIfdByteEncoder()
ib := NewIfdBuilder(GpsIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, GpsIi, TestDefaultByteOrder)
bt := NewStandardBuilderTag(GpsIi, uint16(0x0000), TestDefaultByteOrder, []uint8 { uint8(0x12), uint8(0x34), uint8(0x56), uint8(0x78) }) it, err := ti.Get(ib.ii, uint16(0x0000))
log.PanicIf(err)
bt := NewStandardBuilderTag(GpsIi, it, TestDefaultByteOrder, []uint8{uint8(0x12), uint8(0x34), uint8(0x56), uint8(0x78)})
b := new(bytes.Buffer) b := new(bytes.Buffer)
bw := NewByteWriter(b, TestDefaultByteOrder) bw := NewByteWriter(b, TestDefaultByteOrder)
@ -240,7 +246,8 @@ func Test_IfdByteEncoder_encodeTagToBytes_bytes_embedded2(t *testing.T) {
func Test_IfdByteEncoder_encodeTagToBytes_bytes_allocated(t *testing.T) { func Test_IfdByteEncoder_encodeTagToBytes_bytes_allocated(t *testing.T) {
ibe := NewIfdByteEncoder() ibe := NewIfdByteEncoder()
ib := NewIfdBuilder(GpsIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, GpsIi, TestDefaultByteOrder)
b := new(bytes.Buffer) b := new(bytes.Buffer)
bw := NewByteWriter(b, TestDefaultByteOrder) bw := NewByteWriter(b, TestDefaultByteOrder)
@ -248,7 +255,10 @@ func Test_IfdByteEncoder_encodeTagToBytes_bytes_allocated(t *testing.T) {
addressableOffset := uint32(0x1234) addressableOffset := uint32(0x1234)
ida := newIfdDataAllocator(addressableOffset) ida := newIfdDataAllocator(addressableOffset)
bt := NewStandardBuilderTag(GpsIi, uint16(0x0000), TestDefaultByteOrder, []uint8 { uint8(0x12), uint8(0x34), uint8(0x56), uint8(0x78), uint8(0x9a) }) it, err := ti.Get(ib.ii, uint16(0x0000))
log.PanicIf(err)
bt := NewStandardBuilderTag(GpsIi, it, TestDefaultByteOrder, []uint8{uint8(0x12), uint8(0x34), uint8(0x56), uint8(0x78), uint8(0x9a)})
childIfdBlock, err := ibe.encodeTagToBytes(ib, bt, bw, ida, uint32(0)) childIfdBlock, err := ibe.encodeTagToBytes(ib, bt, bw, ida, uint32(0))
log.PanicIf(err) log.PanicIf(err)
@ -265,7 +275,7 @@ func Test_IfdByteEncoder_encodeTagToBytes_bytes_allocated(t *testing.T) {
// Test that another allocation encodes to the new offset. // Test that another allocation encodes to the new offset.
bt = NewStandardBuilderTag(GpsIi, uint16(0x0000), TestDefaultByteOrder, []uint8 { uint8(0xbc), uint8(0xde), uint8(0xf0), uint8(0x12), uint8(0x34) }) bt = NewStandardBuilderTag(GpsIi, it, TestDefaultByteOrder, []uint8{uint8(0xbc), uint8(0xde), uint8(0xf0), uint8(0x12), uint8(0x34)})
childIfdBlock, err = ibe.encodeTagToBytes(ib, bt, bw, ida, uint32(0)) childIfdBlock, err = ibe.encodeTagToBytes(ib, bt, bw, ida, uint32(0))
log.PanicIf(err) log.PanicIf(err)
@ -290,7 +300,8 @@ func Test_IfdByteEncoder_encodeTagToBytes_bytes_allocated(t *testing.T) {
func Test_IfdByteEncoder_encodeTagToBytes_childIfd__withoutAllocate(t *testing.T) { func Test_IfdByteEncoder_encodeTagToBytes_childIfd__withoutAllocate(t *testing.T) {
ibe := NewIfdByteEncoder() ibe := NewIfdByteEncoder()
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
b := new(bytes.Buffer) b := new(bytes.Buffer)
bw := NewByteWriter(b, TestDefaultByteOrder) bw := NewByteWriter(b, TestDefaultByteOrder)
@ -298,7 +309,7 @@ func Test_IfdByteEncoder_encodeTagToBytes_childIfd__withoutAllocate(t *testing.T
addressableOffset := uint32(0x1234) addressableOffset := uint32(0x1234)
ida := newIfdDataAllocator(addressableOffset) ida := newIfdDataAllocator(addressableOffset)
childIb := NewIfdBuilder(ExifIi, TestDefaultByteOrder) childIb := NewIfdBuilder(ti, ExifIi, TestDefaultByteOrder)
tagValue := NewIfdBuilderTagValueFromIfdBuilder(childIb) tagValue := NewIfdBuilderTagValueFromIfdBuilder(childIb)
bt := NewChildIfdBuilderTag(RootIi, IfdExifId, tagValue) bt := NewChildIfdBuilderTag(RootIi, IfdExifId, tagValue)
@ -320,7 +331,8 @@ func Test_IfdByteEncoder_encodeTagToBytes_childIfd__withAllocate(t *testing.T) {
// space for and then attach to a tag (which would normally be an entry, // space for and then attach to a tag (which would normally be an entry,
// then, in a higher IFD). // then, in a higher IFD).
childIb := NewIfdBuilder(ExifIi, TestDefaultByteOrder) ti := NewTagIndex()
childIb := NewIfdBuilder(ti, ExifIi, TestDefaultByteOrder)
childIbTestTag := &BuilderTag{ childIbTestTag := &BuilderTag{
ii: ExifIi, ii: ExifIi,
@ -343,7 +355,7 @@ func Test_IfdByteEncoder_encodeTagToBytes_childIfd__withAllocate(t *testing.T) {
ibe := NewIfdByteEncoder() ibe := NewIfdByteEncoder()
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
b := new(bytes.Buffer) b := new(bytes.Buffer)
bw := NewByteWriter(b, TestDefaultByteOrder) bw := NewByteWriter(b, TestDefaultByteOrder)
@ -443,10 +455,14 @@ func Test_IfdByteEncoder_encodeTagToBytes_simpleTag_allocate(t *testing.T) {
ibe := NewIfdByteEncoder() ibe := NewIfdByteEncoder()
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
it, err := ib.tagIndex.Get(ib.ii, uint16(0x000b))
log.PanicIf(err)
valueString := "testvalue" valueString := "testvalue"
bt := NewStandardBuilderTag(RootIi, uint16(0x000b), TestDefaultByteOrder, valueString) bt := NewStandardBuilderTag(RootIi, it, TestDefaultByteOrder, valueString)
b := new(bytes.Buffer) b := new(bytes.Buffer)
bw := NewByteWriter(b, TestDefaultByteOrder) bw := NewByteWriter(b, TestDefaultByteOrder)
@ -474,7 +490,6 @@ func Test_IfdByteEncoder_encodeTagToBytes_simpleTag_allocate(t *testing.T) {
t.Fatalf("Child IFD not have been allocated.") t.Fatalf("Child IFD not have been allocated.")
} }
ite, err := ParseOneTag(RootIi, TestDefaultByteOrder, tagBytes, false) ite, err := ParseOneTag(RootIi, TestDefaultByteOrder, tagBytes, false)
log.PanicIf(err) log.PanicIf(err)
@ -508,7 +523,8 @@ func Test_IfdByteEncoder_encodeTagToBytes_simpleTag_allocate(t *testing.T) {
} }
func getExifSimpleTestIb() *IfdBuilder { func getExifSimpleTestIb() *IfdBuilder {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
err := ib.AddStandard(0x000b, "asciivalue") err := ib.AddStandard(0x000b, "asciivalue")
log.PanicIf(err) log.PanicIf(err)
@ -526,7 +542,9 @@ func getExifSimpleTestIb() *IfdBuilder {
} }
func validateExifSimpleTestIb(exifData []byte, t *testing.T) { func validateExifSimpleTestIb(exifData []byte, t *testing.T) {
eh, index, err := Collect(exifData) ti := NewTagIndex()
eh, index, err := Collect(ti, exifData)
log.PanicIf(err) log.PanicIf(err)
if eh.ByteOrder != TestDefaultByteOrder { if eh.ByteOrder != TestDefaultByteOrder {
@ -557,7 +575,6 @@ func validateExifSimpleTestIb(exifData []byte, t *testing.T) {
t.Fatalf("Next-IFD pointer is non-nil.") t.Fatalf("Next-IFD pointer is non-nil.")
} }
// Verify the values by using the actual, orginal types (this is awesome). // Verify the values by using the actual, orginal types (this is awesome).
addressableData := exifData[ExifAddressableAreaStart:] addressableData := exifData[ExifAddressableAreaStart:]
@ -632,7 +649,6 @@ func Test_IfdByteEncoder_encodeIfdToBytes_simple(t *testing.T) {
// - Next IFD offset // - Next IFD offset
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// IFD data block. // IFD data block.
// - The one ASCII value // - The one ASCII value
@ -657,7 +673,6 @@ func Test_IfdByteEncoder_encodeIfdToBytes_fullExif(t *testing.T) {
ib := getExifSimpleTestIb() ib := getExifSimpleTestIb()
// Encode the IFD to a byte stream. // Encode the IFD to a byte stream.
ibe := NewIfdByteEncoder() ibe := NewIfdByteEncoder()
@ -677,7 +692,6 @@ func Test_IfdByteEncoder_encodeIfdToBytes_fullExif(t *testing.T) {
t.Fatalf("Table-and-data size doesn't match what was expected: (%d) != (%d + %d)", len(tableAndAllocated), tableSize, allocatedDataSize) t.Fatalf("Table-and-data size doesn't match what was expected: (%d) != (%d + %d)", len(tableAndAllocated), tableSize, allocatedDataSize)
} }
// Wrap the IFD in a formal EXIF block. // Wrap the IFD in a formal EXIF block.
b := new(bytes.Buffer) b := new(bytes.Buffer)
@ -691,7 +705,6 @@ func Test_IfdByteEncoder_encodeIfdToBytes_fullExif(t *testing.T) {
_, err = b.Write(tableAndAllocated) _, err = b.Write(tableAndAllocated)
log.PanicIf(err) log.PanicIf(err)
// Now, try parsing it as EXIF data, making sure to resolve (read: // Now, try parsing it as EXIF data, making sure to resolve (read:
// dereference) the values (which will include the allocated ones). // dereference) the values (which will include the allocated ones).
@ -709,7 +722,6 @@ func Test_IfdByteEncoder_EncodeToExifPayload(t *testing.T) {
ib := getExifSimpleTestIb() ib := getExifSimpleTestIb()
// Encode the IFD to a byte stream. // Encode the IFD to a byte stream.
ibe := NewIfdByteEncoder() ibe := NewIfdByteEncoder()
@ -730,7 +742,6 @@ func Test_IfdByteEncoder_EncodeToExifPayload(t *testing.T) {
_, err = b.Write(encodedIfds) _, err = b.Write(encodedIfds)
log.PanicIf(err) log.PanicIf(err)
// Now, try parsing it as EXIF data, making sure to resolve (read: // Now, try parsing it as EXIF data, making sure to resolve (read:
// dereference) the values (which will include the allocated ones). // dereference) the values (which will include the allocated ones).
@ -759,7 +770,8 @@ func Test_IfdByteEncoder_EncodeToExif_WithChildAndSibling(t *testing.T) {
} }
}() }()
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
err := ib.AddStandard(0x000b, "asciivalue") err := ib.AddStandard(0x000b, "asciivalue")
log.PanicIf(err) log.PanicIf(err)
@ -767,10 +779,9 @@ func Test_IfdByteEncoder_EncodeToExif_WithChildAndSibling(t *testing.T) {
err = ib.AddStandard(0x00ff, []uint16{0x1122}) err = ib.AddStandard(0x00ff, []uint16{0x1122})
log.PanicIf(err) log.PanicIf(err)
// Add a child IB right in the middle. // Add a child IB right in the middle.
childIb := NewIfdBuilder(ExifIi, TestDefaultByteOrder) childIb := NewIfdBuilder(ti, ExifIi, TestDefaultByteOrder)
err = childIb.AddStandardWithName("ISOSpeedRatings", []uint16{0x1122}) err = childIb.AddStandardWithName("ISOSpeedRatings", []uint16{0x1122})
log.PanicIf(err) log.PanicIf(err)
@ -781,15 +792,13 @@ func Test_IfdByteEncoder_EncodeToExif_WithChildAndSibling(t *testing.T) {
err = ib.AddChildIb(childIb) err = ib.AddChildIb(childIb)
log.PanicIf(err) log.PanicIf(err)
err = ib.AddStandard(0x0100, []uint32{0x33445566}) err = ib.AddStandard(0x0100, []uint32{0x33445566})
log.PanicIf(err) log.PanicIf(err)
// Add another child IB, just to ensure a little more punishment and make // Add another child IB, just to ensure a little more punishment and make
// sure we're managing our allocation offsets correctly. // sure we're managing our allocation offsets correctly.
childIb2 := NewIfdBuilder(GpsIi, TestDefaultByteOrder) childIb2 := NewIfdBuilder(ti, GpsIi, TestDefaultByteOrder)
err = childIb2.AddStandardWithName("GPSAltitudeRef", []uint8{0x11, 0x22}) err = childIb2.AddStandardWithName("GPSAltitudeRef", []uint8{0x11, 0x22})
log.PanicIf(err) log.PanicIf(err)
@ -797,15 +806,13 @@ func Test_IfdByteEncoder_EncodeToExif_WithChildAndSibling(t *testing.T) {
err = ib.AddChildIb(childIb2) err = ib.AddChildIb(childIb2)
log.PanicIf(err) log.PanicIf(err)
err = ib.AddStandard(0x013e, []Rational{{Numerator: 0x11112222, Denominator: 0x33334444}}) err = ib.AddStandard(0x013e, []Rational{{Numerator: 0x11112222, Denominator: 0x33334444}})
log.PanicIf(err) log.PanicIf(err)
// Link to another IB (sibling relationship). The root/standard IFD may // Link to another IB (sibling relationship). The root/standard IFD may
// occur twice in some JPEGs (for thumbnail or FlashPix images). // occur twice in some JPEGs (for thumbnail or FlashPix images).
nextIb := NewIfdBuilder(RootIi, TestDefaultByteOrder) nextIb := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
err = nextIb.AddStandard(0x0101, []uint32{0x11223344}) err = nextIb.AddStandard(0x0101, []uint32{0x11223344})
log.PanicIf(err) log.PanicIf(err)
@ -815,7 +822,6 @@ func Test_IfdByteEncoder_EncodeToExif_WithChildAndSibling(t *testing.T) {
ib.SetNextIb(nextIb) ib.SetNextIb(nextIb)
// Encode. // Encode.
ibe := NewIfdByteEncoder() ibe := NewIfdByteEncoder()
@ -823,10 +829,9 @@ func Test_IfdByteEncoder_EncodeToExif_WithChildAndSibling(t *testing.T) {
exifData, err := ibe.EncodeToExif(ib) exifData, err := ibe.EncodeToExif(ib)
log.PanicIf(err) log.PanicIf(err)
// Parse. // Parse.
_, index, err := Collect(exifData) _, index, err := Collect(ti, exifData)
log.PanicIf(err) log.PanicIf(err)
tagsDump := index.RootIfd.DumpTree() tagsDump := index.RootIfd.DumpTree()
@ -875,7 +880,9 @@ func Test_IfdByteEncoder_EncodeToExif_WithChildAndSibling(t *testing.T) {
func ExampleIfdByteEncoder_EncodeToExif() { func ExampleIfdByteEncoder_EncodeToExif() {
// Construct an IFD. // Construct an IFD.
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
err := ib.AddStandardWithName("ProcessingSoftware", "asciivalue") err := ib.AddStandardWithName("ProcessingSoftware", "asciivalue")
log.PanicIf(err) log.PanicIf(err)
@ -895,7 +902,6 @@ func ExampleIfdByteEncoder_EncodeToExif() {
err = ib.AddStandardWithName("ShutterSpeedValue", []SignedRational{{Numerator: 0x11112222, Denominator: 0x33334444}}) err = ib.AddStandardWithName("ShutterSpeedValue", []SignedRational{{Numerator: 0x11112222, Denominator: 0x33334444}})
log.PanicIf(err) log.PanicIf(err)
// Encode it. // Encode it.
ibe := NewIfdByteEncoder() ibe := NewIfdByteEncoder()
@ -903,10 +909,9 @@ func ExampleIfdByteEncoder_EncodeToExif() {
exifData, err := ibe.EncodeToExif(ib) exifData, err := ibe.EncodeToExif(ib)
log.PanicIf(err) log.PanicIf(err)
// Parse it so we can see it. // Parse it so we can see it.
_, index, err := Collect(exifData) _, index, err := Collect(ti, exifData)
log.PanicIf(err) log.PanicIf(err)
// addressableData is the byte-slice where the allocated data can be // addressableData is the byte-slice where the allocated data can be

View File

@ -12,7 +12,8 @@ import (
) )
func TestAdd(t *testing.T) { func TestAdd(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{ bt := &BuilderTag{
ii: RootIi, ii: RootIi,
@ -98,8 +99,10 @@ func TestAdd(t *testing.T) {
} }
func TestSetNextIb(t *testing.T) { func TestSetNextIb(t *testing.T) {
ib1 := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib2 := NewIfdBuilder(RootIi, TestDefaultByteOrder)
ib1 := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
ib2 := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
if ib1.nextIb != nil { if ib1.nextIb != nil {
t.Fatalf("Next-IFD for IB1 not initially terminal.") t.Fatalf("Next-IFD for IB1 not initially terminal.")
@ -117,7 +120,8 @@ func TestSetNextIb(t *testing.T) {
func TestAddChildIb(t *testing.T) { func TestAddChildIb(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{ bt := &BuilderTag{
ii: RootIi, ii: RootIi,
@ -131,7 +135,7 @@ func TestAddChildIb(t *testing.T) {
exifIi, _ := IfdIdOrFail(IfdStandard, IfdExif) exifIi, _ := IfdIdOrFail(IfdStandard, IfdExif)
ibChild := NewIfdBuilder(exifIi, TestDefaultByteOrder) ibChild := NewIfdBuilder(ti, exifIi, TestDefaultByteOrder)
err = ib.AddChildIb(ibChild) err = ib.AddChildIb(ibChild)
log.PanicIf(err) log.PanicIf(err)
@ -164,7 +168,8 @@ func TestAddTagsFromExisting(t *testing.T) {
} }
}() }()
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
entries := make([]*IfdTagEntry, 3) entries := make([]*IfdTagEntry, 3)
@ -194,6 +199,7 @@ func TestAddTagsFromExisting(t *testing.T) {
ifd := &Ifd{ ifd := &Ifd{
Ii: RootIi, Ii: RootIi,
Entries: entries, Entries: entries,
tagIndex: ti,
} }
err := ib.AddTagsFromExisting(ifd, nil, nil, nil) err := ib.AddTagsFromExisting(ifd, nil, nil, nil)
@ -211,7 +217,8 @@ func TestAddTagsFromExisting(t *testing.T) {
} }
func TestAddTagsFromExisting__Includes(t *testing.T) { func TestAddTagsFromExisting__Includes(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
entries := make([]*IfdTagEntry, 3) entries := make([]*IfdTagEntry, 3)
@ -237,6 +244,7 @@ func TestAddTagsFromExisting__Includes(t *testing.T) {
ifd := &Ifd{ ifd := &Ifd{
Ii: RootIi, Ii: RootIi,
Entries: entries, Entries: entries,
tagIndex: ti,
} }
err := ib.AddTagsFromExisting(ifd, nil, []uint16{0x33}, nil) err := ib.AddTagsFromExisting(ifd, nil, []uint16{0x33}, nil)
@ -250,7 +258,8 @@ func TestAddTagsFromExisting__Includes(t *testing.T) {
} }
func TestAddTagsFromExisting__Excludes(t *testing.T) { func TestAddTagsFromExisting__Excludes(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
entries := make([]*IfdTagEntry, 3) entries := make([]*IfdTagEntry, 3)
@ -276,6 +285,7 @@ func TestAddTagsFromExisting__Excludes(t *testing.T) {
ifd := &Ifd{ ifd := &Ifd{
Ii: RootIi, Ii: RootIi,
Entries: entries, Entries: entries,
tagIndex: ti,
} }
err := ib.AddTagsFromExisting(ifd, nil, nil, []uint16{0x11}) err := ib.AddTagsFromExisting(ifd, nil, nil, []uint16{0x11})
@ -289,7 +299,8 @@ func TestAddTagsFromExisting__Excludes(t *testing.T) {
} }
func TestFindN_First_1(t *testing.T) { func TestFindN_First_1(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{ bt := &BuilderTag{
ii: RootIi, ii: RootIi,
@ -339,7 +350,8 @@ func TestFindN_First_1(t *testing.T) {
} }
func TestFindN_First_2_1Returned(t *testing.T) { func TestFindN_First_2_1Returned(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{ bt := &BuilderTag{
ii: RootIi, ii: RootIi,
@ -389,7 +401,8 @@ func TestFindN_First_2_1Returned(t *testing.T) {
} }
func TestFindN_First_2_2Returned(t *testing.T) { func TestFindN_First_2_2Returned(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{ bt := &BuilderTag{
ii: RootIi, ii: RootIi,
@ -466,7 +479,8 @@ func TestFindN_First_2_2Returned(t *testing.T) {
} }
func TestFindN_Middle_WithDuplicates(t *testing.T) { func TestFindN_Middle_WithDuplicates(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{ bt := &BuilderTag{
ii: RootIi, ii: RootIi,
@ -546,7 +560,8 @@ func TestFindN_Middle_WithDuplicates(t *testing.T) {
} }
func TestFindN_Middle_NoDuplicates(t *testing.T) { func TestFindN_Middle_NoDuplicates(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{ bt := &BuilderTag{
ii: RootIi, ii: RootIi,
@ -606,7 +621,8 @@ func TestFindN_Middle_NoDuplicates(t *testing.T) {
} }
func TestFindN_Miss(t *testing.T) { func TestFindN_Miss(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
found, err := ib.FindN(0x11, 1) found, err := ib.FindN(0x11, 1)
log.PanicIf(err) log.PanicIf(err)
@ -617,7 +633,8 @@ func TestFindN_Miss(t *testing.T) {
} }
func TestFind_Hit(t *testing.T) { func TestFind_Hit(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{ bt := &BuilderTag{
ii: RootIi, ii: RootIi,
@ -675,7 +692,8 @@ func TestFind_Hit(t *testing.T) {
} }
func TestFind_Miss(t *testing.T) { func TestFind_Miss(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{ bt := &BuilderTag{
ii: RootIi, ii: RootIi,
@ -726,7 +744,8 @@ func TestFind_Miss(t *testing.T) {
} }
func TestReplace(t *testing.T) { func TestReplace(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{ bt := &BuilderTag{
ii: RootIi, ii: RootIi,
@ -788,7 +807,8 @@ func TestReplace(t *testing.T) {
} }
func TestReplaceN(t *testing.T) { func TestReplaceN(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{ bt := &BuilderTag{
ii: RootIi, ii: RootIi,
@ -850,7 +870,8 @@ func TestReplaceN(t *testing.T) {
} }
func TestDeleteFirst(t *testing.T) { func TestDeleteFirst(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{ bt := &BuilderTag{
ii: RootIi, ii: RootIi,
@ -946,7 +967,8 @@ func TestDeleteFirst(t *testing.T) {
} }
func TestDeleteN(t *testing.T) { func TestDeleteN(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{ bt := &BuilderTag{
ii: RootIi, ii: RootIi,
@ -1042,7 +1064,8 @@ func TestDeleteN(t *testing.T) {
} }
func TestDeleteN_Two(t *testing.T) { func TestDeleteN_Two(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{ bt := &BuilderTag{
ii: RootIi, ii: RootIi,
@ -1122,7 +1145,8 @@ func TestDeleteN_Two(t *testing.T) {
} }
func TestDeleteAll(t *testing.T) { func TestDeleteAll(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{ bt := &BuilderTag{
ii: RootIi, ii: RootIi,
@ -1216,7 +1240,9 @@ func Test_IfdBuilder_CreateIfdBuilderFromExistingChain(t *testing.T) {
rawExif, err := SearchFileAndExtractExif(filepath) rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err) log.PanicIf(err)
_, index, err := Collect(rawExif) ti := NewTagIndex()
_, index, err := Collect(ti, rawExif)
log.PanicIf(err) log.PanicIf(err)
itevr := NewIfdTagEntryValueResolver(rawExif, index.RootIfd.ByteOrder) itevr := NewIfdTagEntryValueResolver(rawExif, index.RootIfd.ByteOrder)
@ -1296,7 +1322,9 @@ func Test_IfdBuilder_CreateIfdBuilderFromExistingChain_RealData(t *testing.T) {
// Decode from binary. // Decode from binary.
_, originalIndex, err := Collect(rawExif) ti := NewTagIndex()
_, originalIndex, err := Collect(ti, rawExif)
log.PanicIf(err) log.PanicIf(err)
originalThumbnailData, err := originalIndex.RootIfd.NextIfd.Thumbnail() originalThumbnailData, err := originalIndex.RootIfd.NextIfd.Thumbnail()
@ -1316,7 +1344,7 @@ func Test_IfdBuilder_CreateIfdBuilderFromExistingChain_RealData(t *testing.T) {
// Parse again. // Parse again.
_, recoveredIndex, err := Collect(updatedExif) _, recoveredIndex, err := Collect(ti, updatedExif)
log.PanicIf(err) log.PanicIf(err)
recoveredTags := recoveredIndex.RootIfd.DumpTags() recoveredTags := recoveredIndex.RootIfd.DumpTags()
@ -1400,143 +1428,145 @@ func Test_IfdBuilder_CreateIfdBuilderFromExistingChain_RealData(t *testing.T) {
} }
} }
func Test_IfdBuilder_CreateIfdBuilderFromExistingChain_RealData_WithUpdate(t *testing.T) { // func Test_IfdBuilder_CreateIfdBuilderFromExistingChain_RealData_WithUpdate(t *testing.T) {
filepath := path.Join(assetsPath, "NDM_8901.jpg") // filepath := path.Join(assetsPath, "NDM_8901.jpg")
rawExif, err := SearchFileAndExtractExif(filepath) // rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err) // log.PanicIf(err)
// Decode from binary. // // Decode from binary.
_, originalIndex, err := Collect(rawExif) // ti := NewTagIndex()
log.PanicIf(err)
originalThumbnailData, err := originalIndex.RootIfd.NextIfd.Thumbnail() // _, originalIndex, err := Collect(ti, rawExif)
log.PanicIf(err) // log.PanicIf(err)
originalTags := originalIndex.RootIfd.DumpTags() // originalThumbnailData, err := originalIndex.RootIfd.NextIfd.Thumbnail()
// log.PanicIf(err)
// Encode back to binary. // originalTags := originalIndex.RootIfd.DumpTags()
ibe := NewIfdByteEncoder() // // Encode back to binary.
itevr := NewIfdTagEntryValueResolver(rawExif, originalIndex.RootIfd.ByteOrder) // ibe := NewIfdByteEncoder()
rootIb := NewIfdBuilderFromExistingChain(originalIndex.RootIfd, itevr)
// Update a tag,. // itevr := NewIfdTagEntryValueResolver(rawExif, originalIndex.RootIfd.ByteOrder)
// rootIb := NewIfdBuilderFromExistingChain(originalIndex.RootIfd, itevr)
exifBt, err := rootIb.FindTagWithName("ExifTag") // // Update a tag,.
log.PanicIf(err)
ucBt, err := exifBt.value.Ib().FindTagWithName("UserComment") // exifBt, err := rootIb.FindTagWithName("ExifTag")
log.PanicIf(err) // log.PanicIf(err)
uc := TagUnknownType_9298_UserComment{ // ucBt, err := exifBt.value.Ib().FindTagWithName("UserComment")
EncodingType: TagUnknownType_9298_UserComment_Encoding_ASCII, // log.PanicIf(err)
EncodingBytes: []byte("TEST COMMENT"),
}
err = ucBt.SetValue(rootIb.byteOrder, uc) // uc := TagUnknownType_9298_UserComment{
log.PanicIf(err) // EncodingType: TagUnknownType_9298_UserComment_Encoding_ASCII,
// EncodingBytes: []byte("TEST COMMENT"),
// }
// Encode. // err = ucBt.SetValue(rootIb.byteOrder, uc)
// log.PanicIf(err)
updatedExif, err := ibe.EncodeToExif(rootIb) // // Encode.
log.PanicIf(err)
// Parse again. // updatedExif, err := ibe.EncodeToExif(rootIb)
// log.PanicIf(err)
_, recoveredIndex, err := Collect(updatedExif) // // Parse again.
log.PanicIf(err)
recoveredTags := recoveredIndex.RootIfd.DumpTags() // _, recoveredIndex, err := Collect(ti, updatedExif)
// log.PanicIf(err)
recoveredThumbnailData, err := recoveredIndex.RootIfd.NextIfd.Thumbnail() // recoveredTags := recoveredIndex.RootIfd.DumpTags()
log.PanicIf(err)
// Check the thumbnail. // recoveredThumbnailData, err := recoveredIndex.RootIfd.NextIfd.Thumbnail()
// log.PanicIf(err)
if bytes.Compare(recoveredThumbnailData, originalThumbnailData) != 0 { // // Check the thumbnail.
t.Fatalf("recovered thumbnail does not match original")
}
// Validate that all of the same IFDs were presented. // if bytes.Compare(recoveredThumbnailData, originalThumbnailData) != 0 {
// t.Fatalf("recovered thumbnail does not match original")
// }
originalIfdTags := make([][2]interface{}, 0) // // Validate that all of the same IFDs were presented.
for _, ite := range originalTags {
if ite.ChildIfdName != "" {
originalIfdTags = append(originalIfdTags, [2]interface{}{ite.Ii, ite.TagId})
}
}
recoveredIfdTags := make([][2]interface{}, 0) // originalIfdTags := make([][2]interface{}, 0)
for _, ite := range recoveredTags { // for _, ite := range originalTags {
if ite.ChildIfdName != "" { // if ite.ChildIfdName != "" {
recoveredIfdTags = append(recoveredIfdTags, [2]interface{}{ite.Ii, ite.TagId}) // originalIfdTags = append(originalIfdTags, [2]interface{}{ite.Ii, ite.TagId})
} // }
} // }
if reflect.DeepEqual(recoveredIfdTags, originalIfdTags) != true { // recoveredIfdTags := make([][2]interface{}, 0)
fmt.Printf("Original IFD tags:\n\n") // for _, ite := range recoveredTags {
// if ite.ChildIfdName != "" {
// recoveredIfdTags = append(recoveredIfdTags, [2]interface{}{ite.Ii, ite.TagId})
// }
// }
for i, x := range originalIfdTags { // if reflect.DeepEqual(recoveredIfdTags, originalIfdTags) != true {
fmt.Printf(" %02d %v\n", i, x) // fmt.Printf("Original IFD tags:\n\n")
}
fmt.Printf("\nRecovered IFD tags:\n\n") // for i, x := range originalIfdTags {
// fmt.Printf(" %02d %v\n", i, x)
// }
for i, x := range recoveredIfdTags { // fmt.Printf("\nRecovered IFD tags:\n\n")
fmt.Printf(" %02d %v\n", i, x)
}
fmt.Printf("\n") // for i, x := range recoveredIfdTags {
// fmt.Printf(" %02d %v\n", i, x)
// }
t.Fatalf("Recovered IFD tags are not correct.") // fmt.Printf("\n")
}
// Validate that all of the tags owned by the IFDs were presented. Note // t.Fatalf("Recovered IFD tags are not correct.")
// that the thumbnail tags are not kept but only produced on the fly, which // }
// is why we check it above.
if len(recoveredTags) != len(originalTags) { // // Validate that all of the tags owned by the IFDs were presented. Note
t.Fatalf("Recovered tag-count does not match original.") // // that the thumbnail tags are not kept but only produced on the fly, which
} // // is why we check it above.
for i, recoveredIte := range recoveredTags { // if len(recoveredTags) != len(originalTags) {
if recoveredIte.ChildIfdName != "" { // t.Fatalf("Recovered tag-count does not match original.")
continue // }
}
originalIte := originalTags[i] // for i, recoveredIte := range recoveredTags {
// if recoveredIte.ChildIfdName != "" {
// continue
// }
if recoveredIte.Ii != originalIte.Ii { // originalIte := originalTags[i]
t.Fatalf("IfdIdentify not as expected: %s != %s ITE=%s", recoveredIte.Ii, originalIte.Ii, recoveredIte)
} else if recoveredIte.TagId != originalIte.TagId {
t.Fatalf("Tag-ID not as expected: %d != %d ITE=%s", recoveredIte.TagId, originalIte.TagId, recoveredIte)
} else if recoveredIte.TagType != originalIte.TagType {
t.Fatalf("Tag-type not as expected: %d != %d ITE=%s", recoveredIte.TagType, originalIte.TagType, recoveredIte)
}
originalValueBytes, err := originalIte.ValueBytes(originalIndex.RootIfd.addressableData, originalIndex.RootIfd.ByteOrder) // if recoveredIte.Ii != originalIte.Ii {
log.PanicIf(err) // t.Fatalf("IfdIdentify not as expected: %s != %s ITE=%s", recoveredIte.Ii, originalIte.Ii, recoveredIte)
// } else if recoveredIte.TagId != originalIte.TagId {
// t.Fatalf("Tag-ID not as expected: %d != %d ITE=%s", recoveredIte.TagId, originalIte.TagId, recoveredIte)
// } else if recoveredIte.TagType != originalIte.TagType {
// t.Fatalf("Tag-type not as expected: %d != %d ITE=%s", recoveredIte.TagType, originalIte.TagType, recoveredIte)
// }
recoveredValueBytes, err := recoveredIte.ValueBytes(recoveredIndex.RootIfd.addressableData, recoveredIndex.RootIfd.ByteOrder) // originalValueBytes, err := originalIte.ValueBytes(originalIndex.RootIfd.addressableData, originalIndex.RootIfd.ByteOrder)
log.PanicIf(err) // log.PanicIf(err)
if recoveredIte.TagId == 0x9286 { // recoveredValueBytes, err := recoveredIte.ValueBytes(recoveredIndex.RootIfd.addressableData, recoveredIndex.RootIfd.ByteOrder)
expectedValueBytes := make([]byte, 0) // log.PanicIf(err)
expectedValueBytes = append(expectedValueBytes, []byte{'A', 'S', 'C', 'I', 'I', 0, 0, 0}...) // if recoveredIte.TagId == 0x9286 {
expectedValueBytes = append(expectedValueBytes, []byte("TEST COMMENT")...) // expectedValueBytes := make([]byte, 0)
if bytes.Compare(recoveredValueBytes, expectedValueBytes) != 0 { // expectedValueBytes = append(expectedValueBytes, []byte{'A', 'S', 'C', 'I', 'I', 0, 0, 0}...)
t.Fatalf("Recovered UserComment does not have the right value: %v != %v", recoveredValueBytes, expectedValueBytes) // expectedValueBytes = append(expectedValueBytes, []byte("TEST COMMENT")...)
}
} else if bytes.Compare(recoveredValueBytes, originalValueBytes) != 0 { // if bytes.Compare(recoveredValueBytes, expectedValueBytes) != 0 {
t.Fatalf("bytes of tag content not correct: %v != %v ITE=%s", recoveredValueBytes, originalValueBytes, recoveredIte) // t.Fatalf("Recovered UserComment does not have the right value: %v != %v", recoveredValueBytes, expectedValueBytes)
} // }
} // } else if bytes.Compare(recoveredValueBytes, originalValueBytes) != 0 {
} // t.Fatalf("bytes of tag content not correct: %v != %v ITE=%s", recoveredValueBytes, originalValueBytes, recoveredIte)
// }
// }
// }
func ExampleIfd_Thumbnail() { func ExampleIfd_Thumbnail() {
filepath := path.Join(assetsPath, "NDM_8901.jpg") filepath := path.Join(assetsPath, "NDM_8901.jpg")
@ -1544,7 +1574,9 @@ func ExampleIfd_Thumbnail() {
rawExif, err := SearchFileAndExtractExif(filepath) rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err) log.PanicIf(err)
_, index, err := Collect(rawExif) ti := NewTagIndex()
_, index, err := Collect(ti, rawExif)
log.PanicIf(err) log.PanicIf(err)
thumbnailData, err := index.RootIfd.NextIfd.Thumbnail() thumbnailData, err := index.RootIfd.NextIfd.Thumbnail()
@ -1560,7 +1592,9 @@ func ExampleBuilderTag_SetValue() {
rawExif, err := SearchFileAndExtractExif(filepath) rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err) log.PanicIf(err)
_, index, err := Collect(rawExif) ti := NewTagIndex()
_, index, err := Collect(ti, rawExif)
log.PanicIf(err) log.PanicIf(err)
// Create builder. // Create builder.
@ -1599,11 +1633,14 @@ func ExampleBuilderTag_SetValue() {
} }
func Test_IfdBuilder_CreateIfdBuilderWithExistingIfd(t *testing.T) { func Test_IfdBuilder_CreateIfdBuilderWithExistingIfd(t *testing.T) {
ti := NewTagIndex()
tagId := IfdTagIdWithIdentityOrFail(GpsIi) tagId := IfdTagIdWithIdentityOrFail(GpsIi)
parentIfd := &Ifd{ parentIfd := &Ifd{
Ii: RootIi, Ii: RootIi,
Name: IfdStandard, Name: IfdStandard,
tagIndex: ti,
} }
ifd := &Ifd{ ifd := &Ifd{
@ -1612,6 +1649,7 @@ func Test_IfdBuilder_CreateIfdBuilderWithExistingIfd(t *testing.T) {
ByteOrder: TestDefaultByteOrder, ByteOrder: TestDefaultByteOrder,
Offset: 0x123, Offset: 0x123,
ParentIfd: parentIfd, ParentIfd: parentIfd,
tagIndex: ti,
} }
ib := NewIfdBuilderWithExistingIfd(ifd) ib := NewIfdBuilderWithExistingIfd(ifd)
@ -1628,7 +1666,12 @@ func Test_IfdBuilder_CreateIfdBuilderWithExistingIfd(t *testing.T) {
} }
func TestNewStandardBuilderTag_OneUnit(t *testing.T) { func TestNewStandardBuilderTag_OneUnit(t *testing.T) {
bt := NewStandardBuilderTag(ExifIi, uint16(0x8833), TestDefaultByteOrder, []uint32{uint32(0x1234)}) ti := NewTagIndex()
it, err := ti.Get(ExifIi, uint16(0x8833))
log.PanicIf(err)
bt := NewStandardBuilderTag(ExifIi, it, TestDefaultByteOrder, []uint32{uint32(0x1234)})
if bt.ii != ExifIi { if bt.ii != ExifIi {
t.Fatalf("II in BuilderTag not correct") t.Fatalf("II in BuilderTag not correct")
@ -1640,21 +1683,12 @@ func TestNewStandardBuilderTag_OneUnit(t *testing.T) {
} }
func TestNewStandardBuilderTag_TwoUnits(t *testing.T) { func TestNewStandardBuilderTag_TwoUnits(t *testing.T) {
bt := NewStandardBuilderTag(ExifIi, uint16(0x8833), TestDefaultByteOrder, []uint32{uint32(0x1234), uint32(0x5678)}) ti := NewTagIndex()
if bt.ii != ExifIi { it, err := ti.Get(ExifIi, uint16(0x8833))
t.Fatalf("II in BuilderTag not correct") log.PanicIf(err)
} else if bt.tagId != 0x8833 {
t.Fatalf("tag-ID not correct")
} else if bytes.Compare(bt.value.Bytes(), []byte{
0x0, 0x0, 0x12, 0x34,
0x0, 0x0, 0x56, 0x78}) != 0 {
t.Fatalf("value not correct")
}
}
func TestNewStandardBuilderTagWithName(t *testing.T) { bt := NewStandardBuilderTag(ExifIi, it, TestDefaultByteOrder, []uint32{uint32(0x1234), uint32(0x5678)})
bt := NewStandardBuilderTagWithName(ExifIi, "ISOSpeed", TestDefaultByteOrder, []uint32{uint32(0x1234), uint32(0x5678)})
if bt.ii != ExifIi { if bt.ii != ExifIi {
t.Fatalf("II in BuilderTag not correct") t.Fatalf("II in BuilderTag not correct")
@ -1668,7 +1702,8 @@ func TestNewStandardBuilderTagWithName(t *testing.T) {
} }
func TestAddStandardWithName(t *testing.T) { func TestAddStandardWithName(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
err := ib.AddStandardWithName("ProcessingSoftware", "some software") err := ib.AddStandardWithName("ProcessingSoftware", "some software")
log.PanicIf(err) log.PanicIf(err)

View File

@ -2,12 +2,12 @@ package exif
import ( import (
"bytes" "bytes"
"fmt"
"strings"
"errors" "errors"
"fmt"
"reflect" "reflect"
"time"
"strconv" "strconv"
"strings"
"time"
"encoding/binary" "encoding/binary"
@ -23,7 +23,6 @@ var (
ErrNoGpsTags = errors.New("no gps tags") ErrNoGpsTags = errors.New("no gps tags")
) )
// IfdTagEnumerator knows how to decode an IFD and all of the tags it // IfdTagEnumerator knows how to decode an IFD and all of the tags it
// describes. // describes.
// //
@ -96,19 +95,20 @@ func (ife *IfdTagEnumerator) getUint32() (value uint32, raw []byte, err error) {
return value, raw, nil return value, raw, nil
} }
type IfdEnumerate struct { type IfdEnumerate struct {
exifData []byte exifData []byte
buffer *bytes.Buffer buffer *bytes.Buffer
byteOrder binary.ByteOrder byteOrder binary.ByteOrder
currentOffset uint32 currentOffset uint32
tagIndex *TagIndex
} }
func NewIfdEnumerate(exifData []byte, byteOrder binary.ByteOrder) *IfdEnumerate { func NewIfdEnumerate(tagIndex *TagIndex, exifData []byte, byteOrder binary.ByteOrder) *IfdEnumerate {
return &IfdEnumerate{ return &IfdEnumerate{
exifData: exifData, exifData: exifData,
buffer: bytes.NewBuffer(exifData), buffer: bytes.NewBuffer(exifData),
byteOrder: byteOrder, byteOrder: byteOrder,
tagIndex: tagIndex,
} }
} }
@ -130,7 +130,7 @@ func (ie *IfdEnumerate) getTagEnumerator(ifdOffset uint32) (ite *IfdTagEnumerato
return ite return ite
} }
func (ie *IfdEnumerate) parseTag(ii IfdIdentity, tagIndex int, ite *IfdTagEnumerator, resolveValue bool) (tag *IfdTagEntry, err error) { func (ie *IfdEnumerate) parseTag(ii IfdIdentity, tagPosition int, ite *IfdTagEnumerator, resolveValue bool) (tag *IfdTagEntry, err error) {
defer func() { defer func() {
if state := recover(); state != nil { if state := recover(); state != nil {
err = log.Wrap(state.(error)) err = log.Wrap(state.(error))
@ -152,7 +152,7 @@ func (ie *IfdEnumerate) parseTag(ii IfdIdentity, tagIndex int, ite *IfdTagEnumer
tag = &IfdTagEntry{ tag = &IfdTagEntry{
Ii: ii, Ii: ii,
TagId: tagId, TagId: tagId,
TagIndex: tagIndex, TagIndex: tagPosition,
TagType: tagType, TagType: tagType,
UnitCount: unitCount, UnitCount: unitCount,
ValueOffset: valueOffset, ValueOffset: valueOffset,
@ -336,7 +336,6 @@ func (ie *IfdEnumerate) parseThumbnail(offsetIte, lengthIte *IfdTagEntry) (thumb
addressableData := ie.exifData[ExifAddressableAreaStart:] addressableData := ie.exifData[ExifAddressableAreaStart:]
vRaw, err := lengthIte.Value(addressableData, ie.byteOrder) vRaw, err := lengthIte.Value(addressableData, ie.byteOrder)
log.PanicIf(err) log.PanicIf(err)
@ -398,7 +397,6 @@ func (ie *IfdEnumerate) Scan(ifdOffset uint32, visitor RawTagVisitor, resolveVal
return nil return nil
} }
// Ifd represents a single parsed IFD. // Ifd represents a single parsed IFD.
type Ifd struct { type Ifd struct {
// This is just for convenience, just so that we can easily get the values // This is just for convenience, just so that we can easily get the values
@ -435,6 +433,8 @@ type Ifd struct {
NextIfd *Ifd NextIfd *Ifd
thumbnailData []byte thumbnailData []byte
tagIndex *TagIndex
} }
func (ifd *Ifd) ChildWithIfdIdentity(ii IfdIdentity) (childIfd *Ifd, err error) { func (ifd *Ifd) ChildWithIfdIdentity(ii IfdIdentity) (childIfd *Ifd, err error) {
@ -524,7 +524,7 @@ func (ifd *Ifd) FindTagWithName(tagName string) (results []*IfdTagEntry, err err
}() }()
ii := ifd.Identity() ii := ifd.Identity()
it, err := tagIndex.GetWithName(ii, tagName) it, err := ifd.tagIndex.GetWithName(ii, tagName)
if log.Is(err, ErrTagNotFound) == true { if log.Is(err, ErrTagNotFound) == true {
log.Panic(ErrTagNotStandard) log.Panic(ErrTagNotStandard)
} else if err != nil { } else if err != nil {
@ -630,7 +630,7 @@ func (ifd *Ifd) printTagTree(populateValues bool, index, level int, nextLink boo
if tag.ChildIfdName != "" { if tag.ChildIfdName != "" {
fmt.Printf("%s - TAG: %s\n", indent, tag) fmt.Printf("%s - TAG: %s\n", indent, tag)
} else { } else {
it, err := tagIndex.Get(ifd.Identity(), tag.TagId) it, err := ifd.tagIndex.Get(ifd.Identity(), tag.TagId)
tagName := "" tagName := ""
if err == nil { if err == nil {
@ -785,10 +785,8 @@ func (ifd *Ifd) GpsInfo() (gi *GpsInfo, err error) {
} }
}() }()
// TODO(dustin): !! Also add functionality to update the GPS info. // TODO(dustin): !! Also add functionality to update the GPS info.
gi = new(GpsInfo) gi = new(GpsInfo)
if ifd.Ii != GpsIi { if ifd.Ii != GpsIi {
@ -801,7 +799,6 @@ func (ifd *Ifd) GpsInfo() (gi *GpsInfo, err error) {
log.Panic(ErrNoGpsTags) log.Panic(ErrNoGpsTags)
} }
tags, found := ifd.EntriesByTagId[TagLatitudeId] tags, found := ifd.EntriesByTagId[TagLatitudeId]
if found == false { if found == false {
log.Panicf("latitude not found") log.Panicf("latitude not found")
@ -834,7 +831,6 @@ func (ifd *Ifd) GpsInfo() (gi *GpsInfo, err error) {
longitudeRefValue, err := ifd.TagValue(tags[0]) longitudeRefValue, err := ifd.TagValue(tags[0])
log.PanicIf(err) log.PanicIf(err)
// Parse location. // Parse location.
latitudeRaw := latitudeValue.([]Rational) latitudeRaw := latitudeValue.([]Rational)
@ -855,7 +851,6 @@ func (ifd *Ifd) GpsInfo() (gi *GpsInfo, err error) {
Seconds: int(float64(longitudeRaw[2].Numerator) / float64(longitudeRaw[2].Denominator)), Seconds: int(float64(longitudeRaw[2].Numerator) / float64(longitudeRaw[2].Denominator)),
} }
// Parse altitude. // Parse altitude.
altitudeTags, foundAltitude := ifd.EntriesByTagId[TagAltitudeId] altitudeTags, foundAltitude := ifd.EntriesByTagId[TagAltitudeId]
@ -877,7 +872,6 @@ func (ifd *Ifd) GpsInfo() (gi *GpsInfo, err error) {
gi.Altitude = altitude gi.Altitude = altitude
} }
// Parse time. // Parse time.
timestampTags, foundTimestamp := ifd.EntriesByTagId[TagTimestampId] timestampTags, foundTimestamp := ifd.EntriesByTagId[TagTimestampId]
@ -910,7 +904,6 @@ func (ifd *Ifd) GpsInfo() (gi *GpsInfo, err error) {
return gi, nil return gi, nil
} }
type ParsedTagVisitor func(*Ifd, *IfdTagEntry) error type ParsedTagVisitor func(*Ifd, *IfdTagEntry) error
func (ifd *Ifd) EnumerateTagsRecursively(visitor ParsedTagVisitor) (err error) { func (ifd *Ifd) EnumerateTagsRecursively(visitor ParsedTagVisitor) (err error) {
@ -937,7 +930,6 @@ func (ifd *Ifd) EnumerateTagsRecursively(visitor ParsedTagVisitor) (err error) {
return nil return nil
} }
type QueuedIfd struct { type QueuedIfd struct {
Ii IfdIdentity Ii IfdIdentity
TagId uint16 TagId uint16
@ -952,7 +944,6 @@ type QueuedIfd struct {
ParentTagIndex int ParentTagIndex int
} }
type IfdIndex struct { type IfdIndex struct {
RootIfd *Ifd RootIfd *Ifd
Ifds []*Ifd Ifds []*Ifd
@ -960,7 +951,6 @@ type IfdIndex struct {
Lookup map[IfdIdentity][]*Ifd Lookup map[IfdIdentity][]*Ifd
} }
// Scan enumerates the different EXIF blocks (called IFDs). // Scan enumerates the different EXIF blocks (called IFDs).
func (ie *IfdEnumerate) Collect(rootIfdOffset uint32, resolveValues bool) (index IfdIndex, err error) { func (ie *IfdEnumerate) Collect(rootIfdOffset uint32, resolveValues bool) (index IfdIndex, err error) {
defer func() { defer func() {
@ -1043,6 +1033,8 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32, resolveValues bool) (index
NextIfdOffset: nextIfdOffset, NextIfdOffset: nextIfdOffset,
thumbnailData: thumbnailData, thumbnailData: thumbnailData,
tagIndex: ie.tagIndex,
} }
// Add ourselves to a big list of IFDs. // Add ourselves to a big list of IFDs.
@ -1146,7 +1138,6 @@ func (ie *IfdEnumerate) setChildrenIndex(ifd *Ifd) (err error) {
return nil return nil
} }
// ParseOneIfd is a hack to use an IE to parse a raw IFD block. Can be used for // ParseOneIfd is a hack to use an IE to parse a raw IFD block. Can be used for
// testing. // testing.
func ParseOneIfd(ii IfdIdentity, byteOrder binary.ByteOrder, ifdBlock []byte, visitor RawTagVisitor, resolveValues bool) (nextIfdOffset uint32, entries []*IfdTagEntry, err error) { func ParseOneIfd(ii IfdIdentity, byteOrder binary.ByteOrder, ifdBlock []byte, visitor RawTagVisitor, resolveValues bool) (nextIfdOffset uint32, entries []*IfdTagEntry, err error) {

View File

@ -1,11 +1,11 @@
package exif package exif
import ( import (
"path"
"testing"
"bytes" "bytes"
"fmt" "fmt"
"path"
"reflect" "reflect"
"testing"
"encoding/binary" "encoding/binary"
"io/ioutil" "io/ioutil"
@ -53,7 +53,9 @@ func TestIfdTagEntry_ValueBytes_RealData(t *testing.T) {
rawExif, err := SearchFileAndExtractExif(filepath) rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err) log.PanicIf(err)
eh, index, err := Collect(rawExif) ti := NewTagIndex()
eh, index, err := Collect(ti, rawExif)
log.PanicIf(err) log.PanicIf(err)
var ite *IfdTagEntry var ite *IfdTagEntry
@ -95,7 +97,9 @@ func TestIfdTagEntry_Resolver_ValueBytes(t *testing.T) {
rawExif, err := SearchFileAndExtractExif(filepath) rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err) log.PanicIf(err)
eh, index, err := Collect(rawExif) ti := NewTagIndex()
eh, index, err := Collect(ti, rawExif)
log.PanicIf(err) log.PanicIf(err)
var ite *IfdTagEntry var ite *IfdTagEntry
@ -138,7 +142,9 @@ func TestIfdTagEntry_Resolver_ValueBytes__Unknown_Field_And_Nonroot_Ifd(t *testi
rawExif, err := SearchFileAndExtractExif(filepath) rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err) log.PanicIf(err)
eh, index, err := Collect(rawExif) ti := NewTagIndex()
eh, index, err := Collect(ti, rawExif)
log.PanicIf(err) log.PanicIf(err)
ii, _ := IfdIdOrFail(IfdStandard, IfdExif) ii, _ := IfdIdOrFail(IfdStandard, IfdExif)
@ -176,7 +182,9 @@ func Test_Ifd_FindTagWithId_Hit(t *testing.T) {
rawExif, err := SearchFileAndExtractExif(filepath) rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err) log.PanicIf(err)
_, index, err := Collect(rawExif) ti := NewTagIndex()
_, index, err := Collect(ti, rawExif)
log.PanicIf(err) log.PanicIf(err)
ifd := index.RootIfd ifd := index.RootIfd
@ -195,7 +203,9 @@ func Test_Ifd_FindTagWithId_Miss(t *testing.T) {
rawExif, err := SearchFileAndExtractExif(filepath) rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err) log.PanicIf(err)
_, index, err := Collect(rawExif) ti := NewTagIndex()
_, index, err := Collect(ti, rawExif)
log.PanicIf(err) log.PanicIf(err)
ifd := index.RootIfd ifd := index.RootIfd
@ -214,7 +224,9 @@ func Test_Ifd_FindTagWithName_Hit(t *testing.T) {
rawExif, err := SearchFileAndExtractExif(filepath) rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err) log.PanicIf(err)
_, index, err := Collect(rawExif) ti := NewTagIndex()
_, index, err := Collect(ti, rawExif)
log.PanicIf(err) log.PanicIf(err)
ifd := index.RootIfd ifd := index.RootIfd
@ -233,7 +245,9 @@ func Test_Ifd_FindTagWithName_Miss(t *testing.T) {
rawExif, err := SearchFileAndExtractExif(filepath) rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err) log.PanicIf(err)
_, index, err := Collect(rawExif) ti := NewTagIndex()
_, index, err := Collect(ti, rawExif)
log.PanicIf(err) log.PanicIf(err)
ifd := index.RootIfd ifd := index.RootIfd
@ -252,7 +266,9 @@ func Test_Ifd_FindTagWithName_NonStandard(t *testing.T) {
rawExif, err := SearchFileAndExtractExif(filepath) rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err) log.PanicIf(err)
_, index, err := Collect(rawExif) ti := NewTagIndex()
_, index, err := Collect(ti, rawExif)
log.PanicIf(err) log.PanicIf(err)
ifd := index.RootIfd ifd := index.RootIfd
@ -271,7 +287,9 @@ func Test_Ifd_Thumbnail(t *testing.T) {
rawExif, err := SearchFileAndExtractExif(filepath) rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err) log.PanicIf(err)
_, index, err := Collect(rawExif) ti := NewTagIndex()
_, index, err := Collect(ti, rawExif)
log.PanicIf(err) log.PanicIf(err)
ifd := index.RootIfd ifd := index.RootIfd
@ -307,7 +325,9 @@ func TestIfd_GpsInfo(t *testing.T) {
rawExif, err := SearchFileAndExtractExif(filepath) rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err) log.PanicIf(err)
_, index, err := Collect(rawExif) ti := NewTagIndex()
_, index, err := Collect(ti, rawExif)
log.PanicIf(err) log.PanicIf(err)
ifd, err := index.RootIfd.ChildWithIfdIdentity(GpsIi) ifd, err := index.RootIfd.ChildWithIfdIdentity(GpsIi)
@ -335,9 +355,10 @@ func TestIfd_EnumerateTagsRecursively(t *testing.T) {
rawExif, err := SearchFileAndExtractExif(filepath) rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err) log.PanicIf(err)
_, index, err := Collect(rawExif) ti := NewTagIndex()
log.PanicIf(err)
_, index, err := Collect(ti, rawExif)
log.PanicIf(err)
collected := make([][2]interface{}, 0) collected := make([][2]interface{}, 0)
@ -487,7 +508,9 @@ func ExampleIfd_EnumerateTagsRecursively() {
rawExif, err := SearchFileAndExtractExif(filepath) rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err) log.PanicIf(err)
_, index, err := Collect(rawExif) ti := NewTagIndex()
_, index, err := Collect(ti, rawExif)
log.PanicIf(err) log.PanicIf(err)
cb := func(ifd *Ifd, ite *IfdTagEntry) error { cb := func(ifd *Ifd, ite *IfdTagEntry) error {
@ -503,14 +526,15 @@ func ExampleIfd_EnumerateTagsRecursively() {
// Output: // Output:
} }
func ExampleIfd_GpsInfo() { func ExampleIfd_GpsInfo() {
filepath := path.Join(assetsPath, "gps.jpg") filepath := path.Join(assetsPath, "gps.jpg")
rawExif, err := SearchFileAndExtractExif(filepath) rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err) log.PanicIf(err)
_, index, err := Collect(rawExif) ti := NewTagIndex()
_, index, err := Collect(ti, rawExif)
log.PanicIf(err) log.PanicIf(err)
ifd, err := index.RootIfd.ChildWithIfdIdentity(GpsIi) ifd, err := index.RootIfd.ChildWithIfdIdentity(GpsIi)

12
tags.go
View File

@ -37,8 +37,6 @@ var (
// blob (not a slice of longs). // blob (not a slice of longs).
ThumbnailOffsetTagId: struct{}{}, ThumbnailOffsetTagId: struct{}{},
} }
tagIndex *TagIndex
) )
var ( var (
@ -64,15 +62,15 @@ type IndexedTag struct {
Type uint16 Type uint16
} }
func (it IndexedTag) String() string { func (it *IndexedTag) String() string {
return fmt.Sprintf("TAG<ID=(0x%04x) NAME=[%s] IFD=[%s]>", it.Id, it.Name, it.Ifd) return fmt.Sprintf("TAG<ID=(0x%04x) NAME=[%s] IFD=[%s]>", it.Id, it.Name, it.Ifd)
} }
func (it IndexedTag) IsName(ifd, name string) bool { func (it *IndexedTag) IsName(ifd, name string) bool {
return it.Name == name && it.Ifd == ifd return it.Name == name && it.Ifd == ifd
} }
func (it IndexedTag) Is(ifd string, id uint16) bool { func (it *IndexedTag) Is(ifd string, id uint16) bool {
return it.Id == id && it.Ifd == ifd return it.Id == id && it.Ifd == ifd
} }
@ -318,7 +316,3 @@ func IfdId(parentIfdName, ifdName string) (ii IfdIdentity, id int) {
return ii, id return ii, id
} }
func init() {
tagIndex = NewTagIndex()
}

View File

@ -14,7 +14,9 @@ var (
) )
func TestGet(t *testing.T) { func TestGet(t *testing.T) {
it, err := tagIndex.Get(RootIi, 0x10f) ti := NewTagIndex()
it, err := ti.Get(RootIi, 0x10f)
log.PanicIf(err) log.PanicIf(err)
if it.Is("IFD", 0x10f) == false || it.IsName("IFD", "Make") == false { if it.Is("IFD", 0x10f) == false || it.IsName("IFD", "Make") == false {
@ -23,7 +25,9 @@ func TestGet(t *testing.T) {
} }
func TestGetWithName(t *testing.T) { func TestGetWithName(t *testing.T) {
it, err := tagIndex.GetWithName(RootIi, "Make") ti := NewTagIndex()
it, err := ti.GetWithName(RootIi, "Make")
log.PanicIf(err) log.PanicIf(err)
if it.Is("IFD", 0x10f) == false || it.Is("IFD", 0x10f) == false { if it.Is("IFD", 0x10f) == false || it.Is("IFD", 0x10f) == false {

View File

@ -1,8 +1,8 @@
package exif package exif
import ( import (
"testing"
"bytes" "bytes"
"testing"
"github.com/dsoprea/go-logging" "github.com/dsoprea/go-logging"
) )
@ -11,7 +11,6 @@ func TestUndefinedValue_ExifVersion(t *testing.T) {
byteOrder := TestDefaultByteOrder byteOrder := TestDefaultByteOrder
ii := ExifIi ii := ExifIi
// Create our unknown-type tag's value using the fact that we know it's a // Create our unknown-type tag's value using the fact that we know it's a
// non-null-terminated string. // non-null-terminated string.
@ -23,7 +22,6 @@ func TestUndefinedValue_ExifVersion(t *testing.T) {
ed, err := ve.EncodeWithType(tt, valueString) ed, err := ve.EncodeWithType(tt, valueString)
log.PanicIf(err) log.PanicIf(err)
// Create the tag using the official "unknown" type now that we already // Create the tag using the official "unknown" type now that we already
// have the bytes. // have the bytes.
@ -36,11 +34,12 @@ func TestUndefinedValue_ExifVersion(t *testing.T) {
value: encodedValue, value: encodedValue,
} }
// Stage the build. // Stage the build.
ti := NewTagIndex()
ibe := NewIfdByteEncoder() ibe := NewIfdByteEncoder()
ib := NewIfdBuilder(ii, byteOrder) ib := NewIfdBuilder(ti, ii, byteOrder)
b := new(bytes.Buffer) b := new(bytes.Buffer)
bw := NewByteWriter(b, byteOrder) bw := NewByteWriter(b, byteOrder)
@ -48,7 +47,6 @@ func TestUndefinedValue_ExifVersion(t *testing.T) {
addressableOffset := uint32(0x1234) addressableOffset := uint32(0x1234)
ida := newIfdDataAllocator(addressableOffset) ida := newIfdDataAllocator(addressableOffset)
// Encode. // Encode.
_, err = ibe.encodeTagToBytes(ib, bt, bw, ida, uint32(0)) _, err = ibe.encodeTagToBytes(ib, bt, bw, ida, uint32(0))