mirror of https://github.com/dsoprea/go-exif.git
224 lines
6.2 KiB
Go
224 lines
6.2 KiB
Go
package exifcommon
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/dsoprea/go-logging"
|
|
)
|
|
|
|
// IfdTag describes a single IFD tag and its parent (if any).
|
|
type IfdTag struct {
|
|
parentIfdTag *IfdTag
|
|
tagId uint16
|
|
name string
|
|
}
|
|
|
|
func NewIfdTag(parentIfdTag *IfdTag, tagId uint16, name string) IfdTag {
|
|
return IfdTag{
|
|
parentIfdTag: parentIfdTag,
|
|
tagId: tagId,
|
|
name: name,
|
|
}
|
|
}
|
|
|
|
// ParentIfd returns the IfdTag of this IFD's parent.
|
|
func (it IfdTag) ParentIfd() *IfdTag {
|
|
return it.parentIfdTag
|
|
}
|
|
|
|
// TagId returns the tag-ID of this IFD.
|
|
func (it IfdTag) TagId() uint16 {
|
|
return it.tagId
|
|
}
|
|
|
|
// Name returns the simple name of this IFD.
|
|
func (it IfdTag) Name() string {
|
|
return it.name
|
|
}
|
|
|
|
// String returns a descriptive string.
|
|
func (it IfdTag) String() string {
|
|
parentIfdPhrase := ""
|
|
if it.parentIfdTag != nil {
|
|
parentIfdPhrase = fmt.Sprintf(" PARENT=(0x%04x)[%s]", it.parentIfdTag.tagId, it.parentIfdTag.name)
|
|
}
|
|
|
|
return fmt.Sprintf("IfdTag<TAG-ID=(0x%04x) NAME=[%s]%s>", it.tagId, it.name, parentIfdPhrase)
|
|
}
|
|
|
|
var (
|
|
// rootStandardIfd is the standard root IFD.
|
|
rootStandardIfd = NewIfdTag(nil, 0x0000, "IFD") // IFD
|
|
|
|
// exifStandardIfd is the standard "Exif" IFD.
|
|
exifStandardIfd = NewIfdTag(&rootStandardIfd, 0x8769, "Exif") // IFD/Exif
|
|
|
|
// iopStandardIfd is the standard "Iop" IFD.
|
|
iopStandardIfd = NewIfdTag(&exifStandardIfd, 0xA005, "Iop") // IFD/Exif/Iop
|
|
|
|
// gpsInfoStandardIfd is the standard "GPS" IFD.
|
|
gpsInfoStandardIfd = NewIfdTag(&rootStandardIfd, 0x8825, "GPSInfo") // IFD/GPSInfo
|
|
)
|
|
|
|
// IfdIdentityPart represents one component in an IFD path.
|
|
type IfdIdentityPart struct {
|
|
Name string
|
|
Index int
|
|
}
|
|
|
|
// String returns a fully-qualified IFD path.
|
|
func (iip IfdIdentityPart) String() string {
|
|
if iip.Index > 0 {
|
|
return fmt.Sprintf("%s%d", iip.Name, iip.Index)
|
|
} else {
|
|
return iip.Name
|
|
}
|
|
}
|
|
|
|
// UnindexedString returned a non-fully-qualified IFD path.
|
|
func (iip IfdIdentityPart) UnindexedString() string {
|
|
return iip.Name
|
|
}
|
|
|
|
// IfdIdentity represents a single IFD path and provides access to various
|
|
// information and representations.
|
|
//
|
|
// Only global instances can be used for equality checks.
|
|
type IfdIdentity struct {
|
|
ifdTag IfdTag
|
|
parts []IfdIdentityPart
|
|
ifdPath string
|
|
fqIfdPath string
|
|
}
|
|
|
|
// NewIfdIdentity returns a new IfdIdentity struct.
|
|
func NewIfdIdentity(ifdTag IfdTag, parts ...IfdIdentityPart) (ii *IfdIdentity) {
|
|
ii = &IfdIdentity{
|
|
ifdTag: ifdTag,
|
|
parts: parts,
|
|
}
|
|
|
|
ii.ifdPath = ii.getIfdPath()
|
|
ii.fqIfdPath = ii.getFqIfdPath()
|
|
|
|
return ii
|
|
}
|
|
|
|
func (ii *IfdIdentity) getFqIfdPath() string {
|
|
partPhrases := make([]string, len(ii.parts))
|
|
for i, iip := range ii.parts {
|
|
partPhrases[i] = iip.String()
|
|
}
|
|
|
|
return strings.Join(partPhrases, "/")
|
|
}
|
|
|
|
func (ii *IfdIdentity) getIfdPath() string {
|
|
partPhrases := make([]string, len(ii.parts))
|
|
for i, iip := range ii.parts {
|
|
partPhrases[i] = iip.UnindexedString()
|
|
}
|
|
|
|
return strings.Join(partPhrases, "/")
|
|
}
|
|
|
|
// String returns a fully-qualified IFD path.
|
|
func (ii *IfdIdentity) String() string {
|
|
return ii.fqIfdPath
|
|
}
|
|
|
|
// UnindexedString returns a non-fully-qualified IFD path.
|
|
func (ii *IfdIdentity) UnindexedString() string {
|
|
return ii.ifdPath
|
|
}
|
|
|
|
// IfdTag returns the tag struct behind this IFD.
|
|
func (ii *IfdIdentity) IfdTag() IfdTag {
|
|
return ii.ifdTag
|
|
}
|
|
|
|
// TagId returns the tag-ID of the IFD.
|
|
func (ii *IfdIdentity) TagId() uint16 {
|
|
return ii.ifdTag.TagId()
|
|
}
|
|
|
|
// LeafPathPart returns the last right-most path-part, which represents the
|
|
// current IFD.
|
|
func (ii *IfdIdentity) LeafPathPart() IfdIdentityPart {
|
|
return ii.parts[len(ii.parts)-1]
|
|
}
|
|
|
|
// Name returns the simple name of this IFD.
|
|
func (ii *IfdIdentity) Name() string {
|
|
return ii.LeafPathPart().Name
|
|
}
|
|
|
|
// Index returns the index of this IFD (more then one IFD under a parent IFD
|
|
// will be numbered [0..n]).
|
|
func (ii *IfdIdentity) Index() int {
|
|
return ii.LeafPathPart().Index
|
|
}
|
|
|
|
// Equals returns true if the two IfdIdentity instances are effectively
|
|
// identical.
|
|
//
|
|
// Since there's no way to get a specific fully-qualified IFD path without a
|
|
// certain slice of parts and all other fields are also derived from this,
|
|
// checking that the fully-qualified IFD path is equals is sufficient.
|
|
func (ii *IfdIdentity) Equals(ii2 *IfdIdentity) bool {
|
|
return ii.String() == ii2.String()
|
|
}
|
|
|
|
// NewChild creates an IfdIdentity for an IFD that is a child of the current
|
|
// IFD.
|
|
func (ii *IfdIdentity) NewChild(childIfdTag IfdTag, index int) (iiChild *IfdIdentity) {
|
|
if *childIfdTag.parentIfdTag != ii.ifdTag {
|
|
log.Panicf("can not add child; we are not the parent:\nUS=%v\nCHILD=%v", ii.ifdTag, childIfdTag)
|
|
}
|
|
|
|
childPart := IfdIdentityPart{childIfdTag.name, index}
|
|
childParts := append(ii.parts, childPart)
|
|
|
|
iiChild = NewIfdIdentity(childIfdTag, childParts...)
|
|
return iiChild
|
|
}
|
|
|
|
// NewSibling creates an IfdIdentity for an IFD that is a sibling to the current
|
|
// one.
|
|
func (ii *IfdIdentity) NewSibling(index int) (iiSibling *IfdIdentity) {
|
|
parts := make([]IfdIdentityPart, len(ii.parts))
|
|
|
|
copy(parts, ii.parts)
|
|
parts[len(parts)-1].Index = index
|
|
|
|
iiSibling = NewIfdIdentity(ii.ifdTag, parts...)
|
|
return iiSibling
|
|
}
|
|
|
|
var (
|
|
// IfdStandardIfdIdentity represents the IFD path for IFD0.
|
|
IfdStandardIfdIdentity = NewIfdIdentity(rootStandardIfd, IfdIdentityPart{"IFD", 0})
|
|
|
|
// IfdExifStandardIfdIdentity represents the IFD path for IFD0/Exif0.
|
|
IfdExifStandardIfdIdentity = IfdStandardIfdIdentity.NewChild(exifStandardIfd, 0)
|
|
|
|
// IfdExifIopStandardIfdIdentity represents the IFD path for IFD0/Exif0/Iop0.
|
|
IfdExifIopStandardIfdIdentity = IfdExifStandardIfdIdentity.NewChild(iopStandardIfd, 0)
|
|
|
|
// IfdGPSInfoStandardIfdIdentity represents the IFD path for IFD0/GPSInfo0.
|
|
IfdGpsInfoStandardIfdIdentity = IfdStandardIfdIdentity.NewChild(gpsInfoStandardIfd, 0)
|
|
|
|
// Ifd1StandardIfdIdentity represents the IFD path for IFD1.
|
|
Ifd1StandardIfdIdentity = NewIfdIdentity(rootStandardIfd, IfdIdentityPart{"IFD", 1})
|
|
)
|
|
|
|
var (
|
|
// DEPRECATION(dustin): These are for backwards-compatibility. These used to be strings but are now IfdIdentity structs and the newer "StandardIfdIdentity" symbols above should be used instead. These will be removed in the next release.
|
|
|
|
IfdPathStandard = IfdStandardIfdIdentity
|
|
IfdPathStandardExif = IfdExifStandardIfdIdentity
|
|
IfdPathStandardExifIop = IfdExifIopStandardIfdIdentity
|
|
IfdPathStandardGps = IfdGpsInfoStandardIfdIdentity
|
|
)
|