mirror of https://github.com/dsoprea/go-exif.git
type: Implemented workflow for undefined-type tags.
- Implemented for the straightforward tags. Noted what had to be done for the rest.pull/3/head
parent
172013e0f0
commit
42e678f6c2
16
exif_test.go
16
exif_test.go
|
@ -92,10 +92,16 @@ func TestParse(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(dustin): Finish case-specific parsing of known undefined values.
|
|
||||||
valueString := ""
|
valueString := ""
|
||||||
if tagType.Type() == TypeUndefined {
|
if tagType.Type() == TypeUndefined {
|
||||||
|
value, err := UndefinedValue(indexedIfdName, tagId, valueContext, tagType.ByteOrder())
|
||||||
|
if log.Is(err, ErrUnhandledUnknownTypedTag) {
|
||||||
valueString = "!UNDEFINED!"
|
valueString = "!UNDEFINED!"
|
||||||
|
} else if err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
} else {
|
||||||
|
valueString = value.(string)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
valueString, err = tagType.ValueString(valueContext, true)
|
valueString, err = tagType.ValueString(valueContext, true)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
@ -110,6 +116,10 @@ func TestParse(t *testing.T) {
|
||||||
err = e.Parse(data[foundAt:], visitor)
|
err = e.Parse(data[foundAt:], visitor)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
// for _, line := range tags {
|
||||||
|
// fmt.Printf("TAGS: %s\n", line)
|
||||||
|
// }
|
||||||
|
|
||||||
expected := []string {
|
expected := []string {
|
||||||
"IFD=[IFD] ID=(0x010f) NAME=[Make] COUNT=(6) TYPE=[ASCII] VALUE=[Canon]",
|
"IFD=[IFD] ID=(0x010f) NAME=[Make] COUNT=(6) TYPE=[ASCII] VALUE=[Canon]",
|
||||||
"IFD=[IFD] ID=(0x0110) NAME=[Model] COUNT=(22) TYPE=[ASCII] VALUE=[Canon EOS 5D Mark III]",
|
"IFD=[IFD] ID=(0x0110) NAME=[Model] COUNT=(22) TYPE=[ASCII] VALUE=[Canon EOS 5D Mark III]",
|
||||||
|
@ -128,7 +138,7 @@ func TestParse(t *testing.T) {
|
||||||
"IFD=[Exif] ID=(0x8827) NAME=[ISOSpeedRatings] COUNT=(1) TYPE=[SHORT] VALUE=[1600]",
|
"IFD=[Exif] ID=(0x8827) NAME=[ISOSpeedRatings] COUNT=(1) TYPE=[SHORT] VALUE=[1600]",
|
||||||
"IFD=[Exif] ID=(0x8830) NAME=[SensitivityType] COUNT=(1) TYPE=[SHORT] VALUE=[2]",
|
"IFD=[Exif] ID=(0x8830) NAME=[SensitivityType] COUNT=(1) TYPE=[SHORT] VALUE=[2]",
|
||||||
"IFD=[Exif] ID=(0x8832) NAME=[RecommendedExposureIndex] COUNT=(1) TYPE=[LONG] VALUE=[1600]",
|
"IFD=[Exif] ID=(0x8832) NAME=[RecommendedExposureIndex] COUNT=(1) TYPE=[LONG] VALUE=[1600]",
|
||||||
"IFD=[Exif] ID=(0x9000) NAME=[ExifVersion] COUNT=(4) TYPE=[UNDEFINED] VALUE=[!UNDEFINED!]",
|
"IFD=[Exif] ID=(0x9000) NAME=[ExifVersion] COUNT=(4) TYPE=[UNDEFINED] VALUE=[0230]",
|
||||||
"IFD=[Exif] ID=(0x9003) NAME=[DateTimeOriginal] COUNT=(20) TYPE=[ASCII] VALUE=[2017:12:02 08:18:50]",
|
"IFD=[Exif] ID=(0x9003) NAME=[DateTimeOriginal] COUNT=(20) TYPE=[ASCII] VALUE=[2017:12:02 08:18:50]",
|
||||||
"IFD=[Exif] ID=(0x9004) NAME=[DateTimeDigitized] COUNT=(20) TYPE=[ASCII] VALUE=[2017:12:02 08:18:50]",
|
"IFD=[Exif] ID=(0x9004) NAME=[DateTimeDigitized] COUNT=(20) TYPE=[ASCII] VALUE=[2017:12:02 08:18:50]",
|
||||||
"IFD=[Exif] ID=(0x9101) NAME=[ComponentsConfiguration] COUNT=(4) TYPE=[UNDEFINED] VALUE=[!UNDEFINED!]",
|
"IFD=[Exif] ID=(0x9101) NAME=[ComponentsConfiguration] COUNT=(4) TYPE=[UNDEFINED] VALUE=[!UNDEFINED!]",
|
||||||
|
@ -143,7 +153,7 @@ func TestParse(t *testing.T) {
|
||||||
"IFD=[Exif] ID=(0x9290) NAME=[SubSecTime] COUNT=(3) TYPE=[ASCII] VALUE=[00]",
|
"IFD=[Exif] ID=(0x9290) NAME=[SubSecTime] COUNT=(3) TYPE=[ASCII] VALUE=[00]",
|
||||||
"IFD=[Exif] ID=(0x9291) NAME=[SubSecTimeOriginal] COUNT=(3) TYPE=[ASCII] VALUE=[00]",
|
"IFD=[Exif] ID=(0x9291) NAME=[SubSecTimeOriginal] COUNT=(3) TYPE=[ASCII] VALUE=[00]",
|
||||||
"IFD=[Exif] ID=(0x9292) NAME=[SubSecTimeDigitized] COUNT=(3) TYPE=[ASCII] VALUE=[00]",
|
"IFD=[Exif] ID=(0x9292) NAME=[SubSecTimeDigitized] COUNT=(3) TYPE=[ASCII] VALUE=[00]",
|
||||||
"IFD=[Exif] ID=(0xa000) NAME=[FlashpixVersion] COUNT=(4) TYPE=[UNDEFINED] VALUE=[!UNDEFINED!]",
|
"IFD=[Exif] ID=(0xa000) NAME=[FlashpixVersion] COUNT=(4) TYPE=[UNDEFINED] VALUE=[0100]",
|
||||||
"IFD=[Exif] ID=(0xa001) NAME=[ColorSpace] COUNT=(1) TYPE=[SHORT] VALUE=[1]",
|
"IFD=[Exif] ID=(0xa001) NAME=[ColorSpace] COUNT=(1) TYPE=[SHORT] VALUE=[1]",
|
||||||
"IFD=[Exif] ID=(0xa002) NAME=[PixelXDimension] COUNT=(1) TYPE=[SHORT] VALUE=[3840]",
|
"IFD=[Exif] ID=(0xa002) NAME=[PixelXDimension] COUNT=(1) TYPE=[SHORT] VALUE=[3840]",
|
||||||
"IFD=[Exif] ID=(0xa003) NAME=[PixelYDimension] COUNT=(1) TYPE=[SHORT] VALUE=[2560]",
|
"IFD=[Exif] ID=(0xa003) NAME=[PixelYDimension] COUNT=(1) TYPE=[SHORT] VALUE=[2560]",
|
||||||
|
|
119
type.go
119
type.go
|
@ -19,6 +19,9 @@ const (
|
||||||
TypeUndefined = uint16(7)
|
TypeUndefined = uint16(7)
|
||||||
TypeSignedLong = uint16(9)
|
TypeSignedLong = uint16(9)
|
||||||
TypeSignedRational = uint16(10)
|
TypeSignedRational = uint16(10)
|
||||||
|
|
||||||
|
// Custom, for our purposes.
|
||||||
|
TypeAsciiNoNul = uint16(0xf0)
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -31,6 +34,8 @@ var (
|
||||||
TypeUndefined: "UNDEFINED",
|
TypeUndefined: "UNDEFINED",
|
||||||
TypeSignedLong: "SLONG",
|
TypeSignedLong: "SLONG",
|
||||||
TypeSignedRational: "SRATIONAL",
|
TypeSignedRational: "SRATIONAL",
|
||||||
|
|
||||||
|
TypeAsciiNoNul: "_ASCII_NO_NUL",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -47,8 +52,14 @@ var (
|
||||||
// we're trying to parse (sizeof(type) * unit_count).
|
// we're trying to parse (sizeof(type) * unit_count).
|
||||||
ErrNotEnoughData = errors.New("not enough data for type")
|
ErrNotEnoughData = errors.New("not enough data for type")
|
||||||
|
|
||||||
// ErrWrongType is used when we try to parse anything other than the current type.
|
// ErrWrongType is used when we try to parse anything other than the
|
||||||
|
// current type.
|
||||||
ErrWrongType = errors.New("wrong type, can not parse")
|
ErrWrongType = errors.New("wrong type, can not parse")
|
||||||
|
|
||||||
|
// ErrUnhandledUnknownTag is used when we try to parse a tag that's
|
||||||
|
// recorded as an "unknown" type but not a documented tag (therefore
|
||||||
|
// leaving us not knowning how to read it).
|
||||||
|
ErrUnhandledUnknownTypedTag = errors.New("not a standard unknown-typed tag")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -110,11 +121,15 @@ func (tt TagType) Type() uint16 {
|
||||||
return tt.tagType
|
return tt.tagType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tt TagType) ByteOrder() IfdByteOrder {
|
||||||
|
return tt.byteOrder
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func (tt TagType) Size() int {
|
func (tt TagType) Size() int {
|
||||||
if tt.tagType == TypeByte {
|
if tt.tagType == TypeByte {
|
||||||
return 1
|
return 1
|
||||||
} else if tt.tagType == TypeAscii {
|
} else if tt.tagType == TypeAscii || tt.tagType == TypeAsciiNoNul {
|
||||||
return 1
|
return 1
|
||||||
} else if tt.tagType == TypeShort {
|
} else if tt.tagType == TypeShort {
|
||||||
return 2
|
return 2
|
||||||
|
@ -172,7 +187,7 @@ func (tt TagType) ParseAscii(data []byte, rawCount uint32) (value string, err er
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if tt.tagType != TypeAscii {
|
if tt.tagType != TypeAscii && tt.tagType != TypeAsciiNoNul {
|
||||||
log.Panic(ErrWrongType)
|
log.Panic(ErrWrongType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,29 +390,38 @@ func (tt TagType) ReadAsciiValue(valueContext ValueContext) (value string, err e
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
value, err = tt.ReadAsciiNoNulValue(valueContext)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
len_ := len(value)
|
||||||
|
if len_ == 0 || value[len_ - 1] != 0 {
|
||||||
|
typeLogger.Warningf(nil, "ascii value not terminated with nul: [%s]", value)
|
||||||
|
return value, nil
|
||||||
|
} else {
|
||||||
|
return value[:len_ - 1], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tt TagType) ReadAsciiNoNulValue(valueContext ValueContext) (value string, err error) {
|
||||||
|
defer func() {
|
||||||
|
if state := recover(); state != nil {
|
||||||
|
err = log.Wrap(state.(error))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
if tt.ValueIsEmbedded(valueContext.UnitCount) == true {
|
if tt.ValueIsEmbedded(valueContext.UnitCount) == true {
|
||||||
typeLogger.Debugf(nil, "Reading ASCII value (embedded).")
|
typeLogger.Debugf(nil, "Reading ASCII value (no-nul; embedded).")
|
||||||
|
|
||||||
value, err = tt.ParseAscii(valueContext.RawValueOffset, valueContext.UnitCount)
|
value, err = tt.ParseAscii(valueContext.RawValueOffset, valueContext.UnitCount)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
} else {
|
} else {
|
||||||
typeLogger.Debugf(nil, "Reading ASCII value (at offset).")
|
typeLogger.Debugf(nil, "Reading ASCII value (no-nul; at offset).")
|
||||||
|
|
||||||
value, err = tt.ParseAscii(valueContext.RawExif[valueContext.ValueOffset:], valueContext.UnitCount)
|
value, err = tt.ParseAscii(valueContext.RawExif[valueContext.ValueOffset:], valueContext.UnitCount)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
len_ := len(value)
|
|
||||||
if value[len_ - 1] != 0 {
|
|
||||||
typeLogger.Warningf(nil, "ascii value not terminated with nul: [%s]", value)
|
|
||||||
|
|
||||||
// TODO(dustin): !! Debugging
|
|
||||||
fmt.Printf("ascii value not terminated with nul: [%s]", value)
|
|
||||||
|
|
||||||
return value, nil
|
return value, nil
|
||||||
} else {
|
|
||||||
return value[:len_ - 1], nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tt TagType) ReadShortValues(valueContext ValueContext) (value []uint16, err error) {
|
func (tt TagType) ReadShortValues(valueContext ValueContext) (value []uint16, err error) {
|
||||||
|
@ -536,6 +560,11 @@ func (tt TagType) ValueString(valueContext ValueContext, justFirst bool) (value
|
||||||
raw, err := tt.ReadAsciiValue(valueContext)
|
raw, err := tt.ReadAsciiValue(valueContext)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s", raw), nil
|
||||||
|
} else if tt.Type() == TypeAsciiNoNul {
|
||||||
|
raw, err := tt.ReadAsciiNoNulValue(valueContext)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
return fmt.Sprintf("%s", raw), nil
|
return fmt.Sprintf("%s", raw), nil
|
||||||
} else if tt.Type() == TypeShort {
|
} else if tt.Type() == TypeShort {
|
||||||
raw, err := tt.ReadShortValues(valueContext)
|
raw, err := tt.ReadShortValues(valueContext)
|
||||||
|
@ -609,3 +638,63 @@ func (tt TagType) ValueString(valueContext ValueContext, justFirst bool) (value
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UndefinedValue returns the value for a tag of "undefined" type.
|
||||||
|
func UndefinedValue(indexedIfdName string, tagId uint16, valueContext ValueContext, byteOrder IfdByteOrder) (value interface{}, err error) {
|
||||||
|
defer func() {
|
||||||
|
if state := recover(); state != nil {
|
||||||
|
err = log.Wrap(state.(error))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if indexedIfdName == IfdName(IfdExif, 0) {
|
||||||
|
if tagId == 0x9000 {
|
||||||
|
tt := NewTagType(TypeAsciiNoNul, byteOrder)
|
||||||
|
|
||||||
|
value, err = tt.ReadAsciiValue(valueContext)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
return value, nil
|
||||||
|
} else if tagId == 0xa000 {
|
||||||
|
tt := NewTagType(TypeAsciiNoNul, byteOrder)
|
||||||
|
|
||||||
|
value, err = tt.ReadAsciiValue(valueContext)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
} else if indexedIfdName == IfdName(IfdGps, 0) {
|
||||||
|
if tagId == 0x001c {
|
||||||
|
// GPSAreaInformation
|
||||||
|
|
||||||
|
tt := NewTagType(TypeAsciiNoNul, byteOrder)
|
||||||
|
|
||||||
|
value, err = tt.ReadAsciiValue(valueContext)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
return value, nil
|
||||||
|
} else if tagId == 0x001b {
|
||||||
|
// GPSProcessingMethod
|
||||||
|
|
||||||
|
tt := NewTagType(TypeAsciiNoNul, byteOrder)
|
||||||
|
|
||||||
|
value, err = tt.ReadAsciiValue(valueContext)
|
||||||
|
log.PanicIf(err)
|
||||||
|
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(dustin): !! Still need to do:
|
||||||
|
//
|
||||||
|
// complex: 0xa302, 0xa20c, 0x8828
|
||||||
|
// long: 0xa301, 0xa300
|
||||||
|
// bytes: 0x927c, 0x9101 (probably, but not certain)
|
||||||
|
// other: 0x9286 (simple, but needs some processing)
|
||||||
|
|
||||||
|
// 0xa40b is device-specific and unhandled.
|
||||||
|
|
||||||
|
|
||||||
|
log.Panic(ErrUnhandledUnknownTypedTag)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue