mirror of https://github.com/dsoprea/go-exif.git
ifd.go: Moved implementations of IfdMapping and LoadStandardIfds to exifcommon.ifd
In lieu of dropping in next release. We needed this in order to write some unit-tests for exifcommon functionality, and this is common functionality anyway (by any definition of common thus far). - common/ifd.go: Added NewIfdIdentityFromString. The above allowed us to cover this with unit-tests. - This was required for go-exif-knife, to get IFD-paths from the command-line.dustin/master
parent
2a1e3f0fa1
commit
1a62daf305
440
v2/common/ifd.go
440
v2/common/ifd.go
|
@ -1,12 +1,407 @@
|
||||||
package exifcommon
|
package exifcommon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/dsoprea/go-logging"
|
"github.com/dsoprea/go-logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ifdLogger = log.NewLogger("exifcommon.ifd")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrChildIfdNotMapped = errors.New("no child-IFD for that tag-ID under parent")
|
||||||
|
)
|
||||||
|
|
||||||
|
// MappedIfd is one node in the IFD-mapping.
|
||||||
|
type MappedIfd struct {
|
||||||
|
ParentTagId uint16
|
||||||
|
Placement []uint16
|
||||||
|
Path []string
|
||||||
|
|
||||||
|
Name string
|
||||||
|
TagId uint16
|
||||||
|
Children map[uint16]*MappedIfd
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a descriptive string.
|
||||||
|
func (mi *MappedIfd) String() string {
|
||||||
|
pathPhrase := mi.PathPhrase()
|
||||||
|
return fmt.Sprintf("MappedIfd<(0x%04X) [%s] PATH=[%s]>", mi.TagId, mi.Name, pathPhrase)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathPhrase returns a non-fully-qualified IFD path.
|
||||||
|
func (mi *MappedIfd) PathPhrase() string {
|
||||||
|
return strings.Join(mi.Path, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(dustin): Refactor this to use IfdIdentity structs.
|
||||||
|
|
||||||
|
// IfdMapping describes all of the IFDs that we currently recognize.
|
||||||
|
type IfdMapping struct {
|
||||||
|
rootNode *MappedIfd
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIfdMapping returns a new IfdMapping struct.
|
||||||
|
func NewIfdMapping() (ifdMapping *IfdMapping) {
|
||||||
|
rootNode := &MappedIfd{
|
||||||
|
Path: make([]string, 0),
|
||||||
|
Children: make(map[uint16]*MappedIfd),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &IfdMapping{
|
||||||
|
rootNode: rootNode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIfdMappingWithStandard retruns a new IfdMapping struct preloaded with the
|
||||||
|
// standard IFDs.
|
||||||
|
func NewIfdMappingWithStandard() (ifdMapping *IfdMapping) {
|
||||||
|
defer func() {
|
||||||
|
if state := recover(); state != nil {
|
||||||
|
err := log.Wrap(state.(error))
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// RELEASE(dustin): Add error return on next release
|
||||||
|
|
||||||
|
im := NewIfdMapping()
|
||||||
|
|
||||||
|
err := LoadStandardIfds(im)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
return im
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the node given the path slice.
|
||||||
|
func (im *IfdMapping) Get(parentPlacement []uint16) (childIfd *MappedIfd, err error) {
|
||||||
|
defer func() {
|
||||||
|
if state := recover(); state != nil {
|
||||||
|
err = log.Wrap(state.(error))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
ptr := im.rootNode
|
||||||
|
for _, tagId := range parentPlacement {
|
||||||
|
if descendantPtr, found := ptr.Children[tagId]; found == false {
|
||||||
|
log.Panicf("ifd child with tag-ID (%04x) not registered: [%s]", tagId, ptr.PathPhrase())
|
||||||
|
} else {
|
||||||
|
ptr = descendantPtr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWithPath returns the node given the path string.
|
||||||
|
func (im *IfdMapping) GetWithPath(pathPhrase string) (mi *MappedIfd, err error) {
|
||||||
|
defer func() {
|
||||||
|
if state := recover(); state != nil {
|
||||||
|
err = log.Wrap(state.(error))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if pathPhrase == "" {
|
||||||
|
log.Panicf("path-phrase is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
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("ifd child with name [%s] not registered: [%s]", name, ptr.PathPhrase())
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = hit
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetChild is a convenience function to get the child path for a given parent
|
||||||
|
// placement and child tag-ID.
|
||||||
|
func (im *IfdMapping) GetChild(parentPathPhrase string, tagId uint16) (mi *MappedIfd, err error) {
|
||||||
|
defer func() {
|
||||||
|
if state := recover(); state != nil {
|
||||||
|
err = log.Wrap(state.(error))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
mi, err = im.GetWithPath(parentPathPhrase)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
for _, childMi := range mi.Children {
|
||||||
|
if childMi.TagId == tagId {
|
||||||
|
return childMi, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether or not an IFD is defined in data, such an IFD is not registered
|
||||||
|
// and would be unknown.
|
||||||
|
log.Panic(ErrChildIfdNotMapped)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IfdTagIdAndIndex represents a specific part of the IFD path.
|
||||||
|
//
|
||||||
|
// This is a legacy type.
|
||||||
|
type IfdTagIdAndIndex struct {
|
||||||
|
Name string
|
||||||
|
TagId uint16
|
||||||
|
Index int
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a descriptive string.
|
||||||
|
func (itii IfdTagIdAndIndex) String() string {
|
||||||
|
return fmt.Sprintf("IfdTagIdAndIndex<NAME=[%s] ID=(%04x) INDEX=(%d)>", itii.Name, itii.TagId, itii.Index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolvePath takes a list of names, which can also be suffixed with indices
|
||||||
|
// (to identify the second, third, etc.. sibling IFD) and returns a list of
|
||||||
|
// tag-IDs and those indices.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// - IFD/Exif/Iop
|
||||||
|
// - IFD0/Exif/Iop
|
||||||
|
//
|
||||||
|
// This is the only call that supports adding the numeric indices.
|
||||||
|
func (im *IfdMapping) ResolvePath(pathPhrase string) (lineage []IfdTagIdAndIndex, err error) {
|
||||||
|
defer func() {
|
||||||
|
if state := recover(); state != nil {
|
||||||
|
err = log.Wrap(state.(error))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
pathPhrase = strings.TrimSpace(pathPhrase)
|
||||||
|
|
||||||
|
if pathPhrase == "" {
|
||||||
|
log.Panicf("can not resolve empty path-phrase")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := strings.Split(pathPhrase, "/")
|
||||||
|
lineage = make([]IfdTagIdAndIndex, len(path))
|
||||||
|
|
||||||
|
ptr := im.rootNode
|
||||||
|
empty := IfdTagIdAndIndex{}
|
||||||
|
for i, name := range path {
|
||||||
|
indexByte := name[len(name)-1]
|
||||||
|
index := 0
|
||||||
|
if indexByte >= '0' && indexByte <= '9' {
|
||||||
|
index = int(indexByte - '0')
|
||||||
|
name = name[:len(name)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
itii := IfdTagIdAndIndex{}
|
||||||
|
for _, mi := range ptr.Children {
|
||||||
|
if mi.Name != name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
itii.Name = name
|
||||||
|
itii.TagId = mi.TagId
|
||||||
|
itii.Index = index
|
||||||
|
|
||||||
|
ptr = mi
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if itii == empty {
|
||||||
|
log.Panicf("ifd child with name [%s] not registered: [%s]", name, pathPhrase)
|
||||||
|
}
|
||||||
|
|
||||||
|
lineage[i] = itii
|
||||||
|
}
|
||||||
|
|
||||||
|
return lineage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FqPathPhraseFromLineage returns the fully-qualified IFD path from the slice.
|
||||||
|
func (im *IfdMapping) FqPathPhraseFromLineage(lineage []IfdTagIdAndIndex) (fqPathPhrase string) {
|
||||||
|
fqPathParts := make([]string, len(lineage))
|
||||||
|
for i, itii := range lineage {
|
||||||
|
if itii.Index > 0 {
|
||||||
|
fqPathParts[i] = fmt.Sprintf("%s%d", itii.Name, itii.Index)
|
||||||
|
} else {
|
||||||
|
fqPathParts[i] = itii.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(fqPathParts, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathPhraseFromLineage returns the non-fully-qualified IFD path from the
|
||||||
|
// slice.
|
||||||
|
func (im *IfdMapping) PathPhraseFromLineage(lineage []IfdTagIdAndIndex) (pathPhrase string) {
|
||||||
|
pathParts := make([]string, len(lineage))
|
||||||
|
for i, itii := range lineage {
|
||||||
|
pathParts[i] = itii.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(pathParts, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// StripPathPhraseIndices returns a non-fully-qualified path-phrase (no
|
||||||
|
// indices).
|
||||||
|
func (im *IfdMapping) StripPathPhraseIndices(pathPhrase string) (strippedPathPhrase string, err error) {
|
||||||
|
defer func() {
|
||||||
|
if state := recover(); state != nil {
|
||||||
|
err = log.Wrap(state.(error))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
lineage, err := im.ResolvePath(pathPhrase)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
strippedPathPhrase = im.PathPhraseFromLineage(lineage)
|
||||||
|
return strippedPathPhrase, 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
|
||||||
|
// progressive descendants.
|
||||||
|
func (im *IfdMapping) Add(parentPlacement []uint16, tagId uint16, name string) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if state := recover(); state != nil {
|
||||||
|
err = log.Wrap(state.(error))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// TODO(dustin): !! It would be nicer to provide a list of names in the placement rather than tag-IDs.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
placement := make([]uint16, len(parentPlacement)+1)
|
||||||
|
if len(placement) > 0 {
|
||||||
|
copy(placement, ptr.Placement)
|
||||||
|
}
|
||||||
|
|
||||||
|
placement[len(placement)-1] = tagId
|
||||||
|
|
||||||
|
childIfd := &MappedIfd{
|
||||||
|
ParentTagId: ptr.TagId,
|
||||||
|
Path: path,
|
||||||
|
Placement: placement,
|
||||||
|
Name: name,
|
||||||
|
TagId: tagId,
|
||||||
|
Children: make(map[uint16]*MappedIfd),
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, found := ptr.Children[tagId]; found == true {
|
||||||
|
log.Panicf("child IFD with tag-ID (%04x) already registered under IFD [%s] with tag-ID (%04x)", tagId, ptr.Name, ptr.TagId)
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr.Children[tagId] = childIfd
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (im *IfdMapping) dumpLineages(stack []*MappedIfd, input []string) (output []string, err error) {
|
||||||
|
defer func() {
|
||||||
|
if state := recover(); state != nil {
|
||||||
|
err = log.Wrap(state.(error))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
currentIfd := stack[len(stack)-1]
|
||||||
|
|
||||||
|
output = input
|
||||||
|
for _, childIfd := range currentIfd.Children {
|
||||||
|
stackCopy := make([]*MappedIfd, len(stack)+1)
|
||||||
|
|
||||||
|
copy(stackCopy, stack)
|
||||||
|
stackCopy[len(stack)] = childIfd
|
||||||
|
|
||||||
|
// Add to output, but don't include the obligatory root node.
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
return output, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DumpLineages returns a slice of strings representing all mappings.
|
||||||
|
func (im *IfdMapping) DumpLineages() (output []string, err error) {
|
||||||
|
defer func() {
|
||||||
|
if state := recover(); state != nil {
|
||||||
|
err = log.Wrap(state.(error))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
stack := []*MappedIfd{im.rootNode}
|
||||||
|
output = make([]string, 0)
|
||||||
|
|
||||||
|
output, err = im.dumpLineages(stack, output)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
return output, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadStandardIfds loads the standard IFDs into the mapping.
|
||||||
|
func LoadStandardIfds(im *IfdMapping) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if state := recover(); state != nil {
|
||||||
|
err = log.Wrap(state.(error))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = im.Add(
|
||||||
|
[]uint16{},
|
||||||
|
IfdStandardIfdIdentity.TagId(), IfdStandardIfdIdentity.Name())
|
||||||
|
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
err = im.Add(
|
||||||
|
[]uint16{IfdStandardIfdIdentity.TagId()},
|
||||||
|
IfdExifStandardIfdIdentity.TagId(), IfdExifStandardIfdIdentity.Name())
|
||||||
|
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
err = im.Add(
|
||||||
|
[]uint16{IfdStandardIfdIdentity.TagId(), IfdExifStandardIfdIdentity.TagId()},
|
||||||
|
IfdExifIopStandardIfdIdentity.TagId(), IfdExifIopStandardIfdIdentity.Name())
|
||||||
|
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
err = im.Add(
|
||||||
|
[]uint16{IfdStandardIfdIdentity.TagId()},
|
||||||
|
IfdGpsInfoStandardIfdIdentity.TagId(), IfdGpsInfoStandardIfdIdentity.Name())
|
||||||
|
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// IfdTag describes a single IFD tag and its parent (if any).
|
// IfdTag describes a single IFD tag and its parent (if any).
|
||||||
type IfdTag struct {
|
type IfdTag struct {
|
||||||
parentIfdTag *IfdTag
|
parentIfdTag *IfdTag
|
||||||
|
@ -105,6 +500,51 @@ func NewIfdIdentity(ifdTag IfdTag, parts ...IfdIdentityPart) (ii *IfdIdentity) {
|
||||||
return ii
|
return ii
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewIfdIdentityFromString parses a string like "IFD/Exif" or "IFD1" or
|
||||||
|
// something more exotic with custom IFDs ("SomeIFD4/SomeChildIFD6"). Note that
|
||||||
|
// this will valid the unindexed IFD structure (because the standard tags from
|
||||||
|
// the specification are unindexed), but not, obviously, any indices (e.g.
|
||||||
|
// the numbers in "IFD0", "IFD1", "SomeIFD4/SomeChildIFD6"). It is
|
||||||
|
// required for the caller to check whether these specific instances
|
||||||
|
// were actually parsed out of the stream.
|
||||||
|
func NewIfdIdentityFromString(im *IfdMapping, fqIfdPath string) (ii *IfdIdentity, err error) {
|
||||||
|
defer func() {
|
||||||
|
if state := recover(); state != nil {
|
||||||
|
err = log.Wrap(state.(error))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
lineage, err := im.ResolvePath(fqIfdPath)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
var lastIt *IfdTag
|
||||||
|
identityParts := make([]IfdIdentityPart, len(lineage))
|
||||||
|
for i, itii := range lineage {
|
||||||
|
// Build out the tag that will eventually point to the IFD represented
|
||||||
|
// by the right-most part in the IFD path.
|
||||||
|
|
||||||
|
it := &IfdTag{
|
||||||
|
parentIfdTag: lastIt,
|
||||||
|
tagId: itii.TagId,
|
||||||
|
name: itii.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
lastIt = it
|
||||||
|
|
||||||
|
// Create the next IfdIdentity part.
|
||||||
|
|
||||||
|
iip := IfdIdentityPart{
|
||||||
|
Name: itii.Name,
|
||||||
|
Index: itii.Index,
|
||||||
|
}
|
||||||
|
|
||||||
|
identityParts[i] = iip
|
||||||
|
}
|
||||||
|
|
||||||
|
ii = NewIfdIdentity(*lastIt, identityParts...)
|
||||||
|
return ii, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ii *IfdIdentity) getFqIfdPath() string {
|
func (ii *IfdIdentity) getFqIfdPath() string {
|
||||||
partPhrases := make([]string, len(ii.parts))
|
partPhrases := make([]string, len(ii.parts))
|
||||||
for i, iip := range ii.parts {
|
for i, iip := range ii.parts {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package exif
|
package exifcommon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -7,8 +7,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/dsoprea/go-logging"
|
"github.com/dsoprea/go-logging"
|
||||||
|
|
||||||
"github.com/dsoprea/go-exif/v2/common"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIfdMapping_Add(t *testing.T) {
|
func TestIfdMapping_Add(t *testing.T) {
|
||||||
|
@ -109,16 +107,16 @@ func TestIfdMapping_Get(t *testing.T) {
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
mi, err := im.Get([]uint16{
|
mi, err := im.Get([]uint16{
|
||||||
exifcommon.IfdStandardIfdIdentity.TagId(),
|
IfdStandardIfdIdentity.TagId(),
|
||||||
exifcommon.IfdExifStandardIfdIdentity.TagId(),
|
IfdExifStandardIfdIdentity.TagId(),
|
||||||
exifcommon.IfdExifIopStandardIfdIdentity.TagId(),
|
IfdExifIopStandardIfdIdentity.TagId(),
|
||||||
})
|
})
|
||||||
|
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
if mi.ParentTagId != exifcommon.IfdExifStandardIfdIdentity.TagId() {
|
if mi.ParentTagId != IfdExifStandardIfdIdentity.TagId() {
|
||||||
t.Fatalf("Parent tag-ID not correct")
|
t.Fatalf("Parent tag-ID not correct")
|
||||||
} else if mi.TagId != exifcommon.IfdExifIopStandardIfdIdentity.TagId() {
|
} else if mi.TagId != IfdExifIopStandardIfdIdentity.TagId() {
|
||||||
t.Fatalf("Tag-ID not correct")
|
t.Fatalf("Tag-ID not correct")
|
||||||
} else if mi.Name != "Iop" {
|
} else if mi.Name != "Iop" {
|
||||||
t.Fatalf("name not correct")
|
t.Fatalf("name not correct")
|
||||||
|
@ -136,9 +134,9 @@ func TestIfdMapping_GetWithPath(t *testing.T) {
|
||||||
mi, err := im.GetWithPath("IFD/Exif/Iop")
|
mi, err := im.GetWithPath("IFD/Exif/Iop")
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
if mi.ParentTagId != exifcommon.IfdExifStandardIfdIdentity.TagId() {
|
if mi.ParentTagId != IfdExifStandardIfdIdentity.TagId() {
|
||||||
t.Fatalf("Parent tag-ID not correct")
|
t.Fatalf("Parent tag-ID not correct")
|
||||||
} else if mi.TagId != exifcommon.IfdExifIopStandardIfdIdentity.TagId() {
|
} else if mi.TagId != IfdExifIopStandardIfdIdentity.TagId() {
|
||||||
t.Fatalf("Tag-ID not correct")
|
t.Fatalf("Tag-ID not correct")
|
||||||
} else if mi.Name != "Iop" {
|
} else if mi.Name != "Iop" {
|
||||||
t.Fatalf("name not correct")
|
t.Fatalf("name not correct")
|
||||||
|
@ -269,3 +267,67 @@ func TestIfdMapping_NewIfdMappingWithStandard(t *testing.T) {
|
||||||
t.Fatalf("Standard IFDs not loaded correctly.")
|
t.Fatalf("Standard IFDs not loaded correctly.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewIfdIdentityFromString_Valid_WithoutIndexes(t *testing.T) {
|
||||||
|
im := NewIfdMapping()
|
||||||
|
|
||||||
|
err := LoadStandardIfds(im)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
fqIfdPath := "IFD/Exif"
|
||||||
|
|
||||||
|
ii, err := NewIfdIdentityFromString(im, fqIfdPath)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
if ii.String() != fqIfdPath {
|
||||||
|
t.Fatalf("'%s' IFD-path was not parsed correctly: [%s]", fqIfdPath, ii.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewIfdIdentityFromString_Valid_WithIndexes(t *testing.T) {
|
||||||
|
im := NewIfdMapping()
|
||||||
|
|
||||||
|
err := LoadStandardIfds(im)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
fqIfdPath := "IFD2/Exif4"
|
||||||
|
|
||||||
|
ii, err := NewIfdIdentityFromString(im, fqIfdPath)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
if ii.String() != fqIfdPath {
|
||||||
|
t.Fatalf("'%s' IFD-path was not parsed correctly: [%s]", fqIfdPath, ii.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewIfdIdentityFromString_Invalid_IfdPathJustRoot(t *testing.T) {
|
||||||
|
im := NewIfdMapping()
|
||||||
|
|
||||||
|
err := LoadStandardIfds(im)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
fqIfdPath := "XYZ"
|
||||||
|
|
||||||
|
_, err = NewIfdIdentityFromString(im, fqIfdPath)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected error from invalid path.")
|
||||||
|
} else if err.Error() != "ifd child with name [XYZ] not registered: [XYZ]" {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewIfdIdentityFromString_Invalid_IfdPathWithSubdirectory(t *testing.T) {
|
||||||
|
im := NewIfdMapping()
|
||||||
|
|
||||||
|
err := LoadStandardIfds(im)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
fqIfdPath := "IFD/XYZ"
|
||||||
|
|
||||||
|
_, err = NewIfdIdentityFromString(im, fqIfdPath)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected error from invalid path.")
|
||||||
|
} else if err.Error() != "ifd child with name [XYZ] not registered: [IFD/XYZ]" {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -192,7 +192,7 @@ func ParseExifHeader(data []byte) (eh ExifHeader, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visit recursively invokes a callback for every tag.
|
// Visit recursively invokes a callback for every tag.
|
||||||
func Visit(rootIfdIdentity *exifcommon.IfdIdentity, ifdMapping *IfdMapping, tagIndex *TagIndex, exifData []byte, visitor TagVisitorFn) (eh ExifHeader, furthestOffset uint32, err error) {
|
func Visit(rootIfdIdentity *exifcommon.IfdIdentity, ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, exifData []byte, visitor TagVisitorFn) (eh ExifHeader, furthestOffset uint32, 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))
|
||||||
|
@ -213,7 +213,7 @@ func Visit(rootIfdIdentity *exifcommon.IfdIdentity, ifdMapping *IfdMapping, tagI
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect recursively builds a static structure of all IFDs and tags.
|
// Collect recursively builds a static structure of all IFDs and tags.
|
||||||
func Collect(ifdMapping *IfdMapping, tagIndex *TagIndex, exifData []byte) (eh ExifHeader, index IfdIndex, err error) {
|
func Collect(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, exifData []byte) (eh ExifHeader, index IfdIndex, 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))
|
||||||
|
|
387
v2/ifd.go
387
v2/ifd.go
|
@ -1,402 +1,39 @@
|
||||||
package exif
|
package exif
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/dsoprea/go-logging"
|
"github.com/dsoprea/go-logging"
|
||||||
|
|
||||||
"github.com/dsoprea/go-exif/v2/common"
|
"github.com/dsoprea/go-exif/v2/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// TODO(dustin): This file now exists for backwards-compatibility only.
|
||||||
ifdLogger = log.NewLogger("exif.ifd")
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrChildIfdNotMapped = errors.New("no child-IFD for that tag-ID under parent")
|
|
||||||
)
|
|
||||||
|
|
||||||
// MappedIfd is one node in the IFD-mapping.
|
|
||||||
type MappedIfd struct {
|
|
||||||
ParentTagId uint16
|
|
||||||
Placement []uint16
|
|
||||||
Path []string
|
|
||||||
|
|
||||||
Name string
|
|
||||||
TagId uint16
|
|
||||||
Children map[uint16]*MappedIfd
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a descriptive string.
|
|
||||||
func (mi *MappedIfd) String() string {
|
|
||||||
pathPhrase := mi.PathPhrase()
|
|
||||||
return fmt.Sprintf("MappedIfd<(0x%04X) [%s] PATH=[%s]>", mi.TagId, mi.Name, pathPhrase)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PathPhrase returns a non-fully-qualified IFD path.
|
|
||||||
func (mi *MappedIfd) PathPhrase() string {
|
|
||||||
return strings.Join(mi.Path, "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(dustin): Refactor this to use IfdIdentity structs.
|
|
||||||
|
|
||||||
// IfdMapping describes all of the IFDs that we currently recognize.
|
|
||||||
type IfdMapping struct {
|
|
||||||
rootNode *MappedIfd
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewIfdMapping returns a new IfdMapping struct.
|
// NewIfdMapping returns a new IfdMapping struct.
|
||||||
func NewIfdMapping() (ifdMapping *IfdMapping) {
|
//
|
||||||
rootNode := &MappedIfd{
|
// RELEASE(dustin): This is a bridging function for backwards-compatibility. Remove this in the next release.
|
||||||
Path: make([]string, 0),
|
func NewIfdMapping() (ifdMapping *exifcommon.IfdMapping) {
|
||||||
Children: make(map[uint16]*MappedIfd),
|
return exifcommon.NewIfdMapping()
|
||||||
}
|
|
||||||
|
|
||||||
return &IfdMapping{
|
|
||||||
rootNode: rootNode,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIfdMappingWithStandard retruns a new IfdMapping struct preloaded with the
|
// NewIfdMappingWithStandard retruns a new IfdMapping struct preloaded with the
|
||||||
// standard IFDs.
|
// standard IFDs.
|
||||||
func NewIfdMappingWithStandard() (ifdMapping *IfdMapping) {
|
|
||||||
defer func() {
|
|
||||||
if state := recover(); state != nil {
|
|
||||||
err := log.Wrap(state.(error))
|
|
||||||
log.Panic(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
im := NewIfdMapping()
|
|
||||||
|
|
||||||
err := LoadStandardIfds(im)
|
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
return im
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns the node given the path slice.
|
|
||||||
func (im *IfdMapping) Get(parentPlacement []uint16) (childIfd *MappedIfd, err error) {
|
|
||||||
defer func() {
|
|
||||||
if state := recover(); state != nil {
|
|
||||||
err = log.Wrap(state.(error))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
ptr := im.rootNode
|
|
||||||
for _, tagId := range parentPlacement {
|
|
||||||
if descendantPtr, found := ptr.Children[tagId]; found == false {
|
|
||||||
log.Panicf("ifd child with tag-ID (%04x) not registered: [%s]", tagId, ptr.PathPhrase())
|
|
||||||
} else {
|
|
||||||
ptr = descendantPtr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ptr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetWithPath returns the node given the path string.
|
|
||||||
func (im *IfdMapping) GetWithPath(pathPhrase string) (mi *MappedIfd, err error) {
|
|
||||||
defer func() {
|
|
||||||
if state := recover(); state != nil {
|
|
||||||
err = log.Wrap(state.(error))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if pathPhrase == "" {
|
|
||||||
log.Panicf("path-phrase is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
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("ifd child with name [%s] not registered: [%s]", name, ptr.PathPhrase())
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr = hit
|
|
||||||
}
|
|
||||||
|
|
||||||
return ptr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetChild is a convenience function to get the child path for a given parent
|
|
||||||
// placement and child tag-ID.
|
|
||||||
func (im *IfdMapping) GetChild(parentPathPhrase string, tagId uint16) (mi *MappedIfd, err error) {
|
|
||||||
defer func() {
|
|
||||||
if state := recover(); state != nil {
|
|
||||||
err = log.Wrap(state.(error))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
mi, err = im.GetWithPath(parentPathPhrase)
|
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
for _, childMi := range mi.Children {
|
|
||||||
if childMi.TagId == tagId {
|
|
||||||
return childMi, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Whether or not an IFD is defined in data, such an IFD is not registered
|
|
||||||
// and would be unknown.
|
|
||||||
log.Panic(ErrChildIfdNotMapped)
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IfdTagIdAndIndex represents a specific part of the IFD path.
|
|
||||||
//
|
//
|
||||||
// This is a legacy type.
|
// RELEASE(dustin): This is a bridging function for backwards-compatibility. Remove this in the next release.
|
||||||
type IfdTagIdAndIndex struct {
|
func NewIfdMappingWithStandard() (ifdMapping *exifcommon.IfdMapping) {
|
||||||
Name string
|
return exifcommon.NewIfdMappingWithStandard()
|
||||||
TagId uint16
|
|
||||||
Index int
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a descriptive string.
|
|
||||||
func (itii IfdTagIdAndIndex) String() string {
|
|
||||||
return fmt.Sprintf("IfdTagIdAndIndex<NAME=[%s] ID=(%04x) INDEX=(%d)>", itii.Name, itii.TagId, itii.Index)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResolvePath takes a list of names, which can also be suffixed with indices
|
|
||||||
// (to identify the second, third, etc.. sibling IFD) and returns a list of
|
|
||||||
// tag-IDs and those indices.
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
//
|
|
||||||
// - IFD/Exif/Iop
|
|
||||||
// - IFD0/Exif/Iop
|
|
||||||
//
|
|
||||||
// This is the only call that supports adding the numeric indices.
|
|
||||||
func (im *IfdMapping) ResolvePath(pathPhrase string) (lineage []IfdTagIdAndIndex, err error) {
|
|
||||||
defer func() {
|
|
||||||
if state := recover(); state != nil {
|
|
||||||
err = log.Wrap(state.(error))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
pathPhrase = strings.TrimSpace(pathPhrase)
|
|
||||||
|
|
||||||
if pathPhrase == "" {
|
|
||||||
log.Panicf("can not resolve empty path-phrase")
|
|
||||||
}
|
|
||||||
|
|
||||||
path := strings.Split(pathPhrase, "/")
|
|
||||||
lineage = make([]IfdTagIdAndIndex, len(path))
|
|
||||||
|
|
||||||
ptr := im.rootNode
|
|
||||||
empty := IfdTagIdAndIndex{}
|
|
||||||
for i, name := range path {
|
|
||||||
indexByte := name[len(name)-1]
|
|
||||||
index := 0
|
|
||||||
if indexByte >= '0' && indexByte <= '9' {
|
|
||||||
index = int(indexByte - '0')
|
|
||||||
name = name[:len(name)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
itii := IfdTagIdAndIndex{}
|
|
||||||
for _, mi := range ptr.Children {
|
|
||||||
if mi.Name != name {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
itii.Name = name
|
|
||||||
itii.TagId = mi.TagId
|
|
||||||
itii.Index = index
|
|
||||||
|
|
||||||
ptr = mi
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if itii == empty {
|
|
||||||
log.Panicf("ifd child with name [%s] not registered: [%s]", name, pathPhrase)
|
|
||||||
}
|
|
||||||
|
|
||||||
lineage[i] = itii
|
|
||||||
}
|
|
||||||
|
|
||||||
return lineage, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FqPathPhraseFromLineage returns the fully-qualified IFD path from the slice.
|
|
||||||
func (im *IfdMapping) FqPathPhraseFromLineage(lineage []IfdTagIdAndIndex) (fqPathPhrase string) {
|
|
||||||
fqPathParts := make([]string, len(lineage))
|
|
||||||
for i, itii := range lineage {
|
|
||||||
if itii.Index > 0 {
|
|
||||||
fqPathParts[i] = fmt.Sprintf("%s%d", itii.Name, itii.Index)
|
|
||||||
} else {
|
|
||||||
fqPathParts[i] = itii.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(fqPathParts, "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
// PathPhraseFromLineage returns the non-fully-qualified IFD path from the
|
|
||||||
// slice.
|
|
||||||
func (im *IfdMapping) PathPhraseFromLineage(lineage []IfdTagIdAndIndex) (pathPhrase string) {
|
|
||||||
pathParts := make([]string, len(lineage))
|
|
||||||
for i, itii := range lineage {
|
|
||||||
pathParts[i] = itii.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(pathParts, "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
// StripPathPhraseIndices returns a non-fully-qualified path-phrase (no
|
|
||||||
// indices).
|
|
||||||
func (im *IfdMapping) StripPathPhraseIndices(pathPhrase string) (strippedPathPhrase string, err error) {
|
|
||||||
defer func() {
|
|
||||||
if state := recover(); state != nil {
|
|
||||||
err = log.Wrap(state.(error))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
lineage, err := im.ResolvePath(pathPhrase)
|
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
strippedPathPhrase = im.PathPhraseFromLineage(lineage)
|
|
||||||
return strippedPathPhrase, 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
|
|
||||||
// progressive descendants.
|
|
||||||
func (im *IfdMapping) Add(parentPlacement []uint16, tagId uint16, name string) (err error) {
|
|
||||||
defer func() {
|
|
||||||
if state := recover(); state != nil {
|
|
||||||
err = log.Wrap(state.(error))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// TODO(dustin): !! It would be nicer to provide a list of names in the placement rather than tag-IDs.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
placement := make([]uint16, len(parentPlacement)+1)
|
|
||||||
if len(placement) > 0 {
|
|
||||||
copy(placement, ptr.Placement)
|
|
||||||
}
|
|
||||||
|
|
||||||
placement[len(placement)-1] = tagId
|
|
||||||
|
|
||||||
childIfd := &MappedIfd{
|
|
||||||
ParentTagId: ptr.TagId,
|
|
||||||
Path: path,
|
|
||||||
Placement: placement,
|
|
||||||
Name: name,
|
|
||||||
TagId: tagId,
|
|
||||||
Children: make(map[uint16]*MappedIfd),
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, found := ptr.Children[tagId]; found == true {
|
|
||||||
log.Panicf("child IFD with tag-ID (%04x) already registered under IFD [%s] with tag-ID (%04x)", tagId, ptr.Name, ptr.TagId)
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr.Children[tagId] = childIfd
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (im *IfdMapping) dumpLineages(stack []*MappedIfd, input []string) (output []string, err error) {
|
|
||||||
defer func() {
|
|
||||||
if state := recover(); state != nil {
|
|
||||||
err = log.Wrap(state.(error))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
currentIfd := stack[len(stack)-1]
|
|
||||||
|
|
||||||
output = input
|
|
||||||
for _, childIfd := range currentIfd.Children {
|
|
||||||
stackCopy := make([]*MappedIfd, len(stack)+1)
|
|
||||||
|
|
||||||
copy(stackCopy, stack)
|
|
||||||
stackCopy[len(stack)] = childIfd
|
|
||||||
|
|
||||||
// Add to output, but don't include the obligatory root node.
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
return output, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DumpLineages returns a slice of strings representing all mappings.
|
|
||||||
func (im *IfdMapping) DumpLineages() (output []string, err error) {
|
|
||||||
defer func() {
|
|
||||||
if state := recover(); state != nil {
|
|
||||||
err = log.Wrap(state.(error))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
stack := []*MappedIfd{im.rootNode}
|
|
||||||
output = make([]string, 0)
|
|
||||||
|
|
||||||
output, err = im.dumpLineages(stack, output)
|
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
return output, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadStandardIfds loads the standard IFDs into the mapping.
|
// LoadStandardIfds loads the standard IFDs into the mapping.
|
||||||
func LoadStandardIfds(im *IfdMapping) (err error) {
|
//
|
||||||
|
// RELEASE(dustin): This is a bridging function for backwards-compatibility. Remove this in the next release.
|
||||||
|
func LoadStandardIfds(im *exifcommon.IfdMapping) (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))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err = im.Add(
|
err = exifcommon.LoadStandardIfds(im)
|
||||||
[]uint16{},
|
|
||||||
exifcommon.IfdStandardIfdIdentity.TagId(), exifcommon.IfdStandardIfdIdentity.Name())
|
|
||||||
|
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
err = im.Add(
|
|
||||||
[]uint16{exifcommon.IfdStandardIfdIdentity.TagId()},
|
|
||||||
exifcommon.IfdExifStandardIfdIdentity.TagId(), exifcommon.IfdExifStandardIfdIdentity.Name())
|
|
||||||
|
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
err = im.Add(
|
|
||||||
[]uint16{exifcommon.IfdStandardIfdIdentity.TagId(), exifcommon.IfdExifStandardIfdIdentity.TagId()},
|
|
||||||
exifcommon.IfdExifIopStandardIfdIdentity.TagId(), exifcommon.IfdExifIopStandardIfdIdentity.Name())
|
|
||||||
|
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
err = im.Add(
|
|
||||||
[]uint16{exifcommon.IfdStandardIfdIdentity.TagId()},
|
|
||||||
exifcommon.IfdGpsInfoStandardIfdIdentity.TagId(), exifcommon.IfdGpsInfoStandardIfdIdentity.Name())
|
|
||||||
|
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -238,11 +238,11 @@ type IfdBuilder struct {
|
||||||
// data. Otherwise, it's nil.
|
// data. Otherwise, it's nil.
|
||||||
thumbnailData []byte
|
thumbnailData []byte
|
||||||
|
|
||||||
ifdMapping *IfdMapping
|
ifdMapping *exifcommon.IfdMapping
|
||||||
tagIndex *TagIndex
|
tagIndex *TagIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIfdBuilder(ifdMapping *IfdMapping, tagIndex *TagIndex, ii *exifcommon.IfdIdentity, byteOrder binary.ByteOrder) (ib *IfdBuilder) {
|
func NewIfdBuilder(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exifcommon.IfdIdentity, byteOrder binary.ByteOrder) (ib *IfdBuilder) {
|
||||||
ib = &IfdBuilder{
|
ib = &IfdBuilder{
|
||||||
ifdIdentity: ii,
|
ifdIdentity: ii,
|
||||||
|
|
||||||
|
@ -332,7 +332,7 @@ func (ib *IfdBuilder) ChildWithTagId(childIfdTagId uint16) (childIb *IfdBuilder,
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOrCreateIbFromRootIbInner(rootIb *IfdBuilder, parentIb *IfdBuilder, currentLineage []IfdTagIdAndIndex) (ib *IfdBuilder, err error) {
|
func getOrCreateIbFromRootIbInner(rootIb *IfdBuilder, parentIb *IfdBuilder, currentLineage []exifcommon.IfdTagIdAndIndex) (ib *IfdBuilder, 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))
|
||||||
|
@ -346,7 +346,7 @@ func getOrCreateIbFromRootIbInner(rootIb *IfdBuilder, parentIb *IfdBuilder, curr
|
||||||
// Since we're calling ourselves recursively with incrementally different
|
// Since we're calling ourselves recursively with incrementally different
|
||||||
// paths, the FQ IFD-path of the parent that called us needs to be passed
|
// paths, the FQ IFD-path of the parent that called us needs to be passed
|
||||||
// in, in order for us to know it.
|
// in, in order for us to know it.
|
||||||
var parentLineage []IfdTagIdAndIndex
|
var parentLineage []exifcommon.IfdTagIdAndIndex
|
||||||
if parentIb != nil {
|
if parentIb != nil {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
@ -555,7 +555,7 @@ func (ib *IfdBuilder) printTagTree(levels int) {
|
||||||
_, err := ib.ifdMapping.GetChild(currentIb.IfdIdentity().UnindexedString(), tag.tagId)
|
_, err := ib.ifdMapping.GetChild(currentIb.IfdIdentity().UnindexedString(), tag.tagId)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
isChildIb = true
|
isChildIb = true
|
||||||
} else if log.Is(err, ErrChildIfdNotMapped) == false {
|
} else if log.Is(err, exifcommon.ErrChildIfdNotMapped) == false {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -624,7 +624,7 @@ func (ib *IfdBuilder) printIfdTree(levels int) {
|
||||||
_, err := ib.ifdMapping.GetChild(currentIb.IfdIdentity().UnindexedString(), tag.tagId)
|
_, err := ib.ifdMapping.GetChild(currentIb.IfdIdentity().UnindexedString(), tag.tagId)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
isChildIb = true
|
isChildIb = true
|
||||||
} else if log.Is(err, ErrChildIfdNotMapped) == false {
|
} else if log.Is(err, exifcommon.ErrChildIfdNotMapped) == false {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -171,12 +171,12 @@ type IfdEnumerate struct {
|
||||||
buffer *bytes.Buffer
|
buffer *bytes.Buffer
|
||||||
byteOrder binary.ByteOrder
|
byteOrder binary.ByteOrder
|
||||||
tagIndex *TagIndex
|
tagIndex *TagIndex
|
||||||
ifdMapping *IfdMapping
|
ifdMapping *exifcommon.IfdMapping
|
||||||
furthestOffset uint32
|
furthestOffset uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIfdEnumerate returns a new instance of IfdEnumerate.
|
// NewIfdEnumerate returns a new instance of IfdEnumerate.
|
||||||
func NewIfdEnumerate(ifdMapping *IfdMapping, tagIndex *TagIndex, exifData []byte, byteOrder binary.ByteOrder) *IfdEnumerate {
|
func NewIfdEnumerate(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, exifData []byte, byteOrder binary.ByteOrder) *IfdEnumerate {
|
||||||
return &IfdEnumerate{
|
return &IfdEnumerate{
|
||||||
exifData: exifData,
|
exifData: exifData,
|
||||||
buffer: bytes.NewBuffer(exifData),
|
buffer: bytes.NewBuffer(exifData),
|
||||||
|
@ -266,7 +266,7 @@ func (ie *IfdEnumerate) parseTag(ii *exifcommon.IfdIdentity, tagPosition int, bp
|
||||||
|
|
||||||
// We also need to set `tag.ChildFqIfdPath` but can't do it here
|
// We also need to set `tag.ChildFqIfdPath` but can't do it here
|
||||||
// because we don't have the IFD index.
|
// because we don't have the IFD index.
|
||||||
} else if log.Is(err, ErrChildIfdNotMapped) == false {
|
} else if log.Is(err, exifcommon.ErrChildIfdNotMapped) == false {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -656,7 +656,7 @@ type Ifd struct {
|
||||||
|
|
||||||
thumbnailData []byte
|
thumbnailData []byte
|
||||||
|
|
||||||
ifdMapping *IfdMapping
|
ifdMapping *exifcommon.IfdMapping
|
||||||
tagIndex *TagIndex
|
tagIndex *TagIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1387,7 +1387,7 @@ func (ie *IfdEnumerate) FurthestOffset() uint32 {
|
||||||
// in that the numeric index will always be zero (the zeroth child) rather than
|
// in that the numeric index will always be zero (the zeroth child) rather than
|
||||||
// the proper number (if its actually a sibling to the first child, for
|
// the proper number (if its actually a sibling to the first child, for
|
||||||
// instance).
|
// instance).
|
||||||
func ParseOneIfd(ifdMapping *IfdMapping, tagIndex *TagIndex, ii *exifcommon.IfdIdentity, byteOrder binary.ByteOrder, ifdBlock []byte, visitor TagVisitorFn) (nextIfdOffset uint32, entries []*IfdTagEntry, err error) {
|
func ParseOneIfd(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exifcommon.IfdIdentity, byteOrder binary.ByteOrder, ifdBlock []byte, visitor TagVisitorFn) (nextIfdOffset uint32, entries []*IfdTagEntry, 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))
|
||||||
|
@ -1412,7 +1412,7 @@ func ParseOneIfd(ifdMapping *IfdMapping, tagIndex *TagIndex, ii *exifcommon.IfdI
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseOneTag is a hack to use an IE to parse a raw tag block.
|
// ParseOneTag is a hack to use an IE to parse a raw tag block.
|
||||||
func ParseOneTag(ifdMapping *IfdMapping, tagIndex *TagIndex, ii *exifcommon.IfdIdentity, byteOrder binary.ByteOrder, tagBlock []byte) (ite *IfdTagEntry, err error) {
|
func ParseOneTag(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exifcommon.IfdIdentity, byteOrder binary.ByteOrder, tagBlock []byte) (ite *IfdTagEntry, 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))
|
||||||
|
|
Loading…
Reference in New Issue