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
18
exif_test.go
18
exif_test.go
|
@ -92,10 +92,16 @@ func TestParse(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(dustin): Finish case-specific parsing of known undefined values.
|
||||
valueString := ""
|
||||
if tagType.Type() == TypeUndefined {
|
||||
valueString = "!UNDEFINED!"
|
||||
value, err := UndefinedValue(indexedIfdName, tagId, valueContext, tagType.ByteOrder())
|
||||
if log.Is(err, ErrUnhandledUnknownTypedTag) {
|
||||
valueString = "!UNDEFINED!"
|
||||
} else if err != nil {
|
||||
log.Panic(err)
|
||||
} else {
|
||||
valueString = value.(string)
|
||||
}
|
||||
} else {
|
||||
valueString, err = tagType.ValueString(valueContext, true)
|
||||
log.PanicIf(err)
|
||||
|
@ -110,6 +116,10 @@ func TestParse(t *testing.T) {
|
|||
err = e.Parse(data[foundAt:], visitor)
|
||||
log.PanicIf(err)
|
||||
|
||||
// for _, line := range tags {
|
||||
// fmt.Printf("TAGS: %s\n", line)
|
||||
// }
|
||||
|
||||
expected := []string {
|
||||
"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]",
|
||||
|
@ -128,7 +138,7 @@ func TestParse(t *testing.T) {
|
|||
"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=(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=(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!]",
|
||||
|
@ -143,7 +153,7 @@ func TestParse(t *testing.T) {
|
|||
"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=(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=(0xa002) NAME=[PixelXDimension] COUNT=(1) TYPE=[SHORT] VALUE=[3840]",
|
||||
"IFD=[Exif] ID=(0xa003) NAME=[PixelYDimension] COUNT=(1) TYPE=[SHORT] VALUE=[2560]",
|
||||
|
|
121
type.go
121
type.go
|
@ -19,6 +19,9 @@ const (
|
|||
TypeUndefined = uint16(7)
|
||||
TypeSignedLong = uint16(9)
|
||||
TypeSignedRational = uint16(10)
|
||||
|
||||
// Custom, for our purposes.
|
||||
TypeAsciiNoNul = uint16(0xf0)
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -31,6 +34,8 @@ var (
|
|||
TypeUndefined: "UNDEFINED",
|
||||
TypeSignedLong: "SLONG",
|
||||
TypeSignedRational: "SRATIONAL",
|
||||
|
||||
TypeAsciiNoNul: "_ASCII_NO_NUL",
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -47,8 +52,14 @@ var (
|
|||
// we're trying to parse (sizeof(type) * unit_count).
|
||||
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")
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func (tt TagType) ByteOrder() IfdByteOrder {
|
||||
return tt.byteOrder
|
||||
}
|
||||
|
||||
|
||||
func (tt TagType) Size() int {
|
||||
if tt.tagType == TypeByte {
|
||||
return 1
|
||||
} else if tt.tagType == TypeAscii {
|
||||
} else if tt.tagType == TypeAscii || tt.tagType == TypeAsciiNoNul {
|
||||
return 1
|
||||
} else if tt.tagType == TypeShort {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
typeLogger.Debugf(nil, "Reading ASCII value (embedded).")
|
||||
typeLogger.Debugf(nil, "Reading ASCII value (no-nul; embedded).")
|
||||
|
||||
value, err = tt.ParseAscii(valueContext.RawValueOffset, valueContext.UnitCount)
|
||||
log.PanicIf(err)
|
||||
} 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)
|
||||
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
|
||||
} else {
|
||||
return value[:len_ - 1], nil
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
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)
|
||||
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
|
||||
} else if tt.Type() == TypeShort {
|
||||
raw, err := tt.ReadShortValues(valueContext)
|
||||
|
@ -609,3 +638,63 @@ func (tt TagType) ValueString(valueContext ValueContext, justFirst bool) (value
|
|||
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