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:
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 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)
} }
} }
} }

View File

@ -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)

View File

@ -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)>
} }

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) { 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)

File diff suppressed because it is too large Load Diff

View File

@ -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
View File

@ -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()
}

View File

@ -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")
} }
} }

View File

@ -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