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.
pull/3/head
Dustin Oprea 2018-07-26 22:45:43 -04:00
parent d8cdf29fa6
commit d3b45fae77
11 changed files with 3384 additions and 3350 deletions

View File

@ -14,134 +14,133 @@
package main
import (
"os"
"fmt"
"flag"
"flag"
"fmt"
"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 (
filepathArg = ""
printAsJsonArg = false
filepathArg = ""
printAsJsonArg = false
)
type IfdEntry struct {
IfdName string `json:"ifd_name"`
ParentIfdName string `json:"parent_ifd_name"`
IfdIndex int `json:"ifd_index"`
TagId uint16 `json:"tag_id"`
TagName string `json:"tag_name"`
TagTypeId uint16 `json:"tag_type_id"`
TagTypeName string `json:"tag_type_name"`
UnitCount uint32 `json:"unit_count"`
Value interface{} `json:"value"`
ValueString string `json:"value_string"`
IfdName string `json:"ifd_name"`
ParentIfdName string `json:"parent_ifd_name"`
IfdIndex int `json:"ifd_index"`
TagId uint16 `json:"tag_id"`
TagName string `json:"tag_name"`
TagTypeId uint16 `json:"tag_type_id"`
TagTypeName string `json:"tag_type_name"`
UnitCount uint32 `json:"unit_count"`
Value interface{} `json:"value"`
ValueString string `json:"value_string"`
}
func main() {
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
log.PrintErrorf(err, "Program error.")
}
}()
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
log.PrintErrorf(err, "Program error.")
}
}()
flag.StringVar(&filepathArg, "filepath", "", "File-path of image")
flag.BoolVar(&printAsJsonArg, "json", false, "Print JSON")
flag.StringVar(&filepathArg, "filepath", "", "File-path of image")
flag.BoolVar(&printAsJsonArg, "json", false, "Print JSON")
flag.Parse()
flag.Parse()
if filepathArg == "" {
fmt.Printf("Please provide a file-path for an image.\n")
os.Exit(1)
}
if filepathArg == "" {
fmt.Printf("Please provide a file-path for an image.\n")
os.Exit(1)
}
f, err := os.Open(filepathArg)
log.PanicIf(err)
f, err := os.Open(filepathArg)
log.PanicIf(err)
data, err := ioutil.ReadAll(f)
log.PanicIf(err)
data, err := ioutil.ReadAll(f)
log.PanicIf(err)
rawExif, err := exif.SearchAndExtractExif(data)
log.PanicIf(err)
rawExif, err := exif.SearchAndExtractExif(data)
log.PanicIf(err)
// Run the parse.
// Run the parse.
entries := make([]IfdEntry, 0)
entries := make([]IfdEntry, 0)
ti := exif.NewTagIndex()
visitor := func(ii exif.IfdIdentity, ifdIndex int, tagId uint16, tagType exif.TagType, valueContext exif.ValueContext) (err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
log.Panic(err)
}
}()
ti := exif.NewTagIndex()
visitor := func(ii exif.IfdIdentity, ifdIndex int, tagId uint16, tagType exif.TagType, valueContext exif.ValueContext) (err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
log.Panic(err)
}
}()
it, err := ti.Get(ii, tagId)
if err != nil {
if log.Is(err, exif.ErrTagNotFound) {
fmt.Printf("WARNING: Unknown tag: [%s] (%04x)\n", ii, tagId)
return nil
} else {
log.Panic(err)
}
}
it, err := ti.Get(ii, tagId)
if err != nil {
if log.Is(err, exif.ErrTagNotFound) {
fmt.Printf("WARNING: Unknown tag: [%s] (%04x)\n", ii, tagId)
return nil
} else {
log.Panic(err)
}
}
valueString := ""
var value interface{}
if tagType.Type() == exif.TypeUndefined {
var err error
value, err = exif.UndefinedValue(ii, tagId, valueContext, tagType.ByteOrder())
if log.Is(err, exif.ErrUnhandledUnknownTypedTag) {
value = nil
} else if err != nil {
log.Panic(err)
} else {
valueString = fmt.Sprintf("%v", value)
}
} else {
valueString, err = tagType.ResolveAsString(valueContext, true)
log.PanicIf(err)
valueString := ""
var value interface{}
if tagType.Type() == exif.TypeUndefined {
var err error
value, err = exif.UndefinedValue(ii, tagId, valueContext, tagType.ByteOrder())
if log.Is(err, exif.ErrUnhandledUnknownTypedTag) {
value = nil
} else if err != nil {
log.Panic(err)
} else {
valueString = fmt.Sprintf("%v", value)
}
} else {
valueString, err = tagType.ResolveAsString(valueContext, true)
log.PanicIf(err)
value = valueString
}
value = valueString
}
entry := IfdEntry{
IfdName: ii.IfdName,
ParentIfdName: ii.ParentIfdName,
IfdIndex: ifdIndex,
TagId: tagId,
TagName: it.Name,
TagTypeId: tagType.Type(),
TagTypeName: tagType.Name(),
UnitCount: valueContext.UnitCount,
Value: value,
ValueString: valueString,
}
entry := IfdEntry{
IfdName: ii.IfdName,
ParentIfdName: ii.ParentIfdName,
IfdIndex: ifdIndex,
TagId: tagId,
TagName: it.Name,
TagTypeId: tagType.Type(),
TagTypeName: tagType.Name(),
UnitCount: valueContext.UnitCount,
Value: value,
ValueString: valueString,
}
entries = append(entries, entry)
entries = append(entries, entry)
return nil
}
return nil
}
_, err = exif.Visit(rawExif, visitor)
log.PanicIf(err)
_, err = exif.Visit(ti, rawExif, visitor)
log.PanicIf(err)
if printAsJsonArg == true {
data, err := json.MarshalIndent(entries, "", " ")
log.PanicIf(err)
if printAsJsonArg == true {
data, err := json.MarshalIndent(entries, "", " ")
log.PanicIf(err)
fmt.Println(string(data))
} else {
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.Println(string(data))
} else {
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)
}
}
}

View File

@ -168,7 +168,7 @@ func ParseExifHeader(data []byte) (eh ExifHeader, err error) {
}
// 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() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
@ -178,7 +178,7 @@ func Visit(exifData []byte, visitor RawTagVisitor) (eh ExifHeader, err error) {
eh, err = ParseExifHeader(exifData)
log.PanicIf(err)
ie := NewIfdEnumerate(exifData, eh.ByteOrder)
ie := NewIfdEnumerate(tagIndex, exifData, eh.ByteOrder)
err = ie.Scan(eh.FirstIfdOffset, visitor, true)
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.
func Collect(exifData []byte) (eh ExifHeader, index IfdIndex, err error) {
func Collect(tagIndex *TagIndex, exifData []byte) (eh ExifHeader, index IfdIndex, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
@ -197,7 +197,7 @@ func Collect(exifData []byte) (eh ExifHeader, index IfdIndex, err error) {
eh, err = ParseExifHeader(exifData)
log.PanicIf(err)
ie := NewIfdEnumerate(exifData, eh.ByteOrder)
ie := NewIfdEnumerate(tagIndex, exifData, eh.ByteOrder)
index, err = ie.Collect(eh.FirstIfdOffset, true)
log.PanicIf(err)

View File

@ -1,383 +1,385 @@
package exif
import (
"testing"
"os"
"path"
"fmt"
"reflect"
"bytes"
"bytes"
"fmt"
"os"
"path"
"reflect"
"testing"
"io/ioutil"
"encoding/binary"
"encoding/binary"
"io/ioutil"
"github.com/dsoprea/go-logging"
"github.com/dsoprea/go-logging"
)
func TestVisit(t *testing.T) {
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
log.PrintErrorf(err, "Exif failure.")
}
}()
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
log.PrintErrorf(err, "Exif failure.")
}
}()
// Open the file.
ti := NewTagIndex()
filepath := path.Join(assetsPath, "NDM_8901.jpg")
f, err := os.Open(filepath)
log.PanicIf(err)
// Open the file.
defer f.Close()
filepath := path.Join(assetsPath, "NDM_8901.jpg")
f, err := os.Open(filepath)
log.PanicIf(err)
data, err := ioutil.ReadAll(f)
log.PanicIf(err)
defer f.Close()
// Search for the beginning of the EXIF information. The EXIF is near the
// very beginning of our/most JPEGs, so this has a very low cost.
data, err := ioutil.ReadAll(f)
log.PanicIf(err)
foundAt := -1
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)
}
}
// Search for the beginning of the EXIF information. The EXIF is near the
// very beginning of our/most JPEGs, so this has a very low cost.
if foundAt == -1 {
log.Panicf("EXIF start not found")
}
foundAt := -1
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) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
log.Panic(err)
}
}()
tags := make([]string, 0)
it, err := tagIndex.Get(ii, tagId)
if err != nil {
if log.Is(err, ErrTagNotFound) {
fmt.Printf("Unknown tag: [%v] (%04x)\n", ii, tagId)
return nil
} else {
log.Panic(err)
}
}
visitor := func(ii IfdIdentity, ifdIndex int, tagId uint16, tagType TagType, valueContext ValueContext) (err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
log.Panic(err)
}
}()
valueString := ""
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)
}
it, err := ti.Get(ii, tagId)
if err != nil {
if log.Is(err, ErrTagNotFound) {
fmt.Printf("Unknown tag: [%v] (%04x)\n", ii, tagId)
return nil
} else {
log.Panic(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)
tags = append(tags, description)
valueString := ""
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)
log.PanicIf(err)
return nil
}
// for _, line := range tags {
// fmt.Printf("TAGS: %s\n", line)
// }
_, err = Visit(ti, data[foundAt:], visitor)
log.PanicIf(err)
expected := []string {
"IFD=[IFD] ID=(0x010f) NAME=[Make] COUNT=(6) TYPE=[ASCII] VALUE=[Canon]",
"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]",
}
// for _, line := range tags {
// fmt.Printf("TAGS: %s\n", line)
// }
if reflect.DeepEqual(tags, expected) == false {
fmt.Printf("\n")
fmt.Printf("ACTUAL:\n")
fmt.Printf("\n")
expected := []string{
"IFD=[IFD] ID=(0x010f) NAME=[Make] COUNT=(6) TYPE=[ASCII] VALUE=[Canon]",
"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]",
}
for _, line := range tags {
fmt.Println(line)
}
if reflect.DeepEqual(tags, expected) == false {
fmt.Printf("\n")
fmt.Printf("ACTUAL:\n")
fmt.Printf("\n")
fmt.Printf("\n")
fmt.Printf("EXPECTED:\n")
fmt.Printf("\n")
for _, line := range tags {
fmt.Println(line)
}
for _, line := range expected {
fmt.Println(line)
}
fmt.Printf("\n")
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) {
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
// image.
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
// Returns a slice starting with the EXIF data and going to the end of the
// image.
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
if bytes.Compare(rawExif[:len(testExifData)], testExifData) != 0 {
t.Fatalf("found EXIF data not correct")
}
if bytes.Compare(rawExif[:len(testExifData)], testExifData) != 0 {
t.Fatalf("found EXIF data not correct")
}
}
func TestSearchAndExtractExif(t *testing.T) {
filepath := path.Join(assetsPath, "NDM_8901.jpg")
filepath := path.Join(assetsPath, "NDM_8901.jpg")
imageData, err := ioutil.ReadFile(filepath)
log.PanicIf(err)
imageData, err := ioutil.ReadFile(filepath)
log.PanicIf(err)
rawExif, err := SearchAndExtractExif(imageData)
log.PanicIf(err)
rawExif, err := SearchAndExtractExif(imageData)
log.PanicIf(err)
if bytes.Compare(rawExif[:len(testExifData)], testExifData) != 0 {
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) {
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
log.PrintErrorf(err, "Exif failure.")
}
}()
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
log.PrintErrorf(err, "Exif failure.")
}
}()
filepath := path.Join(assetsPath, "NDM_8901.jpg")
filepath := path.Join(assetsPath, "NDM_8901.jpg")
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
_, index, err := Collect(rawExif)
log.PanicIf(err)
ti := NewTagIndex()
rootIfd := index.RootIfd
ifds := index.Ifds
tree := index.Tree
lookup := index.Lookup
_, index, err := Collect(ti, rawExif)
log.PanicIf(err)
if rootIfd.Offset != uint32(0x0008) {
t.Fatalf("Root-IFD not correct: (0x%04d).", rootIfd.Offset)
} else if rootIfd.Id != 0 {
t.Fatalf("Root-IFD does not have the right ID: (%d)", rootIfd.Id)
} 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))
}
rootIfd := index.RootIfd
ifds := index.Ifds
tree := index.Tree
lookup := index.Lookup
if rootIfd.NextIfdOffset != 0x2c54 {
t.Fatalf("Root IFD does not continue correctly: (0x%04x)", rootIfd.NextIfdOffset)
} else if rootIfd.NextIfd.Offset != rootIfd.NextIfdOffset {
t.Fatalf("Root IFD neighbor object does not have the right offset: (0x%04x != 0x%04x)", rootIfd.NextIfd.Offset, rootIfd.NextIfdOffset)
} else if rootIfd.NextIfd.NextIfdOffset != 0 {
t.Fatalf("Root IFD chain not terminated correctly (1).")
} else if rootIfd.NextIfd.NextIfd != nil {
t.Fatalf("Root IFD chain not terminated correctly (2).")
}
if rootIfd.Offset != uint32(0x0008) {
t.Fatalf("Root-IFD not correct: (0x%04d).", rootIfd.Offset)
} else if rootIfd.Id != 0 {
t.Fatalf("Root-IFD does not have the right ID: (%d)", rootIfd.Id)
} 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.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 rootIfd.NextIfdOffset != 0x2c54 {
t.Fatalf("Root IFD does not continue correctly: (0x%04x)", rootIfd.NextIfdOffset)
} else if rootIfd.NextIfd.Offset != rootIfd.NextIfdOffset {
t.Fatalf("Root IFD neighbor object does not have the right offset: (0x%04x != 0x%04x)", rootIfd.NextIfd.Offset, rootIfd.NextIfdOffset)
} else if rootIfd.NextIfd.NextIfdOffset != 0 {
t.Fatalf("Root IFD chain not terminated correctly (1).")
} else if rootIfd.NextIfd.NextIfd != nil {
t.Fatalf("Root IFD chain not terminated correctly (2).")
}
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 {
t.Fatalf("Lookup for standard IFD not correct.")
} else if lookup[rootIi][1].Name != IfdStandard {
t.Fatalf("Lookup for standard IFD not correct.")
}
rootIi, _ := IfdIdOrFail("", IfdStandard)
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 {
t.Fatalf("Lookup for EXIF IFD not correct.")
}
exifIi, _ := IfdIdOrFail(IfdStandard, IfdExif)
gpsIi, _ := IfdIdOrFail(IfdStandard, IfdGps)
if lookup[exifIi][0].Name != IfdExif {
t.Fatalf("Lookup for EXIF IFD not correct.")
}
if lookup[gpsIi][0].Name != IfdGps {
t.Fatalf("Lookup for EXIF IFD not correct.")
}
gpsIi, _ := IfdIdOrFail(IfdStandard, IfdGps)
iopIi, _ := IfdIdOrFail(IfdExif, IfdIop)
if lookup[gpsIi][0].Name != IfdGps {
t.Fatalf("Lookup for EXIF IFD not correct.")
}
if lookup[iopIi][0].Name != IfdIop {
t.Fatalf("Lookup for EXIF IFD not correct.")
}
iopIi, _ := IfdIdOrFail(IfdExif, IfdIop)
foundExif := 0
foundGps := 0
for _, ite := range lookup[rootIi][0].Entries {
if ite.ChildIfdName == IfdExif {
foundExif++
if lookup[iopIi][0].Name != IfdIop {
t.Fatalf("Lookup for EXIF IFD not correct.")
}
name, found := IfdTagNameWithId(IfdStandard, ite.TagId)
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)
}
}
foundExif := 0
foundGps := 0
for _, ite := range lookup[rootIi][0].Entries {
if ite.ChildIfdName == IfdExif {
foundExif++
if ite.ChildIfdName == IfdGps {
foundGps++
name, found := IfdTagNameWithId(IfdStandard, ite.TagId)
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 found != true {
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 ite.ChildIfdName == IfdGps {
foundGps++
if foundExif != 1 {
t.Fatalf("Exactly one EXIF IFD tag wasn't found: (%d)", foundExif)
} else if foundGps != 1 {
t.Fatalf("Exactly one GPS IFD tag wasn't found: (%d)", foundGps)
}
name, found := IfdTagNameWithId(IfdStandard, ite.TagId)
if found != true {
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)
}
}
}
foundIop := 0
for _, ite := range lookup[exifIi][0].Entries {
if ite.ChildIfdName == IfdIop {
foundIop++
if foundExif != 1 {
t.Fatalf("Exactly one EXIF IFD tag wasn't found: (%d)", foundExif)
} else if foundGps != 1 {
t.Fatalf("Exactly one GPS IFD tag wasn't found: (%d)", foundGps)
}
name, found := IfdTagNameWithId(IfdExif, ite.TagId)
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)
}
}
}
foundIop := 0
for _, ite := range lookup[exifIi][0].Entries {
if ite.ChildIfdName == IfdIop {
foundIop++
if foundIop != 1 {
t.Fatalf("Exactly one IOP IFD tag wasn't found: (%d)", foundIop)
}
name, found := IfdTagNameWithId(IfdExif, ite.TagId)
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) {
eh, err := ParseExifHeader(testExifData)
log.PanicIf(err)
eh, err := ParseExifHeader(testExifData)
log.PanicIf(err)
if eh.ByteOrder != binary.LittleEndian {
t.Fatalf("Byte-order of EXIF header not correct.")
} else if eh.FirstIfdOffset != 0x8 {
t.Fatalf("First IFD offset not correct.")
}
if eh.ByteOrder != binary.LittleEndian {
t.Fatalf("Byte-order of EXIF header not correct.")
} else if eh.FirstIfdOffset != 0x8 {
t.Fatalf("First IFD offset not correct.")
}
}
func TestExif_BuildAndParseExifHeader(t *testing.T) {
headerBytes, err := BuildExifHeader(TestDefaultByteOrder, 0x11223344)
log.PanicIf(err)
headerBytes, err := BuildExifHeader(TestDefaultByteOrder, 0x11223344)
log.PanicIf(err)
eh, err := ParseExifHeader(headerBytes)
log.PanicIf(err)
eh, err := ParseExifHeader(headerBytes)
log.PanicIf(err)
if eh.ByteOrder != TestDefaultByteOrder {
t.Fatalf("Byte-order of EXIF header not correct.")
} else if eh.FirstIfdOffset != 0x11223344 {
t.Fatalf("First IFD offset not correct.")
}
if eh.ByteOrder != TestDefaultByteOrder {
t.Fatalf("Byte-order of EXIF header not correct.")
} else if eh.FirstIfdOffset != 0x11223344 {
t.Fatalf("First IFD offset not correct.")
}
}
func ExampleBuildExifHeader() {
headerBytes, err := BuildExifHeader(TestDefaultByteOrder, 0x11223344)
log.PanicIf(err)
headerBytes, err := BuildExifHeader(TestDefaultByteOrder, 0x11223344)
log.PanicIf(err)
eh, err := ParseExifHeader(headerBytes)
log.PanicIf(err)
eh, err := ParseExifHeader(headerBytes)
log.PanicIf(err)
fmt.Printf("%v\n", eh)
// Output: ExifHeader<BYTE-ORDER=[BigEndian] FIRST-IFD-OFFSET=(0x11223344)>
fmt.Printf("%v\n", eh)
// Output: ExifHeader<BYTE-ORDER=[BigEndian] FIRST-IFD-OFFSET=(0x11223344)>
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,8 @@ import (
)
func TestAdd(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{
ii: RootIi,
@ -98,8 +99,10 @@ func TestAdd(t *testing.T) {
}
func TestSetNextIb(t *testing.T) {
ib1 := NewIfdBuilder(RootIi, TestDefaultByteOrder)
ib2 := NewIfdBuilder(RootIi, TestDefaultByteOrder)
ti := NewTagIndex()
ib1 := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
ib2 := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
if ib1.nextIb != nil {
t.Fatalf("Next-IFD for IB1 not initially terminal.")
@ -117,7 +120,8 @@ func TestSetNextIb(t *testing.T) {
func TestAddChildIb(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{
ii: RootIi,
@ -131,7 +135,7 @@ func TestAddChildIb(t *testing.T) {
exifIi, _ := IfdIdOrFail(IfdStandard, IfdExif)
ibChild := NewIfdBuilder(exifIi, TestDefaultByteOrder)
ibChild := NewIfdBuilder(ti, exifIi, TestDefaultByteOrder)
err = ib.AddChildIb(ibChild)
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)
@ -192,8 +197,9 @@ func TestAddTagsFromExisting(t *testing.T) {
}
ifd := &Ifd{
Ii: RootIi,
Entries: entries,
Ii: RootIi,
Entries: entries,
tagIndex: ti,
}
err := ib.AddTagsFromExisting(ifd, nil, nil, nil)
@ -211,7 +217,8 @@ func TestAddTagsFromExisting(t *testing.T) {
}
func TestAddTagsFromExisting__Includes(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
entries := make([]*IfdTagEntry, 3)
@ -235,8 +242,9 @@ func TestAddTagsFromExisting__Includes(t *testing.T) {
}
ifd := &Ifd{
Ii: RootIi,
Entries: entries,
Ii: RootIi,
Entries: entries,
tagIndex: ti,
}
err := ib.AddTagsFromExisting(ifd, nil, []uint16{0x33}, nil)
@ -250,7 +258,8 @@ func TestAddTagsFromExisting__Includes(t *testing.T) {
}
func TestAddTagsFromExisting__Excludes(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
entries := make([]*IfdTagEntry, 3)
@ -274,8 +283,9 @@ func TestAddTagsFromExisting__Excludes(t *testing.T) {
}
ifd := &Ifd{
Ii: RootIi,
Entries: entries,
Ii: RootIi,
Entries: entries,
tagIndex: ti,
}
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) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{
ii: RootIi,
@ -339,7 +350,8 @@ func TestFindN_First_1(t *testing.T) {
}
func TestFindN_First_2_1Returned(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{
ii: RootIi,
@ -389,7 +401,8 @@ func TestFindN_First_2_1Returned(t *testing.T) {
}
func TestFindN_First_2_2Returned(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{
ii: RootIi,
@ -466,7 +479,8 @@ func TestFindN_First_2_2Returned(t *testing.T) {
}
func TestFindN_Middle_WithDuplicates(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{
ii: RootIi,
@ -546,7 +560,8 @@ func TestFindN_Middle_WithDuplicates(t *testing.T) {
}
func TestFindN_Middle_NoDuplicates(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{
ii: RootIi,
@ -606,7 +621,8 @@ func TestFindN_Middle_NoDuplicates(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)
log.PanicIf(err)
@ -617,7 +633,8 @@ func TestFindN_Miss(t *testing.T) {
}
func TestFind_Hit(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{
ii: RootIi,
@ -675,7 +692,8 @@ func TestFind_Hit(t *testing.T) {
}
func TestFind_Miss(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{
ii: RootIi,
@ -726,7 +744,8 @@ func TestFind_Miss(t *testing.T) {
}
func TestReplace(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{
ii: RootIi,
@ -788,7 +807,8 @@ func TestReplace(t *testing.T) {
}
func TestReplaceN(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{
ii: RootIi,
@ -850,7 +870,8 @@ func TestReplaceN(t *testing.T) {
}
func TestDeleteFirst(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{
ii: RootIi,
@ -946,7 +967,8 @@ func TestDeleteFirst(t *testing.T) {
}
func TestDeleteN(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{
ii: RootIi,
@ -1042,7 +1064,8 @@ func TestDeleteN(t *testing.T) {
}
func TestDeleteN_Two(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{
ii: RootIi,
@ -1122,7 +1145,8 @@ func TestDeleteN_Two(t *testing.T) {
}
func TestDeleteAll(t *testing.T) {
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
ti := NewTagIndex()
ib := NewIfdBuilder(ti, RootIi, TestDefaultByteOrder)
bt := &BuilderTag{
ii: RootIi,
@ -1216,7 +1240,9 @@ func Test_IfdBuilder_CreateIfdBuilderFromExistingChain(t *testing.T) {
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
_, index, err := Collect(rawExif)
ti := NewTagIndex()
_, index, err := Collect(ti, rawExif)
log.PanicIf(err)
itevr := NewIfdTagEntryValueResolver(rawExif, index.RootIfd.ByteOrder)
@ -1296,7 +1322,9 @@ func Test_IfdBuilder_CreateIfdBuilderFromExistingChain_RealData(t *testing.T) {
// Decode from binary.
_, originalIndex, err := Collect(rawExif)
ti := NewTagIndex()
_, originalIndex, err := Collect(ti, rawExif)
log.PanicIf(err)
originalThumbnailData, err := originalIndex.RootIfd.NextIfd.Thumbnail()
@ -1316,7 +1344,7 @@ func Test_IfdBuilder_CreateIfdBuilderFromExistingChain_RealData(t *testing.T) {
// Parse again.
_, recoveredIndex, err := Collect(updatedExif)
_, recoveredIndex, err := Collect(ti, updatedExif)
log.PanicIf(err)
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) {
filepath := path.Join(assetsPath, "NDM_8901.jpg")
// func Test_IfdBuilder_CreateIfdBuilderFromExistingChain_RealData_WithUpdate(t *testing.T) {
// filepath := path.Join(assetsPath, "NDM_8901.jpg")
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
// rawExif, err := SearchFileAndExtractExif(filepath)
// log.PanicIf(err)
// Decode from binary.
// // Decode from binary.
_, originalIndex, err := Collect(rawExif)
log.PanicIf(err)
// ti := NewTagIndex()
originalThumbnailData, err := originalIndex.RootIfd.NextIfd.Thumbnail()
log.PanicIf(err)
// _, originalIndex, err := Collect(ti, rawExif)
// 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)
rootIb := NewIfdBuilderFromExistingChain(originalIndex.RootIfd, itevr)
// ibe := NewIfdByteEncoder()
// Update a tag,.
// itevr := NewIfdTagEntryValueResolver(rawExif, originalIndex.RootIfd.ByteOrder)
// rootIb := NewIfdBuilderFromExistingChain(originalIndex.RootIfd, itevr)
exifBt, err := rootIb.FindTagWithName("ExifTag")
log.PanicIf(err)
// // Update a tag,.
ucBt, err := exifBt.value.Ib().FindTagWithName("UserComment")
log.PanicIf(err)
// exifBt, err := rootIb.FindTagWithName("ExifTag")
// log.PanicIf(err)
uc := TagUnknownType_9298_UserComment{
EncodingType: TagUnknownType_9298_UserComment_Encoding_ASCII,
EncodingBytes: []byte("TEST COMMENT"),
}
// ucBt, err := exifBt.value.Ib().FindTagWithName("UserComment")
// log.PanicIf(err)
err = ucBt.SetValue(rootIb.byteOrder, uc)
log.PanicIf(err)
// uc := TagUnknownType_9298_UserComment{
// 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)
log.PanicIf(err)
// // Encode.
// Parse again.
// updatedExif, err := ibe.EncodeToExif(rootIb)
// log.PanicIf(err)
_, recoveredIndex, err := Collect(updatedExif)
log.PanicIf(err)
// // Parse again.
recoveredTags := recoveredIndex.RootIfd.DumpTags()
// _, recoveredIndex, err := Collect(ti, updatedExif)
// log.PanicIf(err)
recoveredThumbnailData, err := recoveredIndex.RootIfd.NextIfd.Thumbnail()
log.PanicIf(err)
// recoveredTags := recoveredIndex.RootIfd.DumpTags()
// Check the thumbnail.
// recoveredThumbnailData, err := recoveredIndex.RootIfd.NextIfd.Thumbnail()
// log.PanicIf(err)
if bytes.Compare(recoveredThumbnailData, originalThumbnailData) != 0 {
t.Fatalf("recovered thumbnail does not match original")
}
// // Check the thumbnail.
// 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)
for _, ite := range originalTags {
if ite.ChildIfdName != "" {
originalIfdTags = append(originalIfdTags, [2]interface{}{ite.Ii, ite.TagId})
}
}
// // Validate that all of the same IFDs were presented.
recoveredIfdTags := make([][2]interface{}, 0)
for _, ite := range recoveredTags {
if ite.ChildIfdName != "" {
recoveredIfdTags = append(recoveredIfdTags, [2]interface{}{ite.Ii, ite.TagId})
}
}
// originalIfdTags := make([][2]interface{}, 0)
// for _, ite := range originalTags {
// if ite.ChildIfdName != "" {
// originalIfdTags = append(originalIfdTags, [2]interface{}{ite.Ii, ite.TagId})
// }
// }
if reflect.DeepEqual(recoveredIfdTags, originalIfdTags) != true {
fmt.Printf("Original IFD tags:\n\n")
// recoveredIfdTags := make([][2]interface{}, 0)
// for _, ite := range recoveredTags {
// if ite.ChildIfdName != "" {
// recoveredIfdTags = append(recoveredIfdTags, [2]interface{}{ite.Ii, ite.TagId})
// }
// }
for i, x := range originalIfdTags {
fmt.Printf(" %02d %v\n", i, x)
}
// if reflect.DeepEqual(recoveredIfdTags, originalIfdTags) != true {
// 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(" %02d %v\n", i, x)
}
// fmt.Printf("\nRecovered IFD tags:\n\n")
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
// that the thumbnail tags are not kept but only produced on the fly, which
// is why we check it above.
// t.Fatalf("Recovered IFD tags are not correct.")
// }
if len(recoveredTags) != len(originalTags) {
t.Fatalf("Recovered tag-count does not match original.")
}
// // Validate that all of the tags owned by the IFDs were presented. Note
// // 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 recoveredIte.ChildIfdName != "" {
continue
}
// if len(recoveredTags) != len(originalTags) {
// t.Fatalf("Recovered tag-count does not match original.")
// }
originalIte := originalTags[i]
// for i, recoveredIte := range recoveredTags {
// if recoveredIte.ChildIfdName != "" {
// continue
// }
if recoveredIte.Ii != originalIte.Ii {
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)
}
// originalIte := originalTags[i]
originalValueBytes, err := originalIte.ValueBytes(originalIndex.RootIfd.addressableData, originalIndex.RootIfd.ByteOrder)
log.PanicIf(err)
// if recoveredIte.Ii != originalIte.Ii {
// 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)
log.PanicIf(err)
// originalValueBytes, err := originalIte.ValueBytes(originalIndex.RootIfd.addressableData, originalIndex.RootIfd.ByteOrder)
// log.PanicIf(err)
if recoveredIte.TagId == 0x9286 {
expectedValueBytes := make([]byte, 0)
// recoveredValueBytes, err := recoveredIte.ValueBytes(recoveredIndex.RootIfd.addressableData, recoveredIndex.RootIfd.ByteOrder)
// log.PanicIf(err)
expectedValueBytes = append(expectedValueBytes, []byte{'A', 'S', 'C', 'I', 'I', 0, 0, 0}...)
expectedValueBytes = append(expectedValueBytes, []byte("TEST COMMENT")...)
// if recoveredIte.TagId == 0x9286 {
// expectedValueBytes := make([]byte, 0)
if bytes.Compare(recoveredValueBytes, expectedValueBytes) != 0 {
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)
}
}
}
// expectedValueBytes = append(expectedValueBytes, []byte{'A', 'S', 'C', 'I', 'I', 0, 0, 0}...)
// expectedValueBytes = append(expectedValueBytes, []byte("TEST COMMENT")...)
// if bytes.Compare(recoveredValueBytes, expectedValueBytes) != 0 {
// 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() {
filepath := path.Join(assetsPath, "NDM_8901.jpg")
@ -1544,7 +1574,9 @@ func ExampleIfd_Thumbnail() {
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
_, index, err := Collect(rawExif)
ti := NewTagIndex()
_, index, err := Collect(ti, rawExif)
log.PanicIf(err)
thumbnailData, err := index.RootIfd.NextIfd.Thumbnail()
@ -1560,7 +1592,9 @@ func ExampleBuilderTag_SetValue() {
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
_, index, err := Collect(rawExif)
ti := NewTagIndex()
_, index, err := Collect(ti, rawExif)
log.PanicIf(err)
// Create builder.
@ -1599,11 +1633,14 @@ func ExampleBuilderTag_SetValue() {
}
func Test_IfdBuilder_CreateIfdBuilderWithExistingIfd(t *testing.T) {
ti := NewTagIndex()
tagId := IfdTagIdWithIdentityOrFail(GpsIi)
parentIfd := &Ifd{
Ii: RootIi,
Name: IfdStandard,
Ii: RootIi,
Name: IfdStandard,
tagIndex: ti,
}
ifd := &Ifd{
@ -1612,6 +1649,7 @@ func Test_IfdBuilder_CreateIfdBuilderWithExistingIfd(t *testing.T) {
ByteOrder: TestDefaultByteOrder,
Offset: 0x123,
ParentIfd: parentIfd,
tagIndex: ti,
}
ib := NewIfdBuilderWithExistingIfd(ifd)
@ -1628,7 +1666,12 @@ func Test_IfdBuilder_CreateIfdBuilderWithExistingIfd(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 {
t.Fatalf("II in BuilderTag not correct")
@ -1640,21 +1683,12 @@ func TestNewStandardBuilderTag_OneUnit(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 {
t.Fatalf("II in BuilderTag not correct")
} 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")
}
}
it, err := ti.Get(ExifIi, uint16(0x8833))
log.PanicIf(err)
func TestNewStandardBuilderTagWithName(t *testing.T) {
bt := NewStandardBuilderTagWithName(ExifIi, "ISOSpeed", TestDefaultByteOrder, []uint32{uint32(0x1234), uint32(0x5678)})
bt := NewStandardBuilderTag(ExifIi, it, TestDefaultByteOrder, []uint32{uint32(0x1234), uint32(0x5678)})
if bt.ii != ExifIi {
t.Fatalf("II in BuilderTag not correct")
@ -1668,7 +1702,8 @@ func TestNewStandardBuilderTagWithName(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")
log.PanicIf(err)

File diff suppressed because it is too large Load Diff

View File

@ -1,526 +1,550 @@
package exif
import (
"path"
"testing"
"bytes"
"fmt"
"reflect"
"bytes"
"fmt"
"path"
"reflect"
"testing"
"encoding/binary"
"io/ioutil"
"encoding/binary"
"io/ioutil"
"github.com/dsoprea/go-logging"
"github.com/dsoprea/go-logging"
)
func TestIfdTagEntry_ValueBytes(t *testing.T) {
byteOrder := binary.BigEndian
ve := NewValueEncoder(byteOrder)
byteOrder := binary.BigEndian
ve := NewValueEncoder(byteOrder)
original := []byte("original text")
original := []byte("original text")
ed, err := ve.encodeBytes(original)
log.PanicIf(err)
ed, err := ve.encodeBytes(original)
log.PanicIf(err)
// 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
// value happened to be recorded at the beginning.
// 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
// value happened to be recorded at the beginning.
ite := IfdTagEntry{
TagType: TypeByte,
UnitCount: uint32(len(original)),
ValueOffset: 0,
}
ite := IfdTagEntry{
TagType: TypeByte,
UnitCount: uint32(len(original)),
ValueOffset: 0,
}
decodedBytes, err := ite.ValueBytes(ed.Encoded, byteOrder)
log.PanicIf(err)
decodedBytes, err := ite.ValueBytes(ed.Encoded, byteOrder)
log.PanicIf(err)
if bytes.Compare(decodedBytes, original) != 0 {
t.Fatalf("Bytes not decoded correctly.")
}
if bytes.Compare(decodedBytes, original) != 0 {
t.Fatalf("Bytes not decoded correctly.")
}
}
func TestIfdTagEntry_ValueBytes_RealData(t *testing.T) {
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
log.PrintErrorf(err, "Test failure.")
}
}()
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
log.PrintErrorf(err, "Test failure.")
}
}()
filepath := path.Join(assetsPath, "NDM_8901.jpg")
filepath := path.Join(assetsPath, "NDM_8901.jpg")
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
eh, index, err := Collect(rawExif)
log.PanicIf(err)
ti := NewTagIndex()
var ite *IfdTagEntry
for _, thisIte := range index.RootIfd.Entries {
if thisIte.TagId == 0x0110 {
ite = thisIte
break
}
}
eh, index, err := Collect(ti, rawExif)
log.PanicIf(err)
if ite == nil {
t.Fatalf("Tag not found.")
}
var ite *IfdTagEntry
for _, thisIte := range index.RootIfd.Entries {
if thisIte.TagId == 0x0110 {
ite = thisIte
break
}
}
addressableData := rawExif[ExifAddressableAreaStart:]
decodedBytes, err := ite.ValueBytes(addressableData, eh.ByteOrder)
log.PanicIf(err)
if ite == nil {
t.Fatalf("Tag not found.")
}
expected := []byte("Canon EOS 5D Mark III")
expected = append(expected, 0)
addressableData := rawExif[ExifAddressableAreaStart:]
decodedBytes, err := ite.ValueBytes(addressableData, eh.ByteOrder)
log.PanicIf(err)
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.")
}
expected := []byte("Canon EOS 5D Mark III")
expected = append(expected, 0)
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) {
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
log.PrintErrorf(err, "Test failure.")
}
}()
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
log.PrintErrorf(err, "Test failure.")
}
}()
filepath := path.Join(assetsPath, "NDM_8901.jpg")
filepath := path.Join(assetsPath, "NDM_8901.jpg")
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
eh, index, err := Collect(rawExif)
log.PanicIf(err)
ti := NewTagIndex()
var ite *IfdTagEntry
for _, thisIte := range index.RootIfd.Entries {
if thisIte.TagId == 0x0110 {
ite = thisIte
break
}
}
eh, index, err := Collect(ti, rawExif)
log.PanicIf(err)
if ite == nil {
t.Fatalf("Tag not found.")
}
var ite *IfdTagEntry
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)
log.PanicIf(err)
itevr := NewIfdTagEntryValueResolver(rawExif, eh.ByteOrder)
expected := []byte("Canon EOS 5D Mark III")
expected = append(expected, 0)
decodedBytes, err := itevr.ValueBytes(ite)
log.PanicIf(err)
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.")
}
expected := []byte("Canon EOS 5D Mark III")
expected = append(expected, 0)
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) {
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
log.PrintErrorf(err, "Test failure.")
}
}()
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
log.PrintErrorf(err, "Test failure.")
}
}()
filepath := path.Join(assetsPath, "NDM_8901.jpg")
filepath := path.Join(assetsPath, "NDM_8901.jpg")
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
eh, index, err := Collect(rawExif)
log.PanicIf(err)
ti := NewTagIndex()
ii, _ := IfdIdOrFail(IfdStandard, IfdExif)
ifdExif := index.Lookup[ii][0]
eh, index, err := Collect(ti, rawExif)
log.PanicIf(err)
var ite *IfdTagEntry
for _, thisIte := range ifdExif.Entries {
if thisIte.TagId == 0x9000 {
ite = thisIte
break
}
}
ii, _ := IfdIdOrFail(IfdStandard, IfdExif)
ifdExif := index.Lookup[ii][0]
if ite == nil {
t.Fatalf("Tag not found.")
}
var ite *IfdTagEntry
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)
log.PanicIf(err)
itevr := NewIfdTagEntryValueResolver(rawExif, eh.ByteOrder)
expected := []byte { '0', '2', '3', '0' }
decodedBytes, err := itevr.ValueBytes(ite)
log.PanicIf(err)
if len(decodedBytes) != int(ite.UnitCount) {
t.Fatalf("Decoded bytes not the right count.")
} else if bytes.Compare(decodedBytes, expected) != 0 {
t.Fatalf("Recovered unknown value is not correct.")
}
expected := []byte{'0', '2', '3', '0'}
if len(decodedBytes) != int(ite.UnitCount) {
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) {
filepath := path.Join(assetsPath, "NDM_8901.jpg")
filepath := path.Join(assetsPath, "NDM_8901.jpg")
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
_, index, err := Collect(rawExif)
log.PanicIf(err)
ti := NewTagIndex()
ifd := index.RootIfd
results, err := ifd.FindTagWithId(0x011b)
_, index, err := Collect(ti, rawExif)
log.PanicIf(err)
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])
}
ifd := index.RootIfd
results, err := ifd.FindTagWithId(0x011b)
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) {
filepath := path.Join(assetsPath, "NDM_8901.jpg")
filepath := path.Join(assetsPath, "NDM_8901.jpg")
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
_, index, err := Collect(rawExif)
log.PanicIf(err)
ti := NewTagIndex()
ifd := index.RootIfd
_, index, err := Collect(ti, rawExif)
log.PanicIf(err)
_, err = ifd.FindTagWithId(0xffff)
if err == nil {
t.Fatalf("Expected error for not-found tag.")
} else if log.Is(err, ErrTagNotFound) == false {
log.Panic(err)
}
ifd := index.RootIfd
_, err = ifd.FindTagWithId(0xffff)
if err == nil {
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) {
filepath := path.Join(assetsPath, "NDM_8901.jpg")
filepath := path.Join(assetsPath, "NDM_8901.jpg")
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
_, index, err := Collect(rawExif)
log.PanicIf(err)
ti := NewTagIndex()
ifd := index.RootIfd
results, err := ifd.FindTagWithName("YResolution")
_, index, err := Collect(ti, rawExif)
log.PanicIf(err)
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])
}
ifd := index.RootIfd
results, err := ifd.FindTagWithName("YResolution")
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) {
filepath := path.Join(assetsPath, "NDM_8901.jpg")
filepath := path.Join(assetsPath, "NDM_8901.jpg")
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
_, index, err := Collect(rawExif)
log.PanicIf(err)
ti := NewTagIndex()
ifd := index.RootIfd
_, index, err := Collect(ti, rawExif)
log.PanicIf(err)
_, err = ifd.FindTagWithName("PlanarConfiguration")
if err == nil {
t.Fatalf("Expected error for not-found tag.")
} else if log.Is(err, ErrTagNotFound) == false {
log.Panic(err)
}
ifd := index.RootIfd
_, err = ifd.FindTagWithName("PlanarConfiguration")
if err == nil {
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) {
filepath := path.Join(assetsPath, "NDM_8901.jpg")
filepath := path.Join(assetsPath, "NDM_8901.jpg")
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
_, index, err := Collect(rawExif)
log.PanicIf(err)
ti := NewTagIndex()
ifd := index.RootIfd
_, index, err := Collect(ti, rawExif)
log.PanicIf(err)
_, err = ifd.FindTagWithName("GeorgeNotAtHome")
if err == nil {
t.Fatalf("Expected error for not-found tag.")
} else if log.Is(err, ErrTagNotStandard) == false {
log.Panic(err)
}
ifd := index.RootIfd
_, err = ifd.FindTagWithName("GeorgeNotAtHome")
if err == nil {
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) {
filepath := path.Join(assetsPath, "NDM_8901.jpg")
filepath := path.Join(assetsPath, "NDM_8901.jpg")
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
_, index, err := Collect(rawExif)
log.PanicIf(err)
ti := NewTagIndex()
ifd := index.RootIfd
_, index, err := Collect(ti, rawExif)
log.PanicIf(err)
if ifd.NextIfd == nil {
t.Fatalf("There is no IFD1.")
}
ifd := index.RootIfd
// The thumbnail is in IFD1 (The second root IFD).
actual, err := ifd.NextIfd.Thumbnail()
log.PanicIf(err)
if ifd.NextIfd == nil {
t.Fatalf("There is no IFD1.")
}
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)
log.PanicIf(err)
expectedFilepath := path.Join(assetsPath, "NDM_8901.jpg.thumbnail")
if bytes.Compare(actual, expected) != 0 {
t.Fatalf("thumbnail not correct")
}
expected, err := ioutil.ReadFile(expectedFilepath)
log.PanicIf(err)
if bytes.Compare(actual, expected) != 0 {
t.Fatalf("thumbnail not correct")
}
}
func TestIfd_GpsInfo(t *testing.T) {
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
log.PrintErrorf(err, "Test failure.")
}
}()
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
log.PrintErrorf(err, "Test failure.")
}
}()
filepath := path.Join(assetsPath, "gps.jpg")
filepath := path.Join(assetsPath, "gps.jpg")
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
_, index, err := Collect(rawExif)
log.PanicIf(err)
ti := NewTagIndex()
ifd, err := index.RootIfd.ChildWithIfdIdentity(GpsIi)
log.PanicIf(err)
_, index, err := Collect(ti, rawExif)
log.PanicIf(err)
gi, err := ifd.GpsInfo()
log.PanicIf(err)
ifd, err := index.RootIfd.ChildWithIfdIdentity(GpsIi)
log.PanicIf(err)
if gi.Latitude.Orientation != 'N' || gi.Latitude.Degrees != 26 || gi.Latitude.Minutes != 35 || gi.Latitude.Seconds != 12 {
t.Fatalf("latitude not correct")
} else if gi.Longitude.Orientation != 'W' || gi.Longitude.Degrees != 80 || gi.Longitude.Minutes != 3 || gi.Longitude.Seconds != 13 {
t.Fatalf("longitude not correct")
} else if gi.Altitude != 0 {
t.Fatalf("altitude not correct")
} else if gi.Timestamp.Unix() != 1524964977 {
t.Fatalf("timestamp not correct")
} else if gi.Altitude != 0 {
t.Fatalf("altitude not correct")
}
gi, err := ifd.GpsInfo()
log.PanicIf(err)
if gi.Latitude.Orientation != 'N' || gi.Latitude.Degrees != 26 || gi.Latitude.Minutes != 35 || gi.Latitude.Seconds != 12 {
t.Fatalf("latitude not correct")
} else if gi.Longitude.Orientation != 'W' || gi.Longitude.Degrees != 80 || gi.Longitude.Minutes != 3 || gi.Longitude.Seconds != 13 {
t.Fatalf("longitude not correct")
} else if gi.Altitude != 0 {
t.Fatalf("altitude not correct")
} else if gi.Timestamp.Unix() != 1524964977 {
t.Fatalf("timestamp not correct")
} else if gi.Altitude != 0 {
t.Fatalf("altitude not correct")
}
}
func TestIfd_EnumerateTagsRecursively(t *testing.T) {
filepath := path.Join(assetsPath, "NDM_8901.jpg")
filepath := path.Join(assetsPath, "NDM_8901.jpg")
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
_, index, err := Collect(rawExif)
log.PanicIf(err)
ti := NewTagIndex()
_, 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 {
item := [2]interface{} {
ifd.Ii.IfdName,
int(ite.TagId),
}
cb := func(ifd *Ifd, ite *IfdTagEntry) error {
item := [2]interface{}{
ifd.Ii.IfdName,
int(ite.TagId),
}
collected = append(collected, item)
collected = append(collected, item)
return nil
}
return nil
}
err = index.RootIfd.EnumerateTagsRecursively(cb)
log.PanicIf(err)
err = index.RootIfd.EnumerateTagsRecursively(cb)
log.PanicIf(err)
expected := [][2]interface{} {
[2]interface{} { "IFD", 0x010f },
[2]interface{} { "IFD", 0x0110 },
[2]interface{} { "IFD", 0x0112 },
[2]interface{} { "IFD", 0x011a },
[2]interface{} { "IFD", 0x011b },
[2]interface{} { "IFD", 0x0128 },
[2]interface{} { "IFD", 0x0132 },
[2]interface{} { "IFD", 0x013b },
[2]interface{} { "IFD", 0x0213 },
[2]interface{} { "IFD", 0x8298 },
[2]interface{} { "Exif", 0x829a },
[2]interface{} { "Exif", 0x829d },
[2]interface{} { "Exif", 0x8822 },
[2]interface{} { "Exif", 0x8827 },
[2]interface{} { "Exif", 0x8830 },
[2]interface{} { "Exif", 0x8832 },
[2]interface{} { "Exif", 0x9000 },
[2]interface{} { "Exif", 0x9003 },
[2]interface{} { "Exif", 0x9004 },
[2]interface{} { "Exif", 0x9101 },
[2]interface{} { "Exif", 0x9201 },
[2]interface{} { "Exif", 0x9202 },
[2]interface{} { "Exif", 0x9204 },
[2]interface{} { "Exif", 0x9207 },
[2]interface{} { "Exif", 0x9209 },
[2]interface{} { "Exif", 0x920a },
[2]interface{} { "Exif", 0x927c },
[2]interface{} { "Exif", 0x9286 },
[2]interface{} { "Exif", 0x9290 },
[2]interface{} { "Exif", 0x9291 },
[2]interface{} { "Exif", 0x9292 },
[2]interface{} { "Exif", 0xa000 },
[2]interface{} { "Exif", 0xa001 },
[2]interface{} { "Exif", 0xa002 },
[2]interface{} { "Exif", 0xa003 },
[2]interface{} { "Iop", 0x0001 },
[2]interface{} { "Iop", 0x0002 },
[2]interface{} { "Exif", 0xa20e },
[2]interface{} { "Exif", 0xa20f },
[2]interface{} { "Exif", 0xa210 },
[2]interface{} { "Exif", 0xa401 },
[2]interface{} { "Exif", 0xa402 },
[2]interface{} { "Exif", 0xa403 },
[2]interface{} { "Exif", 0xa406 },
[2]interface{} { "Exif", 0xa430 },
[2]interface{} { "Exif", 0xa431 },
[2]interface{} { "Exif", 0xa432 },
[2]interface{} { "Exif", 0xa434 },
[2]interface{} { "Exif", 0xa435 },
[2]interface{} { "GPSInfo", 0x0000 },
[2]interface{} { "IFD", 0x010f },
[2]interface{} { "IFD", 0x0110 },
[2]interface{} { "IFD", 0x0112 },
[2]interface{} { "IFD", 0x011a },
[2]interface{} { "IFD", 0x011b },
[2]interface{} { "IFD", 0x0128 },
[2]interface{} { "IFD", 0x0132 },
[2]interface{} { "IFD", 0x013b },
[2]interface{} { "IFD", 0x0213 },
[2]interface{} { "IFD", 0x8298 },
[2]interface{} { "Exif", 0x829a },
[2]interface{} { "Exif", 0x829d },
[2]interface{} { "Exif", 0x8822 },
[2]interface{} { "Exif", 0x8827 },
[2]interface{} { "Exif", 0x8830 },
[2]interface{} { "Exif", 0x8832 },
[2]interface{} { "Exif", 0x9000 },
[2]interface{} { "Exif", 0x9003 },
[2]interface{} { "Exif", 0x9004 },
[2]interface{} { "Exif", 0x9101 },
[2]interface{} { "Exif", 0x9201 },
[2]interface{} { "Exif", 0x9202 },
[2]interface{} { "Exif", 0x9204 },
[2]interface{} { "Exif", 0x9207 },
[2]interface{} { "Exif", 0x9209 },
[2]interface{} { "Exif", 0x920a },
[2]interface{} { "Exif", 0x927c },
[2]interface{} { "Exif", 0x9286 },
[2]interface{} { "Exif", 0x9290 },
[2]interface{} { "Exif", 0x9291 },
[2]interface{} { "Exif", 0x9292 },
[2]interface{} { "Exif", 0xa000 },
[2]interface{} { "Exif", 0xa001 },
[2]interface{} { "Exif", 0xa002 },
[2]interface{} { "Exif", 0xa003 },
[2]interface{} { "Iop", 0x0001 },
[2]interface{} { "Iop", 0x0002 },
[2]interface{} { "Exif", 0xa20e },
[2]interface{} { "Exif", 0xa20f },
[2]interface{} { "Exif", 0xa210 },
[2]interface{} { "Exif", 0xa401 },
[2]interface{} { "Exif", 0xa402 },
[2]interface{} { "Exif", 0xa403 },
[2]interface{} { "Exif", 0xa406 },
[2]interface{} { "Exif", 0xa430 },
[2]interface{} { "Exif", 0xa431 },
[2]interface{} { "Exif", 0xa432 },
[2]interface{} { "Exif", 0xa434 },
[2]interface{} { "Exif", 0xa435 },
[2]interface{} { "GPSInfo", 0x0000 },
}
expected := [][2]interface{}{
[2]interface{}{"IFD", 0x010f},
[2]interface{}{"IFD", 0x0110},
[2]interface{}{"IFD", 0x0112},
[2]interface{}{"IFD", 0x011a},
[2]interface{}{"IFD", 0x011b},
[2]interface{}{"IFD", 0x0128},
[2]interface{}{"IFD", 0x0132},
[2]interface{}{"IFD", 0x013b},
[2]interface{}{"IFD", 0x0213},
[2]interface{}{"IFD", 0x8298},
[2]interface{}{"Exif", 0x829a},
[2]interface{}{"Exif", 0x829d},
[2]interface{}{"Exif", 0x8822},
[2]interface{}{"Exif", 0x8827},
[2]interface{}{"Exif", 0x8830},
[2]interface{}{"Exif", 0x8832},
[2]interface{}{"Exif", 0x9000},
[2]interface{}{"Exif", 0x9003},
[2]interface{}{"Exif", 0x9004},
[2]interface{}{"Exif", 0x9101},
[2]interface{}{"Exif", 0x9201},
[2]interface{}{"Exif", 0x9202},
[2]interface{}{"Exif", 0x9204},
[2]interface{}{"Exif", 0x9207},
[2]interface{}{"Exif", 0x9209},
[2]interface{}{"Exif", 0x920a},
[2]interface{}{"Exif", 0x927c},
[2]interface{}{"Exif", 0x9286},
[2]interface{}{"Exif", 0x9290},
[2]interface{}{"Exif", 0x9291},
[2]interface{}{"Exif", 0x9292},
[2]interface{}{"Exif", 0xa000},
[2]interface{}{"Exif", 0xa001},
[2]interface{}{"Exif", 0xa002},
[2]interface{}{"Exif", 0xa003},
[2]interface{}{"Iop", 0x0001},
[2]interface{}{"Iop", 0x0002},
[2]interface{}{"Exif", 0xa20e},
[2]interface{}{"Exif", 0xa20f},
[2]interface{}{"Exif", 0xa210},
[2]interface{}{"Exif", 0xa401},
[2]interface{}{"Exif", 0xa402},
[2]interface{}{"Exif", 0xa403},
[2]interface{}{"Exif", 0xa406},
[2]interface{}{"Exif", 0xa430},
[2]interface{}{"Exif", 0xa431},
[2]interface{}{"Exif", 0xa432},
[2]interface{}{"Exif", 0xa434},
[2]interface{}{"Exif", 0xa435},
[2]interface{}{"GPSInfo", 0x0000},
[2]interface{}{"IFD", 0x010f},
[2]interface{}{"IFD", 0x0110},
[2]interface{}{"IFD", 0x0112},
[2]interface{}{"IFD", 0x011a},
[2]interface{}{"IFD", 0x011b},
[2]interface{}{"IFD", 0x0128},
[2]interface{}{"IFD", 0x0132},
[2]interface{}{"IFD", 0x013b},
[2]interface{}{"IFD", 0x0213},
[2]interface{}{"IFD", 0x8298},
[2]interface{}{"Exif", 0x829a},
[2]interface{}{"Exif", 0x829d},
[2]interface{}{"Exif", 0x8822},
[2]interface{}{"Exif", 0x8827},
[2]interface{}{"Exif", 0x8830},
[2]interface{}{"Exif", 0x8832},
[2]interface{}{"Exif", 0x9000},
[2]interface{}{"Exif", 0x9003},
[2]interface{}{"Exif", 0x9004},
[2]interface{}{"Exif", 0x9101},
[2]interface{}{"Exif", 0x9201},
[2]interface{}{"Exif", 0x9202},
[2]interface{}{"Exif", 0x9204},
[2]interface{}{"Exif", 0x9207},
[2]interface{}{"Exif", 0x9209},
[2]interface{}{"Exif", 0x920a},
[2]interface{}{"Exif", 0x927c},
[2]interface{}{"Exif", 0x9286},
[2]interface{}{"Exif", 0x9290},
[2]interface{}{"Exif", 0x9291},
[2]interface{}{"Exif", 0x9292},
[2]interface{}{"Exif", 0xa000},
[2]interface{}{"Exif", 0xa001},
[2]interface{}{"Exif", 0xa002},
[2]interface{}{"Exif", 0xa003},
[2]interface{}{"Iop", 0x0001},
[2]interface{}{"Iop", 0x0002},
[2]interface{}{"Exif", 0xa20e},
[2]interface{}{"Exif", 0xa20f},
[2]interface{}{"Exif", 0xa210},
[2]interface{}{"Exif", 0xa401},
[2]interface{}{"Exif", 0xa402},
[2]interface{}{"Exif", 0xa403},
[2]interface{}{"Exif", 0xa406},
[2]interface{}{"Exif", 0xa430},
[2]interface{}{"Exif", 0xa431},
[2]interface{}{"Exif", 0xa432},
[2]interface{}{"Exif", 0xa434},
[2]interface{}{"Exif", 0xa435},
[2]interface{}{"GPSInfo", 0x0000},
}
if reflect.DeepEqual(collected, expected) != true {
fmt.Printf("ACTUAL:\n")
fmt.Printf("\n")
if reflect.DeepEqual(collected, expected) != true {
fmt.Printf("ACTUAL:\n")
fmt.Printf("\n")
for _, item := range collected {
fmt.Printf("[2]interface{} { \"%s\", 0x%04x },\n", item[0], item[1])
}
for _, item := range collected {
fmt.Printf("[2]interface{} { \"%s\", 0x%04x },\n", item[0], item[1])
}
fmt.Printf("\n")
fmt.Printf("\n")
fmt.Printf("EXPECTED:\n")
fmt.Printf("\n")
fmt.Printf("EXPECTED:\n")
fmt.Printf("\n")
for _, item := range expected {
fmt.Printf("[2]interface{} { \"%s\", 0x%04x },\n", item[0], item[1])
}
for _, item := range expected {
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() {
filepath := path.Join(assetsPath, "NDM_8901.jpg")
filepath := path.Join(assetsPath, "NDM_8901.jpg")
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
_, index, err := Collect(rawExif)
log.PanicIf(err)
ti := NewTagIndex()
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)
log.PanicIf(err)
return nil
}
// Output:
err = index.RootIfd.EnumerateTagsRecursively(cb)
log.PanicIf(err)
// Output:
}
func ExampleIfd_GpsInfo() {
filepath := path.Join(assetsPath, "gps.jpg")
filepath := path.Join(assetsPath, "gps.jpg")
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)
_, index, err := Collect(rawExif)
log.PanicIf(err)
ti := NewTagIndex()
ifd, err := index.RootIfd.ChildWithIfdIdentity(GpsIi)
log.PanicIf(err)
_, index, err := Collect(ti, rawExif)
log.PanicIf(err)
gi, err := ifd.GpsInfo()
log.PanicIf(err)
ifd, err := index.RootIfd.ChildWithIfdIdentity(GpsIi)
log.PanicIf(err)
fmt.Printf("%s\n", gi)
gi, err := ifd.GpsInfo()
log.PanicIf(err)
// Output:
// GpsInfo<LAT=(26.58667) LON=(-80.05361) ALT=(0) TIME=[2018-04-29 01:22:57 +0000 UTC]>
fmt.Printf("%s\n", gi)
// Output:
// GpsInfo<LAT=(26.58667) LON=(-80.05361) ALT=(0) TIME=[2018-04-29 01:22:57 +0000 UTC]>
}

12
tags.go
View File

@ -37,8 +37,6 @@ var (
// blob (not a slice of longs).
ThumbnailOffsetTagId: struct{}{},
}
tagIndex *TagIndex
)
var (
@ -64,15 +62,15 @@ type IndexedTag struct {
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)
}
func (it IndexedTag) IsName(ifd, name string) bool {
func (it *IndexedTag) IsName(ifd, name string) bool {
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
}
@ -318,7 +316,3 @@ func IfdId(parentIfdName, ifdName string) (ii IfdIdentity, id int) {
return ii, id
}
func init() {
tagIndex = NewTagIndex()
}

View File

@ -1,194 +1,198 @@
package exif
import (
"testing"
"testing"
"github.com/dsoprea/go-logging"
"github.com/dsoprea/go-logging"
)
var (
invalidIi = IfdIdentity{
ParentIfdName: "invalid-parent",
IfdName: "invalid-child",
}
invalidIi = IfdIdentity{
ParentIfdName: "invalid-parent",
IfdName: "invalid-child",
}
)
func TestGet(t *testing.T) {
it, err := tagIndex.Get(RootIi, 0x10f)
log.PanicIf(err)
ti := NewTagIndex()
if it.Is("IFD", 0x10f) == false || it.IsName("IFD", "Make") == false {
t.Fatalf("tag info not correct")
}
it, err := ti.Get(RootIi, 0x10f)
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) {
it, err := tagIndex.GetWithName(RootIi, "Make")
log.PanicIf(err)
ti := NewTagIndex()
if it.Is("IFD", 0x10f) == false || it.Is("IFD", 0x10f) == false {
t.Fatalf("tag info not correct")
}
it, err := ti.GetWithName(RootIi, "Make")
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) {
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
if err.Error() != "tag-ID (0x1234) under parent IFD [Exif] not associated with a child IFD" {
log.Panic(err)
}
}
}()
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
if err.Error() != "tag-ID (0x1234) under parent IFD [Exif] not associated with a child IFD" {
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) {
name := IfdTagNameWithIdOrFail(IfdStandard, IfdExifId)
if name != IfdExif {
t.Fatalf("expected EXIF IFD name for hit on EXIF IFD")
}
name := IfdTagNameWithIdOrFail(IfdStandard, IfdExifId)
if name != IfdExif {
t.Fatalf("expected EXIF IFD name for hit on EXIF IFD")
}
}
func TestIfdTagNameWithIdOrFail_ChildChildIfd(t *testing.T) {
name := IfdTagNameWithIdOrFail(IfdExif, IfdIopId)
if name != IfdIop {
t.Fatalf("expected IOP IFD name for hit on IOP IFD")
}
name := IfdTagNameWithIdOrFail(IfdExif, IfdIopId)
if name != IfdIop {
t.Fatalf("expected IOP IFD name for hit on IOP IFD")
}
}
func TestIfdTagNameWithId_Hit(t *testing.T) {
name, found := IfdTagNameWithId(IfdExif, IfdIopId)
if found != true {
t.Fatalf("could not get name for IOP IFD tag-ID")
} else if name != IfdIop {
t.Fatalf("expected IOP IFD name for hit on IOP IFD")
}
name, found := IfdTagNameWithId(IfdExif, IfdIopId)
if found != true {
t.Fatalf("could not get name for IOP IFD tag-ID")
} else if name != IfdIop {
t.Fatalf("expected IOP IFD name for hit on IOP IFD")
}
}
func TestIfdTagNameWithId_Miss(t *testing.T) {
name, found := IfdTagNameWithId(IfdExif, 0x1234)
if found != false {
t.Fatalf("expected failure for invalid IFD iag-ID under EXIF IFD")
} else if name != "" {
t.Fatalf("expected empty IFD name for miss")
}
name, found := IfdTagNameWithId(IfdExif, 0x1234)
if found != false {
t.Fatalf("expected failure for invalid IFD iag-ID under EXIF IFD")
} else if name != "" {
t.Fatalf("expected empty IFD name for miss")
}
}
func TestIfdTagIdWithIdentity_Hit(t *testing.T) {
tagId, found := IfdTagIdWithIdentity(GpsIi)
if found != true {
t.Fatalf("could not get tag-ID for II")
} else if tagId != IfdGpsId {
t.Fatalf("incorrect tag-ID returned for II")
}
tagId, found := IfdTagIdWithIdentity(GpsIi)
if found != true {
t.Fatalf("could not get tag-ID for II")
} else if tagId != IfdGpsId {
t.Fatalf("incorrect tag-ID returned for II")
}
}
func TestIfdTagIdWithIdentity_Miss(t *testing.T) {
tagId, found := IfdTagIdWithIdentity(invalidIi)
if found != false {
t.Fatalf("expected failure")
} else if tagId != 0 {
t.Fatalf("expected tag-ID of (0) for failure")
}
tagId, found := IfdTagIdWithIdentity(invalidIi)
if found != false {
t.Fatalf("expected failure")
} else if tagId != 0 {
t.Fatalf("expected tag-ID of (0) for failure")
}
}
func TestIfdTagIdWithIdentityOrFail_Hit(t *testing.T) {
IfdTagIdWithIdentityOrFail(GpsIi)
IfdTagIdWithIdentityOrFail(GpsIi)
}
func TestIfdTagIdWithIdentityOrFail_Miss(t *testing.T) {
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
if err.Error() != "no tag for invalid IFD identity: IfdIdentity<PARENT-NAME=[invalid-parent] NAME=[invalid-child]>" {
log.Panic(err)
}
}
}()
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
if err.Error() != "no tag for invalid IFD identity: IfdIdentity<PARENT-NAME=[invalid-parent] NAME=[invalid-child]>" {
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) {
id := IfdIdWithIdentity(GpsIi)
if id != 3 {
t.Fatalf("II doesn't have the right ID")
}
id := IfdIdWithIdentity(GpsIi)
if id != 3 {
t.Fatalf("II doesn't have the right ID")
}
}
func TestIfdIdWithIdentity_Miss(t *testing.T) {
id := IfdIdWithIdentity(invalidIi)
if id != 0 {
t.Fatalf("II doesn't have the right ID for a miss")
}
id := IfdIdWithIdentity(invalidIi)
if id != 0 {
t.Fatalf("II doesn't have the right ID for a miss")
}
}
func TestIfdIdWithIdentityOrFail_Hit(t *testing.T) {
id := IfdIdWithIdentityOrFail(GpsIi)
if id != 3 {
t.Fatalf("II doesn't have the right ID")
}
id := IfdIdWithIdentityOrFail(GpsIi)
if id != 3 {
t.Fatalf("II doesn't have the right ID")
}
}
func TestIfdIdWithIdentityOrFail_Miss(t *testing.T) {
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
if err.Error() != "IFD not valid: IfdIdentity<PARENT-NAME=[invalid-parent] NAME=[invalid-child]>" {
log.Panic(err)
}
}
}()
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
if err.Error() != "IFD not valid: IfdIdentity<PARENT-NAME=[invalid-parent] NAME=[invalid-child]>" {
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) {
ii, id := IfdIdOrFail(IfdStandard, IfdExif)
if ii != ExifIi {
t.Fatalf("wrong II for IFD returned")
} else if id != 2 {
t.Fatalf("wrong ID for II returned")
}
ii, id := IfdIdOrFail(IfdStandard, IfdExif)
if ii != ExifIi {
t.Fatalf("wrong II for IFD returned")
} else if id != 2 {
t.Fatalf("wrong ID for II returned")
}
}
func TestIfdIdOrFail_Miss(t *testing.T) {
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
if err.Error() != "IFD is not valid: [IFD] [invalid-ifd]" {
log.Panic(err)
}
}
}()
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
if err.Error() != "IFD is not valid: [IFD] [invalid-ifd]" {
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) {
ii, id := IfdId(IfdStandard, IfdExif)
if ii != ExifIi {
t.Fatalf("wrong II for IFD returned")
} else if id != 2 {
t.Fatalf("wrong ID for II returned")
}
ii, id := IfdId(IfdStandard, IfdExif)
if ii != ExifIi {
t.Fatalf("wrong II for IFD returned")
} else if id != 2 {
t.Fatalf("wrong ID for II returned")
}
}
func TestIfdId_Miss(t *testing.T) {
ii, id := IfdId(IfdStandard, "invalid-ifd")
if id != 0 {
t.Fatalf("non-zero ID returned for invalid IFD")
} else if ii != ZeroIi {
t.Fatalf("expected zero-instance result for miss")
}
ii, id := IfdId(IfdStandard, "invalid-ifd")
if id != 0 {
t.Fatalf("non-zero ID returned for invalid IFD")
} else if ii != ZeroIi {
t.Fatalf("expected zero-instance result for miss")
}
}

View File

@ -1,83 +1,81 @@
package exif
import (
"testing"
"bytes"
"bytes"
"testing"
"github.com/dsoprea/go-logging"
"github.com/dsoprea/go-logging"
)
func TestUndefinedValue_ExifVersion(t *testing.T) {
byteOrder := TestDefaultByteOrder
ii := ExifIi
byteOrder := TestDefaultByteOrder
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
// non-null-terminated string.
ve := NewValueEncoder(byteOrder)
ve := NewValueEncoder(byteOrder)
tt := NewTagType(TypeAsciiNoNul, byteOrder)
valueString := "0230"
tt := NewTagType(TypeAsciiNoNul, byteOrder)
valueString := "0230"
ed, err := ve.EncodeWithType(tt, valueString)
log.PanicIf(err)
ed, err := ve.EncodeWithType(tt, valueString)
log.PanicIf(err)
// Create the tag using the official "unknown" type now that we already
// have the bytes.
encodedValue := NewIfdBuilderTagValueFromBytes(ed.Encoded)
// Create the tag using the official "unknown" type now that we already
// have the bytes.
bt := &BuilderTag{
ii: ii,
tagId: 0x9000,
typeId: TypeUndefined,
value: encodedValue,
}
encodedValue := NewIfdBuilderTagValueFromBytes(ed.Encoded)
// Stage the build.
bt := &BuilderTag{
ii: ii,
tagId: 0x9000,
typeId: TypeUndefined,
value: encodedValue,
}
ti := NewTagIndex()
ibe := NewIfdByteEncoder()
ib := NewIfdBuilder(ti, ii, byteOrder)
// Stage the build.
b := new(bytes.Buffer)
bw := NewByteWriter(b, byteOrder)
ibe := NewIfdByteEncoder()
ib := NewIfdBuilder(ii, byteOrder)
addressableOffset := uint32(0x1234)
ida := newIfdDataAllocator(addressableOffset)
b := new(bytes.Buffer)
bw := NewByteWriter(b, byteOrder)
// Encode.
addressableOffset := uint32(0x1234)
ida := newIfdDataAllocator(addressableOffset)
_, err = ibe.encodeTagToBytes(ib, bt, bw, ida, uint32(0))
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))
log.PanicIf(err)
ite, err := ParseOneTag(ii, byteOrder, tagBytes, false)
log.PanicIf(err)
tagBytes := b.Bytes()
if len(tagBytes) != 12 {
t.Fatalf("Tag not encoded to the right number of bytes: (%d)", len(tagBytes))
}
ite, err := ParseOneTag(ii, byteOrder, tagBytes, false)
log.PanicIf(err)
if ite.TagId != 0x9000 {
t.Fatalf("Tag-ID not correct: (0x%02x)", ite.TagId)
} else if ite.TagIndex != 0 {
t.Fatalf("Tag index not correct: (%d)", ite.TagIndex)
} else if ite.TagType != TypeUndefined {
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)
}
if ite.TagId != 0x9000 {
t.Fatalf("Tag-ID not correct: (0x%02x)", ite.TagId)
} else if ite.TagIndex != 0 {
t.Fatalf("Tag index not correct: (%d)", ite.TagIndex)
} else if ite.TagType != TypeUndefined {
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