type: Implemented workflow for undefined-type tags.

- Implemented for the straightforward tags. Noted what had to be done
  for the rest.
pull/3/head
Dustin Oprea 2018-04-16 00:30:39 -04:00
parent 172013e0f0
commit 42e678f6c2
2 changed files with 119 additions and 20 deletions

View File

@ -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
View File

@ -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
}