diff --git a/exif-read-tool/main.go b/exif-read-tool/main.go index ce4a79f..aeb790f 100644 --- a/exif-read-tool/main.go +++ b/exif-read-tool/main.go @@ -1,3 +1,16 @@ +// This tool dumps EXIF information from images. +// +// Example command-line: +// +// exif-read-tool -filepath +// +// Example Output: +// +// IFD=[IfdIdentity] ID=(0x010f) NAME=[Make] COUNT=(6) TYPE=[ASCII] VALUE=[Canon] +// IFD=[IfdIdentity] ID=(0x0110) NAME=[Model] COUNT=(22) TYPE=[ASCII] VALUE=[Canon EOS 5D Mark III] +// IFD=[IfdIdentity] ID=(0x0112) NAME=[Orientation] COUNT=(1) TYPE=[SHORT] VALUE=[1] +// IFD=[IfdIdentity] ID=(0x011a) NAME=[XResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1] +// ... package main import ( @@ -56,7 +69,7 @@ func main() { // Run the parse. ti := exif.NewTagIndex() - visitor := func(indexedIfdName string, 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() { if state := recover(); state != nil { err = log.Wrap(state.(error)) @@ -64,10 +77,10 @@ func main() { } }() - it, err := ti.Get(indexedIfdName, tagId) + it, err := ti.Get(ii, tagId) if err != nil { if log.Is(err, exif.ErrTagNotFound) { - fmt.Printf("WARNING: Unknown tag: [%s] (%04x)\n", indexedIfdName, tagId) + fmt.Printf("WARNING: Unknown tag: [%s] (%04x)\n", ii, tagId) return nil } else { log.Panic(err) @@ -76,7 +89,7 @@ func main() { valueString := "" if tagType.Type() == exif.TypeUndefined { - value, err := exif.UndefinedValue(indexedIfdName, tagId, valueContext, tagType.ByteOrder()) + value, err := exif.UndefinedValue(ii, tagId, valueContext, tagType.ByteOrder()) if log.Is(err, exif.ErrUnhandledUnknownTypedTag) { valueString = "!UNDEFINED!" } else if err != nil { @@ -89,10 +102,10 @@ func main() { log.PanicIf(err) } - fmt.Printf("IFD=[%s] ID=(0x%04x) NAME=[%s] COUNT=(%d) TYPE=[%s] VALUE=[%s]\n", indexedIfdName, tagId, it.Name, valueContext.UnitCount, tagType.Name(), valueString) + fmt.Printf("IFD=[%s] ID=(0x%04x) NAME=[%s] COUNT=(%d) TYPE=[%s] VALUE=[%s]\n", ii, tagId, it.Name, valueContext.UnitCount, tagType.Name(), valueString) return nil } - err = e.Visit(data[foundAt:], visitor) + _, err = e.Visit(data[foundAt:], visitor) log.PanicIf(err) } diff --git a/exif-read-tool/main_test.go b/exif-read-tool/main_test.go new file mode 100644 index 0000000..f7e1dac --- /dev/null +++ b/exif-read-tool/main_test.go @@ -0,0 +1,113 @@ +package main + +import ( + "testing" + "os" + "path" + "bytes" + "fmt" + + "os/exec" + + "github.com/dsoprea/go-logging" +) + +var ( + assetsPath = "" + appFilepath = "" +) + +func TestMain(t *testing.T) { + imageFilepath := path.Join(assetsPath, "NDM_8901.jpg") + + cmd := exec.Command( + "go", "run", appFilepath, + "-filepath", imageFilepath) + + b := new(bytes.Buffer) + cmd.Stdout = b + cmd.Stderr = b + + err := cmd.Run() + actual := b.String() + + if err != nil { + fmt.Printf(actual) + log.Panic(err) + } + + expected := `IFD=[IfdIdentity] ID=(0x010f) NAME=[Make] COUNT=(6) TYPE=[ASCII] VALUE=[Canon] +IFD=[IfdIdentity] ID=(0x0110) NAME=[Model] COUNT=(22) TYPE=[ASCII] VALUE=[Canon EOS 5D Mark III] +IFD=[IfdIdentity] ID=(0x0112) NAME=[Orientation] COUNT=(1) TYPE=[SHORT] VALUE=[1] +IFD=[IfdIdentity] ID=(0x011a) NAME=[XResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1] +IFD=[IfdIdentity] ID=(0x011b) NAME=[YResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1] +IFD=[IfdIdentity] ID=(0x0128) NAME=[ResolutionUnit] COUNT=(1) TYPE=[SHORT] VALUE=[2] +IFD=[IfdIdentity] ID=(0x0132) NAME=[DateTime] COUNT=(20) TYPE=[ASCII] VALUE=[2017:12:02 08:18:50] +IFD=[IfdIdentity] ID=(0x013b) NAME=[Artist] COUNT=(1) TYPE=[ASCII] VALUE=[] +IFD=[IfdIdentity] ID=(0x0213) NAME=[YCbCrPositioning] COUNT=(1) TYPE=[SHORT] VALUE=[2] +IFD=[IfdIdentity] ID=(0x8298) NAME=[Copyright] COUNT=(1) TYPE=[ASCII] VALUE=[] +IFD=[IfdIdentity] ID=(0x8769) NAME=[ExifTag] COUNT=(1) TYPE=[LONG] VALUE=[360] +IFD=[IfdIdentity] ID=(0x829a) NAME=[ExposureTime] COUNT=(1) TYPE=[RATIONAL] VALUE=[1/640] +IFD=[IfdIdentity] ID=(0x829d) NAME=[FNumber] COUNT=(1) TYPE=[RATIONAL] VALUE=[4/1] +IFD=[IfdIdentity] ID=(0x8822) NAME=[ExposureProgram] COUNT=(1) TYPE=[SHORT] VALUE=[4] +IFD=[IfdIdentity] ID=(0x8827) NAME=[ISOSpeedRatings] COUNT=(1) TYPE=[SHORT] VALUE=[1600] +IFD=[IfdIdentity] ID=(0x8830) NAME=[SensitivityType] COUNT=(1) TYPE=[SHORT] VALUE=[2] +IFD=[IfdIdentity] ID=(0x8832) NAME=[RecommendedExposureIndex] COUNT=(1) TYPE=[LONG] VALUE=[1600] +IFD=[IfdIdentity] ID=(0x9000) NAME=[ExifVersion] COUNT=(4) TYPE=[UNDEFINED] VALUE=[0230] +IFD=[IfdIdentity] ID=(0x9003) NAME=[DateTimeOriginal] COUNT=(20) TYPE=[ASCII] VALUE=[2017:12:02 08:18:50] +IFD=[IfdIdentity] ID=(0x9004) NAME=[DateTimeDigitized] COUNT=(20) TYPE=[ASCII] VALUE=[2017:12:02 08:18:50] +IFD=[IfdIdentity] ID=(0x9101) NAME=[ComponentsConfiguration] COUNT=(4) TYPE=[UNDEFINED] VALUE=[ComponentsConfiguration] +IFD=[IfdIdentity] ID=(0x9201) NAME=[ShutterSpeedValue] COUNT=(1) TYPE=[SRATIONAL] VALUE=[614400/65536] +IFD=[IfdIdentity] ID=(0x9202) NAME=[ApertureValue] COUNT=(1) TYPE=[RATIONAL] VALUE=[262144/65536] +IFD=[IfdIdentity] ID=(0x9204) NAME=[ExposureBiasValue] COUNT=(1) TYPE=[SRATIONAL] VALUE=[0/1] +IFD=[IfdIdentity] ID=(0x9207) NAME=[MeteringMode] COUNT=(1) TYPE=[SHORT] VALUE=[5] +IFD=[IfdIdentity] ID=(0x9209) NAME=[Flash] COUNT=(1) TYPE=[SHORT] VALUE=[16] +IFD=[IfdIdentity] ID=(0x920a) NAME=[FocalLength] COUNT=(1) TYPE=[RATIONAL] VALUE=[16/1] +IFD=[IfdIdentity] ID=(0x927c) NAME=[MakerNote] COUNT=(8152) TYPE=[UNDEFINED] VALUE=[MakerNote] +IFD=[IfdIdentity] ID=(0x9286) NAME=[UserComment] COUNT=(264) TYPE=[UNDEFINED] VALUE=[UserComment] +IFD=[IfdIdentity] ID=(0x9290) NAME=[SubSecTime] COUNT=(3) TYPE=[ASCII] VALUE=[00] +IFD=[IfdIdentity] ID=(0x9291) NAME=[SubSecTimeOriginal] COUNT=(3) TYPE=[ASCII] VALUE=[00] +IFD=[IfdIdentity] ID=(0x9292) NAME=[SubSecTimeDigitized] COUNT=(3) TYPE=[ASCII] VALUE=[00] +IFD=[IfdIdentity] ID=(0xa000) NAME=[FlashpixVersion] COUNT=(4) TYPE=[UNDEFINED] VALUE=[0100] +IFD=[IfdIdentity] ID=(0xa001) NAME=[ColorSpace] COUNT=(1) TYPE=[SHORT] VALUE=[1] +IFD=[IfdIdentity] ID=(0xa002) NAME=[PixelXDimension] COUNT=(1) TYPE=[SHORT] VALUE=[3840] +IFD=[IfdIdentity] ID=(0xa003) NAME=[PixelYDimension] COUNT=(1) TYPE=[SHORT] VALUE=[2560] +IFD=[IfdIdentity] ID=(0xa005) NAME=[InteroperabilityTag] COUNT=(1) TYPE=[LONG] VALUE=[9326] +IFD=[IfdIdentity] ID=(0x0001) NAME=[InteroperabilityIndex] COUNT=(4) TYPE=[ASCII] VALUE=[R98] +IFD=[IfdIdentity] ID=(0x0002) NAME=[InteroperabilityVersion] COUNT=(4) TYPE=[UNDEFINED] VALUE=[0100] +IFD=[IfdIdentity] ID=(0xa20e) NAME=[FocalPlaneXResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[3840000/1461] +IFD=[IfdIdentity] ID=(0xa20f) NAME=[FocalPlaneYResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[2560000/972] +IFD=[IfdIdentity] ID=(0xa210) NAME=[FocalPlaneResolutionUnit] COUNT=(1) TYPE=[SHORT] VALUE=[2] +IFD=[IfdIdentity] ID=(0xa401) NAME=[CustomRendered] COUNT=(1) TYPE=[SHORT] VALUE=[0] +IFD=[IfdIdentity] ID=(0xa402) NAME=[ExposureMode] COUNT=(1) TYPE=[SHORT] VALUE=[0] +IFD=[IfdIdentity] ID=(0xa403) NAME=[WhiteBalance] COUNT=(1) TYPE=[SHORT] VALUE=[0] +IFD=[IfdIdentity] ID=(0xa406) NAME=[SceneCaptureType] COUNT=(1) TYPE=[SHORT] VALUE=[0] +IFD=[IfdIdentity] ID=(0xa430) NAME=[CameraOwnerName] COUNT=(1) TYPE=[ASCII] VALUE=[] +IFD=[IfdIdentity] ID=(0xa431) NAME=[BodySerialNumber] COUNT=(13) TYPE=[ASCII] VALUE=[063024020097] +IFD=[IfdIdentity] ID=(0xa432) NAME=[LensSpecification] COUNT=(4) TYPE=[RATIONAL] VALUE=[16/1] +IFD=[IfdIdentity] ID=(0xa434) NAME=[LensModel] COUNT=(22) TYPE=[ASCII] VALUE=[EF16-35mm f/4L IS USM] +IFD=[IfdIdentity] ID=(0xa435) NAME=[LensSerialNumber] COUNT=(11) TYPE=[ASCII] VALUE=[2400001068] +IFD=[IfdIdentity] ID=(0x8825) NAME=[GPSTag] COUNT=(1) TYPE=[LONG] VALUE=[9554] +IFD=[IfdIdentity] ID=(0x0000) NAME=[GPSVersionID] COUNT=(4) TYPE=[BYTE] VALUE=[2] +IFD=[IfdIdentity] ID=(0x0103) NAME=[Compression] COUNT=(1) TYPE=[SHORT] VALUE=[6] +IFD=[IfdIdentity] ID=(0x011a) NAME=[XResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1] +IFD=[IfdIdentity] ID=(0x011b) NAME=[YResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1] +IFD=[IfdIdentity] ID=(0x0128) NAME=[ResolutionUnit] COUNT=(1) TYPE=[SHORT] VALUE=[2] +IFD=[IfdIdentity] ID=(0x0201) NAME=[JPEGInterchangeFormat] COUNT=(1) TYPE=[LONG] VALUE=[11444] +IFD=[IfdIdentity] ID=(0x0202) NAME=[JPEGInterchangeFormatLength] COUNT=(1) TYPE=[LONG] VALUE=[21491] +` + + if actual != expected { + t.Fatalf("Output not as expected:\n%s", actual) + } +} + +func init() { + goPath := os.Getenv("GOPATH") + if goPath == "" { + log.Panicf("GOPATH is empty") + } + + assetsPath = path.Join(goPath, "src", "github.com", "dsoprea", "go-exif", "assets") + appFilepath = path.Join(goPath, "src", "github.com", "dsoprea", "go-exif", "exif-read-tool", "main.go") +}