mirror of https://github.com/dsoprea/go-exif.git
ifd_builder_encode: Added tests for encoding child IBs to EXIF.
- Big step. This is the most complicated thing we can do. - ifd_builder_encode: Need to debug encoding linked IFDs to EXIF. - ifd_builder: Renamed `SetNextIfd()` to `SetNextIb()`. - ifd_builder: Bugfix to size assertion on return of (IfdBuilderEncode).encodeIfdToBytes(). - ifd_enumerate: Rename PrintNode() to PrintTree(). - ifd_enumerate: Added DumpTree() (to return a list of strings).pull/3/head
parent
b75f980bc4
commit
75b2c75c5a
|
@ -268,7 +268,7 @@ func NewIfdBuilderFromExistingChain(rootIfd *Ifd, exifData []byte) (rootIb *IfdB
|
|||
|
||||
newIb = NewIfdBuilder(ii, binary.BigEndian)
|
||||
if lastIb != nil {
|
||||
lastIb.SetNextIfd(newIb)
|
||||
lastIb.SetNextIb(newIb)
|
||||
}
|
||||
|
||||
if rootIb == nil {
|
||||
|
@ -410,7 +410,7 @@ func (ib *IfdBuilder) DumpToStrings() (lines []string) {
|
|||
return ib.dumpToStrings(ib, "", lines)
|
||||
}
|
||||
|
||||
func (ib *IfdBuilder) SetNextIfd(nextIb *IfdBuilder) (err error) {
|
||||
func (ib *IfdBuilder) SetNextIb(nextIb *IfdBuilder) (err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
|
|
|
@ -346,11 +346,16 @@ func (ibe *IfdByteEncoder) encodeAndAttachIfd(ib *IfdBuilder, ifdAddressableOffs
|
|||
|
||||
setNextIb := thisIb.nextIb != nil
|
||||
|
||||
tableAndAllocated, tableSize, allocatedDataSize, _, err := ibe.encodeIfdToBytes(ib, addressableOffset, nextIfdOffsetToWrite, setNextIb)
|
||||
tableAndAllocated, tableSize, allocatedDataSize, childIfdSizes, err := ibe.encodeIfdToBytes(ib, addressableOffset, nextIfdOffsetToWrite, setNextIb)
|
||||
log.PanicIf(err)
|
||||
|
||||
if len(tableAndAllocated) != int(tableSize + allocatedDataSize) {
|
||||
log.Panicf("IFD table and data is not a consistent size: (%d) != (%d)", len(tableAndAllocated), tableSize + allocatedDataSize)
|
||||
totalChildIfdSize := uint32(0)
|
||||
for _, childIfdSize := range childIfdSizes {
|
||||
totalChildIfdSize += childIfdSize
|
||||
}
|
||||
|
||||
if len(tableAndAllocated) != int(tableSize + allocatedDataSize + totalChildIfdSize) {
|
||||
log.Panicf("IFD table and data is not a consistent size: (%d) != (%d)", len(tableAndAllocated), tableSize + allocatedDataSize + totalChildIfdSize)
|
||||
}
|
||||
|
||||
_, err = b.Write(tableAndAllocated)
|
||||
|
|
|
@ -742,6 +742,94 @@ func Test_IfdByteEncoder_EncodeToExif(t *testing.T) {
|
|||
validateExifSimpleTestIb(exifData, t)
|
||||
}
|
||||
|
||||
func Test_IfdByteEncoder_EncodeToExif_WithChild(t *testing.T) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err := log.Wrap(state.(error))
|
||||
log.PrintErrorf(err, "Test failure.")
|
||||
}
|
||||
}()
|
||||
|
||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
||||
|
||||
err := ib.AddFromConfig(0x000b, "asciivalue")
|
||||
log.PanicIf(err)
|
||||
|
||||
err = ib.AddFromConfig(0x00ff, []uint16 { 0x1122 })
|
||||
log.PanicIf(err)
|
||||
|
||||
|
||||
// Add a child IB right in the middle.
|
||||
|
||||
childIb := NewIfdBuilder(ExifIi, EncodeDefaultByteOrder)
|
||||
|
||||
err = childIb.AddFromConfigWithName("ISOSpeedRatings", []uint16 { 0x1122 })
|
||||
log.PanicIf(err)
|
||||
|
||||
err = childIb.AddFromConfigWithName("ISOSpeed", []uint32 { 0x33445566 })
|
||||
log.PanicIf(err)
|
||||
|
||||
err = ib.AddChildIb(childIb)
|
||||
log.PanicIf(err)
|
||||
|
||||
|
||||
err = ib.AddFromConfig(0x0100, []uint32 { 0x33445566 })
|
||||
log.PanicIf(err)
|
||||
|
||||
err = ib.AddFromConfig(0x013e, []Rational { { Numerator: 0x11112222, Denominator: 0x33334444 } })
|
||||
log.PanicIf(err)
|
||||
|
||||
|
||||
// TODO(dustin): !! Finish this.
|
||||
// // Link to another IB (sibling relationship). The root IFD may occur twice
|
||||
// // in some JPEGs (for thumbnail or FlashPix images).
|
||||
|
||||
// nextIb := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
||||
|
||||
// err = nextIb.AddFromConfig(0x0101, []uint32 { 0x11223344 })
|
||||
// log.PanicIf(err)
|
||||
|
||||
// err = nextIb.AddFromConfig(0x0102, []uint16 { 0x5566 })
|
||||
// log.PanicIf(err)
|
||||
|
||||
// ib.SetNextIb(nextIb)
|
||||
|
||||
|
||||
// Encode.
|
||||
|
||||
ibe := NewIfdByteEncoder()
|
||||
|
||||
exifData, err := ibe.EncodeToExif(ib)
|
||||
log.PanicIf(err)
|
||||
|
||||
|
||||
// Parse.
|
||||
|
||||
e := NewExif()
|
||||
|
||||
_, index, err := e.Collect(exifData)
|
||||
log.PanicIf(err)
|
||||
|
||||
|
||||
// index.RootIfd.PrintTree(true)
|
||||
|
||||
tagsDump := index.RootIfd.DumpTree()
|
||||
|
||||
expected := []string {
|
||||
"[ROOT]->[IFD] (0x0b)",
|
||||
"[ROOT]->[IFD] (0xff)",
|
||||
"[ROOT]->[IFD] (0x8769)",
|
||||
"[IFD]->[Exif] (0x8827)",
|
||||
"[IFD]->[Exif] (0x8833)",
|
||||
"[ROOT]->[IFD] (0x100)",
|
||||
"[ROOT]->[IFD] (0x13e)",
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(tagsDump, expected) != true {
|
||||
t.Fatalf("IFD hierarchy not correct")
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleIfdByteEncoder_EncodeToExif() {
|
||||
// Construct an IFD.
|
||||
|
||||
|
@ -802,4 +890,79 @@ func ExampleIfdByteEncoder_EncodeToExif() {
|
|||
// 5: IfdTagEntry<TAG-IFD=[] TAG-ID=(0x9201) TAG-TYPE=[SRATIONAL] UNIT-COUNT=(1)> [{286335522 858997828}]
|
||||
}
|
||||
|
||||
// func ExampleIfdByteEncoder_EncodeToExif_WithChild() {
|
||||
// // Construct an IFD.
|
||||
|
||||
// ib := NewIfdBuilder(RootIi, EncodeDefaultByteOrder)
|
||||
|
||||
// err := ib.AddFromConfigWithName("ProcessingSoftware", "asciivalue")
|
||||
// log.PanicIf(err)
|
||||
|
||||
// err = ib.AddFromConfigWithName("DotRange", []uint8 { 0x11 })
|
||||
// log.PanicIf(err)
|
||||
|
||||
// err = ib.AddFromConfigWithName("SubfileType", []uint16 { 0x2233 })
|
||||
// log.PanicIf(err)
|
||||
|
||||
|
||||
// // Add a child IB right in the middle.
|
||||
|
||||
// childIb := NewIfdBuilder(ExifIi, EncodeDefaultByteOrder)
|
||||
|
||||
// err = childIb.AddFromConfigWithName("ISOSpeedRatings", []uint16 { 0x1122 })
|
||||
// log.PanicIf(err)
|
||||
|
||||
// err = childIb.AddFromConfigWithName("ISOSpeed", []uint32 { 0x33445566 })
|
||||
// log.PanicIf(err)
|
||||
|
||||
// err = ib.AddChildIb(childIb)
|
||||
// log.PanicIf(err)
|
||||
|
||||
|
||||
// err = ib.AddFromConfigWithName("ImageWidth", []uint32 { 0x44556677 })
|
||||
// log.PanicIf(err)
|
||||
|
||||
// err = ib.AddFromConfigWithName("WhitePoint", []Rational { { Numerator: 0x11112222, Denominator: 0x33334444 } })
|
||||
// log.PanicIf(err)
|
||||
|
||||
// err = ib.AddFromConfigWithName("ShutterSpeedValue", []SignedRational { { Numerator: 0x11112222, Denominator: 0x33334444 } })
|
||||
// log.PanicIf(err)
|
||||
|
||||
|
||||
// // Encode it.
|
||||
|
||||
// ibe := NewIfdByteEncoder()
|
||||
|
||||
// exifData, err := ibe.EncodeToExif(ib)
|
||||
// log.PanicIf(err)
|
||||
|
||||
|
||||
// // Parse it so we can see it.
|
||||
|
||||
// e := NewExif()
|
||||
|
||||
// _, index, err := e.Collect(exifData)
|
||||
// log.PanicIf(err)
|
||||
|
||||
// // addressableData is the byte-slice where the allocated data can be
|
||||
// // resolved (where position 0x0 will correlate with offset 0x0).
|
||||
// addressableData := exifData[ExifAddressableAreaStart:]
|
||||
|
||||
// for i, e := range index.RootIfd.Entries {
|
||||
// value, err := e.Value(EncodeDefaultByteOrder, addressableData)
|
||||
// log.PanicIf(err)
|
||||
|
||||
// fmt.Printf("%d: %s %v\n", i, e, value)
|
||||
// }
|
||||
|
||||
// // Output:
|
||||
// //
|
||||
// // 0: IfdTagEntry<TAG-IFD=[] TAG-ID=(0x0b) TAG-TYPE=[ASCII] UNIT-COUNT=(11)> asciivalue
|
||||
// // 1: IfdTagEntry<TAG-IFD=[] TAG-ID=(0x150) TAG-TYPE=[BYTE] UNIT-COUNT=(1)> [17]
|
||||
// // 2: IfdTagEntry<TAG-IFD=[] TAG-ID=(0xff) TAG-TYPE=[SHORT] UNIT-COUNT=(1)> [8755]
|
||||
// // 3: IfdTagEntry<TAG-IFD=[] TAG-ID=(0x100) TAG-TYPE=[LONG] UNIT-COUNT=(1)> [1146447479]
|
||||
// // 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}]
|
||||
// }
|
||||
|
||||
// TODO(dustin): !! Write test with both chained and child IFDs
|
||||
|
|
|
@ -87,7 +87,7 @@ func TestAdd(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSetNextIfd(t *testing.T) {
|
||||
func TestSetNextIb(t *testing.T) {
|
||||
ib1 := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
||||
ib2 := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
||||
|
||||
|
@ -95,7 +95,7 @@ func TestSetNextIfd(t *testing.T) {
|
|||
t.Fatalf("Next-IFD for IB1 not initially terminal.")
|
||||
}
|
||||
|
||||
err := ib1.SetNextIfd(ib2)
|
||||
err := ib1.SetNextIb(ib2)
|
||||
log.PanicIf(err)
|
||||
|
||||
if ib1.nextIb != ib2 {
|
||||
|
|
|
@ -352,7 +352,7 @@ func (ifd Ifd) Identity() IfdIdentity {
|
|||
return ifd.Ii
|
||||
}
|
||||
|
||||
func (ifd Ifd) printNode(level int, nextLink bool) {
|
||||
func (ifd Ifd) printTree(level int, printTags bool, nextLink bool) {
|
||||
indent := strings.Repeat(" ", level * 2)
|
||||
|
||||
prefix := " "
|
||||
|
@ -360,21 +360,105 @@ func (ifd Ifd) printNode(level int, nextLink bool) {
|
|||
prefix = ">"
|
||||
}
|
||||
|
||||
fmt.Printf("%s%s%s\n", indent, prefix, ifd)
|
||||
fmt.Printf("%s%sIFD: %s\n", indent, prefix, ifd)
|
||||
|
||||
// Quickly create an index of the child-IFDs.
|
||||
|
||||
childIfdIndex := make(map[string]*Ifd)
|
||||
for _, childIfd := range ifd.Children {
|
||||
childIfd.printNode(level + 1, false)
|
||||
childIfdIndex[childIfd.Ii.IfdName] = childIfd
|
||||
}
|
||||
|
||||
// Now, print the tags while also descending to child-IFDS as we encounter them.
|
||||
|
||||
ifdsFoundCount := 0
|
||||
|
||||
ti := NewTagIndex()
|
||||
for _, tag := range ifd.Entries {
|
||||
if printTags == true {
|
||||
if tag.ChildIfdName != "" {
|
||||
fmt.Printf("%s - TAG: %s\n", indent, tag)
|
||||
} else {
|
||||
it, err := ti.Get(ifd.Identity(), tag.TagId)
|
||||
|
||||
tagName := ""
|
||||
if err == nil {
|
||||
tagName = it.Name
|
||||
}
|
||||
|
||||
fmt.Printf("%s - TAG: %s NAME=[%s]\n", indent, tag, tagName)
|
||||
}
|
||||
}
|
||||
|
||||
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.printTree(level + 1, printTags, 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.printNode(level, true)
|
||||
ifd.NextIfd.printTree(level, printTags, true)
|
||||
}
|
||||
}
|
||||
|
||||
func (ifd Ifd) PrintTree() {
|
||||
ifd.printNode(0, false)
|
||||
// PrintTree prints the IFD hierarchy.
|
||||
func (ifd Ifd) PrintTree(printTags bool) {
|
||||
ifd.printTree(0, printTags, false)
|
||||
}
|
||||
|
||||
func (ifd Ifd) dumpTree(tagsDump []string) []string {
|
||||
if tagsDump == nil {
|
||||
tagsDump = make([]string, 0)
|
||||
}
|
||||
|
||||
// Quickly create an index of the child-IFDs.
|
||||
|
||||
childIfdIndex := make(map[string]*Ifd)
|
||||
for _, childIfd := range ifd.Children {
|
||||
childIfdIndex[childIfd.Ii.IfdName] = childIfd
|
||||
}
|
||||
|
||||
ifdsFoundCount := 0
|
||||
for _, tag := range ifd.Entries {
|
||||
if ifd.ParentIfd != nil {
|
||||
tagsDump = append(tagsDump, fmt.Sprintf("[%s]->[%s] (0x%02x)", ifd.ParentIfd.Ii.IfdName, tag.Ii.IfdName, tag.TagId))
|
||||
} else {
|
||||
tagsDump = append(tagsDump, fmt.Sprintf("[ROOT]->[%s] (0x%02x)", tag.Ii.IfdName, tag.TagId))
|
||||
}
|
||||
|
||||
if tag.ChildIfdName != "" {
|
||||
ifdsFoundCount++
|
||||
|
||||
childIfd, found := childIfdIndex[tag.ChildIfdName]
|
||||
if found != true {
|
||||
log.Panicf("alien child IFD referenced by a tag: [%s]", tag.ChildIfdName)
|
||||
}
|
||||
|
||||
tagsDump = childIfd.dumpTree(tagsDump)
|
||||
}
|
||||
}
|
||||
|
||||
if len(ifd.Children) != ifdsFoundCount {
|
||||
log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.Children), ifdsFoundCount)
|
||||
}
|
||||
|
||||
return tagsDump
|
||||
}
|
||||
|
||||
// DumpTree returns a list of strings describing the IFD hierarchy.
|
||||
func (ifd Ifd) DumpTree() []string {
|
||||
return ifd.dumpTree(nil)
|
||||
}
|
||||
|
||||
type QueuedIfd struct {
|
||||
Ii IfdIdentity
|
||||
|
|
Loading…
Reference in New Issue