mirror of https://github.com/dsoprea/go-exif.git
ifd_enumerate: Now extract and store value bytes immediately.
parent
4cd9e35801
commit
cda05ba702
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
"errors"
|
||||
"reflect"
|
||||
|
||||
"encoding/binary"
|
||||
|
||||
|
@ -153,6 +154,12 @@ func (ie *IfdEnumerate) parseTag(ii IfdIdentity, tagIndex int, ite *IfdTagEnumer
|
|||
RawValueOffset: rawValueOffset,
|
||||
}
|
||||
|
||||
value, isUnhandledUnknown, err := ie.resolveTagValue(tag)
|
||||
log.PanicIf(err)
|
||||
|
||||
tag.value = value
|
||||
tag.isUnhandledUnknown = isUnhandledUnknown
|
||||
|
||||
// If it's an IFD but not a standard one, it'll just be seen as a LONG
|
||||
// (the standard IFD tag type), later, unless we skip it because it's
|
||||
// [likely] not even in the standard list of known tags.
|
||||
|
@ -164,6 +171,72 @@ func (ie *IfdEnumerate) parseTag(ii IfdIdentity, tagIndex int, ite *IfdTagEnumer
|
|||
return tag, nil
|
||||
}
|
||||
|
||||
func (ie *IfdEnumerate) resolveTagValue(ite *IfdTagEntry) (valueBytes []byte, isUnhandledUnknown bool, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
addressableData := ie.exifData[ExifAddressableAreaStart:]
|
||||
|
||||
// Return the exact bytes of the unknown-type value. Returning a string
|
||||
// (`ValueString`) is easy because we can just pass everything to
|
||||
// `Sprintf()`. Returning the raw, typed value (`Value`) is easy
|
||||
// (obviously). However, here, in order to produce the list of bytes, we
|
||||
// need to coerce whatever `UndefinedValue()` returns.
|
||||
if ite.TagType == TypeUndefined {
|
||||
valueContext := ValueContext{
|
||||
UnitCount: ite.UnitCount,
|
||||
ValueOffset: ite.ValueOffset,
|
||||
RawValueOffset: ite.RawValueOffset,
|
||||
AddressableData: addressableData,
|
||||
}
|
||||
|
||||
value, err := UndefinedValue(ite.Ii, ite.TagId, valueContext, ie.byteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
switch value.(type) {
|
||||
case []byte:
|
||||
return value.([]byte), false, nil
|
||||
case string:
|
||||
return []byte(value.(string)), false, nil
|
||||
case UnknownTagValue:
|
||||
valueBytes, err := value.(UnknownTagValue).ValueBytes()
|
||||
|
||||
// TODO(dustin): Is this always bytes? What about the tag-specific structures that are built? Handle unhandled unknown. Set isUnhandledUnknown.
|
||||
|
||||
log.PanicIf(err)
|
||||
|
||||
return valueBytes, false, nil
|
||||
default:
|
||||
// TODO(dustin): !! Finish translating the rest of the types (make reusable and replace into other similar implementations?)
|
||||
log.Panicf("can not produce bytes for unknown-type tag (0x%04x): [%s]", ite.TagId, reflect.TypeOf(value))
|
||||
}
|
||||
}
|
||||
|
||||
originalType := NewTagType(ite.TagType, ie.byteOrder)
|
||||
byteCount := uint32(originalType.Size()) * ite.UnitCount
|
||||
|
||||
tt := NewTagType(TypeByte, ie.byteOrder)
|
||||
|
||||
if tt.ValueIsEmbedded(byteCount) == true {
|
||||
iteLogger.Debugf(nil, "Reading BYTE value (ITE; embedded).")
|
||||
|
||||
// In this case, the bytes normally used for the offset are actually
|
||||
// data.
|
||||
valueBytes, err = tt.ParseBytes(ite.RawValueOffset, byteCount)
|
||||
log.PanicIf(err)
|
||||
} else {
|
||||
iteLogger.Debugf(nil, "Reading BYTE value (ITE; at offset).")
|
||||
|
||||
valueBytes, err = tt.ParseBytes(addressableData[ite.ValueOffset:], byteCount)
|
||||
log.PanicIf(err)
|
||||
}
|
||||
|
||||
return valueBytes, false, nil
|
||||
}
|
||||
|
||||
// TagVisitor is an optional callback that can get hit for every tag we parse
|
||||
// through. `addressableData` is the byte array startign after the EXIF header
|
||||
// (where the offsets of all IFDs and values are calculated from).
|
||||
|
|
|
@ -28,6 +28,10 @@ type IfdTagEntry struct {
|
|||
|
||||
// IfdName is the IFD that this tag belongs to.
|
||||
Ii IfdIdentity
|
||||
|
||||
// TODO(dustin): !! We now parse and read the value immediately. Update the rest of the logic to use this and get rid of all ofthe staggered and different resolution mechanisms.
|
||||
value []byte
|
||||
isUnhandledUnknown bool
|
||||
}
|
||||
|
||||
func (ite IfdTagEntry) String() string {
|
||||
|
|
27
tags.go
27
tags.go
|
@ -24,6 +24,11 @@ const (
|
|||
ThumbnailSizeTagId = 0x0202
|
||||
)
|
||||
|
||||
type IfdNameAndIndex struct {
|
||||
Ii IfdIdentity
|
||||
Index int
|
||||
}
|
||||
|
||||
var (
|
||||
tagDataFilepath = ""
|
||||
|
||||
|
@ -89,12 +94,30 @@ var (
|
|||
}
|
||||
|
||||
tagIndex *TagIndex
|
||||
|
||||
IfdDesignations = map[string]IfdNameAndIndex {
|
||||
"ifd0": { RootIi, 0 },
|
||||
"ifd1": { RootIi, 1 },
|
||||
"exif": { ExifIi, 0 },
|
||||
"gps": { GpsIi, 0 },
|
||||
"iop": { ExifIopIi, 0 },
|
||||
}
|
||||
|
||||
IfdDesignationsR = make(map[IfdNameAndIndex]string)
|
||||
)
|
||||
|
||||
var (
|
||||
tagsLogger = log.NewLogger("exif.tags")
|
||||
)
|
||||
|
||||
func IfdDesignation(ii IfdIdentity, index int) string {
|
||||
if ii == RootIi {
|
||||
return fmt.Sprintf("%s%d", ii.IfdName, index)
|
||||
} else {
|
||||
return ii.IfdName
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
type IfdIdentity struct {
|
||||
ParentIfdName string
|
||||
|
@ -397,5 +420,9 @@ func init() {
|
|||
IfdTagNames[ifdName] = tagsR
|
||||
}
|
||||
|
||||
for designation, ni := range IfdDesignations {
|
||||
IfdDesignationsR[ni] = designation
|
||||
}
|
||||
|
||||
tagIndex = NewTagIndex()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue