...

Source file src/github.com/dsoprea/go-exif/v3/command/exif-read-tool/main.go

Documentation: github.com/dsoprea/go-exif/v3/command/exif-read-tool

     1  // This tool dumps EXIF information from images.
     2  //
     3  // Example command-line:
     4  //
     5  //   exif-read-tool -filepath <file-path>
     6  //
     7  // Example Output:
     8  //
     9  //   IFD=[IfdIdentity<PARENT-NAME=[] NAME=[IFD]>] ID=(0x010f) NAME=[Make] COUNT=(6) TYPE=[ASCII] VALUE=[Canon]
    10  //   IFD=[IfdIdentity<PARENT-NAME=[] NAME=[IFD]>] ID=(0x0110) NAME=[Model] COUNT=(22) TYPE=[ASCII] VALUE=[Canon EOS 5D Mark III]
    11  //   IFD=[IfdIdentity<PARENT-NAME=[] NAME=[IFD]>] ID=(0x0112) NAME=[Orientation] COUNT=(1) TYPE=[SHORT] VALUE=[1]
    12  //   IFD=[IfdIdentity<PARENT-NAME=[] NAME=[IFD]>] ID=(0x011a) NAME=[XResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1]
    13  //   ...
    14  package main
    15  
    16  import (
    17  	"fmt"
    18  	"os"
    19  
    20  	"encoding/json"
    21  	"io/ioutil"
    22  
    23  	"github.com/dsoprea/go-logging"
    24  	"github.com/jessevdk/go-flags"
    25  
    26  	"github.com/dsoprea/go-exif/v3"
    27  	"github.com/dsoprea/go-exif/v3/common"
    28  )
    29  
    30  const (
    31  	thumbnailFilenameIndexPlaceholder = "<index>"
    32  )
    33  
    34  var (
    35  	mainLogger = log.NewLogger("main.main")
    36  )
    37  
    38  // IfdEntry is a JSON model for representing a single tag.
    39  type IfdEntry struct {
    40  	IfdPath     string                      `json:"ifd_path"`
    41  	FqIfdPath   string                      `json:"fq_ifd_path"`
    42  	IfdIndex    int                         `json:"ifd_index"`
    43  	TagId       uint16                      `json:"tag_id"`
    44  	TagName     string                      `json:"tag_name"`
    45  	TagTypeId   exifcommon.TagTypePrimitive `json:"tag_type_id"`
    46  	TagTypeName string                      `json:"tag_type_name"`
    47  	UnitCount   uint32                      `json:"unit_count"`
    48  	Value       interface{}                 `json:"value"`
    49  	ValueString string                      `json:"value_string"`
    50  }
    51  
    52  type parameters struct {
    53  	Filepath                string `short:"f" long:"filepath" required:"true" description:"File-path of image"`
    54  	PrintAsJson             bool   `short:"j" long:"json" description:"Print out as JSON"`
    55  	IsVerbose               bool   `short:"v" long:"verbose" description:"Print logging"`
    56  	ThumbnailOutputFilepath string `short:"t" long:"thumbnail-output-filepath" description:"File-path to write thumbnail to (if present)"`
    57  	DoNotPrintTags          bool   `short:"n" long:"no-tags" description:"Do not actually print tags. Good for auditing the logs or merely checking the EXIF structure for errors."`
    58  	SkipBlocks              int    `short:"s" long:"skip" description:"Skip this many EXIF blocks before returning"`
    59  	DoUniversalTagSearch    bool   `short:"u" long:"universal-tags" description:"If tags not found in known mapped IFDs, fallback to trying all IFDs."`
    60  }
    61  
    62  var (
    63  	arguments = new(parameters)
    64  )
    65  
    66  func main() {
    67  	defer func() {
    68  		if errRaw := recover(); errRaw != nil {
    69  			err := errRaw.(error)
    70  			log.PrintError(err)
    71  
    72  			os.Exit(-2)
    73  		}
    74  	}()
    75  
    76  	_, err := flags.Parse(arguments)
    77  	if err != nil {
    78  		os.Exit(-1)
    79  	}
    80  
    81  	if arguments.IsVerbose == true {
    82  		cla := log.NewConsoleLogAdapter()
    83  		log.AddAdapter("console", cla)
    84  
    85  		scp := log.NewStaticConfigurationProvider()
    86  		scp.SetLevelName(log.LevelNameDebug)
    87  
    88  		log.LoadConfiguration(scp)
    89  	}
    90  
    91  	f, err := os.Open(arguments.Filepath)
    92  	log.PanicIf(err)
    93  
    94  	data, err := ioutil.ReadAll(f)
    95  	log.PanicIf(err)
    96  
    97  	rawExif, err := exif.SearchAndExtractExifN(data, arguments.SkipBlocks)
    98  	if err != nil {
    99  		if err == exif.ErrNoExif {
   100  			fmt.Printf("No EXIF data.\n")
   101  			os.Exit(1)
   102  		}
   103  
   104  		log.Panic(err)
   105  	}
   106  
   107  	mainLogger.Debugf(nil, "EXIF blob is (%d) bytes.", len(rawExif))
   108  
   109  	// Run the parse.
   110  
   111  	entries, _, err := exif.GetFlatExifDataUniversalSearch(rawExif, nil, arguments.DoUniversalTagSearch)
   112  	if err != nil {
   113  		if arguments.SkipBlocks > 0 {
   114  			mainLogger.Warningf(nil, "Encountered an error. This might be related to the request to skip EXIF blocks.")
   115  		}
   116  
   117  		log.Panic(err)
   118  	}
   119  
   120  	// Write the thumbnail is requested and present.
   121  
   122  	thumbnailOutputFilepath := arguments.ThumbnailOutputFilepath
   123  	if thumbnailOutputFilepath != "" {
   124  		im, err := exifcommon.NewIfdMappingWithStandard()
   125  		log.PanicIf(err)
   126  
   127  		ti := exif.NewTagIndex()
   128  
   129  		_, index, err := exif.Collect(im, ti, rawExif)
   130  		log.PanicIf(err)
   131  
   132  		var thumbnail []byte
   133  		if ifd, found := index.Lookup[exif.ThumbnailFqIfdPath]; found == true {
   134  			thumbnail, err = ifd.Thumbnail()
   135  			if err != nil && err != exif.ErrNoThumbnail {
   136  				log.Panic(err)
   137  			}
   138  		}
   139  
   140  		if thumbnail == nil {
   141  			mainLogger.Debugf(nil, "No thumbnails found.")
   142  		} else {
   143  			if arguments.PrintAsJson == false {
   144  				fmt.Printf("Writing (%d) bytes for thumbnail: [%s]\n", len(thumbnail), thumbnailOutputFilepath)
   145  				fmt.Printf("\n")
   146  			}
   147  
   148  			err := ioutil.WriteFile(thumbnailOutputFilepath, thumbnail, 0644)
   149  			log.PanicIf(err)
   150  		}
   151  	}
   152  
   153  	if arguments.DoNotPrintTags == false {
   154  		if arguments.PrintAsJson == true {
   155  			data, err := json.MarshalIndent(entries, "", "    ")
   156  			log.PanicIf(err)
   157  
   158  			fmt.Println(string(data))
   159  		} else {
   160  			thumbnailTags := 0
   161  			for _, entry := range entries {
   162  				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.Formatted)
   163  			}
   164  
   165  			fmt.Printf("\n")
   166  
   167  			if thumbnailTags == 2 {
   168  				fmt.Printf("There is a thumbnail.\n")
   169  				fmt.Printf("\n")
   170  			}
   171  		}
   172  	}
   173  }
   174  

View as plain text