ifd_enumerate.go (45458B)
1 package exif 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io" 8 "strconv" 9 "strings" 10 "time" 11 12 "encoding/binary" 13 14 "github.com/dsoprea/go-logging" 15 16 "github.com/dsoprea/go-exif/v3/common" 17 "github.com/dsoprea/go-exif/v3/undefined" 18 ) 19 20 var ( 21 ifdEnumerateLogger = log.NewLogger("exif.ifd_enumerate") 22 ) 23 24 var ( 25 // ErrNoThumbnail means that no thumbnail was found. 26 ErrNoThumbnail = errors.New("no thumbnail") 27 28 // ErrNoGpsTags means that no GPS info was found. 29 ErrNoGpsTags = errors.New("no gps tags") 30 31 // ErrTagTypeNotValid means that the tag-type is not valid. 32 ErrTagTypeNotValid = errors.New("tag type invalid") 33 34 // ErrOffsetInvalid means that the file offset is not valid. 35 ErrOffsetInvalid = errors.New("file offset invalid") 36 ) 37 38 var ( 39 // ValidGpsVersions is the list of recognized EXIF GPS versions/signatures. 40 ValidGpsVersions = [][4]byte{ 41 // 2.0.0.0 appears to have a very similar format to 2.2.0.0, so enabling 42 // it under that assumption. 43 // 44 // IFD-PATH=[IFD] ID=(0x8825) NAME=[GPSTag] COUNT=(1) TYPE=[LONG] VALUE=[114] 45 // IFD-PATH=[IFD/GPSInfo] ID=(0x0000) NAME=[GPSVersionID] COUNT=(4) TYPE=[BYTE] VALUE=[02 00 00 00] 46 // IFD-PATH=[IFD/GPSInfo] ID=(0x0001) NAME=[GPSLatitudeRef] COUNT=(2) TYPE=[ASCII] VALUE=[S] 47 // IFD-PATH=[IFD/GPSInfo] ID=(0x0002) NAME=[GPSLatitude] COUNT=(3) TYPE=[RATIONAL] VALUE=[38/1...] 48 // IFD-PATH=[IFD/GPSInfo] ID=(0x0003) NAME=[GPSLongitudeRef] COUNT=(2) TYPE=[ASCII] VALUE=[E] 49 // IFD-PATH=[IFD/GPSInfo] ID=(0x0004) NAME=[GPSLongitude] COUNT=(3) TYPE=[RATIONAL] VALUE=[144/1...] 50 // IFD-PATH=[IFD/GPSInfo] ID=(0x0012) NAME=[GPSMapDatum] COUNT=(7) TYPE=[ASCII] VALUE=[WGS-84] 51 // 52 {2, 0, 0, 0}, 53 54 {2, 2, 0, 0}, 55 56 // Suddenly appeared at the default in 2.31: https://home.jeita.or.jp/tsc/std-pdf/CP-3451D.pdf 57 // 58 // Note that the presence of 2.3.0.0 doesn't seem to guarantee 59 // coordinates. In some cases, we seen just the following: 60 // 61 // GPS Tag Version |2.3.0.0 62 // GPS Receiver Status |V 63 // Geodetic Survey Data|WGS-84 64 // GPS Differential Cor|0 65 // 66 {2, 3, 0, 0}, 67 } 68 ) 69 70 // byteParser knows how to decode an IFD and all of the tags it 71 // describes. 72 // 73 // The IFDs and the actual values can float throughout the EXIF block, but the 74 // IFD itself is just a minor header followed by a set of repeating, 75 // statically-sized records. So, the tags (though notnecessarily their values) 76 // are fairly simple to enumerate. 77 type byteParser struct { 78 byteOrder binary.ByteOrder 79 rs io.ReadSeeker 80 ifdOffset uint32 81 currentOffset uint32 82 } 83 84 // newByteParser returns a new byteParser struct. 85 // 86 // initialOffset is for arithmetic-based tracking of where we should be at in 87 // the stream. 88 func newByteParser(rs io.ReadSeeker, byteOrder binary.ByteOrder, initialOffset uint32) (bp *byteParser, err error) { 89 // TODO(dustin): Add test 90 91 bp = &byteParser{ 92 rs: rs, 93 byteOrder: byteOrder, 94 currentOffset: initialOffset, 95 } 96 97 return bp, nil 98 } 99 100 // getUint16 reads a uint16 and advances both our current and our current 101 // accumulator (which allows us to know how far to seek to the beginning of the 102 // next IFD when it's time to jump). 103 func (bp *byteParser) getUint16() (value uint16, raw []byte, err error) { 104 defer func() { 105 if state := recover(); state != nil { 106 err = log.Wrap(state.(error)) 107 } 108 }() 109 110 // TODO(dustin): Add test 111 112 needBytes := 2 113 114 raw = make([]byte, needBytes) 115 116 _, err = io.ReadFull(bp.rs, raw) 117 log.PanicIf(err) 118 119 value = bp.byteOrder.Uint16(raw) 120 121 bp.currentOffset += uint32(needBytes) 122 123 return value, raw, nil 124 } 125 126 // getUint32 reads a uint32 and advances both our current and our current 127 // accumulator (which allows us to know how far to seek to the beginning of the 128 // next IFD when it's time to jump). 129 func (bp *byteParser) getUint32() (value uint32, raw []byte, err error) { 130 defer func() { 131 if state := recover(); state != nil { 132 err = log.Wrap(state.(error)) 133 } 134 }() 135 136 // TODO(dustin): Add test 137 138 needBytes := 4 139 140 raw = make([]byte, needBytes) 141 142 _, err = io.ReadFull(bp.rs, raw) 143 log.PanicIf(err) 144 145 value = bp.byteOrder.Uint32(raw) 146 147 bp.currentOffset += uint32(needBytes) 148 149 return value, raw, nil 150 } 151 152 // CurrentOffset returns the starting offset but the number of bytes that we 153 // have parsed. This is arithmetic-based tracking, not a seek(0) operation. 154 func (bp *byteParser) CurrentOffset() uint32 { 155 return bp.currentOffset 156 } 157 158 // IfdEnumerate is the main enumeration type. It knows how to parse the IFD 159 // containers in the EXIF blob. 160 type IfdEnumerate struct { 161 ebs ExifBlobSeeker 162 byteOrder binary.ByteOrder 163 tagIndex *TagIndex 164 ifdMapping *exifcommon.IfdMapping 165 furthestOffset uint32 166 167 visitedIfdOffsets map[uint32]struct{} 168 } 169 170 // NewIfdEnumerate returns a new instance of IfdEnumerate. 171 func NewIfdEnumerate(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ebs ExifBlobSeeker, byteOrder binary.ByteOrder) *IfdEnumerate { 172 return &IfdEnumerate{ 173 ebs: ebs, 174 byteOrder: byteOrder, 175 ifdMapping: ifdMapping, 176 tagIndex: tagIndex, 177 178 visitedIfdOffsets: make(map[uint32]struct{}), 179 } 180 } 181 182 func (ie *IfdEnumerate) getByteParser(ifdOffset uint32) (bp *byteParser, err error) { 183 defer func() { 184 if state := recover(); state != nil { 185 err = log.Wrap(state.(error)) 186 } 187 }() 188 189 initialOffset := ExifAddressableAreaStart + ifdOffset 190 191 rs, err := ie.ebs.GetReadSeeker(int64(initialOffset)) 192 log.PanicIf(err) 193 194 bp, err = 195 newByteParser( 196 rs, 197 ie.byteOrder, 198 initialOffset) 199 200 if err != nil { 201 if err == ErrOffsetInvalid { 202 return nil, err 203 } 204 205 log.Panic(err) 206 } 207 208 return bp, nil 209 } 210 211 func (ie *IfdEnumerate) parseTag(ii *exifcommon.IfdIdentity, tagPosition int, bp *byteParser) (ite *IfdTagEntry, err error) { 212 defer func() { 213 if state := recover(); state != nil { 214 err = log.Wrap(state.(error)) 215 } 216 }() 217 218 tagId, _, err := bp.getUint16() 219 log.PanicIf(err) 220 221 tagTypeRaw, _, err := bp.getUint16() 222 log.PanicIf(err) 223 224 tagType := exifcommon.TagTypePrimitive(tagTypeRaw) 225 226 unitCount, _, err := bp.getUint32() 227 log.PanicIf(err) 228 229 valueOffset, rawValueOffset, err := bp.getUint32() 230 log.PanicIf(err) 231 232 // Check whether the embedded type indicator is valid. 233 234 if tagType.IsValid() == false { 235 // Technically, we have the type on-file in the tags-index, but 236 // if the type stored alongside the data disagrees with it, 237 // which it apparently does, all bets are off. 238 ifdEnumerateLogger.Warningf(nil, 239 "Tag (0x%04x) in IFD [%s] at position (%d) has invalid type (0x%04x) and will be skipped.", 240 tagId, ii, tagPosition, int(tagType)) 241 242 ite = &IfdTagEntry{ 243 tagId: tagId, 244 tagType: tagType, 245 } 246 247 return ite, ErrTagTypeNotValid 248 } 249 250 // Check whether the embedded type is listed among the supported types for 251 // the registered tag. If not, skip processing the tag. 252 253 it, err := ie.tagIndex.Get(ii, tagId) 254 if err != nil { 255 if log.Is(err, ErrTagNotFound) == true { 256 ifdEnumerateLogger.Warningf(nil, "Tag (0x%04x) is not known and will be skipped.", tagId) 257 258 ite = &IfdTagEntry{ 259 tagId: tagId, 260 } 261 262 return ite, ErrTagNotFound 263 } 264 265 log.Panic(err) 266 } 267 268 // If we're trying to be as forgiving as possible then use whatever type was 269 // reported in the format. Otherwise, only accept a type that's expected for 270 // this tag. 271 if ie.tagIndex.UniversalSearch() == false && it.DoesSupportType(tagType) == false { 272 // The type in the stream disagrees with the type that this tag is 273 // expected to have. This can present issues with how we handle the 274 // special-case tags (e.g. thumbnails, GPS, etc..) when those tags 275 // suddenly have data that we no longer manipulate correctly/ 276 // accurately. 277 ifdEnumerateLogger.Warningf(nil, 278 "Tag (0x%04x) in IFD [%s] at position (%d) has unsupported type (0x%02x) and will be skipped.", 279 tagId, ii, tagPosition, int(tagType)) 280 281 return nil, ErrTagTypeNotValid 282 } 283 284 // Construct tag struct. 285 286 rs, err := ie.ebs.GetReadSeeker(0) 287 log.PanicIf(err) 288 289 ite = newIfdTagEntry( 290 ii, 291 tagId, 292 tagPosition, 293 tagType, 294 unitCount, 295 valueOffset, 296 rawValueOffset, 297 rs, 298 ie.byteOrder) 299 300 ifdPath := ii.UnindexedString() 301 302 // If it's an IFD but not a standard one, it'll just be seen as a LONG 303 // (the standard IFD tag type), later, unless we skip it because it's 304 // [likely] not even in the standard list of known tags. 305 mi, err := ie.ifdMapping.GetChild(ifdPath, tagId) 306 if err == nil { 307 currentIfdTag := ii.IfdTag() 308 309 childIt := exifcommon.NewIfdTag(¤tIfdTag, tagId, mi.Name) 310 iiChild := ii.NewChild(childIt, 0) 311 ite.SetChildIfd(iiChild) 312 313 // We also need to set `tag.ChildFqIfdPath` but can't do it here 314 // because we don't have the IFD index. 315 } else if log.Is(err, exifcommon.ErrChildIfdNotMapped) == false { 316 log.Panic(err) 317 } 318 319 return ite, nil 320 } 321 322 // TagVisitorFn is called for each tag when enumerating through the EXIF. 323 type TagVisitorFn func(ite *IfdTagEntry) (err error) 324 325 // tagPostParse do some tag-level processing here following the parse of each. 326 func (ie *IfdEnumerate) tagPostParse(ite *IfdTagEntry, med *MiscellaneousExifData) (err error) { 327 defer func() { 328 if state := recover(); state != nil { 329 err = log.Wrap(state.(error)) 330 } 331 }() 332 333 // TODO(dustin): Add test 334 335 ii := ite.IfdIdentity() 336 337 tagId := ite.TagId() 338 tagType := ite.TagType() 339 340 it, err := ie.tagIndex.Get(ii, tagId) 341 if err == nil { 342 ite.setTagName(it.Name) 343 } else { 344 if err != ErrTagNotFound { 345 log.Panic(err) 346 } 347 348 // This is an unknown tag. 349 350 originalBt := exifcommon.BasicTag{ 351 FqIfdPath: ii.String(), 352 IfdPath: ii.UnindexedString(), 353 TagId: tagId, 354 } 355 356 if med != nil { 357 med.unknownTags[originalBt] = exifcommon.BasicTag{} 358 } 359 360 utilityLogger.Debugf(nil, 361 "Tag (0x%04x) is not valid for IFD [%s]. Attempting secondary "+ 362 "lookup.", tagId, ii.String()) 363 364 // This will overwrite the existing `it` and `err`. Since `FindFirst()` 365 // might generate different Errors than `Get()`, the log message above 366 // is import to try and mitigate confusion in that case. 367 it, err = ie.tagIndex.FindFirst(tagId, tagType, nil) 368 if err != nil { 369 if err != ErrTagNotFound { 370 log.Panic(err) 371 } 372 373 // This is supposed to be a convenience function and if we were 374 // to keep the name empty or set it to some placeholder, it 375 // might be mismanaged by the package that is calling us. If 376 // they want to specifically manage these types of tags, they 377 // can use more advanced functionality to specifically -handle 378 // unknown tags. 379 utilityLogger.Warningf(nil, 380 "Tag with ID (0x%04x) in IFD [%s] is not recognized and "+ 381 "will be ignored.", tagId, ii.String()) 382 383 return ErrTagNotFound 384 } 385 386 ite.setTagName(it.Name) 387 388 utilityLogger.Warningf(nil, 389 "Tag with ID (0x%04x) is not valid for IFD [%s], but it *is* "+ 390 "valid as tag [%s] under IFD [%s] and has the same type "+ 391 "[%s], so we will use that. This EXIF blob was probably "+ 392 "written by a buggy implementation.", 393 tagId, ii.UnindexedString(), it.Name, it.IfdPath, 394 tagType) 395 396 if med != nil { 397 med.unknownTags[originalBt] = exifcommon.BasicTag{ 398 IfdPath: it.IfdPath, 399 TagId: tagId, 400 } 401 } 402 } 403 404 // This is a known tag (from the standard, unless the user did 405 // something different). 406 407 // Skip any tags that have a type that doesn't match the type in the 408 // index (which is loaded with the standard and accept tag 409 // information unless configured otherwise). 410 // 411 // We've run into multiple instances of the same tag, where a) no 412 // tag should ever be repeated, and b) all but one had an incorrect 413 // type and caused parsing/conversion woes. So, this is a quick fix 414 // for those scenarios. 415 if ie.tagIndex.UniversalSearch() == false && it.DoesSupportType(tagType) == false { 416 ifdEnumerateLogger.Warningf(nil, 417 "Skipping tag [%s] (0x%04x) [%s] with an unexpected type: %v ∉ %v", 418 ii.UnindexedString(), tagId, it.Name, 419 tagType, it.SupportedTypes) 420 421 return ErrTagNotFound 422 } 423 424 return nil 425 } 426 427 // parseIfd decodes the IFD block that we're currently sitting on the first 428 // byte of. 429 func (ie *IfdEnumerate) parseIfd(ii *exifcommon.IfdIdentity, bp *byteParser, visitor TagVisitorFn, doDescend bool, med *MiscellaneousExifData) (nextIfdOffset uint32, entries []*IfdTagEntry, thumbnailData []byte, err error) { 430 defer func() { 431 if state := recover(); state != nil { 432 err = log.Wrap(state.(error)) 433 } 434 }() 435 436 tagCount, _, err := bp.getUint16() 437 log.PanicIf(err) 438 439 ifdEnumerateLogger.Debugf(nil, "IFD [%s] tag-count: (%d)", ii.String(), tagCount) 440 441 entries = make([]*IfdTagEntry, 0) 442 443 var enumeratorThumbnailOffset *IfdTagEntry 444 var enumeratorThumbnailSize *IfdTagEntry 445 446 for i := 0; i < int(tagCount); i++ { 447 ite, err := ie.parseTag(ii, i, bp) 448 if err != nil { 449 if log.Is(err, ErrTagNotFound) == true || log.Is(err, ErrTagTypeNotValid) == true { 450 // These tags should've been fully logged in parseTag(). The 451 // ITE returned is nil so we can't print anything about them, now. 452 continue 453 } 454 455 log.Panic(err) 456 } 457 458 err = ie.tagPostParse(ite, med) 459 if err == nil { 460 if err == ErrTagNotFound { 461 continue 462 } 463 464 log.PanicIf(err) 465 } 466 467 tagId := ite.TagId() 468 469 if visitor != nil { 470 err := visitor(ite) 471 log.PanicIf(err) 472 } 473 474 if ite.IsThumbnailOffset() == true { 475 ifdEnumerateLogger.Debugf(nil, "Skipping the thumbnail offset tag (0x%04x). Use accessors to get it or set it.", tagId) 476 477 enumeratorThumbnailOffset = ite 478 entries = append(entries, ite) 479 480 continue 481 } else if ite.IsThumbnailSize() == true { 482 ifdEnumerateLogger.Debugf(nil, "Skipping the thumbnail size tag (0x%04x). Use accessors to get it or set it.", tagId) 483 484 enumeratorThumbnailSize = ite 485 entries = append(entries, ite) 486 487 continue 488 } 489 490 if ite.TagType() != exifcommon.TypeUndefined { 491 // If this tag's value is an offset, bump our max-offset value to 492 // what that offset is plus however large that value is. 493 494 vc := ite.getValueContext() 495 496 farOffset, err := vc.GetFarOffset() 497 if err == nil { 498 candidateOffset := farOffset + uint32(vc.SizeInBytes()) 499 if candidateOffset > ie.furthestOffset { 500 ie.furthestOffset = candidateOffset 501 } 502 } else if err != exifcommon.ErrNotFarValue { 503 log.PanicIf(err) 504 } 505 } 506 507 // If it's an IFD but not a standard one, it'll just be seen as a LONG 508 // (the standard IFD tag type), later, unless we skip it because it's 509 // [likely] not even in the standard list of known tags. 510 if ite.ChildIfdPath() != "" { 511 if doDescend == true { 512 ifdEnumerateLogger.Debugf(nil, "Descending from IFD [%s] to IFD [%s].", ii, ite.ChildIfdPath()) 513 514 currentIfdTag := ii.IfdTag() 515 516 childIfdTag := 517 exifcommon.NewIfdTag( 518 ¤tIfdTag, 519 ite.TagId(), 520 ite.ChildIfdName()) 521 522 iiChild := ii.NewChild(childIfdTag, 0) 523 524 err := ie.scan(iiChild, ite.getValueOffset(), visitor, med) 525 log.PanicIf(err) 526 527 ifdEnumerateLogger.Debugf(nil, "Ascending from IFD [%s] to IFD [%s].", ite.ChildIfdPath(), ii) 528 } 529 } 530 531 entries = append(entries, ite) 532 } 533 534 if enumeratorThumbnailOffset != nil && enumeratorThumbnailSize != nil { 535 thumbnailData, err = ie.parseThumbnail(enumeratorThumbnailOffset, enumeratorThumbnailSize) 536 if err != nil { 537 ifdEnumerateLogger.Errorf( 538 nil, err, 539 "We tried to bump our furthest-offset counter but there was an issue first seeking past the thumbnail.") 540 } else { 541 // In this case, the value is always an offset. 542 offset := enumeratorThumbnailOffset.getValueOffset() 543 544 // This this case, the value is always a length. 545 length := enumeratorThumbnailSize.getValueOffset() 546 547 ifdEnumerateLogger.Debugf(nil, "Found thumbnail in IFD [%s]. Its offset is (%d) and is (%d) bytes.", ii, offset, length) 548 549 furthestOffset := offset + length 550 551 if furthestOffset > ie.furthestOffset { 552 ie.furthestOffset = furthestOffset 553 } 554 } 555 } 556 557 nextIfdOffset, _, err = bp.getUint32() 558 log.PanicIf(err) 559 560 _, alreadyVisited := ie.visitedIfdOffsets[nextIfdOffset] 561 562 if alreadyVisited == true { 563 ifdEnumerateLogger.Warningf(nil, "IFD at offset (0x%08x) has been linked-to more than once. There might be a cycle in the IFD chain. Not reparsing.", nextIfdOffset) 564 nextIfdOffset = 0 565 } 566 567 if nextIfdOffset != 0 { 568 ie.visitedIfdOffsets[nextIfdOffset] = struct{}{} 569 ifdEnumerateLogger.Debugf(nil, "[%s] Next IFD at offset: (0x%08x)", ii.String(), nextIfdOffset) 570 } else { 571 ifdEnumerateLogger.Debugf(nil, "[%s] IFD chain has terminated.", ii.String()) 572 } 573 574 return nextIfdOffset, entries, thumbnailData, nil 575 } 576 577 func (ie *IfdEnumerate) parseThumbnail(offsetIte, lengthIte *IfdTagEntry) (thumbnailData []byte, err error) { 578 defer func() { 579 if state := recover(); state != nil { 580 err = log.Wrap(state.(error)) 581 } 582 }() 583 584 vRaw, err := lengthIte.Value() 585 log.PanicIf(err) 586 587 vList := vRaw.([]uint32) 588 if len(vList) != 1 { 589 log.Panicf("not exactly one long: (%d)", len(vList)) 590 } 591 592 length := vList[0] 593 594 // The tag is official a LONG type, but it's actually an offset to a blob of bytes. 595 offsetIte.updateTagType(exifcommon.TypeByte) 596 offsetIte.updateUnitCount(length) 597 598 thumbnailData, err = offsetIte.GetRawBytes() 599 log.PanicIf(err) 600 601 return thumbnailData, nil 602 } 603 604 // scan parses and enumerates the different IFD blocks and invokes a visitor 605 // callback for each tag. No information is kept or returned. 606 func (ie *IfdEnumerate) scan(iiGeneral *exifcommon.IfdIdentity, ifdOffset uint32, visitor TagVisitorFn, med *MiscellaneousExifData) (err error) { 607 defer func() { 608 if state := recover(); state != nil { 609 err = log.Wrap(state.(error)) 610 } 611 }() 612 613 // TODO(dustin): Add test 614 615 for ifdIndex := 0; ; ifdIndex++ { 616 iiSibling := iiGeneral.NewSibling(ifdIndex) 617 618 ifdEnumerateLogger.Debugf(nil, "Parsing IFD [%s] at offset (0x%04x) (scan).", iiSibling.String(), ifdOffset) 619 620 bp, err := ie.getByteParser(ifdOffset) 621 if err != nil { 622 if err == ErrOffsetInvalid { 623 ifdEnumerateLogger.Errorf(nil, nil, "IFD [%s] at offset (0x%04x) is unreachable. Terminating scan.", iiSibling.String(), ifdOffset) 624 break 625 } 626 627 log.Panic(err) 628 } 629 630 nextIfdOffset, _, _, err := ie.parseIfd(iiSibling, bp, visitor, true, med) 631 log.PanicIf(err) 632 633 currentOffset := bp.CurrentOffset() 634 if currentOffset > ie.furthestOffset { 635 ie.furthestOffset = currentOffset 636 } 637 638 if nextIfdOffset == 0 { 639 break 640 } 641 642 ifdOffset = nextIfdOffset 643 } 644 645 return nil 646 } 647 648 // MiscellaneousExifData is reports additional data collected during the parse. 649 type MiscellaneousExifData struct { 650 // UnknownTags contains all tags that were invalid for their containing 651 // IFDs. The values represent alternative IFDs that were correctly matched 652 // to those tags and used instead. 653 unknownTags map[exifcommon.BasicTag]exifcommon.BasicTag 654 } 655 656 // UnknownTags returns the unknown tags encountered during the scan. 657 func (med *MiscellaneousExifData) UnknownTags() map[exifcommon.BasicTag]exifcommon.BasicTag { 658 return med.unknownTags 659 } 660 661 // ScanOptions tweaks parser behavior/choices. 662 type ScanOptions struct { 663 // NOTE(dustin): Reserved for future usage. 664 } 665 666 // Scan enumerates the different EXIF blocks (called IFDs). `rootIfdName` will 667 // be "IFD" in the TIFF standard. 668 func (ie *IfdEnumerate) Scan(iiRoot *exifcommon.IfdIdentity, ifdOffset uint32, visitor TagVisitorFn, so *ScanOptions) (med *MiscellaneousExifData, err error) { 669 defer func() { 670 if state := recover(); state != nil { 671 err = log.Wrap(state.(error)) 672 } 673 }() 674 675 // TODO(dustin): Add test 676 677 med = &MiscellaneousExifData{ 678 unknownTags: make(map[exifcommon.BasicTag]exifcommon.BasicTag), 679 } 680 681 err = ie.scan(iiRoot, ifdOffset, visitor, med) 682 log.PanicIf(err) 683 684 ifdEnumerateLogger.Debugf(nil, "Scan: It looks like the furthest offset that contained EXIF data in the EXIF blob was (%d) (Scan).", ie.FurthestOffset()) 685 686 return med, nil 687 } 688 689 // Ifd represents a single, parsed IFD. 690 type Ifd struct { 691 ifdIdentity *exifcommon.IfdIdentity 692 693 ifdMapping *exifcommon.IfdMapping 694 tagIndex *TagIndex 695 696 offset uint32 697 byteOrder binary.ByteOrder 698 id int 699 700 parentIfd *Ifd 701 702 // ParentTagIndex is our tag position in the parent IFD, if we had a parent 703 // (if `ParentIfd` is not nil and we weren't an IFD referenced as a sibling 704 // instead of as a child). 705 parentTagIndex int 706 707 entries []*IfdTagEntry 708 entriesByTagId map[uint16][]*IfdTagEntry 709 710 children []*Ifd 711 childIfdIndex map[string]*Ifd 712 713 thumbnailData []byte 714 715 nextIfdOffset uint32 716 nextIfd *Ifd 717 } 718 719 // IfdIdentity returns IFD identity that this struct represents. 720 func (ifd *Ifd) IfdIdentity() *exifcommon.IfdIdentity { 721 return ifd.ifdIdentity 722 } 723 724 // Entries returns a flat list of all tags for this IFD. 725 func (ifd *Ifd) Entries() []*IfdTagEntry { 726 727 // TODO(dustin): Add test 728 729 return ifd.entries 730 } 731 732 // EntriesByTagId returns a map of all tags for this IFD. 733 func (ifd *Ifd) EntriesByTagId() map[uint16][]*IfdTagEntry { 734 735 // TODO(dustin): Add test 736 737 return ifd.entriesByTagId 738 } 739 740 // Children returns a flat list of all child IFDs of this IFD. 741 func (ifd *Ifd) Children() []*Ifd { 742 743 // TODO(dustin): Add test 744 745 return ifd.children 746 } 747 748 // ChildWithIfdPath returns a map of all child IFDs of this IFD. 749 func (ifd *Ifd) ChildIfdIndex() map[string]*Ifd { 750 751 // TODO(dustin): Add test 752 753 return ifd.childIfdIndex 754 } 755 756 // ParentTagIndex returns the position of this IFD's tag in its parent IFD (*if* 757 // there is a parent). 758 func (ifd *Ifd) ParentTagIndex() int { 759 760 // TODO(dustin): Add test 761 762 return ifd.parentTagIndex 763 } 764 765 // Offset returns the offset of the IFD in the stream. 766 func (ifd *Ifd) Offset() uint32 { 767 768 // TODO(dustin): Add test 769 770 return ifd.offset 771 } 772 773 // Offset returns the offset of the IFD in the stream. 774 func (ifd *Ifd) ByteOrder() binary.ByteOrder { 775 776 // TODO(dustin): Add test 777 778 return ifd.byteOrder 779 } 780 781 // NextIfd returns the Ifd struct for the next IFD in the chain. 782 func (ifd *Ifd) NextIfd() *Ifd { 783 784 // TODO(dustin): Add test 785 786 return ifd.nextIfd 787 } 788 789 // ChildWithIfdPath returns an `Ifd` struct for the given child of the current 790 // IFD. 791 func (ifd *Ifd) ChildWithIfdPath(iiChild *exifcommon.IfdIdentity) (childIfd *Ifd, err error) { 792 defer func() { 793 if state := recover(); state != nil { 794 err = log.Wrap(state.(error)) 795 } 796 }() 797 798 // TODO(dustin): This is a bridge while we're introducing the IFD type-system. We should be able to use the (IfdIdentity).Equals() method for this. 799 ifdPath := iiChild.UnindexedString() 800 801 for _, childIfd := range ifd.children { 802 if childIfd.ifdIdentity.UnindexedString() == ifdPath { 803 return childIfd, nil 804 } 805 } 806 807 log.Panic(ErrTagNotFound) 808 return nil, nil 809 } 810 811 // FindTagWithId returns a list of tags (usually just zero or one) that match 812 // the given tag ID. This is efficient. 813 func (ifd *Ifd) FindTagWithId(tagId uint16) (results []*IfdTagEntry, err error) { 814 defer func() { 815 if state := recover(); state != nil { 816 err = log.Wrap(state.(error)) 817 } 818 }() 819 820 results, found := ifd.entriesByTagId[tagId] 821 if found != true { 822 log.Panic(ErrTagNotFound) 823 } 824 825 return results, nil 826 } 827 828 // FindTagWithName returns a list of tags (usually just zero or one) that match 829 // the given tag name. This is not efficient (though the labor is trivial). 830 func (ifd *Ifd) FindTagWithName(tagName string) (results []*IfdTagEntry, err error) { 831 defer func() { 832 if state := recover(); state != nil { 833 err = log.Wrap(state.(error)) 834 } 835 }() 836 837 it, err := ifd.tagIndex.GetWithName(ifd.ifdIdentity, tagName) 838 if log.Is(err, ErrTagNotFound) == true { 839 log.Panic(ErrTagNotKnown) 840 } else if err != nil { 841 log.Panic(err) 842 } 843 844 results = make([]*IfdTagEntry, 0) 845 for _, ite := range ifd.entries { 846 if ite.TagId() == it.Id { 847 results = append(results, ite) 848 } 849 } 850 851 if len(results) == 0 { 852 log.Panic(ErrTagNotFound) 853 } 854 855 return results, nil 856 } 857 858 // String returns a description string. 859 func (ifd *Ifd) String() string { 860 parentOffset := uint32(0) 861 if ifd.parentIfd != nil { 862 parentOffset = ifd.parentIfd.offset 863 } 864 865 return fmt.Sprintf("Ifd<ID=(%d) IFD-PATH=[%s] INDEX=(%d) COUNT=(%d) OFF=(0x%04x) CHILDREN=(%d) PARENT=(0x%04x) NEXT-IFD=(0x%04x)>", ifd.id, ifd.ifdIdentity.UnindexedString(), ifd.ifdIdentity.Index(), len(ifd.entries), ifd.offset, len(ifd.children), parentOffset, ifd.nextIfdOffset) 866 } 867 868 // Thumbnail returns the raw thumbnail bytes. This is typically directly 869 // readable by any standard image viewer. 870 func (ifd *Ifd) Thumbnail() (data []byte, err error) { 871 872 if ifd.thumbnailData == nil { 873 return nil, ErrNoThumbnail 874 } 875 876 return ifd.thumbnailData, nil 877 } 878 879 // dumpTags recursively builds a list of tags from an IFD. 880 func (ifd *Ifd) dumpTags(tags []*IfdTagEntry) []*IfdTagEntry { 881 if tags == nil { 882 tags = make([]*IfdTagEntry, 0) 883 } 884 885 // Now, print the tags while also descending to child-IFDS as we encounter them. 886 887 ifdsFoundCount := 0 888 889 for _, ite := range ifd.entries { 890 tags = append(tags, ite) 891 892 childIfdPath := ite.ChildIfdPath() 893 if childIfdPath != "" { 894 ifdsFoundCount++ 895 896 childIfd, found := ifd.childIfdIndex[childIfdPath] 897 if found != true { 898 log.Panicf("alien child IFD referenced by a tag: [%s]", childIfdPath) 899 } 900 901 tags = childIfd.dumpTags(tags) 902 } 903 } 904 905 if len(ifd.children) != ifdsFoundCount { 906 log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.children), ifdsFoundCount) 907 } 908 909 if ifd.nextIfd != nil { 910 tags = ifd.nextIfd.dumpTags(tags) 911 } 912 913 return tags 914 } 915 916 // DumpTags prints the IFD hierarchy. 917 func (ifd *Ifd) DumpTags() []*IfdTagEntry { 918 return ifd.dumpTags(nil) 919 } 920 921 func (ifd *Ifd) printTagTree(populateValues bool, index, level int, nextLink bool) { 922 indent := strings.Repeat(" ", level*2) 923 924 prefix := " " 925 if nextLink { 926 prefix = ">" 927 } 928 929 fmt.Printf("%s%sIFD: %s\n", indent, prefix, ifd) 930 931 // Now, print the tags while also descending to child-IFDS as we encounter them. 932 933 ifdsFoundCount := 0 934 935 for _, ite := range ifd.entries { 936 if ite.ChildIfdPath() != "" { 937 fmt.Printf("%s - TAG: %s\n", indent, ite) 938 } else { 939 // This will just add noise to the output (byte-tags are fully 940 // dumped). 941 if ite.IsThumbnailOffset() == true || ite.IsThumbnailSize() == true { 942 continue 943 } 944 945 it, err := ifd.tagIndex.Get(ifd.ifdIdentity, ite.TagId()) 946 947 tagName := "" 948 if err == nil { 949 tagName = it.Name 950 } 951 952 var valuePhrase string 953 if populateValues == true { 954 var err error 955 956 valuePhrase, err = ite.Format() 957 if err != nil { 958 if log.Is(err, exifcommon.ErrUnhandledUndefinedTypedTag) == true { 959 ifdEnumerateLogger.Warningf(nil, "Skipping non-standard undefined tag: [%s] (%04x)", ifd.ifdIdentity.UnindexedString(), ite.TagId()) 960 continue 961 } else if err == exifundefined.ErrUnparseableValue { 962 ifdEnumerateLogger.Warningf(nil, "Skipping unparseable undefined tag: [%s] (%04x) [%s]", ifd.ifdIdentity.UnindexedString(), ite.TagId(), it.Name) 963 continue 964 } 965 966 log.Panic(err) 967 } 968 } else { 969 valuePhrase = "!UNRESOLVED" 970 } 971 972 fmt.Printf("%s - TAG: %s NAME=[%s] VALUE=[%v]\n", indent, ite, tagName, valuePhrase) 973 } 974 975 childIfdPath := ite.ChildIfdPath() 976 if childIfdPath != "" { 977 ifdsFoundCount++ 978 979 childIfd, found := ifd.childIfdIndex[childIfdPath] 980 if found != true { 981 log.Panicf("alien child IFD referenced by a tag: [%s]", childIfdPath) 982 } 983 984 childIfd.printTagTree(populateValues, 0, level+1, false) 985 } 986 } 987 988 if len(ifd.children) != ifdsFoundCount { 989 log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.children), ifdsFoundCount) 990 } 991 992 if ifd.nextIfd != nil { 993 ifd.nextIfd.printTagTree(populateValues, index+1, level, true) 994 } 995 } 996 997 // PrintTagTree prints the IFD hierarchy. 998 func (ifd *Ifd) PrintTagTree(populateValues bool) { 999 ifd.printTagTree(populateValues, 0, 0, false) 1000 } 1001 1002 func (ifd *Ifd) printIfdTree(level int, nextLink bool) { 1003 indent := strings.Repeat(" ", level*2) 1004 1005 prefix := " " 1006 if nextLink { 1007 prefix = ">" 1008 } 1009 1010 fmt.Printf("%s%s%s\n", indent, prefix, ifd) 1011 1012 // Now, print the tags while also descending to child-IFDS as we encounter them. 1013 1014 ifdsFoundCount := 0 1015 1016 for _, ite := range ifd.entries { 1017 childIfdPath := ite.ChildIfdPath() 1018 if childIfdPath != "" { 1019 ifdsFoundCount++ 1020 1021 childIfd, found := ifd.childIfdIndex[childIfdPath] 1022 if found != true { 1023 log.Panicf("alien child IFD referenced by a tag: [%s]", childIfdPath) 1024 } 1025 1026 childIfd.printIfdTree(level+1, false) 1027 } 1028 } 1029 1030 if len(ifd.children) != ifdsFoundCount { 1031 log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.children), ifdsFoundCount) 1032 } 1033 1034 if ifd.nextIfd != nil { 1035 ifd.nextIfd.printIfdTree(level, true) 1036 } 1037 } 1038 1039 // PrintIfdTree prints the IFD hierarchy. 1040 func (ifd *Ifd) PrintIfdTree() { 1041 ifd.printIfdTree(0, false) 1042 } 1043 1044 func (ifd *Ifd) dumpTree(tagsDump []string, level int) []string { 1045 if tagsDump == nil { 1046 tagsDump = make([]string, 0) 1047 } 1048 1049 indent := strings.Repeat(" ", level*2) 1050 1051 var ifdPhrase string 1052 if ifd.parentIfd != nil { 1053 ifdPhrase = fmt.Sprintf("[%s]->[%s]:(%d)", ifd.parentIfd.ifdIdentity.UnindexedString(), ifd.ifdIdentity.UnindexedString(), ifd.ifdIdentity.Index()) 1054 } else { 1055 ifdPhrase = fmt.Sprintf("[ROOT]->[%s]:(%d)", ifd.ifdIdentity.UnindexedString(), ifd.ifdIdentity.Index()) 1056 } 1057 1058 startBlurb := fmt.Sprintf("%s> IFD %s TOP", indent, ifdPhrase) 1059 tagsDump = append(tagsDump, startBlurb) 1060 1061 ifdsFoundCount := 0 1062 for _, ite := range ifd.entries { 1063 tagsDump = append(tagsDump, fmt.Sprintf("%s - (0x%04x)", indent, ite.TagId())) 1064 1065 childIfdPath := ite.ChildIfdPath() 1066 if childIfdPath != "" { 1067 ifdsFoundCount++ 1068 1069 childIfd, found := ifd.childIfdIndex[childIfdPath] 1070 if found != true { 1071 log.Panicf("alien child IFD referenced by a tag: [%s]", childIfdPath) 1072 } 1073 1074 tagsDump = childIfd.dumpTree(tagsDump, level+1) 1075 } 1076 } 1077 1078 if len(ifd.children) != ifdsFoundCount { 1079 log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.children), ifdsFoundCount) 1080 } 1081 1082 finishBlurb := fmt.Sprintf("%s< IFD %s BOTTOM", indent, ifdPhrase) 1083 tagsDump = append(tagsDump, finishBlurb) 1084 1085 if ifd.nextIfd != nil { 1086 siblingBlurb := fmt.Sprintf("%s* LINKING TO SIBLING IFD [%s]:(%d)", indent, ifd.nextIfd.ifdIdentity.UnindexedString(), ifd.nextIfd.ifdIdentity.Index()) 1087 tagsDump = append(tagsDump, siblingBlurb) 1088 1089 tagsDump = ifd.nextIfd.dumpTree(tagsDump, level) 1090 } 1091 1092 return tagsDump 1093 } 1094 1095 // DumpTree returns a list of strings describing the IFD hierarchy. 1096 func (ifd *Ifd) DumpTree() []string { 1097 return ifd.dumpTree(nil, 0) 1098 } 1099 1100 // GpsInfo parses and consolidates the GPS info. This can only be called on the 1101 // GPS IFD. 1102 func (ifd *Ifd) GpsInfo() (gi *GpsInfo, err error) { 1103 defer func() { 1104 if state := recover(); state != nil { 1105 err = log.Wrap(state.(error)) 1106 } 1107 }() 1108 1109 gi = new(GpsInfo) 1110 1111 if ifd.ifdIdentity.Equals(exifcommon.IfdGpsInfoStandardIfdIdentity) == false { 1112 log.Panicf("GPS can only be read on GPS IFD: [%s]", ifd.ifdIdentity.UnindexedString()) 1113 } 1114 1115 if tags, found := ifd.entriesByTagId[TagGpsVersionId]; found == false { 1116 // We've seen this. We'll just have to default to assuming we're in a 1117 // 2.2.0.0 format. 1118 ifdEnumerateLogger.Warningf(nil, "No GPS version tag (0x%04x) found.", TagGpsVersionId) 1119 } else { 1120 versionBytes, err := tags[0].GetRawBytes() 1121 log.PanicIf(err) 1122 1123 hit := false 1124 for _, acceptedGpsVersion := range ValidGpsVersions { 1125 if bytes.Compare(versionBytes, acceptedGpsVersion[:]) == 0 { 1126 hit = true 1127 break 1128 } 1129 } 1130 1131 if hit != true { 1132 ifdEnumerateLogger.Warningf(nil, "GPS version not supported: %v", versionBytes) 1133 log.Panic(ErrNoGpsTags) 1134 } 1135 } 1136 1137 tags, found := ifd.entriesByTagId[TagLatitudeId] 1138 if found == false { 1139 ifdEnumerateLogger.Warningf(nil, "latitude not found") 1140 log.Panic(ErrNoGpsTags) 1141 } 1142 1143 latitudeValue, err := tags[0].Value() 1144 log.PanicIf(err) 1145 1146 // Look for whether North or South. 1147 tags, found = ifd.entriesByTagId[TagLatitudeRefId] 1148 if found == false { 1149 ifdEnumerateLogger.Warningf(nil, "latitude-ref not found") 1150 log.Panic(ErrNoGpsTags) 1151 } 1152 1153 latitudeRefValue, err := tags[0].Value() 1154 log.PanicIf(err) 1155 1156 tags, found = ifd.entriesByTagId[TagLongitudeId] 1157 if found == false { 1158 ifdEnumerateLogger.Warningf(nil, "longitude not found") 1159 log.Panic(ErrNoGpsTags) 1160 } 1161 1162 longitudeValue, err := tags[0].Value() 1163 log.PanicIf(err) 1164 1165 // Look for whether West or East. 1166 tags, found = ifd.entriesByTagId[TagLongitudeRefId] 1167 if found == false { 1168 ifdEnumerateLogger.Warningf(nil, "longitude-ref not found") 1169 log.Panic(ErrNoGpsTags) 1170 } 1171 1172 longitudeRefValue, err := tags[0].Value() 1173 log.PanicIf(err) 1174 1175 // Parse location. 1176 1177 latitudeRaw := latitudeValue.([]exifcommon.Rational) 1178 1179 gi.Latitude, err = NewGpsDegreesFromRationals(latitudeRefValue.(string), latitudeRaw) 1180 log.PanicIf(err) 1181 1182 longitudeRaw := longitudeValue.([]exifcommon.Rational) 1183 1184 gi.Longitude, err = NewGpsDegreesFromRationals(longitudeRefValue.(string), longitudeRaw) 1185 log.PanicIf(err) 1186 1187 // Parse altitude. 1188 1189 altitudeTags, foundAltitude := ifd.entriesByTagId[TagAltitudeId] 1190 altitudeRefTags, foundAltitudeRef := ifd.entriesByTagId[TagAltitudeRefId] 1191 1192 if foundAltitude == true && foundAltitudeRef == true { 1193 altitudePhrase, err := altitudeTags[0].Format() 1194 log.PanicIf(err) 1195 1196 ifdEnumerateLogger.Debugf(nil, "Altitude is [%s].", altitudePhrase) 1197 1198 altitudeValue, err := altitudeTags[0].Value() 1199 log.PanicIf(err) 1200 1201 altitudeRefPhrase, err := altitudeRefTags[0].Format() 1202 log.PanicIf(err) 1203 1204 ifdEnumerateLogger.Debugf(nil, "Altitude-reference is [%s].", altitudeRefPhrase) 1205 1206 altitudeRefValue, err := altitudeRefTags[0].Value() 1207 log.PanicIf(err) 1208 1209 altitudeRaw := altitudeValue.([]exifcommon.Rational) 1210 if altitudeRaw[0].Denominator > 0 { 1211 altitude := int(altitudeRaw[0].Numerator / altitudeRaw[0].Denominator) 1212 1213 if altitudeRefValue.([]byte)[0] == 1 { 1214 altitude *= -1 1215 } 1216 1217 gi.Altitude = altitude 1218 } 1219 } 1220 1221 // Parse timestamp from separate date and time tags. 1222 1223 timestampTags, foundTimestamp := ifd.entriesByTagId[TagTimestampId] 1224 datestampTags, foundDatestamp := ifd.entriesByTagId[TagDatestampId] 1225 1226 if foundTimestamp == true && foundDatestamp == true { 1227 datestampValue, err := datestampTags[0].Value() 1228 log.PanicIf(err) 1229 1230 datePhrase := datestampValue.(string) 1231 ifdEnumerateLogger.Debugf(nil, "Date tag value is [%s].", datePhrase) 1232 1233 // Normalize the separators. 1234 datePhrase = strings.ReplaceAll(datePhrase, "-", ":") 1235 1236 dateParts := strings.Split(datePhrase, ":") 1237 1238 year, err1 := strconv.ParseUint(dateParts[0], 10, 16) 1239 month, err2 := strconv.ParseUint(dateParts[1], 10, 8) 1240 day, err3 := strconv.ParseUint(dateParts[2], 10, 8) 1241 1242 if err1 == nil && err2 == nil && err3 == nil { 1243 timestampValue, err := timestampTags[0].Value() 1244 log.PanicIf(err) 1245 1246 timePhrase, err := timestampTags[0].Format() 1247 log.PanicIf(err) 1248 1249 ifdEnumerateLogger.Debugf(nil, "Time tag value is [%s].", timePhrase) 1250 1251 timestampRaw := timestampValue.([]exifcommon.Rational) 1252 1253 hour := int(timestampRaw[0].Numerator / timestampRaw[0].Denominator) 1254 minute := int(timestampRaw[1].Numerator / timestampRaw[1].Denominator) 1255 second := int(timestampRaw[2].Numerator / timestampRaw[2].Denominator) 1256 1257 gi.Timestamp = time.Date(int(year), time.Month(month), int(day), hour, minute, second, 0, time.UTC) 1258 } 1259 } 1260 1261 return gi, nil 1262 } 1263 1264 // ParsedTagVisitor is a callback used if wanting to visit through all tags and 1265 // child IFDs from the current IFD and going down. 1266 type ParsedTagVisitor func(*Ifd, *IfdTagEntry) error 1267 1268 // EnumerateTagsRecursively calls the given visitor function for every tag and 1269 // IFD in the current IFD, recursively. 1270 func (ifd *Ifd) EnumerateTagsRecursively(visitor ParsedTagVisitor) (err error) { 1271 defer func() { 1272 if state := recover(); state != nil { 1273 err = log.Wrap(state.(error)) 1274 } 1275 }() 1276 1277 for ptr := ifd; ptr != nil; ptr = ptr.nextIfd { 1278 for _, ite := range ifd.entries { 1279 childIfdPath := ite.ChildIfdPath() 1280 if childIfdPath != "" { 1281 childIfd := ifd.childIfdIndex[childIfdPath] 1282 1283 err := childIfd.EnumerateTagsRecursively(visitor) 1284 log.PanicIf(err) 1285 } else { 1286 err := visitor(ifd, ite) 1287 log.PanicIf(err) 1288 } 1289 } 1290 } 1291 1292 return nil 1293 } 1294 1295 // QueuedIfd is one IFD that has been identified but yet to be processed. 1296 type QueuedIfd struct { 1297 IfdIdentity *exifcommon.IfdIdentity 1298 1299 Offset uint32 1300 Parent *Ifd 1301 1302 // ParentTagIndex is our tag position in the parent IFD, if we had a parent 1303 // (if `ParentIfd` is not nil and we weren't an IFD referenced as a sibling 1304 // instead of as a child). 1305 ParentTagIndex int 1306 } 1307 1308 // IfdIndex collects a bunch of IFD and tag information stored in several 1309 // different ways in order to provide convenient lookups. 1310 type IfdIndex struct { 1311 RootIfd *Ifd 1312 Ifds []*Ifd 1313 Tree map[int]*Ifd 1314 Lookup map[string]*Ifd 1315 } 1316 1317 // Collect enumerates the different EXIF blocks (called IFDs) and builds out an 1318 // index struct for referencing all of the parsed data. 1319 func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error) { 1320 defer func() { 1321 if state := recover(); state != nil { 1322 err = log.Wrap(state.(error)) 1323 } 1324 }() 1325 1326 // TODO(dustin): Add MiscellaneousExifData to IfdIndex 1327 1328 tree := make(map[int]*Ifd) 1329 ifds := make([]*Ifd, 0) 1330 lookup := make(map[string]*Ifd) 1331 1332 queue := []QueuedIfd{ 1333 { 1334 IfdIdentity: exifcommon.IfdStandardIfdIdentity, 1335 Offset: rootIfdOffset, 1336 }, 1337 } 1338 1339 edges := make(map[uint32]*Ifd) 1340 1341 for { 1342 if len(queue) == 0 { 1343 break 1344 } 1345 1346 qi := queue[0] 1347 ii := qi.IfdIdentity 1348 1349 offset := qi.Offset 1350 parentIfd := qi.Parent 1351 1352 queue = queue[1:] 1353 1354 ifdEnumerateLogger.Debugf(nil, "Parsing IFD [%s] (%d) at offset (0x%04x) (Collect).", ii.String(), ii.Index(), offset) 1355 1356 bp, err := ie.getByteParser(offset) 1357 if err != nil { 1358 if err == ErrOffsetInvalid { 1359 return index, err 1360 } 1361 1362 log.Panic(err) 1363 } 1364 1365 // TODO(dustin): We don't need to pass the index in as a separate argument. Get from the II. 1366 1367 nextIfdOffset, entries, thumbnailData, err := ie.parseIfd(ii, bp, nil, false, nil) 1368 log.PanicIf(err) 1369 1370 currentOffset := bp.CurrentOffset() 1371 if currentOffset > ie.furthestOffset { 1372 ie.furthestOffset = currentOffset 1373 } 1374 1375 id := len(ifds) 1376 1377 entriesByTagId := make(map[uint16][]*IfdTagEntry) 1378 for _, ite := range entries { 1379 tagId := ite.TagId() 1380 1381 tags, found := entriesByTagId[tagId] 1382 if found == false { 1383 tags = make([]*IfdTagEntry, 0) 1384 } 1385 1386 entriesByTagId[tagId] = append(tags, ite) 1387 } 1388 1389 ifd := &Ifd{ 1390 ifdIdentity: ii, 1391 1392 byteOrder: ie.byteOrder, 1393 1394 id: id, 1395 1396 parentIfd: parentIfd, 1397 parentTagIndex: qi.ParentTagIndex, 1398 1399 offset: offset, 1400 entries: entries, 1401 entriesByTagId: entriesByTagId, 1402 1403 // This is populated as each child is processed. 1404 children: make([]*Ifd, 0), 1405 1406 nextIfdOffset: nextIfdOffset, 1407 thumbnailData: thumbnailData, 1408 1409 ifdMapping: ie.ifdMapping, 1410 tagIndex: ie.tagIndex, 1411 } 1412 1413 // Add ourselves to a big list of IFDs. 1414 ifds = append(ifds, ifd) 1415 1416 // Install ourselves into a by-id lookup table (keys are unique). 1417 tree[id] = ifd 1418 1419 // Install into by-name buckets. 1420 lookup[ii.String()] = ifd 1421 1422 // Add a link from the previous IFD in the chain to us. 1423 if previousIfd, found := edges[offset]; found == true { 1424 previousIfd.nextIfd = ifd 1425 } 1426 1427 // Attach as a child to our parent (where we appeared as a tag in 1428 // that IFD). 1429 if parentIfd != nil { 1430 parentIfd.children = append(parentIfd.children, ifd) 1431 } 1432 1433 // Determine if any of our entries is a child IFD and queue it. 1434 for i, ite := range entries { 1435 if ite.ChildIfdPath() == "" { 1436 continue 1437 } 1438 1439 tagId := ite.TagId() 1440 childIfdName := ite.ChildIfdName() 1441 1442 currentIfdTag := ii.IfdTag() 1443 1444 childIfdTag := 1445 exifcommon.NewIfdTag( 1446 ¤tIfdTag, 1447 tagId, 1448 childIfdName) 1449 1450 iiChild := ii.NewChild(childIfdTag, 0) 1451 1452 qi := QueuedIfd{ 1453 IfdIdentity: iiChild, 1454 1455 Offset: ite.getValueOffset(), 1456 Parent: ifd, 1457 ParentTagIndex: i, 1458 } 1459 1460 queue = append(queue, qi) 1461 } 1462 1463 // If there's another IFD in the chain. 1464 if nextIfdOffset != 0 { 1465 iiSibling := ii.NewSibling(ii.Index() + 1) 1466 1467 // Allow the next link to know what the previous link was. 1468 edges[nextIfdOffset] = ifd 1469 1470 qi := QueuedIfd{ 1471 IfdIdentity: iiSibling, 1472 Offset: nextIfdOffset, 1473 } 1474 1475 queue = append(queue, qi) 1476 } 1477 } 1478 1479 index.RootIfd = tree[0] 1480 index.Ifds = ifds 1481 index.Tree = tree 1482 index.Lookup = lookup 1483 1484 err = ie.setChildrenIndex(index.RootIfd) 1485 log.PanicIf(err) 1486 1487 ifdEnumerateLogger.Debugf(nil, "Collect: It looks like the furthest offset that contained EXIF data in the EXIF blob was (%d).", ie.FurthestOffset()) 1488 1489 return index, nil 1490 } 1491 1492 func (ie *IfdEnumerate) setChildrenIndex(ifd *Ifd) (err error) { 1493 defer func() { 1494 if state := recover(); state != nil { 1495 err = log.Wrap(state.(error)) 1496 } 1497 }() 1498 1499 childIfdIndex := make(map[string]*Ifd) 1500 for _, childIfd := range ifd.children { 1501 childIfdIndex[childIfd.ifdIdentity.UnindexedString()] = childIfd 1502 } 1503 1504 ifd.childIfdIndex = childIfdIndex 1505 1506 for _, childIfd := range ifd.children { 1507 err := ie.setChildrenIndex(childIfd) 1508 log.PanicIf(err) 1509 } 1510 1511 return nil 1512 } 1513 1514 // FurthestOffset returns the furthest offset visited in the EXIF blob. This 1515 // *does not* account for the locations of any undefined tags since we always 1516 // evaluate the furthest offset, whether or not the user wants to know it. 1517 // 1518 // We are not willing to incur the cost of actually parsing those tags just to 1519 // know their length when there are still undefined tags that are out there 1520 // that we still won't have any idea how to parse, thus making this an 1521 // approximation regardless of how clever we get. 1522 func (ie *IfdEnumerate) FurthestOffset() uint32 { 1523 1524 // TODO(dustin): Add test 1525 1526 return ie.furthestOffset 1527 } 1528 1529 // parseOneIfd is a hack to use an IE to parse a raw IFD block. Can be used for 1530 // testing. The fqIfdPath ("fully-qualified IFD path") will be less qualified 1531 // in that the numeric index will always be zero (the zeroth child) rather than 1532 // the proper number (if its actually a sibling to the first child, for 1533 // instance). 1534 func parseOneIfd(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exifcommon.IfdIdentity, byteOrder binary.ByteOrder, ifdBlock []byte, visitor TagVisitorFn) (nextIfdOffset uint32, entries []*IfdTagEntry, err error) { 1535 defer func() { 1536 if state := recover(); state != nil { 1537 err = log.Wrap(state.(error)) 1538 } 1539 }() 1540 1541 // TODO(dustin): Add test 1542 1543 ebs := NewExifReadSeekerWithBytes(ifdBlock) 1544 1545 rs, err := ebs.GetReadSeeker(0) 1546 log.PanicIf(err) 1547 1548 bp, err := newByteParser(rs, byteOrder, 0) 1549 if err != nil { 1550 if err == ErrOffsetInvalid { 1551 return 0, nil, err 1552 } 1553 1554 log.Panic(err) 1555 } 1556 1557 dummyEbs := NewExifReadSeekerWithBytes([]byte{}) 1558 ie := NewIfdEnumerate(ifdMapping, tagIndex, dummyEbs, byteOrder) 1559 1560 nextIfdOffset, entries, _, err = ie.parseIfd(ii, bp, visitor, true, nil) 1561 log.PanicIf(err) 1562 1563 return nextIfdOffset, entries, nil 1564 } 1565 1566 // parseOneTag is a hack to use an IE to parse a raw tag block. 1567 func parseOneTag(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exifcommon.IfdIdentity, byteOrder binary.ByteOrder, tagBlock []byte) (ite *IfdTagEntry, err error) { 1568 defer func() { 1569 if state := recover(); state != nil { 1570 err = log.Wrap(state.(error)) 1571 } 1572 }() 1573 1574 // TODO(dustin): Add test 1575 1576 ebs := NewExifReadSeekerWithBytes(tagBlock) 1577 1578 rs, err := ebs.GetReadSeeker(0) 1579 log.PanicIf(err) 1580 1581 bp, err := newByteParser(rs, byteOrder, 0) 1582 if err != nil { 1583 if err == ErrOffsetInvalid { 1584 return nil, err 1585 } 1586 1587 log.Panic(err) 1588 } 1589 1590 dummyEbs := NewExifReadSeekerWithBytes([]byte{}) 1591 ie := NewIfdEnumerate(ifdMapping, tagIndex, dummyEbs, byteOrder) 1592 1593 ite, err = ie.parseTag(ii, 0, bp) 1594 log.PanicIf(err) 1595 1596 err = ie.tagPostParse(ite, nil) 1597 if err != nil { 1598 if err == ErrTagNotFound { 1599 return nil, err 1600 } 1601 1602 log.Panic(err) 1603 } 1604 1605 return ite, nil 1606 } 1607 1608 // FindIfdFromRootIfd returns the given `Ifd` given the root-IFD and path of the 1609 // desired IFD. 1610 func FindIfdFromRootIfd(rootIfd *Ifd, ifdPath string) (ifd *Ifd, err error) { 1611 defer func() { 1612 if state := recover(); state != nil { 1613 err = log.Wrap(state.(error)) 1614 } 1615 }() 1616 1617 // TODO(dustin): !! Add test. 1618 1619 lineage, err := rootIfd.ifdMapping.ResolvePath(ifdPath) 1620 log.PanicIf(err) 1621 1622 // Confirm the first IFD is our root IFD type, and then prune it because 1623 // from then on we'll be searching down through our children. 1624 1625 if len(lineage) == 0 { 1626 log.Panicf("IFD path must be non-empty.") 1627 } else if lineage[0].Name != exifcommon.IfdStandardIfdIdentity.Name() { 1628 log.Panicf("First IFD path item must be [%s].", exifcommon.IfdStandardIfdIdentity.Name()) 1629 } 1630 1631 desiredRootIndex := lineage[0].Index 1632 lineage = lineage[1:] 1633 1634 // TODO(dustin): !! This is a poorly conceived fix that just doubles the work we already have to do below, which then interacts badly with the indices not being properly represented in the IFD-phrase. 1635 // TODO(dustin): !! <-- However, we're not sure whether we shouldn't store a secondary IFD-path with the indices. Some IFDs may not necessarily restrict which IFD indices they can be a child of (only the IFD itself matters). Validation should be delegated to the caller. 1636 thisIfd := rootIfd 1637 for currentRootIndex := 0; currentRootIndex < desiredRootIndex; currentRootIndex++ { 1638 if thisIfd.nextIfd == nil { 1639 log.Panicf("Root-IFD index (%d) does not exist in the data.", currentRootIndex) 1640 } 1641 1642 thisIfd = thisIfd.nextIfd 1643 } 1644 1645 for _, itii := range lineage { 1646 var hit *Ifd 1647 for _, childIfd := range thisIfd.children { 1648 if childIfd.ifdIdentity.TagId() == itii.TagId { 1649 hit = childIfd 1650 break 1651 } 1652 } 1653 1654 // If we didn't find the child, add it. 1655 if hit == nil { 1656 log.Panicf("IFD [%s] in [%s] not found: %s", itii.Name, ifdPath, thisIfd.children) 1657 } 1658 1659 thisIfd = hit 1660 1661 // If we didn't find the sibling, add it. 1662 for i := 0; i < itii.Index; i++ { 1663 if thisIfd.nextIfd == nil { 1664 log.Panicf("IFD [%s] does not have (%d) occurrences/siblings", thisIfd.ifdIdentity.UnindexedString(), itii.Index) 1665 } 1666 1667 thisIfd = thisIfd.nextIfd 1668 } 1669 } 1670 1671 return thisIfd, nil 1672 }