go-exif/v2/exif-read-tool/main.go

175 lines
4.3 KiB
Go

// This tool dumps EXIF information from images.
//
// Example command-line:
//
// exif-read-tool -filepath <file-path>
//
// Example Output:
//
// IFD=[IfdIdentity<PARENT-NAME=[] NAME=[IFD]>] ID=(0x010f) NAME=[Make] COUNT=(6) TYPE=[ASCII] VALUE=[Canon]
// IFD=[IfdIdentity<PARENT-NAME=[] NAME=[IFD]>] ID=(0x0110) NAME=[Model] COUNT=(22) TYPE=[ASCII] VALUE=[Canon EOS 5D Mark III]
// IFD=[IfdIdentity<PARENT-NAME=[] NAME=[IFD]>] ID=(0x0112) NAME=[Orientation] COUNT=(1) TYPE=[SHORT] VALUE=[1]
// IFD=[IfdIdentity<PARENT-NAME=[] NAME=[IFD]>] ID=(0x011a) NAME=[XResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1]
// ...
package main
import (
"flag"
"fmt"
"os"
"encoding/json"
"io/ioutil"
"github.com/dsoprea/go-logging"
"github.com/dsoprea/go-exif/v2"
"github.com/dsoprea/go-exif/v2/common"
"github.com/dsoprea/go-exif/v2/undefined"
)
var (
mainLogger = log.NewLogger("main.main")
)
var (
filepathArg = ""
printAsJsonArg = false
printLoggingArg = false
)
type IfdEntry struct {
IfdPath string `json:"ifd_path"`
FqIfdPath string `json:"fq_ifd_path"`
IfdIndex int `json:"ifd_index"`
TagId uint16 `json:"tag_id"`
TagName string `json:"tag_name"`
TagTypeId exifcommon.TagTypePrimitive `json:"tag_type_id"`
TagTypeName string `json:"tag_type_name"`
UnitCount uint32 `json:"unit_count"`
Value interface{} `json:"value"`
ValueString string `json:"value_string"`
}
func main() {
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
log.PrintErrorf(err, "Program error.")
os.Exit(1)
}
}()
flag.StringVar(&filepathArg, "filepath", "", "File-path of image")
flag.BoolVar(&printAsJsonArg, "json", false, "Print JSON")
flag.BoolVar(&printLoggingArg, "verbose", false, "Print logging")
flag.Parse()
if filepathArg == "" {
fmt.Printf("Please provide a file-path for an image.\n")
os.Exit(1)
}
if printLoggingArg == true {
cla := log.NewConsoleLogAdapter()
log.AddAdapter("console", cla)
scp := log.NewStaticConfigurationProvider()
scp.SetLevelName(log.LevelNameDebug)
log.LoadConfiguration(scp)
}
f, err := os.Open(filepathArg)
log.PanicIf(err)
data, err := ioutil.ReadAll(f)
log.PanicIf(err)
rawExif, err := exif.SearchAndExtractExif(data)
if err != nil {
if err == exif.ErrNoExif {
fmt.Printf("No EXIF data.\n")
os.Exit(1)
}
log.Panic(err)
}
// Run the parse.
im := exif.NewIfdMappingWithStandard()
ti := exif.NewTagIndex()
entries := make([]IfdEntry, 0)
visitor := func(fqIfdPath string, ifdIndex int, ite *exif.IfdTagEntry) (err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
log.Panic(err)
}
}()
tagId := ite.TagId()
tagType := ite.TagType()
ifdPath, err := im.StripPathPhraseIndices(fqIfdPath)
log.PanicIf(err)
it, err := ti.Get(ifdPath, tagId)
if err != nil {
if log.Is(err, exif.ErrTagNotFound) {
mainLogger.Warningf(nil, "Unknown tag: [%s] (%04x)", ifdPath, tagId)
return nil
} else {
log.Panic(err)
}
}
value, err := ite.Value()
if err != nil {
if log.Is(err, exifcommon.ErrUnhandledUndefinedTypedTag) == true {
mainLogger.Warningf(nil, "Non-standard undefined tag: [%s] (%04x)", ifdPath, tagId)
return nil
}
log.Panic(err)
}
valueString, err := ite.FormatFirst()
log.PanicIf(err)
entry := IfdEntry{
IfdPath: ifdPath,
FqIfdPath: fqIfdPath,
IfdIndex: ifdIndex,
TagId: tagId,
TagName: it.Name,
TagTypeId: tagType,
TagTypeName: tagType.String(),
UnitCount: ite.UnitCount(),
Value: value,
ValueString: valueString,
}
entries = append(entries, entry)
return nil
}
_, err = exif.Visit(exifcommon.IfdStandard, im, ti, rawExif, visitor)
log.PanicIf(err)
if printAsJsonArg == true {
data, err := json.MarshalIndent(entries, "", " ")
log.PanicIf(err)
fmt.Println(string(data))
} else {
for _, entry := range entries {
fmt.Printf("IFD-PATH=[%s] ID=(0x%04x) NAME=[%s] COUNT=(%d) TYPE=[%s] VALUE=[%s]\n", entry.IfdPath, entry.TagId, entry.TagName, entry.UnitCount, entry.TagTypeName, entry.ValueString)
}
}
}