This commit is contained in:
2025-06-09 13:57:01 +08:00
commit 2bc6bd257a
89 changed files with 24207 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
## 0xa40b
The specification is not specific/clear enough to be handled. Without a working example ,we're deferring until some point in the future when either we or someone else has a better understanding.
+62
View File
@@ -0,0 +1,62 @@
package exifundefined
import (
"encoding/binary"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
// Encode encodes the given encodeable undefined value to bytes.
func Encode(value EncodeableValue, byteOrder binary.ByteOrder) (encoded []byte, unitCount uint32, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
encoderName := value.EncoderName()
encoder, found := encoders[encoderName]
if found == false {
log.Panicf("no encoder registered for type [%s]", encoderName)
}
encoded, unitCount, err = encoder.Encode(value, byteOrder)
log.PanicIf(err)
return encoded, unitCount, nil
}
// Decode constructs a value from raw encoded bytes
func Decode(valueContext *exifcommon.ValueContext) (value EncodeableValue, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
uth := UndefinedTagHandle{
IfdPath: valueContext.IfdPath(),
TagId: valueContext.TagId(),
}
decoder, found := decoders[uth]
if found == false {
// We have no choice but to return the error. We have no way of knowing how
// much data there is without already knowing what data-type this tag is.
return nil, exifcommon.ErrUnhandledUndefinedTypedTag
}
value, err = decoder.Decode(valueContext)
if err != nil {
if err == ErrUnparseableValue {
return nil, err
}
log.Panic(err)
}
return value, nil
}
+148
View File
@@ -0,0 +1,148 @@
package exifundefined
import (
"bytes"
"fmt"
"encoding/binary"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
type Tag8828Oecf struct {
Columns uint16
Rows uint16
ColumnNames []string
Values []exifcommon.SignedRational
}
func (oecf Tag8828Oecf) String() string {
return fmt.Sprintf("Tag8828Oecf<COLUMNS=(%d) ROWS=(%d)>", oecf.Columns, oecf.Rows)
}
func (oecf Tag8828Oecf) EncoderName() string {
return "Codec8828Oecf"
}
type Codec8828Oecf struct {
}
func (Codec8828Oecf) Encode(value interface{}, byteOrder binary.ByteOrder) (encoded []byte, unitCount uint32, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
// TODO(dustin): Add test
oecf, ok := value.(Tag8828Oecf)
if ok == false {
log.Panicf("can only encode a Tag8828Oecf")
}
b := new(bytes.Buffer)
err = binary.Write(b, byteOrder, oecf.Columns)
log.PanicIf(err)
err = binary.Write(b, byteOrder, oecf.Rows)
log.PanicIf(err)
for _, name := range oecf.ColumnNames {
_, err := b.Write([]byte(name))
log.PanicIf(err)
_, err = b.Write([]byte{0})
log.PanicIf(err)
}
ve := exifcommon.NewValueEncoder(byteOrder)
ed, err := ve.Encode(oecf.Values)
log.PanicIf(err)
_, err = b.Write(ed.Encoded)
log.PanicIf(err)
return b.Bytes(), uint32(b.Len()), nil
}
func (Codec8828Oecf) Decode(valueContext *exifcommon.ValueContext) (value EncodeableValue, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
// TODO(dustin): Add test using known good data.
valueContext.SetUndefinedValueType(exifcommon.TypeByte)
valueBytes, err := valueContext.ReadBytes()
log.PanicIf(err)
oecf := Tag8828Oecf{}
oecf.Columns = valueContext.ByteOrder().Uint16(valueBytes[0:2])
oecf.Rows = valueContext.ByteOrder().Uint16(valueBytes[2:4])
columnNames := make([]string, oecf.Columns)
// startAt is where the current column name starts.
startAt := 4
// offset is our current position.
offset := startAt
currentColumnNumber := uint16(0)
for currentColumnNumber < oecf.Columns {
if valueBytes[offset] == 0 {
columnName := string(valueBytes[startAt:offset])
if len(columnName) == 0 {
log.Panicf("SFR column (%d) has zero length", currentColumnNumber)
}
columnNames[currentColumnNumber] = columnName
currentColumnNumber++
offset++
startAt = offset
continue
}
offset++
}
oecf.ColumnNames = columnNames
rawRationalBytes := valueBytes[offset:]
rationalSize := exifcommon.TypeSignedRational.Size()
if len(rawRationalBytes)%rationalSize > 0 {
log.Panicf("OECF signed-rationals not aligned: (%d) %% (%d) > 0", len(rawRationalBytes), rationalSize)
}
rationalCount := len(rawRationalBytes) / rationalSize
parser := new(exifcommon.Parser)
byteOrder := valueContext.ByteOrder()
items, err := parser.ParseSignedRationals(rawRationalBytes, uint32(rationalCount), byteOrder)
log.PanicIf(err)
oecf.Values = items
return oecf, nil
}
func init() {
registerDecoder(
exifcommon.IfdExifStandardIfdIdentity.UnindexedString(),
0x8828,
Codec8828Oecf{})
}
+90
View File
@@ -0,0 +1,90 @@
package exifundefined
import (
"bytes"
"reflect"
"testing"
"github.com/dsoprea/go-logging"
"github.com/dsoprea/go-utility/v2/filesystem"
"b612.me/exif/common"
)
func TestTag8828Oecf_String(t *testing.T) {
ut := Tag8828Oecf{
Columns: 11,
Rows: 22,
}
s := ut.String()
if s != "Tag8828Oecf<COLUMNS=(11) ROWS=(22)>" {
t.Fatalf("String not correct: [%s]", s)
}
}
func TestCodec8828Oecf_Encode(t *testing.T) {
ut := Tag8828Oecf{
Columns: 2,
Rows: 22,
ColumnNames: []string{"aa", "bb"},
Values: []exifcommon.SignedRational{{11, 22}},
}
codec := Codec8828Oecf{}
encoded, unitCount, err := codec.Encode(ut, exifcommon.TestDefaultByteOrder)
log.PanicIf(err)
expectedBytes := []byte{
0x00, 0x02,
0x00, 0x16,
0x61, 0x61, 0x00, 0x62, 0x62, 0x00,
0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x16}
if bytes.Equal(encoded, expectedBytes) != true {
exifcommon.DumpBytesClause(encoded)
t.Fatalf("Encoded bytes not correct.")
} else if unitCount != 18 {
t.Fatalf("Unit-count not correct: (%d)", unitCount)
}
}
func TestCodec8828Oecf_Decode(t *testing.T) {
encoded := []byte{
0x00, 0x02,
0x00, 0x16,
0x61, 0x61, 0x00, 0x62, 0x62, 0x00,
0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x16}
addressableData := encoded
sb := rifs.NewSeekableBufferWithBytes(addressableData)
valueContext := exifcommon.NewValueContext(
"",
0,
uint32(len(encoded)),
0,
nil,
sb,
exifcommon.TypeUndefined,
exifcommon.TestDefaultByteOrder)
codec := Codec8828Oecf{}
value, err := codec.Decode(valueContext)
log.PanicIf(err)
expectedValue := Tag8828Oecf{
Columns: 2,
Rows: 22,
ColumnNames: []string{"aa", "bb"},
Values: []exifcommon.SignedRational{{11, 22}},
}
if reflect.DeepEqual(value, expectedValue) != true {
t.Fatalf("Decoded value not correct: %s", value)
}
}
+69
View File
@@ -0,0 +1,69 @@
package exifundefined
import (
"encoding/binary"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
type Tag9000ExifVersion struct {
ExifVersion string
}
func (Tag9000ExifVersion) EncoderName() string {
return "Codec9000ExifVersion"
}
func (ev Tag9000ExifVersion) String() string {
return ev.ExifVersion
}
type Codec9000ExifVersion struct {
}
func (Codec9000ExifVersion) Encode(value interface{}, byteOrder binary.ByteOrder) (encoded []byte, unitCount uint32, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
s, ok := value.(Tag9000ExifVersion)
if ok == false {
log.Panicf("can only encode a Tag9000ExifVersion")
}
return []byte(s.ExifVersion), uint32(len(s.ExifVersion)), nil
}
func (Codec9000ExifVersion) Decode(valueContext *exifcommon.ValueContext) (value EncodeableValue, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
valueContext.SetUndefinedValueType(exifcommon.TypeAsciiNoNul)
valueString, err := valueContext.ReadAsciiNoNul()
log.PanicIf(err)
ev := Tag9000ExifVersion{
ExifVersion: valueString,
}
return ev, nil
}
func init() {
registerEncoder(
Tag9000ExifVersion{},
Codec9000ExifVersion{})
registerDecoder(
exifcommon.IfdExifStandardIfdIdentity.UnindexedString(),
0x9000,
Codec9000ExifVersion{})
}
+64
View File
@@ -0,0 +1,64 @@
package exifundefined
import (
"bytes"
"reflect"
"testing"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
func TestTag9000ExifVersion_String(t *testing.T) {
ut := Tag9000ExifVersion{"abc"}
s := ut.String()
if s != "abc" {
t.Fatalf("String not correct: [%s]", s)
}
}
func TestCodec9000ExifVersion_Encode(t *testing.T) {
s := "abc"
ut := Tag9000ExifVersion{s}
codec := Codec9000ExifVersion{}
encoded, unitCount, err := codec.Encode(ut, exifcommon.TestDefaultByteOrder)
log.PanicIf(err)
if bytes.Equal(encoded, []byte(s)) != true {
t.Fatalf("Encoded bytes not correct: %v", encoded)
} else if unitCount != uint32(len(s)) {
t.Fatalf("Unit-count not correct: (%d)", unitCount)
}
}
func TestCodec9000ExifVersion_Decode(t *testing.T) {
s := "abc"
ut := Tag9000ExifVersion{s}
encoded := []byte(s)
rawValueOffset := encoded
valueContext := exifcommon.NewValueContext(
"",
0,
uint32(len(encoded)),
0,
rawValueOffset,
nil,
exifcommon.TypeUndefined,
exifcommon.TestDefaultByteOrder)
codec := Codec9000ExifVersion{}
value, err := codec.Decode(valueContext)
log.PanicIf(err)
if reflect.DeepEqual(value, ut) != true {
t.Fatalf("Decoded value not correct: %s\n", value)
}
}
@@ -0,0 +1,124 @@
package exifundefined
import (
"bytes"
"fmt"
"encoding/binary"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
const (
TagUndefinedType_9101_ComponentsConfiguration_Channel_Y = 0x1
TagUndefinedType_9101_ComponentsConfiguration_Channel_Cb = 0x2
TagUndefinedType_9101_ComponentsConfiguration_Channel_Cr = 0x3
TagUndefinedType_9101_ComponentsConfiguration_Channel_R = 0x4
TagUndefinedType_9101_ComponentsConfiguration_Channel_G = 0x5
TagUndefinedType_9101_ComponentsConfiguration_Channel_B = 0x6
)
const (
TagUndefinedType_9101_ComponentsConfiguration_OTHER = iota
TagUndefinedType_9101_ComponentsConfiguration_RGB = iota
TagUndefinedType_9101_ComponentsConfiguration_YCBCR = iota
)
var (
TagUndefinedType_9101_ComponentsConfiguration_Names = map[int]string{
TagUndefinedType_9101_ComponentsConfiguration_OTHER: "OTHER",
TagUndefinedType_9101_ComponentsConfiguration_RGB: "RGB",
TagUndefinedType_9101_ComponentsConfiguration_YCBCR: "YCBCR",
}
TagUndefinedType_9101_ComponentsConfiguration_Configurations = map[int][]byte{
TagUndefinedType_9101_ComponentsConfiguration_RGB: {
TagUndefinedType_9101_ComponentsConfiguration_Channel_R,
TagUndefinedType_9101_ComponentsConfiguration_Channel_G,
TagUndefinedType_9101_ComponentsConfiguration_Channel_B,
0,
},
TagUndefinedType_9101_ComponentsConfiguration_YCBCR: {
TagUndefinedType_9101_ComponentsConfiguration_Channel_Y,
TagUndefinedType_9101_ComponentsConfiguration_Channel_Cb,
TagUndefinedType_9101_ComponentsConfiguration_Channel_Cr,
0,
},
}
)
type TagExif9101ComponentsConfiguration struct {
ConfigurationId int
ConfigurationBytes []byte
}
func (TagExif9101ComponentsConfiguration) EncoderName() string {
return "CodecExif9101ComponentsConfiguration"
}
func (cc TagExif9101ComponentsConfiguration) String() string {
return fmt.Sprintf("Exif9101ComponentsConfiguration<ID=[%s] BYTES=%v>", TagUndefinedType_9101_ComponentsConfiguration_Names[cc.ConfigurationId], cc.ConfigurationBytes)
}
type CodecExif9101ComponentsConfiguration struct {
}
func (CodecExif9101ComponentsConfiguration) Encode(value interface{}, byteOrder binary.ByteOrder) (encoded []byte, unitCount uint32, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
cc, ok := value.(TagExif9101ComponentsConfiguration)
if ok == false {
log.Panicf("can only encode a TagExif9101ComponentsConfiguration")
}
return cc.ConfigurationBytes, uint32(len(cc.ConfigurationBytes)), nil
}
func (CodecExif9101ComponentsConfiguration) Decode(valueContext *exifcommon.ValueContext) (value EncodeableValue, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
valueContext.SetUndefinedValueType(exifcommon.TypeByte)
valueBytes, err := valueContext.ReadBytes()
log.PanicIf(err)
for configurationId, configurationBytes := range TagUndefinedType_9101_ComponentsConfiguration_Configurations {
if bytes.Equal(configurationBytes, valueBytes) == true {
cc := TagExif9101ComponentsConfiguration{
ConfigurationId: configurationId,
ConfigurationBytes: valueBytes,
}
return cc, nil
}
}
cc := TagExif9101ComponentsConfiguration{
ConfigurationId: TagUndefinedType_9101_ComponentsConfiguration_OTHER,
ConfigurationBytes: valueBytes,
}
return cc, nil
}
func init() {
registerEncoder(
TagExif9101ComponentsConfiguration{},
CodecExif9101ComponentsConfiguration{})
registerDecoder(
exifcommon.IfdExifStandardIfdIdentity.UnindexedString(),
0x9101,
CodecExif9101ComponentsConfiguration{})
}
@@ -0,0 +1,82 @@
package exifundefined
import (
"bytes"
"reflect"
"testing"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
func TestTagExif9101ComponentsConfiguration_String(t *testing.T) {
ut := TagExif9101ComponentsConfiguration{
ConfigurationId: TagUndefinedType_9101_ComponentsConfiguration_RGB,
ConfigurationBytes: []byte{0x11, 0x22, 0x33, 0x44},
}
s := ut.String()
if s != "Exif9101ComponentsConfiguration<ID=[RGB] BYTES=[17 34 51 68]>" {
t.Fatalf("String not correct: [%s]", s)
}
}
func TestCodecExif9101ComponentsConfiguration_Encode(t *testing.T) {
configurationBytes := []byte(TagUndefinedType_9101_ComponentsConfiguration_Names[TagUndefinedType_9101_ComponentsConfiguration_RGB])
ut := TagExif9101ComponentsConfiguration{
ConfigurationId: TagUndefinedType_9101_ComponentsConfiguration_RGB,
ConfigurationBytes: configurationBytes,
}
codec := CodecExif9101ComponentsConfiguration{}
encoded, unitCount, err := codec.Encode(ut, exifcommon.TestDefaultByteOrder)
log.PanicIf(err)
if bytes.Equal(encoded, configurationBytes) != true {
exifcommon.DumpBytesClause(encoded)
t.Fatalf("Encoded bytes not correct: %v", encoded)
} else if unitCount != uint32(len(configurationBytes)) {
t.Fatalf("Unit-count not correct: (%d)", unitCount)
}
s := string(configurationBytes)
if s != TagUndefinedType_9101_ComponentsConfiguration_Names[TagUndefinedType_9101_ComponentsConfiguration_RGB] {
t.Fatalf("Recovered configuration name not correct: [%s]", s)
}
}
func TestCodecExif9101ComponentsConfiguration_Decode(t *testing.T) {
configurationBytes := TagUndefinedType_9101_ComponentsConfiguration_Configurations[TagUndefinedType_9101_ComponentsConfiguration_RGB]
ut := TagExif9101ComponentsConfiguration{
ConfigurationId: TagUndefinedType_9101_ComponentsConfiguration_RGB,
ConfigurationBytes: configurationBytes,
}
rawValueOffset := configurationBytes
valueContext := exifcommon.NewValueContext(
"",
0,
uint32(len(configurationBytes)),
0,
rawValueOffset,
nil,
exifcommon.TypeUndefined,
exifcommon.TestDefaultByteOrder)
codec := CodecExif9101ComponentsConfiguration{}
value, err := codec.Decode(valueContext)
log.PanicIf(err)
if reflect.DeepEqual(value, ut) != true {
t.Fatalf("Decoded value not correct: %s != %s\n", value, ut)
}
}
+114
View File
@@ -0,0 +1,114 @@
package exifundefined
import (
"fmt"
"strings"
"crypto/sha1"
"encoding/binary"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
type Tag927CMakerNote struct {
MakerNoteType []byte
MakerNoteBytes []byte
}
func (Tag927CMakerNote) EncoderName() string {
return "Codec927CMakerNote"
}
func (mn Tag927CMakerNote) String() string {
parts := make([]string, len(mn.MakerNoteType))
for i, c := range mn.MakerNoteType {
parts[i] = fmt.Sprintf("%02x", c)
}
h := sha1.New()
_, err := h.Write(mn.MakerNoteBytes)
log.PanicIf(err)
digest := h.Sum(nil)
return fmt.Sprintf("MakerNote<TYPE-ID=[%s] LEN=(%d) SHA1=[%020x]>", strings.Join(parts, " "), len(mn.MakerNoteBytes), digest)
}
type Codec927CMakerNote struct {
}
func (Codec927CMakerNote) Encode(value interface{}, byteOrder binary.ByteOrder) (encoded []byte, unitCount uint32, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
mn, ok := value.(Tag927CMakerNote)
if ok == false {
log.Panicf("can only encode a Tag927CMakerNote")
}
// TODO(dustin): Confirm this size against the specification.
return mn.MakerNoteBytes, uint32(len(mn.MakerNoteBytes)), nil
}
func (Codec927CMakerNote) Decode(valueContext *exifcommon.ValueContext) (value EncodeableValue, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
// MakerNote
// TODO(dustin): !! This is the Wild Wild West. This very well might be a child IFD, but any and all OEM's define their own formats. If we're going to be writing changes and this is complete EXIF (which may not have the first eight bytes), it might be fine. However, if these are just IFDs they'll be relative to the main EXIF, this will invalidate the MakerNote data for IFDs and any other implementations that use offsets unless we can interpret them all. It be best to return to this later and just exclude this from being written for now, though means a loss of a wealth of image metadata.
// -> We can also just blindly try to interpret as an IFD and just validate that it's looks good (maybe it will even have a 'next ifd' pointer that we can validate is 0x0).
valueContext.SetUndefinedValueType(exifcommon.TypeByte)
valueBytes, err := valueContext.ReadBytes()
log.PanicIf(err)
// TODO(dustin): Doesn't work, but here as an example.
// ie := NewIfdEnumerate(valueBytes, byteOrder)
// // TODO(dustin): !! Validate types (might have proprietary types, but it might be worth splitting the list between valid and not valid; maybe fail if a certain proportion are invalid, or maybe aren't less then a certain small integer)?
// ii, err := ie.Collect(0x0)
// for _, entry := range ii.RootIfd.Entries {
// fmt.Printf("ENTRY: 0x%02x %d\n", entry.TagId, entry.TagType)
// }
var makerNoteType []byte
if len(valueBytes) >= 20 {
makerNoteType = valueBytes[:20]
} else {
makerNoteType = valueBytes
}
mn := Tag927CMakerNote{
MakerNoteType: makerNoteType,
// MakerNoteBytes has the whole length of bytes. There's always
// the chance that the first 20 bytes includes actual data.
MakerNoteBytes: valueBytes,
}
return mn, nil
}
func init() {
registerEncoder(
Tag927CMakerNote{},
Codec927CMakerNote{})
registerDecoder(
exifcommon.IfdExifStandardIfdIdentity.UnindexedString(),
0x927c,
Codec927CMakerNote{})
}
+75
View File
@@ -0,0 +1,75 @@
package exifundefined
import (
"bytes"
"reflect"
"testing"
"github.com/dsoprea/go-logging"
"github.com/dsoprea/go-utility/v2/filesystem"
"b612.me/exif/common"
)
func TestTag927CMakerNote_String(t *testing.T) {
ut := Tag927CMakerNote{
MakerNoteType: []byte{0, 1, 2, 3, 4},
MakerNoteBytes: []byte{5, 6, 7, 8, 9},
}
s := ut.String()
if s != "MakerNote<TYPE-ID=[00 01 02 03 04] LEN=(5) SHA1=[bdb42cb7eb76e64efe49b22369b404c67b0af55a]>" {
t.Fatalf("String not correct: [%s]", s)
}
}
func TestCodec927CMakerNote_Encode(t *testing.T) {
codec := Codec927CMakerNote{}
prefix := []byte{0, 1, 2, 3, 4}
b := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
ut := Tag927CMakerNote{
MakerNoteType: prefix,
MakerNoteBytes: b,
}
encoded, unitCount, err := codec.Encode(ut, exifcommon.TestDefaultByteOrder)
log.PanicIf(err)
if bytes.Equal(encoded, b) != true {
t.Fatalf("Encoding not correct: %v", encoded)
} else if unitCount != uint32(len(b)) {
t.Fatalf("Unit-count not correct: (%d)", len(b))
}
}
func TestCodec927CMakerNote_Decode(t *testing.T) {
b := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
ut := Tag927CMakerNote{
MakerNoteType: b,
MakerNoteBytes: b,
}
sb := rifs.NewSeekableBufferWithBytes(b)
valueContext := exifcommon.NewValueContext(
"",
0,
uint32(len(b)),
0,
nil,
sb,
exifcommon.TypeUndefined,
exifcommon.TestDefaultByteOrder)
codec := Codec927CMakerNote{}
value, err := codec.Decode(valueContext)
log.PanicIf(err)
if reflect.DeepEqual(value, ut) != true {
t.Fatalf("Decoded value not correct: %s != %s", value, ut)
}
}
+142
View File
@@ -0,0 +1,142 @@
package exifundefined
import (
"bytes"
"fmt"
"encoding/binary"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
var (
exif9286Logger = log.NewLogger("exifundefined.exif_9286_user_comment")
)
const (
TagUndefinedType_9286_UserComment_Encoding_ASCII = iota
TagUndefinedType_9286_UserComment_Encoding_JIS = iota
TagUndefinedType_9286_UserComment_Encoding_UNICODE = iota
TagUndefinedType_9286_UserComment_Encoding_UNDEFINED = iota
)
var (
TagUndefinedType_9286_UserComment_Encoding_Names = map[int]string{
TagUndefinedType_9286_UserComment_Encoding_ASCII: "ASCII",
TagUndefinedType_9286_UserComment_Encoding_JIS: "JIS",
TagUndefinedType_9286_UserComment_Encoding_UNICODE: "UNICODE",
TagUndefinedType_9286_UserComment_Encoding_UNDEFINED: "UNDEFINED",
}
TagUndefinedType_9286_UserComment_Encodings = map[int][]byte{
TagUndefinedType_9286_UserComment_Encoding_ASCII: {'A', 'S', 'C', 'I', 'I', 0, 0, 0},
TagUndefinedType_9286_UserComment_Encoding_JIS: {'J', 'I', 'S', 0, 0, 0, 0, 0},
TagUndefinedType_9286_UserComment_Encoding_UNICODE: {'U', 'n', 'i', 'c', 'o', 'd', 'e', 0},
TagUndefinedType_9286_UserComment_Encoding_UNDEFINED: {0, 0, 0, 0, 0, 0, 0, 0},
}
)
type Tag9286UserComment struct {
EncodingType int
EncodingBytes []byte
}
func (Tag9286UserComment) EncoderName() string {
return "Codec9286UserComment"
}
func (uc Tag9286UserComment) String() string {
var valuePhrase string
if uc.EncodingType == TagUndefinedType_9286_UserComment_Encoding_ASCII {
return fmt.Sprintf("[ASCII] %s", string(uc.EncodingBytes))
} else {
if len(uc.EncodingBytes) <= 8 {
valuePhrase = fmt.Sprintf("%v", uc.EncodingBytes)
} else {
valuePhrase = fmt.Sprintf("%v...", uc.EncodingBytes[:8])
}
}
return fmt.Sprintf("UserComment<SIZE=(%d) ENCODING=[%s] V=%v LEN=(%d)>", len(uc.EncodingBytes), TagUndefinedType_9286_UserComment_Encoding_Names[uc.EncodingType], valuePhrase, len(uc.EncodingBytes))
}
type Codec9286UserComment struct {
}
func (Codec9286UserComment) Encode(value interface{}, byteOrder binary.ByteOrder) (encoded []byte, unitCount uint32, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
uc, ok := value.(Tag9286UserComment)
if ok == false {
log.Panicf("can only encode a Tag9286UserComment")
}
encodingTypeBytes, found := TagUndefinedType_9286_UserComment_Encodings[uc.EncodingType]
if found == false {
log.Panicf("encoding-type not valid for unknown-type tag 9286 (UserComment): (%d)", uc.EncodingType)
}
encoded = make([]byte, len(uc.EncodingBytes)+8)
copy(encoded[:8], encodingTypeBytes)
copy(encoded[8:], uc.EncodingBytes)
// TODO(dustin): Confirm this size against the specification.
return encoded, uint32(len(encoded)), nil
}
func (Codec9286UserComment) Decode(valueContext *exifcommon.ValueContext) (value EncodeableValue, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
valueContext.SetUndefinedValueType(exifcommon.TypeByte)
valueBytes, err := valueContext.ReadBytes()
log.PanicIf(err)
if len(valueBytes) < 8 {
return nil, ErrUnparseableValue
}
unknownUc := Tag9286UserComment{
EncodingType: TagUndefinedType_9286_UserComment_Encoding_UNDEFINED,
EncodingBytes: []byte{},
}
encoding := valueBytes[:8]
for encodingIndex, encodingBytes := range TagUndefinedType_9286_UserComment_Encodings {
if bytes.Compare(encoding, encodingBytes) == 0 {
uc := Tag9286UserComment{
EncodingType: encodingIndex,
EncodingBytes: valueBytes[8:],
}
return uc, nil
}
}
exif9286Logger.Warningf(nil, "User-comment encoding not valid. Returning 'unknown' type (the default).")
return unknownUc, nil
}
func init() {
registerEncoder(
Tag9286UserComment{},
Codec9286UserComment{})
registerDecoder(
exifcommon.IfdExifStandardIfdIdentity.UnindexedString(),
0x9286,
Codec9286UserComment{})
}
+96
View File
@@ -0,0 +1,96 @@
package exifundefined
import (
"bytes"
"reflect"
"testing"
"github.com/dsoprea/go-logging"
"github.com/dsoprea/go-utility/v2/filesystem"
"b612.me/exif/common"
)
func TestTag9286UserComment_String(t *testing.T) {
comment := "some comment"
ut := Tag9286UserComment{
EncodingType: TagUndefinedType_9286_UserComment_Encoding_ASCII,
EncodingBytes: []byte(comment),
}
s := ut.String()
if s != "[ASCII] some comment" {
t.Fatalf("String not correct: [%s]", s)
}
}
func TestCodec9286UserComment_Encode(t *testing.T) {
comment := "some comment"
ut := Tag9286UserComment{
EncodingType: TagUndefinedType_9286_UserComment_Encoding_ASCII,
EncodingBytes: []byte(comment),
}
codec := Codec9286UserComment{}
encoded, unitCount, err := codec.Encode(ut, exifcommon.TestDefaultByteOrder)
log.PanicIf(err)
typeBytes := TagUndefinedType_9286_UserComment_Encodings[TagUndefinedType_9286_UserComment_Encoding_ASCII]
if bytes.Equal(encoded[:8], typeBytes) != true {
exifcommon.DumpBytesClause(encoded[:8])
t.Fatalf("Encoding type not correct.")
}
if bytes.Equal(encoded[8:], []byte(comment)) != true {
exifcommon.DumpBytesClause(encoded[8:])
t.Fatalf("Encoded comment not correct.")
}
if unitCount != uint32(len(encoded)) {
t.Fatalf("Unit-count not correct: (%d)", unitCount)
}
exifcommon.DumpBytesClause(encoded)
}
func TestCodec9286UserComment_Decode(t *testing.T) {
encoded := []byte{
0x41, 0x53, 0x43, 0x49, 0x49, 0x00, 0x00, 0x00,
0x73, 0x6f, 0x6d, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74,
}
addressableBytes := encoded
sb := rifs.NewSeekableBufferWithBytes(addressableBytes)
valueContext := exifcommon.NewValueContext(
"",
0,
uint32(len(encoded)),
0,
nil,
sb,
exifcommon.TypeUndefined,
exifcommon.TestDefaultByteOrder)
codec := Codec9286UserComment{}
decoded, err := codec.Decode(valueContext)
log.PanicIf(err)
comment := "some comment"
expectedUt := Tag9286UserComment{
EncodingType: TagUndefinedType_9286_UserComment_Encoding_ASCII,
EncodingBytes: []byte(comment),
}
if reflect.DeepEqual(decoded, expectedUt) != true {
t.Fatalf("Decoded struct not correct.")
}
}
+69
View File
@@ -0,0 +1,69 @@
package exifundefined
import (
"encoding/binary"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
type TagA000FlashpixVersion struct {
FlashpixVersion string
}
func (TagA000FlashpixVersion) EncoderName() string {
return "CodecA000FlashpixVersion"
}
func (fv TagA000FlashpixVersion) String() string {
return fv.FlashpixVersion
}
type CodecA000FlashpixVersion struct {
}
func (CodecA000FlashpixVersion) Encode(value interface{}, byteOrder binary.ByteOrder) (encoded []byte, unitCount uint32, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
s, ok := value.(TagA000FlashpixVersion)
if ok == false {
log.Panicf("can only encode a TagA000FlashpixVersion")
}
return []byte(s.FlashpixVersion), uint32(len(s.FlashpixVersion)), nil
}
func (CodecA000FlashpixVersion) Decode(valueContext *exifcommon.ValueContext) (value EncodeableValue, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
valueContext.SetUndefinedValueType(exifcommon.TypeAsciiNoNul)
valueString, err := valueContext.ReadAsciiNoNul()
log.PanicIf(err)
fv := TagA000FlashpixVersion{
FlashpixVersion: valueString,
}
return fv, nil
}
func init() {
registerEncoder(
TagA000FlashpixVersion{},
CodecA000FlashpixVersion{})
registerDecoder(
exifcommon.IfdExifStandardIfdIdentity.UnindexedString(),
0xa000,
CodecA000FlashpixVersion{})
}
@@ -0,0 +1,72 @@
package exifundefined
import (
"bytes"
"reflect"
"testing"
"github.com/dsoprea/go-logging"
"github.com/dsoprea/go-utility/v2/filesystem"
"b612.me/exif/common"
)
func TestTagA000FlashpixVersion_String(t *testing.T) {
versionPhrase := "some version"
ut := TagA000FlashpixVersion{versionPhrase}
s := ut.String()
if s != versionPhrase {
t.Fatalf("String not correct: [%s]", s)
}
}
func TestCodecA000FlashpixVersion_Encode(t *testing.T) {
versionPhrase := "some version"
ut := TagA000FlashpixVersion{versionPhrase}
codec := CodecA000FlashpixVersion{}
encoded, unitCount, err := codec.Encode(ut, exifcommon.TestDefaultByteOrder)
log.PanicIf(err)
if bytes.Equal(encoded, []byte(versionPhrase)) != true {
exifcommon.DumpBytesClause(encoded)
t.Fatalf("Encoding not correct.")
} else if unitCount != uint32(len(encoded)) {
t.Fatalf("Unit-count not correct: (%d)", unitCount)
}
}
func TestCodecA000FlashpixVersion_Decode(t *testing.T) {
versionPhrase := "some version"
expectedUt := TagA000FlashpixVersion{versionPhrase}
encoded := []byte(versionPhrase)
addressableBytes := encoded
sb := rifs.NewSeekableBufferWithBytes(addressableBytes)
valueContext := exifcommon.NewValueContext(
"",
0,
uint32(len(encoded)),
0,
nil,
sb,
exifcommon.TypeUndefined,
exifcommon.TestDefaultByteOrder)
codec := CodecA000FlashpixVersion{}
decoded, err := codec.Decode(valueContext)
log.PanicIf(err)
if reflect.DeepEqual(decoded, expectedUt) != true {
t.Fatalf("Decoded struct not correct.")
}
}
@@ -0,0 +1,160 @@
package exifundefined
import (
"bytes"
"fmt"
"encoding/binary"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
type TagA20CSpatialFrequencyResponse struct {
Columns uint16
Rows uint16
ColumnNames []string
Values []exifcommon.Rational
}
func (TagA20CSpatialFrequencyResponse) EncoderName() string {
return "CodecA20CSpatialFrequencyResponse"
}
func (sfr TagA20CSpatialFrequencyResponse) String() string {
return fmt.Sprintf("CodecA20CSpatialFrequencyResponse<COLUMNS=(%d) ROWS=(%d)>", sfr.Columns, sfr.Rows)
}
type CodecA20CSpatialFrequencyResponse struct {
}
func (CodecA20CSpatialFrequencyResponse) Encode(value interface{}, byteOrder binary.ByteOrder) (encoded []byte, unitCount uint32, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
// TODO(dustin): Add test.
sfr, ok := value.(TagA20CSpatialFrequencyResponse)
if ok == false {
log.Panicf("can only encode a TagA20CSpatialFrequencyResponse")
}
b := new(bytes.Buffer)
err = binary.Write(b, byteOrder, sfr.Columns)
log.PanicIf(err)
err = binary.Write(b, byteOrder, sfr.Rows)
log.PanicIf(err)
// Write columns.
for _, name := range sfr.ColumnNames {
_, err := b.WriteString(name)
log.PanicIf(err)
err = b.WriteByte(0)
log.PanicIf(err)
}
// Write values.
ve := exifcommon.NewValueEncoder(byteOrder)
ed, err := ve.Encode(sfr.Values)
log.PanicIf(err)
_, err = b.Write(ed.Encoded)
log.PanicIf(err)
encoded = b.Bytes()
// TODO(dustin): Confirm this size against the specification.
return encoded, uint32(len(encoded)), nil
}
func (CodecA20CSpatialFrequencyResponse) Decode(valueContext *exifcommon.ValueContext) (value EncodeableValue, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
// TODO(dustin): Add test using known good data.
byteOrder := valueContext.ByteOrder()
valueContext.SetUndefinedValueType(exifcommon.TypeByte)
valueBytes, err := valueContext.ReadBytes()
log.PanicIf(err)
sfr := TagA20CSpatialFrequencyResponse{}
sfr.Columns = byteOrder.Uint16(valueBytes[0:2])
sfr.Rows = byteOrder.Uint16(valueBytes[2:4])
columnNames := make([]string, sfr.Columns)
// startAt is where the current column name starts.
startAt := 4
// offset is our current position.
offset := 4
currentColumnNumber := uint16(0)
for currentColumnNumber < sfr.Columns {
if valueBytes[offset] == 0 {
columnName := string(valueBytes[startAt:offset])
if len(columnName) == 0 {
log.Panicf("SFR column (%d) has zero length", currentColumnNumber)
}
columnNames[currentColumnNumber] = columnName
currentColumnNumber++
offset++
startAt = offset
continue
}
offset++
}
sfr.ColumnNames = columnNames
rawRationalBytes := valueBytes[offset:]
rationalSize := exifcommon.TypeRational.Size()
if len(rawRationalBytes)%rationalSize > 0 {
log.Panicf("SFR rationals not aligned: (%d) %% (%d) > 0", len(rawRationalBytes), rationalSize)
}
rationalCount := len(rawRationalBytes) / rationalSize
parser := new(exifcommon.Parser)
items, err := parser.ParseRationals(rawRationalBytes, uint32(rationalCount), byteOrder)
log.PanicIf(err)
sfr.Values = items
return sfr, nil
}
func init() {
registerEncoder(
TagA20CSpatialFrequencyResponse{},
CodecA20CSpatialFrequencyResponse{})
registerDecoder(
exifcommon.IfdExifStandardIfdIdentity.UnindexedString(),
0xa20c,
CodecA20CSpatialFrequencyResponse{})
}
@@ -0,0 +1,106 @@
package exifundefined
import (
"bytes"
"reflect"
"testing"
"github.com/dsoprea/go-logging"
"github.com/dsoprea/go-utility/v2/filesystem"
"b612.me/exif/common"
)
func TestTagA20CSpatialFrequencyResponse_String(t *testing.T) {
ut := TagA20CSpatialFrequencyResponse{
Columns: 2,
Rows: 9,
ColumnNames: []string{"column1", "column2"},
Values: []exifcommon.Rational{
{1, 2},
{3, 4},
},
}
s := ut.String()
if s != "CodecA20CSpatialFrequencyResponse<COLUMNS=(2) ROWS=(9)>" {
t.Fatalf("String not correct: [%s]", s)
}
}
func TestCodecA20CSpatialFrequencyResponse_Encode(t *testing.T) {
ut := TagA20CSpatialFrequencyResponse{
Columns: 2,
Rows: 9,
ColumnNames: []string{"column1", "column2"},
Values: []exifcommon.Rational{
{1, 2},
{3, 4},
},
}
codec := CodecA20CSpatialFrequencyResponse{}
encoded, unitCount, err := codec.Encode(ut, exifcommon.TestDefaultByteOrder)
log.PanicIf(err)
expectedEncoded := []byte{
0x00, 0x02,
0x00, 0x09,
0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x31, 0x00,
0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x32, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
}
if bytes.Equal(encoded, expectedEncoded) != true {
exifcommon.DumpBytesClause(encoded)
t.Fatalf("Encoding not correct.")
} else if unitCount != uint32(len(encoded)) {
t.Fatalf("Unit-count not correct: (%d)", unitCount)
}
}
func TestCodecA20CSpatialFrequencyResponse_Decode(t *testing.T) {
expectedUt := TagA20CSpatialFrequencyResponse{
Columns: 2,
Rows: 9,
ColumnNames: []string{"column1", "column2"},
Values: []exifcommon.Rational{
{1, 2},
{3, 4},
},
}
encoded := []byte{
0x00, 0x02,
0x00, 0x09,
0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x31, 0x00,
0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x32, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
}
addressableBytes := encoded
sb := rifs.NewSeekableBufferWithBytes(addressableBytes)
valueContext := exifcommon.NewValueContext(
"",
0,
uint32(len(encoded)),
0,
nil,
sb,
exifcommon.TypeUndefined,
exifcommon.TestDefaultByteOrder)
codec := CodecA20CSpatialFrequencyResponse{}
decoded, err := codec.Decode(valueContext)
log.PanicIf(err)
if reflect.DeepEqual(decoded, expectedUt) != true {
t.Fatalf("Decoded struct not correct.")
}
}
+79
View File
@@ -0,0 +1,79 @@
package exifundefined
import (
"fmt"
"encoding/binary"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
type TagExifA300FileSource uint32
func (TagExifA300FileSource) EncoderName() string {
return "CodecExifA300FileSource"
}
func (af TagExifA300FileSource) String() string {
return fmt.Sprintf("0x%08x", uint32(af))
}
const (
TagUndefinedType_A300_SceneType_Others TagExifA300FileSource = 0
TagUndefinedType_A300_SceneType_ScannerOfTransparentType TagExifA300FileSource = 1
TagUndefinedType_A300_SceneType_ScannerOfReflexType TagExifA300FileSource = 2
TagUndefinedType_A300_SceneType_Dsc TagExifA300FileSource = 3
)
type CodecExifA300FileSource struct {
}
func (CodecExifA300FileSource) Encode(value interface{}, byteOrder binary.ByteOrder) (encoded []byte, unitCount uint32, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
st, ok := value.(TagExifA300FileSource)
if ok == false {
log.Panicf("can only encode a TagExifA300FileSource")
}
ve := exifcommon.NewValueEncoder(byteOrder)
ed, err := ve.Encode([]uint32{uint32(st)})
log.PanicIf(err)
// TODO(dustin): Confirm this size against the specification. It's non-specific about what type it is, but it looks to be no more than a single integer scalar. So, we're assuming it's a LONG.
return ed.Encoded, 1, nil
}
func (CodecExifA300FileSource) Decode(valueContext *exifcommon.ValueContext) (value EncodeableValue, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
valueContext.SetUndefinedValueType(exifcommon.TypeLong)
valueLongs, err := valueContext.ReadLongs()
log.PanicIf(err)
return TagExifA300FileSource(valueLongs[0]), nil
}
func init() {
registerEncoder(
TagExifA300FileSource(0),
CodecExifA300FileSource{})
registerDecoder(
exifcommon.IfdExifStandardIfdIdentity.UnindexedString(),
0xa300,
CodecExifA300FileSource{})
}
+66
View File
@@ -0,0 +1,66 @@
package exifundefined
import (
"bytes"
"reflect"
"testing"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
func TestTagExifA300FileSource_String(t *testing.T) {
ut := TagExifA300FileSource(0x1234)
s := ut.String()
if s != "0x00001234" {
t.Fatalf("String not correct: [%s]", s)
}
}
func TestCodecExifA300FileSource_Encode(t *testing.T) {
ut := TagExifA300FileSource(0x1234)
codec := CodecExifA300FileSource{}
encoded, unitCount, err := codec.Encode(ut, exifcommon.TestDefaultByteOrder)
log.PanicIf(err)
expectedEncoded := []byte{0, 0, 0x12, 0x34}
if bytes.Equal(encoded, expectedEncoded) != true {
exifcommon.DumpBytesClause(encoded)
t.Fatalf("Encoding not correct.")
} else if unitCount != 1 {
t.Fatalf("Unit-count not correct: (%d)", unitCount)
}
}
func TestCodecExifA300FileSource_Decode(t *testing.T) {
expectedUt := TagExifA300FileSource(0x1234)
encoded := []byte{0, 0, 0x12, 0x34}
rawValueOffset := encoded
valueContext := exifcommon.NewValueContext(
"",
0,
1,
0,
rawValueOffset,
nil,
exifcommon.TypeUndefined,
exifcommon.TestDefaultByteOrder)
codec := CodecExifA300FileSource{}
decoded, err := codec.Decode(valueContext)
log.PanicIf(err)
if reflect.DeepEqual(decoded, expectedUt) != true {
t.Fatalf("Decoded struct not correct.")
}
}
+76
View File
@@ -0,0 +1,76 @@
package exifundefined
import (
"fmt"
"encoding/binary"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
type TagExifA301SceneType uint32
func (TagExifA301SceneType) EncoderName() string {
return "CodecExifA301SceneType"
}
func (st TagExifA301SceneType) String() string {
return fmt.Sprintf("0x%08x", uint32(st))
}
const (
TagUndefinedType_A301_SceneType_DirectlyPhotographedImage TagExifA301SceneType = 1
)
type CodecExifA301SceneType struct {
}
func (CodecExifA301SceneType) Encode(value interface{}, byteOrder binary.ByteOrder) (encoded []byte, unitCount uint32, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
st, ok := value.(TagExifA301SceneType)
if ok == false {
log.Panicf("can only encode a TagExif9101ComponentsConfiguration")
}
ve := exifcommon.NewValueEncoder(byteOrder)
ed, err := ve.Encode([]uint32{uint32(st)})
log.PanicIf(err)
// TODO(dustin): Confirm this size against the specification. It's non-specific about what type it is, but it looks to be no more than a single integer scalar. So, we're assuming it's a LONG.
return ed.Encoded, uint32(int(ed.UnitCount)), nil
}
func (CodecExifA301SceneType) Decode(valueContext *exifcommon.ValueContext) (value EncodeableValue, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
valueContext.SetUndefinedValueType(exifcommon.TypeLong)
valueLongs, err := valueContext.ReadLongs()
log.PanicIf(err)
return TagExifA301SceneType(valueLongs[0]), nil
}
func init() {
registerEncoder(
TagExifA301SceneType(0),
CodecExifA301SceneType{})
registerDecoder(
exifcommon.IfdExifStandardIfdIdentity.UnindexedString(),
0xa301,
CodecExifA301SceneType{})
}
+66
View File
@@ -0,0 +1,66 @@
package exifundefined
import (
"bytes"
"reflect"
"testing"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
func TestTagExifA301SceneType_String(t *testing.T) {
ut := TagExifA301SceneType(0x1234)
s := ut.String()
if s != "0x00001234" {
t.Fatalf("String not correct: [%s]", s)
}
}
func TestCodecExifA301SceneType_Encode(t *testing.T) {
ut := TagExifA301SceneType(0x1234)
codec := CodecExifA301SceneType{}
encoded, unitCount, err := codec.Encode(ut, exifcommon.TestDefaultByteOrder)
log.PanicIf(err)
expectedEncoded := []byte{0, 0, 0x12, 0x34}
if bytes.Equal(encoded, expectedEncoded) != true {
exifcommon.DumpBytesClause(encoded)
t.Fatalf("Encoding not correct.")
} else if unitCount != 1 {
t.Fatalf("Unit-count not correct: (%d)", unitCount)
}
}
func TestCodecExifA301SceneType_Decode(t *testing.T) {
expectedUt := TagExifA301SceneType(0x1234)
encoded := []byte{0, 0, 0x12, 0x34}
rawValueOffset := encoded
valueContext := exifcommon.NewValueContext(
"",
0,
1,
0,
rawValueOffset,
nil,
exifcommon.TypeUndefined,
exifcommon.TestDefaultByteOrder)
codec := CodecExifA301SceneType{}
decoded, err := codec.Decode(valueContext)
log.PanicIf(err)
if reflect.DeepEqual(decoded, expectedUt) != true {
t.Fatalf("Decoded struct not correct.")
}
}
+97
View File
@@ -0,0 +1,97 @@
package exifundefined
import (
"bytes"
"fmt"
"encoding/binary"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
type TagA302CfaPattern struct {
HorizontalRepeat uint16
VerticalRepeat uint16
CfaValue []byte
}
func (TagA302CfaPattern) EncoderName() string {
return "CodecA302CfaPattern"
}
func (cp TagA302CfaPattern) String() string {
return fmt.Sprintf("TagA302CfaPattern<HORZ-REPEAT=(%d) VERT-REPEAT=(%d) CFA-VALUE=(%d)>", cp.HorizontalRepeat, cp.VerticalRepeat, len(cp.CfaValue))
}
type CodecA302CfaPattern struct {
}
func (CodecA302CfaPattern) Encode(value interface{}, byteOrder binary.ByteOrder) (encoded []byte, unitCount uint32, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
// TODO(dustin): Add test.
cp, ok := value.(TagA302CfaPattern)
if ok == false {
log.Panicf("can only encode a TagA302CfaPattern")
}
b := new(bytes.Buffer)
err = binary.Write(b, byteOrder, cp.HorizontalRepeat)
log.PanicIf(err)
err = binary.Write(b, byteOrder, cp.VerticalRepeat)
log.PanicIf(err)
_, err = b.Write(cp.CfaValue)
log.PanicIf(err)
encoded = b.Bytes()
// TODO(dustin): Confirm this size against the specification.
return encoded, uint32(len(encoded)), nil
}
func (CodecA302CfaPattern) Decode(valueContext *exifcommon.ValueContext) (value EncodeableValue, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
// TODO(dustin): Add test using known good data.
valueContext.SetUndefinedValueType(exifcommon.TypeByte)
valueBytes, err := valueContext.ReadBytes()
log.PanicIf(err)
cp := TagA302CfaPattern{}
cp.HorizontalRepeat = valueContext.ByteOrder().Uint16(valueBytes[0:2])
cp.VerticalRepeat = valueContext.ByteOrder().Uint16(valueBytes[2:4])
expectedLength := int(cp.HorizontalRepeat * cp.VerticalRepeat)
cp.CfaValue = valueBytes[4 : 4+expectedLength]
return cp, nil
}
func init() {
registerEncoder(
TagA302CfaPattern{},
CodecA302CfaPattern{})
registerDecoder(
exifcommon.IfdExifStandardIfdIdentity.UnindexedString(),
0xa302,
CodecA302CfaPattern{})
}
+97
View File
@@ -0,0 +1,97 @@
package exifundefined
import (
"bytes"
"reflect"
"testing"
"github.com/dsoprea/go-logging"
"github.com/dsoprea/go-utility/v2/filesystem"
"b612.me/exif/common"
)
func TestTagA302CfaPattern_String(t *testing.T) {
ut := TagA302CfaPattern{
HorizontalRepeat: 2,
VerticalRepeat: 3,
CfaValue: []byte{
0, 1, 2, 3, 4, 5,
},
}
s := ut.String()
if s != "TagA302CfaPattern<HORZ-REPEAT=(2) VERT-REPEAT=(3) CFA-VALUE=(6)>" {
t.Fatalf("String not correct: [%s]", s)
}
}
func TestCodecA302CfaPattern_Encode(t *testing.T) {
ut := TagA302CfaPattern{
HorizontalRepeat: 2,
VerticalRepeat: 3,
CfaValue: []byte{
0, 1, 2, 3, 4,
5, 6, 7, 8, 9,
10, 11, 12, 13, 14,
},
}
codec := CodecA302CfaPattern{}
encoded, unitCount, err := codec.Encode(ut, exifcommon.TestDefaultByteOrder)
log.PanicIf(err)
expectedBytes := []byte{
0x00, 0x02,
0x00, 0x03,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
}
if bytes.Equal(encoded, expectedBytes) != true {
exifcommon.DumpBytesClause(encoded)
t.Fatalf("Encoded bytes not correct.")
} else if unitCount != 19 {
t.Fatalf("Unit-count not correct: (%d)", unitCount)
}
}
func TestCodecA302CfaPattern_Decode(t *testing.T) {
encoded := []byte{
0x00, 0x02,
0x00, 0x03,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
}
addressableBytes := encoded
sb := rifs.NewSeekableBufferWithBytes(addressableBytes)
valueContext := exifcommon.NewValueContext(
"",
0,
uint32(len(encoded)),
0,
nil,
sb,
exifcommon.TypeUndefined,
exifcommon.TestDefaultByteOrder)
codec := CodecA302CfaPattern{}
value, err := codec.Decode(valueContext)
log.PanicIf(err)
expectedValue := TagA302CfaPattern{
HorizontalRepeat: 2,
VerticalRepeat: 3,
CfaValue: []byte{
0, 1, 2, 3, 4, 5,
},
}
if reflect.DeepEqual(value, expectedValue) != true {
t.Fatalf("Decoded value not correct: %s", value)
}
}
@@ -0,0 +1,69 @@
package exifundefined
import (
"encoding/binary"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
type Tag0002InteropVersion struct {
InteropVersion string
}
func (Tag0002InteropVersion) EncoderName() string {
return "Codec0002InteropVersion"
}
func (iv Tag0002InteropVersion) String() string {
return iv.InteropVersion
}
type Codec0002InteropVersion struct {
}
func (Codec0002InteropVersion) Encode(value interface{}, byteOrder binary.ByteOrder) (encoded []byte, unitCount uint32, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
s, ok := value.(Tag0002InteropVersion)
if ok == false {
log.Panicf("can only encode a Tag0002InteropVersion")
}
return []byte(s.InteropVersion), uint32(len(s.InteropVersion)), nil
}
func (Codec0002InteropVersion) Decode(valueContext *exifcommon.ValueContext) (value EncodeableValue, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
valueContext.SetUndefinedValueType(exifcommon.TypeAsciiNoNul)
valueString, err := valueContext.ReadAsciiNoNul()
log.PanicIf(err)
iv := Tag0002InteropVersion{
InteropVersion: valueString,
}
return iv, nil
}
func init() {
registerEncoder(
Tag0002InteropVersion{},
Codec0002InteropVersion{})
registerDecoder(
exifcommon.IfdExifIopStandardIfdIdentity.UnindexedString(),
0x0002,
Codec0002InteropVersion{})
}
@@ -0,0 +1,64 @@
package exifundefined
import (
"bytes"
"reflect"
"testing"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
func TestTag0002InteropVersion_String(t *testing.T) {
ut := Tag0002InteropVersion{"abc"}
s := ut.String()
if s != "abc" {
t.Fatalf("String not correct: [%s]", s)
}
}
func TestCodec0002InteropVersion_Encode(t *testing.T) {
s := "abc"
ut := Tag0002InteropVersion{s}
codec := Codec0002InteropVersion{}
encoded, unitCount, err := codec.Encode(ut, exifcommon.TestDefaultByteOrder)
log.PanicIf(err)
if bytes.Equal(encoded, []byte(s)) != true {
t.Fatalf("Encoded bytes not correct: %v", encoded)
} else if unitCount != uint32(len(s)) {
t.Fatalf("Unit-count not correct: (%d)", unitCount)
}
}
func TestCodec0002InteropVersion_Decode(t *testing.T) {
s := "abc"
ut := Tag0002InteropVersion{s}
encoded := []byte(s)
rawValueOffset := encoded
valueContext := exifcommon.NewValueContext(
"",
0,
uint32(len(encoded)),
0,
rawValueOffset,
nil,
exifcommon.TypeUndefined,
exifcommon.TestDefaultByteOrder)
codec := Codec0002InteropVersion{}
value, err := codec.Decode(valueContext)
log.PanicIf(err)
if reflect.DeepEqual(value, ut) != true {
t.Fatalf("Decoded value not correct: %s\n", value)
}
}
@@ -0,0 +1,65 @@
package exifundefined
import (
"encoding/binary"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
type Tag001BGPSProcessingMethod struct {
string
}
func (Tag001BGPSProcessingMethod) EncoderName() string {
return "Codec001BGPSProcessingMethod"
}
func (gpm Tag001BGPSProcessingMethod) String() string {
return gpm.string
}
type Codec001BGPSProcessingMethod struct {
}
func (Codec001BGPSProcessingMethod) Encode(value interface{}, byteOrder binary.ByteOrder) (encoded []byte, unitCount uint32, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
s, ok := value.(Tag001BGPSProcessingMethod)
if ok == false {
log.Panicf("can only encode a Tag001BGPSProcessingMethod")
}
return []byte(s.string), uint32(len(s.string)), nil
}
func (Codec001BGPSProcessingMethod) Decode(valueContext *exifcommon.ValueContext) (value EncodeableValue, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
valueContext.SetUndefinedValueType(exifcommon.TypeAsciiNoNul)
valueString, err := valueContext.ReadAsciiNoNul()
log.PanicIf(err)
return Tag001BGPSProcessingMethod{valueString}, nil
}
func init() {
registerEncoder(
Tag001BGPSProcessingMethod{},
Codec001BGPSProcessingMethod{})
registerDecoder(
exifcommon.IfdGpsInfoStandardIfdIdentity.UnindexedString(),
0x001b,
Codec001BGPSProcessingMethod{})
}
@@ -0,0 +1,64 @@
package exifundefined
import (
"bytes"
"reflect"
"testing"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
func TestTag001BGPSProcessingMethod_String(t *testing.T) {
ut := Tag001BGPSProcessingMethod{"abc"}
s := ut.String()
if s != "abc" {
t.Fatalf("String not correct: [%s]", s)
}
}
func TestCodec001BGPSProcessingMethod_Encode(t *testing.T) {
s := "abc"
ut := Tag001BGPSProcessingMethod{s}
codec := Codec001BGPSProcessingMethod{}
encoded, unitCount, err := codec.Encode(ut, exifcommon.TestDefaultByteOrder)
log.PanicIf(err)
if bytes.Equal(encoded, []byte(s)) != true {
t.Fatalf("Encoded bytes not correct: %v", encoded)
} else if unitCount != uint32(len(s)) {
t.Fatalf("Unit-count not correct: (%d)", unitCount)
}
}
func TestCodec001BGPSProcessingMethod_Decode(t *testing.T) {
s := "abc"
ut := Tag001BGPSProcessingMethod{s}
encoded := []byte(s)
rawValueOffset := encoded
valueContext := exifcommon.NewValueContext(
"",
0,
uint32(len(encoded)),
0,
rawValueOffset,
nil,
exifcommon.TypeUndefined,
exifcommon.TestDefaultByteOrder)
codec := Codec001BGPSProcessingMethod{}
value, err := codec.Decode(valueContext)
log.PanicIf(err)
if reflect.DeepEqual(value, ut) != true {
t.Fatalf("Decoded value not correct: %s\n", value)
}
}
@@ -0,0 +1,65 @@
package exifundefined
import (
"encoding/binary"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
type Tag001CGPSAreaInformation struct {
string
}
func (Tag001CGPSAreaInformation) EncoderName() string {
return "Codec001CGPSAreaInformation"
}
func (gai Tag001CGPSAreaInformation) String() string {
return gai.string
}
type Codec001CGPSAreaInformation struct {
}
func (Codec001CGPSAreaInformation) Encode(value interface{}, byteOrder binary.ByteOrder) (encoded []byte, unitCount uint32, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
s, ok := value.(Tag001CGPSAreaInformation)
if ok == false {
log.Panicf("can only encode a Tag001CGPSAreaInformation")
}
return []byte(s.string), uint32(len(s.string)), nil
}
func (Codec001CGPSAreaInformation) Decode(valueContext *exifcommon.ValueContext) (value EncodeableValue, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
valueContext.SetUndefinedValueType(exifcommon.TypeAsciiNoNul)
valueString, err := valueContext.ReadAsciiNoNul()
log.PanicIf(err)
return Tag001CGPSAreaInformation{valueString}, nil
}
func init() {
registerEncoder(
Tag001CGPSAreaInformation{},
Codec001CGPSAreaInformation{})
registerDecoder(
exifcommon.IfdGpsInfoStandardIfdIdentity.UnindexedString(),
0x001c,
Codec001CGPSAreaInformation{})
}
@@ -0,0 +1,64 @@
package exifundefined
import (
"bytes"
"reflect"
"testing"
"github.com/dsoprea/go-logging"
"b612.me/exif/common"
)
func TestTag001CGPSAreaInformation_String(t *testing.T) {
ut := Tag001CGPSAreaInformation{"abc"}
s := ut.String()
if s != "abc" {
t.Fatalf("String not correct: [%s]", s)
}
}
func TestCodec001CGPSAreaInformation_Encode(t *testing.T) {
s := "abc"
ut := Tag001CGPSAreaInformation{s}
codec := Codec001CGPSAreaInformation{}
encoded, unitCount, err := codec.Encode(ut, exifcommon.TestDefaultByteOrder)
log.PanicIf(err)
if bytes.Equal(encoded, []byte(s)) != true {
t.Fatalf("Encoded bytes not correct: %v", encoded)
} else if unitCount != uint32(len(s)) {
t.Fatalf("Unit-count not correct: (%d)", unitCount)
}
}
func TestCodec001CGPSAreaInformation_Decode(t *testing.T) {
s := "abc"
ut := Tag001CGPSAreaInformation{s}
encoded := []byte(s)
rawValueOffset := encoded
valueContext := exifcommon.NewValueContext(
"",
0,
uint32(len(encoded)),
0,
rawValueOffset,
nil,
exifcommon.TypeUndefined,
exifcommon.TestDefaultByteOrder)
codec := Codec001CGPSAreaInformation{}
value, err := codec.Decode(valueContext)
log.PanicIf(err)
if reflect.DeepEqual(value, ut) != true {
t.Fatalf("Decoded value not correct: %s\n", value)
}
}
+42
View File
@@ -0,0 +1,42 @@
package exifundefined
import (
"github.com/dsoprea/go-logging"
)
// UndefinedTagHandle defines one undefined-type tag with a corresponding
// decoder.
type UndefinedTagHandle struct {
IfdPath string
TagId uint16
}
func registerEncoder(entity EncodeableValue, encoder UndefinedValueEncoder) {
typeName := entity.EncoderName()
_, found := encoders[typeName]
if found == true {
log.Panicf("encoder already registered: %v", typeName)
}
encoders[typeName] = encoder
}
func registerDecoder(ifdPath string, tagId uint16, decoder UndefinedValueDecoder) {
uth := UndefinedTagHandle{
IfdPath: ifdPath,
TagId: tagId,
}
_, found := decoders[uth]
if found == true {
log.Panicf("decoder already registered: %v", uth)
}
decoders[uth] = decoder
}
var (
encoders = make(map[string]UndefinedValueEncoder)
decoders = make(map[UndefinedTagHandle]UndefinedValueDecoder)
)
+44
View File
@@ -0,0 +1,44 @@
package exifundefined
import (
"errors"
"encoding/binary"
"b612.me/exif/common"
)
const (
// UnparseableUnknownTagValuePlaceholder is the string to use for an unknown
// undefined tag.
UnparseableUnknownTagValuePlaceholder = "!UNKNOWN"
// UnparseableHandledTagValuePlaceholder is the string to use for a known
// value that is not parseable.
UnparseableHandledTagValuePlaceholder = "!MALFORMED"
)
var (
// ErrUnparseableValue is the error for a value that we should have been
// able to parse but were not able to.
ErrUnparseableValue = errors.New("unparseable undefined tag")
)
// UndefinedValueEncoder knows how to encode an undefined-type tag's value to
// bytes.
type UndefinedValueEncoder interface {
Encode(value interface{}, byteOrder binary.ByteOrder) (encoded []byte, unitCount uint32, err error)
}
// EncodeableValue wraps a value with the information that will be needed to re-
// encode it later.
type EncodeableValue interface {
EncoderName() string
String() string
}
// UndefinedValueDecoder knows how to decode an undefined-type tag's value from
// bytes.
type UndefinedValueDecoder interface {
Decode(valueContext *exifcommon.ValueContext) (value EncodeableValue, err error)
}