mirror of https://github.com/dsoprea/go-exif.git
ifd_builder_encoder: Added method to package full EXIF.
- Moved the EXIF-parsing being done in the tests to reusable code.pull/3/head
parent
9c721a573d
commit
2fca65a8ea
3
exif.go
3
exif.go
|
@ -35,6 +35,9 @@ var (
|
||||||
exifLogger = log.NewLogger("exif.exif")
|
exifLogger = log.NewLogger("exif.exif")
|
||||||
|
|
||||||
ExifHeaderPrefixBytes = []byte("Exif\000\000")
|
ExifHeaderPrefixBytes = []byte("Exif\000\000")
|
||||||
|
|
||||||
|
// DefaultEncodeByteOrder is the default byte-order for encoding operations.
|
||||||
|
DefaultEncodeByteOrder = binary.BigEndian
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -376,8 +376,9 @@ func (ibe *IfdByteEncoder) encodeAndAttachIfd(ib *IfdBuilder, ifdAddressableOffs
|
||||||
return b.Bytes(), nil
|
return b.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeToBytes is the base encoding step.
|
// EncodeToExifPayload is the base encoding step that transcribes the entire IB
|
||||||
func (ibe *IfdByteEncoder) EncodeToBytes(ib *IfdBuilder) (data []byte, err error) {
|
// structure to its on-disk layout.
|
||||||
|
func (ibe *IfdByteEncoder) EncodeToExifPayload(ib *IfdBuilder) (data []byte, 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))
|
||||||
|
@ -389,3 +390,31 @@ func (ibe *IfdByteEncoder) EncodeToBytes(ib *IfdBuilder) (data []byte, err error
|
||||||
|
|
||||||
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))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
encodedIfds, err := ibe.EncodeToExifPayload(ib)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
// Wrap the IFD in a formal EXIF block.
|
||||||
|
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
|
||||||
|
headerBytes, err := BuildExifHeader(DefaultEncodeByteOrder, ExifDefaultFirstIfdOffset)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
_, err = b.Write(headerBytes)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
_, err = b.Write(encodedIfds)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
|
@ -498,9 +498,7 @@ func Test_IfdByteEncoder_encodeTagToBytes_simpleTag_allocate(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_IfdByteEncoder_encodeIfdToBytes_simple(t *testing.T) {
|
func getExifSimpleTestIb() *IfdBuilder {
|
||||||
// Build the IB.
|
|
||||||
|
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
||||||
|
|
||||||
err := ib.AddFromConfig(0x000b, "asciivalue")
|
err := ib.AddFromConfig(0x000b, "asciivalue")
|
||||||
|
@ -515,6 +513,75 @@ func Test_IfdByteEncoder_encodeIfdToBytes_simple(t *testing.T) {
|
||||||
err = ib.AddFromConfig(0x013e, []Rational { { Numerator: 0x11112222, Denominator: 0x33334444 } })
|
err = ib.AddFromConfig(0x013e, []Rational { { Numerator: 0x11112222, Denominator: 0x33334444 } })
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
return ib
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateExifSimpleTestIb(exifData []byte, t *testing.T) {
|
||||||
|
e := NewExif()
|
||||||
|
|
||||||
|
eh, index, err := e.Collect(exifData)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
if eh.ByteOrder != TestDefaultByteOrder {
|
||||||
|
t.Fatalf("EXIF byte-order is not correct: %v", eh.ByteOrder)
|
||||||
|
} else if eh.FirstIfdOffset != ExifDefaultFirstIfdOffset {
|
||||||
|
t.Fatalf("EXIF first IFD-offset not correct: (0x%02x)", eh.FirstIfdOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(index.Ifds) != 1 {
|
||||||
|
t.Fatalf("There wasn't exactly one IFD decoded: (%d)", len(index.Ifds))
|
||||||
|
}
|
||||||
|
|
||||||
|
ifd := index.RootIfd
|
||||||
|
|
||||||
|
if ifd.ByteOrder != TestDefaultByteOrder {
|
||||||
|
t.Fatalf("IFD byte-order not correct.")
|
||||||
|
} else if ifd.Name != IfdStandard {
|
||||||
|
t.Fatalf("IFD name not correct.")
|
||||||
|
} else if ifd.Index != 0 {
|
||||||
|
t.Fatalf("IFD index not zero: (%d)", ifd.Index)
|
||||||
|
} else if ifd.Offset != RootIfdExifOffset {
|
||||||
|
t.Fatalf("IFD offset not correct.")
|
||||||
|
} else if len(ifd.Entries) != 4 {
|
||||||
|
t.Fatalf("IFD number of entries not correct: (%d)", len(ifd.Entries))
|
||||||
|
} else if ifd.NextIfdOffset != uint32(0) {
|
||||||
|
t.Fatalf("Next-IFD offset is non-zero.")
|
||||||
|
} else if ifd.NextIfd != nil {
|
||||||
|
t.Fatalf("Next-IFD pointer is non-nil.")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Verify the values by using the actual, orginal types (this is awesome).
|
||||||
|
|
||||||
|
addressableData := exifData[ExifAddressableAreaStart:]
|
||||||
|
|
||||||
|
expected := []struct{
|
||||||
|
tagId uint16
|
||||||
|
value interface{}
|
||||||
|
}{
|
||||||
|
{ tagId: 0x000b, value: "asciivalue" },
|
||||||
|
{ tagId: 0x00ff, value: []uint16 { 0x1122 } },
|
||||||
|
{ tagId: 0x0100, value: []uint32 { 0x33445566 } },
|
||||||
|
{ tagId: 0x013e, value: []Rational {{ Numerator: 0x11112222, Denominator: 0x33334444 }} },
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, e := range ifd.Entries {
|
||||||
|
if e.TagId != expected[i].tagId {
|
||||||
|
t.Fatalf("Tag-ID for entry (%d) not correct: (0x%02x) != (0x%02x)", i, e.TagId, expected[i].tagId)
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := e.Value(TestDefaultByteOrder, addressableData)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
if reflect.DeepEqual(value, expected[i].value) != true {
|
||||||
|
t.Fatalf("Value for entry (%d) not correct: [%v] != [%v]", i, value, expected[i].value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_IfdByteEncoder_encodeIfdToBytes_simple(t *testing.T) {
|
||||||
|
ib := getExifSimpleTestIb()
|
||||||
|
|
||||||
// Write the byte stream.
|
// Write the byte stream.
|
||||||
|
|
||||||
ibe := NewIfdByteEncoder()
|
ibe := NewIfdByteEncoder()
|
||||||
|
@ -581,21 +648,7 @@ func Test_IfdByteEncoder_encodeIfdToBytes_fullExif(t *testing.T) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Build the IB.
|
ib := getExifSimpleTestIb()
|
||||||
|
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
|
||||||
|
|
||||||
err := ib.AddFromConfig(0x000b, "asciivalue")
|
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
err = ib.AddFromConfig(0x00ff, []uint16 { 0x1122 })
|
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
err = ib.AddFromConfig(0x0100, []uint32 { 0x33445566 })
|
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
err = ib.AddFromConfig(0x013e, []Rational { { Numerator: 0x11112222, Denominator: 0x33334444 } })
|
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
|
|
||||||
// Encode the IFD to a byte stream.
|
// Encode the IFD to a byte stream.
|
||||||
|
@ -636,70 +689,10 @@ func Test_IfdByteEncoder_encodeIfdToBytes_fullExif(t *testing.T) {
|
||||||
// dereference) the values (which will include the allocated ones).
|
// dereference) the values (which will include the allocated ones).
|
||||||
|
|
||||||
exifData := b.Bytes()
|
exifData := b.Bytes()
|
||||||
|
validateExifSimpleTestIb(exifData, t)
|
||||||
e := NewExif()
|
|
||||||
|
|
||||||
eh, index, err := e.Collect(exifData)
|
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
if eh.ByteOrder != TestDefaultByteOrder {
|
|
||||||
t.Fatalf("EXIF byte-order is not correct: %v", eh.ByteOrder)
|
|
||||||
} else if eh.FirstIfdOffset != ExifDefaultFirstIfdOffset {
|
|
||||||
t.Fatalf("EXIF first IFD-offset not correct: (0x%02x)", eh.FirstIfdOffset)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(index.Ifds) != 1 {
|
|
||||||
t.Fatalf("There wasn't exactly one IFD decoded: (%d)", len(index.Ifds))
|
|
||||||
}
|
|
||||||
|
|
||||||
ifd := index.RootIfd
|
|
||||||
|
|
||||||
if ifd.ByteOrder != TestDefaultByteOrder {
|
|
||||||
t.Fatalf("IFD byte-order not correct.")
|
|
||||||
} else if ifd.Name != IfdStandard {
|
|
||||||
t.Fatalf("IFD name not correct.")
|
|
||||||
} else if ifd.Index != 0 {
|
|
||||||
t.Fatalf("IFD index not zero: (%d)", ifd.Index)
|
|
||||||
} else if ifd.Offset != RootIfdExifOffset {
|
|
||||||
t.Fatalf("IFD offset not correct.")
|
|
||||||
} else if len(ifd.Entries) != 4 {
|
|
||||||
t.Fatalf("IFD number of entries not correct: (%d)", len(ifd.Entries))
|
|
||||||
} else if ifd.NextIfdOffset != uint32(0) {
|
|
||||||
t.Fatalf("Next-IFD offset is non-zero.")
|
|
||||||
} else if ifd.NextIfd != nil {
|
|
||||||
t.Fatalf("Next-IFD pointer is non-nil.")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Verify the values by using the actual, orginal types (this is awesome).
|
|
||||||
|
|
||||||
addressableData := exifData[ExifAddressableAreaStart:]
|
|
||||||
|
|
||||||
expected := []struct{
|
|
||||||
tagId uint16
|
|
||||||
value interface{}
|
|
||||||
}{
|
|
||||||
{ tagId: 0x000b, value: "asciivalue" },
|
|
||||||
{ tagId: 0x00ff, value: []uint16 { 0x1122 } },
|
|
||||||
{ tagId: 0x0100, value: []uint32 { 0x33445566 } },
|
|
||||||
{ tagId: 0x013e, value: []Rational {{ Numerator: 0x11112222, Denominator: 0x33334444 }} },
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, e := range ifd.Entries {
|
|
||||||
if e.TagId != expected[i].tagId {
|
|
||||||
t.Fatalf("Tag-ID for entry (%d) not correct: (0x%02x) != (0x%02x)", i, e.TagId, expected[i].tagId)
|
|
||||||
}
|
|
||||||
|
|
||||||
value, err := e.Value(TestDefaultByteOrder, addressableData)
|
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
if reflect.DeepEqual(value, expected[i].value) != true {
|
|
||||||
t.Fatalf("Value for entry (%d) not correct: [%v] != [%v]", i, value, expected[i].value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_IfdByteEncoder_EncodeToBytes(t *testing.T) {
|
func Test_IfdByteEncoder_EncodeToExifPayload(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))
|
||||||
|
@ -707,28 +700,14 @@ func Test_IfdByteEncoder_EncodeToBytes(t *testing.T) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Build the IB.
|
ib := getExifSimpleTestIb()
|
||||||
|
|
||||||
ib := NewIfdBuilder(RootIi, TestDefaultByteOrder)
|
|
||||||
|
|
||||||
err := ib.AddFromConfig(0x000b, "asciivalue")
|
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
err = ib.AddFromConfig(0x00ff, []uint16 { 0x1122 })
|
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
err = ib.AddFromConfig(0x0100, []uint32 { 0x33445566 })
|
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
err = ib.AddFromConfig(0x013e, []Rational { { Numerator: 0x11112222, Denominator: 0x33334444 } })
|
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
|
|
||||||
// Encode the IFD to a byte stream.
|
// Encode the IFD to a byte stream.
|
||||||
|
|
||||||
ibe := NewIfdByteEncoder()
|
ibe := NewIfdByteEncoder()
|
||||||
|
|
||||||
encodedIfds, err := ibe.EncodeToBytes(ib)
|
encodedIfds, err := ibe.EncodeToExifPayload(ib)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
// Wrap the IFD in a formal EXIF block.
|
// Wrap the IFD in a formal EXIF block.
|
||||||
|
@ -749,70 +728,19 @@ func Test_IfdByteEncoder_EncodeToBytes(t *testing.T) {
|
||||||
// dereference) the values (which will include the allocated ones).
|
// dereference) the values (which will include the allocated ones).
|
||||||
|
|
||||||
exifData := b.Bytes()
|
exifData := b.Bytes()
|
||||||
|
validateExifSimpleTestIb(exifData, t)
|
||||||
e := NewExif()
|
|
||||||
|
|
||||||
eh, index, err := e.Collect(exifData)
|
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
if eh.ByteOrder != TestDefaultByteOrder {
|
|
||||||
t.Fatalf("EXIF byte-order is not correct: %v", eh.ByteOrder)
|
|
||||||
} else if eh.FirstIfdOffset != ExifDefaultFirstIfdOffset {
|
|
||||||
t.Fatalf("EXIF first IFD-offset not correct: (0x%02x)", eh.FirstIfdOffset)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(index.Ifds) != 1 {
|
|
||||||
t.Fatalf("There wasn't exactly one IFD decoded: (%d)", len(index.Ifds))
|
|
||||||
}
|
|
||||||
|
|
||||||
ifd := index.RootIfd
|
|
||||||
|
|
||||||
if ifd.ByteOrder != TestDefaultByteOrder {
|
|
||||||
t.Fatalf("IFD byte-order not correct.")
|
|
||||||
} else if ifd.Name != IfdStandard {
|
|
||||||
t.Fatalf("IFD name not correct.")
|
|
||||||
} else if ifd.Index != 0 {
|
|
||||||
t.Fatalf("IFD index not zero: (%d)", ifd.Index)
|
|
||||||
} else if ifd.Offset != RootIfdExifOffset {
|
|
||||||
t.Fatalf("IFD offset not correct.")
|
|
||||||
} else if len(ifd.Entries) != 4 {
|
|
||||||
t.Fatalf("IFD number of entries not correct: (%d)", len(ifd.Entries))
|
|
||||||
} else if ifd.NextIfdOffset != uint32(0) {
|
|
||||||
t.Fatalf("Next-IFD offset is non-zero.")
|
|
||||||
} else if ifd.NextIfd != nil {
|
|
||||||
t.Fatalf("Next-IFD pointer is non-nil.")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Verify the values by using the actual, orginal types (this is awesome).
|
|
||||||
|
|
||||||
addressableData := exifData[ExifAddressableAreaStart:]
|
|
||||||
|
|
||||||
expected := []struct{
|
|
||||||
tagId uint16
|
|
||||||
value interface{}
|
|
||||||
}{
|
|
||||||
{ tagId: 0x000b, value: "asciivalue" },
|
|
||||||
{ tagId: 0x00ff, value: []uint16 { 0x1122 } },
|
|
||||||
{ tagId: 0x0100, value: []uint32 { 0x33445566 } },
|
|
||||||
{ tagId: 0x013e, value: []Rational {{ Numerator: 0x11112222, Denominator: 0x33334444 }} },
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, e := range ifd.Entries {
|
|
||||||
if e.TagId != expected[i].tagId {
|
|
||||||
t.Fatalf("Tag-ID for entry (%d) not correct: (0x%02x) != (0x%02x)", i, e.TagId, expected[i].tagId)
|
|
||||||
}
|
|
||||||
|
|
||||||
value, err := e.Value(TestDefaultByteOrder, addressableData)
|
|
||||||
log.PanicIf(err)
|
|
||||||
|
|
||||||
if reflect.DeepEqual(value, expected[i].value) != true {
|
|
||||||
t.Fatalf("Value for entry (%d) not correct: [%v] != [%v]", i, value, expected[i].value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_IfdByteEncoder_EncodeToExif(t *testing.T) {
|
||||||
|
ib := getExifSimpleTestIb()
|
||||||
|
|
||||||
|
ibe := NewIfdByteEncoder()
|
||||||
|
|
||||||
|
exifData, err := ibe.EncodeToExif(ib)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
validateExifSimpleTestIb(exifData, t)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(dustin): !! Write test with both chained and child IFDs
|
// TODO(dustin): !! Write test with both chained and child IFDs
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue