mirror of https://github.com/dsoprea/go-exif.git
type_decode: Implemented immediate remaining 'unknown'-type tags.
- Except for MakerNote. We're just going to have to skip that when writing, for now.pull/3/head
parent
00a5a5ad5b
commit
a909226514
10
exif_test.go
10
exif_test.go
|
@ -100,7 +100,7 @@ func TestVisit(t *testing.T) {
|
|||
} else if err != nil {
|
||||
log.Panic(err)
|
||||
} else {
|
||||
valueString = value.(string)
|
||||
valueString = fmt.Sprintf("%v", value)
|
||||
}
|
||||
} else {
|
||||
valueString, err = tagType.ValueString(valueContext, true)
|
||||
|
@ -141,15 +141,15 @@ func TestVisit(t *testing.T) {
|
|||
"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!]",
|
||||
"IFD=[Exif] ID=(0x9101) NAME=[ComponentsConfiguration] COUNT=(4) TYPE=[UNDEFINED] VALUE=[ComponentsConfiguration<ID=[YCBCR] BYTES=[1 2 3 0]>]",
|
||||
"IFD=[Exif] ID=(0x9201) NAME=[ShutterSpeedValue] COUNT=(1) TYPE=[SRATIONAL] VALUE=[614400/65536]",
|
||||
"IFD=[Exif] ID=(0x9202) NAME=[ApertureValue] COUNT=(1) TYPE=[RATIONAL] VALUE=[262144/65536]",
|
||||
"IFD=[Exif] ID=(0x9204) NAME=[ExposureBiasValue] COUNT=(1) TYPE=[SRATIONAL] VALUE=[0/1]",
|
||||
"IFD=[Exif] ID=(0x9207) NAME=[MeteringMode] COUNT=(1) TYPE=[SHORT] VALUE=[5]",
|
||||
"IFD=[Exif] ID=(0x9209) NAME=[Flash] COUNT=(1) TYPE=[SHORT] VALUE=[16]",
|
||||
"IFD=[Exif] ID=(0x920a) NAME=[FocalLength] COUNT=(1) TYPE=[RATIONAL] VALUE=[16/1]",
|
||||
"IFD=[Exif] ID=(0x927c) NAME=[MakerNote] COUNT=(8152) TYPE=[UNDEFINED] VALUE=[!UNDEFINED!]",
|
||||
"IFD=[Exif] ID=(0x9286) NAME=[UserComment] COUNT=(264) TYPE=[UNDEFINED] VALUE=[!UNDEFINED!]",
|
||||
"IFD=[Exif] ID=(0x927c) NAME=[MakerNote] COUNT=(8152) TYPE=[UNDEFINED] VALUE=[MakerNote<TYPE-ID=[28 00 01 00 03 00 31 00 00 00 74 05 00 00 02 00 03 00 04 00]>]",
|
||||
"IFD=[Exif] ID=(0x9286) NAME=[UserComment] COUNT=(264) TYPE=[UNDEFINED] VALUE=[UserComment<ENCODING=[UNDEFINED] V=[]>]",
|
||||
"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]",
|
||||
|
@ -159,7 +159,7 @@ func TestVisit(t *testing.T) {
|
|||
"IFD=[Exif] ID=(0xa003) NAME=[PixelYDimension] COUNT=(1) TYPE=[SHORT] VALUE=[2560]",
|
||||
"IFD=[Exif] ID=(0xa005) NAME=[InteroperabilityTag] COUNT=(1) TYPE=[LONG] VALUE=[9326]",
|
||||
"IFD=[Iop] ID=(0x0001) NAME=[InteroperabilityIndex] COUNT=(4) TYPE=[ASCII] VALUE=[R98]",
|
||||
"IFD=[Iop] ID=(0x0002) NAME=[InteroperabilityVersion] COUNT=(4) TYPE=[UNDEFINED] VALUE=[!UNDEFINED!]",
|
||||
"IFD=[Iop] ID=(0x0002) NAME=[InteroperabilityVersion] COUNT=(4) TYPE=[UNDEFINED] VALUE=[0100]",
|
||||
"IFD=[Exif] ID=(0xa20e) NAME=[FocalPlaneXResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[3840000/1461]",
|
||||
"IFD=[Exif] ID=(0xa20f) NAME=[FocalPlaneYResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[2560000/972]",
|
||||
"IFD=[Exif] ID=(0xa210) NAME=[FocalPlaneResolutionUnit] COUNT=(1) TYPE=[SHORT] VALUE=[2]",
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
package exif
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
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,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type TagUnknownType_9298_UserComment struct {
|
||||
EncodingType int
|
||||
EncodingBytes []byte
|
||||
}
|
||||
|
||||
func (uc TagUnknownType_9298_UserComment) String() string {
|
||||
return fmt.Sprintf("UserComment<ENCODING=[%s] V=%v>", TagUnknownType_9298_UserComment_Encoding_Names[uc.EncodingType], uc.EncodingBytes)
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("MakerNote<TYPE-ID=[%s]>", strings.Join(parts, " "))
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
}
|
|
@ -601,6 +601,8 @@ func UndefinedValue(indexedIfdName string, tagId uint16, valueContext ValueConte
|
|||
|
||||
if indexedIfdName == IfdName(IfdExif, 0) {
|
||||
if tagId == 0x9000 {
|
||||
// ExifVersion
|
||||
|
||||
tt := NewTagType(TypeAsciiNoNul, byteOrder)
|
||||
|
||||
value, err = tt.ReadAsciiValue(valueContext)
|
||||
|
@ -608,12 +610,93 @@ func UndefinedValue(indexedIfdName string, tagId uint16, valueContext ValueConte
|
|||
|
||||
return value, nil
|
||||
} else if tagId == 0xa000 {
|
||||
// FlashpixVersion
|
||||
|
||||
tt := NewTagType(TypeAsciiNoNul, byteOrder)
|
||||
|
||||
value, err = tt.ReadAsciiValue(valueContext)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
} else if tagId == 0x9286 {
|
||||
// UserComment
|
||||
|
||||
tt := NewTagType(TypeByte, byteOrder)
|
||||
|
||||
valueBytes, err := tt.ReadByteValues(valueContext)
|
||||
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 {
|
||||
// If unknown, return the default rather than what we have
|
||||
// because there will be a big list of NULs (which aren't
|
||||
// functional) and this won't equal the default instance
|
||||
// (above).
|
||||
if encodingIndex == TagUnknownType_9298_UserComment_Encoding_UNDEFINED {
|
||||
return unknownUc, nil
|
||||
} else {
|
||||
uc := TagUnknownType_9298_UserComment{
|
||||
EncodingType: encodingIndex,
|
||||
EncodingBytes: valueBytes[8:],
|
||||
}
|
||||
|
||||
return uc, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typeDecodeLogger.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).
|
||||
|
||||
tt := NewTagType(TypeByte, byteOrder)
|
||||
|
||||
valueBytes, err := tt.ReadByteValues(valueContext)
|
||||
log.PanicIf(err)
|
||||
|
||||
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
|
||||
|
||||
tt := NewTagType(TypeByte, byteOrder)
|
||||
|
||||
valueBytes, err := tt.ReadByteValues(valueContext)
|
||||
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 indexedIfdName == IfdName(IfdGps, 0) {
|
||||
if tagId == 0x001c {
|
||||
|
@ -633,6 +716,17 @@ func UndefinedValue(indexedIfdName string, tagId uint16, valueContext ValueConte
|
|||
value, err = tt.ReadAsciiValue(valueContext)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
} else if indexedIfdName == IfdName(IfdIop, 0) {
|
||||
if tagId == 0x0002 {
|
||||
// InteropVersion
|
||||
|
||||
tt := NewTagType(TypeAsciiNoNul, byteOrder)
|
||||
|
||||
value, err := tt.ReadAsciiNoNulValue(valueContext)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
|
@ -641,8 +735,6 @@ func UndefinedValue(indexedIfdName string, tagId uint16, valueContext ValueConte
|
|||
//
|
||||
// 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.
|
||||
|
||||
|
|
Loading…
Reference in New Issue