mirror of https://github.com/perkeep/perkeep.git
goexif: sync with upsteam
Change-Id: Idbb4a38760132b3a782d9cf3898875472411d2f0
This commit is contained in:
parent
c0d20d6bfc
commit
3d128a5191
|
@ -16,6 +16,21 @@ import (
|
||||||
"camlistore.org/third_party/github.com/camlistore/goexif/tiff"
|
"camlistore.org/third_party/github.com/camlistore/goexif/tiff"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var validField map[FieldName]bool
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
validField = make(map[FieldName]bool)
|
||||||
|
for _, name := range exifFields {
|
||||||
|
validField[name] = true
|
||||||
|
}
|
||||||
|
for _, name := range gpsFields {
|
||||||
|
validField[name] = true
|
||||||
|
}
|
||||||
|
for _, name := range interopFields {
|
||||||
|
validField[name] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
exifPointer = 0x8769
|
exifPointer = 0x8769
|
||||||
gpsPointer = 0x8825
|
gpsPointer = 0x8825
|
||||||
|
@ -38,7 +53,7 @@ func isTagNotPresentErr(err error) bool {
|
||||||
type Exif struct {
|
type Exif struct {
|
||||||
tif *tiff.Tiff
|
tif *tiff.Tiff
|
||||||
|
|
||||||
main map[uint16]*tiff.Tag
|
main map[FieldName]*tiff.Tag
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode parses EXIF-encoded data from r and returns a queryable Exif object.
|
// Decode parses EXIF-encoded data from r and returns a queryable Exif object.
|
||||||
|
@ -58,31 +73,32 @@ func Decode(r io.Reader) (*Exif, error) {
|
||||||
|
|
||||||
// build an exif structure from the tiff
|
// build an exif structure from the tiff
|
||||||
x := &Exif{
|
x := &Exif{
|
||||||
main: map[uint16]*tiff.Tag{},
|
main: map[FieldName]*tiff.Tag{},
|
||||||
tif: tif,
|
tif: tif,
|
||||||
}
|
}
|
||||||
|
|
||||||
ifd0 := tif.Dirs[0]
|
ifd0 := tif.Dirs[0]
|
||||||
for _, tag := range ifd0.Tags {
|
for _, tag := range ifd0.Tags {
|
||||||
x.main[tag.Id] = tag
|
name := exifFields[tag.Id]
|
||||||
|
x.main[name] = tag
|
||||||
}
|
}
|
||||||
|
|
||||||
// recurse into exif, gps, and interop sub-IFDs
|
// recurse into exif, gps, and interop sub-IFDs
|
||||||
if err = x.loadSubDir(er, exifPointer); err != nil {
|
if err = x.loadSubDir(er, exifIFDPointer, exifFields); err != nil {
|
||||||
return x, err
|
return x, err
|
||||||
}
|
}
|
||||||
if err = x.loadSubDir(er, gpsPointer); err != nil {
|
if err = x.loadSubDir(er, gpsInfoIFDPointer, gpsFields); err != nil {
|
||||||
return x, err
|
return x, err
|
||||||
}
|
}
|
||||||
if err = x.loadSubDir(er, interopPointer); err != nil {
|
if err = x.loadSubDir(er, interoperabilityIFDPointer, interopFields); err != nil {
|
||||||
return x, err
|
return x, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return x, nil
|
return x, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Exif) loadSubDir(r *bytes.Reader, tagId uint16) error {
|
func (x *Exif) loadSubDir(r *bytes.Reader, ptrName FieldName, fieldMap map[uint16]FieldName) error {
|
||||||
tag, ok := x.main[tagId]
|
tag, ok := x.main[ptrName]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -97,7 +113,8 @@ func (x *Exif) loadSubDir(r *bytes.Reader, tagId uint16) error {
|
||||||
return errors.New("exif: sub-IFD decode failed: " + err.Error())
|
return errors.New("exif: sub-IFD decode failed: " + err.Error())
|
||||||
}
|
}
|
||||||
for _, tag := range subDir.Tags {
|
for _, tag := range subDir.Tags {
|
||||||
x.main[tag.Id] = tag
|
name := fieldMap[tag.Id]
|
||||||
|
x.main[name] = tag
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -107,11 +124,9 @@ func (x *Exif) loadSubDir(r *bytes.Reader, tagId uint16) error {
|
||||||
// If the tag is not known or not present, an error is returned. If the
|
// If the tag is not known or not present, an error is returned. If the
|
||||||
// tag name is known, the error will be a TagNotPresentError.
|
// tag name is known, the error will be a TagNotPresentError.
|
||||||
func (x *Exif) Get(name FieldName) (*tiff.Tag, error) {
|
func (x *Exif) Get(name FieldName) (*tiff.Tag, error) {
|
||||||
id, ok := fields[name]
|
if !validField[name] {
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("exif: invalid tag name %q", name)
|
return nil, fmt.Errorf("exif: invalid tag name %q", name)
|
||||||
}
|
} else if tg, ok := x.main[name]; ok {
|
||||||
if tg, ok := x.main[id]; ok {
|
|
||||||
return tg, nil
|
return tg, nil
|
||||||
}
|
}
|
||||||
return nil, TagNotPresentError(name)
|
return nil, TagNotPresentError(name)
|
||||||
|
@ -126,16 +141,8 @@ type Walker interface {
|
||||||
// Walk calls the Walk method of w with the name and tag for every non-nil exif
|
// Walk calls the Walk method of w with the name and tag for every non-nil exif
|
||||||
// field.
|
// field.
|
||||||
func (x *Exif) Walk(w Walker) error {
|
func (x *Exif) Walk(w Walker) error {
|
||||||
for name, _ := range fields {
|
for name, tag := range x.main {
|
||||||
tag, err := x.Get(name)
|
if err := w.Walk(name, tag); err != nil {
|
||||||
if isTagNotPresentErr(err) {
|
|
||||||
continue
|
|
||||||
} else if err != nil {
|
|
||||||
panic("field list access/construction is broken - this should never happen")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = w.Walk(name, tag)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,24 +175,14 @@ func (x *Exif) DateTime() (time.Time, error) {
|
||||||
// String returns a pretty text representation of the decoded exif data.
|
// String returns a pretty text representation of the decoded exif data.
|
||||||
func (x *Exif) String() string {
|
func (x *Exif) String() string {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
for name, id := range fields {
|
for name, tag := range x.main {
|
||||||
if tag, ok := x.main[id]; ok {
|
fmt.Fprintf(&buf, "%s: %s\n", name, tag)
|
||||||
fmt.Fprintf(&buf, "%s: %s\n", name, tag)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x Exif) MarshalJSON() ([]byte, error) {
|
func (x Exif) MarshalJSON() ([]byte, error) {
|
||||||
m := map[string]interface{}{}
|
return json.Marshal(x.main)
|
||||||
|
|
||||||
for name, id := range fields {
|
|
||||||
if tag, ok := x.main[id]; ok {
|
|
||||||
m[string(name)] = tag
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.Marshal(m)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type appSec struct {
|
type appSec struct {
|
||||||
|
@ -196,43 +193,41 @@ type appSec struct {
|
||||||
// newAppSec finds marker in r and returns the corresponding application data
|
// newAppSec finds marker in r and returns the corresponding application data
|
||||||
// section.
|
// section.
|
||||||
func newAppSec(marker byte, r io.Reader) (*appSec, error) {
|
func newAppSec(marker byte, r io.Reader) (*appSec, error) {
|
||||||
|
br := bufio.NewReader(r)
|
||||||
app := &appSec{marker: marker}
|
app := &appSec{marker: marker}
|
||||||
|
var dataLen uint16
|
||||||
buf := bufio.NewReader(r)
|
|
||||||
|
|
||||||
// seek to marker
|
// seek to marker
|
||||||
for {
|
for dataLen == 0 {
|
||||||
b, err := buf.ReadByte()
|
if _, err := br.ReadBytes(0xFF); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c, err := br.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if c != marker {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dataLenBytes, err := br.Peek(2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
n, err := buf.Peek(1)
|
dataLen = binary.BigEndian.Uint16(dataLenBytes)
|
||||||
if b == 0xFF && n[0] == marker {
|
|
||||||
buf.ReadByte()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// read section size
|
|
||||||
var dataLen uint16
|
|
||||||
err := binary.Read(buf, binary.BigEndian, &dataLen)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
dataLen -= 2 // subtract length of the 2 byte size marker itself
|
|
||||||
|
|
||||||
// read section data
|
// read section data
|
||||||
nread := 0
|
nread := 0
|
||||||
for nread < int(dataLen) {
|
for nread < int(dataLen) {
|
||||||
s := make([]byte, int(dataLen)-nread)
|
s := make([]byte, int(dataLen)-nread)
|
||||||
n, err := buf.Read(s)
|
n, err := br.Read(s)
|
||||||
if err != nil {
|
nread += n
|
||||||
|
if err != nil && nread < int(dataLen) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
nread += n
|
app.data = append(app.data, s[:n]...)
|
||||||
app.data = append(app.data, s...)
|
|
||||||
}
|
}
|
||||||
|
app.data = app.data[2:] // exclude dataLenBytes
|
||||||
return app, nil
|
return app, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,13 +239,13 @@ func (app *appSec) reader() *bytes.Reader {
|
||||||
// exifReader returns a reader on this appSec with the read cursor advanced to
|
// exifReader returns a reader on this appSec with the read cursor advanced to
|
||||||
// the start of the exif's tiff encoded portion.
|
// the start of the exif's tiff encoded portion.
|
||||||
func (app *appSec) exifReader() (*bytes.Reader, error) {
|
func (app *appSec) exifReader() (*bytes.Reader, error) {
|
||||||
// read/check for exif special mark
|
|
||||||
if len(app.data) < 6 {
|
if len(app.data) < 6 {
|
||||||
return nil, errors.New("exif: failed to find exif intro marker")
|
return nil, errors.New("exif: failed to find exif intro marker")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read/check for exif special mark
|
||||||
exif := app.data[:6]
|
exif := app.data[:6]
|
||||||
if string(exif) != "Exif"+string([]byte{0x00, 0x00}) {
|
if !bytes.Equal(exif, append([]byte("Exif"), 0x00, 0x00)) {
|
||||||
return nil, errors.New("exif: failed to find exif intro marker")
|
return nil, errors.New("exif: failed to find exif intro marker")
|
||||||
}
|
}
|
||||||
return bytes.NewReader(app.data[6:]), nil
|
return bytes.NewReader(app.data[6:]), nil
|
||||||
|
|
|
@ -9,146 +9,156 @@ const (
|
||||||
DateTime FieldName = "DateTime"
|
DateTime FieldName = "DateTime"
|
||||||
)
|
)
|
||||||
|
|
||||||
var fields = map[FieldName]uint16{
|
const (
|
||||||
|
exifIFDPointer FieldName = "ExifIFDPointer"
|
||||||
|
gpsInfoIFDPointer = "GPSInfoIFDPointer"
|
||||||
|
interoperabilityIFDPointer = "InteroperabilityIFDPointer"
|
||||||
|
)
|
||||||
|
|
||||||
|
var exifFields = map[uint16]FieldName{
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
////////// IFD 0 ////////////////////
|
////////// IFD 0 ////////////////////
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
|
|
||||||
// image data structure
|
// image data structure
|
||||||
"ImageWidth": 0x0100,
|
0x0100: "ImageWidth",
|
||||||
"ImageLength": 0x0101,
|
0x0101: "ImageLength",
|
||||||
"BitsPerSample": 0x0102,
|
0x0102: "BitsPerSample",
|
||||||
"Compression": 0x0103,
|
0x0103: "Compression",
|
||||||
"PhotometricInterpretation": 0x0106,
|
0x0106: "PhotometricInterpretation",
|
||||||
"Orientation": 0x0112,
|
0x0112: "Orientation",
|
||||||
"SamplesPerPixel": 0x0115,
|
0x0115: "SamplesPerPixel",
|
||||||
"PlanarConfiguration": 0x011C,
|
0x011C: "PlanarConfiguration",
|
||||||
"YCbCrSubSampling": 0x0212,
|
0x0212: "YCbCrSubSampling",
|
||||||
"YCbCrPositioning": 0x0213,
|
0x0213: "YCbCrPositioning",
|
||||||
"XResolution": 0x011A,
|
0x011A: "XResolution",
|
||||||
"YResolution": 0x011B,
|
0x011B: "YResolution",
|
||||||
"ResolutionUnit": 0x0128,
|
0x0128: "ResolutionUnit",
|
||||||
|
|
||||||
// Other tags
|
// Other tags
|
||||||
"DateTime": 0x0132,
|
0x0132: "DateTime",
|
||||||
"ImageDescription": 0x010E,
|
0x010E: "ImageDescription",
|
||||||
"Make": 0x010F,
|
0x010F: "Make",
|
||||||
"Model": 0x0110,
|
0x0110: "Model",
|
||||||
"Software": 0x0131,
|
0x0131: "Software",
|
||||||
"Artist": 0x010e,
|
0x013B: "Artist",
|
||||||
"Copyright": 0x010e,
|
0x8298: "Copyright",
|
||||||
|
|
||||||
// private tags
|
// private tags
|
||||||
"ExifIFDPointer": exifPointer,
|
exifPointer: "ExifIFDPointer",
|
||||||
|
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
////////// Exif sub IFD /////////////
|
////////// Exif sub IFD /////////////
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
|
|
||||||
"GPSInfoIFDPointer": gpsPointer,
|
gpsPointer: "GPSInfoIFDPointer",
|
||||||
"InteroperabilityIFDPointer": interopPointer,
|
interopPointer: "InteroperabilityIFDPointer",
|
||||||
|
|
||||||
"ExifVersion": 0x9000,
|
0x9000: "ExifVersion",
|
||||||
"FlashpixVersion": 0xA000,
|
0xA000: "FlashpixVersion",
|
||||||
|
|
||||||
"ColorSpace": 0xA001,
|
0xA001: "ColorSpace",
|
||||||
|
|
||||||
"ComponentsConfiguration": 0x9101,
|
0x9101: "ComponentsConfiguration",
|
||||||
"CompressedBitsPerPixel": 0x9102,
|
0x9102: "CompressedBitsPerPixel",
|
||||||
"PixelXDimension": 0xA002,
|
0xA002: "PixelXDimension",
|
||||||
"PixelYDimension": 0xA003,
|
0xA003: "PixelYDimension",
|
||||||
|
|
||||||
"MakerNote": 0x927C,
|
0x927C: "MakerNote",
|
||||||
"UserComment": 0x9286,
|
0x9286: "UserComment",
|
||||||
|
|
||||||
"RelatedSoundFile": 0xA004,
|
0xA004: "RelatedSoundFile",
|
||||||
"DateTimeOriginal": 0x9003,
|
0x9003: "DateTimeOriginal",
|
||||||
"DateTimeDigitized": 0x9004,
|
0x9004: "DateTimeDigitized",
|
||||||
"SubSecTime": 0x9290,
|
0x9290: "SubSecTime",
|
||||||
"SubSecTimeOriginal": 0x9291,
|
0x9291: "SubSecTimeOriginal",
|
||||||
"SubSecTimeDigitized": 0x9292,
|
0x9292: "SubSecTimeDigitized",
|
||||||
|
|
||||||
"ImageUniqueID": 0xA420,
|
0xA420: "ImageUniqueID",
|
||||||
|
|
||||||
// picture conditions
|
// picture conditions
|
||||||
"ExposureTime": 0x829A,
|
0x829A: "ExposureTime",
|
||||||
"FNumber": 0x829D,
|
0x829D: "FNumber",
|
||||||
"ExposureProgram": 0x8822,
|
0x8822: "ExposureProgram",
|
||||||
"SpectralSensitivity": 0x8824,
|
0x8824: "SpectralSensitivity",
|
||||||
"ISOSpeedRatings": 0x8827,
|
0x8827: "ISOSpeedRatings",
|
||||||
"OECF": 0x8828,
|
0x8828: "OECF",
|
||||||
"ShutterSpeedValue": 0x9201,
|
0x9201: "ShutterSpeedValue",
|
||||||
"ApertureValue": 0x9202,
|
0x9202: "ApertureValue",
|
||||||
"BrightnessValue": 0x9203,
|
0x9203: "BrightnessValue",
|
||||||
"ExposureBiasValue": 0x9204,
|
0x9204: "ExposureBiasValue",
|
||||||
"MaxApertureValue": 0x9205,
|
0x9205: "MaxApertureValue",
|
||||||
"SubjectDistance": 0x9206,
|
0x9206: "SubjectDistance",
|
||||||
"MeteringMode": 0x9207,
|
0x9207: "MeteringMode",
|
||||||
"LightSource": 0x9208,
|
0x9208: "LightSource",
|
||||||
"Flash": 0x9209,
|
0x9209: "Flash",
|
||||||
"FocalLength": 0x920A,
|
0x920A: "FocalLength",
|
||||||
"SubjectArea": 0x9214,
|
0x9214: "SubjectArea",
|
||||||
"FlashEnergy": 0xA20B,
|
0xA20B: "FlashEnergy",
|
||||||
"SpatialFrequencyResponse": 0xA20C,
|
0xA20C: "SpatialFrequencyResponse",
|
||||||
"FocalPlaneXResolution": 0xA20E,
|
0xA20E: "FocalPlaneXResolution",
|
||||||
"FocalPlaneYResolution": 0xA20F,
|
0xA20F: "FocalPlaneYResolution",
|
||||||
"FocalPlaneResolutionUnit": 0xA210,
|
0xA210: "FocalPlaneResolutionUnit",
|
||||||
"SubjectLocation": 0xA214,
|
0xA214: "SubjectLocation",
|
||||||
"ExposureIndex": 0xA215,
|
0xA215: "ExposureIndex",
|
||||||
"SensingMethod": 0xA217,
|
0xA217: "SensingMethod",
|
||||||
"FileSource": 0xA300,
|
0xA300: "FileSource",
|
||||||
"SceneType": 0xA301,
|
0xA301: "SceneType",
|
||||||
"CFAPattern": 0xA302,
|
0xA302: "CFAPattern",
|
||||||
"CustomRendered": 0xA401,
|
0xA401: "CustomRendered",
|
||||||
"ExposureMode": 0xA402,
|
0xA402: "ExposureMode",
|
||||||
"WhiteBalance": 0xA403,
|
0xA403: "WhiteBalance",
|
||||||
"DigitalZoomRatio": 0xA404,
|
0xA404: "DigitalZoomRatio",
|
||||||
"FocalLengthIn35mmFilm": 0xA405,
|
0xA405: "FocalLengthIn35mmFilm",
|
||||||
"SceneCaptureType": 0xA406,
|
0xA406: "SceneCaptureType",
|
||||||
"GainControl": 0xA407,
|
0xA407: "GainControl",
|
||||||
"Contrast": 0xA408,
|
0xA408: "Contrast",
|
||||||
"Saturation": 0xA409,
|
0xA409: "Saturation",
|
||||||
"Sharpness": 0xA40A,
|
0xA40A: "Sharpness",
|
||||||
"DeviceSettingDescription": 0xA40B,
|
0xA40B: "DeviceSettingDescription",
|
||||||
"SubjectDistanceRange": 0xA40C,
|
0xA40C: "SubjectDistanceRange",
|
||||||
|
}
|
||||||
|
|
||||||
|
var gpsFields = map[uint16]FieldName{
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
//// GPS sub-IFD ////////////////////
|
//// GPS sub-IFD ////////////////////
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
"GPSVersionID": 0x0,
|
0x0: "GPSVersionID",
|
||||||
"GPSLatitudeRef": 0x1,
|
0x1: "GPSLatitudeRef",
|
||||||
"GPSLatitude": 0x2,
|
0x2: "GPSLatitude",
|
||||||
"GPSLongitudeRef": 0x3,
|
0x3: "GPSLongitudeRef",
|
||||||
"GPSLongitude": 0x4,
|
0x4: "GPSLongitude",
|
||||||
"GPSAltitudeRef": 0x5,
|
0x5: "GPSAltitudeRef",
|
||||||
"GPSAltitude": 0x6,
|
0x6: "GPSAltitude",
|
||||||
"GPSTimeStamp": 0x7,
|
0x7: "GPSTimeStamp",
|
||||||
"GPSSatelites": 0x8,
|
0x8: "GPSSatelites",
|
||||||
"GPSStatus": 0x9,
|
0x9: "GPSStatus",
|
||||||
"GPSMeasureMode": 0xA,
|
0xA: "GPSMeasureMode",
|
||||||
"GPSDOP": 0xB,
|
0xB: "GPSDOP",
|
||||||
"GPSSpeedRef": 0xC,
|
0xC: "GPSSpeedRef",
|
||||||
"GPSSpeed": 0xD,
|
0xD: "GPSSpeed",
|
||||||
"GPSTrackRef": 0xE,
|
0xE: "GPSTrackRef",
|
||||||
"GPSTrack": 0xF,
|
0xF: "GPSTrack",
|
||||||
"GPSImgDirectionRef": 0x10,
|
0x10: "GPSImgDirectionRef",
|
||||||
"GPSImgDirection": 0x11,
|
0x11: "GPSImgDirection",
|
||||||
"GPSMapDatum": 0x12,
|
0x12: "GPSMapDatum",
|
||||||
"GPSDestLatitudeRef": 0x13,
|
0x13: "GPSDestLatitudeRef",
|
||||||
"GPSDestLatitude": 0x14,
|
0x14: "GPSDestLatitude",
|
||||||
"GPSDestLongitudeRef": 0x15,
|
0x15: "GPSDestLongitudeRef",
|
||||||
"GPSDestLongitude": 0x16,
|
0x16: "GPSDestLongitude",
|
||||||
"GPSDestBearingRef": 0x17,
|
0x17: "GPSDestBearingRef",
|
||||||
"GPSDestBearing": 0x18,
|
0x18: "GPSDestBearing",
|
||||||
"GPSDestDistanceRef": 0x19,
|
0x19: "GPSDestDistanceRef",
|
||||||
"GPSDestDistance": 0x1A,
|
0x1A: "GPSDestDistance",
|
||||||
"GPSProcessingMethod": 0x1B,
|
0x1B: "GPSProcessingMethod",
|
||||||
"GPSAreaInformation": 0x1C,
|
0x1C: "GPSAreaInformation",
|
||||||
"GPSDateStamp": 0x1D,
|
0x1D: "GPSDateStamp",
|
||||||
"GPSDifferential": 0x1E,
|
0x1E: "GPSDifferential",
|
||||||
|
}
|
||||||
|
|
||||||
|
var interopFields = map[uint16]FieldName{
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
//// Interoperability sub-IFD ///////
|
//// Interoperability sub-IFD ///////
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
"InteroperabilityIndex": 0x1,
|
0x1: "InteroperabilityIndex",
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue