...

Source file src/github.com/dsoprea/go-exif/v3/ifd_builder.go

Documentation: github.com/dsoprea/go-exif/v3

     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  }
  1200  

View as plain text