gtsocial-umbx

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

ifd_builder.go (29578B)


      1 package exif
      2 
      3 // NOTES:
      4 //
      5 // The thumbnail offset and length tags shouldn't be set directly. Use the
      6 // (*IfdBuilder).SetThumbnail() method instead.
      7 
      8 import (
      9 	"errors"
     10 	"fmt"
     11 	"strings"
     12 
     13 	"encoding/binary"
     14 
     15 	"github.com/dsoprea/go-logging"
     16 
     17 	"github.com/dsoprea/go-exif/v3/common"
     18 	"github.com/dsoprea/go-exif/v3/undefined"
     19 )
     20 
     21 var (
     22 	ifdBuilderLogger = log.NewLogger("exif.ifd_builder")
     23 )
     24 
     25 var (
     26 	ErrTagEntryNotFound = errors.New("tag entry not found")
     27 	ErrChildIbNotFound  = errors.New("child IB not found")
     28 )
     29 
     30 type IfdBuilderTagValue struct {
     31 	valueBytes []byte
     32 	ib         *IfdBuilder
     33 }
     34 
     35 func (ibtv IfdBuilderTagValue) String() string {
     36 	if ibtv.IsBytes() == true {
     37 		var valuePhrase string
     38 		if len(ibtv.valueBytes) <= 8 {
     39 			valuePhrase = fmt.Sprintf("%v", ibtv.valueBytes)
     40 		} else {
     41 			valuePhrase = fmt.Sprintf("%v...", ibtv.valueBytes[:8])
     42 		}
     43 
     44 		return fmt.Sprintf("IfdBuilderTagValue<BYTES=%v LEN=(%d)>", valuePhrase, len(ibtv.valueBytes))
     45 	} else if ibtv.IsIb() == true {
     46 		return fmt.Sprintf("IfdBuilderTagValue<IB=%s>", ibtv.ib)
     47 	} else {
     48 		log.Panicf("IBTV state undefined")
     49 		return ""
     50 	}
     51 }
     52 
     53 func NewIfdBuilderTagValueFromBytes(valueBytes []byte) *IfdBuilderTagValue {
     54 	return &IfdBuilderTagValue{
     55 		valueBytes: valueBytes,
     56 	}
     57 }
     58 
     59 func NewIfdBuilderTagValueFromIfdBuilder(ib *IfdBuilder) *IfdBuilderTagValue {
     60 	return &IfdBuilderTagValue{
     61 		ib: ib,
     62 	}
     63 }
     64 
     65 // IsBytes returns true if the bytes are populated. This is always the case
     66 // when we're loaded from a tag in an existing IFD.
     67 func (ibtv IfdBuilderTagValue) IsBytes() bool {
     68 	return ibtv.valueBytes != nil
     69 }
     70 
     71 func (ibtv IfdBuilderTagValue) Bytes() []byte {
     72 	if ibtv.IsBytes() == false {
     73 		log.Panicf("this tag is not a byte-slice value")
     74 	} else if ibtv.IsIb() == true {
     75 		log.Panicf("this tag is an IFD-builder value not a byte-slice")
     76 	}
     77 
     78 	return ibtv.valueBytes
     79 }
     80 
     81 func (ibtv IfdBuilderTagValue) IsIb() bool {
     82 	return ibtv.ib != nil
     83 }
     84 
     85 func (ibtv IfdBuilderTagValue) Ib() *IfdBuilder {
     86 	if ibtv.IsIb() == false {
     87 		log.Panicf("this tag is not an IFD-builder value")
     88 	} else if ibtv.IsBytes() == true {
     89 		log.Panicf("this tag is a byte-slice, not a IFD-builder")
     90 	}
     91 
     92 	return ibtv.ib
     93 }
     94 
     95 type BuilderTag struct {
     96 	// ifdPath is the path of the IFD that hosts this tag.
     97 	ifdPath string
     98 
     99 	tagId  uint16
    100 	typeId exifcommon.TagTypePrimitive
    101 
    102 	// value is either a value that can be encoded, an IfdBuilder instance (for
    103 	// child IFDs), or an IfdTagEntry instance representing an existing,
    104 	// previously-stored tag.
    105 	value *IfdBuilderTagValue
    106 
    107 	// byteOrder is the byte order. It's chiefly/originally here to support
    108 	// printing the value.
    109 	byteOrder binary.ByteOrder
    110 }
    111 
    112 func NewBuilderTag(ifdPath string, tagId uint16, typeId exifcommon.TagTypePrimitive, value *IfdBuilderTagValue, byteOrder binary.ByteOrder) *BuilderTag {
    113 	return &BuilderTag{
    114 		ifdPath:   ifdPath,
    115 		tagId:     tagId,
    116 		typeId:    typeId,
    117 		value:     value,
    118 		byteOrder: byteOrder,
    119 	}
    120 }
    121 
    122 func NewChildIfdBuilderTag(ifdPath string, tagId uint16, value *IfdBuilderTagValue) *BuilderTag {
    123 	return &BuilderTag{
    124 		ifdPath: ifdPath,
    125 		tagId:   tagId,
    126 		typeId:  exifcommon.TypeLong,
    127 		value:   value,
    128 	}
    129 }
    130 
    131 func (bt *BuilderTag) Value() (value *IfdBuilderTagValue) {
    132 	return bt.value
    133 }
    134 
    135 func (bt *BuilderTag) String() string {
    136 	var valueString string
    137 
    138 	if bt.value.IsBytes() == true {
    139 		var err error
    140 
    141 		valueString, err = exifcommon.FormatFromBytes(bt.value.Bytes(), bt.typeId, false, bt.byteOrder)
    142 		log.PanicIf(err)
    143 	} else {
    144 		valueString = fmt.Sprintf("%v", bt.value)
    145 	}
    146 
    147 	return fmt.Sprintf("BuilderTag<IFD-PATH=[%s] TAG-ID=(0x%04x) TAG-TYPE=[%s] VALUE=[%s]>", bt.ifdPath, bt.tagId, bt.typeId.String(), valueString)
    148 }
    149 
    150 func (bt *BuilderTag) SetValue(byteOrder binary.ByteOrder, value interface{}) (err error) {
    151 	defer func() {
    152 		if state := recover(); state != nil {
    153 			err = log.Wrap(state.(error))
    154 		}
    155 	}()
    156 
    157 	// TODO(dustin): !! Add test.
    158 
    159 	var ed exifcommon.EncodedData
    160 	if bt.typeId == exifcommon.TypeUndefined {
    161 		encodeable := value.(exifundefined.EncodeableValue)
    162 
    163 		encoded, unitCount, err := exifundefined.Encode(encodeable, byteOrder)
    164 		log.PanicIf(err)
    165 
    166 		ed = exifcommon.EncodedData{
    167 			Type:      exifcommon.TypeUndefined,
    168 			Encoded:   encoded,
    169 			UnitCount: unitCount,
    170 		}
    171 	} else {
    172 		ve := exifcommon.NewValueEncoder(byteOrder)
    173 
    174 		var err error
    175 
    176 		ed, err = ve.Encode(value)
    177 		log.PanicIf(err)
    178 	}
    179 
    180 	bt.value = NewIfdBuilderTagValueFromBytes(ed.Encoded)
    181 
    182 	return nil
    183 }
    184 
    185 // NewStandardBuilderTag constructs a `BuilderTag` instance. The type is looked
    186 // up. `ii` is the type of IFD that owns this tag.
    187 func NewStandardBuilderTag(ifdPath string, it *IndexedTag, byteOrder binary.ByteOrder, value interface{}) *BuilderTag {
    188 	// If there is more than one supported type, we'll go with the larger to
    189 	// encode with. It'll use the same amount of fixed-space, and we'll
    190 	// eliminate unnecessary overflows/issues.
    191 	tagType := it.GetEncodingType(value)
    192 
    193 	var rawBytes []byte
    194 	if it.DoesSupportType(exifcommon.TypeUndefined) == true {
    195 		encodeable := value.(exifundefined.EncodeableValue)
    196 
    197 		var err error
    198 
    199 		rawBytes, _, err = exifundefined.Encode(encodeable, byteOrder)
    200 		log.PanicIf(err)
    201 	} else {
    202 		ve := exifcommon.NewValueEncoder(byteOrder)
    203 
    204 		ed, err := ve.Encode(value)
    205 		log.PanicIf(err)
    206 
    207 		rawBytes = ed.Encoded
    208 	}
    209 
    210 	tagValue := NewIfdBuilderTagValueFromBytes(rawBytes)
    211 
    212 	return NewBuilderTag(
    213 		ifdPath,
    214 		it.Id,
    215 		tagType,
    216 		tagValue,
    217 		byteOrder)
    218 }
    219 
    220 type IfdBuilder struct {
    221 	ifdIdentity *exifcommon.IfdIdentity
    222 
    223 	byteOrder binary.ByteOrder
    224 
    225 	// Includes both normal tags and IFD tags (which point to child IFDs).
    226 	// TODO(dustin): Keep a separate list of children like with `Ifd`.
    227 	// TODO(dustin): Either rename this or `Entries` in `Ifd` to be the same thing.
    228 	tags []*BuilderTag
    229 
    230 	// existingOffset will be the offset that this IFD is currently found at if
    231 	// it represents an IFD that has previously been stored (or 0 if not).
    232 	existingOffset uint32
    233 
    234 	// nextIb represents the next link if we're chaining to another.
    235 	nextIb *IfdBuilder
    236 
    237 	// thumbnailData is populated with thumbnail data if there was thumbnail
    238 	// data. Otherwise, it's nil.
    239 	thumbnailData []byte
    240 
    241 	ifdMapping *exifcommon.IfdMapping
    242 	tagIndex   *TagIndex
    243 }
    244 
    245 func NewIfdBuilder(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exifcommon.IfdIdentity, byteOrder binary.ByteOrder) (ib *IfdBuilder) {
    246 	ib = &IfdBuilder{
    247 		ifdIdentity: ii,
    248 
    249 		byteOrder: byteOrder,
    250 		tags:      make([]*BuilderTag, 0),
    251 
    252 		ifdMapping: ifdMapping,
    253 		tagIndex:   tagIndex,
    254 	}
    255 
    256 	return ib
    257 }
    258 
    259 // NewIfdBuilderWithExistingIfd creates a new IB using the same header type
    260 // information as the given IFD.
    261 func NewIfdBuilderWithExistingIfd(ifd *Ifd) (ib *IfdBuilder) {
    262 	ib = &IfdBuilder{
    263 		ifdIdentity: ifd.IfdIdentity(),
    264 
    265 		byteOrder:      ifd.ByteOrder(),
    266 		existingOffset: ifd.Offset(),
    267 		ifdMapping:     ifd.ifdMapping,
    268 		tagIndex:       ifd.tagIndex,
    269 	}
    270 
    271 	return ib
    272 }
    273 
    274 // NewIfdBuilderFromExistingChain creates a chain of IB instances from an
    275 // IFD chain generated from real data.
    276 func NewIfdBuilderFromExistingChain(rootIfd *Ifd) (firstIb *IfdBuilder) {
    277 	var lastIb *IfdBuilder
    278 	i := 0
    279 	for thisExistingIfd := rootIfd; thisExistingIfd != nil; thisExistingIfd = thisExistingIfd.nextIfd {
    280 		newIb := NewIfdBuilder(
    281 			rootIfd.ifdMapping,
    282 			rootIfd.tagIndex,
    283 			rootIfd.ifdIdentity,
    284 			thisExistingIfd.ByteOrder())
    285 
    286 		if firstIb == nil {
    287 			firstIb = newIb
    288 		} else {
    289 			lastIb.SetNextIb(newIb)
    290 		}
    291 
    292 		err := newIb.AddTagsFromExisting(thisExistingIfd, nil, nil)
    293 		log.PanicIf(err)
    294 
    295 		lastIb = newIb
    296 		i++
    297 	}
    298 
    299 	return firstIb
    300 }
    301 
    302 func (ib *IfdBuilder) IfdIdentity() *exifcommon.IfdIdentity {
    303 	return ib.ifdIdentity
    304 }
    305 
    306 func (ib *IfdBuilder) NextIb() (nextIb *IfdBuilder, err error) {
    307 	return ib.nextIb, nil
    308 }
    309 
    310 func (ib *IfdBuilder) ChildWithTagId(childIfdTagId uint16) (childIb *IfdBuilder, err error) {
    311 	defer func() {
    312 		if state := recover(); state != nil {
    313 			err = log.Wrap(state.(error))
    314 		}
    315 	}()
    316 
    317 	for _, bt := range ib.tags {
    318 		if bt.value.IsIb() == false {
    319 			continue
    320 		}
    321 
    322 		childIbThis := bt.value.Ib()
    323 
    324 		if childIbThis.IfdIdentity().TagId() == childIfdTagId {
    325 			return childIbThis, nil
    326 		}
    327 	}
    328 
    329 	log.Panic(ErrChildIbNotFound)
    330 
    331 	// Never reached.
    332 	return nil, nil
    333 }
    334 
    335 func getOrCreateIbFromRootIbInner(rootIb *IfdBuilder, parentIb *IfdBuilder, currentLineage []exifcommon.IfdTagIdAndIndex) (ib *IfdBuilder, err error) {
    336 	defer func() {
    337 		if state := recover(); state != nil {
    338 			err = log.Wrap(state.(error))
    339 		}
    340 	}()
    341 
    342 	// TODO(dustin): !! Add test.
    343 
    344 	thisIb := rootIb
    345 
    346 	// Since we're calling ourselves recursively with incrementally different
    347 	// paths, the FQ IFD-path of the parent that called us needs to be passed
    348 	// in, in order for us to know it.
    349 	var parentLineage []exifcommon.IfdTagIdAndIndex
    350 	if parentIb != nil {
    351 		var err error
    352 
    353 		parentLineage, err = thisIb.ifdMapping.ResolvePath(parentIb.IfdIdentity().String())
    354 		log.PanicIf(err)
    355 	}
    356 
    357 	// Process the current path part.
    358 	currentItIi := currentLineage[0]
    359 
    360 	// Make sure the leftmost part of the FQ IFD-path agrees with the IB we
    361 	// were given.
    362 
    363 	expectedFqRootIfdPath := ""
    364 	if parentLineage != nil {
    365 		expectedLineage := append(parentLineage, currentItIi)
    366 		expectedFqRootIfdPath = thisIb.ifdMapping.PathPhraseFromLineage(expectedLineage)
    367 	} else {
    368 		expectedFqRootIfdPath = thisIb.ifdMapping.PathPhraseFromLineage(currentLineage[:1])
    369 	}
    370 
    371 	if expectedFqRootIfdPath != thisIb.IfdIdentity().String() {
    372 		log.Panicf("the FQ IFD-path [%s] we were given does not match the builder's FQ IFD-path [%s]", expectedFqRootIfdPath, thisIb.IfdIdentity().String())
    373 	}
    374 
    375 	// If we actually wanted a sibling (currentItIi.Index > 0) then seek to it,
    376 	// appending new siblings, as required, until we get there.
    377 	for i := 0; i < currentItIi.Index; i++ {
    378 		if thisIb.nextIb == nil {
    379 			// Generate an FQ IFD-path for the sibling. It'll use the same
    380 			// non-FQ IFD-path as the current IB.
    381 
    382 			iiSibling := thisIb.IfdIdentity().NewSibling(i + 1)
    383 			thisIb.nextIb = NewIfdBuilder(thisIb.ifdMapping, thisIb.tagIndex, iiSibling, thisIb.byteOrder)
    384 		}
    385 
    386 		thisIb = thisIb.nextIb
    387 	}
    388 
    389 	// There is no child IFD to process. We're done.
    390 	if len(currentLineage) == 1 {
    391 		return thisIb, nil
    392 	}
    393 
    394 	// Establish the next child to be processed.
    395 
    396 	childItii := currentLineage[1]
    397 
    398 	var foundChild *IfdBuilder
    399 	for _, bt := range thisIb.tags {
    400 		if bt.value.IsIb() == false {
    401 			continue
    402 		}
    403 
    404 		childIb := bt.value.Ib()
    405 
    406 		if childIb.IfdIdentity().TagId() == childItii.TagId {
    407 			foundChild = childIb
    408 			break
    409 		}
    410 	}
    411 
    412 	// If we didn't find the child, add it.
    413 
    414 	if foundChild == nil {
    415 		currentIfdTag := thisIb.IfdIdentity().IfdTag()
    416 
    417 		childIfdTag :=
    418 			exifcommon.NewIfdTag(
    419 				&currentIfdTag,
    420 				childItii.TagId,
    421 				childItii.Name)
    422 
    423 		iiChild := thisIb.IfdIdentity().NewChild(childIfdTag, 0)
    424 
    425 		foundChild =
    426 			NewIfdBuilder(
    427 				thisIb.ifdMapping,
    428 				thisIb.tagIndex,
    429 				iiChild,
    430 				thisIb.byteOrder)
    431 
    432 		err = thisIb.AddChildIb(foundChild)
    433 		log.PanicIf(err)
    434 	}
    435 
    436 	finalIb, err := getOrCreateIbFromRootIbInner(foundChild, thisIb, currentLineage[1:])
    437 	log.PanicIf(err)
    438 
    439 	return finalIb, nil
    440 }
    441 
    442 // GetOrCreateIbFromRootIb returns an IB representing the requested IFD, even if
    443 // an IB doesn't already exist for it. This function may call itself
    444 // recursively.
    445 func GetOrCreateIbFromRootIb(rootIb *IfdBuilder, fqIfdPath string) (ib *IfdBuilder, err error) {
    446 	defer func() {
    447 		if state := recover(); state != nil {
    448 			err = log.Wrap(state.(error))
    449 		}
    450 	}()
    451 
    452 	// lineage is a necessity of our recursion process. It doesn't include any
    453 	// parent IFDs on its left-side; it starts with the current IB only.
    454 	lineage, err := rootIb.ifdMapping.ResolvePath(fqIfdPath)
    455 	log.PanicIf(err)
    456 
    457 	ib, err = getOrCreateIbFromRootIbInner(rootIb, nil, lineage)
    458 	log.PanicIf(err)
    459 
    460 	return ib, nil
    461 }
    462 
    463 func (ib *IfdBuilder) String() string {
    464 	nextIfdPhrase := ""
    465 	if ib.nextIb != nil {
    466 		// TODO(dustin): We were setting this to ii.String(), but we were getting hex-data when printing this after building from an existing chain.
    467 		nextIfdPhrase = ib.nextIb.IfdIdentity().UnindexedString()
    468 	}
    469 
    470 	return fmt.Sprintf("IfdBuilder<PATH=[%s] TAG-ID=(0x%04x) COUNT=(%d) OFF=(0x%04x) NEXT-IFD-PATH=[%s]>", ib.IfdIdentity().UnindexedString(), ib.IfdIdentity().TagId(), len(ib.tags), ib.existingOffset, nextIfdPhrase)
    471 }
    472 
    473 func (ib *IfdBuilder) Tags() (tags []*BuilderTag) {
    474 	return ib.tags
    475 }
    476 
    477 // SetThumbnail sets thumbnail data.
    478 //
    479 // NOTES:
    480 //
    481 // - We don't manage any facet of the thumbnail data. This is the
    482 //   responsibility of the user/developer.
    483 // - This method will fail unless the thumbnail is set on a the root IFD.
    484 //   However, in order to be valid, it must be set on the second one, linked to
    485 //   by the first, as per the EXIF/TIFF specification.
    486 // - We set the offset to (0) now but will allocate the data and properly assign
    487 //   the offset when the IB is encoded (later).
    488 func (ib *IfdBuilder) SetThumbnail(data []byte) (err error) {
    489 	defer func() {
    490 		if state := recover(); state != nil {
    491 			err = log.Wrap(state.(error))
    492 		}
    493 	}()
    494 
    495 	if ib.IfdIdentity().UnindexedString() != exifcommon.IfdStandardIfdIdentity.UnindexedString() {
    496 		log.Panicf("thumbnails can only go into a root Ifd (and only the second one)")
    497 	}
    498 
    499 	// TODO(dustin): !! Add a test for this function.
    500 
    501 	if data == nil || len(data) == 0 {
    502 		log.Panic("thumbnail is empty")
    503 	}
    504 
    505 	ib.thumbnailData = data
    506 
    507 	ibtvfb := NewIfdBuilderTagValueFromBytes(ib.thumbnailData)
    508 	offsetBt :=
    509 		NewBuilderTag(
    510 			ib.IfdIdentity().UnindexedString(),
    511 			ThumbnailOffsetTagId,
    512 			exifcommon.TypeLong,
    513 			ibtvfb,
    514 			ib.byteOrder)
    515 
    516 	err = ib.Set(offsetBt)
    517 	log.PanicIf(err)
    518 
    519 	thumbnailSizeIt, err := ib.tagIndex.Get(ib.IfdIdentity(), ThumbnailSizeTagId)
    520 	log.PanicIf(err)
    521 
    522 	sizeBt := NewStandardBuilderTag(ib.IfdIdentity().UnindexedString(), thumbnailSizeIt, ib.byteOrder, []uint32{uint32(len(ib.thumbnailData))})
    523 
    524 	err = ib.Set(sizeBt)
    525 	log.PanicIf(err)
    526 
    527 	return nil
    528 }
    529 
    530 func (ib *IfdBuilder) Thumbnail() []byte {
    531 	return ib.thumbnailData
    532 }
    533 
    534 func (ib *IfdBuilder) printTagTree(levels int) {
    535 	indent := strings.Repeat(" ", levels*2)
    536 
    537 	i := 0
    538 	for currentIb := ib; currentIb != nil; currentIb = currentIb.nextIb {
    539 		prefix := " "
    540 		if i > 0 {
    541 			prefix = ">"
    542 		}
    543 
    544 		if levels == 0 {
    545 			fmt.Printf("%s%sIFD: %s INDEX=(%d)\n", indent, prefix, currentIb, i)
    546 		} else {
    547 			fmt.Printf("%s%sChild IFD: %s\n", indent, prefix, currentIb)
    548 		}
    549 
    550 		if len(currentIb.tags) > 0 {
    551 			fmt.Printf("\n")
    552 
    553 			for i, tag := range currentIb.tags {
    554 				isChildIb := false
    555 				_, err := ib.ifdMapping.GetChild(currentIb.IfdIdentity().UnindexedString(), tag.tagId)
    556 				if err == nil {
    557 					isChildIb = true
    558 				} else if log.Is(err, exifcommon.ErrChildIfdNotMapped) == false {
    559 					log.Panic(err)
    560 				}
    561 
    562 				tagName := ""
    563 
    564 				// If a normal tag (not a child IFD) get the name.
    565 				if isChildIb == true {
    566 					tagName = "<Child IFD>"
    567 				} else {
    568 					it, err := ib.tagIndex.Get(ib.ifdIdentity, tag.tagId)
    569 					if log.Is(err, ErrTagNotFound) == true {
    570 						tagName = "<UNKNOWN>"
    571 					} else if err != nil {
    572 						log.Panic(err)
    573 					} else {
    574 						tagName = it.Name
    575 					}
    576 				}
    577 
    578 				value := tag.Value()
    579 
    580 				if value.IsIb() == true {
    581 					fmt.Printf("%s  (%d): [%s] %s\n", indent, i, tagName, value.Ib())
    582 				} else {
    583 					fmt.Printf("%s  (%d): [%s] %s\n", indent, i, tagName, tag)
    584 				}
    585 
    586 				if isChildIb == true {
    587 					if tag.value.IsIb() == false {
    588 						log.Panicf("tag-ID (0x%04x) is an IFD but the tag value is not an IB instance: %v", tag.tagId, tag)
    589 					}
    590 
    591 					fmt.Printf("\n")
    592 
    593 					childIb := tag.value.Ib()
    594 					childIb.printTagTree(levels + 1)
    595 				}
    596 			}
    597 
    598 			fmt.Printf("\n")
    599 		}
    600 
    601 		i++
    602 	}
    603 }
    604 
    605 func (ib *IfdBuilder) PrintTagTree() {
    606 	ib.printTagTree(0)
    607 }
    608 
    609 func (ib *IfdBuilder) printIfdTree(levels int) {
    610 	indent := strings.Repeat(" ", levels*2)
    611 
    612 	i := 0
    613 	for currentIb := ib; currentIb != nil; currentIb = currentIb.nextIb {
    614 		prefix := " "
    615 		if i > 0 {
    616 			prefix = ">"
    617 		}
    618 
    619 		fmt.Printf("%s%s%s\n", indent, prefix, currentIb)
    620 
    621 		if len(currentIb.tags) > 0 {
    622 			for _, tag := range currentIb.tags {
    623 				isChildIb := false
    624 				_, err := ib.ifdMapping.GetChild(currentIb.IfdIdentity().UnindexedString(), tag.tagId)
    625 				if err == nil {
    626 					isChildIb = true
    627 				} else if log.Is(err, exifcommon.ErrChildIfdNotMapped) == false {
    628 					log.Panic(err)
    629 				}
    630 
    631 				if isChildIb == true {
    632 					if tag.value.IsIb() == false {
    633 						log.Panicf("tag-ID (0x%04x) is an IFD but the tag value is not an IB instance: %v", tag.tagId, tag)
    634 					}
    635 
    636 					childIb := tag.value.Ib()
    637 					childIb.printIfdTree(levels + 1)
    638 				}
    639 			}
    640 		}
    641 
    642 		i++
    643 	}
    644 }
    645 
    646 func (ib *IfdBuilder) PrintIfdTree() {
    647 	ib.printIfdTree(0)
    648 }
    649 
    650 func (ib *IfdBuilder) dumpToStrings(thisIb *IfdBuilder, prefix string, tagId uint16, lines []string) (linesOutput []string) {
    651 	if lines == nil {
    652 		linesOutput = make([]string, 0)
    653 	} else {
    654 		linesOutput = lines
    655 	}
    656 
    657 	siblingIfdIndex := 0
    658 	for ; thisIb != nil; thisIb = thisIb.nextIb {
    659 		line := fmt.Sprintf("IFD<PARENTS=[%s] FQ-IFD-PATH=[%s] IFD-INDEX=(%d) IFD-TAG-ID=(0x%04x) TAG=[0x%04x]>", prefix, thisIb.IfdIdentity().String(), siblingIfdIndex, thisIb.IfdIdentity().TagId(), tagId)
    660 		linesOutput = append(linesOutput, line)
    661 
    662 		for i, tag := range thisIb.tags {
    663 			var childIb *IfdBuilder
    664 			childIfdName := ""
    665 			if tag.value.IsIb() == true {
    666 				childIb = tag.value.Ib()
    667 				childIfdName = childIb.IfdIdentity().UnindexedString()
    668 			}
    669 
    670 			line := fmt.Sprintf("TAG<PARENTS=[%s] FQ-IFD-PATH=[%s] IFD-TAG-ID=(0x%04x) CHILD-IFD=[%s] TAG-INDEX=(%d) TAG=[0x%04x]>", prefix, thisIb.IfdIdentity().String(), thisIb.IfdIdentity().TagId(), childIfdName, i, tag.tagId)
    671 			linesOutput = append(linesOutput, line)
    672 
    673 			if childIb == nil {
    674 				continue
    675 			}
    676 
    677 			childPrefix := ""
    678 			if prefix == "" {
    679 				childPrefix = fmt.Sprintf("%s", thisIb.IfdIdentity().UnindexedString())
    680 			} else {
    681 				childPrefix = fmt.Sprintf("%s->%s", prefix, thisIb.IfdIdentity().UnindexedString())
    682 			}
    683 
    684 			linesOutput = thisIb.dumpToStrings(childIb, childPrefix, tag.tagId, linesOutput)
    685 		}
    686 
    687 		siblingIfdIndex++
    688 	}
    689 
    690 	return linesOutput
    691 }
    692 
    693 func (ib *IfdBuilder) DumpToStrings() (lines []string) {
    694 	return ib.dumpToStrings(ib, "", 0, lines)
    695 }
    696 
    697 func (ib *IfdBuilder) SetNextIb(nextIb *IfdBuilder) (err error) {
    698 	defer func() {
    699 		if state := recover(); state != nil {
    700 			err = log.Wrap(state.(error))
    701 		}
    702 	}()
    703 
    704 	ib.nextIb = nextIb
    705 
    706 	return nil
    707 }
    708 
    709 func (ib *IfdBuilder) DeleteN(tagId uint16, n int) (err error) {
    710 	defer func() {
    711 		if state := recover(); state != nil {
    712 			err = log.Wrap(state.(error))
    713 		}
    714 	}()
    715 
    716 	if n < 1 {
    717 		log.Panicf("N must be at least 1: (%d)", n)
    718 	}
    719 
    720 	for n > 0 {
    721 		j := -1
    722 		for i, bt := range ib.tags {
    723 			if bt.tagId == tagId {
    724 				j = i
    725 				break
    726 			}
    727 		}
    728 
    729 		if j == -1 {
    730 			log.Panic(ErrTagEntryNotFound)
    731 		}
    732 
    733 		ib.tags = append(ib.tags[:j], ib.tags[j+1:]...)
    734 		n--
    735 	}
    736 
    737 	return nil
    738 }
    739 
    740 func (ib *IfdBuilder) DeleteFirst(tagId uint16) (err error) {
    741 	defer func() {
    742 		if state := recover(); state != nil {
    743 			err = log.Wrap(state.(error))
    744 		}
    745 	}()
    746 
    747 	err = ib.DeleteN(tagId, 1)
    748 	log.PanicIf(err)
    749 
    750 	return nil
    751 }
    752 
    753 func (ib *IfdBuilder) DeleteAll(tagId uint16) (n int, err error) {
    754 	defer func() {
    755 		if state := recover(); state != nil {
    756 			err = log.Wrap(state.(error))
    757 		}
    758 	}()
    759 
    760 	for {
    761 		err = ib.DeleteN(tagId, 1)
    762 		if log.Is(err, ErrTagEntryNotFound) == true {
    763 			break
    764 		} else if err != nil {
    765 			log.Panic(err)
    766 		}
    767 
    768 		n++
    769 	}
    770 
    771 	return n, nil
    772 }
    773 
    774 func (ib *IfdBuilder) ReplaceAt(position int, bt *BuilderTag) (err error) {
    775 	defer func() {
    776 		if state := recover(); state != nil {
    777 			err = log.Wrap(state.(error))
    778 		}
    779 	}()
    780 
    781 	if position < 0 {
    782 		log.Panicf("replacement position must be 0 or greater")
    783 	} else if position >= len(ib.tags) {
    784 		log.Panicf("replacement position does not exist")
    785 	}
    786 
    787 	ib.tags[position] = bt
    788 
    789 	return nil
    790 }
    791 
    792 func (ib *IfdBuilder) Replace(tagId uint16, bt *BuilderTag) (err error) {
    793 	defer func() {
    794 		if state := recover(); state != nil {
    795 			err = log.Wrap(state.(error))
    796 		}
    797 	}()
    798 
    799 	position, err := ib.Find(tagId)
    800 	log.PanicIf(err)
    801 
    802 	ib.tags[position] = bt
    803 
    804 	return nil
    805 }
    806 
    807 // Set will add a new entry or update an existing entry.
    808 func (ib *IfdBuilder) Set(bt *BuilderTag) (err error) {
    809 	defer func() {
    810 		if state := recover(); state != nil {
    811 			err = log.Wrap(state.(error))
    812 		}
    813 	}()
    814 
    815 	position, err := ib.Find(bt.tagId)
    816 	if err == nil {
    817 		ib.tags[position] = bt
    818 	} else if log.Is(err, ErrTagEntryNotFound) == true {
    819 		err = ib.add(bt)
    820 		log.PanicIf(err)
    821 	} else {
    822 		log.Panic(err)
    823 	}
    824 
    825 	return nil
    826 }
    827 
    828 func (ib *IfdBuilder) FindN(tagId uint16, maxFound int) (found []int, err error) {
    829 	defer func() {
    830 		if state := recover(); state != nil {
    831 			err = log.Wrap(state.(error))
    832 		}
    833 	}()
    834 
    835 	found = make([]int, 0)
    836 
    837 	for i, bt := range ib.tags {
    838 		if bt.tagId == tagId {
    839 			found = append(found, i)
    840 			if maxFound == 0 || len(found) >= maxFound {
    841 				break
    842 			}
    843 		}
    844 	}
    845 
    846 	return found, nil
    847 }
    848 
    849 func (ib *IfdBuilder) Find(tagId uint16) (position int, err error) {
    850 	defer func() {
    851 		if state := recover(); state != nil {
    852 			err = log.Wrap(state.(error))
    853 		}
    854 	}()
    855 
    856 	found, err := ib.FindN(tagId, 1)
    857 	log.PanicIf(err)
    858 
    859 	if len(found) == 0 {
    860 		log.Panic(ErrTagEntryNotFound)
    861 	}
    862 
    863 	return found[0], nil
    864 }
    865 
    866 func (ib *IfdBuilder) FindTag(tagId uint16) (bt *BuilderTag, err error) {
    867 	defer func() {
    868 		if state := recover(); state != nil {
    869 			err = log.Wrap(state.(error))
    870 		}
    871 	}()
    872 
    873 	found, err := ib.FindN(tagId, 1)
    874 	log.PanicIf(err)
    875 
    876 	if len(found) == 0 {
    877 		log.Panic(ErrTagEntryNotFound)
    878 	}
    879 
    880 	position := found[0]
    881 
    882 	return ib.tags[position], nil
    883 }
    884 
    885 func (ib *IfdBuilder) FindTagWithName(tagName string) (bt *BuilderTag, err error) {
    886 	defer func() {
    887 		if state := recover(); state != nil {
    888 			err = log.Wrap(state.(error))
    889 		}
    890 	}()
    891 
    892 	it, err := ib.tagIndex.GetWithName(ib.IfdIdentity(), tagName)
    893 	log.PanicIf(err)
    894 
    895 	found, err := ib.FindN(it.Id, 1)
    896 	log.PanicIf(err)
    897 
    898 	if len(found) == 0 {
    899 		log.Panic(ErrTagEntryNotFound)
    900 	}
    901 
    902 	position := found[0]
    903 
    904 	return ib.tags[position], nil
    905 }
    906 
    907 func (ib *IfdBuilder) add(bt *BuilderTag) (err error) {
    908 	defer func() {
    909 		if state := recover(); state != nil {
    910 			err = log.Wrap(state.(error))
    911 		}
    912 	}()
    913 
    914 	if bt.ifdPath == "" {
    915 		log.Panicf("BuilderTag ifdPath is not set: %s", bt)
    916 	} else if bt.typeId == 0x0 {
    917 		log.Panicf("BuilderTag type-ID is not set: %s", bt)
    918 	} else if bt.value == nil {
    919 		log.Panicf("BuilderTag value is not set: %s", bt)
    920 	}
    921 
    922 	ib.tags = append(ib.tags, bt)
    923 	return nil
    924 }
    925 
    926 func (ib *IfdBuilder) Add(bt *BuilderTag) (err error) {
    927 	defer func() {
    928 		if state := recover(); state != nil {
    929 			err = log.Wrap(state.(error))
    930 		}
    931 	}()
    932 
    933 	if bt.value.IsIb() == true {
    934 		log.Panicf("child IfdBuilders must be added via AddChildIb() or AddTagsFromExisting(), not Add()")
    935 	}
    936 
    937 	err = ib.add(bt)
    938 	log.PanicIf(err)
    939 
    940 	return nil
    941 }
    942 
    943 // AddChildIb adds a tag that branches to a new IFD.
    944 func (ib *IfdBuilder) AddChildIb(childIb *IfdBuilder) (err error) {
    945 	defer func() {
    946 		if state := recover(); state != nil {
    947 			err = log.Wrap(state.(error))
    948 		}
    949 	}()
    950 
    951 	if childIb.IfdIdentity().TagId() == 0 {
    952 		log.Panicf("IFD can not be used as a child IFD (not associated with a tag-ID): %v", childIb)
    953 	} else if childIb.byteOrder != ib.byteOrder {
    954 		log.Panicf("Child IFD does not have the same byte-order: [%s] != [%s]", childIb.byteOrder, ib.byteOrder)
    955 	}
    956 
    957 	// Since no standard IFDs supports occur`ring more than once, check that a
    958 	// tag of this type has not been previously added. Note that we just search
    959 	// the current IFD and *not every* IFD.
    960 	for _, bt := range childIb.tags {
    961 		if bt.tagId == childIb.IfdIdentity().TagId() {
    962 			log.Panicf("child-IFD already added: %v", childIb.IfdIdentity().UnindexedString())
    963 		}
    964 	}
    965 
    966 	bt := ib.NewBuilderTagFromBuilder(childIb)
    967 	ib.tags = append(ib.tags, bt)
    968 
    969 	return nil
    970 }
    971 
    972 func (ib *IfdBuilder) NewBuilderTagFromBuilder(childIb *IfdBuilder) (bt *BuilderTag) {
    973 	defer func() {
    974 		if state := recover(); state != nil {
    975 			err := log.Wrap(state.(error))
    976 			log.Panic(err)
    977 		}
    978 	}()
    979 
    980 	value := NewIfdBuilderTagValueFromIfdBuilder(childIb)
    981 
    982 	bt = NewChildIfdBuilderTag(
    983 		ib.IfdIdentity().UnindexedString(),
    984 		childIb.IfdIdentity().TagId(),
    985 		value)
    986 
    987 	return bt
    988 }
    989 
    990 // AddTagsFromExisting does a verbatim copy of the entries in `ifd` to this
    991 // builder. It excludes child IFDs. These must be added explicitly via
    992 // `AddChildIb()`.
    993 func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, includeTagIds []uint16, excludeTagIds []uint16) (err error) {
    994 	defer func() {
    995 		if state := recover(); state != nil {
    996 			err = log.Wrap(state.(error))
    997 		}
    998 	}()
    999 
   1000 	thumbnailData, err := ifd.Thumbnail()
   1001 	if err == nil {
   1002 		err = ib.SetThumbnail(thumbnailData)
   1003 		log.PanicIf(err)
   1004 	} else if log.Is(err, ErrNoThumbnail) == false {
   1005 		log.Panic(err)
   1006 	}
   1007 
   1008 	for i, ite := range ifd.Entries() {
   1009 		if ite.IsThumbnailOffset() == true || ite.IsThumbnailSize() {
   1010 			// These will be added on-the-fly when we encode.
   1011 			continue
   1012 		}
   1013 
   1014 		if excludeTagIds != nil && len(excludeTagIds) > 0 {
   1015 			found := false
   1016 			for _, excludedTagId := range excludeTagIds {
   1017 				if excludedTagId == ite.TagId() {
   1018 					found = true
   1019 				}
   1020 			}
   1021 
   1022 			if found == true {
   1023 				continue
   1024 			}
   1025 		}
   1026 
   1027 		if includeTagIds != nil && len(includeTagIds) > 0 {
   1028 			// Whether or not there was a list of excludes, if there is a list
   1029 			// of includes than the current tag has to be in it.
   1030 
   1031 			found := false
   1032 			for _, includedTagId := range includeTagIds {
   1033 				if includedTagId == ite.TagId() {
   1034 					found = true
   1035 					break
   1036 				}
   1037 			}
   1038 
   1039 			if found == false {
   1040 				continue
   1041 			}
   1042 		}
   1043 
   1044 		var bt *BuilderTag
   1045 
   1046 		if ite.ChildIfdPath() != "" {
   1047 			// If we want to add an IFD tag, we'll have to build it first and
   1048 			// *then* add it via a different method.
   1049 
   1050 			// Figure out which of the child-IFDs that are associated with
   1051 			// this IFD represents this specific child IFD.
   1052 
   1053 			var childIfd *Ifd
   1054 			for _, thisChildIfd := range ifd.Children() {
   1055 				if thisChildIfd.ParentTagIndex() != i {
   1056 					continue
   1057 				} else if thisChildIfd.ifdIdentity.TagId() != 0xffff && thisChildIfd.ifdIdentity.TagId() != ite.TagId() {
   1058 					log.Panicf("child-IFD tag is not correct: TAG-POSITION=(%d) ITE=%s CHILD-IFD=%s", thisChildIfd.ParentTagIndex(), ite, thisChildIfd)
   1059 				}
   1060 
   1061 				childIfd = thisChildIfd
   1062 				break
   1063 			}
   1064 
   1065 			if childIfd == nil {
   1066 				childTagIds := make([]string, len(ifd.Children()))
   1067 				for j, childIfd := range ifd.Children() {
   1068 					childTagIds[j] = fmt.Sprintf("0x%04x (parent tag-position %d)", childIfd.ifdIdentity.TagId(), childIfd.ParentTagIndex())
   1069 				}
   1070 
   1071 				log.Panicf("could not find child IFD for child ITE: IFD-PATH=[%s] TAG-ID=(0x%04x) CURRENT-TAG-POSITION=(%d) CHILDREN=%v", ite.IfdPath(), ite.TagId(), i, childTagIds)
   1072 			}
   1073 
   1074 			childIb := NewIfdBuilderFromExistingChain(childIfd)
   1075 			bt = ib.NewBuilderTagFromBuilder(childIb)
   1076 		} else {
   1077 			// Non-IFD tag.
   1078 
   1079 			rawBytes, err := ite.GetRawBytes()
   1080 			log.PanicIf(err)
   1081 
   1082 			value := NewIfdBuilderTagValueFromBytes(rawBytes)
   1083 
   1084 			bt = NewBuilderTag(
   1085 				ifd.ifdIdentity.UnindexedString(),
   1086 				ite.TagId(),
   1087 				ite.TagType(),
   1088 				value,
   1089 				ib.byteOrder)
   1090 		}
   1091 
   1092 		err := ib.add(bt)
   1093 		log.PanicIf(err)
   1094 	}
   1095 
   1096 	return nil
   1097 }
   1098 
   1099 // AddStandard quickly and easily composes and adds the tag using the
   1100 // information already known about a tag. Only works with standard tags.
   1101 func (ib *IfdBuilder) AddStandard(tagId uint16, value interface{}) (err error) {
   1102 	defer func() {
   1103 		if state := recover(); state != nil {
   1104 			err = log.Wrap(state.(error))
   1105 		}
   1106 	}()
   1107 
   1108 	it, err := ib.tagIndex.Get(ib.IfdIdentity(), tagId)
   1109 	log.PanicIf(err)
   1110 
   1111 	bt := NewStandardBuilderTag(ib.IfdIdentity().UnindexedString(), it, ib.byteOrder, value)
   1112 
   1113 	err = ib.add(bt)
   1114 	log.PanicIf(err)
   1115 
   1116 	return nil
   1117 }
   1118 
   1119 // AddStandardWithName quickly and easily composes and adds the tag using the
   1120 // information already known about a tag (using the name). Only works with
   1121 // standard tags.
   1122 func (ib *IfdBuilder) AddStandardWithName(tagName string, value interface{}) (err error) {
   1123 	defer func() {
   1124 		if state := recover(); state != nil {
   1125 			err = log.Wrap(state.(error))
   1126 		}
   1127 	}()
   1128 
   1129 	it, err := ib.tagIndex.GetWithName(ib.IfdIdentity(), tagName)
   1130 	log.PanicIf(err)
   1131 
   1132 	bt := NewStandardBuilderTag(ib.IfdIdentity().UnindexedString(), it, ib.byteOrder, value)
   1133 
   1134 	err = ib.add(bt)
   1135 	log.PanicIf(err)
   1136 
   1137 	return nil
   1138 }
   1139 
   1140 // SetStandard quickly and easily composes and adds or replaces the tag using
   1141 // the information already known about a tag. Only works with standard tags.
   1142 func (ib *IfdBuilder) SetStandard(tagId uint16, value interface{}) (err error) {
   1143 	defer func() {
   1144 		if state := recover(); state != nil {
   1145 			err = log.Wrap(state.(error))
   1146 		}
   1147 	}()
   1148 
   1149 	// TODO(dustin): !! Add test for this function.
   1150 
   1151 	it, err := ib.tagIndex.Get(ib.IfdIdentity(), tagId)
   1152 	log.PanicIf(err)
   1153 
   1154 	bt := NewStandardBuilderTag(ib.IfdIdentity().UnindexedString(), it, ib.byteOrder, value)
   1155 
   1156 	i, err := ib.Find(tagId)
   1157 	if err != nil {
   1158 		if log.Is(err, ErrTagEntryNotFound) == false {
   1159 			log.Panic(err)
   1160 		}
   1161 
   1162 		ib.tags = append(ib.tags, bt)
   1163 	} else {
   1164 		ib.tags[i] = bt
   1165 	}
   1166 
   1167 	return nil
   1168 }
   1169 
   1170 // SetStandardWithName quickly and easily composes and adds or replaces the
   1171 // tag using the information already known about a tag (using the name). Only
   1172 // works with standard tags.
   1173 func (ib *IfdBuilder) SetStandardWithName(tagName string, value interface{}) (err error) {
   1174 	defer func() {
   1175 		if state := recover(); state != nil {
   1176 			err = log.Wrap(state.(error))
   1177 		}
   1178 	}()
   1179 
   1180 	// TODO(dustin): !! Add test for this function.
   1181 
   1182 	it, err := ib.tagIndex.GetWithName(ib.IfdIdentity(), tagName)
   1183 	log.PanicIf(err)
   1184 
   1185 	bt := NewStandardBuilderTag(ib.IfdIdentity().UnindexedString(), it, ib.byteOrder, value)
   1186 
   1187 	i, err := ib.Find(bt.tagId)
   1188 	if err != nil {
   1189 		if log.Is(err, ErrTagEntryNotFound) == false {
   1190 			log.Panic(err)
   1191 		}
   1192 
   1193 		ib.tags = append(ib.tags, bt)
   1194 	} else {
   1195 		ib.tags[i] = bt
   1196 	}
   1197 
   1198 	return nil
   1199 }