diff --git a/exif.go b/exif.go index 87577bd..9f87dab 100644 --- a/exif.go +++ b/exif.go @@ -35,6 +35,9 @@ var ( exifLogger = log.NewLogger("exif.exif") ExifHeaderPrefixBytes = []byte("Exif\000\000") + + // DefaultEncodeByteOrder is the default byte-order for encoding operations. + DefaultEncodeByteOrder = binary.BigEndian ) var ( diff --git a/ifd_builder_encode.go b/ifd_builder_encode.go index d2e8f41..e8dec5d 100644 --- a/ifd_builder_encode.go +++ b/ifd_builder_encode.go @@ -376,8 +376,9 @@ func (ibe *IfdByteEncoder) encodeAndAttachIfd(ib *IfdBuilder, ifdAddressableOffs return b.Bytes(), nil } -// EncodeToBytes is the base encoding step. -func (ibe *IfdByteEncoder) EncodeToBytes(ib *IfdBuilder) (data []byte, err error) { +// EncodeToExifPayload is the base encoding step that transcribes the entire IB +// structure to its on-disk layout. +func (ibe *IfdByteEncoder) EncodeToExifPayload(ib *IfdBuilder) (data []byte, err error) { defer func() { if state := recover(); state != nil { err = log.Wrap(state.(error)) @@ -389,3 +390,31 @@ func (ibe *IfdByteEncoder) EncodeToBytes(ib *IfdBuilder) (data []byte, err error 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 +} diff --git a/ifd_builder_encode_test.go b/ifd_builder_encode_test.go index 475a2ea..4d250ea 100644 --- a/ifd_builder_encode_test.go +++ b/ifd_builder_encode_test.go @@ -498,9 +498,7 @@ func Test_IfdByteEncoder_encodeTagToBytes_simpleTag_allocate(t *testing.T) { } } -func Test_IfdByteEncoder_encodeIfdToBytes_simple(t *testing.T) { - // Build the IB. - +func getExifSimpleTestIb() *IfdBuilder { ib := NewIfdBuilder(RootIi, TestDefaultByteOrder) 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 } }) 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. ibe := NewIfdByteEncoder() @@ -581,21 +648,7 @@ func Test_IfdByteEncoder_encodeIfdToBytes_fullExif(t *testing.T) { } }() - // Build the IB. - - 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) + ib := getExifSimpleTestIb() // 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). exifData := b.Bytes() - - 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) - } - } + validateExifSimpleTestIb(exifData, t) } -func Test_IfdByteEncoder_EncodeToBytes(t *testing.T) { +func Test_IfdByteEncoder_EncodeToExifPayload(t *testing.T) { defer func() { if state := recover(); state != nil { err := log.Wrap(state.(error)) @@ -707,28 +700,14 @@ func Test_IfdByteEncoder_EncodeToBytes(t *testing.T) { } }() - // Build the IB. - - 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) + ib := getExifSimpleTestIb() // Encode the IFD to a byte stream. ibe := NewIfdByteEncoder() - encodedIfds, err := ibe.EncodeToBytes(ib) + encodedIfds, err := ibe.EncodeToExifPayload(ib) log.PanicIf(err) // 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). exifData := b.Bytes() - - 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) - } - } + validateExifSimpleTestIb(exifData, t) } +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