mirror of
https://github.com/dsoprea/go-exif.git
synced 2025-04-27 13:12:39 +00:00
encode: We now encode child IFDs as they occur.
- ..rather than after the normal tags.
This commit is contained in:
parent
5996e32e27
commit
3c1c668a9f
162
ifd_builder.go
162
ifd_builder.go
@ -298,9 +298,7 @@ func NewIfdBuilderWithExistingIfd(ifd *Ifd) (ib *IfdBuilder) {
|
|||||||
|
|
||||||
// NewIfdBuilderFromExistingChain creates a chain of IB instances from an
|
// NewIfdBuilderFromExistingChain creates a chain of IB instances from an
|
||||||
// IFD chain generated from real data.
|
// IFD chain generated from real data.
|
||||||
func NewIfdBuilderFromExistingChain(rootIfd *Ifd, exifData []byte) (firstIb *IfdBuilder) {
|
func NewIfdBuilderFromExistingChain(rootIfd *Ifd, itevr *IfdTagEntryValueResolver) (firstIb *IfdBuilder) {
|
||||||
itevr := NewIfdTagEntryValueResolver(exifData, rootIfd.ByteOrder)
|
|
||||||
|
|
||||||
// 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
|
var lastIb *IfdBuilder
|
||||||
@ -318,15 +316,6 @@ func NewIfdBuilderFromExistingChain(rootIfd *Ifd, exifData []byte) (firstIb *Ifd
|
|||||||
err := newIb.AddTagsFromExisting(thisExistingIfd, itevr, nil, nil)
|
err := newIb.AddTagsFromExisting(thisExistingIfd, itevr, nil, nil)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
// Any child IFDs will still not be copied. Do that now.
|
|
||||||
|
|
||||||
for _, childIfd := range thisExistingIfd.Children {
|
|
||||||
childIb := NewIfdBuilderFromExistingChain(childIfd, exifData)
|
|
||||||
|
|
||||||
err = newIb.AddChildIb(childIb)
|
|
||||||
log.PanicIf(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
lastIb = newIb
|
lastIb = newIb
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
@ -375,14 +364,14 @@ func (ib *IfdBuilder) SetThumbnail(data []byte) (err error) {
|
|||||||
if data == nil || len(data) == 0 {
|
if data == nil || len(data) == 0 {
|
||||||
|
|
||||||
// TODO(dustin): !! Debugging.
|
// TODO(dustin): !! Debugging.
|
||||||
fmt.Printf("Thumbnail empty.\n")
|
// 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)
|
ibtvfb := NewIfdBuilderTagValueFromBytes(ib.thumbnailData)
|
||||||
offsetBt := NewBuilderTag(ib.ii, ThumbnailOffsetTagId, TypeLong, ibtvfb)
|
offsetBt := NewBuilderTag(ib.ii, ThumbnailOffsetTagId, TypeLong, ibtvfb)
|
||||||
@ -398,7 +387,7 @@ fmt.Printf("SETTING THUMBNAIL.\n")
|
|||||||
|
|
||||||
|
|
||||||
// TODO(dustin): !! Debugging.
|
// TODO(dustin): !! Debugging.
|
||||||
fmt.Printf("Set thumbnail into IB.\n")
|
// fmt.Printf("Set thumbnail into IB.\n")
|
||||||
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -664,7 +653,7 @@ func (ib *IfdBuilder) Set(bt builderTag) (err error) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
ib.tags[position] = bt
|
ib.tags[position] = bt
|
||||||
} else if log.Is(err, ErrTagEntryNotFound) == true {
|
} else if log.Is(err, ErrTagEntryNotFound) == true {
|
||||||
err = ib.Add(bt)
|
err = ib.add(bt)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
} else {
|
} else {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
@ -711,7 +700,7 @@ func (ib *IfdBuilder) Find(tagId uint16) (position int, err error) {
|
|||||||
return found[0], nil
|
return found[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ib *IfdBuilder) Add(bt builderTag) (err error) {
|
func (ib *IfdBuilder) add(bt builderTag) (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if state := recover(); state != nil {
|
if state := recover(); state != nil {
|
||||||
err = log.Wrap(state.(error))
|
err = log.Wrap(state.(error))
|
||||||
@ -726,11 +715,24 @@ func (ib *IfdBuilder) Add(bt builderTag) (err error) {
|
|||||||
log.Panicf("builderTag value is not set: %s", bt)
|
log.Panicf("builderTag value is not set: %s", bt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
if bt.value.IsIb() == true {
|
if bt.value.IsIb() == true {
|
||||||
log.Panicf("child IfdBuilders must be added via AddChildIb() not Add()")
|
log.Panicf("child IfdBuilders must be added via AddChildIb() or AddTagsFromExisting(), not Add()")
|
||||||
}
|
}
|
||||||
|
|
||||||
ib.tags = append(ib.tags, bt)
|
err = ib.add(bt)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -757,16 +759,28 @@ func (ib *IfdBuilder) AddChildIb(childIb *IfdBuilder) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bt := ib.NewBuilderTagFromBuilder(childIb)
|
||||||
|
ib.tags = append(ib.tags, bt)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
value := NewIfdBuilderTagValueFromIfdBuilder(childIb)
|
value := NewIfdBuilderTagValueFromIfdBuilder(childIb)
|
||||||
|
|
||||||
bt := NewChildIfdBuilderTag(
|
bt = NewChildIfdBuilderTag(
|
||||||
ib.ii,
|
ib.ii,
|
||||||
childIb.ifdTagId,
|
childIb.ifdTagId,
|
||||||
value)
|
value)
|
||||||
|
|
||||||
ib.tags = append(ib.tags, bt)
|
return bt
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTagsFromExisting does a verbatim copy of the entries in `ifd` to this
|
// AddTagsFromExisting does a verbatim copy of the entries in `ifd` to this
|
||||||
@ -782,8 +796,10 @@ func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, itevr *IfdTagEntryValueResol
|
|||||||
thumbnailData, err := ifd.Thumbnail()
|
thumbnailData, err := ifd.Thumbnail()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
||||||
|
// TODO(dustin): The thumbnail tags will be added out of order.
|
||||||
|
|
||||||
// TODO(dustin): !! Debugging.
|
// TODO(dustin): !! Debugging.
|
||||||
fmt.Printf("Importing thumbnail: %s\n", ifd.Identity())
|
// fmt.Printf("Importing thumbnail: %s\n", ifd.Identity())
|
||||||
|
|
||||||
err = ib.SetThumbnail(thumbnailData)
|
err = ib.SetThumbnail(thumbnailData)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
@ -792,17 +808,12 @@ fmt.Printf("Importing thumbnail: %s\n", ifd.Identity())
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
// TODO(dustin): !! Debugging.
|
// TODO(dustin): !! Debugging.
|
||||||
fmt.Printf("NO THUMBNAIL FOUND: %s\n", ifd.Identity())
|
// fmt.Printf("NO THUMBNAIL FOUND: %s\n", ifd.Identity())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ite := range ifd.Entries {
|
for i, ite := range ifd.Entries {
|
||||||
if ite.ChildIfdName != "" {
|
if ite.TagId == ThumbnailOffsetTagId || ite.TagId == ThumbnailSizeTagId {
|
||||||
// If we want to add an IFD tag, we'll have to build it first and
|
|
||||||
// *then* add it via a different method.
|
|
||||||
|
|
||||||
continue
|
|
||||||
} else if ite.TagId == ThumbnailOffsetTagId || ite.TagId == ThumbnailSizeTagId {
|
|
||||||
// These will be added on-the-fly when we encode.
|
// These will be added on-the-fly when we encode.
|
||||||
|
|
||||||
continue
|
continue
|
||||||
@ -838,34 +849,79 @@ fmt.Printf("NO THUMBNAIL FOUND: %s\n", ifd.Identity())
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var value *IfdBuilderTagValue
|
var bt builderTag
|
||||||
|
|
||||||
if itevr == nil {
|
if ite.ChildIfdName != "" {
|
||||||
// rawValueOffsetCopy is our own private copy of the original data.
|
// If we want to add an IFD tag, we'll have to build it first and
|
||||||
// It should always be four-bytes, but just copy whatever there is.
|
// *then* add it via a different method.
|
||||||
rawValueOffsetCopy := make([]byte, len(ite.RawValueOffset))
|
|
||||||
copy(rawValueOffsetCopy, ite.RawValueOffset)
|
|
||||||
|
|
||||||
value = NewIfdBuilderTagValueFromBytes(rawValueOffsetCopy)
|
if itevr == nil {
|
||||||
} else {
|
// We don't have any ability to resolve the structure of the
|
||||||
var err error
|
// 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.
|
||||||
|
|
||||||
valueBytes, err := itevr.ValueBytes(ite)
|
var childIfd *Ifd
|
||||||
if err != nil {
|
for _, thisChildIfd := range ifd.Children {
|
||||||
if log.Is(err, ErrUnhandledUnknownTypedTag) == true {
|
if thisChildIfd.ParentTagIndex != i {
|
||||||
ifdBuilderLogger.Warningf(nil, "Unknown-type tag can't be parsed so it can't be copied to the new IFD.")
|
continue
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Panic(err)
|
if childIfd == nil {
|
||||||
|
childTagIds := make([]string, len(ifd.Children))
|
||||||
|
for j, childIfd := range ifd.Children {
|
||||||
|
childTagIds[j] = fmt.Sprintf("0x%02x (parent tag-position %d)", childIfd.TagId, childIfd.ParentTagIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Panicf("could not find child IFD for child ITE: II=[%s] TAG-ID=(0x%02x) CURRENT-TAG-POSITION=(%d) CHILDREN=%v", ite.Ii, ite.TagId, i, childTagIds)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
err := ib.Add(bt)
|
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -883,7 +939,7 @@ func (ib *IfdBuilder) AddFromConfig(tagId uint16, value interface{}) (err error)
|
|||||||
|
|
||||||
bt := NewStandardBuilderTagFromConfig(ib.ii, tagId, ib.byteOrder, value)
|
bt := NewStandardBuilderTagFromConfig(ib.ii, tagId, ib.byteOrder, value)
|
||||||
|
|
||||||
err = ib.Add(bt)
|
err = ib.add(bt)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -922,7 +978,7 @@ func (ib *IfdBuilder) AddFromConfigWithName(tagName string, value interface{}) (
|
|||||||
|
|
||||||
bt := NewStandardBuilderTagFromConfigWithName(ib.ii, tagName, ib.byteOrder, value)
|
bt := NewStandardBuilderTagFromConfigWithName(ib.ii, tagName, ib.byteOrder, value)
|
||||||
|
|
||||||
err = ib.Add(bt)
|
err = ib.add(bt)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -1211,7 +1211,7 @@ func TestDeleteAll(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewIfdBuilderFromExistingChain(t *testing.T) {
|
func Test_IfdBuilder_CreateIfdBuilderFromExistingChain(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if state := recover(); state != nil {
|
if state := recover(); state != nil {
|
||||||
err := log.Wrap(state.(error))
|
err := log.Wrap(state.(error))
|
||||||
@ -1223,13 +1223,15 @@ func TestNewIfdBuilderFromExistingChain(t *testing.T) {
|
|||||||
|
|
||||||
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
||||||
|
|
||||||
exifData, err := e.SearchAndExtractExif(filepath)
|
rawExif, err := e.SearchAndExtractExif(filepath)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
_, index, err := e.Collect(exifData)
|
_, index, err := e.Collect(rawExif)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
ib := NewIfdBuilderFromExistingChain(index.RootIfd, exifData)
|
itevr := NewIfdTagEntryValueResolver(rawExif, index.RootIfd.ByteOrder)
|
||||||
|
ib := NewIfdBuilderFromExistingChain(index.RootIfd, itevr)
|
||||||
|
|
||||||
lines := ib.DumpToStrings()
|
lines := ib.DumpToStrings()
|
||||||
|
|
||||||
expected := []string {
|
expected := []string {
|
||||||
@ -1295,7 +1297,7 @@ func TestNewIfdBuilderFromExistingChain(t *testing.T) {
|
|||||||
|
|
||||||
// TODO(dustin): !! Test with an actual GPS-attached image.
|
// TODO(dustin): !! Test with an actual GPS-attached image.
|
||||||
|
|
||||||
func TestNewIfdBuilderFromExistingChain_RealData(t *testing.T) {
|
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()
|
||||||
@ -1310,12 +1312,14 @@ func TestNewIfdBuilderFromExistingChain_RealData(t *testing.T) {
|
|||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
originalTags := index.RootIfd.DumpTags()
|
originalTags := index.RootIfd.DumpTags()
|
||||||
|
index.RootIfd.PrintTagTree(true)
|
||||||
|
|
||||||
// Encode back to binary.
|
// Encode back to binary.
|
||||||
|
|
||||||
ibe := NewIfdByteEncoder()
|
ibe := NewIfdByteEncoder()
|
||||||
|
|
||||||
rootIb := NewIfdBuilderFromExistingChain(index.RootIfd, rawExif)
|
itevr := NewIfdTagEntryValueResolver(rawExif, index.RootIfd.ByteOrder)
|
||||||
|
rootIb := NewIfdBuilderFromExistingChain(index.RootIfd, itevr)
|
||||||
|
|
||||||
updatedExif, err := ibe.EncodeToExif(rootIb)
|
updatedExif, err := ibe.EncodeToExif(rootIb)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
@ -1326,8 +1330,11 @@ func TestNewIfdBuilderFromExistingChain_RealData(t *testing.T) {
|
|||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
recoveredTags := index.RootIfd.DumpTags()
|
recoveredTags := index.RootIfd.DumpTags()
|
||||||
|
index.RootIfd.PrintTagTree(true)
|
||||||
|
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
// Validate that all of the same IFDs were presented.
|
// Validate that all of the same IFDs were presented.
|
||||||
|
|
||||||
originalIfdTags := make([][2]interface{}, 0)
|
originalIfdTags := make([][2]interface{}, 0)
|
||||||
@ -1405,7 +1412,7 @@ func TestNewIfdBuilderFromExistingChain_RealData(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewIfdBuilderWithExistingIfd(t *testing.T) {
|
func Test_IfdBuilder_CreateIfdBuilderWithExistingIfd(t *testing.T) {
|
||||||
tagId := IfdTagIdWithIdentityOrFail(GpsIi)
|
tagId := IfdTagIdWithIdentityOrFail(GpsIi)
|
||||||
|
|
||||||
parentIfd := &Ifd{
|
parentIfd := &Ifd{
|
||||||
|
@ -330,9 +330,17 @@ type Ifd struct {
|
|||||||
ByteOrder binary.ByteOrder
|
ByteOrder binary.ByteOrder
|
||||||
|
|
||||||
Ii IfdIdentity
|
Ii IfdIdentity
|
||||||
|
TagId uint16
|
||||||
|
|
||||||
Id int
|
Id int
|
||||||
|
|
||||||
ParentIfd *Ifd
|
ParentIfd *Ifd
|
||||||
|
|
||||||
|
// ParentTagIndex is our tag position in the parent IFD, if we had a parent
|
||||||
|
// (if `ParentIfd` is not nil and we weren't an IFD referenced as a sibling
|
||||||
|
// instead of as a child).
|
||||||
|
ParentTagIndex int
|
||||||
|
|
||||||
Name string
|
Name string
|
||||||
Index int
|
Index int
|
||||||
Offset uint32
|
Offset uint32
|
||||||
@ -429,7 +437,7 @@ func (ifd Ifd) String() string {
|
|||||||
parentOffset = ifd.ParentIfd.Offset
|
parentOffset = ifd.ParentIfd.Offset
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("Ifd<ID=(%d) PARENT-IFD=[%s] IFD=[%s] IDX=(%d) COUNT=(%d) OFF=(0x%04x) CHILDREN=(%d) PARENT=(0x%04x) NEXT-IFD=(0x%04x)", ifd.Id, ifd.Ii.ParentIfdName, ifd.Ii.IfdName, ifd.Index, len(ifd.Entries), ifd.Offset, len(ifd.Children), parentOffset, ifd.NextIfdOffset)
|
return fmt.Sprintf("Ifd<ID=(%d) PARENT-IFD=[%s] IFD=[%s] INDEX=(%d) COUNT=(%d) OFF=(0x%04x) CHILDREN=(%d) PARENT=(0x%04x) NEXT-IFD=(0x%04x)>", ifd.Id, ifd.Ii.ParentIfdName, ifd.Ii.IfdName, ifd.Index, len(ifd.Entries), ifd.Offset, len(ifd.Children), parentOffset, ifd.NextIfdOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ifd *Ifd) Thumbnail() (data []byte, err error) {
|
func (ifd *Ifd) Thumbnail() (data []byte, err error) {
|
||||||
@ -497,7 +505,7 @@ func (ifd *Ifd) DumpTags() []*IfdTagEntry {
|
|||||||
return ifd.dumpTags(nil)
|
return ifd.dumpTags(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ifd *Ifd) printTagTree(index, level int, nextLink bool) {
|
func (ifd *Ifd) printTagTree(populateValues bool, index, level int, nextLink bool) {
|
||||||
indent := strings.Repeat(" ", level * 2)
|
indent := strings.Repeat(" ", level * 2)
|
||||||
|
|
||||||
prefix := " "
|
prefix := " "
|
||||||
@ -505,7 +513,7 @@ func (ifd *Ifd) printTagTree(index, level int, nextLink bool) {
|
|||||||
prefix = ">"
|
prefix = ">"
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s%sIFD: %s INDEX=(%d)\n", indent, prefix, ifd, index)
|
fmt.Printf("%s%sIFD: %s\n", indent, prefix, ifd)
|
||||||
|
|
||||||
// Quickly create an index of the child-IFDs.
|
// Quickly create an index of the child-IFDs.
|
||||||
|
|
||||||
@ -530,7 +538,15 @@ func (ifd *Ifd) printTagTree(index, level int, nextLink bool) {
|
|||||||
tagName = it.Name
|
tagName = it.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s - TAG: %s NAME=[%s]\n", indent, tag, tagName)
|
var value interface{}
|
||||||
|
if populateValues == true {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
value, err = ifd.TagValue(tag)
|
||||||
|
log.PanicIf(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s - TAG: %s NAME=[%s] VALUE=[%v]\n", indent, tag, tagName, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tag.ChildIfdName != "" {
|
if tag.ChildIfdName != "" {
|
||||||
@ -541,7 +557,7 @@ func (ifd *Ifd) printTagTree(index, level int, nextLink bool) {
|
|||||||
log.Panicf("alien child IFD referenced by a tag: [%s]", tag.ChildIfdName)
|
log.Panicf("alien child IFD referenced by a tag: [%s]", tag.ChildIfdName)
|
||||||
}
|
}
|
||||||
|
|
||||||
childIfd.printTagTree(0, level + 1, false)
|
childIfd.printTagTree(populateValues, 0, level + 1, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -550,13 +566,13 @@ func (ifd *Ifd) printTagTree(index, level int, nextLink bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ifd.NextIfd != nil {
|
if ifd.NextIfd != nil {
|
||||||
ifd.NextIfd.printTagTree(index + 1, level, true)
|
ifd.NextIfd.printTagTree(populateValues, index + 1, level, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintTagTree prints the IFD hierarchy.
|
// PrintTagTree prints the IFD hierarchy.
|
||||||
func (ifd *Ifd) PrintTagTree() {
|
func (ifd *Ifd) PrintTagTree(populateValues bool) {
|
||||||
ifd.printTagTree(0, 0, false)
|
ifd.printTagTree(populateValues, 0, 0, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ifd *Ifd) printIfdTree(level int, nextLink bool) {
|
func (ifd *Ifd) printIfdTree(level int, nextLink bool) {
|
||||||
@ -671,9 +687,16 @@ func (ifd *Ifd) DumpTree() []string {
|
|||||||
|
|
||||||
type QueuedIfd struct {
|
type QueuedIfd struct {
|
||||||
Ii IfdIdentity
|
Ii IfdIdentity
|
||||||
|
TagId uint16
|
||||||
|
|
||||||
Index int
|
Index int
|
||||||
Offset uint32
|
Offset uint32
|
||||||
Parent *Ifd
|
Parent *Ifd
|
||||||
|
|
||||||
|
// ParentTagIndex is our tag position in the parent IFD, if we had a parent
|
||||||
|
// (if `ParentIfd` is not nil and we weren't an IFD referenced as a sibling
|
||||||
|
// instead of as a child).
|
||||||
|
ParentTagIndex int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -700,6 +723,8 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error
|
|||||||
queue := []QueuedIfd{
|
queue := []QueuedIfd{
|
||||||
{
|
{
|
||||||
Ii: RootIi,
|
Ii: RootIi,
|
||||||
|
TagId: 0xffff,
|
||||||
|
|
||||||
Index: 0,
|
Index: 0,
|
||||||
Offset: rootIfdOffset,
|
Offset: rootIfdOffset,
|
||||||
},
|
},
|
||||||
@ -712,14 +737,14 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
ii := queue[0].Ii
|
qi := queue[0]
|
||||||
|
|
||||||
|
|
||||||
|
ii := qi.Ii
|
||||||
name := ii.IfdName
|
name := ii.IfdName
|
||||||
index := queue[0].Index
|
|
||||||
offset := queue[0].Offset
|
|
||||||
|
|
||||||
parentIfd := queue[0].Parent
|
index := qi.Index
|
||||||
|
offset := qi.Offset
|
||||||
|
parentIfd := qi.Parent
|
||||||
|
|
||||||
queue = queue[1:]
|
queue = queue[1:]
|
||||||
|
|
||||||
@ -741,53 +766,62 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error
|
|||||||
entriesByTagId[tag.TagId] = append(tags, tag)
|
entriesByTagId[tag.TagId] = append(tags, tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
ifd := Ifd{
|
ifd := &Ifd{
|
||||||
addressableData: ie.exifData[ExifAddressableAreaStart:],
|
addressableData: ie.exifData[ExifAddressableAreaStart:],
|
||||||
|
|
||||||
ByteOrder: ie.byteOrder,
|
ByteOrder: ie.byteOrder,
|
||||||
|
|
||||||
Ii: ii,
|
Ii: ii,
|
||||||
|
TagId: qi.TagId,
|
||||||
|
|
||||||
Id: id,
|
Id: id,
|
||||||
|
|
||||||
ParentIfd: parentIfd,
|
ParentIfd: parentIfd,
|
||||||
|
ParentTagIndex: qi.ParentTagIndex,
|
||||||
|
|
||||||
Name: name,
|
Name: name,
|
||||||
Index: index,
|
Index: index,
|
||||||
Offset: offset,
|
Offset: offset,
|
||||||
Entries: entries,
|
Entries: entries,
|
||||||
EntriesByTagId: entriesByTagId,
|
EntriesByTagId: entriesByTagId,
|
||||||
|
|
||||||
|
// This is populated as each child is processed.
|
||||||
Children: make([]*Ifd, 0),
|
Children: make([]*Ifd, 0),
|
||||||
|
|
||||||
NextIfdOffset: nextIfdOffset,
|
NextIfdOffset: nextIfdOffset,
|
||||||
thumbnailData: thumbnailData,
|
thumbnailData: thumbnailData,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add ourselves to a big list of IFDs.
|
// Add ourselves to a big list of IFDs.
|
||||||
ifds = append(ifds, &ifd)
|
ifds = append(ifds, ifd)
|
||||||
|
|
||||||
// Install ourselves into a by-id lookup table (keys are unique).
|
// Install ourselves into a by-id lookup table (keys are unique).
|
||||||
tree[id] = &ifd
|
tree[id] = ifd
|
||||||
|
|
||||||
// Install into by-name buckets.
|
// Install into by-name buckets.
|
||||||
|
|
||||||
if list_, found := lookup[ii]; found == true {
|
if list_, found := lookup[ii]; found == true {
|
||||||
lookup[ii] = append(list_, &ifd)
|
lookup[ii] = append(list_, ifd)
|
||||||
} else {
|
} else {
|
||||||
list_ = make([]*Ifd, 1)
|
list_ = make([]*Ifd, 1)
|
||||||
list_[0] = &ifd
|
list_[0] = ifd
|
||||||
|
|
||||||
lookup[ii] = list_
|
lookup[ii] = list_
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a link from the previous IFD in the chain to us.
|
// Add a link from the previous IFD in the chain to us.
|
||||||
if previousIfd, found := edges[offset]; found == true {
|
if previousIfd, found := edges[offset]; found == true {
|
||||||
previousIfd.NextIfd = &ifd
|
previousIfd.NextIfd = ifd
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach as a child to our parent (where we appeared as a tag in
|
// Attach as a child to our parent (where we appeared as a tag in
|
||||||
// that IFD).
|
// that IFD).
|
||||||
if parentIfd != nil {
|
if parentIfd != nil {
|
||||||
parentIfd.Children = append(parentIfd.Children, &ifd)
|
parentIfd.Children = append(parentIfd.Children, ifd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if any of our entries is a child IFD and queue it.
|
// Determine if any of our entries is a child IFD and queue it.
|
||||||
for _, entry := range entries {
|
for i, entry := range entries {
|
||||||
if entry.ChildIfdName == "" {
|
if entry.ChildIfdName == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -799,9 +833,12 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error
|
|||||||
|
|
||||||
qi := QueuedIfd{
|
qi := QueuedIfd{
|
||||||
Ii: childIi,
|
Ii: childIi,
|
||||||
|
TagId: entry.TagId,
|
||||||
|
|
||||||
Index: 0,
|
Index: 0,
|
||||||
Offset: entry.ValueOffset,
|
Offset: entry.ValueOffset,
|
||||||
Parent: &ifd,
|
Parent: ifd,
|
||||||
|
ParentTagIndex: i,
|
||||||
}
|
}
|
||||||
|
|
||||||
queue = append(queue, qi)
|
queue = append(queue, qi)
|
||||||
@ -810,10 +847,11 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error
|
|||||||
// If there's another IFD in the chain.
|
// If there's another IFD in the chain.
|
||||||
if nextIfdOffset != 0 {
|
if nextIfdOffset != 0 {
|
||||||
// Allow the next link to know what the previous link was.
|
// Allow the next link to know what the previous link was.
|
||||||
edges[nextIfdOffset] = &ifd
|
edges[nextIfdOffset] = ifd
|
||||||
|
|
||||||
qi := QueuedIfd{
|
qi := QueuedIfd{
|
||||||
Ii: ii,
|
Ii: ii,
|
||||||
|
TagId: 0xffff,
|
||||||
Index: index + 1,
|
Index: index + 1,
|
||||||
Offset: nextIfdOffset,
|
Offset: nextIfdOffset,
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user