mirror of
https://github.com/dsoprea/go-exif.git
synced 2025-05-31 11:41:57 +00:00
ifd_builder_encoder: Fixed subtle but detrimental bugs.
- ifd_builder_encode - Bugfix: We were assigning the "next" IFD in correctly (so, chaining was broken). - Bugfix: We weren't using the right value when *writing* the "next" IFD offset (so, chaining was, again, broken). - The same IFD was showing as both a child and a sibling, so we were seeing the EXIF IFD also being chained-to by the first root IFD instead of the *actual* sibling, but only after encoding and then decoding (so, this had to be tracked down in the encoding semantics, which was non-trivial). - Added a test for the stuff that we fixed. - Implemented journaling, where we can dump a hierarchically-formatted list of operations involved in a completed encode. - ifd_enumerate: Added (Ifd).TagValueBytes() to return the raw tag- value's bytes (rather than just having (Ifd).TagValue(), which returns the value marshaled into a string). Good for comparing raw encoded and decoded values without imposing interpretation (which gets us into trouble with unknown-type tags whose values are non-standard and undocumentedand, therefore, not actually parseable). - Rewired a lot of print and dump functions. - For naming-specificity as well as functionality. - Removed from debugging.
This commit is contained in:
parent
6207bd6200
commit
3cee9b956b
166
ifd_builder.go
166
ifd_builder.go
@ -289,27 +289,23 @@ 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) (rootIb *IfdBuilder) {
|
func NewIfdBuilderFromExistingChain(rootIfd *Ifd, exifData []byte) (firstIb *IfdBuilder) {
|
||||||
itevr := NewIfdTagEntryValueResolver(exifData, rootIfd.ByteOrder)
|
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 newIb *IfdBuilder
|
var lastIb *IfdBuilder
|
||||||
|
i := 0
|
||||||
for thisExistingIfd := rootIfd; thisExistingIfd != nil; thisExistingIfd = thisExistingIfd.NextIfd {
|
for thisExistingIfd := rootIfd; thisExistingIfd != nil; thisExistingIfd = thisExistingIfd.NextIfd {
|
||||||
lastIb := newIb
|
|
||||||
|
|
||||||
ii := thisExistingIfd.Identity()
|
ii := thisExistingIfd.Identity()
|
||||||
|
|
||||||
newIb = NewIfdBuilder(ii, rootIfd.ByteOrder)
|
newIb := NewIfdBuilder(ii, thisExistingIfd.ByteOrder)
|
||||||
if lastIb != nil {
|
if firstIb == nil {
|
||||||
fmt.Printf("SETTING NEXT-IB: %s\n", newIb)
|
firstIb = newIb
|
||||||
|
} else {
|
||||||
lastIb.SetNextIb(newIb)
|
lastIb.SetNextIb(newIb)
|
||||||
}
|
}
|
||||||
|
|
||||||
if rootIb == nil {
|
|
||||||
rootIb = newIb
|
|
||||||
}
|
|
||||||
|
|
||||||
err := newIb.AddTagsFromExisting(thisExistingIfd, itevr, nil, nil)
|
err := newIb.AddTagsFromExisting(thisExistingIfd, itevr, nil, nil)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
@ -321,9 +317,12 @@ func NewIfdBuilderFromExistingChain(rootIfd *Ifd, exifData []byte) (rootIb *IfdB
|
|||||||
err = newIb.AddChildIb(childIb)
|
err = newIb.AddChildIb(childIb)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastIb = newIb
|
||||||
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
return rootIb
|
return firstIb
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ib *IfdBuilder) String() string {
|
func (ib *IfdBuilder) String() string {
|
||||||
@ -333,66 +332,110 @@ func (ib *IfdBuilder) String() string {
|
|||||||
nextIfdPhrase = ib.nextIb.ii.IfdName
|
nextIfdPhrase = ib.nextIb.ii.IfdName
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("IfdBuilder<NAME=[%s] TAG-ID=(0x%02x) BO=[%s] COUNT=(%d) OFFSET=(0x%04x) NEXT-IFD=(0x%04x)>", ib.ii, ib.ifdTagId, ib.byteOrder, len(ib.tags), ib.existingOffset, nextIfdPhrase)
|
return fmt.Sprintf("IfdBuilder<PARENT-IFD=[%s] IFD=[%s] TAG-ID=(0x%02x) COUNT=(%d) OFF=(0x%04x) NEXT-IFD=(0x%04x)>", ib.ii.ParentIfdName, ib.ii.IfdName, ib.ifdTagId, len(ib.tags), ib.existingOffset, nextIfdPhrase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ib *IfdBuilder) Tags() (tags []builderTag) {
|
func (ib *IfdBuilder) Tags() (tags []builderTag) {
|
||||||
return ib.tags
|
return ib.tags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ib *IfdBuilder) dump(levels int) {
|
func (ib *IfdBuilder) printTagTree(levels int) {
|
||||||
indent := strings.Repeat(" ", levels * 4)
|
indent := strings.Repeat(" ", levels * 2)
|
||||||
|
|
||||||
if levels == 0 {
|
|
||||||
fmt.Printf("%sIFD: %s\n", indent, ib)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("%sChild IFD: %s\n", indent, ib)
|
|
||||||
}
|
|
||||||
|
|
||||||
ti := NewTagIndex()
|
ti := NewTagIndex()
|
||||||
|
i := 0
|
||||||
if len(ib.tags) > 0 {
|
for currentIb := ib; currentIb != nil; currentIb = currentIb.nextIb {
|
||||||
fmt.Printf("\n")
|
prefix := " "
|
||||||
|
if i > 0 {
|
||||||
for i, tag := range ib.tags {
|
prefix = ">"
|
||||||
_, isChildIb := IfdTagNameWithId(ib.ii.IfdName, tag.tagId)
|
|
||||||
|
|
||||||
tagName := ""
|
|
||||||
|
|
||||||
// If a normal tag (not a child IFD) get the name.
|
|
||||||
if isChildIb == true {
|
|
||||||
tagName = "<Child IFD>"
|
|
||||||
} else {
|
|
||||||
it, err := ti.Get(tag.ii, tag.tagId)
|
|
||||||
if log.Is(err, ErrTagNotFound) == true {
|
|
||||||
tagName = "<UNKNOWN>"
|
|
||||||
} else if err != nil {
|
|
||||||
log.Panic(err)
|
|
||||||
} else {
|
|
||||||
tagName = it.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("%s (%d): [%s] %s\n", indent, i, tagName, tag)
|
|
||||||
|
|
||||||
if isChildIb == true {
|
|
||||||
if tag.value.IsIb() == false {
|
|
||||||
log.Panicf("tag-ID (0x%02x) is an IFD but the tag value is not an IB instance: %v", tag.tagId, tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("\n")
|
|
||||||
|
|
||||||
childIb := tag.value.Ib()
|
|
||||||
childIb.dump(levels + 1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("\n")
|
if levels == 0 {
|
||||||
|
fmt.Printf("%s%sIFD: %s\n", indent, prefix, currentIb)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%s%sChild IFD: %s\n", indent, prefix, currentIb)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(currentIb.tags) > 0 {
|
||||||
|
fmt.Printf("\n")
|
||||||
|
|
||||||
|
for i, tag := range currentIb.tags {
|
||||||
|
_, isChildIb := IfdTagNameWithId(currentIb.ii.IfdName, tag.tagId)
|
||||||
|
|
||||||
|
tagName := ""
|
||||||
|
|
||||||
|
// If a normal tag (not a child IFD) get the name.
|
||||||
|
if isChildIb == true {
|
||||||
|
tagName = "<Child IFD>"
|
||||||
|
} else {
|
||||||
|
it, err := ti.Get(tag.ii, tag.tagId)
|
||||||
|
if log.Is(err, ErrTagNotFound) == true {
|
||||||
|
tagName = "<UNKNOWN>"
|
||||||
|
} else if err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
} else {
|
||||||
|
tagName = it.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s (%d): [%s] %s\n", indent, i, tagName, tag)
|
||||||
|
|
||||||
|
if isChildIb == true {
|
||||||
|
if tag.value.IsIb() == false {
|
||||||
|
log.Panicf("tag-ID (0x%02x) is an IFD but the tag value is not an IB instance: %v", tag.tagId, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\n")
|
||||||
|
|
||||||
|
childIb := tag.value.Ib()
|
||||||
|
childIb.printTagTree(levels + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ib *IfdBuilder) Dump() {
|
func (ib *IfdBuilder) PrintTagTree() {
|
||||||
ib.dump(0)
|
ib.printTagTree(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ib *IfdBuilder) printIfdTree(levels int) {
|
||||||
|
indent := strings.Repeat(" ", levels * 2)
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for currentIb := ib; currentIb != nil; currentIb = currentIb.nextIb {
|
||||||
|
prefix := " "
|
||||||
|
if i > 0 {
|
||||||
|
prefix = ">"
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s%s%s\n", indent, prefix,currentIb)
|
||||||
|
|
||||||
|
if len(currentIb.tags) > 0 {
|
||||||
|
for _, tag := range currentIb.tags {
|
||||||
|
_, isChildIb := IfdTagNameWithId(currentIb.ii.IfdName, tag.tagId)
|
||||||
|
|
||||||
|
if isChildIb == true {
|
||||||
|
if tag.value.IsIb() == false {
|
||||||
|
log.Panicf("tag-ID (0x%02x) is an IFD but the tag value is not an IB instance: %v", tag.tagId, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
childIb := tag.value.Ib()
|
||||||
|
childIb.printIfdTree(levels + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ib *IfdBuilder) PrintIfdTree() {
|
||||||
|
ib.printIfdTree(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ib *IfdBuilder) dumpToStrings(thisIb *IfdBuilder, prefix string, lines []string) (linesOutput []string) {
|
func (ib *IfdBuilder) dumpToStrings(thisIb *IfdBuilder, prefix string, lines []string) (linesOutput []string) {
|
||||||
@ -789,11 +832,6 @@ func (ib *IfdBuilder) SetFromConfigWithName(tagName string, value interface{}) (
|
|||||||
i, err := ib.Find(bt.tagId)
|
i, err := ib.Find(bt.tagId)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
fmt.Printf("FOUND [%s] AT POSITION (%d) OF (%d)\n", tagName, i + 1, len(ib.tags))
|
|
||||||
|
|
||||||
fmt.Printf("ORIGINAL TAG: %s\n", ib.tags[i])
|
|
||||||
fmt.Printf("UPDATED TAG: %s\n", bt)
|
|
||||||
|
|
||||||
ib.tags[i] = bt
|
ib.tags[i] = bt
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -3,6 +3,7 @@ package exif
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
|
||||||
@ -121,10 +122,18 @@ func (ida *ifdDataAllocator) Bytes() []byte {
|
|||||||
// out all of the allocations and indirection that is required for extended
|
// out all of the allocations and indirection that is required for extended
|
||||||
// data.
|
// data.
|
||||||
type IfdByteEncoder struct {
|
type IfdByteEncoder struct {
|
||||||
|
// journal holds a list of actions taken while encoding.
|
||||||
|
journal [][3]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIfdByteEncoder() (ibe *IfdByteEncoder) {
|
func NewIfdByteEncoder() (ibe *IfdByteEncoder) {
|
||||||
return new(IfdByteEncoder)
|
return &IfdByteEncoder{
|
||||||
|
journal: make([][3]string, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ibe *IfdByteEncoder) Journal() [][3]string {
|
||||||
|
return ibe.journal
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ibe *IfdByteEncoder) TableSize(entryCount int) uint32 {
|
func (ibe *IfdByteEncoder) TableSize(entryCount int) uint32 {
|
||||||
@ -132,6 +141,61 @@ func (ibe *IfdByteEncoder) TableSize(entryCount int) uint32 {
|
|||||||
return uint32(2) + (IfdTagEntrySize * uint32(entryCount)) + uint32(4)
|
return uint32(2) + (IfdTagEntrySize * uint32(entryCount)) + uint32(4)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ibe *IfdByteEncoder) pushToJournal(where, direction, format string, args ...interface{}) {
|
||||||
|
event := [3]string {
|
||||||
|
direction,
|
||||||
|
where,
|
||||||
|
fmt.Sprintf(format, args...),
|
||||||
|
}
|
||||||
|
|
||||||
|
ibe.journal = append(ibe.journal, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintJournal prints a hierarchical representation of the steps taken during
|
||||||
|
// encoding.
|
||||||
|
func (ibe *IfdByteEncoder) PrintJournal() {
|
||||||
|
maxWhereLength := 0
|
||||||
|
for _, event := range ibe.journal {
|
||||||
|
where := event[1]
|
||||||
|
|
||||||
|
len_ := len(where)
|
||||||
|
if len_ > maxWhereLength {
|
||||||
|
maxWhereLength = len_
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
level := 0
|
||||||
|
for i, event := range ibe.journal {
|
||||||
|
direction := event[0]
|
||||||
|
where := event[1]
|
||||||
|
message := event[2]
|
||||||
|
|
||||||
|
if direction != ">" && direction != "<" && direction != "-" {
|
||||||
|
log.Panicf("journal operation not valid: [%s]", direction)
|
||||||
|
}
|
||||||
|
|
||||||
|
if direction == "<" {
|
||||||
|
if level <= 0 {
|
||||||
|
log.Panicf("journal operations unbalanced (too many closes)")
|
||||||
|
}
|
||||||
|
|
||||||
|
level--
|
||||||
|
}
|
||||||
|
|
||||||
|
indent := strings.Repeat(" ", level)
|
||||||
|
|
||||||
|
fmt.Printf("%3d %s%s %s: %s\n", i, indent, direction, where, message)
|
||||||
|
|
||||||
|
if direction == ">" {
|
||||||
|
level++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if level != 0 {
|
||||||
|
log.Panicf("journal operations unbalanced (too many opens)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// encodeTagToBytes encodes the given tag to a byte stream. If
|
// encodeTagToBytes encodes the given tag to a byte stream. If
|
||||||
// `nextIfdOffsetToWrite` is more than (0), recurse into child IFDs
|
// `nextIfdOffsetToWrite` is more than (0), recurse into child IFDs
|
||||||
// (`nextIfdOffsetToWrite` is required in order for them to know where the its
|
// (`nextIfdOffsetToWrite` is required in order for them to know where the its
|
||||||
@ -144,12 +208,6 @@ func (ibe *IfdByteEncoder) encodeTagToBytes(ib *IfdBuilder, bt *builderTag, bw *
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// TODO(dustin): !! This function is running more times than it should.
|
|
||||||
|
|
||||||
if bt.tagId == 0x9286 {
|
|
||||||
fmt.Printf("ENCODING/WRITING: %s\n", bt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write tag-ID.
|
// Write tag-ID.
|
||||||
err = bw.WriteUint16(bt.tagId)
|
err = bw.WriteUint16(bt.tagId)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
@ -188,24 +246,12 @@ func (ibe *IfdByteEncoder) encodeTagToBytes(ib *IfdBuilder, bt *builderTag, bw *
|
|||||||
// Write four-byte value/offset.
|
// Write four-byte value/offset.
|
||||||
|
|
||||||
if len_ > 4 {
|
if len_ > 4 {
|
||||||
if bt.tagId == 0x9286 {
|
|
||||||
fmt.Printf("USERCOMMENTS: Allocating: [%s]\n", string(valueBytes[:5]))
|
|
||||||
}
|
|
||||||
|
|
||||||
offset, err := ida.Allocate(valueBytes)
|
offset, err := ida.Allocate(valueBytes)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
if bt.tagId == 0x9286 {
|
|
||||||
fmt.Printf("USERCOMMENTS: Offset: (%d) [0x%02x]\n", offset, offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = bw.WriteUint32(offset)
|
err = bw.WriteUint32(offset)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
} else {
|
} else {
|
||||||
if bt.tagId == 0x9286 {
|
|
||||||
fmt.Printf("USERCOMMENTS: Not allocating: [%v]\n", valueBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
fourBytes := make([]byte, 4)
|
fourBytes := make([]byte, 4)
|
||||||
copy(fourBytes, valueBytes)
|
copy(fourBytes, valueBytes)
|
||||||
|
|
||||||
@ -224,10 +270,14 @@ func (ibe *IfdByteEncoder) encodeTagToBytes(ib *IfdBuilder, bt *builderTag, bw *
|
|||||||
if nextIfdOffsetToWrite > 0 {
|
if nextIfdOffsetToWrite > 0 {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
ibe.pushToJournal("encodeTagToBytes", ">", "[%s]->[%s]", ib.ii.IfdName, bt.value.Ib().ii.IfdName)
|
||||||
|
|
||||||
// Create the block of IFD data and everything it requires.
|
// Create the block of IFD data and everything it requires.
|
||||||
childIfdBlock, err = ibe.encodeAndAttachIfd(bt.value.Ib(), nextIfdOffsetToWrite)
|
childIfdBlock, err = ibe.encodeAndAttachIfd(bt.value.Ib(), nextIfdOffsetToWrite)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
ibe.pushToJournal("encodeTagToBytes", "<", "[%s]->[%s]", bt.value.Ib().ii.IfdName, ib.ii.IfdName)
|
||||||
|
|
||||||
// Use the next-IFD offset for it. The IFD will actually get
|
// Use the next-IFD offset for it. The IFD will actually get
|
||||||
// attached after we return.
|
// attached after we return.
|
||||||
err = bw.WriteUint32(nextIfdOffsetToWrite)
|
err = bw.WriteUint32(nextIfdOffsetToWrite)
|
||||||
@ -237,6 +287,8 @@ func (ibe *IfdByteEncoder) encodeTagToBytes(ib *IfdBuilder, bt *builderTag, bw *
|
|||||||
// No child-IFDs are to be allocated. Finish the entry with a NULL
|
// No child-IFDs are to be allocated. Finish the entry with a NULL
|
||||||
// pointer.
|
// pointer.
|
||||||
|
|
||||||
|
ibe.pushToJournal("encodeTagToBytes", "-", "*Not* descending to child: [%s]", bt.value.Ib().ii.IfdName)
|
||||||
|
|
||||||
err = bw.WriteUint32(0)
|
err = bw.WriteUint32(0)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
}
|
}
|
||||||
@ -261,6 +313,8 @@ func (ibe *IfdByteEncoder) encodeIfdToBytes(ib *IfdBuilder, ifdAddressableOffset
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
ibe.pushToJournal("encodeIfdToBytes", ">", "%s", ib)
|
||||||
|
|
||||||
tableSize = ibe.TableSize(len(ib.tags))
|
tableSize = ibe.TableSize(len(ib.tags))
|
||||||
|
|
||||||
b := new(bytes.Buffer)
|
b := new(bytes.Buffer)
|
||||||
@ -309,10 +363,18 @@ func (ibe *IfdByteEncoder) encodeIfdToBytes(ib *IfdBuilder, ifdAddressableOffset
|
|||||||
// Write address of next IFD in chain. This will be the original
|
// Write address of next IFD in chain. This will be the original
|
||||||
// allocation offset plus the size of everything we have allocated for
|
// allocation offset plus the size of everything we have allocated for
|
||||||
// this IFD and its child-IFDs.
|
// this IFD and its child-IFDs.
|
||||||
|
//
|
||||||
|
// It is critical that this number is stepped properly. We experienced
|
||||||
|
// an issue whereby it first looked like we were duplicating the IFD and
|
||||||
|
// then that we were duplicating the tags in the wrong IFD, and then
|
||||||
|
// finally we determined that the next-IFD offset for the first IFD was
|
||||||
|
// accidentally pointing back to the EXIF IFD, so we were visiting it
|
||||||
|
// twice when visiting through the tags after decoding. It was an
|
||||||
|
// expensive bug to find.
|
||||||
|
|
||||||
offset := ifdAddressableOffset + dataSize
|
ibe.pushToJournal("encodeIfdToBytes", "-", "Setting 'next' IFD to (0x%08x).", nextIfdOffsetToWrite)
|
||||||
|
|
||||||
err := bw.WriteUint32(offset)
|
err := bw.WriteUint32(nextIfdOffsetToWrite)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
} else {
|
} else {
|
||||||
err := bw.WriteUint32(0)
|
err := bw.WriteUint32(0)
|
||||||
@ -336,6 +398,8 @@ func (ibe *IfdByteEncoder) encodeIfdToBytes(ib *IfdBuilder, ifdAddressableOffset
|
|||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ibe.pushToJournal("encodeIfdToBytes", "<", "%s", ib)
|
||||||
|
|
||||||
return b.Bytes(), tableSize, dataSize, childIfdSizes, nil
|
return b.Bytes(), tableSize, dataSize, childIfdSizes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,6 +411,8 @@ func (ibe *IfdByteEncoder) encodeAndAttachIfd(ib *IfdBuilder, ifdAddressableOffs
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
ibe.pushToJournal("encodeAndAttachIfd", ">", "%s", ib)
|
||||||
|
|
||||||
if len(ib.tags) == 0 {
|
if len(ib.tags) == 0 {
|
||||||
log.Panicf("trying to encode an IfdBuilder that doesn't have any tags")
|
log.Panicf("trying to encode an IfdBuilder that doesn't have any tags")
|
||||||
}
|
}
|
||||||
@ -354,12 +420,19 @@ func (ibe *IfdByteEncoder) encodeAndAttachIfd(ib *IfdBuilder, ifdAddressableOffs
|
|||||||
b := new(bytes.Buffer)
|
b := new(bytes.Buffer)
|
||||||
|
|
||||||
nextIfdOffsetToWrite := uint32(0)
|
nextIfdOffsetToWrite := uint32(0)
|
||||||
|
i := 0
|
||||||
for thisIb := ib; thisIb != nil; thisIb = thisIb.nextIb {
|
for thisIb := ib; thisIb != nil; thisIb = thisIb.nextIb {
|
||||||
// Do a dry-run in order to pre-determine its size requirement.
|
// Do a dry-run in order to pre-determine its size requirement.
|
||||||
|
|
||||||
_, tableSize, allocatedDataSize, _, err := ibe.encodeIfdToBytes(ib, ifdAddressableOffset, 0, false)
|
ibe.pushToJournal("encodeAndAttachIfd", ">", "Beginning encoding process: (%d) [%s] NEXT-IFD-OFFSET-TO-WRITE=(0x%08x)", i, thisIb.ii.IfdName, nextIfdOffsetToWrite)
|
||||||
|
|
||||||
|
ibe.pushToJournal("encodeAndAttachIfd", ">", "Calculating size: (%d) [%s]", i, thisIb.ii.IfdName)
|
||||||
|
|
||||||
|
_, tableSize, allocatedDataSize, _, err := ibe.encodeIfdToBytes(thisIb, ifdAddressableOffset, 0, false)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
ibe.pushToJournal("encodeAndAttachIfd", "<", "Finished calculating size: (%d) [%s]", i, thisIb.ii.IfdName)
|
||||||
|
|
||||||
ifdAddressableOffset += tableSize
|
ifdAddressableOffset += tableSize
|
||||||
nextIfdOffsetToWrite = ifdAddressableOffset + allocatedDataSize
|
nextIfdOffsetToWrite = ifdAddressableOffset + allocatedDataSize
|
||||||
|
|
||||||
@ -368,9 +441,15 @@ func (ibe *IfdByteEncoder) encodeAndAttachIfd(ib *IfdBuilder, ifdAddressableOffs
|
|||||||
|
|
||||||
setNextIb := thisIb.nextIb != nil
|
setNextIb := thisIb.nextIb != nil
|
||||||
|
|
||||||
tableAndAllocated, tableSize, allocatedDataSize, childIfdSizes, err := ibe.encodeIfdToBytes(ib, ifdAddressableOffset, nextIfdOffsetToWrite, setNextIb)
|
ibe.pushToJournal("encodeAndAttachIfd", ">", "Encoding starting: (%d) [%s] NEXT-IFD-OFFSET-TO-WRITE=(0x%08x)", i, thisIb.ii.IfdName, nextIfdOffsetToWrite)
|
||||||
|
|
||||||
|
tableAndAllocated, tableSize, allocatedDataSize, childIfdSizes, err :=
|
||||||
|
ibe.encodeIfdToBytes(thisIb, ifdAddressableOffset, nextIfdOffsetToWrite, setNextIb)
|
||||||
|
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
ibe.pushToJournal("encodeAndAttachIfd", "<", "Encoding done: (%d) [%s]", i, thisIb.ii.IfdName)
|
||||||
|
|
||||||
totalChildIfdSize := uint32(0)
|
totalChildIfdSize := uint32(0)
|
||||||
for _, childIfdSize := range childIfdSizes {
|
for _, childIfdSize := range childIfdSizes {
|
||||||
totalChildIfdSize += childIfdSize
|
totalChildIfdSize += childIfdSize
|
||||||
@ -383,10 +462,17 @@ func (ibe *IfdByteEncoder) encodeAndAttachIfd(ib *IfdBuilder, ifdAddressableOffs
|
|||||||
_, err = b.Write(tableAndAllocated)
|
_, err = b.Write(tableAndAllocated)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
// TODO(dustin): !! The core problem is the offset being the same for two IFDs.
|
||||||
// Advance past what we've allocated, thus far.
|
// Advance past what we've allocated, thus far.
|
||||||
ifdAddressableOffset = nextIfdOffsetToWrite
|
ifdAddressableOffset = nextIfdOffsetToWrite
|
||||||
|
|
||||||
|
ibe.pushToJournal("encodeAndAttachIfd", "<", "Finishing encoding process: (%d) [%s] [FINAL:] NEXT-IFD-OFFSET-TO-WRITE=(0x%08x)", i, ib.ii.IfdName, nextIfdOffsetToWrite)
|
||||||
|
|
||||||
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ibe.pushToJournal("encodeAndAttachIfd", "<", "%s", ib)
|
||||||
|
|
||||||
return b.Bytes(), nil
|
return b.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"path"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/dsoprea/go-logging"
|
"github.com/dsoprea/go-logging"
|
||||||
)
|
)
|
||||||
@ -925,3 +927,113 @@ func ExampleIfdByteEncoder_EncodeToExif() {
|
|||||||
// 4: IfdTagEntry<TAG-IFD=[] TAG-ID=(0x13e) TAG-TYPE=[RATIONAL] UNIT-COUNT=(1)> [[{286335522 858997828}]]
|
// 4: IfdTagEntry<TAG-IFD=[] TAG-ID=(0x13e) TAG-TYPE=[RATIONAL] UNIT-COUNT=(1)> [[{286335522 858997828}]]
|
||||||
// 5: IfdTagEntry<TAG-IFD=[] TAG-ID=(0x9201) TAG-TYPE=[SRATIONAL] UNIT-COUNT=(1)> [[{286335522 858997828}]]
|
// 5: IfdTagEntry<TAG-IFD=[] TAG-ID=(0x9201) TAG-TYPE=[SRATIONAL] UNIT-COUNT=(1)> [[{286335522 858997828}]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewIfdBuilderFromExistingChain_RealData(t *testing.T) {
|
||||||
|
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
||||||
|
|
||||||
|
e := NewExif()
|
||||||
|
|
||||||
|
|
||||||
|
rawExif, err := e.SearchAndExtractExif(filepath)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
// Decode from binary.
|
||||||
|
|
||||||
|
_, index, err := e.Collect(rawExif)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
originalTags := index.RootIfd.DumpTags()
|
||||||
|
|
||||||
|
// Encode back to binary.
|
||||||
|
|
||||||
|
ibe := NewIfdByteEncoder()
|
||||||
|
|
||||||
|
rootIb := NewIfdBuilderFromExistingChain(index.RootIfd, rawExif)
|
||||||
|
|
||||||
|
updatedExif, err := ibe.EncodeToExif(rootIb)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
// Parse again.
|
||||||
|
|
||||||
|
_, index, err = e.Collect(updatedExif)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
recoveredTags := index.RootIfd.DumpTags()
|
||||||
|
|
||||||
|
|
||||||
|
// Validate that all of the same IFDs were presented.
|
||||||
|
|
||||||
|
originalIfdTags := make([][2]interface{}, 0)
|
||||||
|
for _, ite := range originalTags {
|
||||||
|
if ite.ChildIfdName != "" {
|
||||||
|
originalIfdTags = append(originalIfdTags, [2]interface{} { ite.Ii, ite.TagId })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recoveredIfdTags := make([][2]interface{}, 0)
|
||||||
|
for _, ite := range recoveredTags {
|
||||||
|
if ite.ChildIfdName != "" {
|
||||||
|
recoveredIfdTags = append(recoveredIfdTags, [2]interface{} { ite.Ii, ite.TagId })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if reflect.DeepEqual(recoveredIfdTags, originalIfdTags) != true {
|
||||||
|
fmt.Printf("Original IFD tags:\n\n")
|
||||||
|
|
||||||
|
for i, x := range originalIfdTags {
|
||||||
|
fmt.Printf(" %02d %v\n", i, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\nRecovered IFD tags:\n\n")
|
||||||
|
|
||||||
|
for i, x := range recoveredIfdTags {
|
||||||
|
fmt.Printf(" %02d %v\n", i, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\n")
|
||||||
|
|
||||||
|
t.Fatalf("Recovered IFD tags are not correct.")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Validate that all of the tags owned by the IFDs were presented. The tags
|
||||||
|
// might not be in the same order since the IFD tags are allocated after
|
||||||
|
// the non-IFD ones.
|
||||||
|
|
||||||
|
originalNonIfdTags := make([]string, 0)
|
||||||
|
for _, ite := range originalTags {
|
||||||
|
if ite.ChildIfdName == "" {
|
||||||
|
originalNonIfdTags = append(originalNonIfdTags, fmt.Sprintf("%s 0x%02x", ite.Ii, ite.TagId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.StringSlice(originalNonIfdTags).Sort()
|
||||||
|
|
||||||
|
recoveredNonIfdTags := make([]string, 0)
|
||||||
|
for _, ite := range recoveredTags {
|
||||||
|
if ite.ChildIfdName == "" {
|
||||||
|
recoveredNonIfdTags = append(recoveredNonIfdTags, fmt.Sprintf("%s 0x%02x", ite.Ii, ite.TagId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.StringSlice(recoveredNonIfdTags).Sort()
|
||||||
|
|
||||||
|
|
||||||
|
if reflect.DeepEqual(recoveredNonIfdTags, originalNonIfdTags) != true {
|
||||||
|
fmt.Printf("Original non-IFD tags:\n\n")
|
||||||
|
|
||||||
|
for i, x := range originalNonIfdTags {
|
||||||
|
fmt.Printf(" %02d %v\n", i, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\nRecovered non-IFD tags:\n\n")
|
||||||
|
|
||||||
|
for i, x := range recoveredNonIfdTags {
|
||||||
|
fmt.Printf(" %02d %v\n", i, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\n")
|
||||||
|
|
||||||
|
t.Fatalf("Recovered non-IFD tags are not correct.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
150
ifd_enumerate.go
150
ifd_enumerate.go
@ -228,7 +228,7 @@ func (ie *IfdEnumerate) ParseIfd(ii IfdIdentity, ifdIndex int, ite *IfdTagEnumer
|
|||||||
return nextIfdOffset, entries, nil
|
return nextIfdOffset, entries, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan enumerates the different EXIF blocks (called IFDs).
|
// Scan enumerates the different EXIF's IFD blocks.
|
||||||
func (ie *IfdEnumerate) scan(ii IfdIdentity, ifdOffset uint32, visitor TagVisitor) (err error) {
|
func (ie *IfdEnumerate) scan(ii IfdIdentity, ifdOffset uint32, visitor TagVisitor) (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if state := recover(); state != nil {
|
if state := recover(); state != nil {
|
||||||
@ -270,6 +270,7 @@ func (ie *IfdEnumerate) Scan(ifdOffset uint32, visitor TagVisitor) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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
|
||||||
// and not involve other projects in semantics that they won't otherwise
|
// and not involve other projects in semantics that they won't otherwise
|
||||||
@ -307,6 +308,19 @@ func (ifd *Ifd) TagValue(ite *IfdTagEntry) (value interface{}, err error) {
|
|||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ifd *Ifd) TagValueBytes(ite *IfdTagEntry) (value []byte, err error) {
|
||||||
|
defer func() {
|
||||||
|
if state := recover(); state != nil {
|
||||||
|
err = log.Wrap(state.(error))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
value, err = ite.ValueBytes(ifd.addressableData, ifd.ByteOrder)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
// FindTagWithId returns a list of tags (usually just zero or one) that match
|
// FindTagWithId returns a list of tags (usually just zero or one) that match
|
||||||
// the given tag ID. This is efficient.
|
// the given tag ID. This is efficient.
|
||||||
func (ifd Ifd) FindTagWithId(tagId uint16) (results []*IfdTagEntry, err error) {
|
func (ifd Ifd) FindTagWithId(tagId uint16) (results []*IfdTagEntry, err error) {
|
||||||
@ -363,14 +377,61 @@ func (ifd Ifd) String() string {
|
|||||||
parentOffset = ifd.ParentIfd.Offset
|
parentOffset = ifd.ParentIfd.Offset
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("IFD<ID=(%d) N=[%s] IDX=(%d) OFF=(0x%04x) COUNT=(%d) CHILDREN=(%d) PARENT=(0x%04x) NEXT-IFD=(0x%04x)", ifd.Id, ifd.Name, ifd.Index, ifd.Offset, len(ifd.Entries), len(ifd.Children), parentOffset, ifd.NextIfdOffset)
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ifd Ifd) Identity() IfdIdentity {
|
func (ifd Ifd) Identity() IfdIdentity {
|
||||||
return ifd.Ii
|
return ifd.Ii
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ifd Ifd) printTree(level int, printTags bool, nextLink bool) {
|
func (ifd Ifd) dumpTags(tags []*IfdTagEntry) []*IfdTagEntry {
|
||||||
|
if tags == nil {
|
||||||
|
tags = make([]*IfdTagEntry, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quickly create an index of the child-IFDs.
|
||||||
|
|
||||||
|
childIfdIndex := make(map[string]*Ifd)
|
||||||
|
for _, childIfd := range ifd.Children {
|
||||||
|
childIfdIndex[childIfd.Ii.IfdName] = childIfd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, print the tags while also descending to child-IFDS as we encounter them.
|
||||||
|
|
||||||
|
ifdsFoundCount := 0
|
||||||
|
|
||||||
|
for _, tag := range ifd.Entries {
|
||||||
|
tags = append(tags, tag)
|
||||||
|
|
||||||
|
if tag.ChildIfdName != "" {
|
||||||
|
ifdsFoundCount++
|
||||||
|
|
||||||
|
childIfd, found := childIfdIndex[tag.ChildIfdName]
|
||||||
|
if found != true {
|
||||||
|
log.Panicf("alien child IFD referenced by a tag: [%s]", tag.ChildIfdName)
|
||||||
|
}
|
||||||
|
|
||||||
|
tags = childIfd.dumpTags(tags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ifd.Children) != ifdsFoundCount {
|
||||||
|
log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.Children), ifdsFoundCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ifd.NextIfd != nil {
|
||||||
|
tags = ifd.NextIfd.dumpTags(tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tags
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintTagTree prints the IFD hierarchy.
|
||||||
|
func (ifd Ifd) DumpTags() []*IfdTagEntry {
|
||||||
|
return ifd.dumpTags(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ifd Ifd) printTagTree(level int, nextLink bool) {
|
||||||
indent := strings.Repeat(" ", level * 2)
|
indent := strings.Repeat(" ", level * 2)
|
||||||
|
|
||||||
prefix := " "
|
prefix := " "
|
||||||
@ -393,19 +454,17 @@ func (ifd Ifd) printTree(level int, printTags bool, nextLink bool) {
|
|||||||
|
|
||||||
ti := NewTagIndex()
|
ti := NewTagIndex()
|
||||||
for _, tag := range ifd.Entries {
|
for _, tag := range ifd.Entries {
|
||||||
if printTags == true {
|
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 := ti.Get(ifd.Identity(), tag.TagId)
|
||||||
it, err := ti.Get(ifd.Identity(), tag.TagId)
|
|
||||||
|
|
||||||
tagName := ""
|
tagName := ""
|
||||||
if err == nil {
|
if err == nil {
|
||||||
tagName = it.Name
|
tagName = it.Name
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("%s - TAG: %s NAME=[%s]\n", indent, tag, tagName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s - TAG: %s NAME=[%s]\n", indent, tag, tagName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tag.ChildIfdName != "" {
|
if tag.ChildIfdName != "" {
|
||||||
@ -416,7 +475,7 @@ func (ifd Ifd) printTree(level int, printTags bool, 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.printTree(level + 1, printTags, false)
|
childIfd.printTagTree(level + 1, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,13 +484,61 @@ func (ifd Ifd) printTree(level int, printTags bool, nextLink bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ifd.NextIfd != nil {
|
if ifd.NextIfd != nil {
|
||||||
ifd.NextIfd.printTree(level, printTags, true)
|
ifd.NextIfd.printTagTree(level, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintTree prints the IFD hierarchy.
|
// PrintTagTree prints the IFD hierarchy.
|
||||||
func (ifd Ifd) PrintTree(printTags bool) {
|
func (ifd Ifd) PrintTagTree() {
|
||||||
ifd.printTree(0, printTags, false)
|
ifd.printTagTree(0, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ifd Ifd) printIfdTree(level int, nextLink bool) {
|
||||||
|
indent := strings.Repeat(" ", level * 2)
|
||||||
|
|
||||||
|
prefix := " "
|
||||||
|
if nextLink {
|
||||||
|
prefix = ">"
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s%s%s\n", indent, prefix, ifd)
|
||||||
|
|
||||||
|
// Quickly create an index of the child-IFDs.
|
||||||
|
|
||||||
|
childIfdIndex := make(map[string]*Ifd)
|
||||||
|
for _, childIfd := range ifd.Children {
|
||||||
|
childIfdIndex[childIfd.Ii.IfdName] = childIfd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, print the tags while also descending to child-IFDS as we encounter them.
|
||||||
|
|
||||||
|
ifdsFoundCount := 0
|
||||||
|
|
||||||
|
for _, tag := range ifd.Entries {
|
||||||
|
if tag.ChildIfdName != "" {
|
||||||
|
ifdsFoundCount++
|
||||||
|
|
||||||
|
childIfd, found := childIfdIndex[tag.ChildIfdName]
|
||||||
|
if found != true {
|
||||||
|
log.Panicf("alien child IFD referenced by a tag: [%s]", tag.ChildIfdName)
|
||||||
|
}
|
||||||
|
|
||||||
|
childIfd.printIfdTree(level + 1, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ifd.Children) != ifdsFoundCount {
|
||||||
|
log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.Children), ifdsFoundCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ifd.NextIfd != nil {
|
||||||
|
ifd.NextIfd.printIfdTree(level, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintIfdTree prints the IFD hierarchy.
|
||||||
|
func (ifd Ifd) PrintIfdTree() {
|
||||||
|
ifd.printIfdTree(0, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ifd Ifd) dumpTree(tagsDump []string, level int) []string {
|
func (ifd Ifd) dumpTree(tagsDump []string, level int) []string {
|
||||||
@ -545,6 +652,7 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error
|
|||||||
name := ii.IfdName
|
name := ii.IfdName
|
||||||
index := queue[0].Index
|
index := queue[0].Index
|
||||||
offset := queue[0].Offset
|
offset := queue[0].Offset
|
||||||
|
|
||||||
parentIfd := queue[0].Parent
|
parentIfd := queue[0].Parent
|
||||||
|
|
||||||
queue = queue[1:]
|
queue = queue[1:]
|
||||||
@ -617,13 +725,13 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
childId := IfdIdentity{
|
childIi := IfdIdentity{
|
||||||
ParentIfdName: name,
|
ParentIfdName: name,
|
||||||
IfdName: entry.ChildIfdName,
|
IfdName: entry.ChildIfdName,
|
||||||
}
|
}
|
||||||
|
|
||||||
qi := QueuedIfd{
|
qi := QueuedIfd{
|
||||||
Ii: childId,
|
Ii: childIi,
|
||||||
Index: 0,
|
Index: 0,
|
||||||
Offset: entry.ValueOffset,
|
Offset: entry.ValueOffset,
|
||||||
Parent: &ifd,
|
Parent: &ifd,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user