1 package exifcommon
2
3 import (
4 "errors"
5 "fmt"
6 "strings"
7
8 "github.com/dsoprea/go-logging"
9 )
10
11 var (
12 ifdLogger = log.NewLogger("exifcommon.ifd")
13 )
14
15 var (
16 ErrChildIfdNotMapped = errors.New("no child-IFD for that tag-ID under parent")
17 )
18
19
20 type MappedIfd struct {
21 ParentTagId uint16
22 Placement []uint16
23 Path []string
24
25 Name string
26 TagId uint16
27 Children map[uint16]*MappedIfd
28 }
29
30
31 func (mi *MappedIfd) String() string {
32 pathPhrase := mi.PathPhrase()
33 return fmt.Sprintf("MappedIfd<(0x%04X) [%s] PATH=[%s]>", mi.TagId, mi.Name, pathPhrase)
34 }
35
36
37 func (mi *MappedIfd) PathPhrase() string {
38 return strings.Join(mi.Path, "/")
39 }
40
41
42
43
44 type IfdMapping struct {
45 rootNode *MappedIfd
46 }
47
48
49 func NewIfdMapping() (ifdMapping *IfdMapping) {
50 rootNode := &MappedIfd{
51 Path: make([]string, 0),
52 Children: make(map[uint16]*MappedIfd),
53 }
54
55 return &IfdMapping{
56 rootNode: rootNode,
57 }
58 }
59
60
61
62 func NewIfdMappingWithStandard() (ifdMapping *IfdMapping, err error) {
63 defer func() {
64 if state := recover(); state != nil {
65 err = log.Wrap(state.(error))
66 }
67 }()
68
69 im := NewIfdMapping()
70
71 err = LoadStandardIfds(im)
72 log.PanicIf(err)
73
74 return im, nil
75 }
76
77
78 func (im *IfdMapping) Get(parentPlacement []uint16) (childIfd *MappedIfd, err error) {
79 defer func() {
80 if state := recover(); state != nil {
81 err = log.Wrap(state.(error))
82 }
83 }()
84
85 ptr := im.rootNode
86 for _, tagId := range parentPlacement {
87 if descendantPtr, found := ptr.Children[tagId]; found == false {
88 log.Panicf("ifd child with tag-ID (%04x) not registered: [%s]", tagId, ptr.PathPhrase())
89 } else {
90 ptr = descendantPtr
91 }
92 }
93
94 return ptr, nil
95 }
96
97
98 func (im *IfdMapping) GetWithPath(pathPhrase string) (mi *MappedIfd, err error) {
99 defer func() {
100 if state := recover(); state != nil {
101 err = log.Wrap(state.(error))
102 }
103 }()
104
105 if pathPhrase == "" {
106 log.Panicf("path-phrase is empty")
107 }
108
109 path := strings.Split(pathPhrase, "/")
110 ptr := im.rootNode
111
112 for _, name := range path {
113 var hit *MappedIfd
114 for _, mi := range ptr.Children {
115 if mi.Name == name {
116 hit = mi
117 break
118 }
119 }
120
121 if hit == nil {
122 log.Panicf("ifd child with name [%s] not registered: [%s]", name, ptr.PathPhrase())
123 }
124
125 ptr = hit
126 }
127
128 return ptr, nil
129 }
130
131
132
133 func (im *IfdMapping) GetChild(parentPathPhrase string, tagId uint16) (mi *MappedIfd, err error) {
134 defer func() {
135 if state := recover(); state != nil {
136 err = log.Wrap(state.(error))
137 }
138 }()
139
140 mi, err = im.GetWithPath(parentPathPhrase)
141 log.PanicIf(err)
142
143 for _, childMi := range mi.Children {
144 if childMi.TagId == tagId {
145 return childMi, nil
146 }
147 }
148
149
150
151 log.Panic(ErrChildIfdNotMapped)
152 return nil, nil
153 }
154
155
156
157
158 type IfdTagIdAndIndex struct {
159 Name string
160 TagId uint16
161 Index int
162 }
163
164
165 func (itii IfdTagIdAndIndex) String() string {
166 return fmt.Sprintf("IfdTagIdAndIndex<NAME=[%s] ID=(%04x) INDEX=(%d)>", itii.Name, itii.TagId, itii.Index)
167 }
168
169
170
171
172
173
174
175
176
177
178
179 func (im *IfdMapping) ResolvePath(pathPhrase string) (lineage []IfdTagIdAndIndex, err error) {
180 defer func() {
181 if state := recover(); state != nil {
182 err = log.Wrap(state.(error))
183 }
184 }()
185
186 pathPhrase = strings.TrimSpace(pathPhrase)
187
188 if pathPhrase == "" {
189 log.Panicf("can not resolve empty path-phrase")
190 }
191
192 path := strings.Split(pathPhrase, "/")
193 lineage = make([]IfdTagIdAndIndex, len(path))
194
195 ptr := im.rootNode
196 empty := IfdTagIdAndIndex{}
197 for i, name := range path {
198 indexByte := name[len(name)-1]
199 index := 0
200 if indexByte >= '0' && indexByte <= '9' {
201 index = int(indexByte - '0')
202 name = name[:len(name)-1]
203 }
204
205 itii := IfdTagIdAndIndex{}
206 for _, mi := range ptr.Children {
207 if mi.Name != name {
208 continue
209 }
210
211 itii.Name = name
212 itii.TagId = mi.TagId
213 itii.Index = index
214
215 ptr = mi
216
217 break
218 }
219
220 if itii == empty {
221 log.Panicf("ifd child with name [%s] not registered: [%s]", name, pathPhrase)
222 }
223
224 lineage[i] = itii
225 }
226
227 return lineage, nil
228 }
229
230
231 func (im *IfdMapping) FqPathPhraseFromLineage(lineage []IfdTagIdAndIndex) (fqPathPhrase string) {
232 fqPathParts := make([]string, len(lineage))
233 for i, itii := range lineage {
234 if itii.Index > 0 {
235 fqPathParts[i] = fmt.Sprintf("%s%d", itii.Name, itii.Index)
236 } else {
237 fqPathParts[i] = itii.Name
238 }
239 }
240
241 return strings.Join(fqPathParts, "/")
242 }
243
244
245
246 func (im *IfdMapping) PathPhraseFromLineage(lineage []IfdTagIdAndIndex) (pathPhrase string) {
247 pathParts := make([]string, len(lineage))
248 for i, itii := range lineage {
249 pathParts[i] = itii.Name
250 }
251
252 return strings.Join(pathParts, "/")
253 }
254
255
256
257 func (im *IfdMapping) StripPathPhraseIndices(pathPhrase string) (strippedPathPhrase string, err error) {
258 defer func() {
259 if state := recover(); state != nil {
260 err = log.Wrap(state.(error))
261 }
262 }()
263
264 lineage, err := im.ResolvePath(pathPhrase)
265 log.PanicIf(err)
266
267 strippedPathPhrase = im.PathPhraseFromLineage(lineage)
268 return strippedPathPhrase, nil
269 }
270
271
272
273
274
275 func (im *IfdMapping) Add(parentPlacement []uint16, tagId uint16, name string) (err error) {
276 defer func() {
277 if state := recover(); state != nil {
278 err = log.Wrap(state.(error))
279 }
280 }()
281
282
283
284 ptr, err := im.Get(parentPlacement)
285 log.PanicIf(err)
286
287 path := make([]string, len(parentPlacement)+1)
288 if len(parentPlacement) > 0 {
289 copy(path, ptr.Path)
290 }
291
292 path[len(path)-1] = name
293
294 placement := make([]uint16, len(parentPlacement)+1)
295 if len(placement) > 0 {
296 copy(placement, ptr.Placement)
297 }
298
299 placement[len(placement)-1] = tagId
300
301 childIfd := &MappedIfd{
302 ParentTagId: ptr.TagId,
303 Path: path,
304 Placement: placement,
305 Name: name,
306 TagId: tagId,
307 Children: make(map[uint16]*MappedIfd),
308 }
309
310 if _, found := ptr.Children[tagId]; found == true {
311 log.Panicf("child IFD with tag-ID (%04x) already registered under IFD [%s] with tag-ID (%04x)", tagId, ptr.Name, ptr.TagId)
312 }
313
314 ptr.Children[tagId] = childIfd
315
316 return nil
317 }
318
319 func (im *IfdMapping) dumpLineages(stack []*MappedIfd, input []string) (output []string, err error) {
320 defer func() {
321 if state := recover(); state != nil {
322 err = log.Wrap(state.(error))
323 }
324 }()
325
326 currentIfd := stack[len(stack)-1]
327
328 output = input
329 for _, childIfd := range currentIfd.Children {
330 stackCopy := make([]*MappedIfd, len(stack)+1)
331
332 copy(stackCopy, stack)
333 stackCopy[len(stack)] = childIfd
334
335
336 parts := make([]string, len(stackCopy)-1)
337 for i, mi := range stackCopy[1:] {
338 parts[i] = mi.Name
339 }
340
341 output = append(output, strings.Join(parts, "/"))
342
343 output, err = im.dumpLineages(stackCopy, output)
344 log.PanicIf(err)
345 }
346
347 return output, nil
348 }
349
350
351 func (im *IfdMapping) DumpLineages() (output []string, err error) {
352 defer func() {
353 if state := recover(); state != nil {
354 err = log.Wrap(state.(error))
355 }
356 }()
357
358 stack := []*MappedIfd{im.rootNode}
359 output = make([]string, 0)
360
361 output, err = im.dumpLineages(stack, output)
362 log.PanicIf(err)
363
364 return output, nil
365 }
366
367
368 func LoadStandardIfds(im *IfdMapping) (err error) {
369 defer func() {
370 if state := recover(); state != nil {
371 err = log.Wrap(state.(error))
372 }
373 }()
374
375 err = im.Add(
376 []uint16{},
377 IfdStandardIfdIdentity.TagId(), IfdStandardIfdIdentity.Name())
378
379 log.PanicIf(err)
380
381 err = im.Add(
382 []uint16{IfdStandardIfdIdentity.TagId()},
383 IfdExifStandardIfdIdentity.TagId(), IfdExifStandardIfdIdentity.Name())
384
385 log.PanicIf(err)
386
387 err = im.Add(
388 []uint16{IfdStandardIfdIdentity.TagId(), IfdExifStandardIfdIdentity.TagId()},
389 IfdExifIopStandardIfdIdentity.TagId(), IfdExifIopStandardIfdIdentity.Name())
390
391 log.PanicIf(err)
392
393 err = im.Add(
394 []uint16{IfdStandardIfdIdentity.TagId()},
395 IfdGpsInfoStandardIfdIdentity.TagId(), IfdGpsInfoStandardIfdIdentity.Name())
396
397 log.PanicIf(err)
398
399 return nil
400 }
401
402
403 type IfdTag struct {
404 parentIfdTag *IfdTag
405 tagId uint16
406 name string
407 }
408
409 func NewIfdTag(parentIfdTag *IfdTag, tagId uint16, name string) IfdTag {
410 return IfdTag{
411 parentIfdTag: parentIfdTag,
412 tagId: tagId,
413 name: name,
414 }
415 }
416
417
418 func (it IfdTag) ParentIfd() *IfdTag {
419 return it.parentIfdTag
420 }
421
422
423 func (it IfdTag) TagId() uint16 {
424 return it.tagId
425 }
426
427
428 func (it IfdTag) Name() string {
429 return it.name
430 }
431
432
433 func (it IfdTag) String() string {
434 parentIfdPhrase := ""
435 if it.parentIfdTag != nil {
436 parentIfdPhrase = fmt.Sprintf(" PARENT=(0x%04x)[%s]", it.parentIfdTag.tagId, it.parentIfdTag.name)
437 }
438
439 return fmt.Sprintf("IfdTag<TAG-ID=(0x%04x) NAME=[%s]%s>", it.tagId, it.name, parentIfdPhrase)
440 }
441
442 var (
443
444 rootStandardIfd = NewIfdTag(nil, 0x0000, "IFD")
445
446
447 exifStandardIfd = NewIfdTag(&rootStandardIfd, 0x8769, "Exif")
448
449
450 iopStandardIfd = NewIfdTag(&exifStandardIfd, 0xA005, "Iop")
451
452
453 gpsInfoStandardIfd = NewIfdTag(&rootStandardIfd, 0x8825, "GPSInfo")
454 )
455
456
457 type IfdIdentityPart struct {
458 Name string
459 Index int
460 }
461
462
463 func (iip IfdIdentityPart) String() string {
464 if iip.Index > 0 {
465 return fmt.Sprintf("%s%d", iip.Name, iip.Index)
466 } else {
467 return iip.Name
468 }
469 }
470
471
472 func (iip IfdIdentityPart) UnindexedString() string {
473 return iip.Name
474 }
475
476
477
478
479
480 type IfdIdentity struct {
481 ifdTag IfdTag
482 parts []IfdIdentityPart
483 ifdPath string
484 fqIfdPath string
485 }
486
487
488 func NewIfdIdentity(ifdTag IfdTag, parts ...IfdIdentityPart) (ii *IfdIdentity) {
489 ii = &IfdIdentity{
490 ifdTag: ifdTag,
491 parts: parts,
492 }
493
494 ii.ifdPath = ii.getIfdPath()
495 ii.fqIfdPath = ii.getFqIfdPath()
496
497 return ii
498 }
499
500
501
502
503
504
505
506
507 func NewIfdIdentityFromString(im *IfdMapping, fqIfdPath string) (ii *IfdIdentity, err error) {
508 defer func() {
509 if state := recover(); state != nil {
510 err = log.Wrap(state.(error))
511 }
512 }()
513
514 lineage, err := im.ResolvePath(fqIfdPath)
515 log.PanicIf(err)
516
517 var lastIt *IfdTag
518 identityParts := make([]IfdIdentityPart, len(lineage))
519 for i, itii := range lineage {
520
521
522
523 it := &IfdTag{
524 parentIfdTag: lastIt,
525 tagId: itii.TagId,
526 name: itii.Name,
527 }
528
529 lastIt = it
530
531
532
533 iip := IfdIdentityPart{
534 Name: itii.Name,
535 Index: itii.Index,
536 }
537
538 identityParts[i] = iip
539 }
540
541 ii = NewIfdIdentity(*lastIt, identityParts...)
542 return ii, nil
543 }
544
545 func (ii *IfdIdentity) getFqIfdPath() string {
546 partPhrases := make([]string, len(ii.parts))
547 for i, iip := range ii.parts {
548 partPhrases[i] = iip.String()
549 }
550
551 return strings.Join(partPhrases, "/")
552 }
553
554 func (ii *IfdIdentity) getIfdPath() string {
555 partPhrases := make([]string, len(ii.parts))
556 for i, iip := range ii.parts {
557 partPhrases[i] = iip.UnindexedString()
558 }
559
560 return strings.Join(partPhrases, "/")
561 }
562
563
564 func (ii *IfdIdentity) String() string {
565 return ii.fqIfdPath
566 }
567
568
569 func (ii *IfdIdentity) UnindexedString() string {
570 return ii.ifdPath
571 }
572
573
574 func (ii *IfdIdentity) IfdTag() IfdTag {
575 return ii.ifdTag
576 }
577
578
579 func (ii *IfdIdentity) TagId() uint16 {
580 return ii.ifdTag.TagId()
581 }
582
583
584
585 func (ii *IfdIdentity) LeafPathPart() IfdIdentityPart {
586 return ii.parts[len(ii.parts)-1]
587 }
588
589
590 func (ii *IfdIdentity) Name() string {
591 return ii.LeafPathPart().Name
592 }
593
594
595
596 func (ii *IfdIdentity) Index() int {
597 return ii.LeafPathPart().Index
598 }
599
600
601
602
603
604
605
606 func (ii *IfdIdentity) Equals(ii2 *IfdIdentity) bool {
607 return ii.String() == ii2.String()
608 }
609
610
611
612 func (ii *IfdIdentity) NewChild(childIfdTag IfdTag, index int) (iiChild *IfdIdentity) {
613 if *childIfdTag.parentIfdTag != ii.ifdTag {
614 log.Panicf("can not add child; we are not the parent:\nUS=%v\nCHILD=%v", ii.ifdTag, childIfdTag)
615 }
616
617 childPart := IfdIdentityPart{childIfdTag.name, index}
618 childParts := append(ii.parts, childPart)
619
620 iiChild = NewIfdIdentity(childIfdTag, childParts...)
621 return iiChild
622 }
623
624
625
626 func (ii *IfdIdentity) NewSibling(index int) (iiSibling *IfdIdentity) {
627 parts := make([]IfdIdentityPart, len(ii.parts))
628
629 copy(parts, ii.parts)
630 parts[len(parts)-1].Index = index
631
632 iiSibling = NewIfdIdentity(ii.ifdTag, parts...)
633 return iiSibling
634 }
635
636 var (
637
638 IfdStandardIfdIdentity = NewIfdIdentity(rootStandardIfd, IfdIdentityPart{"IFD", 0})
639
640
641 IfdExifStandardIfdIdentity = IfdStandardIfdIdentity.NewChild(exifStandardIfd, 0)
642
643
644 IfdExifIopStandardIfdIdentity = IfdExifStandardIfdIdentity.NewChild(iopStandardIfd, 0)
645
646
647 IfdGpsInfoStandardIfdIdentity = IfdStandardIfdIdentity.NewChild(gpsInfoStandardIfd, 0)
648
649
650 Ifd1StandardIfdIdentity = NewIfdIdentity(rootStandardIfd, IfdIdentityPart{"IFD", 1})
651 )
652
View as plain text