mirror of https://github.com/dsoprea/go-exif.git
Refactor undefined-type tag processing
parent
903910b6a7
commit
bf3bc4e542
|
@ -17,7 +17,9 @@ func TestVisit(t *testing.T) {
|
|||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err := log.Wrap(state.(error))
|
||||
log.PrintErrorf(err, "Exif failure.")
|
||||
log.PrintError(err)
|
||||
|
||||
t.Fatalf("Test failure.")
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -213,7 +215,9 @@ func TestCollect(t *testing.T) {
|
|||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err := log.Wrap(state.(error))
|
||||
log.PrintErrorf(err, "Exif failure.")
|
||||
log.PrintError(err)
|
||||
|
||||
t.Fatalf("Test failure.")
|
||||
}
|
||||
}()
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package exifcommon
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
var (
|
||||
// EncodeDefaultByteOrder is the default byte-order for encoding operations.
|
||||
EncodeDefaultByteOrder = binary.BigEndian
|
||||
|
||||
// Default byte order for tests.
|
||||
TestDefaultByteOrder = binary.BigEndian
|
||||
)
|
|
@ -0,0 +1,29 @@
|
|||
package exifcommon
|
||||
|
||||
const (
|
||||
// IFD names. The paths that we referred to the IFDs with are comprised of
|
||||
// these.
|
||||
|
||||
IfdStandard = "IFD"
|
||||
IfdExif = "Exif"
|
||||
IfdGps = "GPSInfo"
|
||||
IfdIop = "Iop"
|
||||
|
||||
// Tag IDs for child IFDs.
|
||||
|
||||
IfdExifId = 0x8769
|
||||
IfdGpsId = 0x8825
|
||||
IfdIopId = 0xA005
|
||||
|
||||
// Just a placeholder.
|
||||
|
||||
IfdRootId = 0x0000
|
||||
|
||||
// The paths of the standard IFDs expressed in the standard IFD-mappings
|
||||
// and as the group-names in the tag data.
|
||||
|
||||
IfdPathStandard = "IFD"
|
||||
IfdPathStandardExif = "IFD/Exif"
|
||||
IfdPathStandardExifIop = "IFD/Exif/Iop"
|
||||
IfdPathStandardGps = "IFD/GPSInfo"
|
||||
)
|
|
@ -1,4 +1,4 @@
|
|||
package exif
|
||||
package exifcommon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -18,6 +18,8 @@ func (p *Parser) ParseBytes(data []byte, unitCount uint32) (value []uint8, err e
|
|||
}
|
||||
}()
|
||||
|
||||
// TODO(dustin): Add test
|
||||
|
||||
count := int(unitCount)
|
||||
|
||||
if len(data) < (TypeByte.Size() * count) {
|
||||
|
@ -37,6 +39,8 @@ func (p *Parser) ParseAscii(data []byte, unitCount uint32) (value string, err er
|
|||
}
|
||||
}()
|
||||
|
||||
// TODO(dustin): Add test
|
||||
|
||||
count := int(unitCount)
|
||||
|
||||
if len(data) < (TypeAscii.Size() * count) {
|
||||
|
@ -65,6 +69,8 @@ func (p *Parser) ParseAsciiNoNul(data []byte, unitCount uint32) (value string, e
|
|||
}
|
||||
}()
|
||||
|
||||
// TODO(dustin): Add test
|
||||
|
||||
count := int(unitCount)
|
||||
|
||||
if len(data) < (TypeAscii.Size() * count) {
|
||||
|
@ -81,6 +87,8 @@ func (p *Parser) ParseShorts(data []byte, unitCount uint32, byteOrder binary.Byt
|
|||
}
|
||||
}()
|
||||
|
||||
// TODO(dustin): Add test
|
||||
|
||||
count := int(unitCount)
|
||||
|
||||
if len(data) < (TypeShort.Size() * count) {
|
||||
|
@ -102,6 +110,8 @@ func (p *Parser) ParseLongs(data []byte, unitCount uint32, byteOrder binary.Byte
|
|||
}
|
||||
}()
|
||||
|
||||
// TODO(dustin): Add test
|
||||
|
||||
count := int(unitCount)
|
||||
|
||||
if len(data) < (TypeLong.Size() * count) {
|
||||
|
@ -123,6 +133,8 @@ func (p *Parser) ParseRationals(data []byte, unitCount uint32, byteOrder binary.
|
|||
}
|
||||
}()
|
||||
|
||||
// TODO(dustin): Add test
|
||||
|
||||
count := int(unitCount)
|
||||
|
||||
if len(data) < (TypeRational.Size() * count) {
|
||||
|
@ -145,6 +157,8 @@ func (p *Parser) ParseSignedLongs(data []byte, unitCount uint32, byteOrder binar
|
|||
}
|
||||
}()
|
||||
|
||||
// TODO(dustin): Add test
|
||||
|
||||
count := int(unitCount)
|
||||
|
||||
if len(data) < (TypeSignedLong.Size() * count) {
|
||||
|
@ -169,6 +183,8 @@ func (p *Parser) ParseSignedRationals(data []byte, unitCount uint32, byteOrder b
|
|||
}
|
||||
}()
|
||||
|
||||
// TODO(dustin): Add test
|
||||
|
||||
count := int(unitCount)
|
||||
|
||||
if len(data) < (TypeSignedRational.Size() * count) {
|
|
@ -1,4 +1,4 @@
|
|||
package exif
|
||||
package exifcommon
|
||||
|
||||
import (
|
||||
"errors"
|
|
@ -1,4 +1,4 @@
|
|||
package exif
|
||||
package exifcommon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -217,46 +217,3 @@ 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 {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
// TODO(dustin): This is redundant with Encode. Refactor one to use the other.
|
||||
|
||||
switch tt.Type() {
|
||||
case TypeByte:
|
||||
ed, err = ve.encodeBytes(value.([]byte))
|
||||
log.PanicIf(err)
|
||||
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)
|
||||
case TypeLong:
|
||||
ed, err = ve.encodeLongs(value.([]uint32))
|
||||
log.PanicIf(err)
|
||||
case TypeRational:
|
||||
ed, err = ve.encodeRationals(value.([]Rational))
|
||||
log.PanicIf(err)
|
||||
case TypeSignedLong:
|
||||
ed, err = ve.encodeSignedLongs(value.([]int32))
|
||||
log.PanicIf(err)
|
||||
case TypeSignedRational:
|
||||
ed, err = ve.encodeSignedRationals(value.([]SignedRational))
|
||||
log.PanicIf(err)
|
||||
default:
|
||||
log.Panicf("value not encodable (with type): %v [%v]", tt, value)
|
||||
}
|
||||
|
||||
return ed, nil
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
package exif
|
||||
package exifcommon
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
)
|
||||
|
@ -105,7 +105,7 @@ func TestShortCycle(t *testing.T) {
|
|||
byteOrder := TestDefaultByteOrder
|
||||
ve := NewValueEncoder(byteOrder)
|
||||
|
||||
original := []uint16 { 0x11, 0x22, 0x33, 0x44, 0x55 }
|
||||
original := []uint16{0x11, 0x22, 0x33, 0x44, 0x55}
|
||||
|
||||
ed, err := ve.encodeShorts(original)
|
||||
log.PanicIf(err)
|
||||
|
@ -114,7 +114,7 @@ func TestShortCycle(t *testing.T) {
|
|||
t.Fatalf("IFD type not expected.")
|
||||
}
|
||||
|
||||
expected := []byte {
|
||||
expected := []byte{
|
||||
0x00, 0x11,
|
||||
0x00, 0x22,
|
||||
0x00, 0x33,
|
||||
|
@ -140,7 +140,7 @@ func TestLongCycle(t *testing.T) {
|
|||
byteOrder := TestDefaultByteOrder
|
||||
ve := NewValueEncoder(byteOrder)
|
||||
|
||||
original := []uint32 { 0x11, 0x22, 0x33, 0x44, 0x55 }
|
||||
original := []uint32{0x11, 0x22, 0x33, 0x44, 0x55}
|
||||
|
||||
ed, err := ve.encodeLongs(original)
|
||||
log.PanicIf(err)
|
||||
|
@ -149,7 +149,7 @@ func TestLongCycle(t *testing.T) {
|
|||
t.Fatalf("IFD type not expected.")
|
||||
}
|
||||
|
||||
expected := []byte {
|
||||
expected := []byte{
|
||||
0x00, 0x00, 0x00, 0x11,
|
||||
0x00, 0x00, 0x00, 0x22,
|
||||
0x00, 0x00, 0x00, 0x33,
|
||||
|
@ -175,25 +175,25 @@ func TestRationalCycle(t *testing.T) {
|
|||
byteOrder := TestDefaultByteOrder
|
||||
ve := NewValueEncoder(byteOrder)
|
||||
|
||||
original := []Rational {
|
||||
original := []Rational{
|
||||
Rational{
|
||||
Numerator: 0x11,
|
||||
Numerator: 0x11,
|
||||
Denominator: 0x22,
|
||||
},
|
||||
Rational{
|
||||
Numerator: 0x33,
|
||||
Numerator: 0x33,
|
||||
Denominator: 0x44,
|
||||
},
|
||||
Rational{
|
||||
Numerator: 0x55,
|
||||
Numerator: 0x55,
|
||||
Denominator: 0x66,
|
||||
},
|
||||
Rational{
|
||||
Numerator: 0x77,
|
||||
Numerator: 0x77,
|
||||
Denominator: 0x88,
|
||||
},
|
||||
Rational{
|
||||
Numerator: 0x99,
|
||||
Numerator: 0x99,
|
||||
Denominator: 0x00,
|
||||
},
|
||||
}
|
||||
|
@ -205,7 +205,7 @@ func TestRationalCycle(t *testing.T) {
|
|||
t.Fatalf("IFD type not expected.")
|
||||
}
|
||||
|
||||
expected := []byte {
|
||||
expected := []byte{
|
||||
0x00, 0x00, 0x00, 0x11,
|
||||
0x00, 0x00, 0x00, 0x22,
|
||||
0x00, 0x00, 0x00, 0x33,
|
||||
|
@ -236,7 +236,7 @@ func TestSignedLongCycle(t *testing.T) {
|
|||
byteOrder := TestDefaultByteOrder
|
||||
ve := NewValueEncoder(byteOrder)
|
||||
|
||||
original := []int32 { 0x11, 0x22, 0x33, 0x44, 0x55 }
|
||||
original := []int32{0x11, 0x22, 0x33, 0x44, 0x55}
|
||||
|
||||
ed, err := ve.encodeSignedLongs(original)
|
||||
log.PanicIf(err)
|
||||
|
@ -245,7 +245,7 @@ func TestSignedLongCycle(t *testing.T) {
|
|||
t.Fatalf("IFD type not expected.")
|
||||
}
|
||||
|
||||
expected := []byte {
|
||||
expected := []byte{
|
||||
0x00, 0x00, 0x00, 0x11,
|
||||
0x00, 0x00, 0x00, 0x22,
|
||||
0x00, 0x00, 0x00, 0x33,
|
||||
|
@ -271,25 +271,25 @@ func TestSignedRationalCycle(t *testing.T) {
|
|||
byteOrder := TestDefaultByteOrder
|
||||
ve := NewValueEncoder(byteOrder)
|
||||
|
||||
original := []SignedRational {
|
||||
original := []SignedRational{
|
||||
SignedRational{
|
||||
Numerator: 0x11,
|
||||
Numerator: 0x11,
|
||||
Denominator: 0x22,
|
||||
},
|
||||
SignedRational{
|
||||
Numerator: 0x33,
|
||||
Numerator: 0x33,
|
||||
Denominator: 0x44,
|
||||
},
|
||||
SignedRational{
|
||||
Numerator: 0x55,
|
||||
Numerator: 0x55,
|
||||
Denominator: 0x66,
|
||||
},
|
||||
SignedRational{
|
||||
Numerator: 0x77,
|
||||
Numerator: 0x77,
|
||||
Denominator: 0x88,
|
||||
},
|
||||
SignedRational{
|
||||
Numerator: 0x99,
|
||||
Numerator: 0x99,
|
||||
Denominator: 0x00,
|
||||
},
|
||||
}
|
||||
|
@ -301,7 +301,7 @@ func TestSignedRationalCycle(t *testing.T) {
|
|||
t.Fatalf("IFD type not expected.")
|
||||
}
|
||||
|
||||
expected := []byte {
|
||||
expected := []byte{
|
||||
0x00, 0x00, 0x00, 0x11,
|
||||
0x00, 0x00, 0x00, 0x22,
|
||||
0x00, 0x00, 0x00, 0x33,
|
||||
|
@ -377,7 +377,7 @@ func TestEncode_Short(t *testing.T) {
|
|||
byteOrder := TestDefaultByteOrder
|
||||
ve := NewValueEncoder(byteOrder)
|
||||
|
||||
original := []uint16 { 0x11, 0x22, 0x33, 0x44, 0x55 }
|
||||
original := []uint16{0x11, 0x22, 0x33, 0x44, 0x55}
|
||||
|
||||
ed, err := ve.Encode(original)
|
||||
log.PanicIf(err)
|
||||
|
@ -386,7 +386,7 @@ func TestEncode_Short(t *testing.T) {
|
|||
t.Fatalf("IFD type not expected.")
|
||||
}
|
||||
|
||||
expected := []byte {
|
||||
expected := []byte{
|
||||
0x00, 0x11,
|
||||
0x00, 0x22,
|
||||
0x00, 0x33,
|
||||
|
@ -405,7 +405,7 @@ func TestEncode_Long(t *testing.T) {
|
|||
byteOrder := TestDefaultByteOrder
|
||||
ve := NewValueEncoder(byteOrder)
|
||||
|
||||
original := []uint32 { 0x11, 0x22, 0x33, 0x44, 0x55 }
|
||||
original := []uint32{0x11, 0x22, 0x33, 0x44, 0x55}
|
||||
|
||||
ed, err := ve.Encode(original)
|
||||
log.PanicIf(err)
|
||||
|
@ -414,7 +414,7 @@ func TestEncode_Long(t *testing.T) {
|
|||
t.Fatalf("IFD type not expected.")
|
||||
}
|
||||
|
||||
expected := []byte {
|
||||
expected := []byte{
|
||||
0x00, 0x00, 0x00, 0x11,
|
||||
0x00, 0x00, 0x00, 0x22,
|
||||
0x00, 0x00, 0x00, 0x33,
|
||||
|
@ -433,25 +433,25 @@ func TestEncode_Rational(t *testing.T) {
|
|||
byteOrder := TestDefaultByteOrder
|
||||
ve := NewValueEncoder(byteOrder)
|
||||
|
||||
original := []Rational {
|
||||
original := []Rational{
|
||||
Rational{
|
||||
Numerator: 0x11,
|
||||
Numerator: 0x11,
|
||||
Denominator: 0x22,
|
||||
},
|
||||
Rational{
|
||||
Numerator: 0x33,
|
||||
Numerator: 0x33,
|
||||
Denominator: 0x44,
|
||||
},
|
||||
Rational{
|
||||
Numerator: 0x55,
|
||||
Numerator: 0x55,
|
||||
Denominator: 0x66,
|
||||
},
|
||||
Rational{
|
||||
Numerator: 0x77,
|
||||
Numerator: 0x77,
|
||||
Denominator: 0x88,
|
||||
},
|
||||
Rational{
|
||||
Numerator: 0x99,
|
||||
Numerator: 0x99,
|
||||
Denominator: 0x00,
|
||||
},
|
||||
}
|
||||
|
@ -463,7 +463,7 @@ func TestEncode_Rational(t *testing.T) {
|
|||
t.Fatalf("IFD type not expected.")
|
||||
}
|
||||
|
||||
expected := []byte {
|
||||
expected := []byte{
|
||||
0x00, 0x00, 0x00, 0x11,
|
||||
0x00, 0x00, 0x00, 0x22,
|
||||
0x00, 0x00, 0x00, 0x33,
|
||||
|
@ -487,7 +487,7 @@ func TestEncode_SignedLong(t *testing.T) {
|
|||
byteOrder := TestDefaultByteOrder
|
||||
ve := NewValueEncoder(byteOrder)
|
||||
|
||||
original := []int32 { 0x11, 0x22, 0x33, 0x44, 0x55 }
|
||||
original := []int32{0x11, 0x22, 0x33, 0x44, 0x55}
|
||||
|
||||
ed, err := ve.Encode(original)
|
||||
log.PanicIf(err)
|
||||
|
@ -496,7 +496,7 @@ func TestEncode_SignedLong(t *testing.T) {
|
|||
t.Fatalf("IFD type not expected.")
|
||||
}
|
||||
|
||||
expected := []byte {
|
||||
expected := []byte{
|
||||
0x00, 0x00, 0x00, 0x11,
|
||||
0x00, 0x00, 0x00, 0x22,
|
||||
0x00, 0x00, 0x00, 0x33,
|
||||
|
@ -515,25 +515,25 @@ func TestEncode_SignedRational(t *testing.T) {
|
|||
byteOrder := TestDefaultByteOrder
|
||||
ve := NewValueEncoder(byteOrder)
|
||||
|
||||
original := []SignedRational {
|
||||
original := []SignedRational{
|
||||
SignedRational{
|
||||
Numerator: 0x11,
|
||||
Numerator: 0x11,
|
||||
Denominator: 0x22,
|
||||
},
|
||||
SignedRational{
|
||||
Numerator: 0x33,
|
||||
Numerator: 0x33,
|
||||
Denominator: 0x44,
|
||||
},
|
||||
SignedRational{
|
||||
Numerator: 0x55,
|
||||
Numerator: 0x55,
|
||||
Denominator: 0x66,
|
||||
},
|
||||
SignedRational{
|
||||
Numerator: 0x77,
|
||||
Numerator: 0x77,
|
||||
Denominator: 0x88,
|
||||
},
|
||||
SignedRational{
|
||||
Numerator: 0x99,
|
||||
Numerator: 0x99,
|
||||
Denominator: 0x00,
|
||||
},
|
||||
}
|
||||
|
@ -545,7 +545,7 @@ func TestEncode_SignedRational(t *testing.T) {
|
|||
t.Fatalf("IFD type not expected.")
|
||||
}
|
||||
|
||||
expected := []byte {
|
||||
expected := []byte{
|
||||
0x00, 0x00, 0x00, 0x11,
|
||||
0x00, 0x00, 0x00, 0x22,
|
||||
0x00, 0x00, 0x00, 0x33,
|
|
@ -0,0 +1,65 @@
|
|||
package exifcommon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
)
|
||||
|
||||
func DumpBytes(data []byte) {
|
||||
fmt.Printf("DUMP: ")
|
||||
for _, x := range data {
|
||||
fmt.Printf("%02x ", x)
|
||||
}
|
||||
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
func DumpBytesClause(data []byte) {
|
||||
fmt.Printf("DUMP: ")
|
||||
|
||||
fmt.Printf("[]byte { ")
|
||||
|
||||
for i, x := range data {
|
||||
fmt.Printf("0x%02x", x)
|
||||
|
||||
if i < len(data)-1 {
|
||||
fmt.Printf(", ")
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf(" }\n")
|
||||
}
|
||||
|
||||
func DumpBytesToString(data []byte) string {
|
||||
b := new(bytes.Buffer)
|
||||
|
||||
for i, x := range data {
|
||||
_, err := b.WriteString(fmt.Sprintf("%02x", x))
|
||||
log.PanicIf(err)
|
||||
|
||||
if i < len(data)-1 {
|
||||
_, err := b.WriteRune(' ')
|
||||
log.PanicIf(err)
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func DumpBytesClauseToString(data []byte) string {
|
||||
b := new(bytes.Buffer)
|
||||
|
||||
for i, x := range data {
|
||||
_, err := b.WriteString(fmt.Sprintf("0x%02x", x))
|
||||
log.PanicIf(err)
|
||||
|
||||
if i < len(data)-1 {
|
||||
_, err := b.WriteString(", ")
|
||||
log.PanicIf(err)
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
|
@ -0,0 +1,361 @@
|
|||
package exifcommon
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
)
|
||||
|
||||
var (
|
||||
parser *Parser
|
||||
)
|
||||
|
||||
// ValueContext describes all of the parameters required to find and extract
|
||||
// the actual tag value.
|
||||
type ValueContext struct {
|
||||
unitCount uint32
|
||||
valueOffset uint32
|
||||
rawValueOffset []byte
|
||||
addressableData []byte
|
||||
|
||||
tagType TagTypePrimitive
|
||||
byteOrder binary.ByteOrder
|
||||
|
||||
// undefinedValueTagType is the effective type to use if this is an
|
||||
// "undefined" value.
|
||||
undefinedValueTagType TagTypePrimitive
|
||||
|
||||
ifdPath string
|
||||
tagId uint16
|
||||
}
|
||||
|
||||
func newValueContext(ifdPath string, tagId uint16, unitCount, valueOffset uint32, rawValueOffset, addressableData []byte, tagType TagTypePrimitive, byteOrder binary.ByteOrder) *ValueContext {
|
||||
return &ValueContext{
|
||||
unitCount: unitCount,
|
||||
valueOffset: valueOffset,
|
||||
rawValueOffset: rawValueOffset,
|
||||
addressableData: addressableData,
|
||||
|
||||
tagType: tagType,
|
||||
byteOrder: byteOrder,
|
||||
|
||||
ifdPath: ifdPath,
|
||||
tagId: tagId,
|
||||
}
|
||||
}
|
||||
|
||||
func (vc *ValueContext) SetUnknownValueType(tagType TagTypePrimitive) {
|
||||
vc.undefinedValueTagType = tagType
|
||||
}
|
||||
|
||||
func (vc *ValueContext) UnitCount() uint32 {
|
||||
return vc.unitCount
|
||||
}
|
||||
|
||||
func (vc *ValueContext) ValueOffset() uint32 {
|
||||
return vc.valueOffset
|
||||
}
|
||||
|
||||
func (vc *ValueContext) RawValueOffset() []byte {
|
||||
return vc.rawValueOffset
|
||||
}
|
||||
|
||||
func (vc *ValueContext) AddressableData() []byte {
|
||||
return vc.addressableData
|
||||
}
|
||||
|
||||
func (vc *ValueContext) ByteOrder() binary.ByteOrder {
|
||||
return vc.byteOrder
|
||||
}
|
||||
|
||||
func (vc *ValueContext) IfdPath() string {
|
||||
return vc.ifdPath
|
||||
}
|
||||
|
||||
func (vc *ValueContext) TagId() uint16 {
|
||||
return vc.tagId
|
||||
}
|
||||
|
||||
// isEmbedded returns whether the value is embedded or a reference. This can't
|
||||
// be precalculated since the size is not defined for all types (namely the
|
||||
// "undefined" types).
|
||||
func (vc *ValueContext) isEmbedded() bool {
|
||||
tagType := vc.effectiveValueType()
|
||||
|
||||
return (tagType.Size() * int(vc.unitCount)) <= 4
|
||||
}
|
||||
|
||||
func (vc *ValueContext) effectiveValueType() (tagType TagTypePrimitive) {
|
||||
if vc.tagType == TypeUndefined {
|
||||
tagType = vc.undefinedValueTagType
|
||||
|
||||
if tagType == 0 {
|
||||
log.Panicf("undefined-value type not set")
|
||||
}
|
||||
} else {
|
||||
tagType = vc.tagType
|
||||
}
|
||||
|
||||
return tagType
|
||||
}
|
||||
|
||||
func (vc *ValueContext) readRawEncoded() (rawBytes []byte, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
tagType := vc.effectiveValueType()
|
||||
|
||||
unitSizeRaw := uint32(tagType.Size())
|
||||
|
||||
if vc.isEmbedded() == true {
|
||||
byteLength := unitSizeRaw * vc.unitCount
|
||||
return vc.rawValueOffset[:byteLength], nil
|
||||
} else {
|
||||
return vc.addressableData[vc.valueOffset : vc.valueOffset+vc.unitCount*unitSizeRaw], nil
|
||||
}
|
||||
}
|
||||
|
||||
// Format returns a string representation for the value.
|
||||
//
|
||||
// Where the type is not ASCII, `justFirst` indicates whether to just stringify
|
||||
// the first item in the slice (or return an empty string if the slice is
|
||||
// empty).
|
||||
//
|
||||
// Since this method lacks the information to process undefined-type tags (e.g.
|
||||
// byte-order, tag-ID, IFD type), it will return an error if attempted. See
|
||||
// `Undefined()`.
|
||||
func (vc *ValueContext) Format() (value string, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawBytes, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
phrase, err := Format(rawBytes, vc.tagType, false, vc.byteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
return phrase, nil
|
||||
}
|
||||
|
||||
// FormatOne is similar to `Format` but only gets and stringifies the first
|
||||
// item.
|
||||
func (vc *ValueContext) FormatFirst() (value string, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawBytes, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
phrase, err := Format(rawBytes, vc.tagType, true, vc.byteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
return phrase, nil
|
||||
}
|
||||
|
||||
func (vc *ValueContext) ReadBytes() (value []byte, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawValue, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
value, err = parser.ParseBytes(rawValue, vc.unitCount)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (vc *ValueContext) ReadAscii() (value string, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawValue, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
value, err = parser.ParseAscii(rawValue, vc.unitCount)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (vc *ValueContext) ReadAsciiNoNul() (value string, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawValue, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
value, err = parser.ParseAsciiNoNul(rawValue, vc.unitCount)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (vc *ValueContext) ReadShorts() (value []uint16, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawValue, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
value, err = parser.ParseShorts(rawValue, vc.unitCount, vc.byteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (vc *ValueContext) ReadLongs() (value []uint32, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawValue, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
value, err = parser.ParseLongs(rawValue, vc.unitCount, vc.byteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (vc *ValueContext) ReadRationals() (value []Rational, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawValue, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
value, err = parser.ParseRationals(rawValue, vc.unitCount, vc.byteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (vc *ValueContext) ReadSignedLongs() (value []int32, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawValue, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
value, err = parser.ParseSignedLongs(rawValue, vc.unitCount, vc.byteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (vc *ValueContext) ReadSignedRationals() (value []SignedRational, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawValue, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
value, err = parser.ParseSignedRationals(rawValue, vc.unitCount, vc.byteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// Values knows how to resolve the given value. This value is always a list
|
||||
// (undefined-values aside), so we're named accordingly.
|
||||
//
|
||||
// Since this method lacks the information to process unknown-type tags (e.g.
|
||||
// byte-order, tag-ID, IFD type), it will return an error if attempted. See
|
||||
// `Undefined()`.
|
||||
func (vc *ValueContext) Values() (values interface{}, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
if vc.tagType == TypeByte {
|
||||
values, err = vc.ReadBytes()
|
||||
log.PanicIf(err)
|
||||
} else if vc.tagType == TypeAscii {
|
||||
values, err = vc.ReadAscii()
|
||||
log.PanicIf(err)
|
||||
} else if vc.tagType == TypeAsciiNoNul {
|
||||
values, err = vc.ReadAsciiNoNul()
|
||||
log.PanicIf(err)
|
||||
} else if vc.tagType == TypeShort {
|
||||
values, err = vc.ReadShorts()
|
||||
log.PanicIf(err)
|
||||
} else if vc.tagType == TypeLong {
|
||||
values, err = vc.ReadLongs()
|
||||
log.PanicIf(err)
|
||||
} else if vc.tagType == TypeRational {
|
||||
values, err = vc.ReadRationals()
|
||||
log.PanicIf(err)
|
||||
} else if vc.tagType == TypeSignedLong {
|
||||
values, err = vc.ReadSignedLongs()
|
||||
log.PanicIf(err)
|
||||
} else if vc.tagType == TypeSignedRational {
|
||||
values, err = vc.ReadSignedRationals()
|
||||
log.PanicIf(err)
|
||||
} else if vc.tagType == TypeUndefined {
|
||||
log.Panicf("will not parse undefined-type value")
|
||||
|
||||
// Never called.
|
||||
return nil, nil
|
||||
} else {
|
||||
log.Panicf("value of type [%s] is unparseable", vc.tagType)
|
||||
|
||||
// Never called.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// // Undefined attempts to identify and decode supported undefined-type fields.
|
||||
// // This is the primary, preferred interface to reading undefined values.
|
||||
// func (vc *ValueContext) Undefined() (value interface{}, err error) {
|
||||
// defer func() {
|
||||
// if state := recover(); state != nil {
|
||||
// err = log.Wrap(state.(error))
|
||||
// }
|
||||
// }()
|
||||
|
||||
// value, err = UndefinedValue(vc.ifdPath, vc.tagId, vc, vc.byteOrder)
|
||||
// log.PanicIf(err)
|
||||
|
||||
// return value, nil
|
||||
// }
|
||||
|
||||
func init() {
|
||||
parser = new(Parser)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package exif
|
||||
package exifcommon
|
||||
|
||||
import (
|
||||
"bytes"
|
|
@ -29,12 +29,6 @@ const (
|
|||
var (
|
||||
exifLogger = log.NewLogger("exif.exif")
|
||||
|
||||
// EncodeDefaultByteOrder is the default byte-order for encoding operations.
|
||||
EncodeDefaultByteOrder = binary.BigEndian
|
||||
|
||||
// Default byte order for tests.
|
||||
TestDefaultByteOrder = binary.BigEndian
|
||||
|
||||
BigEndianBoBytes = [2]byte{'M', 'M'}
|
||||
LittleEndianBoBytes = [2]byte{'I', 'I'}
|
||||
|
||||
|
|
|
@ -3,9 +3,8 @@ module github.com/dsoprea/go-exif/v2
|
|||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/dsoprea/go-exif v0.0.0-20200102050106-254a6b902a75
|
||||
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696
|
||||
github.com/go-errors/errors v1.0.1 // indirect
|
||||
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.7
|
||||
)
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
github.com/dsoprea/go-exif v0.0.0-20200102050106-254a6b902a75 h1:WeCZb0AeChtLIgHhVtGOuJmomEYgEzckxkzSnfuubzc=
|
||||
github.com/dsoprea/go-exif v0.0.0-20200102050106-254a6b902a75/go.mod h1:lOaOt7+UEppOgyvRy749v3do836U/hw0YVJNjoyPaEs=
|
||||
github.com/dsoprea/go-exif v0.0.0-20200102120628-74567945ac68 h1:A7DoYvhnDGMhePdmSGyoCWhH5pW0VHgT2E6dEJRlzlo=
|
||||
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696 h1:VGFnZAcLwPpt1sHlAxml+pGLZz9A2s+K/s1YNhPC91Y=
|
||||
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696/go.mod h1:Nm/x2ZUNRW6Fe5C3LxdY1PyZY5wmDv/s5dkPJ/VB3iA=
|
||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
||||
|
|
28
v2/ifd.go
28
v2/ifd.go
|
@ -8,34 +8,6 @@ import (
|
|||
"github.com/dsoprea/go-logging"
|
||||
)
|
||||
|
||||
const (
|
||||
// IFD names. The paths that we referred to the IFDs with are comprised of
|
||||
// these.
|
||||
|
||||
IfdStandard = "IFD"
|
||||
IfdExif = "Exif"
|
||||
IfdGps = "GPSInfo"
|
||||
IfdIop = "Iop"
|
||||
|
||||
// Tag IDs for child IFDs.
|
||||
|
||||
IfdExifId = 0x8769
|
||||
IfdGpsId = 0x8825
|
||||
IfdIopId = 0xA005
|
||||
|
||||
// Just a placeholder.
|
||||
|
||||
IfdRootId = 0x0000
|
||||
|
||||
// The paths of the standard IFDs expressed in the standard IFD-mappings
|
||||
// and as the group-names in the tag data.
|
||||
|
||||
IfdPathStandard = "IFD"
|
||||
IfdPathStandardExif = "IFD/Exif"
|
||||
IfdPathStandardExifIop = "IFD/Exif/Iop"
|
||||
IfdPathStandardGps = "IFD/GPSInfo"
|
||||
)
|
||||
|
||||
var (
|
||||
ifdLogger = log.NewLogger("exif.ifd")
|
||||
)
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package exif
|
||||
package exifcommon
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
)
|
||||
|
@ -12,7 +12,7 @@ import (
|
|||
func TestTagType_EncodeDecode_Byte(t *testing.T) {
|
||||
tt := NewTagType(TypeByte, TestDefaultByteOrder)
|
||||
|
||||
data := []byte { 0x11, 0x22, 0x33, 0x44, 0x55 }
|
||||
data := []byte{0x11, 0x22, 0x33, 0x44, 0x55}
|
||||
|
||||
encoded, err := tt.Encode(data)
|
||||
log.PanicIf(err)
|
||||
|
@ -52,12 +52,12 @@ func TestTagType_EncodeDecode_Ascii(t *testing.T) {
|
|||
func TestTagType_EncodeDecode_Shorts(t *testing.T) {
|
||||
tt := NewTagType(TypeShort, TestDefaultByteOrder)
|
||||
|
||||
data := []uint16 { 0x11, 0x22, 0x33 }
|
||||
data := []uint16{0x11, 0x22, 0x33}
|
||||
|
||||
encoded, err := tt.Encode(data)
|
||||
log.PanicIf(err)
|
||||
|
||||
if bytes.Compare(encoded, []byte { 0x00, 0x11, 0x00, 0x22, 0x00, 0x33 }) != 0 {
|
||||
if bytes.Compare(encoded, []byte{0x00, 0x11, 0x00, 0x22, 0x00, 0x33}) != 0 {
|
||||
t.Fatalf("Data not encoded correctly.")
|
||||
}
|
||||
|
||||
|
@ -72,12 +72,12 @@ func TestTagType_EncodeDecode_Shorts(t *testing.T) {
|
|||
func TestTagType_EncodeDecode_Long(t *testing.T) {
|
||||
tt := NewTagType(TypeLong, TestDefaultByteOrder)
|
||||
|
||||
data := []uint32 { 0x11, 0x22, 0x33 }
|
||||
data := []uint32{0x11, 0x22, 0x33}
|
||||
|
||||
encoded, err := tt.Encode(data)
|
||||
log.PanicIf(err)
|
||||
|
||||
if bytes.Compare(encoded, []byte { 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x33 }) != 0 {
|
||||
if bytes.Compare(encoded, []byte{0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x33}) != 0 {
|
||||
t.Fatalf("Data not encoded correctly.")
|
||||
}
|
||||
|
||||
|
@ -92,15 +92,15 @@ func TestTagType_EncodeDecode_Long(t *testing.T) {
|
|||
func TestTagType_EncodeDecode_Rational(t *testing.T) {
|
||||
tt := NewTagType(TypeRational, TestDefaultByteOrder)
|
||||
|
||||
data := []Rational {
|
||||
Rational{ Numerator: 0x11, Denominator: 0x22 },
|
||||
Rational{ Numerator: 0x33, Denominator: 0x44 },
|
||||
data := []Rational{
|
||||
Rational{Numerator: 0x11, Denominator: 0x22},
|
||||
Rational{Numerator: 0x33, Denominator: 0x44},
|
||||
}
|
||||
|
||||
encoded, err := tt.Encode(data)
|
||||
log.PanicIf(err)
|
||||
|
||||
if bytes.Compare(encoded, []byte { 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x44 }) != 0 {
|
||||
if bytes.Compare(encoded, []byte{0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x44}) != 0 {
|
||||
t.Fatalf("Data not encoded correctly.")
|
||||
}
|
||||
|
||||
|
@ -115,12 +115,12 @@ func TestTagType_EncodeDecode_Rational(t *testing.T) {
|
|||
func TestTagType_EncodeDecode_SignedLong(t *testing.T) {
|
||||
tt := NewTagType(TypeSignedLong, TestDefaultByteOrder)
|
||||
|
||||
data := []int32 { 0x11, 0x22, 0x33 }
|
||||
data := []int32{0x11, 0x22, 0x33}
|
||||
|
||||
encoded, err := tt.Encode(data)
|
||||
log.PanicIf(err)
|
||||
|
||||
if bytes.Compare(encoded, []byte { 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x33 }) != 0 {
|
||||
if bytes.Compare(encoded, []byte{0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x33}) != 0 {
|
||||
t.Fatalf("Data not encoded correctly.")
|
||||
}
|
||||
|
||||
|
@ -135,15 +135,15 @@ func TestTagType_EncodeDecode_SignedLong(t *testing.T) {
|
|||
func TestTagType_EncodeDecode_SignedRational(t *testing.T) {
|
||||
tt := NewTagType(TypeSignedRational, TestDefaultByteOrder)
|
||||
|
||||
data := []SignedRational {
|
||||
SignedRational{ Numerator: 0x11, Denominator: 0x22 },
|
||||
SignedRational{ Numerator: 0x33, Denominator: 0x44 },
|
||||
data := []SignedRational{
|
||||
SignedRational{Numerator: 0x11, Denominator: 0x22},
|
||||
SignedRational{Numerator: 0x33, Denominator: 0x44},
|
||||
}
|
||||
|
||||
encoded, err := tt.Encode(data)
|
||||
log.PanicIf(err)
|
||||
|
||||
if bytes.Compare(encoded, []byte { 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x44 }) != 0 {
|
||||
if bytes.Compare(encoded, []byte{0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x44}) != 0 {
|
||||
t.Fatalf("Data not encoded correctly.")
|
||||
}
|
||||
|
||||
|
@ -204,7 +204,7 @@ func TestTagType_FromString_Byte(t *testing.T) {
|
|||
value, err := tt.FromString("abc")
|
||||
log.PanicIf(err)
|
||||
|
||||
if reflect.DeepEqual(value, []byte { 'a', 'b', 'c' }) != true {
|
||||
if reflect.DeepEqual(value, []byte{'a', 'b', 'c'}) != true {
|
||||
t.Fatalf("byte value not correct")
|
||||
}
|
||||
}
|
||||
|
@ -249,7 +249,7 @@ func TestTagType_FromString_Rational(t *testing.T) {
|
|||
log.PanicIf(err)
|
||||
|
||||
expected := Rational{
|
||||
Numerator: 12,
|
||||
Numerator: 12,
|
||||
Denominator: 34,
|
||||
}
|
||||
|
||||
|
@ -276,7 +276,7 @@ func TestTagType_FromString_SignedRational(t *testing.T) {
|
|||
log.PanicIf(err)
|
||||
|
||||
expected := SignedRational{
|
||||
Numerator: -12,
|
||||
Numerator: -12,
|
||||
Denominator: 34,
|
||||
}
|
||||
|
|
@ -1,417 +0,0 @@
|
|||
package exif
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"crypto/sha1"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
)
|
||||
|
||||
const (
|
||||
UnparseableUnknownTagValuePlaceholder = "!UNKNOWN"
|
||||
)
|
||||
|
||||
// TODO(dustin): Rename "unknown" in symbol names to "undefined" in the next release.
|
||||
//
|
||||
// See https://github.com/dsoprea/go-exif/issues/27 .
|
||||
|
||||
const (
|
||||
TagUnknownType_9298_UserComment_Encoding_ASCII = iota
|
||||
TagUnknownType_9298_UserComment_Encoding_JIS = iota
|
||||
TagUnknownType_9298_UserComment_Encoding_UNICODE = iota
|
||||
TagUnknownType_9298_UserComment_Encoding_UNDEFINED = iota
|
||||
)
|
||||
|
||||
const (
|
||||
TagUnknownType_9101_ComponentsConfiguration_Channel_Y = 0x1
|
||||
TagUnknownType_9101_ComponentsConfiguration_Channel_Cb = 0x2
|
||||
TagUnknownType_9101_ComponentsConfiguration_Channel_Cr = 0x3
|
||||
TagUnknownType_9101_ComponentsConfiguration_Channel_R = 0x4
|
||||
TagUnknownType_9101_ComponentsConfiguration_Channel_G = 0x5
|
||||
TagUnknownType_9101_ComponentsConfiguration_Channel_B = 0x6
|
||||
)
|
||||
|
||||
const (
|
||||
TagUnknownType_9101_ComponentsConfiguration_OTHER = iota
|
||||
TagUnknownType_9101_ComponentsConfiguration_RGB = iota
|
||||
TagUnknownType_9101_ComponentsConfiguration_YCBCR = iota
|
||||
)
|
||||
|
||||
var (
|
||||
TagUnknownType_9298_UserComment_Encoding_Names = map[int]string{
|
||||
TagUnknownType_9298_UserComment_Encoding_ASCII: "ASCII",
|
||||
TagUnknownType_9298_UserComment_Encoding_JIS: "JIS",
|
||||
TagUnknownType_9298_UserComment_Encoding_UNICODE: "UNICODE",
|
||||
TagUnknownType_9298_UserComment_Encoding_UNDEFINED: "UNDEFINED",
|
||||
}
|
||||
|
||||
TagUnknownType_9298_UserComment_Encodings = map[int][]byte{
|
||||
TagUnknownType_9298_UserComment_Encoding_ASCII: []byte{'A', 'S', 'C', 'I', 'I', 0, 0, 0},
|
||||
TagUnknownType_9298_UserComment_Encoding_JIS: []byte{'J', 'I', 'S', 0, 0, 0, 0, 0},
|
||||
TagUnknownType_9298_UserComment_Encoding_UNICODE: []byte{'U', 'n', 'i', 'c', 'o', 'd', 'e', 0},
|
||||
TagUnknownType_9298_UserComment_Encoding_UNDEFINED: []byte{0, 0, 0, 0, 0, 0, 0, 0},
|
||||
}
|
||||
|
||||
TagUnknownType_9101_ComponentsConfiguration_Names = map[int]string{
|
||||
TagUnknownType_9101_ComponentsConfiguration_OTHER: "OTHER",
|
||||
TagUnknownType_9101_ComponentsConfiguration_RGB: "RGB",
|
||||
TagUnknownType_9101_ComponentsConfiguration_YCBCR: "YCBCR",
|
||||
}
|
||||
|
||||
TagUnknownType_9101_ComponentsConfiguration_Configurations = map[int][]byte{
|
||||
TagUnknownType_9101_ComponentsConfiguration_RGB: []byte{
|
||||
TagUnknownType_9101_ComponentsConfiguration_Channel_R,
|
||||
TagUnknownType_9101_ComponentsConfiguration_Channel_G,
|
||||
TagUnknownType_9101_ComponentsConfiguration_Channel_B,
|
||||
0,
|
||||
},
|
||||
|
||||
TagUnknownType_9101_ComponentsConfiguration_YCBCR: []byte{
|
||||
TagUnknownType_9101_ComponentsConfiguration_Channel_Y,
|
||||
TagUnknownType_9101_ComponentsConfiguration_Channel_Cb,
|
||||
TagUnknownType_9101_ComponentsConfiguration_Channel_Cr,
|
||||
0,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// TODO(dustin): Rename `UnknownTagValue` to `UndefinedTagValue`.
|
||||
|
||||
type UnknownTagValue interface {
|
||||
ValueBytes() ([]byte, error)
|
||||
}
|
||||
|
||||
// TODO(dustin): Rename `TagUnknownType_GeneralString` to `TagUnknownType_GeneralString`.
|
||||
|
||||
type TagUnknownType_GeneralString string
|
||||
|
||||
func (gs TagUnknownType_GeneralString) ValueBytes() (value []byte, err error) {
|
||||
return []byte(gs), nil
|
||||
}
|
||||
|
||||
// TODO(dustin): Rename `TagUnknownType_9298_UserComment` to `TagUndefinedType_9298_UserComment`.
|
||||
|
||||
type TagUnknownType_9298_UserComment struct {
|
||||
EncodingType int
|
||||
EncodingBytes []byte
|
||||
}
|
||||
|
||||
func (uc TagUnknownType_9298_UserComment) String() string {
|
||||
var valuePhrase string
|
||||
|
||||
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), TagUnknownType_9298_UserComment_Encoding_Names[uc.EncodingType], valuePhrase, len(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)
|
||||
copy(value[8:], uc.EncodingBytes)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// TODO(dustin): Rename `TagUnknownType_927C_MakerNote` to `TagUndefinedType_927C_MakerNote`.
|
||||
|
||||
type TagUnknownType_927C_MakerNote struct {
|
||||
MakerNoteType []byte
|
||||
MakerNoteBytes []byte
|
||||
}
|
||||
|
||||
func (mn TagUnknownType_927C_MakerNote) String() string {
|
||||
parts := make([]string, 20)
|
||||
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)
|
||||
}
|
||||
|
||||
func (uc TagUnknownType_927C_MakerNote) ValueBytes() (value []byte, err error) {
|
||||
return uc.MakerNoteBytes, nil
|
||||
}
|
||||
|
||||
// TODO(dustin): Rename `TagUnknownType_9101_ComponentsConfiguration` to `TagUndefinedType_9101_ComponentsConfiguration`.
|
||||
|
||||
type TagUnknownType_9101_ComponentsConfiguration struct {
|
||||
ConfigurationId int
|
||||
ConfigurationBytes []byte
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// TODO(dustin): Rename `EncodeUnknown_9286` to `EncodeUndefined_9286`.
|
||||
|
||||
func EncodeUnknown_9286(uc TagUnknownType_9298_UserComment) (encoded []byte, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
|
||||
encodingTypeBytes := TagUnknownType_9298_UserComment_Encodings[uc.EncodingType]
|
||||
|
||||
_, err = b.Write(encodingTypeBytes)
|
||||
log.PanicIf(err)
|
||||
|
||||
_, err = b.Write(uc.EncodingBytes)
|
||||
log.PanicIf(err)
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
type EncodeableUndefinedValue struct {
|
||||
IfdPath string
|
||||
TagId uint16
|
||||
Parameters interface{}
|
||||
}
|
||||
|
||||
func EncodeUndefined(ifdPath string, tagId uint16, value interface{}) (ed EncodedData, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
// TODO(dustin): !! Finish implementing these.
|
||||
if ifdPath == IfdPathStandardExif {
|
||||
if tagId == 0x9286 {
|
||||
encoded, err := EncodeUnknown_9286(value.(TagUnknownType_9298_UserComment))
|
||||
log.PanicIf(err)
|
||||
|
||||
ed.Type = TypeUndefined
|
||||
ed.Encoded = encoded
|
||||
ed.UnitCount = uint32(len(encoded))
|
||||
|
||||
return ed, nil
|
||||
}
|
||||
}
|
||||
|
||||
log.Panicf("undefined value not encodable: %s (0x%02x)", ifdPath, tagId)
|
||||
|
||||
// Never called.
|
||||
return EncodedData{}, nil
|
||||
}
|
||||
|
||||
// TODO(dustin): Rename `TagUnknownType_UnknownValue` to `TagUndefinedType_UnknownValue`.
|
||||
|
||||
type TagUnknownType_UnknownValue []byte
|
||||
|
||||
func (tutuv TagUnknownType_UnknownValue) String() string {
|
||||
parts := make([]string, len(tutuv))
|
||||
for i, c := range tutuv {
|
||||
parts[i] = fmt.Sprintf("%02x", c)
|
||||
}
|
||||
|
||||
h := sha1.New()
|
||||
|
||||
_, err := h.Write(tutuv)
|
||||
log.PanicIf(err)
|
||||
|
||||
digest := h.Sum(nil)
|
||||
|
||||
return fmt.Sprintf("Unknown<DATA=[%s] LEN=(%d) SHA1=[%020x]>", strings.Join(parts, " "), len(tutuv), digest)
|
||||
}
|
||||
|
||||
// UndefinedValue knows how to resolve the value for most unknown-type tags.
|
||||
func UndefinedValue(ifdPath string, tagId uint16, valueContext interface{}, byteOrder binary.ByteOrder) (value interface{}, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
// TODO(dustin): Stop exporting this. Use `(*ValueContext).Undefined()`.
|
||||
|
||||
var valueContextPtr *ValueContext
|
||||
|
||||
if vc, ok := valueContext.(*ValueContext); ok == true {
|
||||
// Legacy usage.
|
||||
|
||||
valueContextPtr = vc
|
||||
} else {
|
||||
// Standard usage.
|
||||
|
||||
valueContextValue := valueContext.(ValueContext)
|
||||
valueContextPtr = &valueContextValue
|
||||
}
|
||||
|
||||
typeLogger.Debugf(nil, "UndefinedValue: IFD-PATH=[%s] TAG-ID=(0x%02x)", ifdPath, tagId)
|
||||
|
||||
if ifdPath == IfdPathStandardExif {
|
||||
if tagId == 0x9000 {
|
||||
// ExifVersion
|
||||
|
||||
valueContextPtr.SetUnknownValueType(TypeAsciiNoNul)
|
||||
|
||||
valueString, err := valueContextPtr.ReadAsciiNoNul()
|
||||
log.PanicIf(err)
|
||||
|
||||
return TagUnknownType_GeneralString(valueString), nil
|
||||
} else if tagId == 0xa000 {
|
||||
// FlashpixVersion
|
||||
|
||||
valueContextPtr.SetUnknownValueType(TypeAsciiNoNul)
|
||||
|
||||
valueString, err := valueContextPtr.ReadAsciiNoNul()
|
||||
log.PanicIf(err)
|
||||
|
||||
return TagUnknownType_GeneralString(valueString), nil
|
||||
} else if tagId == 0x9286 {
|
||||
// UserComment
|
||||
|
||||
valueContextPtr.SetUnknownValueType(TypeByte)
|
||||
|
||||
valueBytes, err := valueContextPtr.ReadBytes()
|
||||
log.PanicIf(err)
|
||||
|
||||
unknownUc := TagUnknownType_9298_UserComment{
|
||||
EncodingType: TagUnknownType_9298_UserComment_Encoding_UNDEFINED,
|
||||
EncodingBytes: []byte{},
|
||||
}
|
||||
|
||||
encoding := valueBytes[:8]
|
||||
for encodingIndex, encodingBytes := range TagUnknownType_9298_UserComment_Encodings {
|
||||
if bytes.Compare(encoding, encodingBytes) == 0 {
|
||||
uc := TagUnknownType_9298_UserComment{
|
||||
EncodingType: encodingIndex,
|
||||
EncodingBytes: valueBytes[8:],
|
||||
}
|
||||
|
||||
return uc, nil
|
||||
}
|
||||
}
|
||||
|
||||
typeLogger.Warningf(nil, "User-comment encoding not valid. Returning 'unknown' type (the default).")
|
||||
return unknownUc, nil
|
||||
} else if tagId == 0x927c {
|
||||
// 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).
|
||||
|
||||
valueContextPtr.SetUnknownValueType(TypeByte)
|
||||
|
||||
valueBytes, err := valueContextPtr.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)
|
||||
// }
|
||||
|
||||
mn := TagUnknownType_927C_MakerNote{
|
||||
MakerNoteType: valueBytes[:20],
|
||||
|
||||
// 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
|
||||
} else if tagId == 0x9101 {
|
||||
// ComponentsConfiguration
|
||||
|
||||
valueContextPtr.SetUnknownValueType(TypeByte)
|
||||
|
||||
valueBytes, err := valueContextPtr.ReadBytes()
|
||||
log.PanicIf(err)
|
||||
|
||||
for configurationId, configurationBytes := range TagUnknownType_9101_ComponentsConfiguration_Configurations {
|
||||
if bytes.Compare(valueBytes, configurationBytes) == 0 {
|
||||
cc := TagUnknownType_9101_ComponentsConfiguration{
|
||||
ConfigurationId: configurationId,
|
||||
ConfigurationBytes: valueBytes,
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
}
|
||||
|
||||
cc := TagUnknownType_9101_ComponentsConfiguration{
|
||||
ConfigurationId: TagUnknownType_9101_ComponentsConfiguration_OTHER,
|
||||
ConfigurationBytes: valueBytes,
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
} else if ifdPath == IfdPathStandardGps {
|
||||
if tagId == 0x001c {
|
||||
// GPSAreaInformation
|
||||
|
||||
valueContextPtr.SetUnknownValueType(TypeAsciiNoNul)
|
||||
|
||||
valueString, err := valueContextPtr.ReadAsciiNoNul()
|
||||
log.PanicIf(err)
|
||||
|
||||
return TagUnknownType_GeneralString(valueString), nil
|
||||
} else if tagId == 0x001b {
|
||||
// GPSProcessingMethod
|
||||
|
||||
valueContextPtr.SetUnknownValueType(TypeAsciiNoNul)
|
||||
|
||||
valueString, err := valueContextPtr.ReadAsciiNoNul()
|
||||
log.PanicIf(err)
|
||||
|
||||
return TagUnknownType_GeneralString(valueString), nil
|
||||
}
|
||||
} else if ifdPath == IfdPathStandardExifIop {
|
||||
if tagId == 0x0002 {
|
||||
// InteropVersion
|
||||
|
||||
valueContextPtr.SetUnknownValueType(TypeAsciiNoNul)
|
||||
|
||||
valueString, err := valueContextPtr.ReadAsciiNoNul()
|
||||
log.PanicIf(err)
|
||||
|
||||
return TagUnknownType_GeneralString(valueString), nil
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(dustin): !! Still need to do:
|
||||
//
|
||||
// complex: 0xa302, 0xa20c, 0x8828
|
||||
// long: 0xa301, 0xa300
|
||||
//
|
||||
// 0xa40b is device-specific and unhandled.
|
||||
//
|
||||
// See https://github.com/dsoprea/go-exif/issues/26.
|
||||
|
||||
// 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, ErrUnhandledUnknownTypedTag
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
package exif
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
)
|
||||
|
||||
func TestUndefinedValue_ExifVersion(t *testing.T) {
|
||||
byteOrder := TestDefaultByteOrder
|
||||
fqIfdPath := "IFD0/Exif0"
|
||||
ifdPath := "IFD/Exif"
|
||||
|
||||
// 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 := &BuilderTag{
|
||||
ifdPath: ifdPath,
|
||||
tagId: 0x9000,
|
||||
typeId: TypeUndefined,
|
||||
value: encodedValue,
|
||||
}
|
||||
|
||||
// Stage the build.
|
||||
|
||||
im := NewIfdMapping()
|
||||
|
||||
err = LoadStandardIfds(im)
|
||||
log.PanicIf(err)
|
||||
|
||||
ti := NewTagIndex()
|
||||
|
||||
ibe := NewIfdByteEncoder()
|
||||
ib := NewIfdBuilder(im, ti, ifdPath, 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(im, ti, fqIfdPath, ifdPath, byteOrder, tagBytes, false)
|
||||
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.ChildIfdPath != "" {
|
||||
t.Fatalf("Tag's child IFD-path should be empty: [%s]", ite.ChildIfdPath)
|
||||
} else if ite.IfdPath != ifdPath {
|
||||
t.Fatalf("Tag's parent IFD is not correct: %v", ite.IfdPath)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(dustin): !! Add tests for remaining, well-defined unknown
|
||||
// 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,4 @@
|
|||
|
||||
## 0xa40b
|
||||
|
||||
The specification is not specific/clear enough to be handled. We're deferring until some point in the future when either we or someone else have a better understanding.
|
|
@ -0,0 +1,61 @@
|
|||
package exifundefined
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
|
||||
"github.com/dsoprea/go-exif/v2/common"
|
||||
)
|
||||
|
||||
func Encode(value interface{}, byteOrder binary.ByteOrder) (encoded []byte, unitCount uint32, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
encoderName := reflect.TypeOf(value).Name()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// UndefinedValue knows how to resolve the value for most unknown-type tags.
|
||||
func Decode(ifdPath string, tagId uint16, valueContext *exifcommon.ValueContext, byteOrder binary.ByteOrder) (value interface{}, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
uth := UndefinedTagHandle{
|
||||
IfdPath: ifdPath,
|
||||
TagId: 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.ErrUnhandledUnknownTypedTag
|
||||
}
|
||||
|
||||
if valueContext.IfdPath() != ifdPath || valueContext.TagId() != tagId {
|
||||
log.Panicf("IFD-path for codec does not match value-context: [%s] (0x%04x) != [%s] (0x%04x)", ifdPath, tagId, valueContext.IfdPath(), valueContext.TagId())
|
||||
}
|
||||
|
||||
value, err = decoder.Decode(valueContext)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package exifundefined
|
||||
|
||||
import (
|
||||
"github.com/dsoprea/go-logging"
|
||||
|
||||
"github.com/dsoprea/go-exif/v2/common"
|
||||
)
|
||||
|
||||
type Tag8828Oecf struct {
|
||||
Columns uint16
|
||||
Rows uint16
|
||||
ColumnNames []string
|
||||
Values []exifcommon.SignedRational
|
||||
}
|
||||
|
||||
type Codec8828Oecf struct {
|
||||
}
|
||||
|
||||
func (Codec8828Oecf) Decode(valueContext *exifcommon.ValueContext) (value interface{}, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
// TODO(dustin): Add test using known good data.
|
||||
|
||||
valueContext.SetUnknownValueType(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 := 4
|
||||
|
||||
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.IfdPathStandardExif,
|
||||
0x8828,
|
||||
Codec8828Oecf{})
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package exifundefined
|
||||
|
||||
import (
|
||||
"github.com/dsoprea/go-logging"
|
||||
|
||||
"github.com/dsoprea/go-exif/v2/common"
|
||||
)
|
||||
|
||||
type Codec9000ExifVersion struct {
|
||||
}
|
||||
|
||||
func (Codec9000ExifVersion) Decode(valueContext *exifcommon.ValueContext) (value interface{}, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
valueContext.SetUnknownValueType(exifcommon.TypeAsciiNoNul)
|
||||
|
||||
valueString, err := valueContext.ReadAsciiNoNul()
|
||||
log.PanicIf(err)
|
||||
|
||||
return TagUndefinedGeneralString(valueString), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
registerDecoder(
|
||||
exifcommon.IfdPathStandardExif,
|
||||
0x9000,
|
||||
Codec9000ExifVersion{})
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
package exifundefined
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
|
||||
"github.com/dsoprea/go-exif/v2/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: []byte{
|
||||
TagUndefinedType_9101_ComponentsConfiguration_Channel_R,
|
||||
TagUndefinedType_9101_ComponentsConfiguration_Channel_G,
|
||||
TagUndefinedType_9101_ComponentsConfiguration_Channel_B,
|
||||
0,
|
||||
},
|
||||
|
||||
TagUndefinedType_9101_ComponentsConfiguration_YCBCR: []byte{
|
||||
TagUndefinedType_9101_ComponentsConfiguration_Channel_Y,
|
||||
TagUndefinedType_9101_ComponentsConfiguration_Channel_Cb,
|
||||
TagUndefinedType_9101_ComponentsConfiguration_Channel_Cr,
|
||||
0,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type TagExif9101ComponentsConfiguration struct {
|
||||
ConfigurationId int
|
||||
ConfigurationBytes []byte
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
// TODO(dustin): Confirm this size against the specification.
|
||||
|
||||
return cc.ConfigurationBytes, uint32(len(cc.ConfigurationBytes)), nil
|
||||
}
|
||||
|
||||
func (CodecExif9101ComponentsConfiguration) Decode(valueContext *exifcommon.ValueContext) (value interface{}, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
valueContext.SetUnknownValueType(exifcommon.TypeByte)
|
||||
|
||||
valueBytes, err := valueContext.ReadBytes()
|
||||
log.PanicIf(err)
|
||||
|
||||
for configurationId, configurationBytes := range TagUndefinedType_9101_ComponentsConfiguration_Configurations {
|
||||
if bytes.Compare(valueBytes, configurationBytes) == 0 {
|
||||
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.IfdPathStandardExif,
|
||||
0x9101,
|
||||
CodecExif9101ComponentsConfiguration{})
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package exifundefined
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"crypto/sha1"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
|
||||
"github.com/dsoprea/go-exif/v2/common"
|
||||
)
|
||||
|
||||
type Tag927CMakerNote struct {
|
||||
MakerNoteType []byte
|
||||
MakerNoteBytes []byte
|
||||
}
|
||||
|
||||
func (mn Tag927CMakerNote) String() string {
|
||||
parts := make([]string, 20)
|
||||
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 interface{}, 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.SetUnknownValueType(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)
|
||||
// }
|
||||
|
||||
mn := Tag927CMakerNote{
|
||||
MakerNoteType: valueBytes[:20],
|
||||
|
||||
// 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.IfdPathStandardExif,
|
||||
0x927c,
|
||||
Codec927CMakerNote{})
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
package exifundefined
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
|
||||
"github.com/dsoprea/go-exif/v2/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: []byte{'A', 'S', 'C', 'I', 'I', 0, 0, 0},
|
||||
TagUndefinedType_9286_UserComment_Encoding_JIS: []byte{'J', 'I', 'S', 0, 0, 0, 0, 0},
|
||||
TagUndefinedType_9286_UserComment_Encoding_UNICODE: []byte{'U', 'n', 'i', 'c', 'o', 'd', 'e', 0},
|
||||
TagUndefinedType_9286_UserComment_Encoding_UNDEFINED: []byte{0, 0, 0, 0, 0, 0, 0, 0},
|
||||
}
|
||||
)
|
||||
|
||||
type Tag9286UserComment struct {
|
||||
EncodingType int
|
||||
EncodingBytes []byte
|
||||
}
|
||||
|
||||
func (uc Tag9286UserComment) String() string {
|
||||
var valuePhrase string
|
||||
|
||||
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 interface{}, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
valueContext.SetUnknownValueType(exifcommon.TypeByte)
|
||||
|
||||
valueBytes, err := valueContext.ReadBytes()
|
||||
log.PanicIf(err)
|
||||
|
||||
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.IfdPathStandardExif,
|
||||
0x9286,
|
||||
Codec9286UserComment{})
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package exifundefined
|
||||
|
||||
import (
|
||||
"github.com/dsoprea/go-logging"
|
||||
|
||||
"github.com/dsoprea/go-exif/v2/common"
|
||||
)
|
||||
|
||||
type CodecA000FlashpixVersion struct {
|
||||
}
|
||||
|
||||
func (CodecA000FlashpixVersion) Decode(valueContext *exifcommon.ValueContext) (value interface{}, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
valueContext.SetUnknownValueType(exifcommon.TypeAsciiNoNul)
|
||||
|
||||
valueString, err := valueContext.ReadAsciiNoNul()
|
||||
log.PanicIf(err)
|
||||
|
||||
return TagUndefinedGeneralString(valueString), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
registerDecoder(
|
||||
exifcommon.IfdPathStandardExif,
|
||||
0xa000,
|
||||
CodecA000FlashpixVersion{})
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
package exifundefined
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
|
||||
"github.com/dsoprea/go-exif/v2/common"
|
||||
)
|
||||
|
||||
type TagA20CSpatialFrequencyResponse struct {
|
||||
Columns uint16
|
||||
Rows uint16
|
||||
ColumnNames []string
|
||||
Values []exifcommon.Rational
|
||||
}
|
||||
|
||||
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 interface{}, 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.SetUnknownValueType(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(
|
||||
TagA302CfaPattern{},
|
||||
CodecA302CfaPattern{})
|
||||
|
||||
registerDecoder(
|
||||
exifcommon.IfdPathStandardExif,
|
||||
0xa302,
|
||||
CodecA302CfaPattern{})
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package exifundefined
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
|
||||
"github.com/dsoprea/go-exif/v2/common"
|
||||
)
|
||||
|
||||
type TagExifA300FileSource uint32
|
||||
|
||||
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, uint32(int(ed.UnitCount)), nil
|
||||
}
|
||||
|
||||
func (CodecExifA300FileSource) Decode(valueContext *exifcommon.ValueContext) (value interface{}, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
valueContext.SetUnknownValueType(exifcommon.TypeLong)
|
||||
|
||||
valueLongs, err := valueContext.ReadLongs()
|
||||
log.PanicIf(err)
|
||||
|
||||
return TagExifA300FileSource(valueLongs[0]), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
registerEncoder(
|
||||
TagExifA300FileSource(0),
|
||||
CodecExifA300FileSource{})
|
||||
|
||||
registerDecoder(
|
||||
exifcommon.IfdPathStandardExif,
|
||||
0xa300,
|
||||
CodecExifA300FileSource{})
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package exifundefined
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
|
||||
"github.com/dsoprea/go-exif/v2/common"
|
||||
)
|
||||
|
||||
type TagExifA301SceneType uint32
|
||||
|
||||
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 interface{}, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
valueContext.SetUnknownValueType(exifcommon.TypeLong)
|
||||
|
||||
valueLongs, err := valueContext.ReadLongs()
|
||||
log.PanicIf(err)
|
||||
|
||||
return TagExifA301SceneType(valueLongs[0]), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
registerEncoder(
|
||||
TagExifA301SceneType(0),
|
||||
CodecExifA301SceneType{})
|
||||
|
||||
registerDecoder(
|
||||
exifcommon.IfdPathStandardExif,
|
||||
0xa301,
|
||||
CodecExifA301SceneType{})
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package exifundefined
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
|
||||
"github.com/dsoprea/go-exif/v2/common"
|
||||
)
|
||||
|
||||
type TagA302CfaPattern struct {
|
||||
HorizontalRepeat uint16
|
||||
VerticalRepeat uint16
|
||||
CfaValue []byte
|
||||
}
|
||||
|
||||
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 interface{}, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
// TODO(dustin): Add test using known good data.
|
||||
|
||||
valueContext.SetUnknownValueType(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.IfdPathStandardExif,
|
||||
0xa302,
|
||||
CodecA302CfaPattern{})
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package exifundefined
|
||||
|
||||
import (
|
||||
"github.com/dsoprea/go-logging"
|
||||
|
||||
"github.com/dsoprea/go-exif/v2/common"
|
||||
)
|
||||
|
||||
type Codec0002InteropVersion struct {
|
||||
}
|
||||
|
||||
func (Codec0002InteropVersion) Decode(valueContext *exifcommon.ValueContext) (value interface{}, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
valueContext.SetUnknownValueType(exifcommon.TypeAsciiNoNul)
|
||||
|
||||
valueString, err := valueContext.ReadAsciiNoNul()
|
||||
log.PanicIf(err)
|
||||
|
||||
return TagUndefinedGeneralString(valueString), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
registerDecoder(
|
||||
exifcommon.IfdPathStandardExifIop,
|
||||
0x0002,
|
||||
Codec0002InteropVersion{})
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package exifundefined
|
||||
|
||||
import (
|
||||
"github.com/dsoprea/go-logging"
|
||||
|
||||
"github.com/dsoprea/go-exif/v2/common"
|
||||
)
|
||||
|
||||
type Codec001BGPSProcessingMethod struct {
|
||||
}
|
||||
|
||||
func (Codec001BGPSProcessingMethod) Decode(valueContext *exifcommon.ValueContext) (value interface{}, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
valueContext.SetUnknownValueType(exifcommon.TypeAsciiNoNul)
|
||||
|
||||
valueString, err := valueContext.ReadAsciiNoNul()
|
||||
log.PanicIf(err)
|
||||
|
||||
return TagUndefinedGeneralString(valueString), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
registerDecoder(
|
||||
exifcommon.IfdPathStandardGps,
|
||||
0x001b,
|
||||
Codec001BGPSProcessingMethod{})
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package exifundefined
|
||||
|
||||
import (
|
||||
"github.com/dsoprea/go-logging"
|
||||
|
||||
"github.com/dsoprea/go-exif/v2/common"
|
||||
)
|
||||
|
||||
type Codec001CGPSAreaInformation struct {
|
||||
}
|
||||
|
||||
func (Codec001CGPSAreaInformation) Decode(valueContext *exifcommon.ValueContext) (value interface{}, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
valueContext.SetUnknownValueType(exifcommon.TypeAsciiNoNul)
|
||||
|
||||
valueString, err := valueContext.ReadAsciiNoNul()
|
||||
log.PanicIf(err)
|
||||
|
||||
return TagUndefinedGeneralString(valueString), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
registerDecoder(
|
||||
exifcommon.IfdPathStandardGps,
|
||||
0x001c,
|
||||
Codec001CGPSAreaInformation{})
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package exifundefined
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
)
|
||||
|
||||
type UndefinedTagHandle struct {
|
||||
IfdPath string
|
||||
TagId uint16
|
||||
}
|
||||
|
||||
func registerEncoder(entity interface{}, encoder UndefinedValueEncoder) {
|
||||
typeName := reflect.TypeOf(entity).Name()
|
||||
|
||||
_, 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)
|
||||
)
|
|
@ -0,0 +1,58 @@
|
|||
package exifundefined
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"crypto/sha1"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
|
||||
"github.com/dsoprea/go-exif/v2/common"
|
||||
)
|
||||
|
||||
const (
|
||||
UnparseableUnknownTagValuePlaceholder = "!UNKNOWN"
|
||||
)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// UndefinedValueEncoder knows how to decode an undefined-type tag's value from
|
||||
// bytes.
|
||||
type UndefinedValueDecoder interface {
|
||||
Decode(valueContext *exifcommon.ValueContext) (value interface{}, err error)
|
||||
}
|
||||
|
||||
// TODO(dustin): Rename `UnknownTagValue` to `UndefinedTagValue`.
|
||||
// OBSOLETE(dustin): Use a `UndefinedValueEncoder` instead of an `UnknownTagValue`.
|
||||
|
||||
type UnknownTagValue interface {
|
||||
ValueBytes() ([]byte, error)
|
||||
}
|
||||
|
||||
// TODO(dustin): Rename `TagUnknownType_UnknownValue` to `TagUndefinedType_UnknownValue`.
|
||||
|
||||
type TagUnknownType_UnknownValue []byte
|
||||
|
||||
func (tutuv TagUnknownType_UnknownValue) String() string {
|
||||
parts := make([]string, len(tutuv))
|
||||
for i, c := range tutuv {
|
||||
parts[i] = fmt.Sprintf("%02x", c)
|
||||
}
|
||||
|
||||
h := sha1.New()
|
||||
|
||||
_, err := h.Write(tutuv)
|
||||
log.PanicIf(err)
|
||||
|
||||
digest := h.Sum(nil)
|
||||
|
||||
return fmt.Sprintf("Unknown<DATA=[%s] LEN=(%d) SHA1=[%020x]>", strings.Join(parts, " "), len(tutuv), digest)
|
||||
}
|
||||
|
||||
type TagUndefinedGeneralString string
|
|
@ -10,63 +10,6 @@ import (
|
|||
"github.com/dsoprea/go-logging"
|
||||
)
|
||||
|
||||
func DumpBytes(data []byte) {
|
||||
fmt.Printf("DUMP: ")
|
||||
for _, x := range data {
|
||||
fmt.Printf("%02x ", x)
|
||||
}
|
||||
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
func DumpBytesClause(data []byte) {
|
||||
fmt.Printf("DUMP: ")
|
||||
|
||||
fmt.Printf("[]byte { ")
|
||||
|
||||
for i, x := range data {
|
||||
fmt.Printf("0x%02x", x)
|
||||
|
||||
if i < len(data)-1 {
|
||||
fmt.Printf(", ")
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf(" }\n")
|
||||
}
|
||||
|
||||
func DumpBytesToString(data []byte) string {
|
||||
b := new(bytes.Buffer)
|
||||
|
||||
for i, x := range data {
|
||||
_, err := b.WriteString(fmt.Sprintf("%02x", x))
|
||||
log.PanicIf(err)
|
||||
|
||||
if i < len(data)-1 {
|
||||
_, err := b.WriteRune(' ')
|
||||
log.PanicIf(err)
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func DumpBytesClauseToString(data []byte) string {
|
||||
b := new(bytes.Buffer)
|
||||
|
||||
for i, x := range data {
|
||||
_, err := b.WriteString(fmt.Sprintf("0x%02x", x))
|
||||
log.PanicIf(err)
|
||||
|
||||
if i < len(data)-1 {
|
||||
_, err := b.WriteString(", ")
|
||||
log.PanicIf(err)
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// ParseExifFullTimestamp parses dates like "2018:11:30 13:01:49" into a UTC
|
||||
// `time.Time` struct.
|
||||
func ParseExifFullTimestamp(fullTimestampPhrase string) (timestamp time.Time, err error) {
|
||||
|
|
|
@ -2,48 +2,8 @@ package exif
|
|||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
)
|
||||
|
||||
var (
|
||||
parser *Parser
|
||||
)
|
||||
|
||||
// ValueContext describes all of the parameters required to find and extract
|
||||
// the actual tag value.
|
||||
type ValueContext struct {
|
||||
unitCount uint32
|
||||
valueOffset uint32
|
||||
rawValueOffset []byte
|
||||
addressableData []byte
|
||||
|
||||
tagType TagTypePrimitive
|
||||
byteOrder binary.ByteOrder
|
||||
|
||||
// undefinedValueTagType is the effective type to use if this is an
|
||||
// "undefined" value.
|
||||
undefinedValueTagType TagTypePrimitive
|
||||
|
||||
ifdPath string
|
||||
tagId uint16
|
||||
}
|
||||
|
||||
func newValueContext(ifdPath string, tagId uint16, unitCount, valueOffset uint32, rawValueOffset, addressableData []byte, tagType TagTypePrimitive, byteOrder binary.ByteOrder) *ValueContext {
|
||||
return &ValueContext{
|
||||
unitCount: unitCount,
|
||||
valueOffset: valueOffset,
|
||||
rawValueOffset: rawValueOffset,
|
||||
addressableData: addressableData,
|
||||
|
||||
tagType: tagType,
|
||||
byteOrder: byteOrder,
|
||||
|
||||
ifdPath: ifdPath,
|
||||
tagId: tagId,
|
||||
}
|
||||
}
|
||||
|
||||
func newValueContextFromTag(ite *IfdTagEntry, addressableData []byte, byteOrder binary.ByteOrder) *ValueContext {
|
||||
return newValueContext(
|
||||
ite.IfdPath,
|
||||
|
@ -55,307 +15,3 @@ func newValueContextFromTag(ite *IfdTagEntry, addressableData []byte, byteOrder
|
|||
ite.TagType,
|
||||
byteOrder)
|
||||
}
|
||||
|
||||
func (vc *ValueContext) SetUnknownValueType(tagType TagTypePrimitive) {
|
||||
vc.undefinedValueTagType = tagType
|
||||
}
|
||||
|
||||
func (vc *ValueContext) UnitCount() uint32 {
|
||||
return vc.unitCount
|
||||
}
|
||||
|
||||
func (vc *ValueContext) ValueOffset() uint32 {
|
||||
return vc.valueOffset
|
||||
}
|
||||
|
||||
func (vc *ValueContext) RawValueOffset() []byte {
|
||||
return vc.rawValueOffset
|
||||
}
|
||||
|
||||
func (vc *ValueContext) AddressableData() []byte {
|
||||
return vc.addressableData
|
||||
}
|
||||
|
||||
// isEmbedded returns whether the value is embedded or a reference. This can't
|
||||
// be precalculated since the size is not defined for all types (namely the
|
||||
// "undefined" types).
|
||||
func (vc *ValueContext) isEmbedded() bool {
|
||||
tagType := vc.effectiveValueType()
|
||||
|
||||
return (tagType.Size() * int(vc.unitCount)) <= 4
|
||||
}
|
||||
|
||||
func (vc *ValueContext) effectiveValueType() (tagType TagTypePrimitive) {
|
||||
if vc.tagType == TypeUndefined {
|
||||
tagType = vc.undefinedValueTagType
|
||||
|
||||
if tagType == 0 {
|
||||
log.Panicf("undefined-value type not set")
|
||||
}
|
||||
} else {
|
||||
tagType = vc.tagType
|
||||
}
|
||||
|
||||
return tagType
|
||||
}
|
||||
|
||||
func (vc *ValueContext) readRawEncoded() (rawBytes []byte, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
tagType := vc.effectiveValueType()
|
||||
|
||||
unitSizeRaw := uint32(tagType.Size())
|
||||
|
||||
if vc.isEmbedded() == true {
|
||||
byteLength := unitSizeRaw * vc.unitCount
|
||||
return vc.rawValueOffset[:byteLength], nil
|
||||
} else {
|
||||
return vc.addressableData[vc.valueOffset : vc.valueOffset+vc.unitCount*unitSizeRaw], nil
|
||||
}
|
||||
}
|
||||
|
||||
// Format returns a string representation for the value.
|
||||
//
|
||||
// Where the type is not ASCII, `justFirst` indicates whether to just stringify
|
||||
// the first item in the slice (or return an empty string if the slice is
|
||||
// empty).
|
||||
//
|
||||
// Since this method lacks the information to process undefined-type tags (e.g.
|
||||
// byte-order, tag-ID, IFD type), it will return an error if attempted. See
|
||||
// `Undefined()`.
|
||||
func (vc *ValueContext) Format() (value string, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawBytes, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
phrase, err := Format(rawBytes, vc.tagType, false, vc.byteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
return phrase, nil
|
||||
}
|
||||
|
||||
// FormatOne is similar to `Format` but only gets and stringifies the first
|
||||
// item.
|
||||
func (vc *ValueContext) FormatFirst() (value string, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawBytes, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
phrase, err := Format(rawBytes, vc.tagType, true, vc.byteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
return phrase, nil
|
||||
}
|
||||
|
||||
func (vc *ValueContext) ReadBytes() (value []byte, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawValue, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
value, err = parser.ParseBytes(rawValue, vc.unitCount)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (vc *ValueContext) ReadAscii() (value string, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawValue, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
value, err = parser.ParseAscii(rawValue, vc.unitCount)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (vc *ValueContext) ReadAsciiNoNul() (value string, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawValue, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
value, err = parser.ParseAsciiNoNul(rawValue, vc.unitCount)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (vc *ValueContext) ReadShorts() (value []uint16, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawValue, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
value, err = parser.ParseShorts(rawValue, vc.unitCount, vc.byteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (vc *ValueContext) ReadLongs() (value []uint32, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawValue, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
value, err = parser.ParseLongs(rawValue, vc.unitCount, vc.byteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (vc *ValueContext) ReadRationals() (value []Rational, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawValue, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
value, err = parser.ParseRationals(rawValue, vc.unitCount, vc.byteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (vc *ValueContext) ReadSignedLongs() (value []int32, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawValue, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
value, err = parser.ParseSignedLongs(rawValue, vc.unitCount, vc.byteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (vc *ValueContext) ReadSignedRationals() (value []SignedRational, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawValue, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
value, err = parser.ParseSignedRationals(rawValue, vc.unitCount, vc.byteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// Values knows how to resolve the given value. This value is always a list
|
||||
// (undefined-values aside), so we're named accordingly.
|
||||
//
|
||||
// Since this method lacks the information to process unknown-type tags (e.g.
|
||||
// byte-order, tag-ID, IFD type), it will return an error if attempted. See
|
||||
// `Undefined()`.
|
||||
func (vc *ValueContext) Values() (values interface{}, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
if vc.tagType == TypeByte {
|
||||
values, err = vc.ReadBytes()
|
||||
log.PanicIf(err)
|
||||
} else if vc.tagType == TypeAscii {
|
||||
values, err = vc.ReadAscii()
|
||||
log.PanicIf(err)
|
||||
} else if vc.tagType == TypeAsciiNoNul {
|
||||
values, err = vc.ReadAsciiNoNul()
|
||||
log.PanicIf(err)
|
||||
} else if vc.tagType == TypeShort {
|
||||
values, err = vc.ReadShorts()
|
||||
log.PanicIf(err)
|
||||
} else if vc.tagType == TypeLong {
|
||||
values, err = vc.ReadLongs()
|
||||
log.PanicIf(err)
|
||||
} else if vc.tagType == TypeRational {
|
||||
values, err = vc.ReadRationals()
|
||||
log.PanicIf(err)
|
||||
} else if vc.tagType == TypeSignedLong {
|
||||
values, err = vc.ReadSignedLongs()
|
||||
log.PanicIf(err)
|
||||
} else if vc.tagType == TypeSignedRational {
|
||||
values, err = vc.ReadSignedRationals()
|
||||
log.PanicIf(err)
|
||||
} else if vc.tagType == TypeUndefined {
|
||||
log.Panicf("will not parse undefined-type value")
|
||||
|
||||
// Never called.
|
||||
return nil, nil
|
||||
} else {
|
||||
log.Panicf("value of type [%s] is unparseable", vc.tagType)
|
||||
|
||||
// Never called.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// Undefined attempts to identify and decode supported undefined-type fields.
|
||||
// This is the primary, preferred interface to reading undefined values.
|
||||
func (vc *ValueContext) Undefined() (value interface{}, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
value, err = UndefinedValue(vc.ifdPath, vc.tagId, vc, vc.byteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
parser = &Parser{}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue