...
1
2
3
4
5
6
7
8
9
10
11
12
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
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
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
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