1 package exif
2
3 import (
4 "bufio"
5 "bytes"
6 "errors"
7 "fmt"
8 "io"
9 "os"
10
11 "encoding/binary"
12 "io/ioutil"
13
14 "github.com/dsoprea/go-logging"
15
16 "github.com/dsoprea/go-exif/v3/common"
17 )
18
19 const (
20
21
22 ExifAddressableAreaStart = uint32(0x0)
23
24
25
26
27
28
29
30 ExifDefaultFirstIfdOffset = uint32(2 + 2 + 4)
31 )
32
33 const (
34
35
36 ExifSignatureLength = 8
37 )
38
39 var (
40 exifLogger = log.NewLogger("exif.exif")
41
42 ExifBigEndianSignature = [4]byte{'M', 'M', 0x00, 0x2a}
43 ExifLittleEndianSignature = [4]byte{'I', 'I', 0x2a, 0x00}
44 )
45
46 var (
47 ErrNoExif = errors.New("no exif data")
48 ErrExifHeaderError = errors.New("exif header error")
49 )
50
51
52 func SearchAndExtractExif(data []byte) (rawExif []byte, err error) {
53 defer func() {
54 if state := recover(); state != nil {
55 err = log.Wrap(state.(error))
56 }
57 }()
58
59 b := bytes.NewBuffer(data)
60
61 rawExif, err = SearchAndExtractExifWithReader(b)
62 if err != nil {
63 if err == ErrNoExif {
64 return nil, err
65 }
66
67 log.Panic(err)
68 }
69
70 return rawExif, nil
71 }
72
73
74
75
76 func SearchAndExtractExifN(data []byte, n int) (rawExif []byte, err error) {
77 defer func() {
78 if state := recover(); state != nil {
79 err = log.Wrap(state.(error))
80 }
81 }()
82
83 skips := 0
84 totalDiscarded := 0
85 for {
86 b := bytes.NewBuffer(data)
87
88 var discarded int
89
90 rawExif, discarded, err = searchAndExtractExifWithReaderWithDiscarded(b)
91 if err != nil {
92 if err == ErrNoExif {
93 return nil, err
94 }
95
96 log.Panic(err)
97 }
98
99 exifLogger.Debugf(nil, "Read EXIF block (%d).", skips)
100
101 totalDiscarded += discarded
102
103 if skips >= n {
104 exifLogger.Debugf(nil, "Reached requested EXIF block (%d).", n)
105 break
106 }
107
108 nextOffset := discarded + 1
109 exifLogger.Debugf(nil, "Skipping EXIF block (%d) by seeking to position (%d).", skips, nextOffset)
110
111 data = data[nextOffset:]
112 skips++
113 }
114
115 exifLogger.Debugf(nil, "Found EXIF blob (%d) bytes from initial position.", totalDiscarded)
116 return rawExif, nil
117 }
118
119
120
121
122
123
124 func searchAndExtractExifWithReaderWithDiscarded(r io.Reader) (rawExif []byte, discarded int, err error) {
125 defer func() {
126 if state := recover(); state != nil {
127 err = log.Wrap(state.(error))
128 }
129 }()
130
131
132
133
134
135 br := bufio.NewReader(r)
136
137 for {
138 window, err := br.Peek(ExifSignatureLength)
139 if err != nil {
140 if err == io.EOF {
141 return nil, 0, ErrNoExif
142 }
143
144 log.Panic(err)
145 }
146
147 _, err = ParseExifHeader(window)
148 if err != nil {
149 if log.Is(err, ErrNoExif) == true {
150
151
152 _, err := br.Discard(1)
153 log.PanicIf(err)
154
155 discarded++
156
157 continue
158 }
159
160
161 log.Panic(err)
162 }
163
164 break
165 }
166
167 exifLogger.Debugf(nil, "Found EXIF blob (%d) bytes from initial position.", discarded)
168
169 rawExif, err = ioutil.ReadAll(br)
170 log.PanicIf(err)
171
172 return rawExif, discarded, nil
173 }
174
175
176
177
178
179
180 func SearchAndExtractExifWithReader(r io.Reader) (rawExif []byte, err error) {
181 defer func() {
182 if state := recover(); state != nil {
183 err = log.Wrap(state.(error))
184 }
185 }()
186
187 rawExif, _, err = searchAndExtractExifWithReaderWithDiscarded(r)
188 if err != nil {
189 if err == ErrNoExif {
190 return nil, err
191 }
192
193 log.Panic(err)
194 }
195
196 return rawExif, nil
197 }
198
199
200
201
202 func SearchFileAndExtractExif(filepath string) (rawExif []byte, err error) {
203 defer func() {
204 if state := recover(); state != nil {
205 err = log.Wrap(state.(error))
206 }
207 }()
208
209
210
211 f, err := os.Open(filepath)
212 log.PanicIf(err)
213
214 defer f.Close()
215
216 rawExif, err = SearchAndExtractExifWithReader(f)
217 log.PanicIf(err)
218
219 return rawExif, nil
220 }
221
222 type ExifHeader struct {
223 ByteOrder binary.ByteOrder
224 FirstIfdOffset uint32
225 }
226
227 func (eh ExifHeader) String() string {
228 return fmt.Sprintf("ExifHeader<BYTE-ORDER=[%v] FIRST-IFD-OFFSET=(0x%02x)>", eh.ByteOrder, eh.FirstIfdOffset)
229 }
230
231
232
233
234
235 func ParseExifHeader(data []byte) (eh ExifHeader, err error) {
236 defer func() {
237 if state := recover(); state != nil {
238 err = log.Wrap(state.(error))
239 }
240 }()
241
242
243
244
245
246
247 if len(data) < ExifSignatureLength {
248 exifLogger.Warningf(nil, "Not enough data for EXIF header: (%d)", len(data))
249 return eh, ErrNoExif
250 }
251
252 if bytes.Equal(data[:4], ExifBigEndianSignature[:]) == true {
253 exifLogger.Debugf(nil, "Byte-order is big-endian.")
254 eh.ByteOrder = binary.BigEndian
255 } else if bytes.Equal(data[:4], ExifLittleEndianSignature[:]) == true {
256 eh.ByteOrder = binary.LittleEndian
257 exifLogger.Debugf(nil, "Byte-order is little-endian.")
258 } else {
259 return eh, ErrNoExif
260 }
261
262 eh.FirstIfdOffset = eh.ByteOrder.Uint32(data[4:8])
263
264 return eh, nil
265 }
266
267
268 func Visit(rootIfdIdentity *exifcommon.IfdIdentity, ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, exifData []byte, visitor TagVisitorFn, so *ScanOptions) (eh ExifHeader, furthestOffset uint32, err error) {
269 defer func() {
270 if state := recover(); state != nil {
271 err = log.Wrap(state.(error))
272 }
273 }()
274
275 eh, err = ParseExifHeader(exifData)
276 log.PanicIf(err)
277
278 ebs := NewExifReadSeekerWithBytes(exifData)
279 ie := NewIfdEnumerate(ifdMapping, tagIndex, ebs, eh.ByteOrder)
280
281 _, err = ie.Scan(rootIfdIdentity, eh.FirstIfdOffset, visitor, so)
282 log.PanicIf(err)
283
284 furthestOffset = ie.FurthestOffset()
285
286 return eh, furthestOffset, nil
287 }
288
289
290 func Collect(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, exifData []byte) (eh ExifHeader, index IfdIndex, err error) {
291 defer func() {
292 if state := recover(); state != nil {
293 err = log.Wrap(state.(error))
294 }
295 }()
296
297 eh, err = ParseExifHeader(exifData)
298 log.PanicIf(err)
299
300 ebs := NewExifReadSeekerWithBytes(exifData)
301 ie := NewIfdEnumerate(ifdMapping, tagIndex, ebs, eh.ByteOrder)
302
303 index, err = ie.Collect(eh.FirstIfdOffset)
304 log.PanicIf(err)
305
306 return eh, index, nil
307 }
308
309
310 func BuildExifHeader(byteOrder binary.ByteOrder, firstIfdOffset uint32) (headerBytes []byte, err error) {
311 defer func() {
312 if state := recover(); state != nil {
313 err = log.Wrap(state.(error))
314 }
315 }()
316
317 b := new(bytes.Buffer)
318
319 var signatureBytes []byte
320 if byteOrder == binary.BigEndian {
321 signatureBytes = ExifBigEndianSignature[:]
322 } else {
323 signatureBytes = ExifLittleEndianSignature[:]
324 }
325
326 _, err = b.Write(signatureBytes)
327 log.PanicIf(err)
328
329 err = binary.Write(b, byteOrder, firstIfdOffset)
330 log.PanicIf(err)
331
332 return b.Bytes(), nil
333 }
334
View as plain text