ifd_enumerate: Now extract and store value bytes immediately.

pull/3/head
Dustin Oprea 2018-06-07 22:51:40 -04:00
parent 4cd9e35801
commit cda05ba702
3 changed files with 104 additions and 0 deletions

View File

@ -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).

View File

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

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