mirror of https://github.com/dsoprea/go-exif.git
ifd: Imp'd notion of IFD paths.
parent
840d133d71
commit
15cf52b558
68
ifd.go
68
ifd.go
|
@ -1,8 +1,8 @@
|
|||
package exif
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
)
|
||||
|
@ -25,10 +25,6 @@ const (
|
|||
IfdRootId = 0x0000
|
||||
)
|
||||
|
||||
var (
|
||||
ErrIfdNotFound = errors.New("IFD not found")
|
||||
)
|
||||
|
||||
type IfdNameAndIndex struct {
|
||||
Ii IfdIdentity
|
||||
Index int
|
||||
|
@ -126,6 +122,7 @@ func (ii IfdIdentity) Id() int {
|
|||
|
||||
type MappedIfd struct {
|
||||
ParentTagId uint16
|
||||
Path []string
|
||||
|
||||
Name string
|
||||
TagId uint16
|
||||
|
@ -133,15 +130,22 @@ type MappedIfd struct {
|
|||
}
|
||||
|
||||
func (mi *MappedIfd) String() string {
|
||||
return fmt.Sprintf("MappedIfd<(0x%04X) [%s]>", mi.TagId, mi.Name)
|
||||
pathPhrase := mi.PathPhrase()
|
||||
return fmt.Sprintf("MappedIfd<(0x%04X) [%s] PATH=[%s]>", mi.TagId, mi.Name, pathPhrase)
|
||||
}
|
||||
|
||||
func (mi *MappedIfd) PathPhrase() string {
|
||||
return strings.Join(mi.Path, "/")
|
||||
}
|
||||
|
||||
// IfdMapping describes all of the IFDs that we currently recognize.
|
||||
type IfdMapping struct {
|
||||
rootNode *MappedIfd
|
||||
}
|
||||
|
||||
func NewIfdMapping() (ifdMapping *IfdMapping) {
|
||||
rootNode := &MappedIfd{
|
||||
Path: make([]string, 0),
|
||||
Children: make(map[uint16]*MappedIfd),
|
||||
}
|
||||
|
||||
|
@ -160,7 +164,7 @@ func (im *IfdMapping) Get(parentPlacement []uint16) (childIfd *MappedIfd, err er
|
|||
ptr := im.rootNode
|
||||
for _, tagId := range parentPlacement {
|
||||
if descendantPtr, found := ptr.Children[tagId]; found == false {
|
||||
log.Panic(ErrIfdNotFound)
|
||||
log.Panicf(fmt.Sprintf("ifd child with tag-ID (%04x) not registered", tagId))
|
||||
} else {
|
||||
ptr = descendantPtr
|
||||
}
|
||||
|
@ -169,6 +173,35 @@ func (im *IfdMapping) Get(parentPlacement []uint16) (childIfd *MappedIfd, err er
|
|||
return ptr, nil
|
||||
}
|
||||
|
||||
func (im *IfdMapping) GetWithPath(pathPhrase string) (mi *MappedIfd, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
path := strings.Split(pathPhrase, "/")
|
||||
ptr := im.rootNode
|
||||
|
||||
for _, name := range path {
|
||||
var hit *MappedIfd
|
||||
for _, mi := range ptr.Children {
|
||||
if mi.Name == name {
|
||||
hit = mi
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if hit == nil {
|
||||
log.Panicf(fmt.Sprintf("ifd child with name [%s] not registered", name))
|
||||
}
|
||||
|
||||
ptr = hit
|
||||
}
|
||||
|
||||
return ptr, nil
|
||||
}
|
||||
|
||||
// Add puts the given IFD at the given position of the tree. The position of the
|
||||
// tree is referred to as the placement and is represented by a set of tag-IDs,
|
||||
// where the leftmost is the root tag and the tags going to the right are
|
||||
|
@ -183,8 +216,16 @@ func (im *IfdMapping) Add(parentPlacement []uint16, tagId uint16, name string) (
|
|||
ptr, err := im.Get(parentPlacement)
|
||||
log.PanicIf(err)
|
||||
|
||||
path := make([]string, len(parentPlacement)+1)
|
||||
if len(parentPlacement) > 0 {
|
||||
copy(path, ptr.Path)
|
||||
}
|
||||
|
||||
path[len(path)-1] = name
|
||||
|
||||
childIfd := &MappedIfd{
|
||||
ParentTagId: ptr.TagId,
|
||||
Path: path,
|
||||
Name: name,
|
||||
TagId: tagId,
|
||||
Children: make(map[uint16]*MappedIfd),
|
||||
|
@ -199,7 +240,7 @@ func (im *IfdMapping) Add(parentPlacement []uint16, tagId uint16, name string) (
|
|||
return nil
|
||||
}
|
||||
|
||||
func (im *IfdMapping) dumpLineages(stack []*MappedIfd, input [][]*MappedIfd) (output [][]*MappedIfd, err error) {
|
||||
func (im *IfdMapping) dumpLineages(stack []*MappedIfd, input []string) (output []string, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
|
@ -216,7 +257,12 @@ func (im *IfdMapping) dumpLineages(stack []*MappedIfd, input [][]*MappedIfd) (ou
|
|||
stackCopy[len(stack)] = childIfd
|
||||
|
||||
// Add to output, but don't include the obligatory root node.
|
||||
output = append(output, stackCopy[1:])
|
||||
parts := make([]string, len(stackCopy)-1)
|
||||
for i, mi := range stackCopy[1:] {
|
||||
parts[i] = mi.Name
|
||||
}
|
||||
|
||||
output = append(output, strings.Join(parts, "/"))
|
||||
|
||||
output, err = im.dumpLineages(stackCopy, output)
|
||||
log.PanicIf(err)
|
||||
|
@ -225,15 +271,15 @@ func (im *IfdMapping) dumpLineages(stack []*MappedIfd, input [][]*MappedIfd) (ou
|
|||
return output, nil
|
||||
}
|
||||
|
||||
func (im *IfdMapping) DumpLineages() (output [][]*MappedIfd, err error) {
|
||||
func (im *IfdMapping) DumpLineages() (output []string, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
output = make([][]*MappedIfd, 0)
|
||||
stack := []*MappedIfd{im.rootNode}
|
||||
output = make([]string, 0)
|
||||
|
||||
output, err = im.dumpLineages(stack, output)
|
||||
log.PanicIf(err)
|
||||
|
|
73
ifd_test.go
73
ifd_test.go
|
@ -4,7 +4,6 @@ import (
|
|||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
|
@ -29,31 +28,23 @@ func TestIfdMapping_Add(t *testing.T) {
|
|||
log.PanicIf(err)
|
||||
|
||||
lineages, err := im.DumpLineages()
|
||||
lines := make([]string, len(lineages))
|
||||
for i, lineage := range lineages {
|
||||
descriptions := make([]string, len(lineage))
|
||||
for i, mi := range lineage {
|
||||
descriptions[i] = fmt.Sprintf("(0x%04x) [%s]", mi.TagId, mi.Name)
|
||||
}
|
||||
log.PanicIf(err)
|
||||
|
||||
lines[i] = strings.Join(descriptions, ", ")
|
||||
}
|
||||
|
||||
sort.Strings(lines)
|
||||
sort.Strings(lineages)
|
||||
|
||||
expected := []string{
|
||||
"(0x1111) [ifd0]",
|
||||
"(0x1111) [ifd0], (0x4444) [ifd00]",
|
||||
"(0x1111) [ifd0], (0x4444) [ifd00], (0x5555) [ifd000]",
|
||||
"(0x2222) [ifd1]",
|
||||
"(0x3333) [ifd2]",
|
||||
"ifd0",
|
||||
"ifd0/ifd00",
|
||||
"ifd0/ifd00/ifd000",
|
||||
"ifd1",
|
||||
"ifd2",
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(lines, expected) != true {
|
||||
if reflect.DeepEqual(lineages, expected) != true {
|
||||
fmt.Printf("Actual:\n")
|
||||
fmt.Printf("\n")
|
||||
|
||||
for i, line := range lines {
|
||||
for i, line := range lineages {
|
||||
fmt.Printf("(%d) %s\n", i, line)
|
||||
}
|
||||
|
||||
|
@ -77,30 +68,22 @@ func TestIfdMapping_LoadStandardIfds(t *testing.T) {
|
|||
log.PanicIf(err)
|
||||
|
||||
lineages, err := im.DumpLineages()
|
||||
lines := make([]string, len(lineages))
|
||||
for i, lineage := range lineages {
|
||||
descriptions := make([]string, len(lineage))
|
||||
for i, mi := range lineage {
|
||||
descriptions[i] = fmt.Sprintf("(0x%04x) [%s]", mi.TagId, mi.Name)
|
||||
}
|
||||
log.PanicIf(err)
|
||||
|
||||
lines[i] = strings.Join(descriptions, ", ")
|
||||
}
|
||||
|
||||
sort.Strings(lines)
|
||||
sort.Strings(lineages)
|
||||
|
||||
expected := []string{
|
||||
"(0x0000) [IFD]",
|
||||
"(0x0000) [IFD], (0x8769) [Exif]",
|
||||
"(0x0000) [IFD], (0x8769) [Exif], (0xa005) [Iop]",
|
||||
"(0x0000) [IFD], (0x8825) [GPSInfo]",
|
||||
"IFD",
|
||||
"IFD/Exif",
|
||||
"IFD/Exif/Iop",
|
||||
"IFD/GPSInfo",
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(lines, expected) != true {
|
||||
if reflect.DeepEqual(lineages, expected) != true {
|
||||
fmt.Printf("Actual:\n")
|
||||
fmt.Printf("\n")
|
||||
|
||||
for i, line := range lines {
|
||||
for i, line := range lineages {
|
||||
fmt.Printf("(%d) %s\n", i, line)
|
||||
}
|
||||
|
||||
|
@ -132,5 +115,27 @@ func TestIfdMapping_Get(t *testing.T) {
|
|||
t.Fatalf("Tag-ID not correct")
|
||||
} else if mi.Name != IfdIop {
|
||||
t.Fatalf("name not correct")
|
||||
} else if mi.PathPhrase() != "IFD/Exif/Iop" {
|
||||
t.Fatalf("path not correct")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIfdMapping_GetWithPath(t *testing.T) {
|
||||
im := NewIfdMapping()
|
||||
|
||||
err := LoadStandardIfds(im)
|
||||
log.PanicIf(err)
|
||||
|
||||
mi, err := im.GetWithPath("IFD/Exif/Iop")
|
||||
log.PanicIf(err)
|
||||
|
||||
if mi.ParentTagId != IfdExifId {
|
||||
t.Fatalf("Parent tag-ID not correct")
|
||||
} else if mi.TagId != IfdIopId {
|
||||
t.Fatalf("Tag-ID not correct")
|
||||
} else if mi.Name != IfdIop {
|
||||
t.Fatalf("name not correct")
|
||||
} else if mi.PathPhrase() != "IFD/Exif/Iop" {
|
||||
t.Fatalf("path not correct")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue