diff --git a/ifd_builder.go b/ifd_builder.go index 4dc25eb..c3cceae 100644 --- a/ifd_builder.go +++ b/ifd_builder.go @@ -6,369 +6,366 @@ package exif // (*IfdBuilder).SetThumbnail() method instead. import ( - "errors" - "fmt" - "strings" + "errors" + "fmt" + "strings" - "encoding/binary" + "encoding/binary" - "github.com/dsoprea/go-logging" + "github.com/dsoprea/go-logging" ) var ( - ifdBuilderLogger = log.NewLogger("exif.ifd_builder") + ifdBuilderLogger = log.NewLogger("exif.ifd_builder") ) var ( - ErrTagEntryNotFound = errors.New("tag entry not found") + ErrTagEntryNotFound = errors.New("tag entry not found") ) - type IfdBuilderTagValue struct { - valueBytes []byte - ib *IfdBuilder + valueBytes []byte + ib *IfdBuilder } func (ibtv IfdBuilderTagValue) String() string { - if ibtv.IsBytes() == true { - var valuePhrase string - if len(ibtv.valueBytes) <= 8 { - valuePhrase = fmt.Sprintf("%v", ibtv.valueBytes) - } else { - valuePhrase = fmt.Sprintf("%v...", ibtv.valueBytes[:8]) - } + if ibtv.IsBytes() == true { + var valuePhrase string + if len(ibtv.valueBytes) <= 8 { + valuePhrase = fmt.Sprintf("%v", ibtv.valueBytes) + } else { + valuePhrase = fmt.Sprintf("%v...", ibtv.valueBytes[:8]) + } - return fmt.Sprintf("IfdBuilderTagValue", valuePhrase, len(ibtv.valueBytes)) - } else if ibtv.IsIb() == true { - return fmt.Sprintf("IfdBuilderTagValue", ibtv.ib) - } else { - log.Panicf("IBTV state undefined") - return "" - } + return fmt.Sprintf("IfdBuilderTagValue", valuePhrase, len(ibtv.valueBytes)) + } else if ibtv.IsIb() == true { + return fmt.Sprintf("IfdBuilderTagValue", ibtv.ib) + } else { + log.Panicf("IBTV state undefined") + return "" + } } func NewIfdBuilderTagValueFromBytes(valueBytes []byte) *IfdBuilderTagValue { - return &IfdBuilderTagValue{ - valueBytes: valueBytes, - } + return &IfdBuilderTagValue{ + valueBytes: valueBytes, + } } func NewIfdBuilderTagValueFromIfdBuilder(ib *IfdBuilder) *IfdBuilderTagValue { - return &IfdBuilderTagValue{ - ib: ib, - } + return &IfdBuilderTagValue{ + ib: ib, + } } func (ibtv IfdBuilderTagValue) IsBytes() bool { - return ibtv.valueBytes != nil + return ibtv.valueBytes != nil } func (ibtv IfdBuilderTagValue) Bytes() []byte { - if ibtv.IsBytes() == false { - log.Panicf("this tag is not a byte-slice value") - } + if ibtv.IsBytes() == false { + log.Panicf("this tag is not a byte-slice value") + } - return ibtv.valueBytes + return ibtv.valueBytes } func (ibtv IfdBuilderTagValue) IsIb() bool { - return ibtv.ib != nil + return ibtv.ib != nil } func (ibtv IfdBuilderTagValue) Ib() *IfdBuilder { - if ibtv.IsIb() == false { - log.Panicf("this tag is not an IFD-builder value") - } + if ibtv.IsIb() == false { + log.Panicf("this tag is not an IFD-builder value") + } - return ibtv.ib + return ibtv.ib } +type BuilderTag struct { + // ii is the IfdIdentity of the IFD that hosts this tag. + ii IfdIdentity -type builderTag struct { - // ii is the IfdIdentity of the IFD that hosts this tag. - ii IfdIdentity + tagId uint16 + typeId uint16 - tagId uint16 - typeId uint16 - - // value is either a value that can be encoded, an IfdBuilder instance (for - // child IFDs), or an IfdTagEntry instance representing an existing, - // previously-stored tag. - value *IfdBuilderTagValue + // value is either a value that can be encoded, an IfdBuilder instance (for + // child IFDs), or an IfdTagEntry instance representing an existing, + // previously-stored tag. + value *IfdBuilderTagValue } -func NewBuilderTag(ii IfdIdentity, tagId uint16, typeId uint16, value *IfdBuilderTagValue) *builderTag { - return &builderTag{ - ii: ii, - tagId: tagId, - typeId: typeId, - value: value, - } +func NewBuilderTag(ii IfdIdentity, tagId uint16, typeId uint16, value *IfdBuilderTagValue) *BuilderTag { + return &BuilderTag{ + ii: ii, + tagId: tagId, + typeId: typeId, + value: value, + } } -func NewStandardBuilderTag(ii IfdIdentity, tagId uint16, value *IfdBuilderTagValue) *builderTag { - ti := NewTagIndex() +func NewStandardBuilderTag(ii IfdIdentity, tagId uint16, value *IfdBuilderTagValue) *BuilderTag { + ti := NewTagIndex() - it, err := ti.Get(ii, tagId) - log.PanicIf(err) + it, err := ti.Get(ii, tagId) + log.PanicIf(err) - return &builderTag{ - ii: ii, - tagId: tagId, - typeId: it.Type, - value: value, - } + return &BuilderTag{ + ii: ii, + tagId: tagId, + typeId: it.Type, + value: value, + } } -func NewChildIfdBuilderTag(ii IfdIdentity, tagId uint16, value *IfdBuilderTagValue) *builderTag { - return &builderTag{ - ii: ii, - tagId: tagId, - typeId: TypeLong, - value: value, - } +func NewChildIfdBuilderTag(ii IfdIdentity, tagId uint16, value *IfdBuilderTagValue) *BuilderTag { + return &BuilderTag{ + ii: ii, + tagId: tagId, + typeId: TypeLong, + value: value, + } } -func (bt *builderTag) Value() (value *IfdBuilderTagValue) { - return bt.value +func (bt *BuilderTag) Value() (value *IfdBuilderTagValue) { + return bt.value } -func (bt *builderTag) String() string { - return fmt.Sprintf("BuilderTag", bt.ii, bt.tagId, TypeNames[bt.typeId], bt.value) +func (bt *BuilderTag) String() string { + return fmt.Sprintf("BuilderTag", bt.ii, bt.tagId, TypeNames[bt.typeId], bt.value) } -func (bt *builderTag) SetValue(byteOrder binary.ByteOrder, value interface{}) (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() +func (bt *BuilderTag) SetValue(byteOrder binary.ByteOrder, value interface{}) (err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() -// TODO(dustin): !! Add test. + // TODO(dustin): !! Add test. - tt := NewTagType(bt.typeId, byteOrder) - ve := NewValueEncoder(byteOrder) + tt := NewTagType(bt.typeId, byteOrder) + ve := NewValueEncoder(byteOrder) - var ed EncodedData - if bt.typeId == TypeUndefined { - var err error + var ed EncodedData + if bt.typeId == TypeUndefined { + var err error - ed, err = EncodeUndefined(bt.ii, bt.tagId, value) - log.PanicIf(err) - } else { - var err error + ed, err = EncodeUndefined(bt.ii, bt.tagId, value) + log.PanicIf(err) + } else { + var err error - ed, err = ve.EncodeWithType(tt, value) - log.PanicIf(err) - } + ed, err = ve.EncodeWithType(tt, value) + log.PanicIf(err) + } - bt.value = NewIfdBuilderTagValueFromBytes(ed.Encoded) + bt.value = NewIfdBuilderTagValueFromBytes(ed.Encoded) - return nil + return nil } -// NewStandardBuilderTagFromConfig constructs a `builderTag` instance. The type +// NewStandardBuilderTagFromConfig constructs a `BuilderTag` instance. The type // is looked up. `ii` is the type of IFD that owns this tag. -func NewStandardBuilderTagFromConfig(ii IfdIdentity, tagId uint16, byteOrder binary.ByteOrder, value interface{}) *builderTag { - ti := NewTagIndex() +func NewStandardBuilderTagFromConfig(ii IfdIdentity, tagId uint16, byteOrder binary.ByteOrder, value interface{}) *BuilderTag { + ti := NewTagIndex() - it, err := ti.Get(ii, tagId) - log.PanicIf(err) + it, err := ti.Get(ii, tagId) + log.PanicIf(err) - typeId := it.Type - tt := NewTagType(typeId, byteOrder) + typeId := it.Type + tt := NewTagType(typeId, byteOrder) - ve := NewValueEncoder(byteOrder) + ve := NewValueEncoder(byteOrder) - var ed EncodedData - if it.Type == TypeUndefined { - var err error + var ed EncodedData + if it.Type == TypeUndefined { + var err error - ed, err = EncodeUndefined(ii, tagId, value) - log.PanicIf(err) - } else { - var err error + ed, err = EncodeUndefined(ii, tagId, value) + log.PanicIf(err) + } else { + var err error - ed, err = ve.EncodeWithType(tt, value) - log.PanicIf(err) - } + ed, err = ve.EncodeWithType(tt, value) + log.PanicIf(err) + } - tagValue := NewIfdBuilderTagValueFromBytes(ed.Encoded) + tagValue := NewIfdBuilderTagValueFromBytes(ed.Encoded) - return NewBuilderTag( - ii, - tagId, - typeId, - tagValue) + return NewBuilderTag( + ii, + tagId, + typeId, + tagValue) } -// NewStandardBuilderTagFromConfig constructs a `builderTag` instance. The type is +// NewStandardBuilderTagFromConfig constructs a `BuilderTag` instance. The type is // explicitly provided. `ii` is the type of IFD that owns this tag. -func NewBuilderTagFromConfig(ii IfdIdentity, tagId uint16, typeId uint16, byteOrder binary.ByteOrder, value interface{}) *builderTag { - tt := NewTagType(typeId, byteOrder) +func NewBuilderTagFromConfig(ii IfdIdentity, tagId uint16, typeId uint16, byteOrder binary.ByteOrder, value interface{}) *BuilderTag { + tt := NewTagType(typeId, byteOrder) - ve := NewValueEncoder(byteOrder) + ve := NewValueEncoder(byteOrder) - var ed EncodedData - if typeId == TypeUndefined { - var err error + var ed EncodedData + if typeId == TypeUndefined { + var err error - ed, err = EncodeUndefined(ii, tagId, value) - log.PanicIf(err) - } else { - var err error + ed, err = EncodeUndefined(ii, tagId, value) + log.PanicIf(err) + } else { + var err error - ed, err = ve.EncodeWithType(tt, value) - log.PanicIf(err) - } + ed, err = ve.EncodeWithType(tt, value) + log.PanicIf(err) + } - tagValue := NewIfdBuilderTagValueFromBytes(ed.Encoded) + tagValue := NewIfdBuilderTagValueFromBytes(ed.Encoded) - return NewBuilderTag( - ii, - tagId, - typeId, - tagValue) + return NewBuilderTag( + ii, + tagId, + typeId, + tagValue) } // NewStandardBuilderTagFromConfigWithName allows us to easily generate solid, consistent tags // for testing with. `ii` is the type of IFD that owns this tag. This can not be // an IFD (IFDs are not associated with standardized, official names). -func NewStandardBuilderTagFromConfigWithName(ii IfdIdentity, tagName string, byteOrder binary.ByteOrder, value interface{}) *builderTag { - ti := NewTagIndex() +func NewStandardBuilderTagFromConfigWithName(ii IfdIdentity, tagName string, byteOrder binary.ByteOrder, value interface{}) *BuilderTag { + ti := NewTagIndex() - it, err := ti.GetWithName(ii, tagName) - log.PanicIf(err) + it, err := ti.GetWithName(ii, tagName) + log.PanicIf(err) - tt := NewTagType(it.Type, byteOrder) + tt := NewTagType(it.Type, byteOrder) - ve := NewValueEncoder(byteOrder) + ve := NewValueEncoder(byteOrder) - var ed EncodedData - if it.Type == TypeUndefined { - var err error + var ed EncodedData + if it.Type == TypeUndefined { + var err error - ed, err = EncodeUndefined(ii, it.Id, value) - log.PanicIf(err) - } else { - var err error + ed, err = EncodeUndefined(ii, it.Id, value) + log.PanicIf(err) + } else { + var err error - ed, err = ve.EncodeWithType(tt, value) - log.PanicIf(err) - } + ed, err = ve.EncodeWithType(tt, value) + log.PanicIf(err) + } - tagValue := NewIfdBuilderTagValueFromBytes(ed.Encoded) + tagValue := NewIfdBuilderTagValueFromBytes(ed.Encoded) - return NewBuilderTag( - ii, - it.Id, - it.Type, - tagValue) + return NewBuilderTag( + ii, + it.Id, + it.Type, + tagValue) } - type IfdBuilder struct { - // ifd is the IfdIdentity instance of the IFD that owns the current tag. - ii IfdIdentity + // ifd is the IfdIdentity instance of the IFD that owns the current tag. + ii IfdIdentity - // ifdTagId will be non-zero if we're a child IFD. - ifdTagId uint16 + // ifdTagId will be non-zero if we're a child IFD. + ifdTagId uint16 - byteOrder binary.ByteOrder + byteOrder binary.ByteOrder - // Includes both normal tags and IFD tags (which point to child IFDs). - tags []*builderTag + // Includes both normal tags and IFD tags (which point to child IFDs). + tags []*BuilderTag - // existingOffset will be the offset that this IFD is currently found at if - // it represents an IFD that has previously been stored (or 0 if not). - existingOffset uint32 + // existingOffset will be the offset that this IFD is currently found at if + // it represents an IFD that has previously been stored (or 0 if not). + existingOffset uint32 - // nextIb represents the next link if we're chaining to another. - nextIb *IfdBuilder + // nextIb represents the next link if we're chaining to another. + nextIb *IfdBuilder - // thumbnailData is populated with thumbnail data if there was thumbnail - // data. Otherwise, it's nil. - thumbnailData []byte + // thumbnailData is populated with thumbnail data if there was thumbnail + // data. Otherwise, it's nil. + thumbnailData []byte } func NewIfdBuilder(ii IfdIdentity, byteOrder binary.ByteOrder) (ib *IfdBuilder) { - ifdTagId, _ := IfdTagIdWithIdentity(ii) + ifdTagId, _ := IfdTagIdWithIdentity(ii) - ib = &IfdBuilder{ - // ii describes the current IFD and its parent. - ii: ii, + ib = &IfdBuilder{ + // ii describes the current IFD and its parent. + ii: ii, - // ifdTagId is empty unless it's a child-IFD. - ifdTagId: ifdTagId, + // ifdTagId is empty unless it's a child-IFD. + ifdTagId: ifdTagId, - byteOrder: byteOrder, - tags: make([]*builderTag, 0), - } + byteOrder: byteOrder, + tags: make([]*BuilderTag, 0), + } - return ib + return ib } // NewIfdBuilderWithExistingIfd creates a new IB using the same header type // information as the given IFD. func NewIfdBuilderWithExistingIfd(ifd *Ifd) (ib *IfdBuilder) { - ii := ifd.Identity() + ii := ifd.Identity() - var ifdTagId uint16 + var ifdTagId uint16 - // There is no tag-ID for the root IFD. It will never be a child IFD. - if ii != RootIi { - ifdTagId = IfdTagIdWithIdentityOrFail(ii) - } + // There is no tag-ID for the root IFD. It will never be a child IFD. + if ii != RootIi { + ifdTagId = IfdTagIdWithIdentityOrFail(ii) + } - ib = &IfdBuilder{ - ii: ii, - ifdTagId: ifdTagId, - byteOrder: ifd.ByteOrder, - existingOffset: ifd.Offset, - } + ib = &IfdBuilder{ + ii: ii, + ifdTagId: ifdTagId, + byteOrder: ifd.ByteOrder, + existingOffset: ifd.Offset, + } - return ib + return ib } // NewIfdBuilderFromExistingChain creates a chain of IB instances from an // IFD chain generated from real data. func NewIfdBuilderFromExistingChain(rootIfd *Ifd, itevr *IfdTagEntryValueResolver) (firstIb *IfdBuilder) { -// TODO(dustin): !! When we actually write the code to flatten the IB to bytes, make sure to skip the tags that have a nil value (which will happen when we add-from-exsting without a resolver instance). + // TODO(dustin): !! When we actually write the code to flatten the IB to bytes, make sure to skip the tags that have a nil value (which will happen when we add-from-exsting without a resolver instance). - var lastIb *IfdBuilder - i := 0 - for thisExistingIfd := rootIfd; thisExistingIfd != nil; thisExistingIfd = thisExistingIfd.NextIfd { - ii := thisExistingIfd.Identity() + var lastIb *IfdBuilder + i := 0 + for thisExistingIfd := rootIfd; thisExistingIfd != nil; thisExistingIfd = thisExistingIfd.NextIfd { + ii := thisExistingIfd.Identity() - newIb := NewIfdBuilder(ii, thisExistingIfd.ByteOrder) - if firstIb == nil { - firstIb = newIb - } else { - lastIb.SetNextIb(newIb) - } + newIb := NewIfdBuilder(ii, thisExistingIfd.ByteOrder) + if firstIb == nil { + firstIb = newIb + } else { + lastIb.SetNextIb(newIb) + } - err := newIb.AddTagsFromExisting(thisExistingIfd, itevr, nil, nil) - log.PanicIf(err) + err := newIb.AddTagsFromExisting(thisExistingIfd, itevr, nil, nil) + log.PanicIf(err) - lastIb = newIb - i++ - } + lastIb = newIb + i++ + } - return firstIb + return firstIb } func (ib *IfdBuilder) String() string { - nextIfdPhrase := "" - if ib.nextIb != nil { -// TODO(dustin): We were setting this to ii.String(), but we were getting hex-data when printing this after building from an existing chain. - nextIfdPhrase = ib.nextIb.ii.IfdName - } + nextIfdPhrase := "" + if ib.nextIb != nil { + // TODO(dustin): We were setting this to ii.String(), but we were getting hex-data when printing this after building from an existing chain. + nextIfdPhrase = ib.nextIb.ii.IfdName + } - return fmt.Sprintf("IfdBuilder", ib.ii.ParentIfdName, ib.ii.IfdName, ib.ifdTagId, len(ib.tags), ib.existingOffset, nextIfdPhrase) + return fmt.Sprintf("IfdBuilder", ib.ii.ParentIfdName, ib.ii.IfdName, ib.ifdTagId, len(ib.tags), ib.existingOffset, nextIfdPhrase) } -func (ib *IfdBuilder) Tags() (tags []*builderTag) { - return ib.tags +func (ib *IfdBuilder) Tags() (tags []*BuilderTag) { + return ib.tags } // SetThumbnail sets thumbnail data. @@ -383,690 +380,688 @@ func (ib *IfdBuilder) Tags() (tags []*builderTag) { // - We set the offset to (0) now but will allocate the data and properly assign // the offset when the IB is encoded (later). func (ib *IfdBuilder) SetThumbnail(data []byte) (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if ib.ii != RootIi { - log.Panicf("thumbnails can only go into a root Ifd (and only the second one)") - } + if ib.ii != RootIi { + log.Panicf("thumbnails can only go into a root Ifd (and only the second one)") + } -// TODO(dustin): !! Add a test for this. + // TODO(dustin): !! Add a test for this. - if data == nil || len(data) == 0 { + if data == nil || len(data) == 0 { -// TODO(dustin): !! Debugging. -// fmt.Printf("Thumbnail empty.\n") + // TODO(dustin): !! Debugging. + // fmt.Printf("Thumbnail empty.\n") - log.Panic("thumbnail is empty") - } + log.Panic("thumbnail is empty") + } - ib.thumbnailData = data + ib.thumbnailData = data -// fmt.Printf("SETTING THUMBNAIL.\n") + // fmt.Printf("SETTING THUMBNAIL.\n") - ibtvfb := NewIfdBuilderTagValueFromBytes(ib.thumbnailData) - offsetBt := NewBuilderTag(ib.ii, ThumbnailOffsetTagId, TypeLong, ibtvfb) - // offsetBt := NewStandardBuilderTagFromConfig(ib.ii, ThumbnailOffsetTagId, ib.byteOrder, []uint32 { 0 }) + ibtvfb := NewIfdBuilderTagValueFromBytes(ib.thumbnailData) + offsetBt := NewBuilderTag(ib.ii, ThumbnailOffsetTagId, TypeLong, ibtvfb) + // offsetBt := NewStandardBuilderTagFromConfig(ib.ii, ThumbnailOffsetTagId, ib.byteOrder, []uint32 { 0 }) - err = ib.Set(offsetBt) - log.PanicIf(err) + err = ib.Set(offsetBt) + log.PanicIf(err) - sizeBt := NewStandardBuilderTagFromConfig(ib.ii, ThumbnailSizeTagId, ib.byteOrder, []uint32 { uint32(len(ib.thumbnailData)) }) + sizeBt := NewStandardBuilderTagFromConfig(ib.ii, ThumbnailSizeTagId, ib.byteOrder, []uint32{uint32(len(ib.thumbnailData))}) - err = ib.Set(sizeBt) - log.PanicIf(err) + err = ib.Set(sizeBt) + log.PanicIf(err) + // TODO(dustin): !! Debugging. + // fmt.Printf("Set thumbnail into IB.\n") -// TODO(dustin): !! Debugging. -// fmt.Printf("Set thumbnail into IB.\n") - - - return nil + return nil } func (ib *IfdBuilder) Thumbnail() []byte { - return ib.thumbnailData + return ib.thumbnailData } func (ib *IfdBuilder) printTagTree(levels int) { - indent := strings.Repeat(" ", levels * 2) + indent := strings.Repeat(" ", levels*2) - ti := NewTagIndex() - i := 0 - for currentIb := ib; currentIb != nil; currentIb = currentIb.nextIb { - prefix := " " - if i > 0 { - prefix = ">" - } + ti := NewTagIndex() + i := 0 + for currentIb := ib; currentIb != nil; currentIb = currentIb.nextIb { + prefix := " " + if i > 0 { + prefix = ">" + } - if levels == 0 { - fmt.Printf("%s%sIFD: %s INDEX=(%d)\n", indent, prefix, currentIb, i) - } else { - fmt.Printf("%s%sChild IFD: %s\n", indent, prefix, currentIb) - } + if levels == 0 { + fmt.Printf("%s%sIFD: %s INDEX=(%d)\n", indent, prefix, currentIb, i) + } else { + fmt.Printf("%s%sChild IFD: %s\n", indent, prefix, currentIb) + } - if len(currentIb.tags) > 0 { - fmt.Printf("\n") + if len(currentIb.tags) > 0 { + fmt.Printf("\n") - for i, tag := range currentIb.tags { - _, isChildIb := IfdTagNameWithId(currentIb.ii.IfdName, tag.tagId) + for i, tag := range currentIb.tags { + _, isChildIb := IfdTagNameWithId(currentIb.ii.IfdName, tag.tagId) - tagName := "" + tagName := "" - // If a normal tag (not a child IFD) get the name. - if isChildIb == true { - tagName = "" - } else { - it, err := ti.Get(tag.ii, tag.tagId) - if log.Is(err, ErrTagNotFound) == true { - tagName = "" - } else if err != nil { - log.Panic(err) - } else { - tagName = it.Name - } - } + // If a normal tag (not a child IFD) get the name. + if isChildIb == true { + tagName = "" + } else { + it, err := ti.Get(tag.ii, tag.tagId) + if log.Is(err, ErrTagNotFound) == true { + tagName = "" + } else if err != nil { + log.Panic(err) + } else { + tagName = it.Name + } + } - fmt.Printf("%s (%d): [%s] %s\n", indent, i, tagName, tag) + fmt.Printf("%s (%d): [%s] %s\n", indent, i, tagName, tag) - if isChildIb == true { - if tag.value.IsIb() == false { - log.Panicf("tag-ID (0x%04x) is an IFD but the tag value is not an IB instance: %v", tag.tagId, tag) - } + if isChildIb == true { + if tag.value.IsIb() == false { + log.Panicf("tag-ID (0x%04x) is an IFD but the tag value is not an IB instance: %v", tag.tagId, tag) + } - fmt.Printf("\n") + fmt.Printf("\n") - childIb := tag.value.Ib() - childIb.printTagTree(levels + 1) - } - } + childIb := tag.value.Ib() + childIb.printTagTree(levels + 1) + } + } - fmt.Printf("\n") - } + fmt.Printf("\n") + } - i++ - } + i++ + } } func (ib *IfdBuilder) PrintTagTree() { - ib.printTagTree(0) + ib.printTagTree(0) } func (ib *IfdBuilder) printIfdTree(levels int) { - indent := strings.Repeat(" ", levels * 2) + indent := strings.Repeat(" ", levels*2) - i := 0 - for currentIb := ib; currentIb != nil; currentIb = currentIb.nextIb { - prefix := " " - if i > 0 { - prefix = ">" - } + i := 0 + for currentIb := ib; currentIb != nil; currentIb = currentIb.nextIb { + prefix := " " + if i > 0 { + prefix = ">" + } - fmt.Printf("%s%s%s\n", indent, prefix,currentIb) + fmt.Printf("%s%s%s\n", indent, prefix, currentIb) - if len(currentIb.tags) > 0 { - for _, tag := range currentIb.tags { - _, isChildIb := IfdTagNameWithId(currentIb.ii.IfdName, tag.tagId) + if len(currentIb.tags) > 0 { + for _, tag := range currentIb.tags { + _, isChildIb := IfdTagNameWithId(currentIb.ii.IfdName, tag.tagId) - if isChildIb == true { - if tag.value.IsIb() == false { - log.Panicf("tag-ID (0x%04x) is an IFD but the tag value is not an IB instance: %v", tag.tagId, tag) - } + if isChildIb == true { + if tag.value.IsIb() == false { + log.Panicf("tag-ID (0x%04x) is an IFD but the tag value is not an IB instance: %v", tag.tagId, tag) + } - childIb := tag.value.Ib() - childIb.printIfdTree(levels + 1) - } - } - } + childIb := tag.value.Ib() + childIb.printIfdTree(levels + 1) + } + } + } - i++ - } + i++ + } } func (ib *IfdBuilder) PrintIfdTree() { - ib.printIfdTree(0) + ib.printIfdTree(0) } func (ib *IfdBuilder) dumpToStrings(thisIb *IfdBuilder, prefix string, lines []string) (linesOutput []string) { - if lines == nil { - linesOutput = make([]string, 0) - } else { - linesOutput = lines - } + if lines == nil { + linesOutput = make([]string, 0) + } else { + linesOutput = lines + } - for i, tag := range thisIb.tags { - childIfdName := "" - if tag.value.IsIb() == true { - childIfdName = tag.value.Ib().ii.IfdName - } + for i, tag := range thisIb.tags { + childIfdName := "" + if tag.value.IsIb() == true { + childIfdName = tag.value.Ib().ii.IfdName + } - line := fmt.Sprintf(" IFD-TAG-ID=(0x%04x) CHILD-IFD=[%s] INDEX=(%d) TAG=[0x%04x]", prefix, thisIb.ii.IfdName, thisIb.ifdTagId, childIfdName, i, tag.tagId) - linesOutput = append(linesOutput, line) + line := fmt.Sprintf(" IFD-TAG-ID=(0x%04x) CHILD-IFD=[%s] INDEX=(%d) TAG=[0x%04x]", prefix, thisIb.ii.IfdName, thisIb.ifdTagId, childIfdName, i, tag.tagId) + linesOutput = append(linesOutput, line) - if tag.value.IsIb() == true { - childPrefix := "" - if prefix == "" { - childPrefix = fmt.Sprintf("%s", thisIb.ii.IfdName) - } else { - childPrefix = fmt.Sprintf("%s->%s", prefix, thisIb.ii.IfdName) - } + if tag.value.IsIb() == true { + childPrefix := "" + if prefix == "" { + childPrefix = fmt.Sprintf("%s", thisIb.ii.IfdName) + } else { + childPrefix = fmt.Sprintf("%s->%s", prefix, thisIb.ii.IfdName) + } - linesOutput = thisIb.dumpToStrings(tag.value.Ib(), childPrefix, linesOutput) - } - } + linesOutput = thisIb.dumpToStrings(tag.value.Ib(), childPrefix, linesOutput) + } + } - return linesOutput + return linesOutput } func (ib *IfdBuilder) DumpToStrings() (lines []string) { - return ib.dumpToStrings(ib, "", lines) + return ib.dumpToStrings(ib, "", lines) } func (ib *IfdBuilder) SetNextIb(nextIb *IfdBuilder) (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - ib.nextIb = nextIb + ib.nextIb = nextIb - return nil + return nil } func (ib *IfdBuilder) DeleteN(tagId uint16, n int) (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if n < 1 { - log.Panicf("N must be at least 1: (%d)", n) - } + if n < 1 { + log.Panicf("N must be at least 1: (%d)", n) + } - for ; n > 0; { - j := -1 - for i, bt := range ib.tags { - if bt.tagId == tagId { - j = i - break - } - } + for n > 0 { + j := -1 + for i, bt := range ib.tags { + if bt.tagId == tagId { + j = i + break + } + } - if j == -1 { - log.Panic(ErrTagEntryNotFound) - } + if j == -1 { + log.Panic(ErrTagEntryNotFound) + } - ib.tags = append(ib.tags[:j], ib.tags[j + 1:]...) - n-- - } + ib.tags = append(ib.tags[:j], ib.tags[j+1:]...) + n-- + } - return nil + return nil } func (ib *IfdBuilder) DeleteFirst(tagId uint16) (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - err = ib.DeleteN(tagId, 1) - log.PanicIf(err) + err = ib.DeleteN(tagId, 1) + log.PanicIf(err) - return nil + return nil } func (ib *IfdBuilder) DeleteAll(tagId uint16) (n int, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - for { - err = ib.DeleteN(tagId, 1) - if log.Is(err, ErrTagEntryNotFound) == true { - break - } else if err != nil { - log.Panic(err) - } + for { + err = ib.DeleteN(tagId, 1) + if log.Is(err, ErrTagEntryNotFound) == true { + break + } else if err != nil { + log.Panic(err) + } - n++ - } + n++ + } - return n, nil + return n, nil } -func (ib *IfdBuilder) ReplaceAt(position int, bt *builderTag) (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() +func (ib *IfdBuilder) ReplaceAt(position int, bt *BuilderTag) (err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if position < 0 { - log.Panicf("replacement position must be 0 or greater") - } else if position >= len(ib.tags) { - log.Panicf("replacement position does not exist") - } + if position < 0 { + log.Panicf("replacement position must be 0 or greater") + } else if position >= len(ib.tags) { + log.Panicf("replacement position does not exist") + } - ib.tags[position] = bt + ib.tags[position] = bt - return nil + return nil } -func (ib *IfdBuilder) Replace(tagId uint16, bt *builderTag) (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() +func (ib *IfdBuilder) Replace(tagId uint16, bt *BuilderTag) (err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - position, err := ib.Find(tagId) - log.PanicIf(err) + position, err := ib.Find(tagId) + log.PanicIf(err) - ib.tags[position] = bt + ib.tags[position] = bt - return nil + return nil } // Set will add a new entry or update an existing entry. -func (ib *IfdBuilder) Set(bt *builderTag) (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() +func (ib *IfdBuilder) Set(bt *BuilderTag) (err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - position, err := ib.Find(bt.tagId) - if err == nil { - ib.tags[position] = bt - } else if log.Is(err, ErrTagEntryNotFound) == true { - err = ib.add(bt) - log.PanicIf(err) - } else { - log.Panic(err) - } + position, err := ib.Find(bt.tagId) + if err == nil { + ib.tags[position] = bt + } else if log.Is(err, ErrTagEntryNotFound) == true { + err = ib.add(bt) + log.PanicIf(err) + } else { + log.Panic(err) + } - return nil + return nil } func (ib *IfdBuilder) FindN(tagId uint16, maxFound int) (found []int, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - found = make([]int, 0) + found = make([]int, 0) - for i, bt := range ib.tags { - if bt.tagId == tagId { - found = append(found, i) - if maxFound == 0 || len(found) >= maxFound { - break - } - } - } + for i, bt := range ib.tags { + if bt.tagId == tagId { + found = append(found, i) + if maxFound == 0 || len(found) >= maxFound { + break + } + } + } - return found, nil + return found, nil } func (ib *IfdBuilder) Find(tagId uint16) (position int, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - found, err := ib.FindN(tagId, 1) - log.PanicIf(err) + found, err := ib.FindN(tagId, 1) + log.PanicIf(err) - if len(found) == 0 { - log.Panic(ErrTagEntryNotFound) - } + if len(found) == 0 { + log.Panic(ErrTagEntryNotFound) + } - return found[0], nil + return found[0], nil } -func (ib *IfdBuilder) FindTag(tagId uint16) (bt *builderTag, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() +func (ib *IfdBuilder) FindTag(tagId uint16) (bt *BuilderTag, err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - found, err := ib.FindN(tagId, 1) - log.PanicIf(err) + found, err := ib.FindN(tagId, 1) + log.PanicIf(err) - if len(found) == 0 { - log.Panic(ErrTagEntryNotFound) - } + if len(found) == 0 { + log.Panic(ErrTagEntryNotFound) + } - position := found[0] + position := found[0] - return ib.tags[position], nil + return ib.tags[position], nil } -func (ib *IfdBuilder) FindTagWithName(tagName string) (bt *builderTag, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() +func (ib *IfdBuilder) FindTagWithName(tagName string) (bt *BuilderTag, err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - ti := NewTagIndex() + ti := NewTagIndex() - it, err := ti.GetWithName(ib.ii, tagName) - log.PanicIf(err) + it, err := ti.GetWithName(ib.ii, tagName) + log.PanicIf(err) - found, err := ib.FindN(it.Id, 1) - log.PanicIf(err) + found, err := ib.FindN(it.Id, 1) + log.PanicIf(err) - if len(found) == 0 { - log.Panic(ErrTagEntryNotFound) - } + if len(found) == 0 { + log.Panic(ErrTagEntryNotFound) + } - position := found[0] + position := found[0] - return ib.tags[position], nil + return ib.tags[position], nil } -func (ib *IfdBuilder) add(bt *builderTag) (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() +func (ib *IfdBuilder) add(bt *BuilderTag) (err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if bt.ii == ZeroIi { - log.Panicf("builderTag IfdIdentity is not set: %s", bt) - } else if bt.typeId == 0x0 { - log.Panicf("builderTag type-ID is not set: %s", bt) - } else if bt.value == nil { - log.Panicf("builderTag value is not set: %s", bt) - } + if bt.ii == ZeroIi { + log.Panicf("BuilderTag IfdIdentity is not set: %s", bt) + } else if bt.typeId == 0x0 { + log.Panicf("BuilderTag type-ID is not set: %s", bt) + } else if bt.value == nil { + log.Panicf("BuilderTag value is not set: %s", bt) + } - ib.tags = append(ib.tags, bt) - return nil + ib.tags = append(ib.tags, bt) + return nil } -func (ib *IfdBuilder) Add(bt *builderTag) (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() +func (ib *IfdBuilder) Add(bt *BuilderTag) (err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if bt.value.IsIb() == true { - log.Panicf("child IfdBuilders must be added via AddChildIb() or AddTagsFromExisting(), not Add()") - } + if bt.value.IsIb() == true { + log.Panicf("child IfdBuilders must be added via AddChildIb() or AddTagsFromExisting(), not Add()") + } - err = ib.add(bt) - log.PanicIf(err) + err = ib.add(bt) + log.PanicIf(err) - return nil + return nil } // AddChildIb adds a tag that branches to a new IFD. func (ib *IfdBuilder) AddChildIb(childIb *IfdBuilder) (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if childIb.ifdTagId == 0 { - log.Panicf("IFD can not be used as a child IFD (not associated with a tag-ID): %v", childIb) - } else if childIb.byteOrder != ib.byteOrder { - log.Panicf("Child IFD does not have the same byte-order: [%s] != [%s]", childIb.byteOrder, ib.byteOrder) - } + if childIb.ifdTagId == 0 { + log.Panicf("IFD can not be used as a child IFD (not associated with a tag-ID): %v", childIb) + } else if childIb.byteOrder != ib.byteOrder { + log.Panicf("Child IFD does not have the same byte-order: [%s] != [%s]", childIb.byteOrder, ib.byteOrder) + } - // Since no standard IFDs supports occuring more than once, check that a - // tag of this type has not been previously added. Note that we just search - // the current IFD and *not every* IFD. - for _, bt := range childIb.tags { - if bt.tagId == childIb.ifdTagId { - log.Panicf("child-IFD already added: %v", childIb.ii) - } - } + // Since no standard IFDs supports occuring more than once, check that a + // tag of this type has not been previously added. Note that we just search + // the current IFD and *not every* IFD. + for _, bt := range childIb.tags { + if bt.tagId == childIb.ifdTagId { + log.Panicf("child-IFD already added: %v", childIb.ii) + } + } - bt := ib.NewBuilderTagFromBuilder(childIb) - ib.tags = append(ib.tags, bt) + bt := ib.NewBuilderTagFromBuilder(childIb) + ib.tags = append(ib.tags, bt) - return nil + return nil } -func (ib *IfdBuilder) NewBuilderTagFromBuilder(childIb *IfdBuilder) (bt *builderTag) { - defer func() { - if state := recover(); state != nil { - err := log.Wrap(state.(error)) - log.Panic(err) - } - }() +func (ib *IfdBuilder) NewBuilderTagFromBuilder(childIb *IfdBuilder) (bt *BuilderTag) { + defer func() { + if state := recover(); state != nil { + err := log.Wrap(state.(error)) + log.Panic(err) + } + }() - value := NewIfdBuilderTagValueFromIfdBuilder(childIb) + value := NewIfdBuilderTagValueFromIfdBuilder(childIb) - bt = NewChildIfdBuilderTag( - ib.ii, - childIb.ifdTagId, - value) + bt = NewChildIfdBuilderTag( + ib.ii, + childIb.ifdTagId, + value) - return bt + return bt } // AddTagsFromExisting does a verbatim copy of the entries in `ifd` to this // builder. It excludes child IFDs. These must be added explicitly via // `AddChildIb()`. func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, itevr *IfdTagEntryValueResolver, includeTagIds []uint16, excludeTagIds []uint16) (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - thumbnailData, err := ifd.Thumbnail() - if err == nil { - err = ib.SetThumbnail(thumbnailData) - log.PanicIf(err) - } else if log.Is(err, ErrNoThumbnail) == false { - log.Panic(err) - } + thumbnailData, err := ifd.Thumbnail() + if err == nil { + err = ib.SetThumbnail(thumbnailData) + log.PanicIf(err) + } else if log.Is(err, ErrNoThumbnail) == false { + log.Panic(err) + } - for i, ite := range ifd.Entries { - if ite.TagId == ThumbnailOffsetTagId || ite.TagId == ThumbnailSizeTagId { - // These will be added on-the-fly when we encode. - continue - } + for i, ite := range ifd.Entries { + if ite.TagId == ThumbnailOffsetTagId || ite.TagId == ThumbnailSizeTagId { + // These will be added on-the-fly when we encode. + continue + } - if excludeTagIds != nil && len(excludeTagIds) > 0 { - found := false - for _, excludedTagId := range excludeTagIds { - if excludedTagId == ite.TagId { - found = true - } - } + if excludeTagIds != nil && len(excludeTagIds) > 0 { + found := false + for _, excludedTagId := range excludeTagIds { + if excludedTagId == ite.TagId { + found = true + } + } - if found == true { - continue - } - } + if found == true { + continue + } + } - if includeTagIds != nil && len(includeTagIds) > 0 { - // Whether or not there was a list of excludes, if there is a list - // of includes than the current tag has to be in it. + if includeTagIds != nil && len(includeTagIds) > 0 { + // Whether or not there was a list of excludes, if there is a list + // of includes than the current tag has to be in it. - found := false - for _, includedTagId := range includeTagIds { - if includedTagId == ite.TagId { - found = true - break - } - } + found := false + for _, includedTagId := range includeTagIds { + if includedTagId == ite.TagId { + found = true + break + } + } - if found == false { - continue - } - } + if found == false { + continue + } + } - var bt *builderTag + var bt *BuilderTag - if ite.ChildIfdName != "" { - // If we want to add an IFD tag, we'll have to build it first and - // *then* add it via a different method. + if ite.ChildIfdName != "" { + // If we want to add an IFD tag, we'll have to build it first and + // *then* add it via a different method. - if itevr == nil { - // We don't have any ability to resolve the structure of the - // child-IFD. Just install it as a normal tag rather than a - // fully-structured child-IFD. We're going to blank the value, - // though, since its original offset will no longer be valid - // (nor does it matter since this is just a temporary - // placeholder, in this situation). - value := NewIfdBuilderTagValueFromBytes([]byte { 0, 0, 0, 0 }) - bt = NewBuilderTag(ite.Ii, ite.TagId, ite.TagType, value) - } else { - // Figure out which of the child-IFDs that are associated with - // this IFD represents this specific child IFD. + if itevr == nil { + // We don't have any ability to resolve the structure of the + // child-IFD. Just install it as a normal tag rather than a + // fully-structured child-IFD. We're going to blank the value, + // though, since its original offset will no longer be valid + // (nor does it matter since this is just a temporary + // placeholder, in this situation). + value := NewIfdBuilderTagValueFromBytes([]byte{0, 0, 0, 0}) + bt = NewBuilderTag(ite.Ii, ite.TagId, ite.TagType, value) + } else { + // Figure out which of the child-IFDs that are associated with + // this IFD represents this specific child IFD. - var childIfd *Ifd - for _, thisChildIfd := range ifd.Children { - if thisChildIfd.ParentTagIndex != i { - continue - } else if thisChildIfd.TagId != 0xffff && thisChildIfd.TagId != ite.TagId { - log.Panicf("child-IFD tag is not correct: TAG-POSITION=(%d) ITE=%s CHILD-IFD=%s", thisChildIfd.ParentTagIndex, ite, thisChildIfd) - } + var childIfd *Ifd + for _, thisChildIfd := range ifd.Children { + if thisChildIfd.ParentTagIndex != i { + continue + } else if thisChildIfd.TagId != 0xffff && thisChildIfd.TagId != ite.TagId { + log.Panicf("child-IFD tag is not correct: TAG-POSITION=(%d) ITE=%s CHILD-IFD=%s", thisChildIfd.ParentTagIndex, ite, thisChildIfd) + } - childIfd = thisChildIfd - break - } + childIfd = thisChildIfd + break + } - if childIfd == nil { - childTagIds := make([]string, len(ifd.Children)) - for j, childIfd := range ifd.Children { - childTagIds[j] = fmt.Sprintf("0x%04x (parent tag-position %d)", childIfd.TagId, childIfd.ParentTagIndex) - } + if childIfd == nil { + childTagIds := make([]string, len(ifd.Children)) + for j, childIfd := range ifd.Children { + childTagIds[j] = fmt.Sprintf("0x%04x (parent tag-position %d)", childIfd.TagId, childIfd.ParentTagIndex) + } - log.Panicf("could not find child IFD for child ITE: II=[%s] TAG-ID=(0x%04x) CURRENT-TAG-POSITION=(%d) CHILDREN=%v", ite.Ii, ite.TagId, i, childTagIds) - } + log.Panicf("could not find child IFD for child ITE: II=[%s] TAG-ID=(0x%04x) CURRENT-TAG-POSITION=(%d) CHILDREN=%v", ite.Ii, ite.TagId, i, childTagIds) + } - childIb := NewIfdBuilderFromExistingChain(childIfd, itevr) - bt = ib.NewBuilderTagFromBuilder(childIb) - } - } else { - var value *IfdBuilderTagValue + childIb := NewIfdBuilderFromExistingChain(childIfd, itevr) + bt = ib.NewBuilderTagFromBuilder(childIb) + } + } else { + var value *IfdBuilderTagValue - if itevr == nil { - // rawValueOffsetCopy is our own private copy of the original data. - // It should always be four-bytes, but just copy whatever there is. - rawValueOffsetCopy := make([]byte, len(ite.RawValueOffset)) - copy(rawValueOffsetCopy, ite.RawValueOffset) + if itevr == nil { + // rawValueOffsetCopy is our own private copy of the original data. + // It should always be four-bytes, but just copy whatever there is. + rawValueOffsetCopy := make([]byte, len(ite.RawValueOffset)) + copy(rawValueOffsetCopy, ite.RawValueOffset) - value = NewIfdBuilderTagValueFromBytes(rawValueOffsetCopy) - } else { - var err error + value = NewIfdBuilderTagValueFromBytes(rawValueOffsetCopy) + } else { + var err error - valueBytes, err := itevr.ValueBytes(ite) - if err != nil { - if log.Is(err, ErrUnhandledUnknownTypedTag) == true { - ifdBuilderLogger.Warningf(nil, "Unknown-type tag can't be parsed so it can't be copied to the new IFD.") - continue - } + valueBytes, err := itevr.ValueBytes(ite) + if err != nil { + if log.Is(err, ErrUnhandledUnknownTypedTag) == true { + ifdBuilderLogger.Warningf(nil, "Unknown-type tag can't be parsed so it can't be copied to the new IFD.") + continue + } - log.Panic(err) - } + log.Panic(err) + } - value = NewIfdBuilderTagValueFromBytes(valueBytes) - } + value = NewIfdBuilderTagValueFromBytes(valueBytes) + } - bt = NewBuilderTag(ifd.Ii, ite.TagId, ite.TagType, value) - } + bt = NewBuilderTag(ifd.Ii, ite.TagId, ite.TagType, value) + } - err := ib.add(bt) - log.PanicIf(err) - } + err := ib.add(bt) + log.PanicIf(err) + } - return nil + return nil } // AddFromConfig quickly and easily composes and adds the tag using the // information already known about a tag. Only works with standard tags. func (ib *IfdBuilder) AddFromConfig(tagId uint16, value interface{}) (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - bt := NewStandardBuilderTagFromConfig(ib.ii, tagId, ib.byteOrder, value) + bt := NewStandardBuilderTagFromConfig(ib.ii, tagId, ib.byteOrder, value) - err = ib.add(bt) - log.PanicIf(err) + err = ib.add(bt) + log.PanicIf(err) - return nil + return nil } // SetFromConfig quickly and easily composes and adds or replaces the tag using // the information already known about a tag. Only works with standard tags. func (ib *IfdBuilder) SetFromConfig(tagId uint16, value interface{}) (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() -// TODO(dustin): !! Add test. + // TODO(dustin): !! Add test. - bt := NewStandardBuilderTagFromConfig(ib.ii, tagId, ib.byteOrder, value) + bt := NewStandardBuilderTagFromConfig(ib.ii, tagId, ib.byteOrder, value) - i, err := ib.Find(tagId) - log.PanicIf(err) + i, err := ib.Find(tagId) + log.PanicIf(err) - ib.tags[i] = bt + ib.tags[i] = bt - return nil + return nil } // AddFromConfigWithName quickly and easily composes and adds the tag using the // information already known about a tag (using the name). Only works with // standard tags. func (ib *IfdBuilder) AddFromConfigWithName(tagName string, value interface{}) (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - bt := NewStandardBuilderTagFromConfigWithName(ib.ii, tagName, ib.byteOrder, value) + bt := NewStandardBuilderTagFromConfigWithName(ib.ii, tagName, ib.byteOrder, value) - err = ib.add(bt) - log.PanicIf(err) + err = ib.add(bt) + log.PanicIf(err) - return nil + return nil } // SetFromConfigWithName quickly and easily composes and adds or replaces the // tag using the information already known about a tag (using the name). Only // works with standard tags. func (ib *IfdBuilder) SetFromConfigWithName(tagName string, value interface{}) (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() -// TODO(dustin): !! Add test. + // TODO(dustin): !! Add test. - bt := NewStandardBuilderTagFromConfigWithName(ib.ii, tagName, ib.byteOrder, value) + bt := NewStandardBuilderTagFromConfigWithName(ib.ii, tagName, ib.byteOrder, value) - i, err := ib.Find(bt.tagId) - log.PanicIf(err) + i, err := ib.Find(bt.tagId) + log.PanicIf(err) - ib.tags[i] = bt + ib.tags[i] = bt - return nil + return nil } diff --git a/ifd_builder_encode.go b/ifd_builder_encode.go index ab737db..c5ea5d7 100644 --- a/ifd_builder_encode.go +++ b/ifd_builder_encode.go @@ -1,199 +1,197 @@ package exif import ( - "bytes" - "fmt" - "strings" + "bytes" + "fmt" + "strings" - "encoding/binary" + "encoding/binary" - "github.com/dsoprea/go-logging" + "github.com/dsoprea/go-logging" ) const ( - // Tag-ID + Tag-Type + Unit-Count + Value/Offset. - IfdTagEntrySize = uint32(2 + 2 + 4 + 4) + // Tag-ID + Tag-Type + Unit-Count + Value/Offset. + IfdTagEntrySize = uint32(2 + 2 + 4 + 4) ) type ByteWriter struct { - b *bytes.Buffer - byteOrder binary.ByteOrder + b *bytes.Buffer + byteOrder binary.ByteOrder } func NewByteWriter(b *bytes.Buffer, byteOrder binary.ByteOrder) (bw *ByteWriter) { - return &ByteWriter{ - b: b, - byteOrder: byteOrder, - } + return &ByteWriter{ + b: b, + byteOrder: byteOrder, + } } func (bw ByteWriter) writeAsBytes(value interface{}) (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - err = binary.Write(bw.b, bw.byteOrder, value) - log.PanicIf(err) + err = binary.Write(bw.b, bw.byteOrder, value) + log.PanicIf(err) - return nil + return nil } func (bw ByteWriter) WriteUint32(value uint32) (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - err = bw.writeAsBytes(value) - log.PanicIf(err) + err = bw.writeAsBytes(value) + log.PanicIf(err) - return nil + return nil } func (bw ByteWriter) WriteUint16(value uint16) (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - err = bw.writeAsBytes(value) - log.PanicIf(err) + err = bw.writeAsBytes(value) + log.PanicIf(err) - return nil + return nil } func (bw ByteWriter) WriteFourBytes(value []byte) (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - len_ := len(value) - if len_ != 4 { - log.Panicf("value is not four-bytes: (%d)", len_) - } + len_ := len(value) + if len_ != 4 { + log.Panicf("value is not four-bytes: (%d)", len_) + } - _, err = bw.b.Write(value) - log.PanicIf(err) + _, err = bw.b.Write(value) + log.PanicIf(err) - return nil + return nil } - // ifdOffsetIterator keeps track of where the next IFD should be written by // keeping track of where the offsets start, the data that has been added, and // bumping the offset *when* the data is added. type ifdDataAllocator struct { - offset uint32 - b bytes.Buffer + offset uint32 + b bytes.Buffer } -func newIfdDataAllocator(ifdDataAddressableOffset uint32) *ifdDataAllocator{ - return &ifdDataAllocator{ - offset: ifdDataAddressableOffset, - } +func newIfdDataAllocator(ifdDataAddressableOffset uint32) *ifdDataAllocator { + return &ifdDataAllocator{ + offset: ifdDataAddressableOffset, + } } func (ida *ifdDataAllocator) Allocate(value []byte) (offset uint32, err error) { - _, err = ida.b.Write(value) - log.PanicIf(err) + _, err = ida.b.Write(value) + log.PanicIf(err) - offset = ida.offset - ida.offset += uint32(len(value)) + offset = ida.offset + ida.offset += uint32(len(value)) - return offset, nil + return offset, nil } func (ida *ifdDataAllocator) NextOffset() uint32 { - return ida.offset + return ida.offset } func (ida *ifdDataAllocator) Bytes() []byte { - return ida.b.Bytes() + return ida.b.Bytes() } - // IfdByteEncoder converts an IB to raw bytes (for writing) while also figuring // out all of the allocations and indirection that is required for extended // data. type IfdByteEncoder struct { - // journal holds a list of actions taken while encoding. - journal [][3]string + // journal holds a list of actions taken while encoding. + journal [][3]string } func NewIfdByteEncoder() (ibe *IfdByteEncoder) { - return &IfdByteEncoder{ - journal: make([][3]string, 0), - } + return &IfdByteEncoder{ + journal: make([][3]string, 0), + } } func (ibe *IfdByteEncoder) Journal() [][3]string { - return ibe.journal + return ibe.journal } func (ibe *IfdByteEncoder) TableSize(entryCount int) uint32 { - // Tag-Count + (Entry-Size * Entry-Count) + Next-IFD-Offset. - return uint32(2) + (IfdTagEntrySize * uint32(entryCount)) + uint32(4) + // Tag-Count + (Entry-Size * Entry-Count) + Next-IFD-Offset. + return uint32(2) + (IfdTagEntrySize * uint32(entryCount)) + uint32(4) } func (ibe *IfdByteEncoder) pushToJournal(where, direction, format string, args ...interface{}) { - event := [3]string { - direction, - where, - fmt.Sprintf(format, args...), - } + event := [3]string{ + direction, + where, + fmt.Sprintf(format, args...), + } - ibe.journal = append(ibe.journal, event) + ibe.journal = append(ibe.journal, event) } // PrintJournal prints a hierarchical representation of the steps taken during // encoding. func (ibe *IfdByteEncoder) PrintJournal() { - maxWhereLength := 0 - for _, event := range ibe.journal { - where := event[1] + maxWhereLength := 0 + for _, event := range ibe.journal { + where := event[1] - len_ := len(where) - if len_ > maxWhereLength { - maxWhereLength = len_ - } - } + len_ := len(where) + if len_ > maxWhereLength { + maxWhereLength = len_ + } + } - level := 0 - for i, event := range ibe.journal { - direction := event[0] - where := event[1] - message := event[2] + level := 0 + for i, event := range ibe.journal { + direction := event[0] + where := event[1] + message := event[2] - if direction != ">" && direction != "<" && direction != "-" { - log.Panicf("journal operation not valid: [%s]", direction) - } + if direction != ">" && direction != "<" && direction != "-" { + log.Panicf("journal operation not valid: [%s]", direction) + } - if direction == "<" { - if level <= 0 { - log.Panicf("journal operations unbalanced (too many closes)") - } + if direction == "<" { + if level <= 0 { + log.Panicf("journal operations unbalanced (too many closes)") + } - level-- - } + level-- + } - indent := strings.Repeat(" ", level) + indent := strings.Repeat(" ", level) - fmt.Printf("%3d %s%s %s: %s\n", i, indent, direction, where, message) + fmt.Printf("%3d %s%s %s: %s\n", i, indent, direction, where, message) - if direction == ">" { - level++ - } - } + if direction == ">" { + level++ + } + } - if level != 0 { - log.Panicf("journal operations unbalanced (too many opens)") - } + if level != 0 { + log.Panicf("journal operations unbalanced (too many opens)") + } } // encodeTagToBytes encodes the given tag to a byte stream. If @@ -201,103 +199,103 @@ func (ibe *IfdByteEncoder) PrintJournal() { // (`nextIfdOffsetToWrite` is required in order for them to know where the its // IFD data will be written, in order for them to know the offset of where // their allocated-data block will start, which follows right behind). -func (ibe *IfdByteEncoder) encodeTagToBytes(ib *IfdBuilder, bt *builderTag, bw *ByteWriter, ida *ifdDataAllocator, nextIfdOffsetToWrite uint32) (childIfdBlock []byte, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() +func (ibe *IfdByteEncoder) encodeTagToBytes(ib *IfdBuilder, bt *BuilderTag, bw *ByteWriter, ida *ifdDataAllocator, nextIfdOffsetToWrite uint32) (childIfdBlock []byte, err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - // Write tag-ID. - err = bw.WriteUint16(bt.tagId) - log.PanicIf(err) + // Write tag-ID. + err = bw.WriteUint16(bt.tagId) + log.PanicIf(err) - // Works for both values and child IFDs (which have an official size of - // LONG). - err = bw.WriteUint16(bt.typeId) - log.PanicIf(err) + // Works for both values and child IFDs (which have an official size of + // LONG). + err = bw.WriteUint16(bt.typeId) + log.PanicIf(err) - // Write unit-count. + // Write unit-count. - if bt.value.IsBytes() == true { - effectiveType := bt.typeId - if bt.typeId == TypeUndefined { - effectiveType = TypeByte - } + if bt.value.IsBytes() == true { + effectiveType := bt.typeId + if bt.typeId == TypeUndefined { + effectiveType = TypeByte + } - // It's a non-unknown value.Calculate the count of values of - // the type that we're writing and the raw bytes for the whole list. + // It's a non-unknown value.Calculate the count of values of + // the type that we're writing and the raw bytes for the whole list. - typeSize := uint32(TagTypeSize(effectiveType)) + typeSize := uint32(TagTypeSize(effectiveType)) - valueBytes := bt.value.Bytes() + valueBytes := bt.value.Bytes() - len_ := len(valueBytes) - unitCount := uint32(len_) / typeSize + len_ := len(valueBytes) + unitCount := uint32(len_) / typeSize - if _, found := tagsWithoutAlignment[bt.tagId]; found == false { - remainder := uint32(len_) % typeSize + if _, found := tagsWithoutAlignment[bt.tagId]; found == false { + remainder := uint32(len_) % typeSize - if remainder > 0 { - log.Panicf("tag (0x%04x) value of (%d) bytes not evenly divisible by type-size (%d)", bt.tagId, len_, typeSize) - } - } + if remainder > 0 { + log.Panicf("tag (0x%04x) value of (%d) bytes not evenly divisible by type-size (%d)", bt.tagId, len_, typeSize) + } + } - err = bw.WriteUint32(unitCount) - log.PanicIf(err) + err = bw.WriteUint32(unitCount) + log.PanicIf(err) - // Write four-byte value/offset. + // Write four-byte value/offset. - if len_ > 4 { - offset, err := ida.Allocate(valueBytes) - log.PanicIf(err) + if len_ > 4 { + offset, err := ida.Allocate(valueBytes) + log.PanicIf(err) - err = bw.WriteUint32(offset) - log.PanicIf(err) - } else { - fourBytes := make([]byte, 4) - copy(fourBytes, valueBytes) + err = bw.WriteUint32(offset) + log.PanicIf(err) + } else { + fourBytes := make([]byte, 4) + copy(fourBytes, valueBytes) - err = bw.WriteFourBytes(fourBytes) - log.PanicIf(err) - } - } else { - if bt.value.IsIb() == false { - log.Panicf("tag value is not a byte-slice but also not a child IB: %v", bt) - } + err = bw.WriteFourBytes(fourBytes) + log.PanicIf(err) + } + } else { + if bt.value.IsIb() == false { + log.Panicf("tag value is not a byte-slice but also not a child IB: %v", bt) + } - // Write unit-count (one LONG representing one offset). - err = bw.WriteUint32(1) - log.PanicIf(err) + // Write unit-count (one LONG representing one offset). + err = bw.WriteUint32(1) + log.PanicIf(err) - if nextIfdOffsetToWrite > 0 { - var err error + if nextIfdOffsetToWrite > 0 { + var err error - ibe.pushToJournal("encodeTagToBytes", ">", "[%s]->[%s]", ib.ii.IfdName, bt.value.Ib().ii.IfdName) + ibe.pushToJournal("encodeTagToBytes", ">", "[%s]->[%s]", ib.ii.IfdName, bt.value.Ib().ii.IfdName) - // Create the block of IFD data and everything it requires. - childIfdBlock, err = ibe.encodeAndAttachIfd(bt.value.Ib(), nextIfdOffsetToWrite) - log.PanicIf(err) + // Create the block of IFD data and everything it requires. + childIfdBlock, err = ibe.encodeAndAttachIfd(bt.value.Ib(), nextIfdOffsetToWrite) + log.PanicIf(err) - ibe.pushToJournal("encodeTagToBytes", "<", "[%s]->[%s]", bt.value.Ib().ii.IfdName, ib.ii.IfdName) + ibe.pushToJournal("encodeTagToBytes", "<", "[%s]->[%s]", bt.value.Ib().ii.IfdName, ib.ii.IfdName) - // Use the next-IFD offset for it. The IFD will actually get - // attached after we return. - err = bw.WriteUint32(nextIfdOffsetToWrite) - log.PanicIf(err) + // Use the next-IFD offset for it. The IFD will actually get + // attached after we return. + err = bw.WriteUint32(nextIfdOffsetToWrite) + log.PanicIf(err) - } else { - // No child-IFDs are to be allocated. Finish the entry with a NULL - // pointer. + } else { + // No child-IFDs are to be allocated. Finish the entry with a NULL + // pointer. - ibe.pushToJournal("encodeTagToBytes", "-", "*Not* descending to child: [%s]", bt.value.Ib().ii.IfdName) + ibe.pushToJournal("encodeTagToBytes", "-", "*Not* descending to child: [%s]", bt.value.Ib().ii.IfdName) - err = bw.WriteUint32(0) - log.PanicIf(err) - } - } + err = bw.WriteUint32(0) + log.PanicIf(err) + } + } - return childIfdBlock, nil + return childIfdBlock, nil } // encodeIfdToBytes encodes the given IB to a byte-slice. We are given the @@ -310,255 +308,254 @@ func (ibe *IfdByteEncoder) encodeTagToBytes(ib *IfdBuilder, bt *builderTag, bw * // IFDs, we will not be able to allocate them without first knowing how much // data we need to allocate for the current IFD. func (ibe *IfdByteEncoder) encodeIfdToBytes(ib *IfdBuilder, ifdAddressableOffset uint32, nextIfdOffsetToWrite uint32, setNextIb bool) (data []byte, tableSize uint32, dataSize uint32, childIfdSizes []uint32, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - ibe.pushToJournal("encodeIfdToBytes", ">", "%s", ib) + ibe.pushToJournal("encodeIfdToBytes", ">", "%s", ib) - tableSize = ibe.TableSize(len(ib.tags)) + tableSize = ibe.TableSize(len(ib.tags)) - b := new(bytes.Buffer) - bw := NewByteWriter(b, ib.byteOrder) + b := new(bytes.Buffer) + bw := NewByteWriter(b, ib.byteOrder) - // Write tag count. - err = bw.WriteUint16(uint16(len(ib.tags))) - log.PanicIf(err) + // Write tag count. + err = bw.WriteUint16(uint16(len(ib.tags))) + log.PanicIf(err) - ida := newIfdDataAllocator(ifdAddressableOffset) + ida := newIfdDataAllocator(ifdAddressableOffset) - childIfdBlocks := make([][]byte, 0) + childIfdBlocks := make([][]byte, 0) - // Write raw bytes for each tag entry. Allocate larger data to be referred - // to in the follow-up data-block as required. Any "unknown"-byte tags that - // we can't parse will not be present here (using AddTagsFromExisting(), at - // least). - for _, bt := range ib.tags { - childIfdBlock, err := ibe.encodeTagToBytes(ib, bt, bw, ida, nextIfdOffsetToWrite) - log.PanicIf(err) + // Write raw bytes for each tag entry. Allocate larger data to be referred + // to in the follow-up data-block as required. Any "unknown"-byte tags that + // we can't parse will not be present here (using AddTagsFromExisting(), at + // least). + for _, bt := range ib.tags { + childIfdBlock, err := ibe.encodeTagToBytes(ib, bt, bw, ida, nextIfdOffsetToWrite) + log.PanicIf(err) - if childIfdBlock != nil { - // We aren't allowed to have non-nil child IFDs if we're just - // sizing things up. - if nextIfdOffsetToWrite == 0 { - log.Panicf("no IFD offset provided for child-IFDs; no new child-IFDs permitted") - } + if childIfdBlock != nil { + // We aren't allowed to have non-nil child IFDs if we're just + // sizing things up. + if nextIfdOffsetToWrite == 0 { + log.Panicf("no IFD offset provided for child-IFDs; no new child-IFDs permitted") + } - nextIfdOffsetToWrite += uint32(len(childIfdBlock)) - childIfdBlocks = append(childIfdBlocks, childIfdBlock) - } - } + nextIfdOffsetToWrite += uint32(len(childIfdBlock)) + childIfdBlocks = append(childIfdBlocks, childIfdBlock) + } + } - dataBytes := ida.Bytes() - dataSize = uint32(len(dataBytes)) + dataBytes := ida.Bytes() + dataSize = uint32(len(dataBytes)) - childIfdSizes = make([]uint32, len(childIfdBlocks)) - childIfdsTotalSize := uint32(0) - for i, childIfdBlock := range childIfdBlocks { - len_ := uint32(len(childIfdBlock)) - childIfdSizes[i] = len_ - childIfdsTotalSize += len_ - } + childIfdSizes = make([]uint32, len(childIfdBlocks)) + childIfdsTotalSize := uint32(0) + for i, childIfdBlock := range childIfdBlocks { + len_ := uint32(len(childIfdBlock)) + childIfdSizes[i] = len_ + childIfdsTotalSize += len_ + } - // N the link from this IFD to the next IFD that will be written in the - // next cycle. - if setNextIb == true { - // Write address of next IFD in chain. This will be the original - // allocation offset plus the size of everything we have allocated for - // this IFD and its child-IFDs. - // - // It is critical that this number is stepped properly. We experienced - // an issue whereby it first looked like we were duplicating the IFD and - // then that we were duplicating the tags in the wrong IFD, and then - // finally we determined that the next-IFD offset for the first IFD was - // accidentally pointing back to the EXIF IFD, so we were visiting it - // twice when visiting through the tags after decoding. It was an - // expensive bug to find. + // N the link from this IFD to the next IFD that will be written in the + // next cycle. + if setNextIb == true { + // Write address of next IFD in chain. This will be the original + // allocation offset plus the size of everything we have allocated for + // this IFD and its child-IFDs. + // + // It is critical that this number is stepped properly. We experienced + // an issue whereby it first looked like we were duplicating the IFD and + // then that we were duplicating the tags in the wrong IFD, and then + // finally we determined that the next-IFD offset for the first IFD was + // accidentally pointing back to the EXIF IFD, so we were visiting it + // twice when visiting through the tags after decoding. It was an + // expensive bug to find. - ibe.pushToJournal("encodeIfdToBytes", "-", "Setting 'next' IFD to (0x%08x).", nextIfdOffsetToWrite) + ibe.pushToJournal("encodeIfdToBytes", "-", "Setting 'next' IFD to (0x%08x).", nextIfdOffsetToWrite) - err := bw.WriteUint32(nextIfdOffsetToWrite) - log.PanicIf(err) - } else { - err := bw.WriteUint32(0) - log.PanicIf(err) - } + err := bw.WriteUint32(nextIfdOffsetToWrite) + log.PanicIf(err) + } else { + err := bw.WriteUint32(0) + log.PanicIf(err) + } - _, err = b.Write(dataBytes) - log.PanicIf(err) + _, err = b.Write(dataBytes) + log.PanicIf(err) -// TODO(dustin): !! Debugging. - // if thumbnailOffset != uint32(0) { - // currentRelativeOffset := thumbnailOffset - ifdAddressableOffset - // currentBuffer := b.Bytes() + // TODO(dustin): !! Debugging. + // if thumbnailOffset != uint32(0) { + // currentRelativeOffset := thumbnailOffset - ifdAddressableOffset + // currentBuffer := b.Bytes() - // len_ := len(thumbnailData) - // extractedThumbnailData := currentBuffer[int(currentRelativeOffset):int(currentRelativeOffset) + len_] + // len_ := len(thumbnailData) + // extractedThumbnailData := currentBuffer[int(currentRelativeOffset):int(currentRelativeOffset) + len_] - // // We didn't have enough data available, which would be queer. - // if len(extractedThumbnailData) != len_ { - // log.Panicf("extracted thumbnail data was truncated; not enough data") - // } + // // We didn't have enough data available, which would be queer. + // if len(extractedThumbnailData) != len_ { + // log.Panicf("extracted thumbnail data was truncated; not enough data") + // } -// // fmt.Printf("Re-extracted (%d) bytes of thumbnail data.\n", len(extractedThumbnailData)) -// // DumpBytes(extractedThumbnailData[:50]) -// } + // // fmt.Printf("Re-extracted (%d) bytes of thumbnail data.\n", len(extractedThumbnailData)) + // // DumpBytes(extractedThumbnailData[:50]) + // } - // Append any child IFD blocks after our table and data blocks. These IFDs - // were equipped with the appropriate offset information so it's expected - // that all offsets referred to by these will be correct. - // - // Note that child-IFDs are append after the current IFD and before the - // next IFD, as opposed to the root IFDs, which are chained together but - // will be interrupted by these child-IFDs (which is expected, per the - // standard). + // Append any child IFD blocks after our table and data blocks. These IFDs + // were equipped with the appropriate offset information so it's expected + // that all offsets referred to by these will be correct. + // + // Note that child-IFDs are append after the current IFD and before the + // next IFD, as opposed to the root IFDs, which are chained together but + // will be interrupted by these child-IFDs (which is expected, per the + // standard). - for _, childIfdBlock := range childIfdBlocks { - _, err = b.Write(childIfdBlock) - log.PanicIf(err) - } + for _, childIfdBlock := range childIfdBlocks { + _, err = b.Write(childIfdBlock) + log.PanicIf(err) + } - ibe.pushToJournal("encodeIfdToBytes", "<", "%s", ib) + ibe.pushToJournal("encodeIfdToBytes", "<", "%s", ib) -// if nextIfdOffsetToWrite > uint32(0) { -// fmt.Printf("Encoded IB: %s ADDRESSABLE-OFFSET=(0x%08x; %d):\n", ib.ii, ifdAddressableOffset, ifdAddressableOffset) -// fmt.Printf("\n") -// DumpBytes(b.Bytes()) -// fmt.Printf("\n") -// } + // if nextIfdOffsetToWrite > uint32(0) { + // fmt.Printf("Encoded IB: %s ADDRESSABLE-OFFSET=(0x%08x; %d):\n", ib.ii, ifdAddressableOffset, ifdAddressableOffset) + // fmt.Printf("\n") + // DumpBytes(b.Bytes()) + // fmt.Printf("\n") + // } - return b.Bytes(), tableSize, dataSize, childIfdSizes, nil + return b.Bytes(), tableSize, dataSize, childIfdSizes, nil } // encodeAndAttachIfd is a reentrant function that processes the IFD chain. func (ibe *IfdByteEncoder) encodeAndAttachIfd(ib *IfdBuilder, ifdAddressableOffset uint32) (data []byte, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - ibe.pushToJournal("encodeAndAttachIfd", ">", "%s", ib) + ibe.pushToJournal("encodeAndAttachIfd", ">", "%s", ib) - if len(ib.tags) == 0 { - log.Panicf("trying to encode an IfdBuilder that doesn't have any tags") - } + if len(ib.tags) == 0 { + log.Panicf("trying to encode an IfdBuilder that doesn't have any tags") + } - b := new(bytes.Buffer) + b := new(bytes.Buffer) - i := 0 + i := 0 -// TODO(dustin): !! We suspect there's an issue with encoding sibling IFDs, here. + // TODO(dustin): !! We suspect there's an issue with encoding sibling IFDs, here. - for thisIb := ib; thisIb != nil; thisIb = thisIb.nextIb { + for thisIb := ib; thisIb != nil; thisIb = thisIb.nextIb { + // Do a dry-run in order to pre-determine its size requirement. - // Do a dry-run in order to pre-determine its size requirement. + ibe.pushToJournal("encodeAndAttachIfd", ">", "Beginning encoding process: (%d) [%s]", i, thisIb.ii.IfdName) - ibe.pushToJournal("encodeAndAttachIfd", ">", "Beginning encoding process: (%d) [%s]", i, thisIb.ii.IfdName) + ibe.pushToJournal("encodeAndAttachIfd", ">", "Calculating size: (%d) [%s]", i, thisIb.ii.IfdName) - ibe.pushToJournal("encodeAndAttachIfd", ">", "Calculating size: (%d) [%s]", i, thisIb.ii.IfdName) + _, tableSize, allocatedDataSize, _, err := ibe.encodeIfdToBytes(thisIb, ifdAddressableOffset, 0, false) + log.PanicIf(err) - _, tableSize, allocatedDataSize, _, err := ibe.encodeIfdToBytes(thisIb, ifdAddressableOffset, 0, false) - log.PanicIf(err) + ibe.pushToJournal("encodeAndAttachIfd", "<", "Finished calculating size: (%d) [%s]", i, thisIb.ii.IfdName) - ibe.pushToJournal("encodeAndAttachIfd", "<", "Finished calculating size: (%d) [%s]", i, thisIb.ii.IfdName) + ifdAddressableOffset += tableSize + nextIfdOffsetToWrite := ifdAddressableOffset + allocatedDataSize - ifdAddressableOffset += tableSize - nextIfdOffsetToWrite := ifdAddressableOffset + allocatedDataSize + ibe.pushToJournal("encodeAndAttachIfd", ">", "Next IFD will be written at offset (0x%08x)", nextIfdOffsetToWrite) - ibe.pushToJournal("encodeAndAttachIfd", ">", "Next IFD will be written at offset (0x%08x)", nextIfdOffsetToWrite) + // Write our IFD as well as any child-IFDs (now that we know the offset + // where new IFDs and their data will be allocated). - // Write our IFD as well as any child-IFDs (now that we know the offset - // where new IFDs and their data will be allocated). + setNextIb := thisIb.nextIb != nil - setNextIb := thisIb.nextIb != nil + ibe.pushToJournal("encodeAndAttachIfd", ">", "Encoding starting: (%d) [%s] NEXT-IFD-OFFSET-TO-WRITE=(0x%08x)", i, thisIb.ii.IfdName, nextIfdOffsetToWrite) - ibe.pushToJournal("encodeAndAttachIfd", ">", "Encoding starting: (%d) [%s] NEXT-IFD-OFFSET-TO-WRITE=(0x%08x)", i, thisIb.ii.IfdName, nextIfdOffsetToWrite) + tableAndAllocated, effectiveTableSize, effectiveAllocatedDataSize, childIfdSizes, err := + ibe.encodeIfdToBytes(thisIb, ifdAddressableOffset, nextIfdOffsetToWrite, setNextIb) - tableAndAllocated, effectiveTableSize, effectiveAllocatedDataSize, childIfdSizes, err := - ibe.encodeIfdToBytes(thisIb, ifdAddressableOffset, nextIfdOffsetToWrite, setNextIb) + log.PanicIf(err) - log.PanicIf(err) + if effectiveTableSize != tableSize { + log.Panicf("written table size does not match the pre-calculated table size: (%d) != (%d) %s", effectiveTableSize, tableSize, ib) + } else if effectiveAllocatedDataSize != allocatedDataSize { + log.Panicf("written allocated-data size does not match the pre-calculated allocated-data size: (%d) != (%d) %s", effectiveAllocatedDataSize, allocatedDataSize, ib) + } - if effectiveTableSize != tableSize { - log.Panicf("written table size does not match the pre-calculated table size: (%d) != (%d) %s", effectiveTableSize, tableSize, ib) - } else if effectiveAllocatedDataSize != allocatedDataSize { - log.Panicf("written allocated-data size does not match the pre-calculated allocated-data size: (%d) != (%d) %s", effectiveAllocatedDataSize, allocatedDataSize, ib) - } + ibe.pushToJournal("encodeAndAttachIfd", "<", "Encoding done: (%d) [%s]", i, thisIb.ii.IfdName) - ibe.pushToJournal("encodeAndAttachIfd", "<", "Encoding done: (%d) [%s]", i, thisIb.ii.IfdName) + totalChildIfdSize := uint32(0) + for _, childIfdSize := range childIfdSizes { + totalChildIfdSize += childIfdSize + } - totalChildIfdSize := uint32(0) - for _, childIfdSize := range childIfdSizes { - totalChildIfdSize += childIfdSize - } + if len(tableAndAllocated) != int(tableSize+allocatedDataSize+totalChildIfdSize) { + log.Panicf("IFD table and data is not a consistent size: (%d) != (%d)", len(tableAndAllocated), tableSize+allocatedDataSize+totalChildIfdSize) + } - if len(tableAndAllocated) != int(tableSize + allocatedDataSize + totalChildIfdSize) { - log.Panicf("IFD table and data is not a consistent size: (%d) != (%d)", len(tableAndAllocated), tableSize + allocatedDataSize + totalChildIfdSize) - } + // TODO(dustin): !! We might want to verify the original tableAndAllocated length, too. -// TODO(dustin): !! We might want to verify the original tableAndAllocated length, too. + _, err = b.Write(tableAndAllocated) + log.PanicIf(err) - _, err = b.Write(tableAndAllocated) - log.PanicIf(err) + // Advance past what we've allocated, thus far. - // Advance past what we've allocated, thus far. + // TODO(dustin): !! If this doesn't work (or doesn't work easily), we may need to send a complex type for the addressable-offset instead of a simple integer. + ifdAddressableOffset += allocatedDataSize + totalChildIfdSize -// TODO(dustin): !! If this doesn't work (or doesn't work easily), we may need to send a complex type for the addressable-offset instead of a simple integer. - ifdAddressableOffset += allocatedDataSize + totalChildIfdSize + ibe.pushToJournal("encodeAndAttachIfd", "<", "Finishing encoding process: (%d) [%s] [FINAL:] NEXT-IFD-OFFSET-TO-WRITE=(0x%08x)", i, ib.ii.IfdName, nextIfdOffsetToWrite) - ibe.pushToJournal("encodeAndAttachIfd", "<", "Finishing encoding process: (%d) [%s] [FINAL:] NEXT-IFD-OFFSET-TO-WRITE=(0x%08x)", i, ib.ii.IfdName, nextIfdOffsetToWrite) + i++ + } - i++ - } + ibe.pushToJournal("encodeAndAttachIfd", "<", "%s", ib) - ibe.pushToJournal("encodeAndAttachIfd", "<", "%s", ib) - - return b.Bytes(), nil + return b.Bytes(), nil } // EncodeToExifPayload is the base encoding step that transcribes the entire IB // structure to its on-disk layout. func (ibe *IfdByteEncoder) EncodeToExifPayload(ib *IfdBuilder) (data []byte, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - data, err = ibe.encodeAndAttachIfd(ib, ExifDefaultFirstIfdOffset) - log.PanicIf(err) + data, err = ibe.encodeAndAttachIfd(ib, ExifDefaultFirstIfdOffset) + log.PanicIf(err) - return data, nil + return data, nil } // EncodeToExif calls EncodeToExifPayload and then packages the result into a // complete EXIF block. func (ibe *IfdByteEncoder) EncodeToExif(ib *IfdBuilder) (data []byte, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - encodedIfds, err := ibe.EncodeToExifPayload(ib) - log.PanicIf(err) + encodedIfds, err := ibe.EncodeToExifPayload(ib) + log.PanicIf(err) - // Wrap the IFD in a formal EXIF block. + // Wrap the IFD in a formal EXIF block. - b := new(bytes.Buffer) + b := new(bytes.Buffer) - headerBytes, err := BuildExifHeader(ib.byteOrder, ExifDefaultFirstIfdOffset) - log.PanicIf(err) + headerBytes, err := BuildExifHeader(ib.byteOrder, ExifDefaultFirstIfdOffset) + log.PanicIf(err) - _, err = b.Write(headerBytes) - log.PanicIf(err) + _, err = b.Write(headerBytes) + log.PanicIf(err) - _, err = b.Write(encodedIfds) - log.PanicIf(err) + _, err = b.Write(encodedIfds) + log.PanicIf(err) - return b.Bytes(), nil + return b.Bytes(), nil } diff --git a/ifd_builder_test.go b/ifd_builder_test.go index b5650ac..0893a08 100644 --- a/ifd_builder_test.go +++ b/ifd_builder_test.go @@ -1,1737 +1,1703 @@ package exif import ( - "testing" - "reflect" - "bytes" - "path" - "fmt" - "strings" + "bytes" + "fmt" + "path" + "reflect" + "strings" + "testing" - "github.com/dsoprea/go-logging" + "github.com/dsoprea/go-logging" ) func TestAdd(t *testing.T) { - ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) - bt := &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string")), - } + bt := &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string")), + } - err := ib.Add(bt) - log.PanicIf(err) + err := ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x22, - value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x22, + value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x33, - value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x33, + value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - originalBytes := []byte { 0x11, 0x22, 0x33 } + originalBytes := []byte{0x11, 0x22, 0x33} - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x44, - value: NewIfdBuilderTagValueFromBytes([]byte(originalBytes)), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x44, + value: NewIfdBuilderTagValueFromBytes([]byte(originalBytes)), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - if ib.ii != RootIi { - t.Fatalf("IFD name not correct.") - } else if ib.ifdTagId != 0 { - t.Fatalf("IFD tag-ID not correct.") - } else if ib.byteOrder != TestDefaultByteOrder { - t.Fatalf("IFD byte-order not correct.") - } else if len(ib.tags) != 4 { - t.Fatalf("IFD tag-count not correct.") - } else if ib.existingOffset != 0 { - t.Fatalf("IFD offset not correct.") - } else if ib.nextIb != nil { - t.Fatalf("Next-IFD not correct.") - } + if ib.ii != RootIi { + t.Fatalf("IFD name not correct.") + } else if ib.ifdTagId != 0 { + t.Fatalf("IFD tag-ID not correct.") + } else if ib.byteOrder != TestDefaultByteOrder { + t.Fatalf("IFD byte-order not correct.") + } else if len(ib.tags) != 4 { + t.Fatalf("IFD tag-count not correct.") + } else if ib.existingOffset != 0 { + t.Fatalf("IFD offset not correct.") + } else if ib.nextIb != nil { + t.Fatalf("Next-IFD not correct.") + } - tags := ib.Tags() + tags := ib.Tags() - if tags[0].tagId != 0x11 { - t.Fatalf("tag (0) tag-ID not correct") - } else if bytes.Compare(tags[0].value.Bytes(), []byte("test string")) != 0 { - t.Fatalf("tag (0) value not correct") - } + if tags[0].tagId != 0x11 { + t.Fatalf("tag (0) tag-ID not correct") + } else if bytes.Compare(tags[0].value.Bytes(), []byte("test string")) != 0 { + t.Fatalf("tag (0) value not correct") + } - if tags[1].tagId != 0x22 { - t.Fatalf("tag (1) tag-ID not correct") - } else if bytes.Compare(tags[1].value.Bytes(), []byte("test string2")) != 0 { - t.Fatalf("tag (1) value not correct") - } + if tags[1].tagId != 0x22 { + t.Fatalf("tag (1) tag-ID not correct") + } else if bytes.Compare(tags[1].value.Bytes(), []byte("test string2")) != 0 { + t.Fatalf("tag (1) value not correct") + } - if tags[2].tagId != 0x33 { - t.Fatalf("tag (2) tag-ID not correct") - } else if bytes.Compare(tags[2].value.Bytes(), []byte("test string3")) != 0 { - t.Fatalf("tag (2) value not correct") - } + if tags[2].tagId != 0x33 { + t.Fatalf("tag (2) tag-ID not correct") + } else if bytes.Compare(tags[2].value.Bytes(), []byte("test string3")) != 0 { + t.Fatalf("tag (2) value not correct") + } - if tags[3].tagId != 0x44 { - t.Fatalf("tag (3) tag-ID not correct") - } else if bytes.Compare(tags[3].value.Bytes(), originalBytes) != 0 { - t.Fatalf("tag (3) value not correct") - } + if tags[3].tagId != 0x44 { + t.Fatalf("tag (3) tag-ID not correct") + } else if bytes.Compare(tags[3].value.Bytes(), originalBytes) != 0 { + t.Fatalf("tag (3) value not correct") + } } func TestSetNextIb(t *testing.T) { - ib1 := NewIfdBuilder(RootIi, TestDefaultByteOrder) - ib2 := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib1 := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib2 := NewIfdBuilder(RootIi, TestDefaultByteOrder) - if ib1.nextIb != nil { - t.Fatalf("Next-IFD for IB1 not initially terminal.") - } + if ib1.nextIb != nil { + t.Fatalf("Next-IFD for IB1 not initially terminal.") + } - err := ib1.SetNextIb(ib2) - log.PanicIf(err) + err := ib1.SetNextIb(ib2) + log.PanicIf(err) - if ib1.nextIb != ib2 { - t.Fatalf("Next-IFD for IB1 not correct.") - } else if ib2.nextIb != nil { - t.Fatalf("Next-IFD for IB2 terminal.") - } + if ib1.nextIb != ib2 { + t.Fatalf("Next-IFD for IB1 not correct.") + } else if ib2.nextIb != nil { + t.Fatalf("Next-IFD for IB2 terminal.") + } } func TestAddChildIb(t *testing.T) { - ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) - bt := &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string")), - } + bt := &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string")), + } - err := ib.Add(bt) - log.PanicIf(err) + err := ib.Add(bt) + log.PanicIf(err) - exifIi, _ := IfdIdOrFail(IfdStandard, IfdExif) + exifIi, _ := IfdIdOrFail(IfdStandard, IfdExif) - ibChild := NewIfdBuilder(exifIi, TestDefaultByteOrder) - err = ib.AddChildIb(ibChild) - log.PanicIf(err) + ibChild := NewIfdBuilder(exifIi, TestDefaultByteOrder) + err = ib.AddChildIb(ibChild) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x22, - value: NewIfdBuilderTagValueFromBytes([]byte("test string")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x22, + value: NewIfdBuilderTagValueFromBytes([]byte("test string")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - if ib.tags[0].tagId != 0x11 { - t.Fatalf("first tag not correct") - } else if ib.tags[1].tagId != ibChild.ifdTagId { - t.Fatalf("second tag ID does not match child-IFD tag-ID: (0x%04x) != (0x%04x)", ib.tags[1].tagId, ibChild.ifdTagId) - } else if ib.tags[1].value.Ib() != ibChild { - t.Fatalf("second tagvalue does not match child-IFD") - } else if ib.tags[2].tagId != 0x22 { - t.Fatalf("third tag not correct") - } + if ib.tags[0].tagId != 0x11 { + t.Fatalf("first tag not correct") + } else if ib.tags[1].tagId != ibChild.ifdTagId { + t.Fatalf("second tag ID does not match child-IFD tag-ID: (0x%04x) != (0x%04x)", ib.tags[1].tagId, ibChild.ifdTagId) + } else if ib.tags[1].value.Ib() != ibChild { + t.Fatalf("second tagvalue does not match child-IFD") + } else if ib.tags[2].tagId != 0x22 { + t.Fatalf("third tag not correct") + } } func TestAddTagsFromExisting(t *testing.T) { - defer func() { - if state := recover(); state != nil { - err := log.Wrap(state.(error)) - log.PrintErrorf(err, "Test failure.") - } - }() + defer func() { + if state := recover(); state != nil { + err := log.Wrap(state.(error)) + log.PrintErrorf(err, "Test failure.") + } + }() - ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) - entries := make([]*IfdTagEntry, 3) + entries := make([]*IfdTagEntry, 3) - entries[0] = &IfdTagEntry{ - Ii: ExifIi, - TagId: 0x11, - TagType: TypeByte, - UnitCount: 4, - RawValueOffset: []byte { 0x12, 0, 0, 0 }, - } + entries[0] = &IfdTagEntry{ + Ii: ExifIi, + TagId: 0x11, + TagType: TypeByte, + UnitCount: 4, + RawValueOffset: []byte{0x12, 0, 0, 0}, + } - entries[1] = &IfdTagEntry{ - Ii: ExifIi, - TagId: 0x22, - TagType: TypeLong, - ChildIfdName: "some ifd", - } + entries[1] = &IfdTagEntry{ + Ii: ExifIi, + TagId: 0x22, + TagType: TypeLong, + ChildIfdName: "some ifd", + } - entries[2] = &IfdTagEntry{ - Ii: ExifIi, - TagId: 0x33, - TagType: TypeByte, - UnitCount: 4, - RawValueOffset: []byte { 0x34, 0, 0, 0 }, - } + entries[2] = &IfdTagEntry{ + Ii: ExifIi, + TagId: 0x33, + TagType: TypeByte, + UnitCount: 4, + RawValueOffset: []byte{0x34, 0, 0, 0}, + } - ifd := &Ifd{ - Ii: RootIi, - Entries: entries, - } + ifd := &Ifd{ + Ii: RootIi, + Entries: entries, + } - err := ib.AddTagsFromExisting(ifd, nil, nil, nil) - log.PanicIf(err) + err := ib.AddTagsFromExisting(ifd, nil, nil, nil) + log.PanicIf(err) - if ib.tags[0].tagId != 0x11 { - t.Fatalf("tag (0) not correct") - } else if ib.tags[1].tagId != 0x22 { - t.Fatalf("tag (1) not correct") - } else if ib.tags[2].tagId != 0x33 { - t.Fatalf("tag (2) not correct") - } else if len(ib.tags) != 3 { - t.Fatalf("tag count not correct") - } + if ib.tags[0].tagId != 0x11 { + t.Fatalf("tag (0) not correct") + } else if ib.tags[1].tagId != 0x22 { + t.Fatalf("tag (1) not correct") + } else if ib.tags[2].tagId != 0x33 { + t.Fatalf("tag (2) not correct") + } else if len(ib.tags) != 3 { + t.Fatalf("tag count not correct") + } } func TestAddTagsFromExisting__Includes(t *testing.T) { - ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) - entries := make([]*IfdTagEntry, 3) + entries := make([]*IfdTagEntry, 3) - entries[0] = &IfdTagEntry{ - Ii: RootIi, - TagType: TypeByte, - TagId: 0x11, - } + entries[0] = &IfdTagEntry{ + Ii: RootIi, + TagType: TypeByte, + TagId: 0x11, + } - entries[1] = &IfdTagEntry{ - Ii: RootIi, - TagType: TypeByte, - TagId: 0x22, - ChildIfdName: "some ifd", - } + entries[1] = &IfdTagEntry{ + Ii: RootIi, + TagType: TypeByte, + TagId: 0x22, + ChildIfdName: "some ifd", + } - entries[2] = &IfdTagEntry{ - Ii: RootIi, - TagType: TypeByte, - TagId: 0x33, - } + entries[2] = &IfdTagEntry{ + Ii: RootIi, + TagType: TypeByte, + TagId: 0x33, + } - ifd := &Ifd{ - Ii: RootIi, - Entries: entries, - } + ifd := &Ifd{ + Ii: RootIi, + Entries: entries, + } - err := ib.AddTagsFromExisting(ifd, nil, []uint16 { 0x33 }, nil) - log.PanicIf(err) + err := ib.AddTagsFromExisting(ifd, nil, []uint16{0x33}, nil) + log.PanicIf(err) - if ib.tags[0].tagId != 0x33 { - t.Fatalf("tag (1) not correct") - } else if len(ib.tags) != 1 { - t.Fatalf("tag count not correct") - } + if ib.tags[0].tagId != 0x33 { + t.Fatalf("tag (1) not correct") + } else if len(ib.tags) != 1 { + t.Fatalf("tag count not correct") + } } func TestAddTagsFromExisting__Excludes(t *testing.T) { - ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) - entries := make([]*IfdTagEntry, 3) + entries := make([]*IfdTagEntry, 3) - entries[0] = &IfdTagEntry{ - Ii: RootIi, - TagType: TypeByte, - TagId: 0x11, - } + entries[0] = &IfdTagEntry{ + Ii: RootIi, + TagType: TypeByte, + TagId: 0x11, + } - entries[1] = &IfdTagEntry{ - Ii: RootIi, - TagType: TypeByte, - TagId: 0x22, - ChildIfdName: "some ifd", - } + entries[1] = &IfdTagEntry{ + Ii: RootIi, + TagType: TypeByte, + TagId: 0x22, + ChildIfdName: "some ifd", + } - entries[2] = &IfdTagEntry{ - Ii: RootIi, - TagType: TypeByte, - TagId: 0x33, - } + entries[2] = &IfdTagEntry{ + Ii: RootIi, + TagType: TypeByte, + TagId: 0x33, + } - ifd := &Ifd{ - Ii: RootIi, - Entries: entries, - } + ifd := &Ifd{ + Ii: RootIi, + Entries: entries, + } - err := ib.AddTagsFromExisting(ifd, nil, nil, []uint16 { 0x11 }) - log.PanicIf(err) + err := ib.AddTagsFromExisting(ifd, nil, nil, []uint16{0x11}) + log.PanicIf(err) - if ib.tags[0].tagId != 0x22 { - t.Fatalf("tag not correct") - } else if len(ib.tags) != 2 { - t.Fatalf("tag count not correct") - } + if ib.tags[0].tagId != 0x22 { + t.Fatalf("tag not correct") + } else if len(ib.tags) != 2 { + t.Fatalf("tag count not correct") + } } func TestFindN_First_1(t *testing.T) { - ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) - bt := &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string")), - } + bt := &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string")), + } - err := ib.Add(bt) - log.PanicIf(err) + err := ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x22, - value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x22, + value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x33, - value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x33, + value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - found, err := ib.FindN(0x11, 1) - log.PanicIf(err) + found, err := ib.FindN(0x11, 1) + log.PanicIf(err) - if len(found) != 1 { - log.Panicf("Exactly one result was not found: (%d)", len(found)) - } else if found[0] != 0 { - log.Panicf("Result was not in the right place: (%d)", found[0]) - } + if len(found) != 1 { + log.Panicf("Exactly one result was not found: (%d)", len(found)) + } else if found[0] != 0 { + log.Panicf("Result was not in the right place: (%d)", found[0]) + } - tags := ib.Tags() - bt = tags[found[0]] + tags := ib.Tags() + bt = tags[found[0]] - if bt.tagId != 0x11 { - log.Panicf("Found entry is not correct: (0x%04x)", bt.tagId) - } + if bt.tagId != 0x11 { + log.Panicf("Found entry is not correct: (0x%04x)", bt.tagId) + } } func TestFindN_First_2_1Returned(t *testing.T) { - ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) - bt := &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string")), - } + bt := &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string")), + } - err := ib.Add(bt) - log.PanicIf(err) + err := ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x22, - value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x22, + value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x33, - value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x33, + value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - found, err := ib.FindN(0x11, 2) - log.PanicIf(err) + found, err := ib.FindN(0x11, 2) + log.PanicIf(err) - if len(found) != 1 { - log.Panicf("Exactly one result was not found: (%d)", len(found)) - } else if found[0] != 0 { - log.Panicf("Result was not in the right place: (%d)", found[0]) - } + if len(found) != 1 { + log.Panicf("Exactly one result was not found: (%d)", len(found)) + } else if found[0] != 0 { + log.Panicf("Result was not in the right place: (%d)", found[0]) + } - tags := ib.Tags() - bt = tags[found[0]] + tags := ib.Tags() + bt = tags[found[0]] - if bt.tagId != 0x11 { - log.Panicf("Found entry is not correct: (0x%04x)", bt.tagId) - } + if bt.tagId != 0x11 { + log.Panicf("Found entry is not correct: (0x%04x)", bt.tagId) + } } func TestFindN_First_2_2Returned(t *testing.T) { - ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) - bt := &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string")), - } + bt := &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string")), + } - err := ib.Add(bt) - log.PanicIf(err) + err := ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x22, - value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x22, + value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x33, - value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x33, + value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string5")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string5")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - found, err := ib.FindN(0x11, 2) - log.PanicIf(err) + found, err := ib.FindN(0x11, 2) + log.PanicIf(err) - if len(found) != 2 { - log.Panicf("Exactly one result was not found: (%d)", len(found)) - } else if found[0] != 0 { - log.Panicf("First result was not in the right place: (%d)", found[0]) - } else if found[1] != 3 { - log.Panicf("Second result was not in the right place: (%d)", found[1]) - } + if len(found) != 2 { + log.Panicf("Exactly one result was not found: (%d)", len(found)) + } else if found[0] != 0 { + log.Panicf("First result was not in the right place: (%d)", found[0]) + } else if found[1] != 3 { + log.Panicf("Second result was not in the right place: (%d)", found[1]) + } - tags := ib.Tags() + tags := ib.Tags() - bt = tags[found[0]] - if bt.tagId != 0x11 || bytes.Compare(bt.value.Bytes(), []byte("test string")) != 0 { - log.Panicf("Found entry 0 is not correct: (0x%04x) [%s]", bt.tagId, bt.value) - } + bt = tags[found[0]] + if bt.tagId != 0x11 || bytes.Compare(bt.value.Bytes(), []byte("test string")) != 0 { + log.Panicf("Found entry 0 is not correct: (0x%04x) [%s]", bt.tagId, bt.value) + } - bt = tags[found[1]] - if bt.tagId != 0x11 || bytes.Compare(bt.value.Bytes(), []byte("test string4")) != 0 { - log.Panicf("Found entry 1 is not correct: (0x%04x) [%s]", bt.tagId, bt.value) - } + bt = tags[found[1]] + if bt.tagId != 0x11 || bytes.Compare(bt.value.Bytes(), []byte("test string4")) != 0 { + log.Panicf("Found entry 1 is not correct: (0x%04x) [%s]", bt.tagId, bt.value) + } } func TestFindN_Middle_WithDuplicates(t *testing.T) { - ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) - bt := &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string")), - } + bt := &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string")), + } - err := ib.Add(bt) - log.PanicIf(err) + err := ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x22, - value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x22, + value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x33, - value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x33, + value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string5")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string5")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x33, - value: NewIfdBuilderTagValueFromBytes([]byte("test string6")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x33, + value: NewIfdBuilderTagValueFromBytes([]byte("test string6")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - found, err := ib.FindN(0x33, 1) - log.PanicIf(err) + found, err := ib.FindN(0x33, 1) + log.PanicIf(err) - if len(found) != 1 { - log.Panicf("Exactly one result was not found: (%d)", len(found)) - } else if found[0] != 2 { - log.Panicf("Result was not in the right place: (%d)", found[0]) - } + if len(found) != 1 { + log.Panicf("Exactly one result was not found: (%d)", len(found)) + } else if found[0] != 2 { + log.Panicf("Result was not in the right place: (%d)", found[0]) + } - tags := ib.Tags() - bt = tags[found[0]] + tags := ib.Tags() + bt = tags[found[0]] - if bt.tagId != 0x33 { - log.Panicf("Found entry is not correct: (0x%04x)", bt.tagId) - } + if bt.tagId != 0x33 { + log.Panicf("Found entry is not correct: (0x%04x)", bt.tagId) + } } func TestFindN_Middle_NoDuplicates(t *testing.T) { - ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) - bt := &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string")), - } + bt := &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string")), + } - err := ib.Add(bt) - log.PanicIf(err) + err := ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x22, - value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x22, + value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x33, - value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x33, + value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - found, err := ib.FindN(0x33, 1) - log.PanicIf(err) + found, err := ib.FindN(0x33, 1) + log.PanicIf(err) - if len(found) != 1 { - log.Panicf("Exactly one result was not found: (%d)", len(found)) - } else if found[0] != 2 { - log.Panicf("Result was not in the right place: (%d)", found[0]) - } + if len(found) != 1 { + log.Panicf("Exactly one result was not found: (%d)", len(found)) + } else if found[0] != 2 { + log.Panicf("Result was not in the right place: (%d)", found[0]) + } - tags := ib.Tags() - bt = tags[found[0]] + tags := ib.Tags() + bt = tags[found[0]] - if bt.tagId != 0x33 { - log.Panicf("Found entry is not correct: (0x%04x)", bt.tagId) - } + if bt.tagId != 0x33 { + log.Panicf("Found entry is not correct: (0x%04x)", bt.tagId) + } } func TestFindN_Miss(t *testing.T) { - ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) - found, err := ib.FindN(0x11, 1) - log.PanicIf(err) + found, err := ib.FindN(0x11, 1) + log.PanicIf(err) - if len(found) != 0 { - t.Fatalf("Expected empty results.") - } + if len(found) != 0 { + t.Fatalf("Expected empty results.") + } } func TestFind_Hit(t *testing.T) { - ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) - bt := &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string")), - } + bt := &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string")), + } - err := ib.Add(bt) - log.PanicIf(err) + err := ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x22, - value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x22, + value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x33, - value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x33, + value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - position, err := ib.Find(0x33) - log.PanicIf(err) + position, err := ib.Find(0x33) + log.PanicIf(err) - if position != 2 { - log.Panicf("Result was not in the right place: (%d)", position) - } + if position != 2 { + log.Panicf("Result was not in the right place: (%d)", position) + } - tags := ib.Tags() - bt = tags[position] + tags := ib.Tags() + bt = tags[position] - if bt.tagId != 0x33 { - log.Panicf("Found entry is not correct: (0x%04x)", bt.tagId) - } + if bt.tagId != 0x33 { + log.Panicf("Found entry is not correct: (0x%04x)", bt.tagId) + } } func TestFind_Miss(t *testing.T) { - ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) - bt := &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string")), - } + bt := &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string")), + } - err := ib.Add(bt) - log.PanicIf(err) + err := ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x22, - value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x22, + value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x33, - value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x33, + value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - _, err = ib.Find(0x99) - if err == nil { - t.Fatalf("Expected an error.") - } else if log.Is(err, ErrTagEntryNotFound) == false { - log.Panic(err) - } + _, err = ib.Find(0x99) + if err == nil { + t.Fatalf("Expected an error.") + } else if log.Is(err, ErrTagEntryNotFound) == false { + log.Panic(err) + } } func TestReplace(t *testing.T) { - ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) - bt := &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string")), - } + bt := &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string")), + } - err := ib.Add(bt) - log.PanicIf(err) + err := ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x22, - value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x22, + value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x33, - value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x33, + value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - currentIds := make([]uint16, 3) - for i, bt := range ib.Tags() { - currentIds[i] = bt.tagId - } + currentIds := make([]uint16, 3) + for i, bt := range ib.Tags() { + currentIds[i] = bt.tagId + } - if reflect.DeepEqual([]uint16 { 0x11, 0x22, 0x33 }, currentIds) == false { - t.Fatalf("Pre-replace tags are not correct.") - } + if reflect.DeepEqual([]uint16{0x11, 0x22, 0x33}, currentIds) == false { + t.Fatalf("Pre-replace tags are not correct.") + } - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x99, - value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x99, + value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), + } - err = ib.Replace(0x22, bt) - log.PanicIf(err) + err = ib.Replace(0x22, bt) + log.PanicIf(err) - currentIds = make([]uint16, 3) - for i, bt := range ib.Tags() { - currentIds[i] = bt.tagId - } + currentIds = make([]uint16, 3) + for i, bt := range ib.Tags() { + currentIds[i] = bt.tagId + } - if reflect.DeepEqual([]uint16 { 0x11, 0x99, 0x33 }, currentIds) == false { - t.Fatalf("Post-replace tags are not correct.") - } + if reflect.DeepEqual([]uint16{0x11, 0x99, 0x33}, currentIds) == false { + t.Fatalf("Post-replace tags are not correct.") + } } func TestReplaceN(t *testing.T) { - ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) - bt := &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string")), - } + bt := &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string")), + } - err := ib.Add(bt) - log.PanicIf(err) + err := ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x22, - value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x22, + value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x33, - value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x33, + value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - currentIds := make([]uint16, 3) - for i, bt := range ib.Tags() { - currentIds[i] = bt.tagId - } + currentIds := make([]uint16, 3) + for i, bt := range ib.Tags() { + currentIds[i] = bt.tagId + } - if reflect.DeepEqual([]uint16 { 0x11, 0x22, 0x33 }, currentIds) == false { - t.Fatalf("Pre-replace tags are not correct.") - } + if reflect.DeepEqual([]uint16{0x11, 0x22, 0x33}, currentIds) == false { + t.Fatalf("Pre-replace tags are not correct.") + } - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0xA9, - value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0xA9, + value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), + } - err = ib.ReplaceAt(1, bt) - log.PanicIf(err) + err = ib.ReplaceAt(1, bt) + log.PanicIf(err) - currentIds = make([]uint16, 3) - for i, bt := range ib.Tags() { - currentIds[i] = bt.tagId - } + currentIds = make([]uint16, 3) + for i, bt := range ib.Tags() { + currentIds[i] = bt.tagId + } - if reflect.DeepEqual([]uint16 { 0x11, 0xA9, 0x33 }, currentIds) == false { - t.Fatalf("Post-replace tags are not correct.") - } + if reflect.DeepEqual([]uint16{0x11, 0xA9, 0x33}, currentIds) == false { + t.Fatalf("Post-replace tags are not correct.") + } } func TestDeleteFirst(t *testing.T) { - ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) - bt := &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string")), - } + bt := &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string")), + } - err := ib.Add(bt) - log.PanicIf(err) + err := ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x22, - value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x22, + value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x22, - value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x22, + value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x33, - value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x33, + value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) + if len(ib.Tags()) != 4 { + t.Fatalf("Pre-delete tag count not correct.") + } - if len(ib.Tags()) != 4 { - t.Fatalf("Pre-delete tag count not correct.") - } + currentIds := make([]uint16, 4) + for i, bt := range ib.Tags() { + currentIds[i] = bt.tagId + } - currentIds := make([]uint16, 4) - for i, bt := range ib.Tags() { - currentIds[i] = bt.tagId - } + if reflect.DeepEqual([]uint16{0x11, 0x22, 0x22, 0x33}, currentIds) == false { + t.Fatalf("Pre-delete tags not correct.") + } - if reflect.DeepEqual([]uint16 { 0x11, 0x22, 0x22, 0x33 }, currentIds) == false { - t.Fatalf("Pre-delete tags not correct.") - } + err = ib.DeleteFirst(0x22) + log.PanicIf(err) + if len(ib.Tags()) != 3 { + t.Fatalf("Post-delete (1) tag count not correct.") + } - err = ib.DeleteFirst(0x22) - log.PanicIf(err) + currentIds = make([]uint16, 3) + for i, bt := range ib.Tags() { + currentIds[i] = bt.tagId + } - if len(ib.Tags()) != 3 { - t.Fatalf("Post-delete (1) tag count not correct.") - } + if reflect.DeepEqual([]uint16{0x11, 0x22, 0x33}, currentIds) == false { + t.Fatalf("Post-delete (1) tags not correct.") + } - currentIds = make([]uint16, 3) - for i, bt := range ib.Tags() { - currentIds[i] = bt.tagId - } + err = ib.DeleteFirst(0x22) + log.PanicIf(err) - if reflect.DeepEqual([]uint16 { 0x11, 0x22, 0x33 }, currentIds) == false { - t.Fatalf("Post-delete (1) tags not correct.") - } + if len(ib.Tags()) != 2 { + t.Fatalf("Post-delete (2) tag count not correct.") + } + currentIds = make([]uint16, 2) + for i, bt := range ib.Tags() { + currentIds[i] = bt.tagId + } - err = ib.DeleteFirst(0x22) - log.PanicIf(err) + if reflect.DeepEqual([]uint16{0x11, 0x33}, currentIds) == false { + t.Fatalf("Post-delete (2) tags not correct.") + } - if len(ib.Tags()) != 2 { - t.Fatalf("Post-delete (2) tag count not correct.") - } - - currentIds = make([]uint16, 2) - for i, bt := range ib.Tags() { - currentIds[i] = bt.tagId - } - - if reflect.DeepEqual([]uint16 { 0x11, 0x33 }, currentIds) == false { - t.Fatalf("Post-delete (2) tags not correct.") - } - - - err = ib.DeleteFirst(0x22) - if err == nil { - t.Fatalf("Expected an error.") - } else if log.Is(err, ErrTagEntryNotFound) == false { - log.Panic(err) - } + err = ib.DeleteFirst(0x22) + if err == nil { + t.Fatalf("Expected an error.") + } else if log.Is(err, ErrTagEntryNotFound) == false { + log.Panic(err) + } } func TestDeleteN(t *testing.T) { - ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) - bt := &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string")), - } + bt := &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string")), + } - err := ib.Add(bt) - log.PanicIf(err) + err := ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x22, - value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x22, + value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x22, - value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x22, + value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x33, - value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x33, + value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) + if len(ib.Tags()) != 4 { + t.Fatalf("Pre-delete tag count not correct.") + } - if len(ib.Tags()) != 4 { - t.Fatalf("Pre-delete tag count not correct.") - } + currentIds := make([]uint16, 4) + for i, bt := range ib.Tags() { + currentIds[i] = bt.tagId + } - currentIds := make([]uint16, 4) - for i, bt := range ib.Tags() { - currentIds[i] = bt.tagId - } + if reflect.DeepEqual([]uint16{0x11, 0x22, 0x22, 0x33}, currentIds) == false { + t.Fatalf("Pre-delete tags not correct.") + } - if reflect.DeepEqual([]uint16 { 0x11, 0x22, 0x22, 0x33 }, currentIds) == false { - t.Fatalf("Pre-delete tags not correct.") - } + err = ib.DeleteN(0x22, 1) + log.PanicIf(err) + if len(ib.Tags()) != 3 { + t.Fatalf("Post-delete (1) tag count not correct.") + } - err = ib.DeleteN(0x22, 1) - log.PanicIf(err) + currentIds = make([]uint16, 3) + for i, bt := range ib.Tags() { + currentIds[i] = bt.tagId + } - if len(ib.Tags()) != 3 { - t.Fatalf("Post-delete (1) tag count not correct.") - } + if reflect.DeepEqual([]uint16{0x11, 0x22, 0x33}, currentIds) == false { + t.Fatalf("Post-delete (1) tags not correct.") + } - currentIds = make([]uint16, 3) - for i, bt := range ib.Tags() { - currentIds[i] = bt.tagId - } + err = ib.DeleteN(0x22, 1) + log.PanicIf(err) - if reflect.DeepEqual([]uint16 { 0x11, 0x22, 0x33 }, currentIds) == false { - t.Fatalf("Post-delete (1) tags not correct.") - } + if len(ib.Tags()) != 2 { + t.Fatalf("Post-delete (2) tag count not correct.") + } + currentIds = make([]uint16, 2) + for i, bt := range ib.Tags() { + currentIds[i] = bt.tagId + } - err = ib.DeleteN(0x22, 1) - log.PanicIf(err) + if reflect.DeepEqual([]uint16{0x11, 0x33}, currentIds) == false { + t.Fatalf("Post-delete (2) tags not correct.") + } - if len(ib.Tags()) != 2 { - t.Fatalf("Post-delete (2) tag count not correct.") - } - - currentIds = make([]uint16, 2) - for i, bt := range ib.Tags() { - currentIds[i] = bt.tagId - } - - if reflect.DeepEqual([]uint16 { 0x11, 0x33 }, currentIds) == false { - t.Fatalf("Post-delete (2) tags not correct.") - } - - - err = ib.DeleteN(0x22, 1) - if err == nil { - t.Fatalf("Expected an error.") - } else if log.Is(err, ErrTagEntryNotFound) == false { - log.Panic(err) - } + err = ib.DeleteN(0x22, 1) + if err == nil { + t.Fatalf("Expected an error.") + } else if log.Is(err, ErrTagEntryNotFound) == false { + log.Panic(err) + } } func TestDeleteN_Two(t *testing.T) { - ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) - bt := &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string")), - } + bt := &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string")), + } - err := ib.Add(bt) - log.PanicIf(err) + err := ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x22, - value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x22, + value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x22, - value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x22, + value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x33, - value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x33, + value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) + if len(ib.Tags()) != 4 { + t.Fatalf("Pre-delete tag count not correct.") + } - if len(ib.Tags()) != 4 { - t.Fatalf("Pre-delete tag count not correct.") - } + currentIds := make([]uint16, 4) + for i, bt := range ib.Tags() { + currentIds[i] = bt.tagId + } - currentIds := make([]uint16, 4) - for i, bt := range ib.Tags() { - currentIds[i] = bt.tagId - } + if reflect.DeepEqual([]uint16{0x11, 0x22, 0x22, 0x33}, currentIds) == false { + t.Fatalf("Pre-delete tags not correct.") + } - if reflect.DeepEqual([]uint16 { 0x11, 0x22, 0x22, 0x33 }, currentIds) == false { - t.Fatalf("Pre-delete tags not correct.") - } + err = ib.DeleteN(0x22, 2) + log.PanicIf(err) + if len(ib.Tags()) != 2 { + t.Fatalf("Post-delete tag count not correct.") + } - err = ib.DeleteN(0x22, 2) - log.PanicIf(err) + currentIds = make([]uint16, 2) + for i, bt := range ib.Tags() { + currentIds[i] = bt.tagId + } - if len(ib.Tags()) != 2 { - t.Fatalf("Post-delete tag count not correct.") - } + if reflect.DeepEqual([]uint16{0x11, 0x33}, currentIds) == false { + t.Fatalf("Post-delete tags not correct.") + } - currentIds = make([]uint16, 2) - for i, bt := range ib.Tags() { - currentIds[i] = bt.tagId - } - - if reflect.DeepEqual([]uint16 { 0x11, 0x33 }, currentIds) == false { - t.Fatalf("Post-delete tags not correct.") - } - - - err = ib.DeleteFirst(0x22) - if err == nil { - t.Fatalf("Expected an error.") - } else if log.Is(err, ErrTagEntryNotFound) == false { - log.Panic(err) - } + err = ib.DeleteFirst(0x22) + if err == nil { + t.Fatalf("Expected an error.") + } else if log.Is(err, ErrTagEntryNotFound) == false { + log.Panic(err) + } } func TestDeleteAll(t *testing.T) { - ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) - bt := &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x11, - value: NewIfdBuilderTagValueFromBytes([]byte("test string")), - } + bt := &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x11, + value: NewIfdBuilderTagValueFromBytes([]byte("test string")), + } - err := ib.Add(bt) - log.PanicIf(err) + err := ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x22, - value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x22, + value: NewIfdBuilderTagValueFromBytes([]byte("test string2")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x22, - value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x22, + value: NewIfdBuilderTagValueFromBytes([]byte("test string3")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) - bt = &builderTag{ - ii: RootIi, - typeId: TypeByte, - tagId: 0x33, - value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), - } + bt = &BuilderTag{ + ii: RootIi, + typeId: TypeByte, + tagId: 0x33, + value: NewIfdBuilderTagValueFromBytes([]byte("test string4")), + } - err = ib.Add(bt) - log.PanicIf(err) + err = ib.Add(bt) + log.PanicIf(err) + if len(ib.Tags()) != 4 { + t.Fatalf("Pre-delete tag count not correct.") + } - if len(ib.Tags()) != 4 { - t.Fatalf("Pre-delete tag count not correct.") - } + currentIds := make([]uint16, 4) + for i, bt := range ib.Tags() { + currentIds[i] = bt.tagId + } - currentIds := make([]uint16, 4) - for i, bt := range ib.Tags() { - currentIds[i] = bt.tagId - } + if reflect.DeepEqual([]uint16{0x11, 0x22, 0x22, 0x33}, currentIds) == false { + t.Fatalf("Pre-delete tags not correct.") + } - if reflect.DeepEqual([]uint16 { 0x11, 0x22, 0x22, 0x33 }, currentIds) == false { - t.Fatalf("Pre-delete tags not correct.") - } + n, err := ib.DeleteAll(0x22) + log.PanicIf(err) + if n != 2 { + t.Fatalf("Returned delete tag count not correct.") + } else if len(ib.Tags()) != 2 { + t.Fatalf("Post-delete tag count not correct.") + } - n, err := ib.DeleteAll(0x22) - log.PanicIf(err) + currentIds = make([]uint16, 2) + for i, bt := range ib.Tags() { + currentIds[i] = bt.tagId + } - if n != 2 { - t.Fatalf("Returned delete tag count not correct.") - } else if len(ib.Tags()) != 2 { - t.Fatalf("Post-delete tag count not correct.") - } + if reflect.DeepEqual([]uint16{0x11, 0x33}, currentIds) == false { + t.Fatalf("Post-delete tags not correct.") + } - currentIds = make([]uint16, 2) - for i, bt := range ib.Tags() { - currentIds[i] = bt.tagId - } - - if reflect.DeepEqual([]uint16 { 0x11, 0x33 }, currentIds) == false { - t.Fatalf("Post-delete tags not correct.") - } - - - err = ib.DeleteFirst(0x22) - if err == nil { - t.Fatalf("Expected an error.") - } else if log.Is(err, ErrTagEntryNotFound) == false { - log.Panic(err) - } + err = ib.DeleteFirst(0x22) + if err == nil { + t.Fatalf("Expected an error.") + } else if log.Is(err, ErrTagEntryNotFound) == false { + log.Panic(err) + } } func Test_IfdBuilder_CreateIfdBuilderFromExistingChain(t *testing.T) { - defer func() { - if state := recover(); state != nil { - err := log.Wrap(state.(error)) - log.PrintErrorf(err, "Test failure.") - } - }() + defer func() { + if state := recover(); state != nil { + err := log.Wrap(state.(error)) + log.PrintErrorf(err, "Test failure.") + } + }() - e := NewExif() + e := NewExif() - filepath := path.Join(assetsPath, "NDM_8901.jpg") + filepath := path.Join(assetsPath, "NDM_8901.jpg") - rawExif, err := e.SearchAndExtractExif(filepath) - log.PanicIf(err) + rawExif, err := e.SearchAndExtractExif(filepath) + log.PanicIf(err) - _, index, err := e.Collect(rawExif) - log.PanicIf(err) + _, index, err := e.Collect(rawExif) + log.PanicIf(err) - itevr := NewIfdTagEntryValueResolver(rawExif, index.RootIfd.ByteOrder) - ib := NewIfdBuilderFromExistingChain(index.RootIfd, itevr) + itevr := NewIfdTagEntryValueResolver(rawExif, index.RootIfd.ByteOrder) + ib := NewIfdBuilderFromExistingChain(index.RootIfd, itevr) - actual := ib.DumpToStrings() + actual := ib.DumpToStrings() - expected := []string { - " IFD-TAG-ID=(0x0000) CHILD-IFD=[] INDEX=(0) TAG=[0x010f]", - " IFD-TAG-ID=(0x0000) CHILD-IFD=[] INDEX=(1) TAG=[0x0110]", - " IFD-TAG-ID=(0x0000) CHILD-IFD=[] INDEX=(2) TAG=[0x0112]", - " IFD-TAG-ID=(0x0000) CHILD-IFD=[] INDEX=(3) TAG=[0x011a]", - " IFD-TAG-ID=(0x0000) CHILD-IFD=[] INDEX=(4) TAG=[0x011b]", - " IFD-TAG-ID=(0x0000) CHILD-IFD=[] INDEX=(5) TAG=[0x0128]", - " IFD-TAG-ID=(0x0000) CHILD-IFD=[] INDEX=(6) TAG=[0x0132]", - " IFD-TAG-ID=(0x0000) CHILD-IFD=[] INDEX=(7) TAG=[0x013b]", - " IFD-TAG-ID=(0x0000) CHILD-IFD=[] INDEX=(8) TAG=[0x0213]", - " IFD-TAG-ID=(0x0000) CHILD-IFD=[] INDEX=(9) TAG=[0x8298]", - " IFD-TAG-ID=(0x0000) CHILD-IFD=[Exif] INDEX=(10) TAG=[0x8769]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(0) TAG=[0x829a]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(1) TAG=[0x829d]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(2) TAG=[0x8822]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(3) TAG=[0x8827]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(4) TAG=[0x8830]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(5) TAG=[0x8832]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(6) TAG=[0x9000]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(7) TAG=[0x9003]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(8) TAG=[0x9004]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(9) TAG=[0x9101]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(10) TAG=[0x9201]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(11) TAG=[0x9202]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(12) TAG=[0x9204]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(13) TAG=[0x9207]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(14) TAG=[0x9209]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(15) TAG=[0x920a]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(16) TAG=[0x927c]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(17) TAG=[0x9286]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(18) TAG=[0x9290]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(19) TAG=[0x9291]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(20) TAG=[0x9292]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(21) TAG=[0xa000]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(22) TAG=[0xa001]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(23) TAG=[0xa002]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(24) TAG=[0xa003]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[Iop] INDEX=(25) TAG=[0xa005]", - "Exif] IFD-NAME=[Iop]> IFD-TAG-ID=(0xa005) CHILD-IFD=[] INDEX=(0) TAG=[0x0001]", - "Exif] IFD-NAME=[Iop]> IFD-TAG-ID=(0xa005) CHILD-IFD=[] INDEX=(1) TAG=[0x0002]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(26) TAG=[0xa20e]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(27) TAG=[0xa20f]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(28) TAG=[0xa210]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(29) TAG=[0xa401]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(30) TAG=[0xa402]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(31) TAG=[0xa403]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(32) TAG=[0xa406]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(33) TAG=[0xa430]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(34) TAG=[0xa431]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(35) TAG=[0xa432]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(36) TAG=[0xa434]", - " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(37) TAG=[0xa435]", - " IFD-TAG-ID=(0x0000) CHILD-IFD=[GPSInfo] INDEX=(11) TAG=[0x8825]", - " IFD-TAG-ID=(0x8825) CHILD-IFD=[] INDEX=(0) TAG=[0x0000]", - } + expected := []string{ + " IFD-TAG-ID=(0x0000) CHILD-IFD=[] INDEX=(0) TAG=[0x010f]", + " IFD-TAG-ID=(0x0000) CHILD-IFD=[] INDEX=(1) TAG=[0x0110]", + " IFD-TAG-ID=(0x0000) CHILD-IFD=[] INDEX=(2) TAG=[0x0112]", + " IFD-TAG-ID=(0x0000) CHILD-IFD=[] INDEX=(3) TAG=[0x011a]", + " IFD-TAG-ID=(0x0000) CHILD-IFD=[] INDEX=(4) TAG=[0x011b]", + " IFD-TAG-ID=(0x0000) CHILD-IFD=[] INDEX=(5) TAG=[0x0128]", + " IFD-TAG-ID=(0x0000) CHILD-IFD=[] INDEX=(6) TAG=[0x0132]", + " IFD-TAG-ID=(0x0000) CHILD-IFD=[] INDEX=(7) TAG=[0x013b]", + " IFD-TAG-ID=(0x0000) CHILD-IFD=[] INDEX=(8) TAG=[0x0213]", + " IFD-TAG-ID=(0x0000) CHILD-IFD=[] INDEX=(9) TAG=[0x8298]", + " IFD-TAG-ID=(0x0000) CHILD-IFD=[Exif] INDEX=(10) TAG=[0x8769]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(0) TAG=[0x829a]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(1) TAG=[0x829d]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(2) TAG=[0x8822]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(3) TAG=[0x8827]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(4) TAG=[0x8830]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(5) TAG=[0x8832]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(6) TAG=[0x9000]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(7) TAG=[0x9003]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(8) TAG=[0x9004]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(9) TAG=[0x9101]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(10) TAG=[0x9201]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(11) TAG=[0x9202]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(12) TAG=[0x9204]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(13) TAG=[0x9207]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(14) TAG=[0x9209]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(15) TAG=[0x920a]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(16) TAG=[0x927c]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(17) TAG=[0x9286]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(18) TAG=[0x9290]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(19) TAG=[0x9291]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(20) TAG=[0x9292]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(21) TAG=[0xa000]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(22) TAG=[0xa001]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(23) TAG=[0xa002]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(24) TAG=[0xa003]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[Iop] INDEX=(25) TAG=[0xa005]", + "Exif] IFD-NAME=[Iop]> IFD-TAG-ID=(0xa005) CHILD-IFD=[] INDEX=(0) TAG=[0x0001]", + "Exif] IFD-NAME=[Iop]> IFD-TAG-ID=(0xa005) CHILD-IFD=[] INDEX=(1) TAG=[0x0002]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(26) TAG=[0xa20e]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(27) TAG=[0xa20f]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(28) TAG=[0xa210]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(29) TAG=[0xa401]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(30) TAG=[0xa402]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(31) TAG=[0xa403]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(32) TAG=[0xa406]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(33) TAG=[0xa430]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(34) TAG=[0xa431]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(35) TAG=[0xa432]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(36) TAG=[0xa434]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(37) TAG=[0xa435]", + " IFD-TAG-ID=(0x0000) CHILD-IFD=[GPSInfo] INDEX=(11) TAG=[0x8825]", + " IFD-TAG-ID=(0x8825) CHILD-IFD=[] INDEX=(0) TAG=[0x0000]", + } - if reflect.DeepEqual(actual, expected) == false { - fmt.Printf("ACTUAL:\n%s\n\nEXPECTED:\n%s\n", strings.Join(actual, "\n"), strings.Join(expected, "\n")) - t.Fatalf("IB did not [correctly] duplicate the IFD structure.") - } + if reflect.DeepEqual(actual, expected) == false { + fmt.Printf("ACTUAL:\n%s\n\nEXPECTED:\n%s\n", strings.Join(actual, "\n"), strings.Join(expected, "\n")) + t.Fatalf("IB did not [correctly] duplicate the IFD structure.") + } } // TODO(dustin): !! Test with an actual GPS-attached image. func Test_IfdBuilder_CreateIfdBuilderFromExistingChain_RealData(t *testing.T) { - filepath := path.Join(assetsPath, "NDM_8901.jpg") + filepath := path.Join(assetsPath, "NDM_8901.jpg") - e := NewExif() + e := NewExif() + rawExif, err := e.SearchAndExtractExif(filepath) + log.PanicIf(err) - rawExif, err := e.SearchAndExtractExif(filepath) - log.PanicIf(err) + // Decode from binary. - // Decode from binary. + _, originalIndex, err := e.Collect(rawExif) + log.PanicIf(err) - _, originalIndex, err := e.Collect(rawExif) - log.PanicIf(err) + originalThumbnailData, err := originalIndex.RootIfd.NextIfd.Thumbnail() + log.PanicIf(err) - originalThumbnailData, err := originalIndex.RootIfd.NextIfd.Thumbnail() - log.PanicIf(err) + originalTags := originalIndex.RootIfd.DumpTags() - originalTags := originalIndex.RootIfd.DumpTags() + // Encode back to binary. + ibe := NewIfdByteEncoder() - // Encode back to binary. + itevr := NewIfdTagEntryValueResolver(rawExif, originalIndex.RootIfd.ByteOrder) + rootIb := NewIfdBuilderFromExistingChain(originalIndex.RootIfd, itevr) - ibe := NewIfdByteEncoder() + updatedExif, err := ibe.EncodeToExif(rootIb) + log.PanicIf(err) - itevr := NewIfdTagEntryValueResolver(rawExif, originalIndex.RootIfd.ByteOrder) - rootIb := NewIfdBuilderFromExistingChain(originalIndex.RootIfd, itevr) + // Parse again. - updatedExif, err := ibe.EncodeToExif(rootIb) - log.PanicIf(err) + _, recoveredIndex, err := e.Collect(updatedExif) + log.PanicIf(err) + recoveredTags := recoveredIndex.RootIfd.DumpTags() - // Parse again. + recoveredThumbnailData, err := recoveredIndex.RootIfd.NextIfd.Thumbnail() + log.PanicIf(err) - _, recoveredIndex, err := e.Collect(updatedExif) - log.PanicIf(err) + // Check the thumbnail. - recoveredTags := recoveredIndex.RootIfd.DumpTags() + if bytes.Compare(recoveredThumbnailData, originalThumbnailData) != 0 { + t.Fatalf("recovered thumbnail does not match original") + } + // Validate that all of the same IFDs were presented. - recoveredThumbnailData, err := recoveredIndex.RootIfd.NextIfd.Thumbnail() - log.PanicIf(err) + originalIfdTags := make([][2]interface{}, 0) + for _, ite := range originalTags { + if ite.ChildIfdName != "" { + originalIfdTags = append(originalIfdTags, [2]interface{}{ite.Ii, ite.TagId}) + } + } + recoveredIfdTags := make([][2]interface{}, 0) + for _, ite := range recoveredTags { + if ite.ChildIfdName != "" { + recoveredIfdTags = append(recoveredIfdTags, [2]interface{}{ite.Ii, ite.TagId}) + } + } - // Check the thumbnail. + if reflect.DeepEqual(recoveredIfdTags, originalIfdTags) != true { + fmt.Printf("Original IFD tags:\n\n") - if bytes.Compare(recoveredThumbnailData, originalThumbnailData) != 0 { - t.Fatalf("recovered thumbnail does not match original") - } + for i, x := range originalIfdTags { + fmt.Printf(" %02d %v\n", i, x) + } + fmt.Printf("\nRecovered IFD tags:\n\n") - // Validate that all of the same IFDs were presented. + for i, x := range recoveredIfdTags { + fmt.Printf(" %02d %v\n", i, x) + } - originalIfdTags := make([][2]interface{}, 0) - for _, ite := range originalTags { - if ite.ChildIfdName != "" { - originalIfdTags = append(originalIfdTags, [2]interface{} { ite.Ii, ite.TagId }) - } - } + fmt.Printf("\n") - recoveredIfdTags := make([][2]interface{}, 0) - for _, ite := range recoveredTags { - if ite.ChildIfdName != "" { - recoveredIfdTags = append(recoveredIfdTags, [2]interface{} { ite.Ii, ite.TagId }) - } - } + t.Fatalf("Recovered IFD tags are not correct.") + } - if reflect.DeepEqual(recoveredIfdTags, originalIfdTags) != true { - fmt.Printf("Original IFD tags:\n\n") + // Validate that all of the tags owned by the IFDs were presented. Note + // that the thumbnail tags are not kept but only produced on the fly, which + // is why we check it above. - for i, x := range originalIfdTags { - fmt.Printf(" %02d %v\n", i, x) - } + if len(recoveredTags) != len(originalTags) { + t.Fatalf("Recovered tag-count does not match original.") + } - fmt.Printf("\nRecovered IFD tags:\n\n") + for i, recoveredIte := range recoveredTags { + if recoveredIte.ChildIfdName != "" { + continue + } - for i, x := range recoveredIfdTags { - fmt.Printf(" %02d %v\n", i, x) - } + originalIte := originalTags[i] - fmt.Printf("\n") + if recoveredIte.Ii != originalIte.Ii { + t.Fatalf("IfdIdentify not as expected: %s != %s ITE=%s", recoveredIte.Ii, originalIte.Ii, recoveredIte) + } else if recoveredIte.TagId != originalIte.TagId { + t.Fatalf("Tag-ID not as expected: %d != %d ITE=%s", recoveredIte.TagId, originalIte.TagId, recoveredIte) + } else if recoveredIte.TagType != originalIte.TagType { + t.Fatalf("Tag-type not as expected: %d != %d ITE=%s", recoveredIte.TagType, originalIte.TagType, recoveredIte) + } - t.Fatalf("Recovered IFD tags are not correct.") - } + // TODO(dustin): We're always accessing the addressable-data using the root-IFD. It shouldn't matter, but we'd rather access it from our specific IFD. + originalValueBytes, err := originalIte.ValueBytes(originalIndex.RootIfd.addressableData, originalIndex.RootIfd.ByteOrder) + log.PanicIf(err) + recoveredValueBytes, err := recoveredIte.ValueBytes(recoveredIndex.RootIfd.addressableData, recoveredIndex.RootIfd.ByteOrder) + log.PanicIf(err) - // Validate that all of the tags owned by the IFDs were presented. Note - // that the thumbnail tags are not kept but only produced on the fly, which - // is why we check it above. - - if len(recoveredTags) != len(originalTags) { - t.Fatalf("Recovered tag-count does not match original.") - } - - for i, recoveredIte := range recoveredTags { - if recoveredIte.ChildIfdName != "" { - continue - } - - originalIte := originalTags[i] - - if recoveredIte.Ii != originalIte.Ii { - t.Fatalf("IfdIdentify not as expected: %s != %s ITE=%s", recoveredIte.Ii, originalIte.Ii, recoveredIte) - } else if recoveredIte.TagId != originalIte.TagId { - t.Fatalf("Tag-ID not as expected: %d != %d ITE=%s", recoveredIte.TagId, originalIte.TagId, recoveredIte) - } else if recoveredIte.TagType != originalIte.TagType { - t.Fatalf("Tag-type not as expected: %d != %d ITE=%s", recoveredIte.TagType, originalIte.TagType, recoveredIte) - } - -// TODO(dustin): We're always accessing the addressable-data using the root-IFD. It shouldn't matter, but we'd rather access it from our specific IFD. - originalValueBytes, err := originalIte.ValueBytes(originalIndex.RootIfd.addressableData, originalIndex.RootIfd.ByteOrder) - log.PanicIf(err) - - recoveredValueBytes, err := recoveredIte.ValueBytes(recoveredIndex.RootIfd.addressableData, recoveredIndex.RootIfd.ByteOrder) - log.PanicIf(err) - - if bytes.Compare(originalValueBytes, recoveredValueBytes) != 0 { - t.Fatalf("bytes of tag content not correct: %s != %s", originalIte, recoveredIte) - } - } + if bytes.Compare(originalValueBytes, recoveredValueBytes) != 0 { + t.Fatalf("bytes of tag content not correct: %s != %s", originalIte, recoveredIte) + } + } } func Test_IfdBuilder_CreateIfdBuilderFromExistingChain_RealData_WithUpdate(t *testing.T) { - filepath := path.Join(assetsPath, "NDM_8901.jpg") + filepath := path.Join(assetsPath, "NDM_8901.jpg") - e := NewExif() + e := NewExif() + rawExif, err := e.SearchAndExtractExif(filepath) + log.PanicIf(err) - rawExif, err := e.SearchAndExtractExif(filepath) - log.PanicIf(err) + // Decode from binary. - // Decode from binary. + _, originalIndex, err := e.Collect(rawExif) + log.PanicIf(err) - _, originalIndex, err := e.Collect(rawExif) - log.PanicIf(err) + originalThumbnailData, err := originalIndex.RootIfd.NextIfd.Thumbnail() + log.PanicIf(err) - originalThumbnailData, err := originalIndex.RootIfd.NextIfd.Thumbnail() - log.PanicIf(err) + originalTags := originalIndex.RootIfd.DumpTags() - originalTags := originalIndex.RootIfd.DumpTags() + // Encode back to binary. + ibe := NewIfdByteEncoder() - // Encode back to binary. + itevr := NewIfdTagEntryValueResolver(rawExif, originalIndex.RootIfd.ByteOrder) + rootIb := NewIfdBuilderFromExistingChain(originalIndex.RootIfd, itevr) - ibe := NewIfdByteEncoder() + // Update a tag,. - itevr := NewIfdTagEntryValueResolver(rawExif, originalIndex.RootIfd.ByteOrder) - rootIb := NewIfdBuilderFromExistingChain(originalIndex.RootIfd, itevr) + exifBt, err := rootIb.FindTagWithName("ExifTag") + log.PanicIf(err) + ucBt, err := exifBt.value.Ib().FindTagWithName("UserComment") + log.PanicIf(err) - // Update a tag,. + uc := TagUnknownType_9298_UserComment{ + EncodingType: TagUnknownType_9298_UserComment_Encoding_ASCII, + EncodingBytes: []byte("TEST COMMENT"), + } - exifBt, err := rootIb.FindTagWithName("ExifTag") - log.PanicIf(err) + err = ucBt.SetValue(rootIb.byteOrder, uc) + log.PanicIf(err) - ucBt, err := exifBt.value.Ib().FindTagWithName("UserComment") - log.PanicIf(err) + // Encode. - uc := TagUnknownType_9298_UserComment{ - EncodingType: TagUnknownType_9298_UserComment_Encoding_ASCII, - EncodingBytes: []byte("TEST COMMENT"), - } + updatedExif, err := ibe.EncodeToExif(rootIb) + log.PanicIf(err) - err = ucBt.SetValue(rootIb.byteOrder, uc) - log.PanicIf(err) + // Parse again. + _, recoveredIndex, err := e.Collect(updatedExif) + log.PanicIf(err) - // Encode. + recoveredTags := recoveredIndex.RootIfd.DumpTags() - updatedExif, err := ibe.EncodeToExif(rootIb) - log.PanicIf(err) + recoveredThumbnailData, err := recoveredIndex.RootIfd.NextIfd.Thumbnail() + log.PanicIf(err) + // Check the thumbnail. - // Parse again. + if bytes.Compare(recoveredThumbnailData, originalThumbnailData) != 0 { + t.Fatalf("recovered thumbnail does not match original") + } - _, recoveredIndex, err := e.Collect(updatedExif) - log.PanicIf(err) + // Validate that all of the same IFDs were presented. - recoveredTags := recoveredIndex.RootIfd.DumpTags() + originalIfdTags := make([][2]interface{}, 0) + for _, ite := range originalTags { + if ite.ChildIfdName != "" { + originalIfdTags = append(originalIfdTags, [2]interface{}{ite.Ii, ite.TagId}) + } + } + recoveredIfdTags := make([][2]interface{}, 0) + for _, ite := range recoveredTags { + if ite.ChildIfdName != "" { + recoveredIfdTags = append(recoveredIfdTags, [2]interface{}{ite.Ii, ite.TagId}) + } + } - recoveredThumbnailData, err := recoveredIndex.RootIfd.NextIfd.Thumbnail() - log.PanicIf(err) + if reflect.DeepEqual(recoveredIfdTags, originalIfdTags) != true { + fmt.Printf("Original IFD tags:\n\n") + for i, x := range originalIfdTags { + fmt.Printf(" %02d %v\n", i, x) + } - // Check the thumbnail. + fmt.Printf("\nRecovered IFD tags:\n\n") - if bytes.Compare(recoveredThumbnailData, originalThumbnailData) != 0 { - t.Fatalf("recovered thumbnail does not match original") - } + for i, x := range recoveredIfdTags { + fmt.Printf(" %02d %v\n", i, x) + } + fmt.Printf("\n") - // Validate that all of the same IFDs were presented. + t.Fatalf("Recovered IFD tags are not correct.") + } - originalIfdTags := make([][2]interface{}, 0) - for _, ite := range originalTags { - if ite.ChildIfdName != "" { - originalIfdTags = append(originalIfdTags, [2]interface{} { ite.Ii, ite.TagId }) - } - } + // Validate that all of the tags owned by the IFDs were presented. Note + // that the thumbnail tags are not kept but only produced on the fly, which + // is why we check it above. - recoveredIfdTags := make([][2]interface{}, 0) - for _, ite := range recoveredTags { - if ite.ChildIfdName != "" { - recoveredIfdTags = append(recoveredIfdTags, [2]interface{} { ite.Ii, ite.TagId }) - } - } + if len(recoveredTags) != len(originalTags) { + t.Fatalf("Recovered tag-count does not match original.") + } - if reflect.DeepEqual(recoveredIfdTags, originalIfdTags) != true { - fmt.Printf("Original IFD tags:\n\n") + for i, recoveredIte := range recoveredTags { + if recoveredIte.ChildIfdName != "" { + continue + } - for i, x := range originalIfdTags { - fmt.Printf(" %02d %v\n", i, x) - } + originalIte := originalTags[i] - fmt.Printf("\nRecovered IFD tags:\n\n") + if recoveredIte.Ii != originalIte.Ii { + t.Fatalf("IfdIdentify not as expected: %s != %s ITE=%s", recoveredIte.Ii, originalIte.Ii, recoveredIte) + } else if recoveredIte.TagId != originalIte.TagId { + t.Fatalf("Tag-ID not as expected: %d != %d ITE=%s", recoveredIte.TagId, originalIte.TagId, recoveredIte) + } else if recoveredIte.TagType != originalIte.TagType { + t.Fatalf("Tag-type not as expected: %d != %d ITE=%s", recoveredIte.TagType, originalIte.TagType, recoveredIte) + } - for i, x := range recoveredIfdTags { - fmt.Printf(" %02d %v\n", i, x) - } + originalValueBytes, err := originalIte.ValueBytes(originalIndex.RootIfd.addressableData, originalIndex.RootIfd.ByteOrder) + log.PanicIf(err) - fmt.Printf("\n") + recoveredValueBytes, err := recoveredIte.ValueBytes(recoveredIndex.RootIfd.addressableData, recoveredIndex.RootIfd.ByteOrder) + log.PanicIf(err) - t.Fatalf("Recovered IFD tags are not correct.") - } + if recoveredIte.TagId == 0x9286 { + expectedValueBytes := make([]byte, 0) + expectedValueBytes = append(expectedValueBytes, []byte{'A', 'S', 'C', 'I', 'I', 0, 0, 0}...) + expectedValueBytes = append(expectedValueBytes, []byte("TEST COMMENT")...) - // Validate that all of the tags owned by the IFDs were presented. Note - // that the thumbnail tags are not kept but only produced on the fly, which - // is why we check it above. - - if len(recoveredTags) != len(originalTags) { - t.Fatalf("Recovered tag-count does not match original.") - } - - for i, recoveredIte := range recoveredTags { - if recoveredIte.ChildIfdName != "" { - continue - } - - originalIte := originalTags[i] - - if recoveredIte.Ii != originalIte.Ii { - t.Fatalf("IfdIdentify not as expected: %s != %s ITE=%s", recoveredIte.Ii, originalIte.Ii, recoveredIte) - } else if recoveredIte.TagId != originalIte.TagId { - t.Fatalf("Tag-ID not as expected: %d != %d ITE=%s", recoveredIte.TagId, originalIte.TagId, recoveredIte) - } else if recoveredIte.TagType != originalIte.TagType { - t.Fatalf("Tag-type not as expected: %d != %d ITE=%s", recoveredIte.TagType, originalIte.TagType, recoveredIte) - } - - originalValueBytes, err := originalIte.ValueBytes(originalIndex.RootIfd.addressableData, originalIndex.RootIfd.ByteOrder) - log.PanicIf(err) - - recoveredValueBytes, err := recoveredIte.ValueBytes(recoveredIndex.RootIfd.addressableData, recoveredIndex.RootIfd.ByteOrder) - log.PanicIf(err) - - if recoveredIte.TagId == 0x9286 { - expectedValueBytes := make([]byte, 0) - - expectedValueBytes = append(expectedValueBytes, []byte{ 'A', 'S', 'C', 'I', 'I', 0, 0, 0 }...) - expectedValueBytes = append(expectedValueBytes, []byte("TEST COMMENT")...) - - if bytes.Compare(recoveredValueBytes, expectedValueBytes) != 0 { - t.Fatalf("Recovered UserComment does not have the right value: %v != %v", recoveredValueBytes, expectedValueBytes) - } - } else if bytes.Compare(recoveredValueBytes, originalValueBytes) != 0 { - t.Fatalf("bytes of tag content not correct: %v != %v ITE=%s", recoveredValueBytes, originalValueBytes, recoveredIte) - } - } + if bytes.Compare(recoveredValueBytes, expectedValueBytes) != 0 { + t.Fatalf("Recovered UserComment does not have the right value: %v != %v", recoveredValueBytes, expectedValueBytes) + } + } else if bytes.Compare(recoveredValueBytes, originalValueBytes) != 0 { + t.Fatalf("bytes of tag content not correct: %v != %v ITE=%s", recoveredValueBytes, originalValueBytes, recoveredIte) + } + } } -func ExampleReadThumbnail() { - filepath := path.Join(assetsPath, "NDM_8901.jpg") +func ExampleIfd_Thumbnail() { + filepath := path.Join(assetsPath, "NDM_8901.jpg") - e := NewExif() + e := NewExif() - rawExif, err := e.SearchAndExtractExif(filepath) - log.PanicIf(err) + rawExif, err := e.SearchAndExtractExif(filepath) + log.PanicIf(err) - _, index, err := e.Collect(rawExif) - log.PanicIf(err) + _, index, err := e.Collect(rawExif) + log.PanicIf(err) - thumbnailData, err := index.RootIfd.NextIfd.Thumbnail() - log.PanicIf(err) + thumbnailData, err := index.RootIfd.NextIfd.Thumbnail() + log.PanicIf(err) - thumbnailData = thumbnailData - // Output: + thumbnailData = thumbnailData + // Output: } -func ExampleUpdateUnknownTag() { - filepath := path.Join(assetsPath, "NDM_8901.jpg") +func ExampleBuilderTag_SetValue() { + filepath := path.Join(assetsPath, "NDM_8901.jpg") - e := NewExif() + e := NewExif() - rawExif, err := e.SearchAndExtractExif(filepath) - log.PanicIf(err) + rawExif, err := e.SearchAndExtractExif(filepath) + log.PanicIf(err) - _, index, err := e.Collect(rawExif) - log.PanicIf(err) + _, index, err := e.Collect(rawExif) + log.PanicIf(err) + // Create builder. - // Create builder. + itevr := NewIfdTagEntryValueResolver(rawExif, index.RootIfd.ByteOrder) + rootIb := NewIfdBuilderFromExistingChain(index.RootIfd, itevr) - itevr := NewIfdTagEntryValueResolver(rawExif, index.RootIfd.ByteOrder) - rootIb := NewIfdBuilderFromExistingChain(index.RootIfd, itevr) + // Find tag to update. + exifBt, err := rootIb.FindTagWithName("ExifTag") + log.PanicIf(err) - // Find tag to update. + ucBt, err := exifBt.value.Ib().FindTagWithName("UserComment") + log.PanicIf(err) - exifBt, err := rootIb.FindTagWithName("ExifTag") - log.PanicIf(err) + // Update the value. Since this is an "undefined"-type tag, we have to use + // its type-specific struct. - ucBt, err := exifBt.value.Ib().FindTagWithName("UserComment") - log.PanicIf(err) + // TODO(dustin): !! Add an example for setting a non-unknown value, too. + uc := TagUnknownType_9298_UserComment{ + EncodingType: TagUnknownType_9298_UserComment_Encoding_ASCII, + EncodingBytes: []byte("TEST COMMENT"), + } + err = ucBt.SetValue(rootIb.byteOrder, uc) + log.PanicIf(err) - // Update the value. Since this is an "undefined"-type tag, we have to use - // its type-specific struct. + // Encode. - uc := TagUnknownType_9298_UserComment{ - EncodingType: TagUnknownType_9298_UserComment_Encoding_ASCII, - EncodingBytes: []byte("TEST COMMENT"), - } + ibe := NewIfdByteEncoder() + updatedExif, err := ibe.EncodeToExif(rootIb) + log.PanicIf(err) - err = ucBt.SetValue(rootIb.byteOrder, uc) - log.PanicIf(err) - - - // Encode. - - ibe := NewIfdByteEncoder() - updatedExif, err := ibe.EncodeToExif(rootIb) - log.PanicIf(err) - - updatedExif = updatedExif - // Output: + updatedExif = updatedExif + // Output: } func Test_IfdBuilder_CreateIfdBuilderWithExistingIfd(t *testing.T) { - tagId := IfdTagIdWithIdentityOrFail(GpsIi) + tagId := IfdTagIdWithIdentityOrFail(GpsIi) - parentIfd := &Ifd{ - Ii: RootIi, - Name: IfdStandard, - } + parentIfd := &Ifd{ + Ii: RootIi, + Name: IfdStandard, + } - ifd := &Ifd{ - Ii: GpsIi, - Name: IfdGps, - ByteOrder: TestDefaultByteOrder, - Offset: 0x123, - ParentIfd: parentIfd, - } + ifd := &Ifd{ + Ii: GpsIi, + Name: IfdGps, + ByteOrder: TestDefaultByteOrder, + Offset: 0x123, + ParentIfd: parentIfd, + } - ib := NewIfdBuilderWithExistingIfd(ifd) + ib := NewIfdBuilderWithExistingIfd(ifd) - if ib.ii.IfdName != ifd.Name { - t.Fatalf("IFD-name not correct.") - } else if ib.ifdTagId != tagId { - t.Fatalf("IFD tag-ID not correct.") - } else if ib.byteOrder != ifd.ByteOrder { - t.Fatalf("IFD byte-order not correct.") - } else if ib.existingOffset != ifd.Offset { - t.Fatalf("IFD offset not correct.") - } + if ib.ii.IfdName != ifd.Name { + t.Fatalf("IFD-name not correct.") + } else if ib.ifdTagId != tagId { + t.Fatalf("IFD tag-ID not correct.") + } else if ib.byteOrder != ifd.ByteOrder { + t.Fatalf("IFD byte-order not correct.") + } else if ib.existingOffset != ifd.Offset { + t.Fatalf("IFD offset not correct.") + } } func TestNewStandardBuilderTagFromConfig_OneUnit(t *testing.T) { - bt := NewStandardBuilderTagFromConfig(ExifIi, uint16(0x8833), TestDefaultByteOrder, []uint32 { uint32(0x1234) }) + bt := NewStandardBuilderTagFromConfig(ExifIi, uint16(0x8833), TestDefaultByteOrder, []uint32{uint32(0x1234)}) - if bt.ii != ExifIi { - t.Fatalf("II in builderTag not correct") - } else if bt.tagId != 0x8833 { - t.Fatalf("tag-ID not correct") - } else if bytes.Compare(bt.value.Bytes(), []byte { 0x0, 0x0, 0x12, 0x34, }) != 0 { - t.Fatalf("value not correct") - } + if bt.ii != ExifIi { + t.Fatalf("II in BuilderTag not correct") + } else if bt.tagId != 0x8833 { + t.Fatalf("tag-ID not correct") + } else if bytes.Compare(bt.value.Bytes(), []byte{0x0, 0x0, 0x12, 0x34}) != 0 { + t.Fatalf("value not correct") + } } func TestNewStandardBuilderTagFromConfig_TwoUnits(t *testing.T) { - bt := NewStandardBuilderTagFromConfig(ExifIi, uint16(0x8833), TestDefaultByteOrder, []uint32 { uint32(0x1234), uint32(0x5678) }) + bt := NewStandardBuilderTagFromConfig(ExifIi, uint16(0x8833), TestDefaultByteOrder, []uint32{uint32(0x1234), uint32(0x5678)}) - if bt.ii != ExifIi { - t.Fatalf("II in builderTag not correct") - } else if bt.tagId != 0x8833 { - t.Fatalf("tag-ID not correct") - } else if bytes.Compare(bt.value.Bytes(), []byte { - 0x0, 0x0, 0x12, 0x34, - 0x0, 0x0, 0x56, 0x78, }) != 0 { - t.Fatalf("value not correct") - } + if bt.ii != ExifIi { + t.Fatalf("II in BuilderTag not correct") + } else if bt.tagId != 0x8833 { + t.Fatalf("tag-ID not correct") + } else if bytes.Compare(bt.value.Bytes(), []byte{ + 0x0, 0x0, 0x12, 0x34, + 0x0, 0x0, 0x56, 0x78}) != 0 { + t.Fatalf("value not correct") + } } func TestNewStandardBuilderTagFromConfigWithName(t *testing.T) { - bt := NewStandardBuilderTagFromConfigWithName(ExifIi, "ISOSpeed", TestDefaultByteOrder, []uint32 { uint32(0x1234), uint32(0x5678) }) + bt := NewStandardBuilderTagFromConfigWithName(ExifIi, "ISOSpeed", TestDefaultByteOrder, []uint32{uint32(0x1234), uint32(0x5678)}) - if bt.ii != ExifIi { - t.Fatalf("II in builderTag not correct") - } else if bt.tagId != 0x8833 { - t.Fatalf("tag-ID not correct") - } else if bytes.Compare(bt.value.Bytes(), []byte { - 0x0, 0x0, 0x12, 0x34, - 0x0, 0x0, 0x56, 0x78, }) != 0 { - t.Fatalf("value not correct") - } + if bt.ii != ExifIi { + t.Fatalf("II in BuilderTag not correct") + } else if bt.tagId != 0x8833 { + t.Fatalf("tag-ID not correct") + } else if bytes.Compare(bt.value.Bytes(), []byte{ + 0x0, 0x0, 0x12, 0x34, + 0x0, 0x0, 0x56, 0x78}) != 0 { + t.Fatalf("value not correct") + } } func TestAddFromConfigWithName(t *testing.T) { - ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) + ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) - err := ib.AddFromConfigWithName("ProcessingSoftware", "some software") - log.PanicIf(err) + err := ib.AddFromConfigWithName("ProcessingSoftware", "some software") + log.PanicIf(err) - if len(ib.tags) != 1 { - t.Fatalf("Exactly one tag was not found: (%d)", len(ib.tags)) - } + if len(ib.tags) != 1 { + t.Fatalf("Exactly one tag was not found: (%d)", len(ib.tags)) + } - bt := ib.tags[0] + bt := ib.tags[0] - if bt.ii != RootIi { - t.Fatalf("II not correct: %s", bt.ii) - } else if bt.tagId != 0x000b { - t.Fatalf("Tag-ID not correct: (0x%04x)", bt.tagId) - } + if bt.ii != RootIi { + t.Fatalf("II not correct: %s", bt.ii) + } else if bt.tagId != 0x000b { + t.Fatalf("Tag-ID not correct: (0x%04x)", bt.tagId) + } - s := string(bt.value.Bytes()) + s := string(bt.value.Bytes()) - if s != "some software\000" { - t.Fatalf("Value not correct: (%d) [%s]", len(s), s) - } + if s != "some software\000" { + t.Fatalf("Value not correct: (%d) [%s]", len(s), s) + } } -