ifd_builder: Can now build a complete chain from existing EXIF.

- ifd_builder:
  - We now validate that `builderTag` instances are either a byte array
    or a child Ifd before allowing to add.
  - Renamed `builderTag.valueBytes` to `builderTag.value` (it's not
    necessarily bytes).
  - `builderTag` now gets the child IFD name if representing a child
    IFD.
  - Added ability to dump structure to string slice.
  - Added NewIfdBuilderFromExistingChain to reconstruct whole chain
    from a read IFD chain (collection).
  - Wrote IB dumper.
  - Added structural tests.
  - Moved ifd_tag_entry types to separate file.

- tags_unknown: Unknown-type tags can now satisfy the `UnknownTagValue`
interface to return `[]byte`.
  - `IfdTagEntry.ValueBytes` automatically uses this. Else, error if not
    `[]byte` or `string`.

- ifd_enumerate
  - Now embed `ByteOrder` in `Ifd` (we otherwise require it to be passed
    a bit too often).

- Renamed some occurences of "IFD" to "IB" where an actual IB in order
  to not be confusing.
pull/3/head
Dustin Oprea 2018-04-23 04:32:17 -04:00
parent 50eafa98d6
commit 4bea09dc78
8 changed files with 639 additions and 306 deletions

View File

@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"bytes"
"strings"
"encoding/binary"
@ -20,20 +21,37 @@ var (
// TODO(dustin): !! Make sure we either replace existing IFDs or validate that the IFD doesn't already exist.
// TODO(dustin): !! Add test for NewIfdBuilderWithExistingIfd.
type builderTag struct {
// ifdName is non-empty if represents a child-IFD.
ifdName string
tagId 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.
valueBytes interface{}
value interface{}
}
func (bt builderTag) String() string {
return fmt.Sprintf("BuilderTag<TAG-ID=(0x%02x) IFD=[%s] VALUE=[%v]>", bt.tagId, bt.ifdName, bt.valueBytes)
valuePhrase := ""
switch bt.value.(type) {
case []byte:
valueBytes := bt.value.([]byte)
if len(valueBytes) <= 8 {
valuePhrase = fmt.Sprintf("%v", valueBytes)
} else {
valuePhrase = fmt.Sprintf("%v...", valueBytes[:8])
}
default:
valuePhrase = fmt.Sprintf("%v", bt.value)
}
return fmt.Sprintf("BuilderTag<TAG-ID=(0x%02x) IFD=[%s] VALUE=[%v]>", bt.tagId, bt.ifdName, valuePhrase)
}
@ -57,21 +75,12 @@ type IfdBuilder struct {
}
func NewIfdBuilder(ifdName string, byteOrder binary.ByteOrder) (ib *IfdBuilder) {
found := false
for _, validName := range validIfds {
if validName == ifdName {
found = true
break
}
}
if found == false {
log.Panicf("ifd not found: [%s]", ifdName)
}
ib = &IfdBuilder{
ifdName: ifdName,
// ifdName is empty unless it's a child-IFD.
ifdTagId: IfdTagIds[ifdName],
byteOrder: byteOrder,
tags: make([]builderTag, 0),
}
@ -79,16 +88,65 @@ func NewIfdBuilder(ifdName string, byteOrder binary.ByteOrder) (ib *IfdBuilder)
return ib
}
func NewIfdBuilderWithExistingIfd(ifd *Ifd, byteOrder binary.ByteOrder) (ib *IfdBuilder) {
// NewIfdBuilderWithExistingIfd creates a new IB using the same header type
// information as the given IFD.
func NewIfdBuilderWithExistingIfd(ifd *Ifd) (ib *IfdBuilder) {
ifdTagId, found := IfdTagIds[ifd.Name]
if found == false {
log.Panicf("tag-ID for IFD not found: [%s]", ifd.Name)
}
ib = &IfdBuilder{
ifdName: ifd.Name,
byteOrder: byteOrder,
ifdTagId: ifdTagId,
byteOrder: ifd.ByteOrder,
existingOffset: ifd.Offset,
}
return ib
}
// NewIfdBuilderFromExistingChain creates a chain of IB instances from an
// IFD chain generated from real data.
func NewIfdBuilderFromExistingChain(rootIfd *Ifd, exifData []byte) (rootIb *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).
var newIb *IfdBuilder
for thisExistingIfd := rootIfd; thisExistingIfd != nil; thisExistingIfd = thisExistingIfd.NextIfd {
lastIb := newIb
ifdName := thisExistingIfd.Name
if ifdName == "" {
ifdName = IfdStandard
}
newIb = NewIfdBuilder(ifdName, binary.BigEndian)
if lastIb != nil {
lastIb.SetNextIfd(newIb)
}
if rootIb == nil {
rootIb = newIb
}
err := newIb.AddTagsFromExisting(thisExistingIfd, itevr, nil, nil)
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)
}
}
return rootIb
}
func (ib *IfdBuilder) String() string {
nextIfdPhrase := ""
if ib.nextIfd != nil {
@ -114,6 +172,90 @@ func (ioi *ifdOffsetIterator) Offset() uint32 {
return ioi.offset
}
func (ib *IfdBuilder) Tags() (tags []builderTag) {
return ib.tags
}
func (ib *IfdBuilder) dump(levels int) {
indent := strings.Repeat(" ", levels * 4)
if levels == 0 {
fmt.Printf("%sIFD: %s\n", indent, ib)
} else {
fmt.Printf("%sChild IFD: %s\n", indent, ib)
}
ti := NewTagIndex()
if len(ib.tags) > 0 {
fmt.Printf("\n")
for i, tag := range ib.tags {
_, isChildIb := IfdTagNames[tag.tagId]
tagName := ""
// If a normal tag (not a child IFD) get the name.
if isChildIb == true {
tagName = "<Child IFD>"
} else {
it, err := ti.Get(tag.ifdName, tag.tagId)
if log.Is(err, ErrTagNotFound) == true {
tagName = "<UNKNOWN>"
} else if err != nil {
log.Panic(err)
} else {
tagName = it.Name
}
}
fmt.Printf("%s (%d): [%s] %s\n", indent, i, tagName, tag)
if isChildIb == true {
fmt.Printf("\n")
childIb := tag.value.(*IfdBuilder)
childIb.dump(levels + 1)
}
}
fmt.Printf("\n")
}
}
func (ib *IfdBuilder) Dump() {
ib.dump(0)
}
func (ib *IfdBuilder) dumpToStrings(thisIb *IfdBuilder, prefix string, lines []string) (linesOutput []string) {
if lines == nil {
linesOutput = make([]string, 0)
} else {
linesOutput = lines
}
for i, tag := range thisIb.tags {
line := fmt.Sprintf("<PARENTS=[%s] IFD-NAME=[%s]> IFD-TAG-ID=(0x%02x) CHILD-IFD=[%s] INDEX=(%d) TAG=[0x%02x]", prefix, thisIb.ifdName, thisIb.ifdTagId, tag.ifdName, i, tag.tagId)
linesOutput = append(linesOutput, line)
if tag.ifdName != "" {
childPrefix := ""
if prefix == "" {
childPrefix = fmt.Sprintf("%s", thisIb.ifdName)
} else {
childPrefix = fmt.Sprintf("%s->%s", prefix, thisIb.ifdName)
}
linesOutput = thisIb.dumpToStrings(tag.value.(*IfdBuilder), childPrefix, linesOutput)
}
}
return linesOutput
}
func (ib *IfdBuilder) DumpToStrings() (lines []string) {
return ib.dumpToStrings(ib, "", lines)
}
// calculateRawTableSize returns the number of bytes required just to store the
// basic IFD header and tags. This needs to be called before we can even write
@ -262,24 +404,6 @@ func (ib *IfdBuilder) BuildExif() (new []byte, err error) {
return b.Bytes(), nil
}
func (ib *IfdBuilder) Tags() (tags []builderTag) {
return ib.tags
}
func (ib *IfdBuilder) Dump() {
fmt.Printf("IFD: %s\n", ib)
if len(ib.tags) > 0 {
fmt.Printf("\n")
for i, tag := range ib.tags {
fmt.Printf(" (%d): %s\n", i, tag)
}
fmt.Printf("\n")
}
}
func (ib *IfdBuilder) SetNextIfd(nextIfd *IfdBuilder) (err error) {
defer func() {
if state := recover(); state != nil {
@ -437,43 +561,45 @@ func (ib *IfdBuilder) Add(bt builderTag) (err error) {
}
}()
if bt.value != nil {
switch bt.value.(type) {
case []byte:
default:
log.Panicf("tag value must be a byte-slice or a child IFD-builder: %v", bt)
}
}
ib.tags = append(ib.tags, bt)
return nil
}
func (ib *IfdBuilder) AddChildIfd(childIfd *IfdBuilder) (err error) {
func (ib *IfdBuilder) AddChildIb(childIb *IfdBuilder) (err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
// TODO(dustin): !! We might not want to take an actual IfdBuilder instance, as
// these are mutable in nature (unless we definitely want to
// allow them to tbe chnaged right up until they're actually
// written). We might be better with a final, immutable tag
// container insted.
if childIfd.ifdTagId == 0 {
log.Panicf("IFD [%s] can not be used as a child IFD (not associated with a tag-ID)")
} else if childIfd.byteOrder != ib.byteOrder {
log.Panicf("Child IFD does not have the same byte-order: [%s] != [%s]", childIfd.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)
}
bt := builderTag{
ifdName: childIfd.ifdName,
tagId: childIfd.ifdTagId,
valueBytes: childIfd,
ifdName: childIb.ifdName,
tagId: childIb.ifdTagId,
value: childIb,
}
ib.Add(bt)
ib.tags = append(ib.tags, bt)
return nil
}
// AddTagsFromExisting does a verbatim copy of the entries in `ifd` to this
// builder. It excludes child IFDs. This must be added explicitly via
// `AddChildIfd()`.
// `AddChildIb()`.
func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, itevr *IfdTagEntryValueResolver, includeTagIds []uint16, excludeTagIds []uint16) (err error) {
defer func() {
if state := recover(); state != nil {
@ -542,8 +668,15 @@ func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, itevr *IfdTagEntryValueResol
if itevr != nil {
var err error
bt.valueBytes, err = itevr.ValueBytes(&ite)
log.PanicIf(err)
bt.value, 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)
}
}
err := ib.Add(bt)

View File

@ -3,6 +3,8 @@ package exif
import (
"testing"
"reflect"
"bytes"
"path"
"encoding/binary"
@ -18,33 +20,37 @@ func TestAdd(t *testing.T) {
bt := builderTag{
tagId: 0x11,
valueBytes: "test string",
value: []byte("test string"),
}
ib.Add(bt)
err := ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x22,
valueBytes: "test string2",
value: []byte("test string2"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x33,
valueBytes: "test string3",
value: []byte("test string3"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
originalShorts := []uint16 { 0x111, 0x222, 0x333 }
originalBytes := []byte { 0x11, 0x22, 0x33 }
bt = builderTag{
tagId: 0x44,
valueBytes: originalShorts,
value: originalBytes,
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
if ib.ifdName != IfdStandard {
t.Fatalf("IFD name not correct.")
@ -64,25 +70,25 @@ func TestAdd(t *testing.T) {
if tags[0].tagId != 0x11 {
t.Fatalf("tag (0) tag-ID not correct")
} else if tags[0].valueBytes != "test string" {
} else if bytes.Compare(tags[0].value.([]byte), []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 tags[1].valueBytes != "test string2" {
} else if bytes.Compare(tags[1].value.([]byte), []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 tags[2].valueBytes != "test string3" {
} else if bytes.Compare(tags[2].value.([]byte), []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 reflect.DeepEqual(tags[3].valueBytes.([]uint16), originalShorts) != true {
} else if bytes.Compare(tags[3].value.([]byte), originalBytes) != 0 {
t.Fatalf("tag (3) value not correct")
}
}
@ -105,33 +111,35 @@ func TestSetNextIfd(t *testing.T) {
}
}
func TestAddChildIfd(t *testing.T) {
func TestAddChildIb(t *testing.T) {
ib := NewIfdBuilder(IfdStandard, binary.BigEndian)
bt := builderTag{
tagId: 0x11,
valueBytes: "test string",
value: []byte("test string"),
}
ib.Add(bt)
err := ib.Add(bt)
log.PanicIf(err)
ibChild := NewIfdBuilder(IfdExif, binary.BigEndian)
err := ib.AddChildIfd(ibChild)
err = ib.AddChildIb(ibChild)
log.PanicIf(err)
bt = builderTag{
tagId: 0x22,
valueBytes: "test string",
value: []byte("test string"),
}
ib.Add(bt)
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")
} else if ib.tags[1].valueBytes != ibChild {
t.Fatalf("second tag ID does not match child-IFD tag-ID: (0x%02x) != (0x%02x)", ib.tags[1].tagId, ibChild.ifdTagId)
} else if ib.tags[1].value != ibChild {
t.Fatalf("second tagvalue does not match child-IFD")
} else if ib.tags[2].tagId != 0x22 {
t.Fatalf("third tag not correct")
@ -241,24 +249,27 @@ func TestFindN_First_1(t *testing.T) {
bt := builderTag{
tagId: 0x11,
valueBytes: "test string",
value: []byte("test string"),
}
ib.Add(bt)
err := ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x22,
valueBytes: "test string2",
value: []byte("test string2"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x33,
valueBytes: "test string3",
value: []byte("test string3"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
found, err := ib.FindN(0x11, 1)
log.PanicIf(err)
@ -282,24 +293,27 @@ func TestFindN_First_2_1Returned(t *testing.T) {
bt := builderTag{
tagId: 0x11,
valueBytes: "test string",
value: []byte("test string"),
}
ib.Add(bt)
err := ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x22,
valueBytes: "test string2",
value: []byte("test string2"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x33,
valueBytes: "test string3",
value: []byte("test string3"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
found, err := ib.FindN(0x11, 2)
log.PanicIf(err)
@ -323,38 +337,43 @@ func TestFindN_First_2_2Returned(t *testing.T) {
bt := builderTag{
tagId: 0x11,
valueBytes: "test string",
value: []byte("test string"),
}
ib.Add(bt)
err := ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x22,
valueBytes: "test string2",
value: []byte("test string2"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x33,
valueBytes: "test string3",
value: []byte("test string3"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x11,
valueBytes: "test string4",
value: []byte("test string4"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x11,
valueBytes: "test string5",
value: []byte("test string5"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
found, err := ib.FindN(0x11, 2)
log.PanicIf(err)
@ -370,13 +389,13 @@ func TestFindN_First_2_2Returned(t *testing.T) {
tags := ib.Tags()
bt = tags[found[0]]
if bt.tagId != 0x11 || bt.valueBytes != "test string" {
log.Panicf("Found entry 0 is not correct: (0x%02x) [%s]", bt.tagId, bt.valueBytes)
if bt.tagId != 0x11 || bytes.Compare(bt.value.([]byte), []byte("test string")) != 0 {
log.Panicf("Found entry 0 is not correct: (0x%02x) [%s]", bt.tagId, bt.value)
}
bt = tags[found[1]]
if bt.tagId != 0x11 || bt.valueBytes != "test string4" {
log.Panicf("Found entry 1 is not correct: (0x%02x) [%s]", bt.tagId, bt.valueBytes)
if bt.tagId != 0x11 || bytes.Compare(bt.value.([]byte), []byte("test string4")) != 0 {
log.Panicf("Found entry 1 is not correct: (0x%02x) [%s]", bt.tagId, bt.value)
}
}
@ -385,45 +404,51 @@ func TestFindN_Middle_WithDuplicates(t *testing.T) {
bt := builderTag{
tagId: 0x11,
valueBytes: "test string",
value: []byte("test string"),
}
ib.Add(bt)
err := ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x22,
valueBytes: "test string2",
value: []byte("test string2"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x33,
valueBytes: "test string3",
value: []byte("test string3"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x11,
valueBytes: "test string4",
value: []byte("test string4"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x11,
valueBytes: "test string5",
value: []byte("test string5"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x33,
valueBytes: "test string6",
value: []byte("test string6"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
found, err := ib.FindN(0x33, 1)
log.PanicIf(err)
@ -447,31 +472,35 @@ func TestFindN_Middle_NoDuplicates(t *testing.T) {
bt := builderTag{
tagId: 0x11,
valueBytes: "test string",
value: []byte("test string"),
}
ib.Add(bt)
err := ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x22,
valueBytes: "test string2",
value: []byte("test string2"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x33,
valueBytes: "test string3",
value: []byte("test string3"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x11,
valueBytes: "test string4",
value: []byte("test string4"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
found, err := ib.FindN(0x33, 1)
log.PanicIf(err)
@ -506,31 +535,35 @@ func TestFind_Hit(t *testing.T) {
bt := builderTag{
tagId: 0x11,
valueBytes: "test string",
value: []byte("test string"),
}
ib.Add(bt)
err := ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x22,
valueBytes: "test string2",
value: []byte("test string2"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x33,
valueBytes: "test string3",
value: []byte("test string3"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x11,
valueBytes: "test string4",
value: []byte("test string4"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
position, err := ib.Find(0x33)
log.PanicIf(err)
@ -552,33 +585,37 @@ func TestFind_Miss(t *testing.T) {
bt := builderTag{
tagId: 0x11,
valueBytes: "test string",
value: []byte("test string"),
}
ib.Add(bt)
err := ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x22,
valueBytes: "test string2",
value: []byte("test string2"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x33,
valueBytes: "test string3",
value: []byte("test string3"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x11,
valueBytes: "test string4",
value: []byte("test string4"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
_, err := ib.Find(0x99)
_, err = ib.Find(0x99)
if err == nil {
t.Fatalf("Expected an error.")
} else if log.Is(err, ErrTagEntryNotFound) == false {
@ -591,24 +628,27 @@ func TestReplace(t *testing.T) {
bt := builderTag{
tagId: 0x11,
valueBytes: "test string",
value: []byte("test string"),
}
ib.Add(bt)
err := ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x22,
valueBytes: "test string2",
value: []byte("test string2"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x33,
valueBytes: "test string3",
value: []byte("test string3"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
currentIds := make([]uint16, 3)
for i, bt := range ib.Tags() {
@ -621,10 +661,10 @@ func TestReplace(t *testing.T) {
bt = builderTag{
tagId: 0x99,
valueBytes: "test string4",
value: []byte("test string4"),
}
err := ib.Replace(0x22, bt)
err = ib.Replace(0x22, bt)
log.PanicIf(err)
currentIds = make([]uint16, 3)
@ -642,24 +682,27 @@ func TestReplaceN(t *testing.T) {
bt := builderTag{
tagId: 0x11,
valueBytes: "test string",
value: []byte("test string"),
}
ib.Add(bt)
err := ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x22,
valueBytes: "test string2",
value: []byte("test string2"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x33,
valueBytes: "test string3",
value: []byte("test string3"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
currentIds := make([]uint16, 3)
for i, bt := range ib.Tags() {
@ -672,10 +715,10 @@ func TestReplaceN(t *testing.T) {
bt = builderTag{
tagId: 0xA9,
valueBytes: "test string4",
value: []byte("test string4"),
}
err := ib.ReplaceAt(1, bt)
err = ib.ReplaceAt(1, bt)
log.PanicIf(err)
currentIds = make([]uint16, 3)
@ -693,31 +736,35 @@ func TestDeleteFirst(t *testing.T) {
bt := builderTag{
tagId: 0x11,
valueBytes: "test string",
value: []byte("test string"),
}
ib.Add(bt)
err := ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x22,
valueBytes: "test string2",
value: []byte("test string2"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x22,
valueBytes: "test string3",
value: []byte("test string3"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x33,
valueBytes: "test string4",
value: []byte("test string4"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
if len(ib.Tags()) != 4 {
@ -734,7 +781,7 @@ func TestDeleteFirst(t *testing.T) {
}
err := ib.DeleteFirst(0x22)
err = ib.DeleteFirst(0x22)
log.PanicIf(err)
if len(ib.Tags()) != 3 {
@ -781,31 +828,35 @@ func TestDeleteN(t *testing.T) {
bt := builderTag{
tagId: 0x11,
valueBytes: "test string",
value: []byte("test string"),
}
ib.Add(bt)
err := ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x22,
valueBytes: "test string2",
value: []byte("test string2"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x22,
valueBytes: "test string3",
value: []byte("test string3"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x33,
valueBytes: "test string4",
value: []byte("test string4"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
if len(ib.Tags()) != 4 {
@ -822,7 +873,7 @@ func TestDeleteN(t *testing.T) {
}
err := ib.DeleteN(0x22, 1)
err = ib.DeleteN(0x22, 1)
log.PanicIf(err)
if len(ib.Tags()) != 3 {
@ -869,31 +920,35 @@ func TestDeleteN_Two(t *testing.T) {
bt := builderTag{
tagId: 0x11,
valueBytes: "test string",
value: []byte("test string"),
}
ib.Add(bt)
err := ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x22,
valueBytes: "test string2",
value: []byte("test string2"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x22,
valueBytes: "test string3",
value: []byte("test string3"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x33,
valueBytes: "test string4",
value: []byte("test string4"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
if len(ib.Tags()) != 4 {
@ -910,7 +965,7 @@ func TestDeleteN_Two(t *testing.T) {
}
err := ib.DeleteN(0x22, 2)
err = ib.DeleteN(0x22, 2)
log.PanicIf(err)
if len(ib.Tags()) != 2 {
@ -940,31 +995,35 @@ func TestDeleteAll(t *testing.T) {
bt := builderTag{
tagId: 0x11,
valueBytes: "test string",
value: []byte("test string"),
}
ib.Add(bt)
err := ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x22,
valueBytes: "test string2",
value: []byte("test string2"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x22,
valueBytes: "test string3",
value: []byte("test string3"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
bt = builderTag{
tagId: 0x33,
valueBytes: "test string4",
value: []byte("test string4"),
}
ib.Add(bt)
err = ib.Add(bt)
log.PanicIf(err)
if len(ib.Tags()) != 4 {
@ -1007,3 +1066,87 @@ func TestDeleteAll(t *testing.T) {
log.Panic(err)
}
}
func TestNewIfdBuilderFromExistingChain(t *testing.T) {
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
log.PrintErrorf(err, "Test failure.")
}
}()
e := NewExif()
filepath := path.Join(assetsPath, "NDM_8901.jpg")
exifData, err := e.SearchAndExtractExif(filepath)
log.PanicIf(err)
_, index, err := e.Collect(exifData)
log.PanicIf(err)
ib := NewIfdBuilderFromExistingChain(index.RootIfd, exifData)
lines := ib.DumpToStrings()
expected := []string {
"<PARENTS=[] IFD-NAME=[IFD]> IFD-TAG-ID=(0x00) CHILD-IFD=[] INDEX=(0) TAG=[0x10f]",
"<PARENTS=[] IFD-NAME=[IFD]> IFD-TAG-ID=(0x00) CHILD-IFD=[] INDEX=(1) TAG=[0x110]",
"<PARENTS=[] IFD-NAME=[IFD]> IFD-TAG-ID=(0x00) CHILD-IFD=[] INDEX=(2) TAG=[0x112]",
"<PARENTS=[] IFD-NAME=[IFD]> IFD-TAG-ID=(0x00) CHILD-IFD=[] INDEX=(3) TAG=[0x11a]",
"<PARENTS=[] IFD-NAME=[IFD]> IFD-TAG-ID=(0x00) CHILD-IFD=[] INDEX=(4) TAG=[0x11b]",
"<PARENTS=[] IFD-NAME=[IFD]> IFD-TAG-ID=(0x00) CHILD-IFD=[] INDEX=(5) TAG=[0x128]",
"<PARENTS=[] IFD-NAME=[IFD]> IFD-TAG-ID=(0x00) CHILD-IFD=[] INDEX=(6) TAG=[0x132]",
"<PARENTS=[] IFD-NAME=[IFD]> IFD-TAG-ID=(0x00) CHILD-IFD=[] INDEX=(7) TAG=[0x13b]",
"<PARENTS=[] IFD-NAME=[IFD]> IFD-TAG-ID=(0x00) CHILD-IFD=[] INDEX=(8) TAG=[0x213]",
"<PARENTS=[] IFD-NAME=[IFD]> IFD-TAG-ID=(0x00) CHILD-IFD=[] INDEX=(9) TAG=[0x8298]",
"<PARENTS=[] IFD-NAME=[IFD]> IFD-TAG-ID=(0x00) CHILD-IFD=[Exif] INDEX=(10) TAG=[0x8769]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(0) TAG=[0x829a]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(1) TAG=[0x829d]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(2) TAG=[0x8822]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(3) TAG=[0x8827]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(4) TAG=[0x8830]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(5) TAG=[0x8832]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(6) TAG=[0x9000]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(7) TAG=[0x9003]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(8) TAG=[0x9004]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(9) TAG=[0x9101]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(10) TAG=[0x9201]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(11) TAG=[0x9202]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(12) TAG=[0x9204]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(13) TAG=[0x9207]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(14) TAG=[0x9209]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(15) TAG=[0x920a]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(16) TAG=[0x927c]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(17) TAG=[0x9286]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(18) TAG=[0x9290]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(19) TAG=[0x9291]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(20) TAG=[0x9292]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(21) TAG=[0xa000]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(22) TAG=[0xa001]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(23) TAG=[0xa002]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(24) TAG=[0xa003]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(25) TAG=[0xa20e]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(26) TAG=[0xa20f]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(27) TAG=[0xa210]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(28) TAG=[0xa401]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(29) TAG=[0xa402]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(30) TAG=[0xa403]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(31) TAG=[0xa406]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(32) TAG=[0xa430]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(33) TAG=[0xa431]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(34) TAG=[0xa432]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(35) TAG=[0xa434]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(36) TAG=[0xa435]",
"<PARENTS=[IFD] IFD-NAME=[Exif]> IFD-TAG-ID=(0x8769) CHILD-IFD=[Iop] INDEX=(37) TAG=[0xa005]",
"<PARENTS=[IFD->Exif] IFD-NAME=[Iop]> IFD-TAG-ID=(0xa005) CHILD-IFD=[] INDEX=(0) TAG=[0x01]",
"<PARENTS=[IFD->Exif] IFD-NAME=[Iop]> IFD-TAG-ID=(0xa005) CHILD-IFD=[] INDEX=(1) TAG=[0x02]",
"<PARENTS=[] IFD-NAME=[IFD]> IFD-TAG-ID=(0x00) CHILD-IFD=[GPSInfo] INDEX=(11) TAG=[0x8825]",
"<PARENTS=[IFD] IFD-NAME=[GPSInfo]> IFD-TAG-ID=(0x8825) CHILD-IFD=[] INDEX=(0) TAG=[0x00]",
}
if reflect.DeepEqual(lines, expected) == false {
t.Fatalf("IB did not [correctly] duplicate the IFD structure")
}
}
// TODO(dustin): !! Test with a GPS-attached image.

View File

@ -124,112 +124,12 @@ func (ie *IfdEnumerate) getTagEnumerator(ifdOffset uint32) (ite *IfdTagEnumerato
return ite
}
// TagVisitor is an optional callback that can get hit for every tag we parse
// through. `addressableData` is the byte array startign after the EXIF header
// (where the offsets of all IFDs and values are calculated from).
type TagVisitor func(indexedIfdName string, tagId uint16, tagType TagType, valueContext ValueContext) (err error)
type IfdTagEntry struct {
TagId uint16
TagIndex int
TagType uint16
UnitCount uint32
ValueOffset uint32
RawValueOffset []byte
// ChildIfdName is a name if this tag represents a child IFD.
ChildIfdName string
// IfdName is the IFD that this tag belongs to.
IfdName string
}
func (ite IfdTagEntry) String() string {
return fmt.Sprintf("IfdTagEntry<TAG-IFD=[%s] TAG-ID=(0x%02x) TAG-TYPE=[%s] UNIT-COUNT=(%d)>", ite.ChildIfdName, ite.TagId, TypeNames[ite.TagType], ite.UnitCount)
}
func (ite IfdTagEntry) ValueBytes(addressableData []byte, byteOrder binary.ByteOrder) (value []byte, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
if ite.TagType == TypeUndefined {
valueContext := ValueContext{
UnitCount: ite.UnitCount,
ValueOffset: ite.ValueOffset,
RawValueOffset: ite.RawValueOffset,
AddressableData: addressableData,
}
value, err := UndefinedValue(ite.IfdName, ite.TagId, valueContext, byteOrder)
log.PanicIf(err)
switch value.(type) {
case []byte:
return value.([]byte), nil
case string:
return []byte(value.(string)), nil
default:
// TODO(dustin): !! Finish translating the rest of the types (make reusable and replace into other similar implementations?)
log.Panicf("can not produce bytes for unknown-type tag (0x%02x)", ite.TagId)
}
}
originalType := NewTagType(ite.TagType, byteOrder)
byteCount := uint32(originalType.Size()) * ite.UnitCount
tt := NewTagType(TypeByte, byteOrder)
if tt.ValueIsEmbedded(byteCount) == true {
typeDecodeLogger.Debugf(nil, "Reading BYTE value (ITE; embedded).")
// In this case, the bytes normally used for the offset are actually
// data.
value, err = tt.ParseBytes(ite.RawValueOffset, byteCount)
log.PanicIf(err)
} else {
typeDecodeLogger.Debugf(nil, "Reading BYTE value (ITE; at offset).")
value, err = tt.ParseBytes(addressableData[ite.ValueOffset:], byteCount)
log.PanicIf(err)
}
return value, nil
}
type IfdTagEntryValueResolver struct {
addressableData []byte
byteOrder binary.ByteOrder
}
func NewIfdTagEntryValueResolver(exifData []byte, byteOrder binary.ByteOrder) (itevr *IfdTagEntryValueResolver) {
// Make it obvious what data we expect and when we don't get it.
if IsExif(exifData) == false {
log.Panicf("not exif data")
}
return &IfdTagEntryValueResolver{
addressableData: exifData[ExifAddressableAreaStart:],
byteOrder: byteOrder,
}
}
func (itevr *IfdTagEntryValueResolver) ValueBytes(ite *IfdTagEntry) (value []byte, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
value, err = ite.ValueBytes(itevr.addressableData, itevr.byteOrder)
return value, err
}
// ParseIfd decodes the IFD block that we're currently sitting on the first
// byte of.
func (ie *IfdEnumerate) ParseIfd(ifdName string, ifdIndex int, ifdOffset uint32, visitor TagVisitor, doDescend bool) (nextIfdOffset uint32, entries []IfdTagEntry, err error) {
@ -347,6 +247,8 @@ func (ie *IfdEnumerate) Scan(ifdName string, ifdOffset uint32, visitor TagVisito
type Ifd struct {
ByteOrder binary.ByteOrder
Id int
ParentIfd *Ifd
Name string
@ -450,6 +352,7 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error
id := len(ifds)
ifd := Ifd{
ByteOrder: ie.byteOrder,
Id: id,
ParentIfd: parentIfd,
Name: name,

120
ifd_tag_entry.go Normal file
View File

@ -0,0 +1,120 @@
package exif
import (
"fmt"
"reflect"
"encoding/binary"
"github.com/dsoprea/go-logging"
)
var (
iteLogger = log.NewLogger("exif.ifd_tag_entry")
)
type IfdTagEntry struct {
TagId uint16
TagIndex int
TagType uint16
UnitCount uint32
ValueOffset uint32
RawValueOffset []byte
// ChildIfdName is a name if this tag represents a child IFD.
ChildIfdName string
// IfdName is the IFD that this tag belongs to.
IfdName string
}
func (ite IfdTagEntry) String() string {
return fmt.Sprintf("IfdTagEntry<TAG-IFD=[%s] TAG-ID=(0x%02x) TAG-TYPE=[%s] UNIT-COUNT=(%d)>", ite.ChildIfdName, ite.TagId, TypeNames[ite.TagType], ite.UnitCount)
}
func (ite IfdTagEntry) ValueBytes(addressableData []byte, byteOrder binary.ByteOrder) (value []byte, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
if ite.TagType == TypeUndefined {
valueContext := ValueContext{
UnitCount: ite.UnitCount,
ValueOffset: ite.ValueOffset,
RawValueOffset: ite.RawValueOffset,
AddressableData: addressableData,
}
value, err := UndefinedValue(ite.IfdName, ite.TagId, valueContext, byteOrder)
log.PanicIf(err)
switch value.(type) {
case []byte:
return value.([]byte), nil
case string:
return []byte(value.(string)), nil
case UnknownTagValue:
valueBytes, err := value.(UnknownTagValue).ValueBytes()
log.PanicIf(err)
return valueBytes, nil
default:
// TODO(dustin): !! Finish translating the rest of the types (make reusable and replace into other similar implementations?)
log.Panicf("can not produce bytes for unknown-type tag (0x%02x): [%s]", ite.TagId, reflect.TypeOf(value))
}
}
originalType := NewTagType(ite.TagType, byteOrder)
byteCount := uint32(originalType.Size()) * ite.UnitCount
tt := NewTagType(TypeByte, byteOrder)
if tt.ValueIsEmbedded(byteCount) == true {
iteLogger.Debugf(nil, "Reading BYTE value (ITE; embedded).")
// In this case, the bytes normally used for the offset are actually
// data.
value, err = tt.ParseBytes(ite.RawValueOffset, byteCount)
log.PanicIf(err)
} else {
iteLogger.Debugf(nil, "Reading BYTE value (ITE; at offset).")
value, err = tt.ParseBytes(addressableData[ite.ValueOffset:], byteCount)
log.PanicIf(err)
}
return value, nil
}
// IfdTagEntryValueResolver instances know how to resolve the values for any
// tag for a particular EXIF block.
type IfdTagEntryValueResolver struct {
addressableData []byte
byteOrder binary.ByteOrder
}
func NewIfdTagEntryValueResolver(exifData []byte, byteOrder binary.ByteOrder) (itevr *IfdTagEntryValueResolver) {
// Make it obvious what data we expect and when we don't get it.
if IsExif(exifData) == false {
log.Panicf("not exif data")
}
return &IfdTagEntryValueResolver{
addressableData: exifData[ExifAddressableAreaStart:],
byteOrder: byteOrder,
}
}
func (itevr *IfdTagEntryValueResolver) ValueBytes(ite *IfdTagEntry) (value []byte, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
value, err = ite.ValueBytes(itevr.addressableData, itevr.byteOrder)
return value, err
}

View File

@ -181,9 +181,9 @@ func (ti *TagIndex) Get(ifdName string, id uint16) (it *IndexedTag, err error) {
return it, nil
}
// GetIfdName returns the known index name for the tags that are expected/
// allowed for the IFD. If there's an error, returns "". If returns "", the IFD
// should be skipped.
// IfdName returns the known index name for the tags that are expected/allowed
// for the IFD. If there's an error, returns "". If returns "", the IFD should
// be skipped.
func IfdName(ifdName string, ifdIndex int) string {
// There's an IFD0 and IFD1, but the others must be unique.
if ifdName == IfdStandard && ifdIndex > 1 {

View File

@ -3,6 +3,8 @@ package exif
import (
"fmt"
"strings"
"github.com/dsoprea/go-logging"
)
const (
@ -69,6 +71,19 @@ var (
}
)
type UnknownTagValue interface {
ValueBytes() ([]byte, error)
}
type TagUnknownType_GeneralString string
func (gs TagUnknownType_GeneralString) ValueBytes() (value []byte, err error) {
return []byte(gs), nil
}
type TagUnknownType_9298_UserComment struct {
EncodingType int
EncodingBytes []byte
@ -78,6 +93,21 @@ func (uc TagUnknownType_9298_UserComment) String() string {
return fmt.Sprintf("UserComment<ENCODING=[%s] V=%v>", TagUnknownType_9298_UserComment_Encoding_Names[uc.EncodingType], uc.EncodingBytes)
}
func (uc TagUnknownType_9298_UserComment) ValueBytes() (value []byte, err error) {
encodingTypeBytes, found := TagUnknownType_9298_UserComment_Encodings[uc.EncodingType]
if found == false {
log.Panicf("encoding-type not valid for unknown-type tag 9298 (UserComment): (%d)", uc.EncodingType)
}
value = make([]byte, len(uc.EncodingBytes) + 8)
copy(value[:8], encodingTypeBytes)
// TODO(dustin): !! With undefined-encoded comments, we always make this empty. However, it comes in with a set of zero bytes. Is there a problem if we send it out with just the encoding bytes?
copy(value[8:], uc.EncodingBytes)
return value, nil
}
type TagUnknownType_927C_MakerNote struct {
MakerNoteType []byte
@ -93,6 +123,10 @@ func (mn TagUnknownType_927C_MakerNote) String() string {
return fmt.Sprintf("MakerNote<TYPE-ID=[%s]>", strings.Join(parts, " "))
}
func (uc TagUnknownType_927C_MakerNote) ValueBytes() (value []byte, err error) {
return uc.MakerNoteBytes, nil
}
type TagUnknownType_9101_ComponentsConfiguration struct {
ConfigurationId int
@ -102,3 +136,7 @@ type TagUnknownType_9101_ComponentsConfiguration struct {
func (cc TagUnknownType_9101_ComponentsConfiguration) String() string {
return fmt.Sprintf("ComponentsConfiguration<ID=[%s] BYTES=%v>", TagUnknownType_9101_ComponentsConfiguration_Names[cc.ConfigurationId], cc.ConfigurationBytes)
}
func (uc TagUnknownType_9101_ComponentsConfiguration) ValueBytes() (value []byte, err error) {
return uc.ConfigurationBytes, nil
}

View File

@ -36,10 +36,6 @@ var (
)
var (
// ErrCantDetermineTagValueSize is used when we're trying to determine a
//size for a non-standard/undefined type.
ErrCantDetermineTagValueSize = errors.New("can not determine tag-value size")
// ErrNotEnoughData is used when there isn't enough data to accomodate what
// we're trying to parse (sizeof(type) * unit_count).
ErrNotEnoughData = errors.New("not enough data for type")

View File

@ -66,7 +66,7 @@ func (tt TagType) Size() int {
} else if tt.tagType == TypeSignedRational {
return 8
} else {
log.Panic(ErrCantDetermineTagValueSize)
log.Panicf("can not determine tag-value size for type (%d): [%s]", tt.tagType, TypeNames[tt.tagType])
// Never called.
return 0
@ -609,19 +609,19 @@ func UndefinedValue(indexedIfdName string, tagId uint16, valueContext ValueConte
tt := NewTagType(TypeAsciiNoNul, byteOrder)
value, err = tt.ReadAsciiValue(valueContext)
valueString, err := tt.ReadAsciiValue(valueContext)
log.PanicIf(err)
return value, nil
return TagUnknownType_GeneralString(valueString), nil
} else if tagId == 0xa000 {
// FlashpixVersion
tt := NewTagType(TypeAsciiNoNul, byteOrder)
value, err = tt.ReadAsciiValue(valueContext)
valueString, err := tt.ReadAsciiValue(valueContext)
log.PanicIf(err)
return value, nil
return TagUnknownType_GeneralString(valueString), nil
} else if tagId == 0x9286 {
// UserComment
@ -719,19 +719,19 @@ func UndefinedValue(indexedIfdName string, tagId uint16, valueContext ValueConte
tt := NewTagType(TypeAsciiNoNul, byteOrder)
value, err = tt.ReadAsciiValue(valueContext)
valueString, err := tt.ReadAsciiValue(valueContext)
log.PanicIf(err)
return value, nil
return TagUnknownType_GeneralString(valueString), nil
} else if tagId == 0x001b {
// GPSProcessingMethod
tt := NewTagType(TypeAsciiNoNul, byteOrder)
value, err = tt.ReadAsciiValue(valueContext)
valueString, err := tt.ReadAsciiValue(valueContext)
log.PanicIf(err)
return value, nil
return TagUnknownType_GeneralString(valueString), nil
}
} else if indexedIfdName == IfdName(IfdIop, 0) {
if tagId == 0x0002 {
@ -739,10 +739,10 @@ func UndefinedValue(indexedIfdName string, tagId uint16, valueContext ValueConte
tt := NewTagType(TypeAsciiNoNul, byteOrder)
value, err := tt.ReadAsciiNoNulValue(valueContext)
valueString, err := tt.ReadAsciiValue(valueContext)
log.PanicIf(err)
return value, nil
return TagUnknownType_GeneralString(valueString), nil
}
}