mirror of https://github.com/dsoprea/go-exif.git
ifd_enumerate: Now parse the GPS info.
- Moved some IFD functionality out to its own file. - Tweaked the permissions on some assets.pull/3/head
parent
1ce5b771db
commit
b9537b58c2
Binary file not shown.
After Width: | Height: | Size: 193 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
@ -0,0 +1,37 @@
|
||||||
|
package exif
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
type GpsDegrees struct {
|
||||||
|
Orientation byte
|
||||||
|
Degrees, Minutes, Seconds int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d GpsDegrees) String() string {
|
||||||
|
return fmt.Sprintf("Degrees<O=[%s] D=(%d) M=(%d) S=(%d)>", string([]byte { d.Orientation }), d.Degrees, d.Minutes, d.Seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d GpsDegrees) Decimal() float64 {
|
||||||
|
decimal := float64(d.Degrees) + float64(d.Minutes) / 60.0 + float64(d.Seconds) / 3600.0
|
||||||
|
|
||||||
|
if d.Orientation == 'S' || d.Orientation == 'W' {
|
||||||
|
return -decimal
|
||||||
|
} else {
|
||||||
|
return decimal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type GpsInfo struct {
|
||||||
|
Latitude, Longitude GpsDegrees
|
||||||
|
Altitude int
|
||||||
|
Timestamp time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gi GpsInfo) String() string {
|
||||||
|
return fmt.Sprintf("GpsInfo<LAT=(%.05f) LON=(%.05f) ALT=(%d) TIME=[%s]>", gi.Latitude.Decimal(), gi.Longitude.Decimal(), gi.Altitude, gi.Timestamp)
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
package exif
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/dsoprea/go-logging"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
IfdStandard = "IFD"
|
||||||
|
|
||||||
|
IfdExif = "Exif"
|
||||||
|
IfdGps = "GPSInfo"
|
||||||
|
IfdIop = "Iop"
|
||||||
|
|
||||||
|
IfdExifId = 0x8769
|
||||||
|
IfdGpsId = 0x8825
|
||||||
|
IfdIopId = 0xA005
|
||||||
|
)
|
||||||
|
|
||||||
|
type IfdNameAndIndex struct {
|
||||||
|
Ii IfdIdentity
|
||||||
|
Index int
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// TODO(dustin): !! Get rid of this in favor of one of the two lookups, just below.
|
||||||
|
validIfds = []string {
|
||||||
|
IfdStandard,
|
||||||
|
IfdExif,
|
||||||
|
IfdGps,
|
||||||
|
IfdIop,
|
||||||
|
}
|
||||||
|
|
||||||
|
// A lookup for IFDs by their parents.
|
||||||
|
// TODO(dustin): !! We should switch to indexing by their unique integer IDs (defined below) rather than exposing ourselves to non-unique IFD names (even if we *do* manage the naming ourselves).
|
||||||
|
IfdTagIds = map[string]map[string]uint16 {
|
||||||
|
"": map[string]uint16 {
|
||||||
|
// A root IFD type. Not allowed to be a child (tag-based) IFD.
|
||||||
|
IfdStandard: 0x0,
|
||||||
|
},
|
||||||
|
|
||||||
|
IfdStandard: map[string]uint16 {
|
||||||
|
IfdExif: IfdExifId,
|
||||||
|
IfdGps: IfdGpsId,
|
||||||
|
},
|
||||||
|
|
||||||
|
IfdExif: map[string]uint16 {
|
||||||
|
IfdIop: IfdIopId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// IfdTagNames contains the tag ID-to-name mappings and is populated by
|
||||||
|
// init().
|
||||||
|
IfdTagNames = map[string]map[uint16]string {}
|
||||||
|
|
||||||
|
// IFD Identities. These are often how we refer to IFDs, from call to call.
|
||||||
|
|
||||||
|
// The NULL-type instance for search misses and empty responses.
|
||||||
|
ZeroIi = IfdIdentity{}
|
||||||
|
|
||||||
|
RootIi = IfdIdentity{ IfdName: IfdStandard }
|
||||||
|
ExifIi = IfdIdentity{ ParentIfdName: IfdStandard, IfdName: IfdExif }
|
||||||
|
GpsIi = IfdIdentity{ ParentIfdName: IfdStandard, IfdName: IfdGps }
|
||||||
|
ExifIopIi = IfdIdentity{ ParentIfdName: IfdExif, IfdName: IfdIop }
|
||||||
|
|
||||||
|
// Produce a list of unique IDs for each IFD that we can pass around (so we
|
||||||
|
// don't always have to be comparing parent and child names).
|
||||||
|
//
|
||||||
|
// For lack of need, this is just static.
|
||||||
|
//
|
||||||
|
// (0) is reserved for not-found/miss responses.
|
||||||
|
IfdIds = map[IfdIdentity]int {
|
||||||
|
RootIi: 1,
|
||||||
|
ExifIi: 2,
|
||||||
|
GpsIi: 3,
|
||||||
|
ExifIopIi: 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
IfdDesignations = map[string]IfdNameAndIndex {
|
||||||
|
"ifd0": { RootIi, 0 },
|
||||||
|
"ifd1": { RootIi, 1 },
|
||||||
|
"exif": { ExifIi, 0 },
|
||||||
|
"gps": { GpsIi, 0 },
|
||||||
|
"iop": { ExifIopIi, 0 },
|
||||||
|
}
|
||||||
|
|
||||||
|
IfdDesignationsR = make(map[IfdNameAndIndex]string)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ifdLogger = log.NewLogger("exif.ifd")
|
||||||
|
)
|
||||||
|
|
||||||
|
func IfdDesignation(ii IfdIdentity, index int) string {
|
||||||
|
if ii == RootIi {
|
||||||
|
return fmt.Sprintf("%s%d", ii.IfdName, index)
|
||||||
|
} else {
|
||||||
|
return ii.IfdName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type IfdIdentity struct {
|
||||||
|
ParentIfdName string
|
||||||
|
IfdName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ii IfdIdentity) String() string {
|
||||||
|
return fmt.Sprintf("IfdIdentity<PARENT-NAME=[%s] NAME=[%s]>", ii.ParentIfdName, ii.IfdName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ii IfdIdentity) Id() int {
|
||||||
|
return IfdIdWithIdentityOrFail(ii)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for ifdName, tags := range IfdTagIds {
|
||||||
|
tagsR := make(map[uint16]string)
|
||||||
|
|
||||||
|
for tagName, tagId := range tags {
|
||||||
|
tagsR[tagId] = tagName
|
||||||
|
}
|
||||||
|
|
||||||
|
IfdTagNames[ifdName] = tagsR
|
||||||
|
}
|
||||||
|
|
||||||
|
for designation, ni := range IfdDesignations {
|
||||||
|
IfdDesignationsR[ni] = designation
|
||||||
|
}
|
||||||
|
}
|
136
ifd_enumerate.go
136
ifd_enumerate.go
|
@ -6,6 +6,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"errors"
|
"errors"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"time"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
|
||||||
|
@ -14,8 +16,11 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ifdEnumerateLogger = log.NewLogger("exifjpeg.ifd")
|
ifdEnumerateLogger = log.NewLogger("exifjpeg.ifd")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
ErrNoThumbnail = errors.New("no thumbnail")
|
ErrNoThumbnail = errors.New("no thumbnail")
|
||||||
|
ErrNoGpsTags = errors.New("no gps tags")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -796,6 +801,137 @@ func (ifd *Ifd) DumpTree() []string {
|
||||||
return ifd.dumpTree(nil, 0)
|
return ifd.dumpTree(nil, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GpsInfo parses and consolidates the GPS info. This can only be called on the
|
||||||
|
// GPS IFD.
|
||||||
|
func (ifd *Ifd) GpsInfo() (gi *GpsInfo, err error) {
|
||||||
|
defer func() {
|
||||||
|
if state := recover(); state != nil {
|
||||||
|
err = log.Wrap(state.(error))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
gi = new(GpsInfo)
|
||||||
|
|
||||||
|
if ifd.Ii != GpsIi {
|
||||||
|
log.Panicf("GPS can only be read on GPS IFD: [%s] != [%s]", ifd.Ii, GpsIi)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tags, found := ifd.EntriesByTagId[TagVersionId]; found == false {
|
||||||
|
log.Panic(ErrNoGpsTags)
|
||||||
|
} else if bytes.Compare(tags[0].value, []byte { 2, 2, 0, 0}) != 0 {
|
||||||
|
log.Panic(ErrNoGpsTags)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tags, found := ifd.EntriesByTagId[TagLatitudeId]
|
||||||
|
if found == false {
|
||||||
|
log.Panicf("latitude not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
latitudeValue, err := ifd.TagValue(tags[0])
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
tags, found = ifd.EntriesByTagId[TagLatitudeRefId]
|
||||||
|
if found == false {
|
||||||
|
log.Panicf("latitude-ref not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
latitudeRefValue, err := ifd.TagValue(tags[0])
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
tags, found = ifd.EntriesByTagId[TagLongitudeId]
|
||||||
|
if found == false {
|
||||||
|
log.Panicf("longitude not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
longitudeValue, err := ifd.TagValue(tags[0])
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
tags, found = ifd.EntriesByTagId[TagLongitudeRefId]
|
||||||
|
if found == false {
|
||||||
|
log.Panicf("longitude-ref not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
longitudeRefValue, err := ifd.TagValue(tags[0])
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
|
||||||
|
// Parse location.
|
||||||
|
|
||||||
|
latitudeRaw := latitudeValue.([]Rational)
|
||||||
|
|
||||||
|
gi.Latitude = GpsDegrees{
|
||||||
|
Orientation: latitudeRefValue.(string)[0],
|
||||||
|
Degrees: int(float64(latitudeRaw[0].Numerator) / float64(latitudeRaw[0].Denominator)),
|
||||||
|
Minutes: int(float64(latitudeRaw[1].Numerator) / float64(latitudeRaw[1].Denominator)),
|
||||||
|
Seconds: int(float64(latitudeRaw[2].Numerator) / float64(latitudeRaw[2].Denominator)),
|
||||||
|
}
|
||||||
|
|
||||||
|
longitudeRaw := longitudeValue.([]Rational)
|
||||||
|
|
||||||
|
gi.Longitude = GpsDegrees{
|
||||||
|
Orientation: longitudeRefValue.(string)[0],
|
||||||
|
Degrees: int(float64(longitudeRaw[0].Numerator) / float64(longitudeRaw[0].Denominator)),
|
||||||
|
Minutes: int(float64(longitudeRaw[1].Numerator) / float64(longitudeRaw[1].Denominator)),
|
||||||
|
Seconds: int(float64(longitudeRaw[2].Numerator) / float64(longitudeRaw[2].Denominator)),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Parse altitude.
|
||||||
|
|
||||||
|
altitudeTags, foundAltitude := ifd.EntriesByTagId[TagAltitudeId]
|
||||||
|
altitudeRefTags, foundAltitudeRef := ifd.EntriesByTagId[TagAltitudeRefId]
|
||||||
|
|
||||||
|
if foundAltitude == true && foundAltitudeRef == true {
|
||||||
|
altitudeValue, err := ifd.TagValue(altitudeTags[0])
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
altitudeRefValue, err := ifd.TagValue(altitudeRefTags[0])
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
altitudeRaw := altitudeValue.([]Rational)
|
||||||
|
altitude := int(altitudeRaw[0].Numerator / altitudeRaw[0].Denominator)
|
||||||
|
if altitudeRefValue.([]byte)[0] == 1 {
|
||||||
|
altitude *= -1
|
||||||
|
}
|
||||||
|
|
||||||
|
gi.Altitude = altitude
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Parse time.
|
||||||
|
|
||||||
|
timestampTags, foundTimestamp := ifd.EntriesByTagId[TagTimestampId]
|
||||||
|
datestampTags, foundDatestamp := ifd.EntriesByTagId[TagDatestampId]
|
||||||
|
|
||||||
|
if foundTimestamp == true && foundDatestamp == true {
|
||||||
|
datestampValue, err := ifd.TagValue(datestampTags[0])
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
dateParts := strings.Split(datestampValue.(string), ":")
|
||||||
|
|
||||||
|
year, err1 := strconv.ParseUint(dateParts[0], 10, 16)
|
||||||
|
month, err2 := strconv.ParseUint(dateParts[1], 10, 8)
|
||||||
|
day, err3 := strconv.ParseUint(dateParts[2], 10, 8)
|
||||||
|
|
||||||
|
if err1 == nil && err2 == nil && err3 == nil {
|
||||||
|
timestampValue, err := ifd.TagValue(timestampTags[0])
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
timestampRaw := timestampValue.([]Rational)
|
||||||
|
|
||||||
|
hour := int(timestampRaw[0].Numerator / timestampRaw[0].Denominator)
|
||||||
|
minute := int(timestampRaw[1].Numerator / timestampRaw[1].Denominator)
|
||||||
|
second := int(timestampRaw[2].Numerator / timestampRaw[2].Denominator)
|
||||||
|
|
||||||
|
gi.Timestamp = time.Date(int(year), time.Month(month), int(day), hour, minute, second, 0, time.UTC)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gi, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
type QueuedIfd struct {
|
type QueuedIfd struct {
|
||||||
Ii IfdIdentity
|
Ii IfdIdentity
|
||||||
TagId uint16
|
TagId uint16
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -291,3 +292,62 @@ func Test_Ifd_Thumbnail(t *testing.T) {
|
||||||
t.Fatalf("thumbnail not correct")
|
t.Fatalf("thumbnail not correct")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIfd_GpsInfo(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
if state := recover(); state != nil {
|
||||||
|
err := log.Wrap(state.(error))
|
||||||
|
log.PrintErrorf(err, "Test failure.")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
filepath := path.Join(assetsPath, "20180428_212312.jpg")
|
||||||
|
|
||||||
|
rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
_, index, err := Collect(rawExif)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
ifd, err := index.RootIfd.ChildWithIfdIdentity(GpsIi)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
gi, err := ifd.GpsInfo()
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
if gi.Latitude.Orientation != 'N' || gi.Latitude.Degrees != 26 || gi.Latitude.Minutes != 35 || gi.Latitude.Seconds != 12 {
|
||||||
|
t.Fatalf("latitude not correct")
|
||||||
|
} else if gi.Longitude.Orientation != 'W' || gi.Longitude.Degrees != 80 || gi.Longitude.Minutes != 3 || gi.Longitude.Seconds != 13 {
|
||||||
|
t.Fatalf("longitude not correct")
|
||||||
|
} else if gi.Altitude != 0 {
|
||||||
|
t.Fatalf("altitude not correct")
|
||||||
|
} else if gi.Timestamp.Unix() != 1524964977 {
|
||||||
|
t.Fatalf("timestamp not correct")
|
||||||
|
} else if gi.Altitude != 0 {
|
||||||
|
t.Fatalf("altitude not correct")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func ExampleIfd_GpsInfo() {
|
||||||
|
filepath := path.Join(assetsPath, "20180428_212312.jpg")
|
||||||
|
|
||||||
|
rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
_, index, err := Collect(rawExif)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
ifd, err := index.RootIfd.ChildWithIfdIdentity(GpsIi)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
gi, err := ifd.GpsInfo()
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
fmt.Printf("%s\n", gi)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// GpsInfo<LAT=(26.58667) LON=(-80.05361) ALT=(0) TIME=[2018-04-29 01:22:57 +0000 UTC]>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,8 @@ type IfdTagEntry struct {
|
||||||
// IfdName is the IFD that this tag belongs to.
|
// IfdName is the IFD that this tag belongs to.
|
||||||
Ii IfdIdentity
|
Ii IfdIdentity
|
||||||
|
|
||||||
// TODO(dustin): !! We now parse and read the value immediately. Update the rest of the logic to use this and get rid of all ofthe staggered and different resolution mechanisms.
|
|
||||||
|
// TODO(dustin): !! We now parse and read the value immediately. Update the rest of the logic to use this and get rid of all of the staggered and different resolution mechanisms.
|
||||||
value []byte
|
value []byte
|
||||||
isUnhandledUnknown bool
|
isUnhandledUnknown bool
|
||||||
}
|
}
|
||||||
|
|
129
tags.go
129
tags.go
|
@ -10,81 +10,30 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
IfdStandard = "IFD"
|
// IFD1
|
||||||
|
|
||||||
IfdExif = "Exif"
|
|
||||||
IfdGps = "GPSInfo"
|
|
||||||
IfdIop = "Iop"
|
|
||||||
|
|
||||||
IfdExifId = 0x8769
|
|
||||||
IfdGpsId = 0x8825
|
|
||||||
IfdIopId = 0xA005
|
|
||||||
|
|
||||||
ThumbnailOffsetTagId = 0x0201
|
ThumbnailOffsetTagId = 0x0201
|
||||||
ThumbnailSizeTagId = 0x0202
|
ThumbnailSizeTagId = 0x0202
|
||||||
)
|
|
||||||
|
|
||||||
type IfdNameAndIndex struct {
|
// Exif
|
||||||
Ii IfdIdentity
|
|
||||||
Index int
|
TagVersionId = 0x0000
|
||||||
}
|
|
||||||
|
TagLatitudeId = 0x0002
|
||||||
|
TagLatitudeRefId = 0x0001
|
||||||
|
TagLongitudeId = 0x0004
|
||||||
|
TagLongitudeRefId = 0x0003
|
||||||
|
|
||||||
|
TagTimestampId = 0x0007
|
||||||
|
TagDatestampId = 0x001d
|
||||||
|
|
||||||
|
TagAltitudeId = 0x0006
|
||||||
|
TagAltitudeRefId = 0x0005
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
tagDataFilepath = ""
|
tagDataFilepath = ""
|
||||||
|
|
||||||
// TODO(dustin): !! Get rid of this in favor of one of the two lookups, just below.
|
|
||||||
validIfds = []string {
|
|
||||||
IfdStandard,
|
|
||||||
IfdExif,
|
|
||||||
IfdGps,
|
|
||||||
IfdIop,
|
|
||||||
}
|
|
||||||
|
|
||||||
// A lookup for IFDs by their parents.
|
|
||||||
// TODO(dustin): !! We should switch to indexing by their unique integer IDs (defined below) rather than exposing ourselves to non-unique IFD names (even if we *do* manage the naming ourselves).
|
|
||||||
IfdTagIds = map[string]map[string]uint16 {
|
|
||||||
"": map[string]uint16 {
|
|
||||||
// A root IFD type. Not allowed to be a child (tag-based) IFD.
|
|
||||||
IfdStandard: 0x0,
|
|
||||||
},
|
|
||||||
|
|
||||||
IfdStandard: map[string]uint16 {
|
|
||||||
IfdExif: IfdExifId,
|
|
||||||
IfdGps: IfdGpsId,
|
|
||||||
},
|
|
||||||
|
|
||||||
IfdExif: map[string]uint16 {
|
|
||||||
IfdIop: IfdIopId,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// IfdTagNames contains the tag ID-to-name mappings and is populated by
|
|
||||||
// init().
|
|
||||||
IfdTagNames = map[string]map[uint16]string {}
|
|
||||||
|
|
||||||
// IFD Identities. These are often how we refer to IFDs, from call to call.
|
|
||||||
|
|
||||||
// The NULL-type instance for search misses and empty responses.
|
|
||||||
ZeroIi = IfdIdentity{}
|
|
||||||
|
|
||||||
RootIi = IfdIdentity{ IfdName: IfdStandard }
|
|
||||||
ExifIi = IfdIdentity{ ParentIfdName: IfdStandard, IfdName: IfdExif }
|
|
||||||
GpsIi = IfdIdentity{ ParentIfdName: IfdStandard, IfdName: IfdGps }
|
|
||||||
ExifIopIi = IfdIdentity{ ParentIfdName: IfdExif, IfdName: IfdIop }
|
|
||||||
|
|
||||||
// Produce a list of unique IDs for each IFD that we can pass around (so we
|
|
||||||
// don't always have to be comparing parent and child names).
|
|
||||||
//
|
|
||||||
// For lack of need, this is just static.
|
|
||||||
//
|
|
||||||
// (0) is reserved for not-found/miss responses.
|
|
||||||
IfdIds = map[IfdIdentity]int {
|
|
||||||
RootIi: 1,
|
|
||||||
ExifIi: 2,
|
|
||||||
GpsIi: 3,
|
|
||||||
ExifIopIi: 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
// tagsWithoutAlignment is a tag-lookup for tags whose value size won't
|
// tagsWithoutAlignment is a tag-lookup for tags whose value size won't
|
||||||
// necessarily be a multiple of its tag-type.
|
// necessarily be a multiple of its tag-type.
|
||||||
tagsWithoutAlignment = map[uint16]struct{} {
|
tagsWithoutAlignment = map[uint16]struct{} {
|
||||||
|
@ -94,44 +43,12 @@ var (
|
||||||
}
|
}
|
||||||
|
|
||||||
tagIndex *TagIndex
|
tagIndex *TagIndex
|
||||||
|
|
||||||
IfdDesignations = map[string]IfdNameAndIndex {
|
|
||||||
"ifd0": { RootIi, 0 },
|
|
||||||
"ifd1": { RootIi, 1 },
|
|
||||||
"exif": { ExifIi, 0 },
|
|
||||||
"gps": { GpsIi, 0 },
|
|
||||||
"iop": { ExifIopIi, 0 },
|
|
||||||
}
|
|
||||||
|
|
||||||
IfdDesignationsR = make(map[IfdNameAndIndex]string)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
tagsLogger = log.NewLogger("exif.tags")
|
tagsLogger = log.NewLogger("exif.tags")
|
||||||
)
|
)
|
||||||
|
|
||||||
func IfdDesignation(ii IfdIdentity, index int) string {
|
|
||||||
if ii == RootIi {
|
|
||||||
return fmt.Sprintf("%s%d", ii.IfdName, index)
|
|
||||||
} else {
|
|
||||||
return ii.IfdName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
type IfdIdentity struct {
|
|
||||||
ParentIfdName string
|
|
||||||
IfdName string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ii IfdIdentity) String() string {
|
|
||||||
return fmt.Sprintf("IfdIdentity<PARENT-NAME=[%s] NAME=[%s]>", ii.ParentIfdName, ii.IfdName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ii IfdIdentity) Id() int {
|
|
||||||
return IfdIdWithIdentityOrFail(ii)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// File structures.
|
// File structures.
|
||||||
|
|
||||||
|
@ -410,19 +327,5 @@ func init() {
|
||||||
assetsPath := path.Join(goPath, "src", "github.com", "dsoprea", "go-exif", "assets")
|
assetsPath := path.Join(goPath, "src", "github.com", "dsoprea", "go-exif", "assets")
|
||||||
tagDataFilepath = path.Join(assetsPath, "tags.yaml")
|
tagDataFilepath = path.Join(assetsPath, "tags.yaml")
|
||||||
|
|
||||||
for ifdName, tags := range IfdTagIds {
|
|
||||||
tagsR := make(map[uint16]string)
|
|
||||||
|
|
||||||
for tagName, tagId := range tags {
|
|
||||||
tagsR[tagId] = tagName
|
|
||||||
}
|
|
||||||
|
|
||||||
IfdTagNames[ifdName] = tagsR
|
|
||||||
}
|
|
||||||
|
|
||||||
for designation, ni := range IfdDesignations {
|
|
||||||
IfdDesignationsR[ni] = designation
|
|
||||||
}
|
|
||||||
|
|
||||||
tagIndex = NewTagIndex()
|
tagIndex = NewTagIndex()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue