encode: We now encode child IFDs as they occur.

- ..rather than after the normal tags.
This commit is contained in:
Dustin Oprea 2018-05-28 20:04:02 -04:00
parent 5996e32e27
commit 3c1c668a9f
3 changed files with 184 additions and 83 deletions

View File

@ -298,9 +298,7 @@ func NewIfdBuilderWithExistingIfd(ifd *Ifd) (ib *IfdBuilder) {
// NewIfdBuilderFromExistingChain creates a chain of IB instances from an // NewIfdBuilderFromExistingChain creates a chain of IB instances from an
// IFD chain generated from real data. // IFD chain generated from real data.
func NewIfdBuilderFromExistingChain(rootIfd *Ifd, exifData []byte) (firstIb *IfdBuilder) { func NewIfdBuilderFromExistingChain(rootIfd *Ifd, itevr *IfdTagEntryValueResolver) (firstIb *IfdBuilder) {
itevr := NewIfdTagEntryValueResolver(exifData, rootIfd.ByteOrder)
// TODO(dustin): !! When we actually write the code to flatten the IB to bytes, make sure to skip the tags that have a nil value (which will happen when we add-from-exsting without a resolver instance). // TODO(dustin): !! When we actually write the code to flatten the IB to bytes, make sure to skip the tags that have a nil value (which will happen when we add-from-exsting without a resolver instance).
var lastIb *IfdBuilder var lastIb *IfdBuilder
@ -318,15 +316,6 @@ func NewIfdBuilderFromExistingChain(rootIfd *Ifd, exifData []byte) (firstIb *Ifd
err := newIb.AddTagsFromExisting(thisExistingIfd, itevr, nil, nil) err := newIb.AddTagsFromExisting(thisExistingIfd, itevr, nil, nil)
log.PanicIf(err) log.PanicIf(err)
// Any child IFDs will still not be copied. Do that now.
for _, childIfd := range thisExistingIfd.Children {
childIb := NewIfdBuilderFromExistingChain(childIfd, exifData)
err = newIb.AddChildIb(childIb)
log.PanicIf(err)
}
lastIb = newIb lastIb = newIb
i++ i++
} }
@ -375,14 +364,14 @@ func (ib *IfdBuilder) SetThumbnail(data []byte) (err error) {
if data == nil || len(data) == 0 { if data == nil || len(data) == 0 {
// TODO(dustin): !! Debugging. // TODO(dustin): !! Debugging.
fmt.Printf("Thumbnail empty.\n") // fmt.Printf("Thumbnail empty.\n")
log.Panic("thumbnail is empty") log.Panic("thumbnail is empty")
} }
ib.thumbnailData = data ib.thumbnailData = data
fmt.Printf("SETTING THUMBNAIL.\n") // fmt.Printf("SETTING THUMBNAIL.\n")
ibtvfb := NewIfdBuilderTagValueFromBytes(ib.thumbnailData) ibtvfb := NewIfdBuilderTagValueFromBytes(ib.thumbnailData)
offsetBt := NewBuilderTag(ib.ii, ThumbnailOffsetTagId, TypeLong, ibtvfb) offsetBt := NewBuilderTag(ib.ii, ThumbnailOffsetTagId, TypeLong, ibtvfb)
@ -398,7 +387,7 @@ fmt.Printf("SETTING THUMBNAIL.\n")
// TODO(dustin): !! Debugging. // TODO(dustin): !! Debugging.
fmt.Printf("Set thumbnail into IB.\n") // fmt.Printf("Set thumbnail into IB.\n")
return nil return nil
@ -664,7 +653,7 @@ func (ib *IfdBuilder) Set(bt builderTag) (err error) {
if err == nil { if err == nil {
ib.tags[position] = bt ib.tags[position] = bt
} else if log.Is(err, ErrTagEntryNotFound) == true { } else if log.Is(err, ErrTagEntryNotFound) == true {
err = ib.Add(bt) err = ib.add(bt)
log.PanicIf(err) log.PanicIf(err)
} else { } else {
log.Panic(err) log.Panic(err)
@ -711,7 +700,7 @@ func (ib *IfdBuilder) Find(tagId uint16) (position int, err error) {
return found[0], nil return found[0], nil
} }
func (ib *IfdBuilder) Add(bt builderTag) (err error) { func (ib *IfdBuilder) add(bt builderTag) (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))
@ -726,11 +715,24 @@ func (ib *IfdBuilder) Add(bt builderTag) (err error) {
log.Panicf("builderTag value is not set: %s", bt) log.Panicf("builderTag value is not set: %s", bt)
} }
ib.tags = append(ib.tags, bt)
return nil
}
func (ib *IfdBuilder) Add(bt builderTag) (err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
if bt.value.IsIb() == true { if bt.value.IsIb() == true {
log.Panicf("child IfdBuilders must be added via AddChildIb() not Add()") log.Panicf("child IfdBuilders must be added via AddChildIb() or AddTagsFromExisting(), not Add()")
} }
ib.tags = append(ib.tags, bt) err = ib.add(bt)
log.PanicIf(err)
return nil return nil
} }
@ -757,16 +759,28 @@ func (ib *IfdBuilder) AddChildIb(childIb *IfdBuilder) (err error) {
} }
} }
bt := ib.NewBuilderTagFromBuilder(childIb)
ib.tags = append(ib.tags, bt)
return nil
}
func (ib *IfdBuilder) NewBuilderTagFromBuilder(childIb *IfdBuilder) (bt builderTag) {
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
log.Panic(err)
}
}()
value := NewIfdBuilderTagValueFromIfdBuilder(childIb) value := NewIfdBuilderTagValueFromIfdBuilder(childIb)
bt := NewChildIfdBuilderTag( bt = NewChildIfdBuilderTag(
ib.ii, ib.ii,
childIb.ifdTagId, childIb.ifdTagId,
value) value)
ib.tags = append(ib.tags, bt) return bt
return nil
} }
// AddTagsFromExisting does a verbatim copy of the entries in `ifd` to this // AddTagsFromExisting does a verbatim copy of the entries in `ifd` to this
@ -782,8 +796,10 @@ func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, itevr *IfdTagEntryValueResol
thumbnailData, err := ifd.Thumbnail() thumbnailData, err := ifd.Thumbnail()
if err == nil { if err == nil {
// TODO(dustin): The thumbnail tags will be added out of order.
// TODO(dustin): !! Debugging. // TODO(dustin): !! Debugging.
fmt.Printf("Importing thumbnail: %s\n", ifd.Identity()) // fmt.Printf("Importing thumbnail: %s\n", ifd.Identity())
err = ib.SetThumbnail(thumbnailData) err = ib.SetThumbnail(thumbnailData)
log.PanicIf(err) log.PanicIf(err)
@ -792,17 +808,12 @@ fmt.Printf("Importing thumbnail: %s\n", ifd.Identity())
} else { } else {
// TODO(dustin): !! Debugging. // TODO(dustin): !! Debugging.
fmt.Printf("NO THUMBNAIL FOUND: %s\n", ifd.Identity()) // fmt.Printf("NO THUMBNAIL FOUND: %s\n", ifd.Identity())
} }
for _, ite := range ifd.Entries { for i, ite := range ifd.Entries {
if ite.ChildIfdName != "" { if ite.TagId == ThumbnailOffsetTagId || ite.TagId == ThumbnailSizeTagId {
// If we want to add an IFD tag, we'll have to build it first and
// *then* add it via a different method.
continue
} else if ite.TagId == ThumbnailOffsetTagId || ite.TagId == ThumbnailSizeTagId {
// These will be added on-the-fly when we encode. // These will be added on-the-fly when we encode.
continue continue
@ -838,34 +849,79 @@ fmt.Printf("NO THUMBNAIL FOUND: %s\n", ifd.Identity())
} }
} }
var value *IfdBuilderTagValue var bt builderTag
if itevr == nil { if ite.ChildIfdName != "" {
// rawValueOffsetCopy is our own private copy of the original data. // If we want to add an IFD tag, we'll have to build it first and
// It should always be four-bytes, but just copy whatever there is. // *then* add it via a different method.
rawValueOffsetCopy := make([]byte, len(ite.RawValueOffset))
copy(rawValueOffsetCopy, ite.RawValueOffset)
value = NewIfdBuilderTagValueFromBytes(rawValueOffsetCopy) if itevr == nil {
} else { // We don't have any ability to resolve the structure of the
var err error // child-IFD. Just install it as a normal tag rather than a
// fully-structured child-IFD. We're going to blank the value,
// though, since its original offset will no longer be valid
// (nor does it matter since this is just a temporary
// placeholder, in this situation).
value := NewIfdBuilderTagValueFromBytes([]byte { 0, 0, 0, 0 })
bt = NewBuilderTag(ite.Ii, ite.TagId, ite.TagType, value)
} else {
// Figure out which of the child-IFDs that are associated with
// this IFD represents this specific child IFD.
valueBytes, err := itevr.ValueBytes(ite) var childIfd *Ifd
if err != nil { for _, thisChildIfd := range ifd.Children {
if log.Is(err, ErrUnhandledUnknownTypedTag) == true { if thisChildIfd.ParentTagIndex != i {
ifdBuilderLogger.Warningf(nil, "Unknown-type tag can't be parsed so it can't be copied to the new IFD.") continue
continue } else if thisChildIfd.TagId != 0xffff && thisChildIfd.TagId != ite.TagId {
log.Panicf("child-IFD tag is not correct: TAG-POSITION=(%d) ITE=%s CHILD-IFD=%s", thisChildIfd.ParentTagIndex, ite, thisChildIfd)
}
childIfd = thisChildIfd
break
} }
log.Panic(err) if childIfd == nil {
childTagIds := make([]string, len(ifd.Children))
for j, childIfd := range ifd.Children {
childTagIds[j] = fmt.Sprintf("0x%02x (parent tag-position %d)", childIfd.TagId, childIfd.ParentTagIndex)
}
log.Panicf("could not find child IFD for child ITE: II=[%s] TAG-ID=(0x%02x) CURRENT-TAG-POSITION=(%d) CHILDREN=%v", ite.Ii, ite.TagId, i, childTagIds)
}
childIb := NewIfdBuilderFromExistingChain(childIfd, itevr)
bt = ib.NewBuilderTagFromBuilder(childIb)
}
} else {
var value *IfdBuilderTagValue
if itevr == nil {
// rawValueOffsetCopy is our own private copy of the original data.
// It should always be four-bytes, but just copy whatever there is.
rawValueOffsetCopy := make([]byte, len(ite.RawValueOffset))
copy(rawValueOffsetCopy, ite.RawValueOffset)
value = NewIfdBuilderTagValueFromBytes(rawValueOffsetCopy)
} else {
var err error
valueBytes, err := itevr.ValueBytes(ite)
if err != nil {
if log.Is(err, ErrUnhandledUnknownTypedTag) == true {
ifdBuilderLogger.Warningf(nil, "Unknown-type tag can't be parsed so it can't be copied to the new IFD.")
continue
}
log.Panic(err)
}
value = NewIfdBuilderTagValueFromBytes(valueBytes)
} }
value = NewIfdBuilderTagValueFromBytes(valueBytes) bt = NewBuilderTag(ifd.Ii, ite.TagId, ite.TagType, value)
} }
bt := NewBuilderTag(ifd.Ii, ite.TagId, ite.TagType, value) err := ib.add(bt)
err := ib.Add(bt)
log.PanicIf(err) log.PanicIf(err)
} }
@ -883,7 +939,7 @@ func (ib *IfdBuilder) AddFromConfig(tagId uint16, value interface{}) (err error)
bt := NewStandardBuilderTagFromConfig(ib.ii, tagId, ib.byteOrder, value) bt := NewStandardBuilderTagFromConfig(ib.ii, tagId, ib.byteOrder, value)
err = ib.Add(bt) err = ib.add(bt)
log.PanicIf(err) log.PanicIf(err)
return nil return nil
@ -922,7 +978,7 @@ func (ib *IfdBuilder) AddFromConfigWithName(tagName string, value interface{}) (
bt := NewStandardBuilderTagFromConfigWithName(ib.ii, tagName, ib.byteOrder, value) bt := NewStandardBuilderTagFromConfigWithName(ib.ii, tagName, ib.byteOrder, value)
err = ib.Add(bt) err = ib.add(bt)
log.PanicIf(err) log.PanicIf(err)
return nil return nil

View File

@ -1211,7 +1211,7 @@ func TestDeleteAll(t *testing.T) {
} }
} }
func TestNewIfdBuilderFromExistingChain(t *testing.T) { func Test_IfdBuilder_CreateIfdBuilderFromExistingChain(t *testing.T) {
defer func() { defer func() {
if state := recover(); state != nil { if state := recover(); state != nil {
err := log.Wrap(state.(error)) err := log.Wrap(state.(error))
@ -1223,13 +1223,15 @@ func TestNewIfdBuilderFromExistingChain(t *testing.T) {
filepath := path.Join(assetsPath, "NDM_8901.jpg") filepath := path.Join(assetsPath, "NDM_8901.jpg")
exifData, err := e.SearchAndExtractExif(filepath) rawExif, err := e.SearchAndExtractExif(filepath)
log.PanicIf(err) log.PanicIf(err)
_, index, err := e.Collect(exifData) _, index, err := e.Collect(rawExif)
log.PanicIf(err) log.PanicIf(err)
ib := NewIfdBuilderFromExistingChain(index.RootIfd, exifData) itevr := NewIfdTagEntryValueResolver(rawExif, index.RootIfd.ByteOrder)
ib := NewIfdBuilderFromExistingChain(index.RootIfd, itevr)
lines := ib.DumpToStrings() lines := ib.DumpToStrings()
expected := []string { expected := []string {
@ -1295,7 +1297,7 @@ func TestNewIfdBuilderFromExistingChain(t *testing.T) {
// TODO(dustin): !! Test with an actual GPS-attached image. // TODO(dustin): !! Test with an actual GPS-attached image.
func TestNewIfdBuilderFromExistingChain_RealData(t *testing.T) { func Test_IfdBuilder_CreateIfdBuilderFromExistingChain_RealData(t *testing.T) {
filepath := path.Join(assetsPath, "NDM_8901.jpg") filepath := path.Join(assetsPath, "NDM_8901.jpg")
e := NewExif() e := NewExif()
@ -1310,12 +1312,14 @@ func TestNewIfdBuilderFromExistingChain_RealData(t *testing.T) {
log.PanicIf(err) log.PanicIf(err)
originalTags := index.RootIfd.DumpTags() originalTags := index.RootIfd.DumpTags()
index.RootIfd.PrintTagTree(true)
// Encode back to binary. // Encode back to binary.
ibe := NewIfdByteEncoder() ibe := NewIfdByteEncoder()
rootIb := NewIfdBuilderFromExistingChain(index.RootIfd, rawExif) itevr := NewIfdTagEntryValueResolver(rawExif, index.RootIfd.ByteOrder)
rootIb := NewIfdBuilderFromExistingChain(index.RootIfd, itevr)
updatedExif, err := ibe.EncodeToExif(rootIb) updatedExif, err := ibe.EncodeToExif(rootIb)
log.PanicIf(err) log.PanicIf(err)
@ -1326,8 +1330,11 @@ func TestNewIfdBuilderFromExistingChain_RealData(t *testing.T) {
log.PanicIf(err) log.PanicIf(err)
recoveredTags := index.RootIfd.DumpTags() recoveredTags := index.RootIfd.DumpTags()
index.RootIfd.PrintTagTree(true)
return
// Validate that all of the same IFDs were presented. // Validate that all of the same IFDs were presented.
originalIfdTags := make([][2]interface{}, 0) originalIfdTags := make([][2]interface{}, 0)
@ -1405,7 +1412,7 @@ func TestNewIfdBuilderFromExistingChain_RealData(t *testing.T) {
} }
} }
func TestNewIfdBuilderWithExistingIfd(t *testing.T) { func Test_IfdBuilder_CreateIfdBuilderWithExistingIfd(t *testing.T) {
tagId := IfdTagIdWithIdentityOrFail(GpsIi) tagId := IfdTagIdWithIdentityOrFail(GpsIi)
parentIfd := &Ifd{ parentIfd := &Ifd{

View File

@ -330,9 +330,17 @@ type Ifd struct {
ByteOrder binary.ByteOrder ByteOrder binary.ByteOrder
Ii IfdIdentity Ii IfdIdentity
TagId uint16
Id int Id int
ParentIfd *Ifd ParentIfd *Ifd
// ParentTagIndex is our tag position in the parent IFD, if we had a parent
// (if `ParentIfd` is not nil and we weren't an IFD referenced as a sibling
// instead of as a child).
ParentTagIndex int
Name string Name string
Index int Index int
Offset uint32 Offset uint32
@ -429,7 +437,7 @@ func (ifd Ifd) String() string {
parentOffset = ifd.ParentIfd.Offset parentOffset = ifd.ParentIfd.Offset
} }
return fmt.Sprintf("Ifd<ID=(%d) PARENT-IFD=[%s] IFD=[%s] IDX=(%d) COUNT=(%d) OFF=(0x%04x) CHILDREN=(%d) PARENT=(0x%04x) NEXT-IFD=(0x%04x)", ifd.Id, ifd.Ii.ParentIfdName, ifd.Ii.IfdName, ifd.Index, len(ifd.Entries), ifd.Offset, len(ifd.Children), parentOffset, ifd.NextIfdOffset) return fmt.Sprintf("Ifd<ID=(%d) PARENT-IFD=[%s] IFD=[%s] INDEX=(%d) COUNT=(%d) OFF=(0x%04x) CHILDREN=(%d) PARENT=(0x%04x) NEXT-IFD=(0x%04x)>", ifd.Id, ifd.Ii.ParentIfdName, ifd.Ii.IfdName, ifd.Index, len(ifd.Entries), ifd.Offset, len(ifd.Children), parentOffset, ifd.NextIfdOffset)
} }
func (ifd *Ifd) Thumbnail() (data []byte, err error) { func (ifd *Ifd) Thumbnail() (data []byte, err error) {
@ -497,7 +505,7 @@ func (ifd *Ifd) DumpTags() []*IfdTagEntry {
return ifd.dumpTags(nil) return ifd.dumpTags(nil)
} }
func (ifd *Ifd) printTagTree(index, level int, nextLink bool) { func (ifd *Ifd) printTagTree(populateValues bool, index, level int, nextLink bool) {
indent := strings.Repeat(" ", level * 2) indent := strings.Repeat(" ", level * 2)
prefix := " " prefix := " "
@ -505,7 +513,7 @@ func (ifd *Ifd) printTagTree(index, level int, nextLink bool) {
prefix = ">" prefix = ">"
} }
fmt.Printf("%s%sIFD: %s INDEX=(%d)\n", indent, prefix, ifd, index) fmt.Printf("%s%sIFD: %s\n", indent, prefix, ifd)
// Quickly create an index of the child-IFDs. // Quickly create an index of the child-IFDs.
@ -530,7 +538,15 @@ func (ifd *Ifd) printTagTree(index, level int, nextLink bool) {
tagName = it.Name tagName = it.Name
} }
fmt.Printf("%s - TAG: %s NAME=[%s]\n", indent, tag, tagName) var value interface{}
if populateValues == true {
var err error
value, err = ifd.TagValue(tag)
log.PanicIf(err)
}
fmt.Printf("%s - TAG: %s NAME=[%s] VALUE=[%v]\n", indent, tag, tagName, value)
} }
if tag.ChildIfdName != "" { if tag.ChildIfdName != "" {
@ -541,7 +557,7 @@ func (ifd *Ifd) printTagTree(index, level int, nextLink bool) {
log.Panicf("alien child IFD referenced by a tag: [%s]", tag.ChildIfdName) log.Panicf("alien child IFD referenced by a tag: [%s]", tag.ChildIfdName)
} }
childIfd.printTagTree(0, level + 1, false) childIfd.printTagTree(populateValues, 0, level + 1, false)
} }
} }
@ -550,13 +566,13 @@ func (ifd *Ifd) printTagTree(index, level int, nextLink bool) {
} }
if ifd.NextIfd != nil { if ifd.NextIfd != nil {
ifd.NextIfd.printTagTree(index + 1, level, true) ifd.NextIfd.printTagTree(populateValues, index + 1, level, true)
} }
} }
// PrintTagTree prints the IFD hierarchy. // PrintTagTree prints the IFD hierarchy.
func (ifd *Ifd) PrintTagTree() { func (ifd *Ifd) PrintTagTree(populateValues bool) {
ifd.printTagTree(0, 0, false) ifd.printTagTree(populateValues, 0, 0, false)
} }
func (ifd *Ifd) printIfdTree(level int, nextLink bool) { func (ifd *Ifd) printIfdTree(level int, nextLink bool) {
@ -671,9 +687,16 @@ func (ifd *Ifd) DumpTree() []string {
type QueuedIfd struct { type QueuedIfd struct {
Ii IfdIdentity Ii IfdIdentity
TagId uint16
Index int Index int
Offset uint32 Offset uint32
Parent *Ifd Parent *Ifd
// ParentTagIndex is our tag position in the parent IFD, if we had a parent
// (if `ParentIfd` is not nil and we weren't an IFD referenced as a sibling
// instead of as a child).
ParentTagIndex int
} }
@ -700,6 +723,8 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error
queue := []QueuedIfd{ queue := []QueuedIfd{
{ {
Ii: RootIi, Ii: RootIi,
TagId: 0xffff,
Index: 0, Index: 0,
Offset: rootIfdOffset, Offset: rootIfdOffset,
}, },
@ -712,14 +737,14 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error
break break
} }
ii := queue[0].Ii qi := queue[0]
ii := qi.Ii
name := ii.IfdName name := ii.IfdName
index := queue[0].Index
offset := queue[0].Offset
parentIfd := queue[0].Parent index := qi.Index
offset := qi.Offset
parentIfd := qi.Parent
queue = queue[1:] queue = queue[1:]
@ -741,53 +766,62 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error
entriesByTagId[tag.TagId] = append(tags, tag) entriesByTagId[tag.TagId] = append(tags, tag)
} }
ifd := Ifd{ ifd := &Ifd{
addressableData: ie.exifData[ExifAddressableAreaStart:], addressableData: ie.exifData[ExifAddressableAreaStart:],
ByteOrder: ie.byteOrder, ByteOrder: ie.byteOrder,
Ii: ii, Ii: ii,
TagId: qi.TagId,
Id: id, Id: id,
ParentIfd: parentIfd, ParentIfd: parentIfd,
ParentTagIndex: qi.ParentTagIndex,
Name: name, Name: name,
Index: index, Index: index,
Offset: offset, Offset: offset,
Entries: entries, Entries: entries,
EntriesByTagId: entriesByTagId, EntriesByTagId: entriesByTagId,
// This is populated as each child is processed.
Children: make([]*Ifd, 0), Children: make([]*Ifd, 0),
NextIfdOffset: nextIfdOffset, NextIfdOffset: nextIfdOffset,
thumbnailData: thumbnailData, thumbnailData: thumbnailData,
} }
// Add ourselves to a big list of IFDs. // Add ourselves to a big list of IFDs.
ifds = append(ifds, &ifd) ifds = append(ifds, ifd)
// Install ourselves into a by-id lookup table (keys are unique). // Install ourselves into a by-id lookup table (keys are unique).
tree[id] = &ifd tree[id] = ifd
// Install into by-name buckets. // Install into by-name buckets.
if list_, found := lookup[ii]; found == true { if list_, found := lookup[ii]; found == true {
lookup[ii] = append(list_, &ifd) lookup[ii] = append(list_, ifd)
} else { } else {
list_ = make([]*Ifd, 1) list_ = make([]*Ifd, 1)
list_[0] = &ifd list_[0] = ifd
lookup[ii] = list_ lookup[ii] = list_
} }
// 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 {
previousIfd.NextIfd = &ifd previousIfd.NextIfd = ifd
} }
// Attach as a child to our parent (where we appeared as a tag in // Attach as a child to our parent (where we appeared as a tag in
// that IFD). // that IFD).
if parentIfd != nil { if parentIfd != nil {
parentIfd.Children = append(parentIfd.Children, &ifd) parentIfd.Children = append(parentIfd.Children, ifd)
} }
// Determine if any of our entries is a child IFD and queue it. // Determine if any of our entries is a child IFD and queue it.
for _, entry := range entries { for i, entry := range entries {
if entry.ChildIfdName == "" { if entry.ChildIfdName == "" {
continue continue
} }
@ -799,9 +833,12 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error
qi := QueuedIfd{ qi := QueuedIfd{
Ii: childIi, Ii: childIi,
TagId: entry.TagId,
Index: 0, Index: 0,
Offset: entry.ValueOffset, Offset: entry.ValueOffset,
Parent: &ifd, Parent: ifd,
ParentTagIndex: i,
} }
queue = append(queue, qi) queue = append(queue, qi)
@ -810,10 +847,11 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error
// If there's another IFD in the chain. // If there's another IFD in the chain.
if nextIfdOffset != 0 { if nextIfdOffset != 0 {
// Allow the next link to know what the previous link was. // Allow the next link to know what the previous link was.
edges[nextIfdOffset] = &ifd edges[nextIfdOffset] = ifd
qi := QueuedIfd{ qi := QueuedIfd{
Ii: ii, Ii: ii,
TagId: 0xffff,
Index: index + 1, Index: index + 1,
Offset: nextIfdOffset, Offset: nextIfdOffset,
} }