gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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(&currentIfdTag, 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 						&currentIfdTag,
    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 					&currentIfdTag,
   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 }