mirror of
https://github.com/dsoprea/go-exif.git
synced 2025-05-31 11:41:57 +00:00
We now have to pass a specific tag-index in from the top.
- This will allow us to handle custom tags and, with a little more work, custom IFDs.
This commit is contained in:
parent
d8cdf29fa6
commit
d3b45fae77
@ -14,134 +14,133 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"flag"
|
"os"
|
||||||
|
|
||||||
"io/ioutil"
|
"encoding/json"
|
||||||
"encoding/json"
|
"io/ioutil"
|
||||||
|
|
||||||
"github.com/dsoprea/go-logging"
|
"github.com/dsoprea/go-exif"
|
||||||
"github.com/dsoprea/go-exif"
|
"github.com/dsoprea/go-logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
filepathArg = ""
|
filepathArg = ""
|
||||||
printAsJsonArg = false
|
printAsJsonArg = false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
type IfdEntry struct {
|
type IfdEntry struct {
|
||||||
IfdName string `json:"ifd_name"`
|
IfdName string `json:"ifd_name"`
|
||||||
ParentIfdName string `json:"parent_ifd_name"`
|
ParentIfdName string `json:"parent_ifd_name"`
|
||||||
IfdIndex int `json:"ifd_index"`
|
IfdIndex int `json:"ifd_index"`
|
||||||
TagId uint16 `json:"tag_id"`
|
TagId uint16 `json:"tag_id"`
|
||||||
TagName string `json:"tag_name"`
|
TagName string `json:"tag_name"`
|
||||||
TagTypeId uint16 `json:"tag_type_id"`
|
TagTypeId uint16 `json:"tag_type_id"`
|
||||||
TagTypeName string `json:"tag_type_name"`
|
TagTypeName string `json:"tag_type_name"`
|
||||||
UnitCount uint32 `json:"unit_count"`
|
UnitCount uint32 `json:"unit_count"`
|
||||||
Value interface{} `json:"value"`
|
Value interface{} `json:"value"`
|
||||||
ValueString string `json:"value_string"`
|
ValueString string `json:"value_string"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
defer func() {
|
defer func() {
|
||||||
if state := recover(); state != nil {
|
if state := recover(); state != nil {
|
||||||
err := log.Wrap(state.(error))
|
err := log.Wrap(state.(error))
|
||||||
log.PrintErrorf(err, "Program error.")
|
log.PrintErrorf(err, "Program error.")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
flag.StringVar(&filepathArg, "filepath", "", "File-path of image")
|
flag.StringVar(&filepathArg, "filepath", "", "File-path of image")
|
||||||
flag.BoolVar(&printAsJsonArg, "json", false, "Print JSON")
|
flag.BoolVar(&printAsJsonArg, "json", false, "Print JSON")
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if filepathArg == "" {
|
if filepathArg == "" {
|
||||||
fmt.Printf("Please provide a file-path for an image.\n")
|
fmt.Printf("Please provide a file-path for an image.\n")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
f, err := os.Open(filepathArg)
|
f, err := os.Open(filepathArg)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
data, err := ioutil.ReadAll(f)
|
data, err := ioutil.ReadAll(f)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
rawExif, err := exif.SearchAndExtractExif(data)
|
rawExif, err := exif.SearchAndExtractExif(data)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
// Run the parse.
|
// Run the parse.
|
||||||
|
|
||||||
entries := make([]IfdEntry, 0)
|
entries := make([]IfdEntry, 0)
|
||||||
|
|
||||||
ti := exif.NewTagIndex()
|
ti := exif.NewTagIndex()
|
||||||
visitor := func(ii exif.IfdIdentity, ifdIndex int, tagId uint16, tagType exif.TagType, valueContext exif.ValueContext) (err error) {
|
visitor := func(ii exif.IfdIdentity, ifdIndex int, tagId uint16, tagType exif.TagType, valueContext exif.ValueContext) (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))
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
it, err := ti.Get(ii, tagId)
|
it, err := ti.Get(ii, tagId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if log.Is(err, exif.ErrTagNotFound) {
|
if log.Is(err, exif.ErrTagNotFound) {
|
||||||
fmt.Printf("WARNING: Unknown tag: [%s] (%04x)\n", ii, tagId)
|
fmt.Printf("WARNING: Unknown tag: [%s] (%04x)\n", ii, tagId)
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
valueString := ""
|
valueString := ""
|
||||||
var value interface{}
|
var value interface{}
|
||||||
if tagType.Type() == exif.TypeUndefined {
|
if tagType.Type() == exif.TypeUndefined {
|
||||||
var err error
|
var err error
|
||||||
value, err = exif.UndefinedValue(ii, tagId, valueContext, tagType.ByteOrder())
|
value, err = exif.UndefinedValue(ii, tagId, valueContext, tagType.ByteOrder())
|
||||||
if log.Is(err, exif.ErrUnhandledUnknownTypedTag) {
|
if log.Is(err, exif.ErrUnhandledUnknownTypedTag) {
|
||||||
value = nil
|
value = nil
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
} else {
|
} else {
|
||||||
valueString = fmt.Sprintf("%v", value)
|
valueString = fmt.Sprintf("%v", value)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
valueString, err = tagType.ResolveAsString(valueContext, true)
|
valueString, err = tagType.ResolveAsString(valueContext, true)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
value = valueString
|
value = valueString
|
||||||
}
|
}
|
||||||
|
|
||||||
entry := IfdEntry{
|
entry := IfdEntry{
|
||||||
IfdName: ii.IfdName,
|
IfdName: ii.IfdName,
|
||||||
ParentIfdName: ii.ParentIfdName,
|
ParentIfdName: ii.ParentIfdName,
|
||||||
IfdIndex: ifdIndex,
|
IfdIndex: ifdIndex,
|
||||||
TagId: tagId,
|
TagId: tagId,
|
||||||
TagName: it.Name,
|
TagName: it.Name,
|
||||||
TagTypeId: tagType.Type(),
|
TagTypeId: tagType.Type(),
|
||||||
TagTypeName: tagType.Name(),
|
TagTypeName: tagType.Name(),
|
||||||
UnitCount: valueContext.UnitCount,
|
UnitCount: valueContext.UnitCount,
|
||||||
Value: value,
|
Value: value,
|
||||||
ValueString: valueString,
|
ValueString: valueString,
|
||||||
}
|
}
|
||||||
|
|
||||||
entries = append(entries, entry)
|
entries = append(entries, entry)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = exif.Visit(rawExif, visitor)
|
_, err = exif.Visit(ti, rawExif, visitor)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
if printAsJsonArg == true {
|
if printAsJsonArg == true {
|
||||||
data, err := json.MarshalIndent(entries, "", " ")
|
data, err := json.MarshalIndent(entries, "", " ")
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
fmt.Println(string(data))
|
fmt.Println(string(data))
|
||||||
} else {
|
} else {
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
fmt.Printf("IFD=[%s] ID=(0x%04x) NAME=[%s] COUNT=(%d) TYPE=[%s] VALUE=[%s]\n", entry.IfdName, entry.TagId, entry.TagName, entry.UnitCount, entry.TagTypeName, entry.ValueString)
|
fmt.Printf("IFD=[%s] ID=(0x%04x) NAME=[%s] COUNT=(%d) TYPE=[%s] VALUE=[%s]\n", entry.IfdName, entry.TagId, entry.TagName, entry.UnitCount, entry.TagTypeName, entry.ValueString)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
8
exif.go
8
exif.go
@ -168,7 +168,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(exifData []byte, visitor RawTagVisitor) (eh ExifHeader, err error) {
|
func Visit(tagIndex *TagIndex, exifData []byte, visitor RawTagVisitor) (eh ExifHeader, 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))
|
||||||
@ -178,7 +178,7 @@ func Visit(exifData []byte, visitor RawTagVisitor) (eh ExifHeader, err error) {
|
|||||||
eh, err = ParseExifHeader(exifData)
|
eh, err = ParseExifHeader(exifData)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
ie := NewIfdEnumerate(exifData, eh.ByteOrder)
|
ie := NewIfdEnumerate(tagIndex, exifData, eh.ByteOrder)
|
||||||
|
|
||||||
err = ie.Scan(eh.FirstIfdOffset, visitor, true)
|
err = ie.Scan(eh.FirstIfdOffset, visitor, true)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
@ -187,7 +187,7 @@ func Visit(exifData []byte, visitor RawTagVisitor) (eh ExifHeader, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Collect recursively builds a static structure of all IFDs and tags.
|
// Collect recursively builds a static structure of all IFDs and tags.
|
||||||
func Collect(exifData []byte) (eh ExifHeader, index IfdIndex, err error) {
|
func Collect(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))
|
||||||
@ -197,7 +197,7 @@ func Collect(exifData []byte) (eh ExifHeader, index IfdIndex, err error) {
|
|||||||
eh, err = ParseExifHeader(exifData)
|
eh, err = ParseExifHeader(exifData)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
ie := NewIfdEnumerate(exifData, eh.ByteOrder)
|
ie := NewIfdEnumerate(tagIndex, exifData, eh.ByteOrder)
|
||||||
|
|
||||||
index, err = ie.Collect(eh.FirstIfdOffset, true)
|
index, err = ie.Collect(eh.FirstIfdOffset, true)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
602
exif_test.go
602
exif_test.go
@ -1,383 +1,385 @@
|
|||||||
package exif
|
package exif
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"bytes"
|
||||||
"os"
|
"fmt"
|
||||||
"path"
|
"os"
|
||||||
"fmt"
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
"bytes"
|
"testing"
|
||||||
|
|
||||||
"io/ioutil"
|
"encoding/binary"
|
||||||
"encoding/binary"
|
"io/ioutil"
|
||||||
|
|
||||||
"github.com/dsoprea/go-logging"
|
"github.com/dsoprea/go-logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
func TestVisit(t *testing.T) {
|
func TestVisit(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if state := recover(); state != nil {
|
if state := recover(); state != nil {
|
||||||
err := log.Wrap(state.(error))
|
err := log.Wrap(state.(error))
|
||||||
log.PrintErrorf(err, "Exif failure.")
|
log.PrintErrorf(err, "Exif failure.")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Open the file.
|
ti := NewTagIndex()
|
||||||
|
|
||||||
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
// Open the file.
|
||||||
f, err := os.Open(filepath)
|
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
defer f.Close()
|
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
||||||
|
f, err := os.Open(filepath)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
data, err := ioutil.ReadAll(f)
|
defer f.Close()
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
// Search for the beginning of the EXIF information. The EXIF is near the
|
data, err := ioutil.ReadAll(f)
|
||||||
// very beginning of our/most JPEGs, so this has a very low cost.
|
log.PanicIf(err)
|
||||||
|
|
||||||
foundAt := -1
|
// Search for the beginning of the EXIF information. The EXIF is near the
|
||||||
for i := 0; i < len(data); i++ {
|
// very beginning of our/most JPEGs, so this has a very low cost.
|
||||||
if _, err := ParseExifHeader(data[i:]); err == nil {
|
|
||||||
foundAt = i
|
|
||||||
break
|
|
||||||
} else if log.Is(err, ErrNoExif) == false {
|
|
||||||
log.Panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if foundAt == -1 {
|
foundAt := -1
|
||||||
log.Panicf("EXIF start not found")
|
for i := 0; i < len(data); i++ {
|
||||||
}
|
if _, err := ParseExifHeader(data[i:]); err == nil {
|
||||||
|
foundAt = i
|
||||||
|
break
|
||||||
|
} else if log.Is(err, ErrNoExif) == false {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Run the parse.
|
if foundAt == -1 {
|
||||||
|
log.Panicf("EXIF start not found")
|
||||||
|
}
|
||||||
|
|
||||||
tags := make([]string, 0)
|
// Run the parse.
|
||||||
|
|
||||||
visitor := func(ii IfdIdentity, ifdIndex int, tagId uint16, tagType TagType, valueContext ValueContext) (err error) {
|
tags := make([]string, 0)
|
||||||
defer func() {
|
|
||||||
if state := recover(); state != nil {
|
|
||||||
err = log.Wrap(state.(error))
|
|
||||||
log.Panic(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
it, err := tagIndex.Get(ii, tagId)
|
visitor := func(ii IfdIdentity, ifdIndex int, tagId uint16, tagType TagType, valueContext ValueContext) (err error) {
|
||||||
if err != nil {
|
defer func() {
|
||||||
if log.Is(err, ErrTagNotFound) {
|
if state := recover(); state != nil {
|
||||||
fmt.Printf("Unknown tag: [%v] (%04x)\n", ii, tagId)
|
err = log.Wrap(state.(error))
|
||||||
return nil
|
log.Panic(err)
|
||||||
} else {
|
}
|
||||||
log.Panic(err)
|
}()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
valueString := ""
|
it, err := ti.Get(ii, tagId)
|
||||||
if tagType.Type() == TypeUndefined {
|
if err != nil {
|
||||||
value, err := UndefinedValue(ii, tagId, valueContext, tagType.ByteOrder())
|
if log.Is(err, ErrTagNotFound) {
|
||||||
if log.Is(err, ErrUnhandledUnknownTypedTag) {
|
fmt.Printf("Unknown tag: [%v] (%04x)\n", ii, tagId)
|
||||||
valueString = "!UNDEFINED!"
|
return nil
|
||||||
} else if err != nil {
|
} else {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
} else {
|
}
|
||||||
valueString = fmt.Sprintf("%v", value)
|
}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
valueString, err = tagType.ResolveAsString(valueContext, true)
|
|
||||||
log.PanicIf(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
description := fmt.Sprintf("IFD=[%s] ID=(0x%04x) NAME=[%s] COUNT=(%d) TYPE=[%s] VALUE=[%s]", ii.IfdName, tagId, it.Name, valueContext.UnitCount, tagType.Name(), valueString)
|
valueString := ""
|
||||||
tags = append(tags, description)
|
if tagType.Type() == TypeUndefined {
|
||||||
|
value, err := UndefinedValue(ii, tagId, valueContext, tagType.ByteOrder())
|
||||||
|
if log.Is(err, ErrUnhandledUnknownTypedTag) {
|
||||||
|
valueString = "!UNDEFINED!"
|
||||||
|
} else if err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
} else {
|
||||||
|
valueString = fmt.Sprintf("%v", value)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
valueString, err = tagType.ResolveAsString(valueContext, true)
|
||||||
|
log.PanicIf(err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
description := fmt.Sprintf("IFD=[%s] ID=(0x%04x) NAME=[%s] COUNT=(%d) TYPE=[%s] VALUE=[%s]", ii.IfdName, tagId, it.Name, valueContext.UnitCount, tagType.Name(), valueString)
|
||||||
}
|
tags = append(tags, description)
|
||||||
|
|
||||||
_, err = Visit(data[foundAt:], visitor)
|
return nil
|
||||||
log.PanicIf(err)
|
}
|
||||||
|
|
||||||
// for _, line := range tags {
|
_, err = Visit(ti, data[foundAt:], visitor)
|
||||||
// fmt.Printf("TAGS: %s\n", line)
|
log.PanicIf(err)
|
||||||
// }
|
|
||||||
|
|
||||||
expected := []string {
|
// for _, line := range tags {
|
||||||
"IFD=[IFD] ID=(0x010f) NAME=[Make] COUNT=(6) TYPE=[ASCII] VALUE=[Canon]",
|
// fmt.Printf("TAGS: %s\n", line)
|
||||||
"IFD=[IFD] ID=(0x0110) NAME=[Model] COUNT=(22) TYPE=[ASCII] VALUE=[Canon EOS 5D Mark III]",
|
// }
|
||||||
"IFD=[IFD] ID=(0x0112) NAME=[Orientation] COUNT=(1) TYPE=[SHORT] VALUE=[1]",
|
|
||||||
"IFD=[IFD] ID=(0x011a) NAME=[XResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1]",
|
|
||||||
"IFD=[IFD] ID=(0x011b) NAME=[YResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1]",
|
|
||||||
"IFD=[IFD] ID=(0x0128) NAME=[ResolutionUnit] COUNT=(1) TYPE=[SHORT] VALUE=[2]",
|
|
||||||
"IFD=[IFD] ID=(0x0132) NAME=[DateTime] COUNT=(20) TYPE=[ASCII] VALUE=[2017:12:02 08:18:50]",
|
|
||||||
"IFD=[IFD] ID=(0x013b) NAME=[Artist] COUNT=(1) TYPE=[ASCII] VALUE=[]",
|
|
||||||
"IFD=[IFD] ID=(0x0213) NAME=[YCbCrPositioning] COUNT=(1) TYPE=[SHORT] VALUE=[2]",
|
|
||||||
"IFD=[IFD] ID=(0x8298) NAME=[Copyright] COUNT=(1) TYPE=[ASCII] VALUE=[]",
|
|
||||||
"IFD=[IFD] ID=(0x8769) NAME=[ExifTag] COUNT=(1) TYPE=[LONG] VALUE=[360]",
|
|
||||||
"IFD=[Exif] ID=(0x829a) NAME=[ExposureTime] COUNT=(1) TYPE=[RATIONAL] VALUE=[1/640]",
|
|
||||||
"IFD=[Exif] ID=(0x829d) NAME=[FNumber] COUNT=(1) TYPE=[RATIONAL] VALUE=[4/1]",
|
|
||||||
"IFD=[Exif] ID=(0x8822) NAME=[ExposureProgram] COUNT=(1) TYPE=[SHORT] VALUE=[4]",
|
|
||||||
"IFD=[Exif] ID=(0x8827) NAME=[ISOSpeedRatings] COUNT=(1) TYPE=[SHORT] VALUE=[1600]",
|
|
||||||
"IFD=[Exif] ID=(0x8830) NAME=[SensitivityType] COUNT=(1) TYPE=[SHORT] VALUE=[2]",
|
|
||||||
"IFD=[Exif] ID=(0x8832) NAME=[RecommendedExposureIndex] COUNT=(1) TYPE=[LONG] VALUE=[1600]",
|
|
||||||
"IFD=[Exif] ID=(0x9000) NAME=[ExifVersion] COUNT=(4) TYPE=[UNDEFINED] VALUE=[0230]",
|
|
||||||
"IFD=[Exif] ID=(0x9003) NAME=[DateTimeOriginal] COUNT=(20) TYPE=[ASCII] VALUE=[2017:12:02 08:18:50]",
|
|
||||||
"IFD=[Exif] ID=(0x9004) NAME=[DateTimeDigitized] COUNT=(20) TYPE=[ASCII] VALUE=[2017:12:02 08:18:50]",
|
|
||||||
"IFD=[Exif] ID=(0x9101) NAME=[ComponentsConfiguration] COUNT=(4) TYPE=[UNDEFINED] VALUE=[ComponentsConfiguration<ID=[YCBCR] BYTES=[1 2 3 0]>]",
|
|
||||||
"IFD=[Exif] ID=(0x9201) NAME=[ShutterSpeedValue] COUNT=(1) TYPE=[SRATIONAL] VALUE=[614400/65536]",
|
|
||||||
"IFD=[Exif] ID=(0x9202) NAME=[ApertureValue] COUNT=(1) TYPE=[RATIONAL] VALUE=[262144/65536]",
|
|
||||||
"IFD=[Exif] ID=(0x9204) NAME=[ExposureBiasValue] COUNT=(1) TYPE=[SRATIONAL] VALUE=[0/1]",
|
|
||||||
"IFD=[Exif] ID=(0x9207) NAME=[MeteringMode] COUNT=(1) TYPE=[SHORT] VALUE=[5]",
|
|
||||||
"IFD=[Exif] ID=(0x9209) NAME=[Flash] COUNT=(1) TYPE=[SHORT] VALUE=[16]",
|
|
||||||
"IFD=[Exif] ID=(0x920a) NAME=[FocalLength] COUNT=(1) TYPE=[RATIONAL] VALUE=[16/1]",
|
|
||||||
"IFD=[Exif] ID=(0x927c) NAME=[MakerNote] COUNT=(8152) TYPE=[UNDEFINED] VALUE=[MakerNote<TYPE-ID=[28 00 01 00 03 00 31 00 00 00 74 05 00 00 02 00 03 00 04 00]>]",
|
|
||||||
"IFD=[Exif] ID=(0x9286) NAME=[UserComment] COUNT=(264) TYPE=[UNDEFINED] VALUE=[UserComment<SIZE=(256) ENCODING=[UNDEFINED] V=[0 0 0 0 0 0 0 0]... LEN=(256)>]",
|
|
||||||
"IFD=[Exif] ID=(0x9290) NAME=[SubSecTime] COUNT=(3) TYPE=[ASCII] VALUE=[00]",
|
|
||||||
"IFD=[Exif] ID=(0x9291) NAME=[SubSecTimeOriginal] COUNT=(3) TYPE=[ASCII] VALUE=[00]",
|
|
||||||
"IFD=[Exif] ID=(0x9292) NAME=[SubSecTimeDigitized] COUNT=(3) TYPE=[ASCII] VALUE=[00]",
|
|
||||||
"IFD=[Exif] ID=(0xa000) NAME=[FlashpixVersion] COUNT=(4) TYPE=[UNDEFINED] VALUE=[0100]",
|
|
||||||
"IFD=[Exif] ID=(0xa001) NAME=[ColorSpace] COUNT=(1) TYPE=[SHORT] VALUE=[1]",
|
|
||||||
"IFD=[Exif] ID=(0xa002) NAME=[PixelXDimension] COUNT=(1) TYPE=[SHORT] VALUE=[3840]",
|
|
||||||
"IFD=[Exif] ID=(0xa003) NAME=[PixelYDimension] COUNT=(1) TYPE=[SHORT] VALUE=[2560]",
|
|
||||||
"IFD=[Exif] ID=(0xa005) NAME=[InteroperabilityTag] COUNT=(1) TYPE=[LONG] VALUE=[9326]",
|
|
||||||
"IFD=[Iop] ID=(0x0001) NAME=[InteroperabilityIndex] COUNT=(4) TYPE=[ASCII] VALUE=[R98]",
|
|
||||||
"IFD=[Iop] ID=(0x0002) NAME=[InteroperabilityVersion] COUNT=(4) TYPE=[UNDEFINED] VALUE=[0100]",
|
|
||||||
"IFD=[Exif] ID=(0xa20e) NAME=[FocalPlaneXResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[3840000/1461]",
|
|
||||||
"IFD=[Exif] ID=(0xa20f) NAME=[FocalPlaneYResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[2560000/972]",
|
|
||||||
"IFD=[Exif] ID=(0xa210) NAME=[FocalPlaneResolutionUnit] COUNT=(1) TYPE=[SHORT] VALUE=[2]",
|
|
||||||
"IFD=[Exif] ID=(0xa401) NAME=[CustomRendered] COUNT=(1) TYPE=[SHORT] VALUE=[0]",
|
|
||||||
"IFD=[Exif] ID=(0xa402) NAME=[ExposureMode] COUNT=(1) TYPE=[SHORT] VALUE=[0]",
|
|
||||||
"IFD=[Exif] ID=(0xa403) NAME=[WhiteBalance] COUNT=(1) TYPE=[SHORT] VALUE=[0]",
|
|
||||||
"IFD=[Exif] ID=(0xa406) NAME=[SceneCaptureType] COUNT=(1) TYPE=[SHORT] VALUE=[0]",
|
|
||||||
"IFD=[Exif] ID=(0xa430) NAME=[CameraOwnerName] COUNT=(1) TYPE=[ASCII] VALUE=[]",
|
|
||||||
"IFD=[Exif] ID=(0xa431) NAME=[BodySerialNumber] COUNT=(13) TYPE=[ASCII] VALUE=[063024020097]",
|
|
||||||
"IFD=[Exif] ID=(0xa432) NAME=[LensSpecification] COUNT=(4) TYPE=[RATIONAL] VALUE=[16/1]",
|
|
||||||
"IFD=[Exif] ID=(0xa434) NAME=[LensModel] COUNT=(22) TYPE=[ASCII] VALUE=[EF16-35mm f/4L IS USM]",
|
|
||||||
"IFD=[Exif] ID=(0xa435) NAME=[LensSerialNumber] COUNT=(11) TYPE=[ASCII] VALUE=[2400001068]",
|
|
||||||
"IFD=[IFD] ID=(0x8825) NAME=[GPSTag] COUNT=(1) TYPE=[LONG] VALUE=[9554]",
|
|
||||||
"IFD=[GPSInfo] ID=(0x0000) NAME=[GPSVersionID] COUNT=(4) TYPE=[BYTE] VALUE=[0x02]",
|
|
||||||
"IFD=[IFD] ID=(0x0103) NAME=[Compression] COUNT=(1) TYPE=[SHORT] VALUE=[6]",
|
|
||||||
"IFD=[IFD] ID=(0x011a) NAME=[XResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1]",
|
|
||||||
"IFD=[IFD] ID=(0x011b) NAME=[YResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1]",
|
|
||||||
"IFD=[IFD] ID=(0x0128) NAME=[ResolutionUnit] COUNT=(1) TYPE=[SHORT] VALUE=[2]",
|
|
||||||
}
|
|
||||||
|
|
||||||
if reflect.DeepEqual(tags, expected) == false {
|
expected := []string{
|
||||||
fmt.Printf("\n")
|
"IFD=[IFD] ID=(0x010f) NAME=[Make] COUNT=(6) TYPE=[ASCII] VALUE=[Canon]",
|
||||||
fmt.Printf("ACTUAL:\n")
|
"IFD=[IFD] ID=(0x0110) NAME=[Model] COUNT=(22) TYPE=[ASCII] VALUE=[Canon EOS 5D Mark III]",
|
||||||
fmt.Printf("\n")
|
"IFD=[IFD] ID=(0x0112) NAME=[Orientation] COUNT=(1) TYPE=[SHORT] VALUE=[1]",
|
||||||
|
"IFD=[IFD] ID=(0x011a) NAME=[XResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1]",
|
||||||
|
"IFD=[IFD] ID=(0x011b) NAME=[YResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1]",
|
||||||
|
"IFD=[IFD] ID=(0x0128) NAME=[ResolutionUnit] COUNT=(1) TYPE=[SHORT] VALUE=[2]",
|
||||||
|
"IFD=[IFD] ID=(0x0132) NAME=[DateTime] COUNT=(20) TYPE=[ASCII] VALUE=[2017:12:02 08:18:50]",
|
||||||
|
"IFD=[IFD] ID=(0x013b) NAME=[Artist] COUNT=(1) TYPE=[ASCII] VALUE=[]",
|
||||||
|
"IFD=[IFD] ID=(0x0213) NAME=[YCbCrPositioning] COUNT=(1) TYPE=[SHORT] VALUE=[2]",
|
||||||
|
"IFD=[IFD] ID=(0x8298) NAME=[Copyright] COUNT=(1) TYPE=[ASCII] VALUE=[]",
|
||||||
|
"IFD=[IFD] ID=(0x8769) NAME=[ExifTag] COUNT=(1) TYPE=[LONG] VALUE=[360]",
|
||||||
|
"IFD=[Exif] ID=(0x829a) NAME=[ExposureTime] COUNT=(1) TYPE=[RATIONAL] VALUE=[1/640]",
|
||||||
|
"IFD=[Exif] ID=(0x829d) NAME=[FNumber] COUNT=(1) TYPE=[RATIONAL] VALUE=[4/1]",
|
||||||
|
"IFD=[Exif] ID=(0x8822) NAME=[ExposureProgram] COUNT=(1) TYPE=[SHORT] VALUE=[4]",
|
||||||
|
"IFD=[Exif] ID=(0x8827) NAME=[ISOSpeedRatings] COUNT=(1) TYPE=[SHORT] VALUE=[1600]",
|
||||||
|
"IFD=[Exif] ID=(0x8830) NAME=[SensitivityType] COUNT=(1) TYPE=[SHORT] VALUE=[2]",
|
||||||
|
"IFD=[Exif] ID=(0x8832) NAME=[RecommendedExposureIndex] COUNT=(1) TYPE=[LONG] VALUE=[1600]",
|
||||||
|
"IFD=[Exif] ID=(0x9000) NAME=[ExifVersion] COUNT=(4) TYPE=[UNDEFINED] VALUE=[0230]",
|
||||||
|
"IFD=[Exif] ID=(0x9003) NAME=[DateTimeOriginal] COUNT=(20) TYPE=[ASCII] VALUE=[2017:12:02 08:18:50]",
|
||||||
|
"IFD=[Exif] ID=(0x9004) NAME=[DateTimeDigitized] COUNT=(20) TYPE=[ASCII] VALUE=[2017:12:02 08:18:50]",
|
||||||
|
"IFD=[Exif] ID=(0x9101) NAME=[ComponentsConfiguration] COUNT=(4) TYPE=[UNDEFINED] VALUE=[ComponentsConfiguration<ID=[YCBCR] BYTES=[1 2 3 0]>]",
|
||||||
|
"IFD=[Exif] ID=(0x9201) NAME=[ShutterSpeedValue] COUNT=(1) TYPE=[SRATIONAL] VALUE=[614400/65536]",
|
||||||
|
"IFD=[Exif] ID=(0x9202) NAME=[ApertureValue] COUNT=(1) TYPE=[RATIONAL] VALUE=[262144/65536]",
|
||||||
|
"IFD=[Exif] ID=(0x9204) NAME=[ExposureBiasValue] COUNT=(1) TYPE=[SRATIONAL] VALUE=[0/1]",
|
||||||
|
"IFD=[Exif] ID=(0x9207) NAME=[MeteringMode] COUNT=(1) TYPE=[SHORT] VALUE=[5]",
|
||||||
|
"IFD=[Exif] ID=(0x9209) NAME=[Flash] COUNT=(1) TYPE=[SHORT] VALUE=[16]",
|
||||||
|
"IFD=[Exif] ID=(0x920a) NAME=[FocalLength] COUNT=(1) TYPE=[RATIONAL] VALUE=[16/1]",
|
||||||
|
"IFD=[Exif] ID=(0x927c) NAME=[MakerNote] COUNT=(8152) TYPE=[UNDEFINED] VALUE=[MakerNote<TYPE-ID=[28 00 01 00 03 00 31 00 00 00 74 05 00 00 02 00 03 00 04 00]>]",
|
||||||
|
"IFD=[Exif] ID=(0x9286) NAME=[UserComment] COUNT=(264) TYPE=[UNDEFINED] VALUE=[UserComment<SIZE=(256) ENCODING=[UNDEFINED] V=[0 0 0 0 0 0 0 0]... LEN=(256)>]",
|
||||||
|
"IFD=[Exif] ID=(0x9290) NAME=[SubSecTime] COUNT=(3) TYPE=[ASCII] VALUE=[00]",
|
||||||
|
"IFD=[Exif] ID=(0x9291) NAME=[SubSecTimeOriginal] COUNT=(3) TYPE=[ASCII] VALUE=[00]",
|
||||||
|
"IFD=[Exif] ID=(0x9292) NAME=[SubSecTimeDigitized] COUNT=(3) TYPE=[ASCII] VALUE=[00]",
|
||||||
|
"IFD=[Exif] ID=(0xa000) NAME=[FlashpixVersion] COUNT=(4) TYPE=[UNDEFINED] VALUE=[0100]",
|
||||||
|
"IFD=[Exif] ID=(0xa001) NAME=[ColorSpace] COUNT=(1) TYPE=[SHORT] VALUE=[1]",
|
||||||
|
"IFD=[Exif] ID=(0xa002) NAME=[PixelXDimension] COUNT=(1) TYPE=[SHORT] VALUE=[3840]",
|
||||||
|
"IFD=[Exif] ID=(0xa003) NAME=[PixelYDimension] COUNT=(1) TYPE=[SHORT] VALUE=[2560]",
|
||||||
|
"IFD=[Exif] ID=(0xa005) NAME=[InteroperabilityTag] COUNT=(1) TYPE=[LONG] VALUE=[9326]",
|
||||||
|
"IFD=[Iop] ID=(0x0001) NAME=[InteroperabilityIndex] COUNT=(4) TYPE=[ASCII] VALUE=[R98]",
|
||||||
|
"IFD=[Iop] ID=(0x0002) NAME=[InteroperabilityVersion] COUNT=(4) TYPE=[UNDEFINED] VALUE=[0100]",
|
||||||
|
"IFD=[Exif] ID=(0xa20e) NAME=[FocalPlaneXResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[3840000/1461]",
|
||||||
|
"IFD=[Exif] ID=(0xa20f) NAME=[FocalPlaneYResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[2560000/972]",
|
||||||
|
"IFD=[Exif] ID=(0xa210) NAME=[FocalPlaneResolutionUnit] COUNT=(1) TYPE=[SHORT] VALUE=[2]",
|
||||||
|
"IFD=[Exif] ID=(0xa401) NAME=[CustomRendered] COUNT=(1) TYPE=[SHORT] VALUE=[0]",
|
||||||
|
"IFD=[Exif] ID=(0xa402) NAME=[ExposureMode] COUNT=(1) TYPE=[SHORT] VALUE=[0]",
|
||||||
|
"IFD=[Exif] ID=(0xa403) NAME=[WhiteBalance] COUNT=(1) TYPE=[SHORT] VALUE=[0]",
|
||||||
|
"IFD=[Exif] ID=(0xa406) NAME=[SceneCaptureType] COUNT=(1) TYPE=[SHORT] VALUE=[0]",
|
||||||
|
"IFD=[Exif] ID=(0xa430) NAME=[CameraOwnerName] COUNT=(1) TYPE=[ASCII] VALUE=[]",
|
||||||
|
"IFD=[Exif] ID=(0xa431) NAME=[BodySerialNumber] COUNT=(13) TYPE=[ASCII] VALUE=[063024020097]",
|
||||||
|
"IFD=[Exif] ID=(0xa432) NAME=[LensSpecification] COUNT=(4) TYPE=[RATIONAL] VALUE=[16/1]",
|
||||||
|
"IFD=[Exif] ID=(0xa434) NAME=[LensModel] COUNT=(22) TYPE=[ASCII] VALUE=[EF16-35mm f/4L IS USM]",
|
||||||
|
"IFD=[Exif] ID=(0xa435) NAME=[LensSerialNumber] COUNT=(11) TYPE=[ASCII] VALUE=[2400001068]",
|
||||||
|
"IFD=[IFD] ID=(0x8825) NAME=[GPSTag] COUNT=(1) TYPE=[LONG] VALUE=[9554]",
|
||||||
|
"IFD=[GPSInfo] ID=(0x0000) NAME=[GPSVersionID] COUNT=(4) TYPE=[BYTE] VALUE=[0x02]",
|
||||||
|
"IFD=[IFD] ID=(0x0103) NAME=[Compression] COUNT=(1) TYPE=[SHORT] VALUE=[6]",
|
||||||
|
"IFD=[IFD] ID=(0x011a) NAME=[XResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1]",
|
||||||
|
"IFD=[IFD] ID=(0x011b) NAME=[YResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1]",
|
||||||
|
"IFD=[IFD] ID=(0x0128) NAME=[ResolutionUnit] COUNT=(1) TYPE=[SHORT] VALUE=[2]",
|
||||||
|
}
|
||||||
|
|
||||||
for _, line := range tags {
|
if reflect.DeepEqual(tags, expected) == false {
|
||||||
fmt.Println(line)
|
fmt.Printf("\n")
|
||||||
}
|
fmt.Printf("ACTUAL:\n")
|
||||||
|
fmt.Printf("\n")
|
||||||
|
|
||||||
fmt.Printf("\n")
|
for _, line := range tags {
|
||||||
fmt.Printf("EXPECTED:\n")
|
fmt.Println(line)
|
||||||
fmt.Printf("\n")
|
}
|
||||||
|
|
||||||
for _, line := range expected {
|
fmt.Printf("\n")
|
||||||
fmt.Println(line)
|
fmt.Printf("EXPECTED:\n")
|
||||||
}
|
fmt.Printf("\n")
|
||||||
|
|
||||||
fmt.Printf("\n")
|
for _, line := range expected {
|
||||||
|
fmt.Println(line)
|
||||||
|
}
|
||||||
|
|
||||||
t.Fatalf("tags not correct.")
|
fmt.Printf("\n")
|
||||||
}
|
|
||||||
|
t.Fatalf("tags not correct.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSearchFileAndExtractExif(t *testing.T) {
|
func TestSearchFileAndExtractExif(t *testing.T) {
|
||||||
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
||||||
|
|
||||||
// Returns a slice starting with the EXIF data and going to the end of the
|
// Returns a slice starting with the EXIF data and going to the end of the
|
||||||
// image.
|
// image.
|
||||||
rawExif, err := SearchFileAndExtractExif(filepath)
|
rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
if bytes.Compare(rawExif[:len(testExifData)], testExifData) != 0 {
|
if bytes.Compare(rawExif[:len(testExifData)], testExifData) != 0 {
|
||||||
t.Fatalf("found EXIF data not correct")
|
t.Fatalf("found EXIF data not correct")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSearchAndExtractExif(t *testing.T) {
|
func TestSearchAndExtractExif(t *testing.T) {
|
||||||
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
||||||
|
|
||||||
imageData, err := ioutil.ReadFile(filepath)
|
imageData, err := ioutil.ReadFile(filepath)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
rawExif, err := SearchAndExtractExif(imageData)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
rawExif, err := SearchAndExtractExif(imageData)
|
if bytes.Compare(rawExif[:len(testExifData)], testExifData) != 0 {
|
||||||
log.PanicIf(err)
|
t.Fatalf("found EXIF data not correct")
|
||||||
|
}
|
||||||
if bytes.Compare(rawExif[:len(testExifData)], testExifData) != 0 {
|
|
||||||
t.Fatalf("found EXIF data not correct")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCollect(t *testing.T) {
|
func TestCollect(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if state := recover(); state != nil {
|
if state := recover(); state != nil {
|
||||||
err := log.Wrap(state.(error))
|
err := log.Wrap(state.(error))
|
||||||
log.PrintErrorf(err, "Exif failure.")
|
log.PrintErrorf(err, "Exif failure.")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
||||||
|
|
||||||
rawExif, err := SearchFileAndExtractExif(filepath)
|
rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
_, index, err := Collect(rawExif)
|
ti := NewTagIndex()
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
rootIfd := index.RootIfd
|
_, index, err := Collect(ti, rawExif)
|
||||||
ifds := index.Ifds
|
log.PanicIf(err)
|
||||||
tree := index.Tree
|
|
||||||
lookup := index.Lookup
|
|
||||||
|
|
||||||
if rootIfd.Offset != uint32(0x0008) {
|
rootIfd := index.RootIfd
|
||||||
t.Fatalf("Root-IFD not correct: (0x%04d).", rootIfd.Offset)
|
ifds := index.Ifds
|
||||||
} else if rootIfd.Id != 0 {
|
tree := index.Tree
|
||||||
t.Fatalf("Root-IFD does not have the right ID: (%d)", rootIfd.Id)
|
lookup := index.Lookup
|
||||||
} else if tree[0] != rootIfd {
|
|
||||||
t.Fatalf("Root-IFD is not indexed properly.")
|
|
||||||
} else if len(ifds) != 5 {
|
|
||||||
t.Fatalf("The IFD list is not the right size: (%d)", len(ifds))
|
|
||||||
} else if len(tree) != 5 {
|
|
||||||
t.Fatalf("The IFD tree is not the right size: (%d)", len(tree))
|
|
||||||
} else if len(lookup) != 4 {
|
|
||||||
t.Fatalf("The IFD lookup is not the right size: (%d)", len(lookup))
|
|
||||||
}
|
|
||||||
|
|
||||||
if rootIfd.NextIfdOffset != 0x2c54 {
|
if rootIfd.Offset != uint32(0x0008) {
|
||||||
t.Fatalf("Root IFD does not continue correctly: (0x%04x)", rootIfd.NextIfdOffset)
|
t.Fatalf("Root-IFD not correct: (0x%04d).", rootIfd.Offset)
|
||||||
} else if rootIfd.NextIfd.Offset != rootIfd.NextIfdOffset {
|
} else if rootIfd.Id != 0 {
|
||||||
t.Fatalf("Root IFD neighbor object does not have the right offset: (0x%04x != 0x%04x)", rootIfd.NextIfd.Offset, rootIfd.NextIfdOffset)
|
t.Fatalf("Root-IFD does not have the right ID: (%d)", rootIfd.Id)
|
||||||
} else if rootIfd.NextIfd.NextIfdOffset != 0 {
|
} else if tree[0] != rootIfd {
|
||||||
t.Fatalf("Root IFD chain not terminated correctly (1).")
|
t.Fatalf("Root-IFD is not indexed properly.")
|
||||||
} else if rootIfd.NextIfd.NextIfd != nil {
|
} else if len(ifds) != 5 {
|
||||||
t.Fatalf("Root IFD chain not terminated correctly (2).")
|
t.Fatalf("The IFD list is not the right size: (%d)", len(ifds))
|
||||||
}
|
} else if len(tree) != 5 {
|
||||||
|
t.Fatalf("The IFD tree is not the right size: (%d)", len(tree))
|
||||||
|
} else if len(lookup) != 4 {
|
||||||
|
t.Fatalf("The IFD lookup is not the right size: (%d)", len(lookup))
|
||||||
|
}
|
||||||
|
|
||||||
if rootIfd.Name != IfdStandard {
|
if rootIfd.NextIfdOffset != 0x2c54 {
|
||||||
t.Fatalf("Root IFD is not labeled correctly: [%s]", rootIfd.Name)
|
t.Fatalf("Root IFD does not continue correctly: (0x%04x)", rootIfd.NextIfdOffset)
|
||||||
} else if rootIfd.NextIfd.Name != IfdStandard {
|
} else if rootIfd.NextIfd.Offset != rootIfd.NextIfdOffset {
|
||||||
t.Fatalf("Root IFD sibling is not labeled correctly: [%s]", rootIfd.Name)
|
t.Fatalf("Root IFD neighbor object does not have the right offset: (0x%04x != 0x%04x)", rootIfd.NextIfd.Offset, rootIfd.NextIfdOffset)
|
||||||
} else if rootIfd.Children[0].Name != IfdExif {
|
} else if rootIfd.NextIfd.NextIfdOffset != 0 {
|
||||||
t.Fatalf("Root IFD child (0) is not labeled correctly: [%s]", rootIfd.Children[0].Name)
|
t.Fatalf("Root IFD chain not terminated correctly (1).")
|
||||||
} else if rootIfd.Children[1].Name != IfdGps {
|
} else if rootIfd.NextIfd.NextIfd != nil {
|
||||||
t.Fatalf("Root IFD child (1) is not labeled correctly: [%s]", rootIfd.Children[1].Name)
|
t.Fatalf("Root IFD chain not terminated correctly (2).")
|
||||||
} else if rootIfd.Children[0].Children[0].Name != IfdIop {
|
}
|
||||||
t.Fatalf("Exif IFD child is not an IOP IFD: [%s]", rootIfd.Children[0].Children[0].Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
rootIi, _ := IfdIdOrFail("", IfdStandard)
|
if rootIfd.Name != IfdStandard {
|
||||||
|
t.Fatalf("Root IFD is not labeled correctly: [%s]", rootIfd.Name)
|
||||||
|
} else if rootIfd.NextIfd.Name != IfdStandard {
|
||||||
|
t.Fatalf("Root IFD sibling is not labeled correctly: [%s]", rootIfd.Name)
|
||||||
|
} else if rootIfd.Children[0].Name != IfdExif {
|
||||||
|
t.Fatalf("Root IFD child (0) is not labeled correctly: [%s]", rootIfd.Children[0].Name)
|
||||||
|
} else if rootIfd.Children[1].Name != IfdGps {
|
||||||
|
t.Fatalf("Root IFD child (1) is not labeled correctly: [%s]", rootIfd.Children[1].Name)
|
||||||
|
} else if rootIfd.Children[0].Children[0].Name != IfdIop {
|
||||||
|
t.Fatalf("Exif IFD child is not an IOP IFD: [%s]", rootIfd.Children[0].Children[0].Name)
|
||||||
|
}
|
||||||
|
|
||||||
if lookup[rootIi][0].Name != IfdStandard {
|
rootIi, _ := IfdIdOrFail("", IfdStandard)
|
||||||
t.Fatalf("Lookup for standard IFD not correct.")
|
|
||||||
} else if lookup[rootIi][1].Name != IfdStandard {
|
|
||||||
t.Fatalf("Lookup for standard IFD not correct.")
|
|
||||||
}
|
|
||||||
|
|
||||||
exifIi, _ := IfdIdOrFail(IfdStandard, IfdExif)
|
if lookup[rootIi][0].Name != IfdStandard {
|
||||||
|
t.Fatalf("Lookup for standard IFD not correct.")
|
||||||
|
} else if lookup[rootIi][1].Name != IfdStandard {
|
||||||
|
t.Fatalf("Lookup for standard IFD not correct.")
|
||||||
|
}
|
||||||
|
|
||||||
if lookup[exifIi][0].Name != IfdExif {
|
exifIi, _ := IfdIdOrFail(IfdStandard, IfdExif)
|
||||||
t.Fatalf("Lookup for EXIF IFD not correct.")
|
|
||||||
}
|
|
||||||
|
|
||||||
gpsIi, _ := IfdIdOrFail(IfdStandard, IfdGps)
|
if lookup[exifIi][0].Name != IfdExif {
|
||||||
|
t.Fatalf("Lookup for EXIF IFD not correct.")
|
||||||
|
}
|
||||||
|
|
||||||
if lookup[gpsIi][0].Name != IfdGps {
|
gpsIi, _ := IfdIdOrFail(IfdStandard, IfdGps)
|
||||||
t.Fatalf("Lookup for EXIF IFD not correct.")
|
|
||||||
}
|
|
||||||
|
|
||||||
iopIi, _ := IfdIdOrFail(IfdExif, IfdIop)
|
if lookup[gpsIi][0].Name != IfdGps {
|
||||||
|
t.Fatalf("Lookup for EXIF IFD not correct.")
|
||||||
|
}
|
||||||
|
|
||||||
if lookup[iopIi][0].Name != IfdIop {
|
iopIi, _ := IfdIdOrFail(IfdExif, IfdIop)
|
||||||
t.Fatalf("Lookup for EXIF IFD not correct.")
|
|
||||||
}
|
|
||||||
|
|
||||||
foundExif := 0
|
if lookup[iopIi][0].Name != IfdIop {
|
||||||
foundGps := 0
|
t.Fatalf("Lookup for EXIF IFD not correct.")
|
||||||
for _, ite := range lookup[rootIi][0].Entries {
|
}
|
||||||
if ite.ChildIfdName == IfdExif {
|
|
||||||
foundExif++
|
|
||||||
|
|
||||||
name, found := IfdTagNameWithId(IfdStandard, ite.TagId)
|
foundExif := 0
|
||||||
if found != true {
|
foundGps := 0
|
||||||
t.Fatalf("could not find tag-ID for EXIF IFD")
|
for _, ite := range lookup[rootIi][0].Entries {
|
||||||
} else if name != IfdExif {
|
if ite.ChildIfdName == IfdExif {
|
||||||
t.Fatalf("EXIF IFD tag-ID mismatch: (0x%02x) [%s] != [%s]", ite.TagId, name, IfdExif)
|
foundExif++
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ite.ChildIfdName == IfdGps {
|
name, found := IfdTagNameWithId(IfdStandard, ite.TagId)
|
||||||
foundGps++
|
if found != true {
|
||||||
|
t.Fatalf("could not find tag-ID for EXIF IFD")
|
||||||
|
} else if name != IfdExif {
|
||||||
|
t.Fatalf("EXIF IFD tag-ID mismatch: (0x%02x) [%s] != [%s]", ite.TagId, name, IfdExif)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
name, found := IfdTagNameWithId(IfdStandard, ite.TagId)
|
if ite.ChildIfdName == IfdGps {
|
||||||
if found != true {
|
foundGps++
|
||||||
t.Fatalf("could not find tag-ID for GPS IFD")
|
|
||||||
} else if name != IfdGps {
|
|
||||||
t.Fatalf("GPS IFD tag-ID mismatch: (0x%02x) [%s] != [%s]", ite.TagId, name, IfdGps)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if foundExif != 1 {
|
name, found := IfdTagNameWithId(IfdStandard, ite.TagId)
|
||||||
t.Fatalf("Exactly one EXIF IFD tag wasn't found: (%d)", foundExif)
|
if found != true {
|
||||||
} else if foundGps != 1 {
|
t.Fatalf("could not find tag-ID for GPS IFD")
|
||||||
t.Fatalf("Exactly one GPS IFD tag wasn't found: (%d)", foundGps)
|
} else if name != IfdGps {
|
||||||
}
|
t.Fatalf("GPS IFD tag-ID mismatch: (0x%02x) [%s] != [%s]", ite.TagId, name, IfdGps)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foundIop := 0
|
if foundExif != 1 {
|
||||||
for _, ite := range lookup[exifIi][0].Entries {
|
t.Fatalf("Exactly one EXIF IFD tag wasn't found: (%d)", foundExif)
|
||||||
if ite.ChildIfdName == IfdIop {
|
} else if foundGps != 1 {
|
||||||
foundIop++
|
t.Fatalf("Exactly one GPS IFD tag wasn't found: (%d)", foundGps)
|
||||||
|
}
|
||||||
|
|
||||||
name, found := IfdTagNameWithId(IfdExif, ite.TagId)
|
foundIop := 0
|
||||||
if found != true {
|
for _, ite := range lookup[exifIi][0].Entries {
|
||||||
t.Fatalf("could not find tag-ID for IOP IFD")
|
if ite.ChildIfdName == IfdIop {
|
||||||
} else if name != IfdIop {
|
foundIop++
|
||||||
t.Fatalf("IOP IFD tag-ID mismatch: (0x%02x) [%s] != [%s]", ite.TagId, name, IfdIop)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if foundIop != 1 {
|
name, found := IfdTagNameWithId(IfdExif, ite.TagId)
|
||||||
t.Fatalf("Exactly one IOP IFD tag wasn't found: (%d)", foundIop)
|
if found != true {
|
||||||
}
|
t.Fatalf("could not find tag-ID for IOP IFD")
|
||||||
|
} else if name != IfdIop {
|
||||||
|
t.Fatalf("IOP IFD tag-ID mismatch: (0x%02x) [%s] != [%s]", ite.TagId, name, IfdIop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if foundIop != 1 {
|
||||||
|
t.Fatalf("Exactly one IOP IFD tag wasn't found: (%d)", foundIop)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseExifHeader(t *testing.T) {
|
func TestParseExifHeader(t *testing.T) {
|
||||||
eh, err := ParseExifHeader(testExifData)
|
eh, err := ParseExifHeader(testExifData)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
if eh.ByteOrder != binary.LittleEndian {
|
if eh.ByteOrder != binary.LittleEndian {
|
||||||
t.Fatalf("Byte-order of EXIF header not correct.")
|
t.Fatalf("Byte-order of EXIF header not correct.")
|
||||||
} else if eh.FirstIfdOffset != 0x8 {
|
} else if eh.FirstIfdOffset != 0x8 {
|
||||||
t.Fatalf("First IFD offset not correct.")
|
t.Fatalf("First IFD offset not correct.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExif_BuildAndParseExifHeader(t *testing.T) {
|
func TestExif_BuildAndParseExifHeader(t *testing.T) {
|
||||||
headerBytes, err := BuildExifHeader(TestDefaultByteOrder, 0x11223344)
|
headerBytes, err := BuildExifHeader(TestDefaultByteOrder, 0x11223344)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
eh, err := ParseExifHeader(headerBytes)
|
eh, err := ParseExifHeader(headerBytes)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
if eh.ByteOrder != TestDefaultByteOrder {
|
if eh.ByteOrder != TestDefaultByteOrder {
|
||||||
t.Fatalf("Byte-order of EXIF header not correct.")
|
t.Fatalf("Byte-order of EXIF header not correct.")
|
||||||
} else if eh.FirstIfdOffset != 0x11223344 {
|
} else if eh.FirstIfdOffset != 0x11223344 {
|
||||||
t.Fatalf("First IFD offset not correct.")
|
t.Fatalf("First IFD offset not correct.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleBuildExifHeader() {
|
func ExampleBuildExifHeader() {
|
||||||
headerBytes, err := BuildExifHeader(TestDefaultByteOrder, 0x11223344)
|
headerBytes, err := BuildExifHeader(TestDefaultByteOrder, 0x11223344)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
eh, err := ParseExifHeader(headerBytes)
|
eh, err := ParseExifHeader(headerBytes)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
fmt.Printf("%v\n", eh)
|
fmt.Printf("%v\n", eh)
|
||||||
// Output: ExifHeader<BYTE-ORDER=[BigEndian] FIRST-IFD-OFFSET=(0x11223344)>
|
// Output: ExifHeader<BYTE-ORDER=[BigEndian] FIRST-IFD-OFFSET=(0x11223344)>
|
||||||
}
|
}
|
||||||
|
1508
ifd_builder.go
1508
ifd_builder.go
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -12,7 +12,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestAdd(t *testing.T) {
|
func TestAdd(t *testing.T) {
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
|
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
bt := &BuilderTag{
|
bt := &BuilderTag{
|
||||||
ii: RootIi,
|
ii: RootIi,
|
||||||
@ -98,8 +99,10 @@ func TestAdd(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSetNextIb(t *testing.T) {
|
func TestSetNextIb(t *testing.T) {
|
||||||
ib1 := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
ib2 := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
|
||||||
|
ib1 := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
ib2 := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
if ib1.nextIb != nil {
|
if ib1.nextIb != nil {
|
||||||
t.Fatalf("Next-IFD for IB1 not initially terminal.")
|
t.Fatalf("Next-IFD for IB1 not initially terminal.")
|
||||||
@ -117,7 +120,8 @@ func TestSetNextIb(t *testing.T) {
|
|||||||
|
|
||||||
func TestAddChildIb(t *testing.T) {
|
func TestAddChildIb(t *testing.T) {
|
||||||
|
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
|
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
bt := &BuilderTag{
|
bt := &BuilderTag{
|
||||||
ii: RootIi,
|
ii: RootIi,
|
||||||
@ -131,7 +135,7 @@ func TestAddChildIb(t *testing.T) {
|
|||||||
|
|
||||||
exifIi, _ := IfdIdOrFail(IfdStandard, IfdExif)
|
exifIi, _ := IfdIdOrFail(IfdStandard, IfdExif)
|
||||||
|
|
||||||
ibChild := NewIfdBuilder(exifIi, TestDefaultByteOrder)
|
ibChild := NewIfdBuilder(ti, exifIi, TestDefaultByteOrder)
|
||||||
err = ib.AddChildIb(ibChild)
|
err = ib.AddChildIb(ibChild)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
@ -164,7 +168,8 @@ func TestAddTagsFromExisting(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
|
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
entries := make([]*IfdTagEntry, 3)
|
entries := make([]*IfdTagEntry, 3)
|
||||||
|
|
||||||
@ -192,8 +197,9 @@ func TestAddTagsFromExisting(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ifd := &Ifd{
|
ifd := &Ifd{
|
||||||
Ii: RootIi,
|
Ii: RootIi,
|
||||||
Entries: entries,
|
Entries: entries,
|
||||||
|
tagIndex: ti,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := ib.AddTagsFromExisting(ifd, nil, nil, nil)
|
err := ib.AddTagsFromExisting(ifd, nil, nil, nil)
|
||||||
@ -211,7 +217,8 @@ func TestAddTagsFromExisting(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAddTagsFromExisting__Includes(t *testing.T) {
|
func TestAddTagsFromExisting__Includes(t *testing.T) {
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
|
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
entries := make([]*IfdTagEntry, 3)
|
entries := make([]*IfdTagEntry, 3)
|
||||||
|
|
||||||
@ -235,8 +242,9 @@ func TestAddTagsFromExisting__Includes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ifd := &Ifd{
|
ifd := &Ifd{
|
||||||
Ii: RootIi,
|
Ii: RootIi,
|
||||||
Entries: entries,
|
Entries: entries,
|
||||||
|
tagIndex: ti,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := ib.AddTagsFromExisting(ifd, nil, []uint16{0x33}, nil)
|
err := ib.AddTagsFromExisting(ifd, nil, []uint16{0x33}, nil)
|
||||||
@ -250,7 +258,8 @@ func TestAddTagsFromExisting__Includes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAddTagsFromExisting__Excludes(t *testing.T) {
|
func TestAddTagsFromExisting__Excludes(t *testing.T) {
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
|
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
entries := make([]*IfdTagEntry, 3)
|
entries := make([]*IfdTagEntry, 3)
|
||||||
|
|
||||||
@ -274,8 +283,9 @@ func TestAddTagsFromExisting__Excludes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ifd := &Ifd{
|
ifd := &Ifd{
|
||||||
Ii: RootIi,
|
Ii: RootIi,
|
||||||
Entries: entries,
|
Entries: entries,
|
||||||
|
tagIndex: ti,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := ib.AddTagsFromExisting(ifd, nil, nil, []uint16{0x11})
|
err := ib.AddTagsFromExisting(ifd, nil, nil, []uint16{0x11})
|
||||||
@ -289,7 +299,8 @@ func TestAddTagsFromExisting__Excludes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFindN_First_1(t *testing.T) {
|
func TestFindN_First_1(t *testing.T) {
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
|
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
bt := &BuilderTag{
|
bt := &BuilderTag{
|
||||||
ii: RootIi,
|
ii: RootIi,
|
||||||
@ -339,7 +350,8 @@ func TestFindN_First_1(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFindN_First_2_1Returned(t *testing.T) {
|
func TestFindN_First_2_1Returned(t *testing.T) {
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
|
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
bt := &BuilderTag{
|
bt := &BuilderTag{
|
||||||
ii: RootIi,
|
ii: RootIi,
|
||||||
@ -389,7 +401,8 @@ func TestFindN_First_2_1Returned(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFindN_First_2_2Returned(t *testing.T) {
|
func TestFindN_First_2_2Returned(t *testing.T) {
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
|
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
bt := &BuilderTag{
|
bt := &BuilderTag{
|
||||||
ii: RootIi,
|
ii: RootIi,
|
||||||
@ -466,7 +479,8 @@ func TestFindN_First_2_2Returned(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFindN_Middle_WithDuplicates(t *testing.T) {
|
func TestFindN_Middle_WithDuplicates(t *testing.T) {
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
|
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
bt := &BuilderTag{
|
bt := &BuilderTag{
|
||||||
ii: RootIi,
|
ii: RootIi,
|
||||||
@ -546,7 +560,8 @@ func TestFindN_Middle_WithDuplicates(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFindN_Middle_NoDuplicates(t *testing.T) {
|
func TestFindN_Middle_NoDuplicates(t *testing.T) {
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
|
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
bt := &BuilderTag{
|
bt := &BuilderTag{
|
||||||
ii: RootIi,
|
ii: RootIi,
|
||||||
@ -606,7 +621,8 @@ func TestFindN_Middle_NoDuplicates(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFindN_Miss(t *testing.T) {
|
func TestFindN_Miss(t *testing.T) {
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
|
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
found, err := ib.FindN(0x11, 1)
|
found, err := ib.FindN(0x11, 1)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
@ -617,7 +633,8 @@ func TestFindN_Miss(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFind_Hit(t *testing.T) {
|
func TestFind_Hit(t *testing.T) {
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
|
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
bt := &BuilderTag{
|
bt := &BuilderTag{
|
||||||
ii: RootIi,
|
ii: RootIi,
|
||||||
@ -675,7 +692,8 @@ func TestFind_Hit(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFind_Miss(t *testing.T) {
|
func TestFind_Miss(t *testing.T) {
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
|
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
bt := &BuilderTag{
|
bt := &BuilderTag{
|
||||||
ii: RootIi,
|
ii: RootIi,
|
||||||
@ -726,7 +744,8 @@ func TestFind_Miss(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestReplace(t *testing.T) {
|
func TestReplace(t *testing.T) {
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
|
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
bt := &BuilderTag{
|
bt := &BuilderTag{
|
||||||
ii: RootIi,
|
ii: RootIi,
|
||||||
@ -788,7 +807,8 @@ func TestReplace(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestReplaceN(t *testing.T) {
|
func TestReplaceN(t *testing.T) {
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
|
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
bt := &BuilderTag{
|
bt := &BuilderTag{
|
||||||
ii: RootIi,
|
ii: RootIi,
|
||||||
@ -850,7 +870,8 @@ func TestReplaceN(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteFirst(t *testing.T) {
|
func TestDeleteFirst(t *testing.T) {
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
|
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
bt := &BuilderTag{
|
bt := &BuilderTag{
|
||||||
ii: RootIi,
|
ii: RootIi,
|
||||||
@ -946,7 +967,8 @@ func TestDeleteFirst(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteN(t *testing.T) {
|
func TestDeleteN(t *testing.T) {
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
|
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
bt := &BuilderTag{
|
bt := &BuilderTag{
|
||||||
ii: RootIi,
|
ii: RootIi,
|
||||||
@ -1042,7 +1064,8 @@ func TestDeleteN(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteN_Two(t *testing.T) {
|
func TestDeleteN_Two(t *testing.T) {
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
|
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
bt := &BuilderTag{
|
bt := &BuilderTag{
|
||||||
ii: RootIi,
|
ii: RootIi,
|
||||||
@ -1122,7 +1145,8 @@ func TestDeleteN_Two(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteAll(t *testing.T) {
|
func TestDeleteAll(t *testing.T) {
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
|
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
bt := &BuilderTag{
|
bt := &BuilderTag{
|
||||||
ii: RootIi,
|
ii: RootIi,
|
||||||
@ -1216,7 +1240,9 @@ func Test_IfdBuilder_CreateIfdBuilderFromExistingChain(t *testing.T) {
|
|||||||
rawExif, err := SearchFileAndExtractExif(filepath)
|
rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
_, index, err := Collect(rawExif)
|
ti := NewTagIndex()
|
||||||
|
|
||||||
|
_, index, err := Collect(ti, rawExif)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
itevr := NewIfdTagEntryValueResolver(rawExif, index.RootIfd.ByteOrder)
|
itevr := NewIfdTagEntryValueResolver(rawExif, index.RootIfd.ByteOrder)
|
||||||
@ -1296,7 +1322,9 @@ func Test_IfdBuilder_CreateIfdBuilderFromExistingChain_RealData(t *testing.T) {
|
|||||||
|
|
||||||
// Decode from binary.
|
// Decode from binary.
|
||||||
|
|
||||||
_, originalIndex, err := Collect(rawExif)
|
ti := NewTagIndex()
|
||||||
|
|
||||||
|
_, originalIndex, err := Collect(ti, rawExif)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
originalThumbnailData, err := originalIndex.RootIfd.NextIfd.Thumbnail()
|
originalThumbnailData, err := originalIndex.RootIfd.NextIfd.Thumbnail()
|
||||||
@ -1316,7 +1344,7 @@ func Test_IfdBuilder_CreateIfdBuilderFromExistingChain_RealData(t *testing.T) {
|
|||||||
|
|
||||||
// Parse again.
|
// Parse again.
|
||||||
|
|
||||||
_, recoveredIndex, err := Collect(updatedExif)
|
_, recoveredIndex, err := Collect(ti, updatedExif)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
recoveredTags := recoveredIndex.RootIfd.DumpTags()
|
recoveredTags := recoveredIndex.RootIfd.DumpTags()
|
||||||
@ -1400,143 +1428,145 @@ func Test_IfdBuilder_CreateIfdBuilderFromExistingChain_RealData(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_IfdBuilder_CreateIfdBuilderFromExistingChain_RealData_WithUpdate(t *testing.T) {
|
// func Test_IfdBuilder_CreateIfdBuilderFromExistingChain_RealData_WithUpdate(t *testing.T) {
|
||||||
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
// filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
||||||
|
|
||||||
rawExif, err := SearchFileAndExtractExif(filepath)
|
// rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
log.PanicIf(err)
|
// log.PanicIf(err)
|
||||||
|
|
||||||
// Decode from binary.
|
// // Decode from binary.
|
||||||
|
|
||||||
_, originalIndex, err := Collect(rawExif)
|
// ti := NewTagIndex()
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
originalThumbnailData, err := originalIndex.RootIfd.NextIfd.Thumbnail()
|
// _, originalIndex, err := Collect(ti, rawExif)
|
||||||
log.PanicIf(err)
|
// log.PanicIf(err)
|
||||||
|
|
||||||
originalTags := originalIndex.RootIfd.DumpTags()
|
// originalThumbnailData, err := originalIndex.RootIfd.NextIfd.Thumbnail()
|
||||||
|
// log.PanicIf(err)
|
||||||
|
|
||||||
// Encode back to binary.
|
// originalTags := originalIndex.RootIfd.DumpTags()
|
||||||
|
|
||||||
ibe := NewIfdByteEncoder()
|
// // Encode back to binary.
|
||||||
|
|
||||||
itevr := NewIfdTagEntryValueResolver(rawExif, originalIndex.RootIfd.ByteOrder)
|
// ibe := NewIfdByteEncoder()
|
||||||
rootIb := NewIfdBuilderFromExistingChain(originalIndex.RootIfd, itevr)
|
|
||||||
|
|
||||||
// Update a tag,.
|
// itevr := NewIfdTagEntryValueResolver(rawExif, originalIndex.RootIfd.ByteOrder)
|
||||||
|
// rootIb := NewIfdBuilderFromExistingChain(originalIndex.RootIfd, itevr)
|
||||||
|
|
||||||
exifBt, err := rootIb.FindTagWithName("ExifTag")
|
// // Update a tag,.
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
ucBt, err := exifBt.value.Ib().FindTagWithName("UserComment")
|
// exifBt, err := rootIb.FindTagWithName("ExifTag")
|
||||||
log.PanicIf(err)
|
// log.PanicIf(err)
|
||||||
|
|
||||||
uc := TagUnknownType_9298_UserComment{
|
// ucBt, err := exifBt.value.Ib().FindTagWithName("UserComment")
|
||||||
EncodingType: TagUnknownType_9298_UserComment_Encoding_ASCII,
|
// log.PanicIf(err)
|
||||||
EncodingBytes: []byte("TEST COMMENT"),
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ucBt.SetValue(rootIb.byteOrder, uc)
|
// uc := TagUnknownType_9298_UserComment{
|
||||||
log.PanicIf(err)
|
// EncodingType: TagUnknownType_9298_UserComment_Encoding_ASCII,
|
||||||
|
// EncodingBytes: []byte("TEST COMMENT"),
|
||||||
|
// }
|
||||||
|
|
||||||
// Encode.
|
// err = ucBt.SetValue(rootIb.byteOrder, uc)
|
||||||
|
// log.PanicIf(err)
|
||||||
|
|
||||||
updatedExif, err := ibe.EncodeToExif(rootIb)
|
// // Encode.
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
// Parse again.
|
// updatedExif, err := ibe.EncodeToExif(rootIb)
|
||||||
|
// log.PanicIf(err)
|
||||||
|
|
||||||
_, recoveredIndex, err := Collect(updatedExif)
|
// // Parse again.
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
recoveredTags := recoveredIndex.RootIfd.DumpTags()
|
// _, recoveredIndex, err := Collect(ti, updatedExif)
|
||||||
|
// log.PanicIf(err)
|
||||||
|
|
||||||
recoveredThumbnailData, err := recoveredIndex.RootIfd.NextIfd.Thumbnail()
|
// recoveredTags := recoveredIndex.RootIfd.DumpTags()
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
// Check the thumbnail.
|
// recoveredThumbnailData, err := recoveredIndex.RootIfd.NextIfd.Thumbnail()
|
||||||
|
// log.PanicIf(err)
|
||||||
|
|
||||||
if bytes.Compare(recoveredThumbnailData, originalThumbnailData) != 0 {
|
// // Check the thumbnail.
|
||||||
t.Fatalf("recovered thumbnail does not match original")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate that all of the same IFDs were presented.
|
// if bytes.Compare(recoveredThumbnailData, originalThumbnailData) != 0 {
|
||||||
|
// t.Fatalf("recovered thumbnail does not match original")
|
||||||
|
// }
|
||||||
|
|
||||||
originalIfdTags := make([][2]interface{}, 0)
|
// // Validate that all of the same IFDs were presented.
|
||||||
for _, ite := range originalTags {
|
|
||||||
if ite.ChildIfdName != "" {
|
|
||||||
originalIfdTags = append(originalIfdTags, [2]interface{}{ite.Ii, ite.TagId})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
recoveredIfdTags := make([][2]interface{}, 0)
|
// originalIfdTags := make([][2]interface{}, 0)
|
||||||
for _, ite := range recoveredTags {
|
// for _, ite := range originalTags {
|
||||||
if ite.ChildIfdName != "" {
|
// if ite.ChildIfdName != "" {
|
||||||
recoveredIfdTags = append(recoveredIfdTags, [2]interface{}{ite.Ii, ite.TagId})
|
// originalIfdTags = append(originalIfdTags, [2]interface{}{ite.Ii, ite.TagId})
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
if reflect.DeepEqual(recoveredIfdTags, originalIfdTags) != true {
|
// recoveredIfdTags := make([][2]interface{}, 0)
|
||||||
fmt.Printf("Original IFD tags:\n\n")
|
// for _, ite := range recoveredTags {
|
||||||
|
// if ite.ChildIfdName != "" {
|
||||||
|
// recoveredIfdTags = append(recoveredIfdTags, [2]interface{}{ite.Ii, ite.TagId})
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
for i, x := range originalIfdTags {
|
// if reflect.DeepEqual(recoveredIfdTags, originalIfdTags) != true {
|
||||||
fmt.Printf(" %02d %v\n", i, x)
|
// fmt.Printf("Original IFD tags:\n\n")
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("\nRecovered IFD tags:\n\n")
|
// for i, x := range originalIfdTags {
|
||||||
|
// fmt.Printf(" %02d %v\n", i, x)
|
||||||
|
// }
|
||||||
|
|
||||||
for i, x := range recoveredIfdTags {
|
// fmt.Printf("\nRecovered IFD tags:\n\n")
|
||||||
fmt.Printf(" %02d %v\n", i, x)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("\n")
|
// for i, x := range recoveredIfdTags {
|
||||||
|
// fmt.Printf(" %02d %v\n", i, x)
|
||||||
|
// }
|
||||||
|
|
||||||
t.Fatalf("Recovered IFD tags are not correct.")
|
// fmt.Printf("\n")
|
||||||
}
|
|
||||||
|
|
||||||
// Validate that all of the tags owned by the IFDs were presented. Note
|
// t.Fatalf("Recovered IFD tags are not correct.")
|
||||||
// that the thumbnail tags are not kept but only produced on the fly, which
|
// }
|
||||||
// is why we check it above.
|
|
||||||
|
|
||||||
if len(recoveredTags) != len(originalTags) {
|
// // Validate that all of the tags owned by the IFDs were presented. Note
|
||||||
t.Fatalf("Recovered tag-count does not match original.")
|
// // that the thumbnail tags are not kept but only produced on the fly, which
|
||||||
}
|
// // is why we check it above.
|
||||||
|
|
||||||
for i, recoveredIte := range recoveredTags {
|
// if len(recoveredTags) != len(originalTags) {
|
||||||
if recoveredIte.ChildIfdName != "" {
|
// t.Fatalf("Recovered tag-count does not match original.")
|
||||||
continue
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
originalIte := originalTags[i]
|
// for i, recoveredIte := range recoveredTags {
|
||||||
|
// if recoveredIte.ChildIfdName != "" {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
|
||||||
if recoveredIte.Ii != originalIte.Ii {
|
// originalIte := originalTags[i]
|
||||||
t.Fatalf("IfdIdentify not as expected: %s != %s ITE=%s", recoveredIte.Ii, originalIte.Ii, recoveredIte)
|
|
||||||
} else if recoveredIte.TagId != originalIte.TagId {
|
|
||||||
t.Fatalf("Tag-ID not as expected: %d != %d ITE=%s", recoveredIte.TagId, originalIte.TagId, recoveredIte)
|
|
||||||
} else if recoveredIte.TagType != originalIte.TagType {
|
|
||||||
t.Fatalf("Tag-type not as expected: %d != %d ITE=%s", recoveredIte.TagType, originalIte.TagType, recoveredIte)
|
|
||||||
}
|
|
||||||
|
|
||||||
originalValueBytes, err := originalIte.ValueBytes(originalIndex.RootIfd.addressableData, originalIndex.RootIfd.ByteOrder)
|
// if recoveredIte.Ii != originalIte.Ii {
|
||||||
log.PanicIf(err)
|
// t.Fatalf("IfdIdentify not as expected: %s != %s ITE=%s", recoveredIte.Ii, originalIte.Ii, recoveredIte)
|
||||||
|
// } else if recoveredIte.TagId != originalIte.TagId {
|
||||||
|
// t.Fatalf("Tag-ID not as expected: %d != %d ITE=%s", recoveredIte.TagId, originalIte.TagId, recoveredIte)
|
||||||
|
// } else if recoveredIte.TagType != originalIte.TagType {
|
||||||
|
// t.Fatalf("Tag-type not as expected: %d != %d ITE=%s", recoveredIte.TagType, originalIte.TagType, recoveredIte)
|
||||||
|
// }
|
||||||
|
|
||||||
recoveredValueBytes, err := recoveredIte.ValueBytes(recoveredIndex.RootIfd.addressableData, recoveredIndex.RootIfd.ByteOrder)
|
// originalValueBytes, err := originalIte.ValueBytes(originalIndex.RootIfd.addressableData, originalIndex.RootIfd.ByteOrder)
|
||||||
log.PanicIf(err)
|
// log.PanicIf(err)
|
||||||
|
|
||||||
if recoveredIte.TagId == 0x9286 {
|
// recoveredValueBytes, err := recoveredIte.ValueBytes(recoveredIndex.RootIfd.addressableData, recoveredIndex.RootIfd.ByteOrder)
|
||||||
expectedValueBytes := make([]byte, 0)
|
// log.PanicIf(err)
|
||||||
|
|
||||||
expectedValueBytes = append(expectedValueBytes, []byte{'A', 'S', 'C', 'I', 'I', 0, 0, 0}...)
|
// if recoveredIte.TagId == 0x9286 {
|
||||||
expectedValueBytes = append(expectedValueBytes, []byte("TEST COMMENT")...)
|
// expectedValueBytes := make([]byte, 0)
|
||||||
|
|
||||||
if bytes.Compare(recoveredValueBytes, expectedValueBytes) != 0 {
|
// expectedValueBytes = append(expectedValueBytes, []byte{'A', 'S', 'C', 'I', 'I', 0, 0, 0}...)
|
||||||
t.Fatalf("Recovered UserComment does not have the right value: %v != %v", recoveredValueBytes, expectedValueBytes)
|
// expectedValueBytes = append(expectedValueBytes, []byte("TEST COMMENT")...)
|
||||||
}
|
|
||||||
} else if bytes.Compare(recoveredValueBytes, originalValueBytes) != 0 {
|
// if bytes.Compare(recoveredValueBytes, expectedValueBytes) != 0 {
|
||||||
t.Fatalf("bytes of tag content not correct: %v != %v ITE=%s", recoveredValueBytes, originalValueBytes, recoveredIte)
|
// t.Fatalf("Recovered UserComment does not have the right value: %v != %v", recoveredValueBytes, expectedValueBytes)
|
||||||
}
|
// }
|
||||||
}
|
// } else if bytes.Compare(recoveredValueBytes, originalValueBytes) != 0 {
|
||||||
}
|
// t.Fatalf("bytes of tag content not correct: %v != %v ITE=%s", recoveredValueBytes, originalValueBytes, recoveredIte)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
func ExampleIfd_Thumbnail() {
|
func ExampleIfd_Thumbnail() {
|
||||||
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
||||||
@ -1544,7 +1574,9 @@ func ExampleIfd_Thumbnail() {
|
|||||||
rawExif, err := SearchFileAndExtractExif(filepath)
|
rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
_, index, err := Collect(rawExif)
|
ti := NewTagIndex()
|
||||||
|
|
||||||
|
_, index, err := Collect(ti, rawExif)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
thumbnailData, err := index.RootIfd.NextIfd.Thumbnail()
|
thumbnailData, err := index.RootIfd.NextIfd.Thumbnail()
|
||||||
@ -1560,7 +1592,9 @@ func ExampleBuilderTag_SetValue() {
|
|||||||
rawExif, err := SearchFileAndExtractExif(filepath)
|
rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
_, index, err := Collect(rawExif)
|
ti := NewTagIndex()
|
||||||
|
|
||||||
|
_, index, err := Collect(ti, rawExif)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
// Create builder.
|
// Create builder.
|
||||||
@ -1599,11 +1633,14 @@ func ExampleBuilderTag_SetValue() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_IfdBuilder_CreateIfdBuilderWithExistingIfd(t *testing.T) {
|
func Test_IfdBuilder_CreateIfdBuilderWithExistingIfd(t *testing.T) {
|
||||||
|
ti := NewTagIndex()
|
||||||
|
|
||||||
tagId := IfdTagIdWithIdentityOrFail(GpsIi)
|
tagId := IfdTagIdWithIdentityOrFail(GpsIi)
|
||||||
|
|
||||||
parentIfd := &Ifd{
|
parentIfd := &Ifd{
|
||||||
Ii: RootIi,
|
Ii: RootIi,
|
||||||
Name: IfdStandard,
|
Name: IfdStandard,
|
||||||
|
tagIndex: ti,
|
||||||
}
|
}
|
||||||
|
|
||||||
ifd := &Ifd{
|
ifd := &Ifd{
|
||||||
@ -1612,6 +1649,7 @@ func Test_IfdBuilder_CreateIfdBuilderWithExistingIfd(t *testing.T) {
|
|||||||
ByteOrder: TestDefaultByteOrder,
|
ByteOrder: TestDefaultByteOrder,
|
||||||
Offset: 0x123,
|
Offset: 0x123,
|
||||||
ParentIfd: parentIfd,
|
ParentIfd: parentIfd,
|
||||||
|
tagIndex: ti,
|
||||||
}
|
}
|
||||||
|
|
||||||
ib := NewIfdBuilderWithExistingIfd(ifd)
|
ib := NewIfdBuilderWithExistingIfd(ifd)
|
||||||
@ -1628,7 +1666,12 @@ func Test_IfdBuilder_CreateIfdBuilderWithExistingIfd(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNewStandardBuilderTag_OneUnit(t *testing.T) {
|
func TestNewStandardBuilderTag_OneUnit(t *testing.T) {
|
||||||
bt := NewStandardBuilderTag(ExifIi, uint16(0x8833), TestDefaultByteOrder, []uint32{uint32(0x1234)})
|
ti := NewTagIndex()
|
||||||
|
|
||||||
|
it, err := ti.Get(ExifIi, uint16(0x8833))
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
bt := NewStandardBuilderTag(ExifIi, it, TestDefaultByteOrder, []uint32{uint32(0x1234)})
|
||||||
|
|
||||||
if bt.ii != ExifIi {
|
if bt.ii != ExifIi {
|
||||||
t.Fatalf("II in BuilderTag not correct")
|
t.Fatalf("II in BuilderTag not correct")
|
||||||
@ -1640,21 +1683,12 @@ func TestNewStandardBuilderTag_OneUnit(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNewStandardBuilderTag_TwoUnits(t *testing.T) {
|
func TestNewStandardBuilderTag_TwoUnits(t *testing.T) {
|
||||||
bt := NewStandardBuilderTag(ExifIi, uint16(0x8833), TestDefaultByteOrder, []uint32{uint32(0x1234), uint32(0x5678)})
|
ti := NewTagIndex()
|
||||||
|
|
||||||
if bt.ii != ExifIi {
|
it, err := ti.Get(ExifIi, uint16(0x8833))
|
||||||
t.Fatalf("II in BuilderTag not correct")
|
log.PanicIf(err)
|
||||||
} else if bt.tagId != 0x8833 {
|
|
||||||
t.Fatalf("tag-ID not correct")
|
|
||||||
} else if bytes.Compare(bt.value.Bytes(), []byte{
|
|
||||||
0x0, 0x0, 0x12, 0x34,
|
|
||||||
0x0, 0x0, 0x56, 0x78}) != 0 {
|
|
||||||
t.Fatalf("value not correct")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewStandardBuilderTagWithName(t *testing.T) {
|
bt := NewStandardBuilderTag(ExifIi, it, TestDefaultByteOrder, []uint32{uint32(0x1234), uint32(0x5678)})
|
||||||
bt := NewStandardBuilderTagWithName(ExifIi, "ISOSpeed", TestDefaultByteOrder, []uint32{uint32(0x1234), uint32(0x5678)})
|
|
||||||
|
|
||||||
if bt.ii != ExifIi {
|
if bt.ii != ExifIi {
|
||||||
t.Fatalf("II in BuilderTag not correct")
|
t.Fatalf("II in BuilderTag not correct")
|
||||||
@ -1668,7 +1702,8 @@ func TestNewStandardBuilderTagWithName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAddStandardWithName(t *testing.T) {
|
func TestAddStandardWithName(t *testing.T) {
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ti := NewTagIndex()
|
||||||
|
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
err := ib.AddStandardWithName("ProcessingSoftware", "some software")
|
err := ib.AddStandardWithName("ProcessingSoftware", "some software")
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
1659
ifd_enumerate.go
1659
ifd_enumerate.go
File diff suppressed because it is too large
Load Diff
@ -1,526 +1,550 @@
|
|||||||
package exif
|
package exif
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"path"
|
"bytes"
|
||||||
"testing"
|
"fmt"
|
||||||
"bytes"
|
"path"
|
||||||
"fmt"
|
"reflect"
|
||||||
"reflect"
|
"testing"
|
||||||
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
||||||
"github.com/dsoprea/go-logging"
|
"github.com/dsoprea/go-logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIfdTagEntry_ValueBytes(t *testing.T) {
|
func TestIfdTagEntry_ValueBytes(t *testing.T) {
|
||||||
byteOrder := binary.BigEndian
|
byteOrder := binary.BigEndian
|
||||||
ve := NewValueEncoder(byteOrder)
|
ve := NewValueEncoder(byteOrder)
|
||||||
|
|
||||||
original := []byte("original text")
|
original := []byte("original text")
|
||||||
|
|
||||||
ed, err := ve.encodeBytes(original)
|
ed, err := ve.encodeBytes(original)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
// Now, pass the raw encoded value as if it was the entire addressable area
|
// Now, pass the raw encoded value as if it was the entire addressable area
|
||||||
// and provide an offset of 0 as if it was a real block of data and this
|
// and provide an offset of 0 as if it was a real block of data and this
|
||||||
// value happened to be recorded at the beginning.
|
// value happened to be recorded at the beginning.
|
||||||
|
|
||||||
ite := IfdTagEntry{
|
ite := IfdTagEntry{
|
||||||
TagType: TypeByte,
|
TagType: TypeByte,
|
||||||
UnitCount: uint32(len(original)),
|
UnitCount: uint32(len(original)),
|
||||||
ValueOffset: 0,
|
ValueOffset: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
decodedBytes, err := ite.ValueBytes(ed.Encoded, byteOrder)
|
decodedBytes, err := ite.ValueBytes(ed.Encoded, byteOrder)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
if bytes.Compare(decodedBytes, original) != 0 {
|
if bytes.Compare(decodedBytes, original) != 0 {
|
||||||
t.Fatalf("Bytes not decoded correctly.")
|
t.Fatalf("Bytes not decoded correctly.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfdTagEntry_ValueBytes_RealData(t *testing.T) {
|
func TestIfdTagEntry_ValueBytes_RealData(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if state := recover(); state != nil {
|
if state := recover(); state != nil {
|
||||||
err := log.Wrap(state.(error))
|
err := log.Wrap(state.(error))
|
||||||
log.PrintErrorf(err, "Test failure.")
|
log.PrintErrorf(err, "Test failure.")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
||||||
|
|
||||||
rawExif, err := SearchFileAndExtractExif(filepath)
|
rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
eh, index, err := Collect(rawExif)
|
ti := NewTagIndex()
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
var ite *IfdTagEntry
|
eh, index, err := Collect(ti, rawExif)
|
||||||
for _, thisIte := range index.RootIfd.Entries {
|
log.PanicIf(err)
|
||||||
if thisIte.TagId == 0x0110 {
|
|
||||||
ite = thisIte
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ite == nil {
|
var ite *IfdTagEntry
|
||||||
t.Fatalf("Tag not found.")
|
for _, thisIte := range index.RootIfd.Entries {
|
||||||
}
|
if thisIte.TagId == 0x0110 {
|
||||||
|
ite = thisIte
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
addressableData := rawExif[ExifAddressableAreaStart:]
|
if ite == nil {
|
||||||
decodedBytes, err := ite.ValueBytes(addressableData, eh.ByteOrder)
|
t.Fatalf("Tag not found.")
|
||||||
log.PanicIf(err)
|
}
|
||||||
|
|
||||||
expected := []byte("Canon EOS 5D Mark III")
|
addressableData := rawExif[ExifAddressableAreaStart:]
|
||||||
expected = append(expected, 0)
|
decodedBytes, err := ite.ValueBytes(addressableData, eh.ByteOrder)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
if len(decodedBytes) != int(ite.UnitCount) {
|
expected := []byte("Canon EOS 5D Mark III")
|
||||||
t.Fatalf("Decoded bytes not the right count.")
|
expected = append(expected, 0)
|
||||||
} else if bytes.Compare(decodedBytes, expected) != 0 {
|
|
||||||
t.Fatalf("Decoded bytes not correct.")
|
if len(decodedBytes) != int(ite.UnitCount) {
|
||||||
}
|
t.Fatalf("Decoded bytes not the right count.")
|
||||||
|
} else if bytes.Compare(decodedBytes, expected) != 0 {
|
||||||
|
t.Fatalf("Decoded bytes not correct.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfdTagEntry_Resolver_ValueBytes(t *testing.T) {
|
func TestIfdTagEntry_Resolver_ValueBytes(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if state := recover(); state != nil {
|
if state := recover(); state != nil {
|
||||||
err := log.Wrap(state.(error))
|
err := log.Wrap(state.(error))
|
||||||
log.PrintErrorf(err, "Test failure.")
|
log.PrintErrorf(err, "Test failure.")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
||||||
|
|
||||||
rawExif, err := SearchFileAndExtractExif(filepath)
|
rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
eh, index, err := Collect(rawExif)
|
ti := NewTagIndex()
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
var ite *IfdTagEntry
|
eh, index, err := Collect(ti, rawExif)
|
||||||
for _, thisIte := range index.RootIfd.Entries {
|
log.PanicIf(err)
|
||||||
if thisIte.TagId == 0x0110 {
|
|
||||||
ite = thisIte
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ite == nil {
|
var ite *IfdTagEntry
|
||||||
t.Fatalf("Tag not found.")
|
for _, thisIte := range index.RootIfd.Entries {
|
||||||
}
|
if thisIte.TagId == 0x0110 {
|
||||||
|
ite = thisIte
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
itevr := NewIfdTagEntryValueResolver(rawExif, eh.ByteOrder)
|
if ite == nil {
|
||||||
|
t.Fatalf("Tag not found.")
|
||||||
|
}
|
||||||
|
|
||||||
decodedBytes, err := itevr.ValueBytes(ite)
|
itevr := NewIfdTagEntryValueResolver(rawExif, eh.ByteOrder)
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
expected := []byte("Canon EOS 5D Mark III")
|
decodedBytes, err := itevr.ValueBytes(ite)
|
||||||
expected = append(expected, 0)
|
log.PanicIf(err)
|
||||||
|
|
||||||
if len(decodedBytes) != int(ite.UnitCount) {
|
expected := []byte("Canon EOS 5D Mark III")
|
||||||
t.Fatalf("Decoded bytes not the right count.")
|
expected = append(expected, 0)
|
||||||
} else if bytes.Compare(decodedBytes, expected) != 0 {
|
|
||||||
t.Fatalf("Decoded bytes not correct.")
|
if len(decodedBytes) != int(ite.UnitCount) {
|
||||||
}
|
t.Fatalf("Decoded bytes not the right count.")
|
||||||
|
} else if bytes.Compare(decodedBytes, expected) != 0 {
|
||||||
|
t.Fatalf("Decoded bytes not correct.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfdTagEntry_Resolver_ValueBytes__Unknown_Field_And_Nonroot_Ifd(t *testing.T) {
|
func TestIfdTagEntry_Resolver_ValueBytes__Unknown_Field_And_Nonroot_Ifd(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if state := recover(); state != nil {
|
if state := recover(); state != nil {
|
||||||
err := log.Wrap(state.(error))
|
err := log.Wrap(state.(error))
|
||||||
log.PrintErrorf(err, "Test failure.")
|
log.PrintErrorf(err, "Test failure.")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
||||||
|
|
||||||
rawExif, err := SearchFileAndExtractExif(filepath)
|
rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
eh, index, err := Collect(rawExif)
|
ti := NewTagIndex()
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
ii, _ := IfdIdOrFail(IfdStandard, IfdExif)
|
eh, index, err := Collect(ti, rawExif)
|
||||||
ifdExif := index.Lookup[ii][0]
|
log.PanicIf(err)
|
||||||
|
|
||||||
var ite *IfdTagEntry
|
ii, _ := IfdIdOrFail(IfdStandard, IfdExif)
|
||||||
for _, thisIte := range ifdExif.Entries {
|
ifdExif := index.Lookup[ii][0]
|
||||||
if thisIte.TagId == 0x9000 {
|
|
||||||
ite = thisIte
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ite == nil {
|
var ite *IfdTagEntry
|
||||||
t.Fatalf("Tag not found.")
|
for _, thisIte := range ifdExif.Entries {
|
||||||
}
|
if thisIte.TagId == 0x9000 {
|
||||||
|
ite = thisIte
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
itevr := NewIfdTagEntryValueResolver(rawExif, eh.ByteOrder)
|
if ite == nil {
|
||||||
|
t.Fatalf("Tag not found.")
|
||||||
|
}
|
||||||
|
|
||||||
decodedBytes, err := itevr.ValueBytes(ite)
|
itevr := NewIfdTagEntryValueResolver(rawExif, eh.ByteOrder)
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
expected := []byte { '0', '2', '3', '0' }
|
decodedBytes, err := itevr.ValueBytes(ite)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
if len(decodedBytes) != int(ite.UnitCount) {
|
expected := []byte{'0', '2', '3', '0'}
|
||||||
t.Fatalf("Decoded bytes not the right count.")
|
|
||||||
} else if bytes.Compare(decodedBytes, expected) != 0 {
|
if len(decodedBytes) != int(ite.UnitCount) {
|
||||||
t.Fatalf("Recovered unknown value is not correct.")
|
t.Fatalf("Decoded bytes not the right count.")
|
||||||
}
|
} else if bytes.Compare(decodedBytes, expected) != 0 {
|
||||||
|
t.Fatalf("Recovered unknown value is not correct.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Ifd_FindTagWithId_Hit(t *testing.T) {
|
func Test_Ifd_FindTagWithId_Hit(t *testing.T) {
|
||||||
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
||||||
|
|
||||||
rawExif, err := SearchFileAndExtractExif(filepath)
|
rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
_, index, err := Collect(rawExif)
|
ti := NewTagIndex()
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
ifd := index.RootIfd
|
_, index, err := Collect(ti, rawExif)
|
||||||
results, err := ifd.FindTagWithId(0x011b)
|
log.PanicIf(err)
|
||||||
|
|
||||||
if len(results) != 1 {
|
ifd := index.RootIfd
|
||||||
t.Fatalf("Exactly one result was not found: (%d)", len(results))
|
results, err := ifd.FindTagWithId(0x011b)
|
||||||
} else if results[0].TagId != 0x011b {
|
|
||||||
t.Fatalf("The result was not expected: %v", results[0])
|
if len(results) != 1 {
|
||||||
}
|
t.Fatalf("Exactly one result was not found: (%d)", len(results))
|
||||||
|
} else if results[0].TagId != 0x011b {
|
||||||
|
t.Fatalf("The result was not expected: %v", results[0])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Ifd_FindTagWithId_Miss(t *testing.T) {
|
func Test_Ifd_FindTagWithId_Miss(t *testing.T) {
|
||||||
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
||||||
|
|
||||||
rawExif, err := SearchFileAndExtractExif(filepath)
|
rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
_, index, err := Collect(rawExif)
|
ti := NewTagIndex()
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
ifd := index.RootIfd
|
_, index, err := Collect(ti, rawExif)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
_, err = ifd.FindTagWithId(0xffff)
|
ifd := index.RootIfd
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("Expected error for not-found tag.")
|
_, err = ifd.FindTagWithId(0xffff)
|
||||||
} else if log.Is(err, ErrTagNotFound) == false {
|
if err == nil {
|
||||||
log.Panic(err)
|
t.Fatalf("Expected error for not-found tag.")
|
||||||
}
|
} else if log.Is(err, ErrTagNotFound) == false {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Ifd_FindTagWithName_Hit(t *testing.T) {
|
func Test_Ifd_FindTagWithName_Hit(t *testing.T) {
|
||||||
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
||||||
|
|
||||||
rawExif, err := SearchFileAndExtractExif(filepath)
|
rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
_, index, err := Collect(rawExif)
|
ti := NewTagIndex()
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
ifd := index.RootIfd
|
_, index, err := Collect(ti, rawExif)
|
||||||
results, err := ifd.FindTagWithName("YResolution")
|
log.PanicIf(err)
|
||||||
|
|
||||||
if len(results) != 1 {
|
ifd := index.RootIfd
|
||||||
t.Fatalf("Exactly one result was not found: (%d)", len(results))
|
results, err := ifd.FindTagWithName("YResolution")
|
||||||
} else if results[0].TagId != 0x011b {
|
|
||||||
t.Fatalf("The result was not expected: %v", results[0])
|
if len(results) != 1 {
|
||||||
}
|
t.Fatalf("Exactly one result was not found: (%d)", len(results))
|
||||||
|
} else if results[0].TagId != 0x011b {
|
||||||
|
t.Fatalf("The result was not expected: %v", results[0])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Ifd_FindTagWithName_Miss(t *testing.T) {
|
func Test_Ifd_FindTagWithName_Miss(t *testing.T) {
|
||||||
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
||||||
|
|
||||||
rawExif, err := SearchFileAndExtractExif(filepath)
|
rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
_, index, err := Collect(rawExif)
|
ti := NewTagIndex()
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
ifd := index.RootIfd
|
_, index, err := Collect(ti, rawExif)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
_, err = ifd.FindTagWithName("PlanarConfiguration")
|
ifd := index.RootIfd
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("Expected error for not-found tag.")
|
_, err = ifd.FindTagWithName("PlanarConfiguration")
|
||||||
} else if log.Is(err, ErrTagNotFound) == false {
|
if err == nil {
|
||||||
log.Panic(err)
|
t.Fatalf("Expected error for not-found tag.")
|
||||||
}
|
} else if log.Is(err, ErrTagNotFound) == false {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Ifd_FindTagWithName_NonStandard(t *testing.T) {
|
func Test_Ifd_FindTagWithName_NonStandard(t *testing.T) {
|
||||||
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
||||||
|
|
||||||
rawExif, err := SearchFileAndExtractExif(filepath)
|
rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
_, index, err := Collect(rawExif)
|
ti := NewTagIndex()
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
ifd := index.RootIfd
|
_, index, err := Collect(ti, rawExif)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
_, err = ifd.FindTagWithName("GeorgeNotAtHome")
|
ifd := index.RootIfd
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("Expected error for not-found tag.")
|
_, err = ifd.FindTagWithName("GeorgeNotAtHome")
|
||||||
} else if log.Is(err, ErrTagNotStandard) == false {
|
if err == nil {
|
||||||
log.Panic(err)
|
t.Fatalf("Expected error for not-found tag.")
|
||||||
}
|
} else if log.Is(err, ErrTagNotStandard) == false {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Ifd_Thumbnail(t *testing.T) {
|
func Test_Ifd_Thumbnail(t *testing.T) {
|
||||||
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
||||||
|
|
||||||
rawExif, err := SearchFileAndExtractExif(filepath)
|
rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
_, index, err := Collect(rawExif)
|
ti := NewTagIndex()
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
ifd := index.RootIfd
|
_, index, err := Collect(ti, rawExif)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
if ifd.NextIfd == nil {
|
ifd := index.RootIfd
|
||||||
t.Fatalf("There is no IFD1.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// The thumbnail is in IFD1 (The second root IFD).
|
if ifd.NextIfd == nil {
|
||||||
actual, err := ifd.NextIfd.Thumbnail()
|
t.Fatalf("There is no IFD1.")
|
||||||
log.PanicIf(err)
|
}
|
||||||
|
|
||||||
expectedFilepath := path.Join(assetsPath, "NDM_8901.jpg.thumbnail")
|
// The thumbnail is in IFD1 (The second root IFD).
|
||||||
|
actual, err := ifd.NextIfd.Thumbnail()
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
expected, err := ioutil.ReadFile(expectedFilepath)
|
expectedFilepath := path.Join(assetsPath, "NDM_8901.jpg.thumbnail")
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
if bytes.Compare(actual, expected) != 0 {
|
expected, err := ioutil.ReadFile(expectedFilepath)
|
||||||
t.Fatalf("thumbnail not correct")
|
log.PanicIf(err)
|
||||||
}
|
|
||||||
|
if bytes.Compare(actual, expected) != 0 {
|
||||||
|
t.Fatalf("thumbnail not correct")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfd_GpsInfo(t *testing.T) {
|
func TestIfd_GpsInfo(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if state := recover(); state != nil {
|
if state := recover(); state != nil {
|
||||||
err := log.Wrap(state.(error))
|
err := log.Wrap(state.(error))
|
||||||
log.PrintErrorf(err, "Test failure.")
|
log.PrintErrorf(err, "Test failure.")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
filepath := path.Join(assetsPath, "gps.jpg")
|
filepath := path.Join(assetsPath, "gps.jpg")
|
||||||
|
|
||||||
rawExif, err := SearchFileAndExtractExif(filepath)
|
rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
_, index, err := Collect(rawExif)
|
ti := NewTagIndex()
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
ifd, err := index.RootIfd.ChildWithIfdIdentity(GpsIi)
|
_, index, err := Collect(ti, rawExif)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
gi, err := ifd.GpsInfo()
|
ifd, err := index.RootIfd.ChildWithIfdIdentity(GpsIi)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
if gi.Latitude.Orientation != 'N' || gi.Latitude.Degrees != 26 || gi.Latitude.Minutes != 35 || gi.Latitude.Seconds != 12 {
|
gi, err := ifd.GpsInfo()
|
||||||
t.Fatalf("latitude not correct")
|
log.PanicIf(err)
|
||||||
} else if gi.Longitude.Orientation != 'W' || gi.Longitude.Degrees != 80 || gi.Longitude.Minutes != 3 || gi.Longitude.Seconds != 13 {
|
|
||||||
t.Fatalf("longitude not correct")
|
if gi.Latitude.Orientation != 'N' || gi.Latitude.Degrees != 26 || gi.Latitude.Minutes != 35 || gi.Latitude.Seconds != 12 {
|
||||||
} else if gi.Altitude != 0 {
|
t.Fatalf("latitude not correct")
|
||||||
t.Fatalf("altitude not correct")
|
} else if gi.Longitude.Orientation != 'W' || gi.Longitude.Degrees != 80 || gi.Longitude.Minutes != 3 || gi.Longitude.Seconds != 13 {
|
||||||
} else if gi.Timestamp.Unix() != 1524964977 {
|
t.Fatalf("longitude not correct")
|
||||||
t.Fatalf("timestamp not correct")
|
} else if gi.Altitude != 0 {
|
||||||
} else if gi.Altitude != 0 {
|
t.Fatalf("altitude not correct")
|
||||||
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 TestIfd_EnumerateTagsRecursively(t *testing.T) {
|
func TestIfd_EnumerateTagsRecursively(t *testing.T) {
|
||||||
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
||||||
|
|
||||||
rawExif, err := SearchFileAndExtractExif(filepath)
|
rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
_, index, err := Collect(rawExif)
|
ti := NewTagIndex()
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
|
_, index, err := Collect(ti, rawExif)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
collected := make([][2]interface{}, 0)
|
collected := make([][2]interface{}, 0)
|
||||||
|
|
||||||
cb := func(ifd *Ifd, ite *IfdTagEntry) error {
|
cb := func(ifd *Ifd, ite *IfdTagEntry) error {
|
||||||
item := [2]interface{} {
|
item := [2]interface{}{
|
||||||
ifd.Ii.IfdName,
|
ifd.Ii.IfdName,
|
||||||
int(ite.TagId),
|
int(ite.TagId),
|
||||||
}
|
}
|
||||||
|
|
||||||
collected = append(collected, item)
|
collected = append(collected, item)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = index.RootIfd.EnumerateTagsRecursively(cb)
|
err = index.RootIfd.EnumerateTagsRecursively(cb)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
expected := [][2]interface{} {
|
expected := [][2]interface{}{
|
||||||
[2]interface{} { "IFD", 0x010f },
|
[2]interface{}{"IFD", 0x010f},
|
||||||
[2]interface{} { "IFD", 0x0110 },
|
[2]interface{}{"IFD", 0x0110},
|
||||||
[2]interface{} { "IFD", 0x0112 },
|
[2]interface{}{"IFD", 0x0112},
|
||||||
[2]interface{} { "IFD", 0x011a },
|
[2]interface{}{"IFD", 0x011a},
|
||||||
[2]interface{} { "IFD", 0x011b },
|
[2]interface{}{"IFD", 0x011b},
|
||||||
[2]interface{} { "IFD", 0x0128 },
|
[2]interface{}{"IFD", 0x0128},
|
||||||
[2]interface{} { "IFD", 0x0132 },
|
[2]interface{}{"IFD", 0x0132},
|
||||||
[2]interface{} { "IFD", 0x013b },
|
[2]interface{}{"IFD", 0x013b},
|
||||||
[2]interface{} { "IFD", 0x0213 },
|
[2]interface{}{"IFD", 0x0213},
|
||||||
[2]interface{} { "IFD", 0x8298 },
|
[2]interface{}{"IFD", 0x8298},
|
||||||
[2]interface{} { "Exif", 0x829a },
|
[2]interface{}{"Exif", 0x829a},
|
||||||
[2]interface{} { "Exif", 0x829d },
|
[2]interface{}{"Exif", 0x829d},
|
||||||
[2]interface{} { "Exif", 0x8822 },
|
[2]interface{}{"Exif", 0x8822},
|
||||||
[2]interface{} { "Exif", 0x8827 },
|
[2]interface{}{"Exif", 0x8827},
|
||||||
[2]interface{} { "Exif", 0x8830 },
|
[2]interface{}{"Exif", 0x8830},
|
||||||
[2]interface{} { "Exif", 0x8832 },
|
[2]interface{}{"Exif", 0x8832},
|
||||||
[2]interface{} { "Exif", 0x9000 },
|
[2]interface{}{"Exif", 0x9000},
|
||||||
[2]interface{} { "Exif", 0x9003 },
|
[2]interface{}{"Exif", 0x9003},
|
||||||
[2]interface{} { "Exif", 0x9004 },
|
[2]interface{}{"Exif", 0x9004},
|
||||||
[2]interface{} { "Exif", 0x9101 },
|
[2]interface{}{"Exif", 0x9101},
|
||||||
[2]interface{} { "Exif", 0x9201 },
|
[2]interface{}{"Exif", 0x9201},
|
||||||
[2]interface{} { "Exif", 0x9202 },
|
[2]interface{}{"Exif", 0x9202},
|
||||||
[2]interface{} { "Exif", 0x9204 },
|
[2]interface{}{"Exif", 0x9204},
|
||||||
[2]interface{} { "Exif", 0x9207 },
|
[2]interface{}{"Exif", 0x9207},
|
||||||
[2]interface{} { "Exif", 0x9209 },
|
[2]interface{}{"Exif", 0x9209},
|
||||||
[2]interface{} { "Exif", 0x920a },
|
[2]interface{}{"Exif", 0x920a},
|
||||||
[2]interface{} { "Exif", 0x927c },
|
[2]interface{}{"Exif", 0x927c},
|
||||||
[2]interface{} { "Exif", 0x9286 },
|
[2]interface{}{"Exif", 0x9286},
|
||||||
[2]interface{} { "Exif", 0x9290 },
|
[2]interface{}{"Exif", 0x9290},
|
||||||
[2]interface{} { "Exif", 0x9291 },
|
[2]interface{}{"Exif", 0x9291},
|
||||||
[2]interface{} { "Exif", 0x9292 },
|
[2]interface{}{"Exif", 0x9292},
|
||||||
[2]interface{} { "Exif", 0xa000 },
|
[2]interface{}{"Exif", 0xa000},
|
||||||
[2]interface{} { "Exif", 0xa001 },
|
[2]interface{}{"Exif", 0xa001},
|
||||||
[2]interface{} { "Exif", 0xa002 },
|
[2]interface{}{"Exif", 0xa002},
|
||||||
[2]interface{} { "Exif", 0xa003 },
|
[2]interface{}{"Exif", 0xa003},
|
||||||
[2]interface{} { "Iop", 0x0001 },
|
[2]interface{}{"Iop", 0x0001},
|
||||||
[2]interface{} { "Iop", 0x0002 },
|
[2]interface{}{"Iop", 0x0002},
|
||||||
[2]interface{} { "Exif", 0xa20e },
|
[2]interface{}{"Exif", 0xa20e},
|
||||||
[2]interface{} { "Exif", 0xa20f },
|
[2]interface{}{"Exif", 0xa20f},
|
||||||
[2]interface{} { "Exif", 0xa210 },
|
[2]interface{}{"Exif", 0xa210},
|
||||||
[2]interface{} { "Exif", 0xa401 },
|
[2]interface{}{"Exif", 0xa401},
|
||||||
[2]interface{} { "Exif", 0xa402 },
|
[2]interface{}{"Exif", 0xa402},
|
||||||
[2]interface{} { "Exif", 0xa403 },
|
[2]interface{}{"Exif", 0xa403},
|
||||||
[2]interface{} { "Exif", 0xa406 },
|
[2]interface{}{"Exif", 0xa406},
|
||||||
[2]interface{} { "Exif", 0xa430 },
|
[2]interface{}{"Exif", 0xa430},
|
||||||
[2]interface{} { "Exif", 0xa431 },
|
[2]interface{}{"Exif", 0xa431},
|
||||||
[2]interface{} { "Exif", 0xa432 },
|
[2]interface{}{"Exif", 0xa432},
|
||||||
[2]interface{} { "Exif", 0xa434 },
|
[2]interface{}{"Exif", 0xa434},
|
||||||
[2]interface{} { "Exif", 0xa435 },
|
[2]interface{}{"Exif", 0xa435},
|
||||||
[2]interface{} { "GPSInfo", 0x0000 },
|
[2]interface{}{"GPSInfo", 0x0000},
|
||||||
[2]interface{} { "IFD", 0x010f },
|
[2]interface{}{"IFD", 0x010f},
|
||||||
[2]interface{} { "IFD", 0x0110 },
|
[2]interface{}{"IFD", 0x0110},
|
||||||
[2]interface{} { "IFD", 0x0112 },
|
[2]interface{}{"IFD", 0x0112},
|
||||||
[2]interface{} { "IFD", 0x011a },
|
[2]interface{}{"IFD", 0x011a},
|
||||||
[2]interface{} { "IFD", 0x011b },
|
[2]interface{}{"IFD", 0x011b},
|
||||||
[2]interface{} { "IFD", 0x0128 },
|
[2]interface{}{"IFD", 0x0128},
|
||||||
[2]interface{} { "IFD", 0x0132 },
|
[2]interface{}{"IFD", 0x0132},
|
||||||
[2]interface{} { "IFD", 0x013b },
|
[2]interface{}{"IFD", 0x013b},
|
||||||
[2]interface{} { "IFD", 0x0213 },
|
[2]interface{}{"IFD", 0x0213},
|
||||||
[2]interface{} { "IFD", 0x8298 },
|
[2]interface{}{"IFD", 0x8298},
|
||||||
[2]interface{} { "Exif", 0x829a },
|
[2]interface{}{"Exif", 0x829a},
|
||||||
[2]interface{} { "Exif", 0x829d },
|
[2]interface{}{"Exif", 0x829d},
|
||||||
[2]interface{} { "Exif", 0x8822 },
|
[2]interface{}{"Exif", 0x8822},
|
||||||
[2]interface{} { "Exif", 0x8827 },
|
[2]interface{}{"Exif", 0x8827},
|
||||||
[2]interface{} { "Exif", 0x8830 },
|
[2]interface{}{"Exif", 0x8830},
|
||||||
[2]interface{} { "Exif", 0x8832 },
|
[2]interface{}{"Exif", 0x8832},
|
||||||
[2]interface{} { "Exif", 0x9000 },
|
[2]interface{}{"Exif", 0x9000},
|
||||||
[2]interface{} { "Exif", 0x9003 },
|
[2]interface{}{"Exif", 0x9003},
|
||||||
[2]interface{} { "Exif", 0x9004 },
|
[2]interface{}{"Exif", 0x9004},
|
||||||
[2]interface{} { "Exif", 0x9101 },
|
[2]interface{}{"Exif", 0x9101},
|
||||||
[2]interface{} { "Exif", 0x9201 },
|
[2]interface{}{"Exif", 0x9201},
|
||||||
[2]interface{} { "Exif", 0x9202 },
|
[2]interface{}{"Exif", 0x9202},
|
||||||
[2]interface{} { "Exif", 0x9204 },
|
[2]interface{}{"Exif", 0x9204},
|
||||||
[2]interface{} { "Exif", 0x9207 },
|
[2]interface{}{"Exif", 0x9207},
|
||||||
[2]interface{} { "Exif", 0x9209 },
|
[2]interface{}{"Exif", 0x9209},
|
||||||
[2]interface{} { "Exif", 0x920a },
|
[2]interface{}{"Exif", 0x920a},
|
||||||
[2]interface{} { "Exif", 0x927c },
|
[2]interface{}{"Exif", 0x927c},
|
||||||
[2]interface{} { "Exif", 0x9286 },
|
[2]interface{}{"Exif", 0x9286},
|
||||||
[2]interface{} { "Exif", 0x9290 },
|
[2]interface{}{"Exif", 0x9290},
|
||||||
[2]interface{} { "Exif", 0x9291 },
|
[2]interface{}{"Exif", 0x9291},
|
||||||
[2]interface{} { "Exif", 0x9292 },
|
[2]interface{}{"Exif", 0x9292},
|
||||||
[2]interface{} { "Exif", 0xa000 },
|
[2]interface{}{"Exif", 0xa000},
|
||||||
[2]interface{} { "Exif", 0xa001 },
|
[2]interface{}{"Exif", 0xa001},
|
||||||
[2]interface{} { "Exif", 0xa002 },
|
[2]interface{}{"Exif", 0xa002},
|
||||||
[2]interface{} { "Exif", 0xa003 },
|
[2]interface{}{"Exif", 0xa003},
|
||||||
[2]interface{} { "Iop", 0x0001 },
|
[2]interface{}{"Iop", 0x0001},
|
||||||
[2]interface{} { "Iop", 0x0002 },
|
[2]interface{}{"Iop", 0x0002},
|
||||||
[2]interface{} { "Exif", 0xa20e },
|
[2]interface{}{"Exif", 0xa20e},
|
||||||
[2]interface{} { "Exif", 0xa20f },
|
[2]interface{}{"Exif", 0xa20f},
|
||||||
[2]interface{} { "Exif", 0xa210 },
|
[2]interface{}{"Exif", 0xa210},
|
||||||
[2]interface{} { "Exif", 0xa401 },
|
[2]interface{}{"Exif", 0xa401},
|
||||||
[2]interface{} { "Exif", 0xa402 },
|
[2]interface{}{"Exif", 0xa402},
|
||||||
[2]interface{} { "Exif", 0xa403 },
|
[2]interface{}{"Exif", 0xa403},
|
||||||
[2]interface{} { "Exif", 0xa406 },
|
[2]interface{}{"Exif", 0xa406},
|
||||||
[2]interface{} { "Exif", 0xa430 },
|
[2]interface{}{"Exif", 0xa430},
|
||||||
[2]interface{} { "Exif", 0xa431 },
|
[2]interface{}{"Exif", 0xa431},
|
||||||
[2]interface{} { "Exif", 0xa432 },
|
[2]interface{}{"Exif", 0xa432},
|
||||||
[2]interface{} { "Exif", 0xa434 },
|
[2]interface{}{"Exif", 0xa434},
|
||||||
[2]interface{} { "Exif", 0xa435 },
|
[2]interface{}{"Exif", 0xa435},
|
||||||
[2]interface{} { "GPSInfo", 0x0000 },
|
[2]interface{}{"GPSInfo", 0x0000},
|
||||||
}
|
}
|
||||||
|
|
||||||
if reflect.DeepEqual(collected, expected) != true {
|
if reflect.DeepEqual(collected, expected) != true {
|
||||||
fmt.Printf("ACTUAL:\n")
|
fmt.Printf("ACTUAL:\n")
|
||||||
fmt.Printf("\n")
|
fmt.Printf("\n")
|
||||||
|
|
||||||
for _, item := range collected {
|
for _, item := range collected {
|
||||||
fmt.Printf("[2]interface{} { \"%s\", 0x%04x },\n", item[0], item[1])
|
fmt.Printf("[2]interface{} { \"%s\", 0x%04x },\n", item[0], item[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("\n")
|
fmt.Printf("\n")
|
||||||
|
|
||||||
fmt.Printf("EXPECTED:\n")
|
fmt.Printf("EXPECTED:\n")
|
||||||
fmt.Printf("\n")
|
fmt.Printf("\n")
|
||||||
|
|
||||||
for _, item := range expected {
|
for _, item := range expected {
|
||||||
fmt.Printf("[2]interface{} { \"%s\", 0x%04x },\n", item[0], item[1])
|
fmt.Printf("[2]interface{} { \"%s\", 0x%04x },\n", item[0], item[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("\n")
|
fmt.Printf("\n")
|
||||||
|
|
||||||
t.Fatalf("tags not visited correctly: (%d) != (%d)", len(collected), len(expected))
|
t.Fatalf("tags not visited correctly: (%d) != (%d)", len(collected), len(expected))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleIfd_EnumerateTagsRecursively() {
|
func ExampleIfd_EnumerateTagsRecursively() {
|
||||||
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
||||||
|
|
||||||
rawExif, err := SearchFileAndExtractExif(filepath)
|
rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
_, index, err := Collect(rawExif)
|
ti := NewTagIndex()
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
cb := func(ifd *Ifd, ite *IfdTagEntry) error {
|
_, index, err := Collect(ti, rawExif)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
// Something useful.
|
cb := func(ifd *Ifd, ite *IfdTagEntry) error {
|
||||||
|
|
||||||
return nil
|
// Something useful.
|
||||||
}
|
|
||||||
|
|
||||||
err = index.RootIfd.EnumerateTagsRecursively(cb)
|
return nil
|
||||||
log.PanicIf(err)
|
}
|
||||||
|
|
||||||
// Output:
|
err = index.RootIfd.EnumerateTagsRecursively(cb)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
// Output:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func ExampleIfd_GpsInfo() {
|
func ExampleIfd_GpsInfo() {
|
||||||
filepath := path.Join(assetsPath, "gps.jpg")
|
filepath := path.Join(assetsPath, "gps.jpg")
|
||||||
|
|
||||||
rawExif, err := SearchFileAndExtractExif(filepath)
|
rawExif, err := SearchFileAndExtractExif(filepath)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
_, index, err := Collect(rawExif)
|
ti := NewTagIndex()
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
ifd, err := index.RootIfd.ChildWithIfdIdentity(GpsIi)
|
_, index, err := Collect(ti, rawExif)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
gi, err := ifd.GpsInfo()
|
ifd, err := index.RootIfd.ChildWithIfdIdentity(GpsIi)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
fmt.Printf("%s\n", gi)
|
gi, err := ifd.GpsInfo()
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
// Output:
|
fmt.Printf("%s\n", gi)
|
||||||
// GpsInfo<LAT=(26.58667) LON=(-80.05361) ALT=(0) TIME=[2018-04-29 01:22:57 +0000 UTC]>
|
|
||||||
|
// Output:
|
||||||
|
// GpsInfo<LAT=(26.58667) LON=(-80.05361) ALT=(0) TIME=[2018-04-29 01:22:57 +0000 UTC]>
|
||||||
}
|
}
|
||||||
|
12
tags.go
12
tags.go
@ -37,8 +37,6 @@ var (
|
|||||||
// blob (not a slice of longs).
|
// blob (not a slice of longs).
|
||||||
ThumbnailOffsetTagId: struct{}{},
|
ThumbnailOffsetTagId: struct{}{},
|
||||||
}
|
}
|
||||||
|
|
||||||
tagIndex *TagIndex
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -64,15 +62,15 @@ type IndexedTag struct {
|
|||||||
Type uint16
|
Type uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it IndexedTag) String() string {
|
func (it *IndexedTag) String() string {
|
||||||
return fmt.Sprintf("TAG<ID=(0x%04x) NAME=[%s] IFD=[%s]>", it.Id, it.Name, it.Ifd)
|
return fmt.Sprintf("TAG<ID=(0x%04x) NAME=[%s] IFD=[%s]>", it.Id, it.Name, it.Ifd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it IndexedTag) IsName(ifd, name string) bool {
|
func (it *IndexedTag) IsName(ifd, name string) bool {
|
||||||
return it.Name == name && it.Ifd == ifd
|
return it.Name == name && it.Ifd == ifd
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it IndexedTag) Is(ifd string, id uint16) bool {
|
func (it *IndexedTag) Is(ifd string, id uint16) bool {
|
||||||
return it.Id == id && it.Ifd == ifd
|
return it.Id == id && it.Ifd == ifd
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,7 +316,3 @@ func IfdId(parentIfdName, ifdName string) (ii IfdIdentity, id int) {
|
|||||||
|
|
||||||
return ii, id
|
return ii, id
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
|
||||||
tagIndex = NewTagIndex()
|
|
||||||
}
|
|
||||||
|
242
tags_test.go
242
tags_test.go
@ -1,194 +1,198 @@
|
|||||||
package exif
|
package exif
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/dsoprea/go-logging"
|
"github.com/dsoprea/go-logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
invalidIi = IfdIdentity{
|
invalidIi = IfdIdentity{
|
||||||
ParentIfdName: "invalid-parent",
|
ParentIfdName: "invalid-parent",
|
||||||
IfdName: "invalid-child",
|
IfdName: "invalid-child",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
it, err := tagIndex.Get(RootIi, 0x10f)
|
ti := NewTagIndex()
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
if it.Is("IFD", 0x10f) == false || it.IsName("IFD", "Make") == false {
|
it, err := ti.Get(RootIi, 0x10f)
|
||||||
t.Fatalf("tag info not correct")
|
log.PanicIf(err)
|
||||||
}
|
|
||||||
|
if it.Is("IFD", 0x10f) == false || it.IsName("IFD", "Make") == false {
|
||||||
|
t.Fatalf("tag info not correct")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetWithName(t *testing.T) {
|
func TestGetWithName(t *testing.T) {
|
||||||
it, err := tagIndex.GetWithName(RootIi, "Make")
|
ti := NewTagIndex()
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
if it.Is("IFD", 0x10f) == false || it.Is("IFD", 0x10f) == false {
|
it, err := ti.GetWithName(RootIi, "Make")
|
||||||
t.Fatalf("tag info not correct")
|
log.PanicIf(err)
|
||||||
}
|
|
||||||
|
if it.Is("IFD", 0x10f) == false || it.Is("IFD", 0x10f) == false {
|
||||||
|
t.Fatalf("tag info not correct")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfdTagNameWithIdOrFail_Miss(t *testing.T) {
|
func TestIfdTagNameWithIdOrFail_Miss(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if state := recover(); state != nil {
|
if state := recover(); state != nil {
|
||||||
err := log.Wrap(state.(error))
|
err := log.Wrap(state.(error))
|
||||||
if err.Error() != "tag-ID (0x1234) under parent IFD [Exif] not associated with a child IFD" {
|
if err.Error() != "tag-ID (0x1234) under parent IFD [Exif] not associated with a child IFD" {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
IfdTagNameWithIdOrFail(IfdExif, 0x1234)
|
IfdTagNameWithIdOrFail(IfdExif, 0x1234)
|
||||||
|
|
||||||
t.Fatalf("expected failing for invalid IFD tag-ID")
|
t.Fatalf("expected failing for invalid IFD tag-ID")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfdTagNameWithIdOrFail_RootChildIfd(t *testing.T) {
|
func TestIfdTagNameWithIdOrFail_RootChildIfd(t *testing.T) {
|
||||||
name := IfdTagNameWithIdOrFail(IfdStandard, IfdExifId)
|
name := IfdTagNameWithIdOrFail(IfdStandard, IfdExifId)
|
||||||
if name != IfdExif {
|
if name != IfdExif {
|
||||||
t.Fatalf("expected EXIF IFD name for hit on EXIF IFD")
|
t.Fatalf("expected EXIF IFD name for hit on EXIF IFD")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfdTagNameWithIdOrFail_ChildChildIfd(t *testing.T) {
|
func TestIfdTagNameWithIdOrFail_ChildChildIfd(t *testing.T) {
|
||||||
name := IfdTagNameWithIdOrFail(IfdExif, IfdIopId)
|
name := IfdTagNameWithIdOrFail(IfdExif, IfdIopId)
|
||||||
if name != IfdIop {
|
if name != IfdIop {
|
||||||
t.Fatalf("expected IOP IFD name for hit on IOP IFD")
|
t.Fatalf("expected IOP IFD name for hit on IOP IFD")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfdTagNameWithId_Hit(t *testing.T) {
|
func TestIfdTagNameWithId_Hit(t *testing.T) {
|
||||||
name, found := IfdTagNameWithId(IfdExif, IfdIopId)
|
name, found := IfdTagNameWithId(IfdExif, IfdIopId)
|
||||||
if found != true {
|
if found != true {
|
||||||
t.Fatalf("could not get name for IOP IFD tag-ID")
|
t.Fatalf("could not get name for IOP IFD tag-ID")
|
||||||
} else if name != IfdIop {
|
} else if name != IfdIop {
|
||||||
t.Fatalf("expected IOP IFD name for hit on IOP IFD")
|
t.Fatalf("expected IOP IFD name for hit on IOP IFD")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfdTagNameWithId_Miss(t *testing.T) {
|
func TestIfdTagNameWithId_Miss(t *testing.T) {
|
||||||
name, found := IfdTagNameWithId(IfdExif, 0x1234)
|
name, found := IfdTagNameWithId(IfdExif, 0x1234)
|
||||||
if found != false {
|
if found != false {
|
||||||
t.Fatalf("expected failure for invalid IFD iag-ID under EXIF IFD")
|
t.Fatalf("expected failure for invalid IFD iag-ID under EXIF IFD")
|
||||||
} else if name != "" {
|
} else if name != "" {
|
||||||
t.Fatalf("expected empty IFD name for miss")
|
t.Fatalf("expected empty IFD name for miss")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfdTagIdWithIdentity_Hit(t *testing.T) {
|
func TestIfdTagIdWithIdentity_Hit(t *testing.T) {
|
||||||
tagId, found := IfdTagIdWithIdentity(GpsIi)
|
tagId, found := IfdTagIdWithIdentity(GpsIi)
|
||||||
if found != true {
|
if found != true {
|
||||||
t.Fatalf("could not get tag-ID for II")
|
t.Fatalf("could not get tag-ID for II")
|
||||||
} else if tagId != IfdGpsId {
|
} else if tagId != IfdGpsId {
|
||||||
t.Fatalf("incorrect tag-ID returned for II")
|
t.Fatalf("incorrect tag-ID returned for II")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfdTagIdWithIdentity_Miss(t *testing.T) {
|
func TestIfdTagIdWithIdentity_Miss(t *testing.T) {
|
||||||
tagId, found := IfdTagIdWithIdentity(invalidIi)
|
tagId, found := IfdTagIdWithIdentity(invalidIi)
|
||||||
if found != false {
|
if found != false {
|
||||||
t.Fatalf("expected failure")
|
t.Fatalf("expected failure")
|
||||||
} else if tagId != 0 {
|
} else if tagId != 0 {
|
||||||
t.Fatalf("expected tag-ID of (0) for failure")
|
t.Fatalf("expected tag-ID of (0) for failure")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfdTagIdWithIdentityOrFail_Hit(t *testing.T) {
|
func TestIfdTagIdWithIdentityOrFail_Hit(t *testing.T) {
|
||||||
IfdTagIdWithIdentityOrFail(GpsIi)
|
IfdTagIdWithIdentityOrFail(GpsIi)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfdTagIdWithIdentityOrFail_Miss(t *testing.T) {
|
func TestIfdTagIdWithIdentityOrFail_Miss(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if state := recover(); state != nil {
|
if state := recover(); state != nil {
|
||||||
err := log.Wrap(state.(error))
|
err := log.Wrap(state.(error))
|
||||||
if err.Error() != "no tag for invalid IFD identity: IfdIdentity<PARENT-NAME=[invalid-parent] NAME=[invalid-child]>" {
|
if err.Error() != "no tag for invalid IFD identity: IfdIdentity<PARENT-NAME=[invalid-parent] NAME=[invalid-child]>" {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
IfdTagIdWithIdentityOrFail(invalidIi)
|
IfdTagIdWithIdentityOrFail(invalidIi)
|
||||||
|
|
||||||
t.Fatalf("invalid II didn't panic")
|
t.Fatalf("invalid II didn't panic")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfdIdWithIdentity_Hit(t *testing.T) {
|
func TestIfdIdWithIdentity_Hit(t *testing.T) {
|
||||||
id := IfdIdWithIdentity(GpsIi)
|
id := IfdIdWithIdentity(GpsIi)
|
||||||
if id != 3 {
|
if id != 3 {
|
||||||
t.Fatalf("II doesn't have the right ID")
|
t.Fatalf("II doesn't have the right ID")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfdIdWithIdentity_Miss(t *testing.T) {
|
func TestIfdIdWithIdentity_Miss(t *testing.T) {
|
||||||
id := IfdIdWithIdentity(invalidIi)
|
id := IfdIdWithIdentity(invalidIi)
|
||||||
if id != 0 {
|
if id != 0 {
|
||||||
t.Fatalf("II doesn't have the right ID for a miss")
|
t.Fatalf("II doesn't have the right ID for a miss")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfdIdWithIdentityOrFail_Hit(t *testing.T) {
|
func TestIfdIdWithIdentityOrFail_Hit(t *testing.T) {
|
||||||
id := IfdIdWithIdentityOrFail(GpsIi)
|
id := IfdIdWithIdentityOrFail(GpsIi)
|
||||||
if id != 3 {
|
if id != 3 {
|
||||||
t.Fatalf("II doesn't have the right ID")
|
t.Fatalf("II doesn't have the right ID")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfdIdWithIdentityOrFail_Miss(t *testing.T) {
|
func TestIfdIdWithIdentityOrFail_Miss(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if state := recover(); state != nil {
|
if state := recover(); state != nil {
|
||||||
err := log.Wrap(state.(error))
|
err := log.Wrap(state.(error))
|
||||||
if err.Error() != "IFD not valid: IfdIdentity<PARENT-NAME=[invalid-parent] NAME=[invalid-child]>" {
|
if err.Error() != "IFD not valid: IfdIdentity<PARENT-NAME=[invalid-parent] NAME=[invalid-child]>" {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
IfdIdWithIdentityOrFail(invalidIi)
|
IfdIdWithIdentityOrFail(invalidIi)
|
||||||
|
|
||||||
t.Fatalf("invalid II doesn't panic")
|
t.Fatalf("invalid II doesn't panic")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfdIdOrFail_Hit(t *testing.T) {
|
func TestIfdIdOrFail_Hit(t *testing.T) {
|
||||||
ii, id := IfdIdOrFail(IfdStandard, IfdExif)
|
ii, id := IfdIdOrFail(IfdStandard, IfdExif)
|
||||||
if ii != ExifIi {
|
if ii != ExifIi {
|
||||||
t.Fatalf("wrong II for IFD returned")
|
t.Fatalf("wrong II for IFD returned")
|
||||||
} else if id != 2 {
|
} else if id != 2 {
|
||||||
t.Fatalf("wrong ID for II returned")
|
t.Fatalf("wrong ID for II returned")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfdIdOrFail_Miss(t *testing.T) {
|
func TestIfdIdOrFail_Miss(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if state := recover(); state != nil {
|
if state := recover(); state != nil {
|
||||||
err := log.Wrap(state.(error))
|
err := log.Wrap(state.(error))
|
||||||
if err.Error() != "IFD is not valid: [IFD] [invalid-ifd]" {
|
if err.Error() != "IFD is not valid: [IFD] [invalid-ifd]" {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
IfdIdOrFail(IfdStandard, "invalid-ifd")
|
IfdIdOrFail(IfdStandard, "invalid-ifd")
|
||||||
|
|
||||||
t.Fatalf("expected panic for invalid IFD")
|
t.Fatalf("expected panic for invalid IFD")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfdId_Hit(t *testing.T) {
|
func TestIfdId_Hit(t *testing.T) {
|
||||||
ii, id := IfdId(IfdStandard, IfdExif)
|
ii, id := IfdId(IfdStandard, IfdExif)
|
||||||
if ii != ExifIi {
|
if ii != ExifIi {
|
||||||
t.Fatalf("wrong II for IFD returned")
|
t.Fatalf("wrong II for IFD returned")
|
||||||
} else if id != 2 {
|
} else if id != 2 {
|
||||||
t.Fatalf("wrong ID for II returned")
|
t.Fatalf("wrong ID for II returned")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIfdId_Miss(t *testing.T) {
|
func TestIfdId_Miss(t *testing.T) {
|
||||||
ii, id := IfdId(IfdStandard, "invalid-ifd")
|
ii, id := IfdId(IfdStandard, "invalid-ifd")
|
||||||
if id != 0 {
|
if id != 0 {
|
||||||
t.Fatalf("non-zero ID returned for invalid IFD")
|
t.Fatalf("non-zero ID returned for invalid IFD")
|
||||||
} else if ii != ZeroIi {
|
} else if ii != ZeroIi {
|
||||||
t.Fatalf("expected zero-instance result for miss")
|
t.Fatalf("expected zero-instance result for miss")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,83 +1,81 @@
|
|||||||
package exif
|
package exif
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"bytes"
|
||||||
"bytes"
|
"testing"
|
||||||
|
|
||||||
"github.com/dsoprea/go-logging"
|
"github.com/dsoprea/go-logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUndefinedValue_ExifVersion(t *testing.T) {
|
func TestUndefinedValue_ExifVersion(t *testing.T) {
|
||||||
byteOrder := TestDefaultByteOrder
|
byteOrder := TestDefaultByteOrder
|
||||||
ii := ExifIi
|
ii := ExifIi
|
||||||
|
|
||||||
|
// Create our unknown-type tag's value using the fact that we know it's a
|
||||||
|
// non-null-terminated string.
|
||||||
|
|
||||||
// Create our unknown-type tag's value using the fact that we know it's a
|
ve := NewValueEncoder(byteOrder)
|
||||||
// non-null-terminated string.
|
|
||||||
|
|
||||||
ve := NewValueEncoder(byteOrder)
|
tt := NewTagType(TypeAsciiNoNul, byteOrder)
|
||||||
|
valueString := "0230"
|
||||||
|
|
||||||
tt := NewTagType(TypeAsciiNoNul, byteOrder)
|
ed, err := ve.EncodeWithType(tt, valueString)
|
||||||
valueString := "0230"
|
log.PanicIf(err)
|
||||||
|
|
||||||
ed, err := ve.EncodeWithType(tt, valueString)
|
// Create the tag using the official "unknown" type now that we already
|
||||||
log.PanicIf(err)
|
// have the bytes.
|
||||||
|
|
||||||
|
encodedValue := NewIfdBuilderTagValueFromBytes(ed.Encoded)
|
||||||
|
|
||||||
// Create the tag using the official "unknown" type now that we already
|
bt := &BuilderTag{
|
||||||
// have the bytes.
|
ii: ii,
|
||||||
|
tagId: 0x9000,
|
||||||
|
typeId: TypeUndefined,
|
||||||
|
value: encodedValue,
|
||||||
|
}
|
||||||
|
|
||||||
encodedValue := NewIfdBuilderTagValueFromBytes(ed.Encoded)
|
// Stage the build.
|
||||||
|
|
||||||
bt := &BuilderTag{
|
ti := NewTagIndex()
|
||||||
ii: ii,
|
|
||||||
tagId: 0x9000,
|
|
||||||
typeId: TypeUndefined,
|
|
||||||
value: encodedValue,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
ibe := NewIfdByteEncoder()
|
||||||
|
ib := NewIfdBuilder(ti, ii, byteOrder)
|
||||||
|
|
||||||
// Stage the build.
|
b := new(bytes.Buffer)
|
||||||
|
bw := NewByteWriter(b, byteOrder)
|
||||||
|
|
||||||
ibe := NewIfdByteEncoder()
|
addressableOffset := uint32(0x1234)
|
||||||
ib := NewIfdBuilder(ii, byteOrder)
|
ida := newIfdDataAllocator(addressableOffset)
|
||||||
|
|
||||||
b := new(bytes.Buffer)
|
// Encode.
|
||||||
bw := NewByteWriter(b, byteOrder)
|
|
||||||
|
|
||||||
addressableOffset := uint32(0x1234)
|
_, err = ibe.encodeTagToBytes(ib, bt, bw, ida, uint32(0))
|
||||||
ida := newIfdDataAllocator(addressableOffset)
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
tagBytes := b.Bytes()
|
||||||
|
|
||||||
// Encode.
|
if len(tagBytes) != 12 {
|
||||||
|
t.Fatalf("Tag not encoded to the right number of bytes: (%d)", len(tagBytes))
|
||||||
|
}
|
||||||
|
|
||||||
_, err = ibe.encodeTagToBytes(ib, bt, bw, ida, uint32(0))
|
ite, err := ParseOneTag(ii, byteOrder, tagBytes, false)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
tagBytes := b.Bytes()
|
if ite.TagId != 0x9000 {
|
||||||
|
t.Fatalf("Tag-ID not correct: (0x%02x)", ite.TagId)
|
||||||
if len(tagBytes) != 12 {
|
} else if ite.TagIndex != 0 {
|
||||||
t.Fatalf("Tag not encoded to the right number of bytes: (%d)", len(tagBytes))
|
t.Fatalf("Tag index not correct: (%d)", ite.TagIndex)
|
||||||
}
|
} else if ite.TagType != TypeUndefined {
|
||||||
|
t.Fatalf("Tag type not correct: (%d)", ite.TagType)
|
||||||
ite, err := ParseOneTag(ii, byteOrder, tagBytes, false)
|
} else if ite.UnitCount != (uint32(len(valueString))) {
|
||||||
log.PanicIf(err)
|
t.Fatalf("Tag unit-count not correct: (%d)", ite.UnitCount)
|
||||||
|
} else if bytes.Compare(ite.RawValueOffset, []byte{'0', '2', '3', '0'}) != 0 {
|
||||||
if ite.TagId != 0x9000 {
|
t.Fatalf("Tag's value (as raw bytes) is not correct: [%x]", ite.RawValueOffset)
|
||||||
t.Fatalf("Tag-ID not correct: (0x%02x)", ite.TagId)
|
} else if ite.ChildIfdName != "" {
|
||||||
} else if ite.TagIndex != 0 {
|
t.Fatalf("Tag's IFD-name should be empty: [%s]", ite.ChildIfdName)
|
||||||
t.Fatalf("Tag index not correct: (%d)", ite.TagIndex)
|
} else if ite.Ii != ii {
|
||||||
} else if ite.TagType != TypeUndefined {
|
t.Fatalf("Tag's parent IFD is not correct: %v", ite.Ii)
|
||||||
t.Fatalf("Tag type not correct: (%d)", ite.TagType)
|
}
|
||||||
} else if ite.UnitCount != (uint32(len(valueString))) {
|
|
||||||
t.Fatalf("Tag unit-count not correct: (%d)", ite.UnitCount)
|
|
||||||
} else if bytes.Compare(ite.RawValueOffset, []byte { '0', '2', '3', '0' }) != 0 {
|
|
||||||
t.Fatalf("Tag's value (as raw bytes) is not correct: [%x]", ite.RawValueOffset)
|
|
||||||
} else if ite.ChildIfdName != "" {
|
|
||||||
t.Fatalf("Tag's IFD-name should be empty: [%s]", ite.ChildIfdName)
|
|
||||||
} else if ite.Ii != ii {
|
|
||||||
t.Fatalf("Tag's parent IFD is not correct: %v", ite.Ii)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(dustin): !! Add tests for remaining, well-defined unknown
|
// TODO(dustin): !! Add tests for remaining, well-defined unknown
|
||||||
|
Loading…
x
Reference in New Issue
Block a user