diff --git a/ifd_builder.go b/ifd_builder.go index 791f121..4e34bca 100644 --- a/ifd_builder.go +++ b/ifd_builder.go @@ -298,9 +298,7 @@ func NewIfdBuilderWithExistingIfd(ifd *Ifd) (ib *IfdBuilder) { // NewIfdBuilderFromExistingChain creates a chain of IB instances from an // IFD chain generated from real data. -func NewIfdBuilderFromExistingChain(rootIfd *Ifd, exifData []byte) (firstIb *IfdBuilder) { - itevr := NewIfdTagEntryValueResolver(exifData, rootIfd.ByteOrder) - +func NewIfdBuilderFromExistingChain(rootIfd *Ifd, itevr *IfdTagEntryValueResolver) (firstIb *IfdBuilder) { // 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 @@ -318,15 +316,6 @@ func NewIfdBuilderFromExistingChain(rootIfd *Ifd, exifData []byte) (firstIb *Ifd err := newIb.AddTagsFromExisting(thisExistingIfd, itevr, nil, nil) 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 i++ } @@ -375,14 +364,14 @@ func (ib *IfdBuilder) SetThumbnail(data []byte) (err error) { if data == nil || len(data) == 0 { // TODO(dustin): !! Debugging. -fmt.Printf("Thumbnail empty.\n") +// fmt.Printf("Thumbnail empty.\n") log.Panic("thumbnail is empty") } ib.thumbnailData = data -fmt.Printf("SETTING THUMBNAIL.\n") +// fmt.Printf("SETTING THUMBNAIL.\n") ibtvfb := NewIfdBuilderTagValueFromBytes(ib.thumbnailData) offsetBt := NewBuilderTag(ib.ii, ThumbnailOffsetTagId, TypeLong, ibtvfb) @@ -398,7 +387,7 @@ fmt.Printf("SETTING THUMBNAIL.\n") // TODO(dustin): !! Debugging. -fmt.Printf("Set thumbnail into IB.\n") +// fmt.Printf("Set thumbnail into IB.\n") return nil @@ -664,7 +653,7 @@ func (ib *IfdBuilder) Set(bt builderTag) (err error) { if err == nil { ib.tags[position] = bt } else if log.Is(err, ErrTagEntryNotFound) == true { - err = ib.Add(bt) + err = ib.add(bt) log.PanicIf(err) } else { log.Panic(err) @@ -711,7 +700,7 @@ func (ib *IfdBuilder) Find(tagId uint16) (position int, err error) { return found[0], nil } -func (ib *IfdBuilder) Add(bt builderTag) (err error) { +func (ib *IfdBuilder) add(bt builderTag) (err error) { defer func() { if state := recover(); state != nil { 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) } + 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 { - 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 } @@ -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) - bt := NewChildIfdBuilderTag( + bt = NewChildIfdBuilderTag( ib.ii, childIb.ifdTagId, value) - ib.tags = append(ib.tags, bt) - - return nil + return bt } // 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() if err == nil { +// TODO(dustin): The thumbnail tags will be added out of order. + // TODO(dustin): !! Debugging. -fmt.Printf("Importing thumbnail: %s\n", ifd.Identity()) +// fmt.Printf("Importing thumbnail: %s\n", ifd.Identity()) err = ib.SetThumbnail(thumbnailData) log.PanicIf(err) @@ -792,17 +808,12 @@ fmt.Printf("Importing thumbnail: %s\n", ifd.Identity()) } else { // 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 { - if ite.ChildIfdName != "" { - // 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 { + for i, ite := range ifd.Entries { + if ite.TagId == ThumbnailOffsetTagId || ite.TagId == ThumbnailSizeTagId { // These will be added on-the-fly when we encode. continue @@ -838,34 +849,79 @@ fmt.Printf("NO THUMBNAIL FOUND: %s\n", ifd.Identity()) } } - var value *IfdBuilderTagValue + var bt builderTag - 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) + if ite.ChildIfdName != "" { + // If we want to add an IFD tag, we'll have to build it first and + // *then* add it via a different method. - value = NewIfdBuilderTagValueFromBytes(rawValueOffsetCopy) - } else { - var err error + if itevr == nil { + // We don't have any ability to resolve the structure of the + // 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) - 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 + var childIfd *Ifd + for _, thisChildIfd := range ifd.Children { + if thisChildIfd.ParentTagIndex != i { + 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) } @@ -883,7 +939,7 @@ func (ib *IfdBuilder) AddFromConfig(tagId uint16, value interface{}) (err error) bt := NewStandardBuilderTagFromConfig(ib.ii, tagId, ib.byteOrder, value) - err = ib.Add(bt) + err = ib.add(bt) log.PanicIf(err) return nil @@ -922,7 +978,7 @@ func (ib *IfdBuilder) AddFromConfigWithName(tagName string, value interface{}) ( bt := NewStandardBuilderTagFromConfigWithName(ib.ii, tagName, ib.byteOrder, value) - err = ib.Add(bt) + err = ib.add(bt) log.PanicIf(err) return nil diff --git a/ifd_builder_test.go b/ifd_builder_test.go index 8834c8a..85750dc 100644 --- a/ifd_builder_test.go +++ b/ifd_builder_test.go @@ -1211,7 +1211,7 @@ func TestDeleteAll(t *testing.T) { } } -func TestNewIfdBuilderFromExistingChain(t *testing.T) { +func Test_IfdBuilder_CreateIfdBuilderFromExistingChain(t *testing.T) { defer func() { if state := recover(); state != nil { err := log.Wrap(state.(error)) @@ -1223,13 +1223,15 @@ func TestNewIfdBuilderFromExistingChain(t *testing.T) { filepath := path.Join(assetsPath, "NDM_8901.jpg") - exifData, err := e.SearchAndExtractExif(filepath) + rawExif, err := e.SearchAndExtractExif(filepath) log.PanicIf(err) - _, index, err := e.Collect(exifData) + _, index, err := e.Collect(rawExif) log.PanicIf(err) - ib := NewIfdBuilderFromExistingChain(index.RootIfd, exifData) + itevr := NewIfdTagEntryValueResolver(rawExif, index.RootIfd.ByteOrder) + ib := NewIfdBuilderFromExistingChain(index.RootIfd, itevr) + lines := ib.DumpToStrings() expected := []string { @@ -1295,7 +1297,7 @@ func TestNewIfdBuilderFromExistingChain(t *testing.T) { // 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") e := NewExif() @@ -1310,12 +1312,14 @@ func TestNewIfdBuilderFromExistingChain_RealData(t *testing.T) { log.PanicIf(err) originalTags := index.RootIfd.DumpTags() + index.RootIfd.PrintTagTree(true) // Encode back to binary. ibe := NewIfdByteEncoder() - rootIb := NewIfdBuilderFromExistingChain(index.RootIfd, rawExif) + itevr := NewIfdTagEntryValueResolver(rawExif, index.RootIfd.ByteOrder) + rootIb := NewIfdBuilderFromExistingChain(index.RootIfd, itevr) updatedExif, err := ibe.EncodeToExif(rootIb) log.PanicIf(err) @@ -1326,8 +1330,11 @@ func TestNewIfdBuilderFromExistingChain_RealData(t *testing.T) { log.PanicIf(err) recoveredTags := index.RootIfd.DumpTags() + index.RootIfd.PrintTagTree(true) +return + // Validate that all of the same IFDs were presented. 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) parentIfd := &Ifd{ diff --git a/ifd_enumerate.go b/ifd_enumerate.go index cd8b431..1a343d8 100644 --- a/ifd_enumerate.go +++ b/ifd_enumerate.go @@ -330,9 +330,17 @@ type Ifd struct { ByteOrder binary.ByteOrder Ii IfdIdentity + TagId uint16 Id int + 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 Index int Offset uint32 @@ -429,7 +437,7 @@ func (ifd Ifd) String() string { parentOffset = ifd.ParentIfd.Offset } - return fmt.Sprintf("Ifd", 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) { @@ -497,7 +505,7 @@ func (ifd *Ifd) DumpTags() []*IfdTagEntry { 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) prefix := " " @@ -505,7 +513,7 @@ func (ifd *Ifd) printTagTree(index, level int, nextLink bool) { 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. @@ -530,7 +538,15 @@ func (ifd *Ifd) printTagTree(index, level int, nextLink bool) { 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 != "" { @@ -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) } - 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 { - ifd.NextIfd.printTagTree(index + 1, level, true) + ifd.NextIfd.printTagTree(populateValues, index + 1, level, true) } } // PrintTagTree prints the IFD hierarchy. -func (ifd *Ifd) PrintTagTree() { - ifd.printTagTree(0, 0, false) +func (ifd *Ifd) PrintTagTree(populateValues bool) { + ifd.printTagTree(populateValues, 0, 0, false) } func (ifd *Ifd) printIfdTree(level int, nextLink bool) { @@ -671,9 +687,16 @@ func (ifd *Ifd) DumpTree() []string { type QueuedIfd struct { Ii IfdIdentity + TagId uint16 + Index int Offset uint32 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{ { Ii: RootIi, + TagId: 0xffff, + Index: 0, Offset: rootIfdOffset, }, @@ -712,14 +737,14 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error break } - ii := queue[0].Ii - + qi := queue[0] + ii := qi.Ii 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:] @@ -741,53 +766,62 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error entriesByTagId[tag.TagId] = append(tags, tag) } - ifd := Ifd{ + ifd := &Ifd{ addressableData: ie.exifData[ExifAddressableAreaStart:], ByteOrder: ie.byteOrder, + Ii: ii, + TagId: qi.TagId, + Id: id, + ParentIfd: parentIfd, + ParentTagIndex: qi.ParentTagIndex, + Name: name, Index: index, Offset: offset, Entries: entries, EntriesByTagId: entriesByTagId, + + // This is populated as each child is processed. Children: make([]*Ifd, 0), + NextIfdOffset: nextIfdOffset, thumbnailData: thumbnailData, } // 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). - tree[id] = &ifd + tree[id] = ifd // Install into by-name buckets. if list_, found := lookup[ii]; found == true { - lookup[ii] = append(list_, &ifd) + lookup[ii] = append(list_, ifd) } else { list_ = make([]*Ifd, 1) - list_[0] = &ifd + list_[0] = ifd lookup[ii] = list_ } // Add a link from the previous IFD in the chain to us. 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 // that IFD). 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. - for _, entry := range entries { + for i, entry := range entries { if entry.ChildIfdName == "" { continue } @@ -799,9 +833,12 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error qi := QueuedIfd{ Ii: childIi, + TagId: entry.TagId, + Index: 0, Offset: entry.ValueOffset, - Parent: &ifd, + Parent: ifd, + ParentTagIndex: i, } 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 nextIfdOffset != 0 { // Allow the next link to know what the previous link was. - edges[nextIfdOffset] = &ifd + edges[nextIfdOffset] = ifd qi := QueuedIfd{ Ii: ii, + TagId: 0xffff, Index: index + 1, Offset: nextIfdOffset, }