mirror of https://github.com/dsoprea/go-exif.git
ifd_enumerate.go: Bugfix for ITEs not representing fully-qualified IFD paths
- This resulted in the lookup table having unique entries, and therefore no longer needing its values to be slices. We are going to fix this. It is not a backwards-compatible change, but this is a fairly internal internal functionality. - Add simple accessors to determine if a thumbnail offset or size.dustin/add_skipped_tags_tracking
parent
efa6e2d6c0
commit
220964731d
|
@ -179,10 +179,8 @@ func main() {
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
var thumbnail []byte
|
var thumbnail []byte
|
||||||
if matches, found := index.Lookup[exif.ThumbnailFqIfdPath]; found == true {
|
if ifd, found := index.Lookup[exif.ThumbnailFqIfdPath]; found == true {
|
||||||
ifd1 := matches[0]
|
thumbnail, err = ifd.Thumbnail()
|
||||||
|
|
||||||
thumbnail, err = ifd1.Thumbnail()
|
|
||||||
if err != nil && err != exif.ErrNoThumbnail {
|
if err != nil && err != exif.ErrNoThumbnail {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ IFD-PATH=[IFD] ID=(0x0128) NAME=[ResolutionUnit] COUNT=(1) TYPE=[SHORT] VALUE=[2
|
||||||
IFD-PATH=[IFD] ID=(0x0201) NAME=[JPEGInterchangeFormat] COUNT=(1) TYPE=[LONG] VALUE=[11444]
|
IFD-PATH=[IFD] ID=(0x0201) NAME=[JPEGInterchangeFormat] COUNT=(1) TYPE=[LONG] VALUE=[11444]
|
||||||
IFD-PATH=[IFD] ID=(0x0202) NAME=[JPEGInterchangeFormatLength] COUNT=(1) TYPE=[LONG] VALUE=[21491]
|
IFD-PATH=[IFD] ID=(0x0202) NAME=[JPEGInterchangeFormatLength] COUNT=(1) TYPE=[LONG] VALUE=[21491]
|
||||||
|
|
||||||
EXIF blob is approximately (11442) bytes.
|
EXIF blob is approximately (32935) bytes.
|
||||||
`
|
`
|
||||||
|
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
@ -98,7 +99,7 @@ func TestVisit(t *testing.T) {
|
||||||
_, furthestOffset, err := Visit(exifcommon.IfdStandard, im, ti, data[foundAt:], visitor)
|
_, furthestOffset, err := Visit(exifcommon.IfdStandard, im, ti, data[foundAt:], visitor)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
if furthestOffset != 11442 {
|
if furthestOffset != 32935 {
|
||||||
t.Fatalf("Furthest-offset is not valid: (%d)", furthestOffset)
|
t.Fatalf("Furthest-offset is not valid: (%d)", furthestOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,8 +277,27 @@ func TestCollect(t *testing.T) {
|
||||||
t.Fatalf("The IFD list is not the right size: (%d)", len(ifds))
|
t.Fatalf("The IFD list is not the right size: (%d)", len(ifds))
|
||||||
} else if len(tree) != 5 {
|
} else if len(tree) != 5 {
|
||||||
t.Fatalf("The IFD tree is not the right size: (%d)", len(tree))
|
t.Fatalf("The IFD tree is not the right size: (%d)", len(tree))
|
||||||
} else if len(lookup) != 4 {
|
}
|
||||||
t.Fatalf("The IFD lookup is not the right size: (%d)", len(lookup))
|
|
||||||
|
actualIfdPaths := make([]string, len(lookup))
|
||||||
|
i := 0
|
||||||
|
for ifdPath, _ := range lookup {
|
||||||
|
actualIfdPaths[i] = ifdPath
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(actualIfdPaths)
|
||||||
|
|
||||||
|
expectedIfdPaths := []string{
|
||||||
|
"IFD",
|
||||||
|
"IFD/Exif",
|
||||||
|
"IFD/Exif/Iop",
|
||||||
|
"IFD/GPSInfo",
|
||||||
|
"IFD1",
|
||||||
|
}
|
||||||
|
|
||||||
|
if reflect.DeepEqual(actualIfdPaths, expectedIfdPaths) != true {
|
||||||
|
t.Fatalf("The IFD lookup is not the right size: %v", actualIfdPaths)
|
||||||
}
|
}
|
||||||
|
|
||||||
if rootIfd.NextIfdOffset != 0x2c54 {
|
if rootIfd.NextIfdOffset != 0x2c54 {
|
||||||
|
@ -302,27 +322,27 @@ func TestCollect(t *testing.T) {
|
||||||
t.Fatalf("Exif IFD child is not an IOP IFD: [%s]", rootIfd.Children[0].Children[0].IfdPath)
|
t.Fatalf("Exif IFD child is not an IOP IFD: [%s]", rootIfd.Children[0].Children[0].IfdPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if lookup[exifcommon.IfdPathStandard][0].IfdPath != exifcommon.IfdPathStandard {
|
if lookup[exifcommon.IfdPathStandard].IfdPath != exifcommon.IfdPathStandard {
|
||||||
t.Fatalf("Lookup for standard IFD not correct.")
|
t.Fatalf("Lookup for standard IFD not correct.")
|
||||||
} else if lookup[exifcommon.IfdPathStandard][1].IfdPath != exifcommon.IfdPathStandard {
|
} else if lookup[exifcommon.IfdPathStandard+"1"].IfdPath != exifcommon.IfdPathStandard {
|
||||||
t.Fatalf("Lookup for standard IFD not correct.")
|
t.Fatalf("Lookup for standard IFD not correct.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if lookup[exifcommon.IfdPathStandardExif][0].IfdPath != exifcommon.IfdPathStandardExif {
|
if lookup[exifcommon.IfdPathStandardExif].IfdPath != exifcommon.IfdPathStandardExif {
|
||||||
t.Fatalf("Lookup for EXIF IFD not correct.")
|
t.Fatalf("Lookup for EXIF IFD not correct.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if lookup[exifcommon.IfdPathStandardGps][0].IfdPath != exifcommon.IfdPathStandardGps {
|
if lookup[exifcommon.IfdPathStandardGps].IfdPath != exifcommon.IfdPathStandardGps {
|
||||||
t.Fatalf("Lookup for GPS IFD not correct.")
|
t.Fatalf("Lookup for GPS IFD not correct.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if lookup[exifcommon.IfdPathStandardExifIop][0].IfdPath != exifcommon.IfdPathStandardExifIop {
|
if lookup[exifcommon.IfdPathStandardExifIop].IfdPath != exifcommon.IfdPathStandardExifIop {
|
||||||
t.Fatalf("Lookup for IOP IFD not correct.")
|
t.Fatalf("Lookup for IOP IFD not correct.")
|
||||||
}
|
}
|
||||||
|
|
||||||
foundExif := 0
|
foundExif := 0
|
||||||
foundGps := 0
|
foundGps := 0
|
||||||
for _, ite := range lookup[exifcommon.IfdPathStandard][0].Entries {
|
for _, ite := range lookup[exifcommon.IfdPathStandard].Entries {
|
||||||
if ite.ChildIfdPath() == exifcommon.IfdPathStandardExif {
|
if ite.ChildIfdPath() == exifcommon.IfdPathStandardExif {
|
||||||
foundExif++
|
foundExif++
|
||||||
|
|
||||||
|
@ -347,7 +367,7 @@ func TestCollect(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
foundIop := 0
|
foundIop := 0
|
||||||
for _, ite := range lookup[exifcommon.IfdPathStandardExif][0].Entries {
|
for _, ite := range lookup[exifcommon.IfdPathStandardExif].Entries {
|
||||||
if ite.ChildIfdPath() == exifcommon.IfdPathStandardExifIop {
|
if ite.ChildIfdPath() == exifcommon.IfdPathStandardExifIop {
|
||||||
foundIop++
|
foundIop++
|
||||||
|
|
||||||
|
|
|
@ -1042,7 +1042,7 @@ func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, includeTagIds []uint16, excl
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, ite := range ifd.Entries {
|
for i, ite := range ifd.Entries {
|
||||||
if (ite.TagId() == ThumbnailOffsetTagId || ite.TagId() == ThumbnailSizeTagId) && ifd.FqIfdPath == ThumbnailFqIfdPath {
|
if ite.IsThumbnailOffset() == true || ite.IsThumbnailSize() {
|
||||||
// These will be added on-the-fly when we encode.
|
// These will be added on-the-fly when we encode.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -422,7 +422,7 @@ func Test_IfdByteEncoder_encodeTagToBytes_childIfd__withAllocate(t *testing.T) {
|
||||||
t.Fatalf("Child IFD is not the right size: (%d)", len(childIfdBlock))
|
t.Fatalf("Child IFD is not the right size: (%d)", len(childIfdBlock))
|
||||||
}
|
}
|
||||||
|
|
||||||
iteV, err := ParseOneTag(im, ti, fmt.Sprintf("%s%d", exifcommon.IfdPathStandard, 0), exifcommon.IfdPathStandard, exifcommon.TestDefaultByteOrder, tagBytes)
|
iteV, err := ParseOneTag(im, ti, exifcommon.IfdPathStandard, exifcommon.IfdPathStandard, exifcommon.TestDefaultByteOrder, tagBytes)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
if iteV.TagId() != exifcommon.IfdExifId {
|
if iteV.TagId() != exifcommon.IfdExifId {
|
||||||
|
@ -443,7 +443,7 @@ func Test_IfdByteEncoder_encodeTagToBytes_childIfd__withAllocate(t *testing.T) {
|
||||||
|
|
||||||
// Validate the child's raw IFD bytes.
|
// Validate the child's raw IFD bytes.
|
||||||
|
|
||||||
childNextIfdOffset, childEntries, err := ParseOneIfd(im, ti, "IFD0/Exif0", "IFD/Exif", exifcommon.TestDefaultByteOrder, childIfdBlock, nil)
|
childNextIfdOffset, childEntries, err := ParseOneIfd(im, ti, "IFD/Exif", "IFD/Exif", exifcommon.TestDefaultByteOrder, childIfdBlock, nil)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
if childNextIfdOffset != uint32(0) {
|
if childNextIfdOffset != uint32(0) {
|
||||||
|
@ -525,7 +525,7 @@ 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(im, ti, fmt.Sprintf("%s%d", exifcommon.IfdPathStandard, 0), exifcommon.IfdPathStandard, exifcommon.TestDefaultByteOrder, tagBytes)
|
ite, err := ParseOneTag(im, ti, exifcommon.IfdPathStandard, exifcommon.IfdPathStandard, exifcommon.TestDefaultByteOrder, tagBytes)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
if ite.TagId() != 0x000b {
|
if ite.TagId() != 0x000b {
|
||||||
|
|
|
@ -1642,6 +1642,12 @@ func TestIfdBuilder_NewIfdBuilderFromExistingChain_RealData(t *testing.T) {
|
||||||
|
|
||||||
originalTagPhrases := make([]string, 0)
|
originalTagPhrases := make([]string, 0)
|
||||||
for _, ite := range originalTags {
|
for _, ite := range originalTags {
|
||||||
|
// Adds a lot of noise if/when debugging, and we're already checking the
|
||||||
|
// thumbnail bytes separately.
|
||||||
|
if ite.IsThumbnailOffset() == true || ite.IsThumbnailSize() == true {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
phrase := ite.String()
|
phrase := ite.String()
|
||||||
|
|
||||||
// The value (the offset) of IFDs will almost never be the same after
|
// The value (the offset) of IFDs will almost never be the same after
|
||||||
|
@ -1660,6 +1666,12 @@ func TestIfdBuilder_NewIfdBuilderFromExistingChain_RealData(t *testing.T) {
|
||||||
|
|
||||||
recoveredTagPhrases := make([]string, 0)
|
recoveredTagPhrases := make([]string, 0)
|
||||||
for _, ite := range recoveredTags {
|
for _, ite := range recoveredTags {
|
||||||
|
// Adds a lot of noise if/when debugging, and we're already checking the
|
||||||
|
// thumbnail bytes separately.
|
||||||
|
if ite.IsThumbnailOffset() == true || ite.IsThumbnailSize() == true {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
phrase := ite.String()
|
phrase := ite.String()
|
||||||
|
|
||||||
// The value (the offset) of IFDs will almost never be the same after
|
// The value (the offset) of IFDs will almost never be the same after
|
||||||
|
|
|
@ -240,7 +240,7 @@ func (ie *IfdEnumerate) parseTag(fqIfdPath string, tagPosition int, bp *bytePars
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
ite = newIfdTagEntry(
|
ite = newIfdTagEntry(
|
||||||
ifdPath,
|
fqIfdPath,
|
||||||
tagId,
|
tagId,
|
||||||
tagPosition,
|
tagPosition,
|
||||||
tagType,
|
tagType,
|
||||||
|
@ -310,13 +310,12 @@ func (ie *IfdEnumerate) ParseIfd(fqIfdPath string, ifdIndex int, bp *byteParser,
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tagId := ite.TagId()
|
if ite.IsThumbnailOffset() == true {
|
||||||
if tagId == ThumbnailOffsetTagId && fqIfdPath == ThumbnailFqIfdPath {
|
|
||||||
enumeratorThumbnailOffset = ite
|
enumeratorThumbnailOffset = ite
|
||||||
entries = append(entries, ite)
|
entries = append(entries, ite)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
} else if tagId == ThumbnailSizeTagId && fqIfdPath == ThumbnailFqIfdPath {
|
} else if ite.IsThumbnailSize() == true {
|
||||||
enumeratorThumbnailSize = ite
|
enumeratorThumbnailSize = ite
|
||||||
entries = append(entries, ite)
|
entries = append(entries, ite)
|
||||||
|
|
||||||
|
@ -1006,7 +1005,7 @@ type IfdIndex struct {
|
||||||
RootIfd *Ifd
|
RootIfd *Ifd
|
||||||
Ifds []*Ifd
|
Ifds []*Ifd
|
||||||
Tree map[int]*Ifd
|
Tree map[int]*Ifd
|
||||||
Lookup map[string][]*Ifd
|
Lookup map[string]*Ifd
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan enumerates the different EXIF blocks (called IFDs).
|
// Scan enumerates the different EXIF blocks (called IFDs).
|
||||||
|
@ -1019,7 +1018,7 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error
|
||||||
|
|
||||||
tree := make(map[int]*Ifd)
|
tree := make(map[int]*Ifd)
|
||||||
ifds := make([]*Ifd, 0)
|
ifds := make([]*Ifd, 0)
|
||||||
lookup := make(map[string][]*Ifd)
|
lookup := make(map[string]*Ifd)
|
||||||
|
|
||||||
queue := []QueuedIfd{
|
queue := []QueuedIfd{
|
||||||
{
|
{
|
||||||
|
@ -1122,14 +1121,7 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error
|
||||||
tree[id] = ifd
|
tree[id] = ifd
|
||||||
|
|
||||||
// Install into by-name buckets.
|
// Install into by-name buckets.
|
||||||
|
lookup[fqIfdPath] = ifd
|
||||||
if list_, found := lookup[fqIfdPath]; found == true {
|
|
||||||
lookup[fqIfdPath] = append(list_, ifd)
|
|
||||||
} else {
|
|
||||||
lookup[fqIfdPath] = []*Ifd{
|
|
||||||
ifd,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a link from the previous IFD in the chain to us.
|
// Add a link from the previous IFD in the chain to us.
|
||||||
if previousIfd, found := edges[offset]; found == true {
|
if previousIfd, found := edges[offset]; found == true {
|
||||||
|
|
|
@ -38,8 +38,8 @@ type IfdTagEntry struct {
|
||||||
|
|
||||||
// TODO(dustin): !! IB's host the child-IBs directly in the tag, but that's not the case here. Refactor to accomodate it for a consistent experience.
|
// TODO(dustin): !! IB's host the child-IBs directly in the tag, but that's not the case here. Refactor to accomodate it for a consistent experience.
|
||||||
|
|
||||||
// ifdPath is the IFD that this tag belongs to.
|
// fqIfdPath is the IFD that this tag belongs to.
|
||||||
ifdPath string
|
fqIfdPath string
|
||||||
|
|
||||||
isUnhandledUnknown bool
|
isUnhandledUnknown bool
|
||||||
|
|
||||||
|
@ -47,9 +47,9 @@ type IfdTagEntry struct {
|
||||||
byteOrder binary.ByteOrder
|
byteOrder binary.ByteOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
func newIfdTagEntry(ifdPath string, tagId uint16, tagIndex int, tagType exifcommon.TagTypePrimitive, unitCount uint32, valueOffset uint32, rawValueOffset []byte, addressableData []byte, byteOrder binary.ByteOrder) *IfdTagEntry {
|
func newIfdTagEntry(fqIfdPath string, tagId uint16, tagIndex int, tagType exifcommon.TagTypePrimitive, unitCount uint32, valueOffset uint32, rawValueOffset []byte, addressableData []byte, byteOrder binary.ByteOrder) *IfdTagEntry {
|
||||||
return &IfdTagEntry{
|
return &IfdTagEntry{
|
||||||
ifdPath: ifdPath,
|
fqIfdPath: fqIfdPath,
|
||||||
tagId: tagId,
|
tagId: tagId,
|
||||||
tagIndex: tagIndex,
|
tagIndex: tagIndex,
|
||||||
tagType: tagType,
|
tagType: tagType,
|
||||||
|
@ -63,12 +63,12 @@ func newIfdTagEntry(ifdPath string, tagId uint16, tagIndex int, tagType exifcomm
|
||||||
|
|
||||||
// String returns a stringified representation of the struct.
|
// String returns a stringified representation of the struct.
|
||||||
func (ite *IfdTagEntry) String() string {
|
func (ite *IfdTagEntry) String() string {
|
||||||
return fmt.Sprintf("IfdTagEntry<TAG-IFD-PATH=[%s] TAG-ID=(0x%04x) TAG-TYPE=[%s] UNIT-COUNT=(%d)>", ite.ifdPath, ite.tagId, ite.tagType.String(), ite.unitCount)
|
return fmt.Sprintf("IfdTagEntry<TAG-IFD-PATH=[%s] TAG-ID=(0x%04x) TAG-TYPE=[%s] UNIT-COUNT=(%d)>", ite.fqIfdPath, ite.tagId, ite.tagType.String(), ite.unitCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IfdPath returns the path of the IFD that owns this tag.
|
// IfdPath returns the fully-qualified path of the IFD that owns this tag.
|
||||||
func (ite *IfdTagEntry) IfdPath() string {
|
func (ite *IfdTagEntry) IfdPath() string {
|
||||||
return ite.ifdPath
|
return ite.fqIfdPath
|
||||||
}
|
}
|
||||||
|
|
||||||
// TagId returns the ID of the tag that we represent. The combination of
|
// TagId returns the ID of the tag that we represent. The combination of
|
||||||
|
@ -77,6 +77,18 @@ func (ite *IfdTagEntry) TagId() uint16 {
|
||||||
return ite.tagId
|
return ite.tagId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsThumbnailOffset returns true if the tag has the IFD and tag-ID of a
|
||||||
|
// thumbnail offset.
|
||||||
|
func (ite *IfdTagEntry) IsThumbnailOffset() bool {
|
||||||
|
return ite.tagId == ThumbnailOffsetTagId && ite.fqIfdPath == ThumbnailFqIfdPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsThumbnailSize returns true if the tag has the IFD and tag-ID of a thumbnail
|
||||||
|
// size.
|
||||||
|
func (ite *IfdTagEntry) IsThumbnailSize() bool {
|
||||||
|
return ite.tagId == ThumbnailSizeTagId && ite.fqIfdPath == ThumbnailFqIfdPath
|
||||||
|
}
|
||||||
|
|
||||||
// TagType is the type of value for this tag.
|
// TagType is the type of value for this tag.
|
||||||
func (ite *IfdTagEntry) TagType() exifcommon.TagTypePrimitive {
|
func (ite *IfdTagEntry) TagType() exifcommon.TagTypePrimitive {
|
||||||
return ite.tagType
|
return ite.tagType
|
||||||
|
@ -244,7 +256,7 @@ func (ite *IfdTagEntry) ChildFqIfdPath() string {
|
||||||
|
|
||||||
func (ite *IfdTagEntry) getValueContext() *exifcommon.ValueContext {
|
func (ite *IfdTagEntry) getValueContext() *exifcommon.ValueContext {
|
||||||
return exifcommon.NewValueContext(
|
return exifcommon.NewValueContext(
|
||||||
ite.ifdPath,
|
ite.fqIfdPath,
|
||||||
ite.tagId,
|
ite.tagId,
|
||||||
ite.unitCount,
|
ite.unitCount,
|
||||||
ite.valueOffset,
|
ite.valueOffset,
|
||||||
|
|
Loading…
Reference in New Issue