diff --git a/ifd_tag_entry.go b/ifd_tag_entry.go index 0970f44..4dd364b 100644 --- a/ifd_tag_entry.go +++ b/ifd_tag_entry.go @@ -32,6 +32,37 @@ func (ite IfdTagEntry) String() string { return fmt.Sprintf("IfdTagEntry", ite.ChildIfdName, ite.TagId, TypeNames[ite.TagType], ite.UnitCount) } +// ValueString renders a string from whatever the value in this tag is. +func (ite IfdTagEntry) ValueString(byteOrder binary.ByteOrder, addressableData []byte) (value string, err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() + + vc := ValueContext{ + UnitCount: ite.UnitCount, + ValueOffset: ite.ValueOffset, + RawValueOffset: ite.RawValueOffset, + AddressableData: addressableData, + } + + if ite.TagType == TypeUndefined { + valueRaw, err = UndefinedValue(ite.Ii, ite.TagId, vc, byteOrder) + log.PanicIf(err) + + value = fmt.Sprintf("%v", valueRaw) + } else { + tt := NewTagType(ite.TagType, byteOrder) + + value, err = tt.ValueString(vc, false) + log.PanicIf(err) + } + + return value, nil +} + +// ValueBytes renders a specific list of bytes from the value in this tag. func (ite IfdTagEntry) ValueBytes(addressableData []byte, byteOrder binary.ByteOrder) (value []byte, err error) { defer func() { if state := recover(); state != nil { @@ -39,6 +70,11 @@ func (ite IfdTagEntry) ValueBytes(addressableData []byte, byteOrder binary.ByteO } }() + // 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, @@ -88,6 +124,34 @@ func (ite IfdTagEntry) ValueBytes(addressableData []byte, byteOrder binary.ByteO return value, nil } +// Value returns the specific, parsed, typed value from the tag. +func (ite IfdTagEntry) Value(byteOrder binary.ByteOrder, addressableData []byte) (value interface{}, err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() + + vc := ValueContext{ + UnitCount: ite.UnitCount, + ValueOffset: ite.ValueOffset, + RawValueOffset: ite.RawValueOffset, + AddressableData: addressableData, + } + + if ite.TagType == TypeUndefined { + value, err = UndefinedValue(ite.Ii, ite.TagId, vc, byteOrder) + log.PanicIf(err) + } else { + tt := NewTagType(ite.TagType, byteOrder) + + value, err = tt.Resolve(vc) + log.PanicIf(err) + } + + return value, nil +} + // IfdTagEntryValueResolver instances know how to resolve the values for any // tag for a particular EXIF block. @@ -108,6 +172,7 @@ func NewIfdTagEntryValueResolver(exifData []byte, byteOrder binary.ByteOrder) (i } } +// ValueBytes will resolve embedded or allocated data from the tag and return the raw bytes. func (itevr *IfdTagEntryValueResolver) ValueBytes(ite *IfdTagEntry) (value []byte, err error) { defer func() { if state := recover(); state != nil { diff --git a/type.go b/type.go index a02c37b..87e3e3e 100644 --- a/type.go +++ b/type.go @@ -416,8 +416,6 @@ func (tt TagType) ReadAsciiValue(valueContext ValueContext) (value string, err e } else { typeLogger.Debugf(nil, "Reading ASCII value (no-nul; at offset).") - fmt.Printf("Reading (%d) bytes of allocated ASCII from (%d) bytes of allocated data at offset (%d).\n", valueContext.UnitCount, len(valueContext.AddressableData), valueContext.ValueOffset) - value, err = tt.ParseAscii(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) log.PanicIf(err) } @@ -576,8 +574,8 @@ func (tt TagType) ValueString(valueContext ValueContext, justFirst bool) (value } }() -// TODO(dustin): Implement Value(), below. - // valueRaw, err := tt.Value(valueContext) +// TODO(dustin): Implement Resolve(), below. + // valueRaw, err := tt.Resolve(valueContext) // log.PanicIf(err) typeId := tt.Type() @@ -676,59 +674,58 @@ func (tt TagType) ValueString(valueContext ValueContext, justFirst bool) (value } } -// // Value knows how to resolve the given value. -// // -// // Since this method lacks the information to process unknown-type tags (e.g. -// // byte-order, tag-ID, IFD type), it will return an error if attempted. See -// // `UndefinedValue()`. +// Value knows how to resolve the given value. +// +// Since this method lacks the information to process unknown-type tags (e.g. +// byte-order, tag-ID, IFD type), it will return an error if attempted. See +// `UndefinedValue()`. +func (tt TagType) Resolve(valueContext ValueContext) (value interface{}, err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() -// // TODO(dustin): !! Rename to Resolve() (to make clear that we're parsing not encoding) + typeId := tt.Type() -// func (tt TagType) Value(valueContext ValueContext) (value interface{}, err error) { -// defer func() { -// if state := recover(); state != nil { -// err = log.Wrap(state.(error)) -// } -// }() + if typeId == TypeByte { + value, err = tt.ReadByteValues(valueContext) + log.PanicIf(err) + } else if typeId == TypeAscii { + value, err = tt.ReadAsciiValue(valueContext) + log.PanicIf(err) + } else if typeId == TypeAsciiNoNul { + value, err = tt.ReadAsciiNoNulValue(valueContext) + log.PanicIf(err) + } else if typeId == TypeShort { + value, err = tt.ReadShortValues(valueContext) + log.PanicIf(err) + } else if typeId == TypeLong { + value, err = tt.ReadLongValues(valueContext) + log.PanicIf(err) + } else if typeId == TypeRational { + value, err = tt.ReadRationalValues(valueContext) + log.PanicIf(err) + } else if typeId == TypeSignedLong { + value, err = tt.ReadSignedLongValues(valueContext) + log.PanicIf(err) + } else if typeId == TypeSignedRational { + value, err = tt.ReadSignedRationalValues(valueContext) + log.PanicIf(err) + } else if typeId == TypeUndefined { + log.Panicf("will not parse unknown-type value: %v", tt) -// typeId := tt.Type() + // Never called. + return nil, nil + } else { + log.Panicf("value of type (%d) [%s] is unparseable", typeId, tt) -// if typeId == TypeByte { -// value, err = tt.ReadByteValues(valueContext) -// log.PanicIf(err) -// } else if typeId == TypeAscii { -// value, err = tt.ReadAsciiValue(valueContext) -// log.PanicIf(err) -// } else if typeId == TypeAsciiNoNul { -// value, err = tt.ReadAsciiNoNulValue(valueContext) -// log.PanicIf(err) -// } else if typeId == TypeShort { -// value, err = tt.ReadShortValues(valueContext) -// log.PanicIf(err) -// } else if typeId == TypeLong { -// value, err = tt.ReadLongValues(valueContext) -// log.PanicIf(err) -// } else if typeId == TypeRational { -// value, err = tt.ReadRationalValues(valueContext) -// log.PanicIf(err) -// } else if typeId == TypeSignedLong { -// value, err = tt.ReadSignedLongValues(valueContext) -// log.PanicIf(err) -// } else if typeId == TypeSignedRational { -// value, err = tt.ReadSignedRationalValues(valueContext) -// log.PanicIf(err) -// } else if typeId == TypeUndefined { -// log.Panicf("will not parse unknown-type value: %v", tt) + // Never called. + return nil, nil + } -// // Never called. -// return "", nil -// } else { -// log.Panicf("value of type (%d) [%s] is unparseable", typeId, tt) - -// // Never called. -// return "", nil -// } -// } + return value, nil +} // ValueBytes knows how to encode the given value to a byte slice.