mirror of https://github.com/dsoprea/go-exif.git
tags_unknown: Seeded a first test for encode/decode.
- type_encode: `EncodeWithType` can now encode TypeAsciiNoNul. - Resolved/removed a bunch of to-do's.pull/3/head
parent
cebf021131
commit
b748843a75
|
@ -0,0 +1,33 @@
|
|||
run the example exif reader tool against all of my images.
|
||||
|
||||
|
||||
updating tags:
|
||||
|
||||
- if the data fits in the previous structure or allocation, stick with it and just update the length.
|
||||
- Note that, if the size decreases to <=4 bytes, we might need to deallocate and embed the value directly in the offset LONG (per the spec).
|
||||
|
||||
adding tags:
|
||||
|
||||
- It would be safest to identify where the next IFD would start (or, (len(raw_exif_data) + 1) if we're in the last IFD), and just append the data, shift the offset of the next IFD, and update the IFD offset (either the pointer in a tag or the offset following the last IFD if we're past the first in a chain).
|
||||
- We might not otherwise be able to derive the total size of the current tags given 1) those tags having an unknown type with a size that is not easily calculated, and 2) non-standard tags that we simply don't know how to parse.
|
||||
- Note that we need to build a table of IFD offsets as well as keep track where the offset of each was recorded.
|
||||
|
||||
- allow to delete tags
|
||||
- allow to delete whole IFDs (and implicitly deallocate tags).
|
||||
- Only possible if we can parse them and determine the offset and length for the value.
|
||||
|
||||
- allow to create IFDs.
|
||||
|
||||
|
||||
Other
|
||||
==
|
||||
|
||||
- allow to import/export tags from structured data.
|
||||
- consider writing a couple of quick tools for GPS
|
||||
- parsing GeJSON and GPX data and installing GPS tags to the EXIF
|
||||
- writing GeJSON and GPX data *from* the EXIF
|
||||
|
||||
|
||||
Notes
|
||||
==
|
||||
We might be discretionary in allowing people to *easily* update certain undefined-type tags if they're too complex. We might provide a method to allow them to pass a raw bye-array and count and take responsibility for that values structure (so we don't prevent sufficiently-advanced users from doing from they need to do.
|
|
@ -73,6 +73,14 @@ type builderTag struct {
|
|||
value *IfdBuilderTagValue
|
||||
}
|
||||
|
||||
func NewBuilderTag(ii IfdIdentity, tagId uint16, value *IfdBuilderTagValue) builderTag {
|
||||
return builderTag{
|
||||
ii: ii,
|
||||
tagId: tagId,
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (bt builderTag) String() string {
|
||||
valuePhrase := ""
|
||||
|
||||
|
|
|
@ -216,8 +216,6 @@ func (ibe *IfdByteEncoder) encodeTagToBytes(ib *IfdBuilder, bt *builderTag, bw *
|
|||
if nextIfdOffsetToWrite > 0 {
|
||||
var err error
|
||||
|
||||
// TODO(dustin): Create a tool to print/dump the structure and allocation of the output data.
|
||||
|
||||
// Create the block of IFD data and everything it requires.
|
||||
childIfdBlock, err = ibe.encodeAndAttachIfd(bt.value.Ib(), nextIfdOffsetToWrite)
|
||||
log.PanicIf(err)
|
||||
|
@ -341,7 +339,6 @@ func (ibe *IfdByteEncoder) encodeAndAttachIfd(ib *IfdBuilder, ifdAddressableOffs
|
|||
log.Panicf("trying to encode an IfdBuilder that doesn't have any tags")
|
||||
}
|
||||
|
||||
// TODO(dustin): !! There's a lot of numbers-agreement required in our current implementation (where offsets are independently calculated in multiple areas, such as in the first and second runs of encodeIfdToBytes). Refactor this to share the offsets rather than repeatedly calculating them (which has a risk of fallign out of aligning and there being cosnsitency problems).
|
||||
b := new(bytes.Buffer)
|
||||
|
||||
nextIfdOffsetToWrite := uint32(0)
|
||||
|
@ -358,7 +355,7 @@ func (ibe *IfdByteEncoder) encodeAndAttachIfd(ib *IfdBuilder, ifdAddressableOffs
|
|||
// where new IFDs and their data will be allocated).
|
||||
|
||||
setNextIb := thisIb.nextIb != nil
|
||||
// TODO(dustin): !! Test the output sizes.
|
||||
|
||||
tableAndAllocated, tableSize, allocatedDataSize, _, err := ibe.encodeIfdToBytes(ib, addressableOffset, nextIfdOffsetToWrite, setNextIb)
|
||||
log.PanicIf(err)
|
||||
|
||||
|
|
|
@ -192,7 +192,6 @@ func Test_IfdByteEncoder_encodeTagToBytes_bytes_embedded1(t *testing.T) {
|
|||
addressableOffset := uint32(0x1234)
|
||||
ida := newIfdDataAllocator(addressableOffset)
|
||||
|
||||
// TODO(dustin): !! Test with and without nextIfdOffsetToWrite.
|
||||
childIfdBlock, err := ibe.encodeTagToBytes(ib, &bt, bw, ida, uint32(0))
|
||||
log.PanicIf(err)
|
||||
|
||||
|
@ -218,7 +217,6 @@ func Test_IfdByteEncoder_encodeTagToBytes_bytes_embedded2(t *testing.T) {
|
|||
addressableOffset := uint32(0x1234)
|
||||
ida := newIfdDataAllocator(addressableOffset)
|
||||
|
||||
// TODO(dustin): !! Test with and without nextIfdOffsetToWrite.
|
||||
childIfdBlock, err := ibe.encodeTagToBytes(ib, &bt, bw, ida, uint32(0))
|
||||
log.PanicIf(err)
|
||||
|
||||
|
@ -244,7 +242,6 @@ func Test_IfdByteEncoder_encodeTagToBytes_bytes_allocated(t *testing.T) {
|
|||
|
||||
bt := NewBuilderTagFromConfig(GpsIi, 0x0000, TestDefaultByteOrder, []uint8 { uint8(0x12), uint8(0x34), uint8(0x56), uint8(0x78), uint8(0x9a) })
|
||||
|
||||
// TODO(dustin): !! Test with and without nextIfdOffsetToWrite.
|
||||
childIfdBlock, err := ibe.encodeTagToBytes(ib, &bt, bw, ida, uint32(0))
|
||||
log.PanicIf(err)
|
||||
|
||||
|
@ -262,7 +259,6 @@ func Test_IfdByteEncoder_encodeTagToBytes_bytes_allocated(t *testing.T) {
|
|||
|
||||
bt = NewBuilderTagFromConfig(GpsIi, 0x0000, TestDefaultByteOrder, []uint8 { uint8(0xbc), uint8(0xde), uint8(0xf0), uint8(0x12), uint8(0x34) })
|
||||
|
||||
// TODO(dustin): !! Test with and without nextIfdOffsetToWrite.
|
||||
childIfdBlock, err = ibe.encodeTagToBytes(ib, &bt, bw, ida, uint32(0))
|
||||
log.PanicIf(err)
|
||||
|
||||
|
@ -389,11 +385,6 @@ func Test_IfdByteEncoder_encodeTagToBytes_childIfd__withAllocate(t *testing.T) {
|
|||
t.Fatalf("IFD first tag parent IFD not correct: %v", iteV.Ii)
|
||||
}
|
||||
|
||||
|
||||
// TODO(dustin): Test writing some tags that require allocation.
|
||||
// TODO(dustin): Do an child-IFD allocation in addition to some tag allocations, and vice-verse.
|
||||
|
||||
|
||||
// Validate the child's raw IFD bytes.
|
||||
|
||||
childNextIfdOffset, childEntries, err := ParseOneIfd(ExifIi, TestDefaultByteOrder, childIfdBlock, nil)
|
||||
|
@ -468,8 +459,6 @@ func Test_IfdByteEncoder_encodeTagToBytes_simpleTag_allocate(t *testing.T) {
|
|||
ite, err := ParseOneTag(RootIi, TestDefaultByteOrder, tagBytes)
|
||||
log.PanicIf(err)
|
||||
|
||||
// TODO(dustin): !! When we eventually start allocating values and child-IFDs, be careful that the offsets are calculated correctly.
|
||||
|
||||
if ite.TagId != 0x000b {
|
||||
t.Fatalf("Tag-ID not correct: (0x%02x)", ite.TagId)
|
||||
} else if ite.TagIndex != 0 {
|
||||
|
@ -735,6 +724,8 @@ func Test_IfdByteEncoder_EncodeToExifPayload(t *testing.T) {
|
|||
func Test_IfdByteEncoder_EncodeToExif(t *testing.T) {
|
||||
ib := getExifSimpleTestIb()
|
||||
|
||||
// TODO(dustin): Do a child-IFD allocation in addition to the tag allocations.
|
||||
|
||||
ibe := NewIfdByteEncoder()
|
||||
|
||||
exifData, err := ibe.EncodeToExif(ib)
|
||||
|
@ -805,6 +796,5 @@ func ExampleIfdByteEncoder_EncodeToExif() {
|
|||
|
||||
// TODO(dustin): !! Write test with both chained and child IFDs
|
||||
|
||||
// TODO(dustin): !! Test all types.
|
||||
// TODO(dustin): !! Test specific unknown-type tags.
|
||||
// TODO(dustin): !! Test what happens with unhandled unknown-type tags (though it should never get to this point in the normal workflow).
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
package exif
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"bytes"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
)
|
||||
|
||||
func TestUndefinedValue_ExifVersion(t *testing.T) {
|
||||
byteOrder := TestDefaultByteOrder
|
||||
ii := ExifIi
|
||||
|
||||
|
||||
// Create our unknown-type tag's value using the fact that we know it's a
|
||||
// non-null-terminated string.
|
||||
|
||||
ve := NewValueEncoder(byteOrder)
|
||||
|
||||
tt := NewTagType(TypeAsciiNoNul, byteOrder)
|
||||
valueString := "0230"
|
||||
|
||||
ed, err := ve.EncodeWithType(tt, valueString)
|
||||
log.PanicIf(err)
|
||||
|
||||
|
||||
// Create the tag using the official "unknown" type now that we already
|
||||
// have the bytes.
|
||||
|
||||
encodedValue := NewIfdBuilderTagValueFromBytes(ed.Encoded)
|
||||
bt := NewBuilderTag(ii, 0x9000, encodedValue)
|
||||
|
||||
|
||||
// Stage the build.
|
||||
|
||||
ibe := NewIfdByteEncoder()
|
||||
ib := NewIfdBuilder(ii, byteOrder)
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
bw := NewByteWriter(b, byteOrder)
|
||||
|
||||
addressableOffset := uint32(0x1234)
|
||||
ida := newIfdDataAllocator(addressableOffset)
|
||||
|
||||
|
||||
// Encode.
|
||||
|
||||
_, err = ibe.encodeTagToBytes(ib, &bt, bw, ida, uint32(0))
|
||||
log.PanicIf(err)
|
||||
|
||||
tagBytes := b.Bytes()
|
||||
|
||||
if len(tagBytes) != 12 {
|
||||
t.Fatalf("Tag not encoded to the right number of bytes: (%d)", len(tagBytes))
|
||||
}
|
||||
|
||||
ite, err := ParseOneTag(ii, byteOrder, tagBytes)
|
||||
log.PanicIf(err)
|
||||
|
||||
if ite.TagId != 0x9000 {
|
||||
t.Fatalf("Tag-ID not correct: (0x%02x)", ite.TagId)
|
||||
} else if ite.TagIndex != 0 {
|
||||
t.Fatalf("Tag index not correct: (%d)", ite.TagIndex)
|
||||
} else if ite.TagType != TypeUndefined {
|
||||
t.Fatalf("Tag type not correct: (%d)", ite.TagType)
|
||||
} else if ite.UnitCount != (uint32(len(valueString))) {
|
||||
t.Fatalf("Tag unit-count not correct: (%d)", ite.UnitCount)
|
||||
} else if bytes.Compare(ite.RawValueOffset, []byte { '0', '2', '3', '0' }) != 0 {
|
||||
t.Fatalf("Tag's value (as raw bytes) is not correct: [%x]", ite.RawValueOffset)
|
||||
} else if ite.ChildIfdName != "" {
|
||||
t.Fatalf("Tag's IFD-name should be empty: [%s]", ite.ChildIfdName)
|
||||
} else if ite.Ii != ii {
|
||||
t.Fatalf("Tag's parent IFD is not correct: %v", ite.Ii)
|
||||
}
|
||||
}
|
|
@ -204,6 +204,9 @@ func (ve *ValueEncoder) encodeSignedRationals(value []SignedRational) (ed Encode
|
|||
return ed, nil
|
||||
}
|
||||
|
||||
// Encode returns bytes for the given value, infering type from the actual
|
||||
// value. This does not support `TypeAsciiNoNull` (all strings are encoded as
|
||||
// `TypeAscii`).
|
||||
func (ve *ValueEncoder) Encode(value interface{}) (ed EncodedData, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
|
@ -240,6 +243,8 @@ func (ve *ValueEncoder) Encode(value interface{}) (ed EncodedData, err error) {
|
|||
return ed, nil
|
||||
}
|
||||
|
||||
// EncodeWithType returns bytes for the given value, using the given `TagType`
|
||||
// value to determine how to encode. This supports `TypeAsciiNoNul`.
|
||||
func (ve *ValueEncoder) EncodeWithType(tt TagType, value interface{}) (ed EncodedData, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
|
@ -254,6 +259,9 @@ func (ve *ValueEncoder) EncodeWithType(tt TagType, value interface{}) (ed Encode
|
|||
case TypeAscii:
|
||||
ed, err = ve.encodeAscii(value.(string))
|
||||
log.PanicIf(err)
|
||||
case TypeAsciiNoNul:
|
||||
ed, err = ve.encodeAsciiNoNul(value.(string))
|
||||
log.PanicIf(err)
|
||||
case TypeShort:
|
||||
ed, err = ve.encodeShorts(value.([]uint16))
|
||||
log.PanicIf(err)
|
||||
|
|
Loading…
Reference in New Issue