var ( // IfdStandardIfdIdentity represents the IFD path for IFD0. IfdStandardIfdIdentity = NewIfdIdentity(rootStandardIfd, IfdIdentityPart{"IFD", 0}) // IfdExifStandardIfdIdentity represents the IFD path for IFD0/Exif0. IfdExifStandardIfdIdentity = IfdStandardIfdIdentity.NewChild(exifStandardIfd, 0) // IfdExifIopStandardIfdIdentity represents the IFD path for IFD0/Exif0/Iop0. IfdExifIopStandardIfdIdentity = IfdExifStandardIfdIdentity.NewChild(iopStandardIfd, 0) // IfdGPSInfoStandardIfdIdentity represents the IFD path for IFD0/GPSInfo0. IfdGpsInfoStandardIfdIdentity = IfdStandardIfdIdentity.NewChild(gpsInfoStandardIfd, 0) // Ifd1StandardIfdIdentity represents the IFD path for IFD1. Ifd1StandardIfdIdentity = NewIfdIdentity(rootStandardIfd, IfdIdentityPart{"IFD", 1}) )
var ( // EncodeDefaultByteOrder is the default byte-order for encoding operations. EncodeDefaultByteOrder = binary.BigEndian // Default byte order for tests. TestDefaultByteOrder = binary.BigEndian )
var ( // ErrNotEnoughData is used when there isn't enough data to accommodate what // 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 = errors.New("wrong type, can not parse") // ErrUnhandledUndefinedTypedTag 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). ErrUnhandledUndefinedTypedTag = errors.New("not a standard unknown-typed tag") )
var ( ErrChildIfdNotMapped = errors.New("no child-IFD for that tag-ID under parent") )
var ( // ErrNotFarValue indicates that an offset-based lookup was attempted for a // non-offset-based (embedded) value. ErrNotFarValue = errors.New("not a far value") )
var ( ErrParseFail = errors.New("parse failure") )
var ( // TODO(dustin): Rename TypeNames() to typeNames() and add getter. TypeNames = map[TagTypePrimitive]string{ TypeByte: "BYTE", TypeAscii: "ASCII", TypeShort: "SHORT", TypeLong: "LONG", TypeRational: "RATIONAL", TypeUndefined: "UNDEFINED", TypeSignedLong: "SLONG", TypeSignedRational: "SRATIONAL", TypeFloat: "FLOAT", TypeDouble: "DOUBLE", TypeAsciiNoNul: "_ASCII_NO_NUL", } )
func DumpBytes(data []byte)
DumpBytes prints a list of hex-encoded bytes.
func DumpBytesClause(data []byte)
DumpBytesClause prints a list like DumpBytes(), but encapsulated in "[]byte { ... }".
func DumpBytesClauseToString(data []byte) string
DumpBytesClauseToString returns a comma-separated list of hex-encoded bytes.
func DumpBytesToString(data []byte) string
DumpBytesToString returns a stringified list of hex-encoded bytes.
func ExifFullTimestampString(t time.Time) (fullTimestampPhrase string)
ExifFullTimestampString produces a string like "2018:11:30 13:01:49" from a `time.Time` struct. It will attempt to convert to UTC first.
▹ Example
func FormatFromBytes(rawBytes []byte, tagType TagTypePrimitive, justFirst bool, byteOrder binary.ByteOrder) (phrase string, err error)
Format returns a stringified value for the given encoding. Automatically parses. Automatically calculates count based on type size.
func FormatFromType(value interface{}, justFirst bool) (phrase string, err error)
Format returns a stringified value for the given encoding. Automatically parses. Automatically calculates count based on type size. This function also supports undefined-type values (the ones that we support, anyway) by way of the String() method that they all require. We can't be more specific because we're a base package and we can't refer to it.
func GetModuleRootPath() string
func GetTestAssetsPath() string
func IsTime(v interface{}) bool
IsTime returns true if the value is a `time.Time`.
func LoadStandardIfds(im *IfdMapping) (err error)
LoadStandardIfds loads the standard IFDs into the mapping.
func ParseExifFullTimestamp(fullTimestampPhrase string) (timestamp time.Time, err error)
ParseExifFullTimestamp parses dates like "2018:11:30 13:01:49" into a UTC `time.Time` struct.
▹ Example
func TranslateStringToType(tagType TagTypePrimitive, valueString string) (value interface{}, err error)
TranslateStringToType converts user-provided strings to properly-typed values. If a string, returns a string. Else, assumes that it's a single number. If a list needs to be processed, it is the caller's responsibility to split it (according to whichever convention has been established).
BasicTag describes a single tag for any purpose.
type BasicTag struct { // FqIfdPath is the fully-qualified IFD-path. FqIfdPath string // IfdPath is the unindexed IFD-path. IfdPath string // TagId is the tag-ID. TagId uint16 }
EncodedData encapsulates the compound output of an encoding operation.
type EncodedData struct { Type TagTypePrimitive Encoded []byte // TODO(dustin): Is this really necessary? We might have this just to correlate to the incoming stream format (raw bytes and a unit-count both for incoming and outgoing). UnitCount uint32 }
IfdIdentity represents a single IFD path and provides access to various information and representations.
Only global instances can be used for equality checks.
type IfdIdentity struct {
// contains filtered or unexported fields
}
func NewIfdIdentity(ifdTag IfdTag, parts ...IfdIdentityPart) (ii *IfdIdentity)
NewIfdIdentity returns a new IfdIdentity struct.
func NewIfdIdentityFromString(im *IfdMapping, fqIfdPath string) (ii *IfdIdentity, err error)
NewIfdIdentityFromString parses a string like "IFD/Exif" or "IFD1" or something more exotic with custom IFDs ("SomeIFD4/SomeChildIFD6"). Note that this will valid the unindexed IFD structure (because the standard tags from the specification are unindexed), but not, obviously, any indices (e.g. the numbers in "IFD0", "IFD1", "SomeIFD4/SomeChildIFD6"). It is required for the caller to check whether these specific instances were actually parsed out of the stream.
func (ii *IfdIdentity) Equals(ii2 *IfdIdentity) bool
Equals returns true if the two IfdIdentity instances are effectively identical.
Since there's no way to get a specific fully-qualified IFD path without a certain slice of parts and all other fields are also derived from this, checking that the fully-qualified IFD path is equals is sufficient.
func (ii *IfdIdentity) IfdTag() IfdTag
IfdTag returns the tag struct behind this IFD.
func (ii *IfdIdentity) Index() int
Index returns the index of this IFD (more then one IFD under a parent IFD will be numbered [0..n]).
func (ii *IfdIdentity) LeafPathPart() IfdIdentityPart
LeafPathPart returns the last right-most path-part, which represents the current IFD.
func (ii *IfdIdentity) Name() string
Name returns the simple name of this IFD.
func (ii *IfdIdentity) NewChild(childIfdTag IfdTag, index int) (iiChild *IfdIdentity)
NewChild creates an IfdIdentity for an IFD that is a child of the current IFD.
func (ii *IfdIdentity) NewSibling(index int) (iiSibling *IfdIdentity)
NewSibling creates an IfdIdentity for an IFD that is a sibling to the current one.
func (ii *IfdIdentity) String() string
String returns a fully-qualified IFD path.
func (ii *IfdIdentity) TagId() uint16
TagId returns the tag-ID of the IFD.
func (ii *IfdIdentity) UnindexedString() string
UnindexedString returns a non-fully-qualified IFD path.
IfdIdentityPart represents one component in an IFD path.
type IfdIdentityPart struct { Name string Index int }
func (iip IfdIdentityPart) String() string
String returns a fully-qualified IFD path.
func (iip IfdIdentityPart) UnindexedString() string
UnindexedString returned a non-fully-qualified IFD path.
IfdMapping describes all of the IFDs that we currently recognize.
type IfdMapping struct {
// contains filtered or unexported fields
}
func NewIfdMapping() (ifdMapping *IfdMapping)
NewIfdMapping returns a new IfdMapping struct.
func NewIfdMappingWithStandard() (ifdMapping *IfdMapping, err error)
NewIfdMappingWithStandard retruns a new IfdMapping struct preloaded with the standard IFDs.
func (im *IfdMapping) Add(parentPlacement []uint16, tagId uint16, name string) (err error)
Add puts the given IFD at the given position of the tree. The position of the tree is referred to as the placement and is represented by a set of tag-IDs, where the leftmost is the root tag and the tags going to the right are progressive descendants.
func (im *IfdMapping) DumpLineages() (output []string, err error)
DumpLineages returns a slice of strings representing all mappings.
func (im *IfdMapping) FqPathPhraseFromLineage(lineage []IfdTagIdAndIndex) (fqPathPhrase string)
FqPathPhraseFromLineage returns the fully-qualified IFD path from the slice.
func (im *IfdMapping) Get(parentPlacement []uint16) (childIfd *MappedIfd, err error)
Get returns the node given the path slice.
func (im *IfdMapping) GetChild(parentPathPhrase string, tagId uint16) (mi *MappedIfd, err error)
GetChild is a convenience function to get the child path for a given parent placement and child tag-ID.
func (im *IfdMapping) GetWithPath(pathPhrase string) (mi *MappedIfd, err error)
GetWithPath returns the node given the path string.
func (im *IfdMapping) PathPhraseFromLineage(lineage []IfdTagIdAndIndex) (pathPhrase string)
PathPhraseFromLineage returns the non-fully-qualified IFD path from the slice.
func (im *IfdMapping) ResolvePath(pathPhrase string) (lineage []IfdTagIdAndIndex, err error)
ResolvePath takes a list of names, which can also be suffixed with indices (to identify the second, third, etc.. sibling IFD) and returns a list of tag-IDs and those indices.
Example:
- IFD/Exif/Iop - IFD0/Exif/Iop
This is the only call that supports adding the numeric indices.
func (im *IfdMapping) StripPathPhraseIndices(pathPhrase string) (strippedPathPhrase string, err error)
StripPathPhraseIndices returns a non-fully-qualified path-phrase (no indices).
IfdTag describes a single IFD tag and its parent (if any).
type IfdTag struct {
// contains filtered or unexported fields
}
func NewIfdTag(parentIfdTag *IfdTag, tagId uint16, name string) IfdTag
func (it IfdTag) Name() string
Name returns the simple name of this IFD.
func (it IfdTag) ParentIfd() *IfdTag
ParentIfd returns the IfdTag of this IFD's parent.
func (it IfdTag) String() string
String returns a descriptive string.
func (it IfdTag) TagId() uint16
TagId returns the tag-ID of this IFD.
IfdTagIdAndIndex represents a specific part of the IFD path.
This is a legacy type.
type IfdTagIdAndIndex struct { Name string TagId uint16 Index int }
func (itii IfdTagIdAndIndex) String() string
String returns a descriptive string.
MappedIfd is one node in the IFD-mapping.
type MappedIfd struct { ParentTagId uint16 Placement []uint16 Path []string Name string TagId uint16 Children map[uint16]*MappedIfd }
func (mi *MappedIfd) PathPhrase() string
PathPhrase returns a non-fully-qualified IFD path.
func (mi *MappedIfd) String() string
String returns a descriptive string.
Parser knows how to parse all well-defined, encoded EXIF types.
type Parser struct { }
func (p *Parser) ParseAscii(data []byte, unitCount uint32) (value string, err error)
ParseAscii returns a string and auto-strips the trailing NUL character that should be at the end of the encoding.
func (p *Parser) ParseAsciiNoNul(data []byte, unitCount uint32) (value string, err error)
ParseAsciiNoNul returns a string without any consideration for a trailing NUL character.
func (p *Parser) ParseBytes(data []byte, unitCount uint32) (value []uint8, err error)
ParseBytesknows how to parse a byte-type value.
func (p *Parser) ParseDoubles(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []float64, err error)
ParseDoubles knows how to encode an encoded list of doubles.
func (p *Parser) ParseFloats(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []float32, err error)
ParseFloats knows how to encode an encoded list of floats.
func (p *Parser) ParseLongs(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []uint32, err error)
ParseLongs knows how to encode an encoded list of unsigned longs.
func (p *Parser) ParseRationals(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []Rational, err error)
ParseRationals knows how to parse an encoded list of unsigned rationals.
func (p *Parser) ParseShorts(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []uint16, err error)
ParseShorts knows how to parse an encoded list of shorts.
func (p *Parser) ParseSignedLongs(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []int32, err error)
ParseSignedLongs knows how to parse an encoded list of signed longs.
func (p *Parser) ParseSignedRationals(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []SignedRational, err error)
ParseSignedRationals knows how to parse an encoded list of signed rationals.
Rational describes an unsigned rational value.
type Rational struct { // Numerator is the numerator of the rational value. Numerator uint32 // Denominator is the numerator of the rational value. Denominator uint32 }
SignedRational describes a signed rational value.
type SignedRational struct { // Numerator is the numerator of the rational value. Numerator int32 // Denominator is the numerator of the rational value. Denominator int32 }
TagTypePrimitive is a type-alias that let's us easily lookup type properties.
type TagTypePrimitive uint16
const ( // TypeByte describes an encoded list of bytes. TypeByte TagTypePrimitive = 1 // TypeAscii describes an encoded list of characters that is terminated // with a NUL in its encoded form. TypeAscii TagTypePrimitive = 2 // TypeShort describes an encoded list of shorts. TypeShort TagTypePrimitive = 3 // TypeLong describes an encoded list of longs. TypeLong TagTypePrimitive = 4 // TypeRational describes an encoded list of rationals. TypeRational TagTypePrimitive = 5 // TypeUndefined describes an encoded value that has a complex/non-clearcut // interpretation. TypeUndefined TagTypePrimitive = 7 // TypeSignedLong describes an encoded list of signed longs. TypeSignedLong TagTypePrimitive = 9 // TypeSignedRational describes an encoded list of signed rationals. TypeSignedRational TagTypePrimitive = 10 // TypeFloat describes an encoded list of floats TypeFloat TagTypePrimitive = 11 // TypeDouble describes an encoded list of doubles. TypeDouble TagTypePrimitive = 12 // TypeAsciiNoNul is just a pseudo-type, for our own purposes. TypeAsciiNoNul TagTypePrimitive = 0xf0 )
func GetTypeByName(typeName string) (tagType TagTypePrimitive, found bool)
GetTypeByName returns the `TagTypePrimitive` for the given type name. Returns (0) if not valid.
func (tagType TagTypePrimitive) IsValid() bool
IsValid returns true if tagType is a valid type.
func (tagType TagTypePrimitive) Size() int
Size returns the size of one atomic unit of the type.
func (typeType TagTypePrimitive) String() string
String returns the name of the type
ValueContext embeds all of the parameters required to find and extract the actual tag value.
type ValueContext struct {
// contains filtered or unexported fields
}
func NewValueContext(ifdPath string, tagId uint16, unitCount, valueOffset uint32, rawValueOffset []byte, rs io.ReadSeeker, tagType TagTypePrimitive, byteOrder binary.ByteOrder) *ValueContext
NewValueContext returns a new ValueContext struct.
func (vc *ValueContext) AddressableData() io.ReadSeeker
AddressableData returns the block of data that we can dereference into.
func (vc *ValueContext) ByteOrder() binary.ByteOrder
ByteOrder returns the byte-order of numbers.
func (vc *ValueContext) Format() (value string, err error)
Format returns a string representation for the value.
Where the type is not ASCII, `justFirst` indicates whether to just stringify the first item in the slice (or return an empty string if the slice is empty).
Since this method lacks the information to process undefined-type tags (e.g. byte-order, tag-ID, IFD type), it will return an error if attempted. See `Undefined()`.
func (vc *ValueContext) FormatFirst() (value string, err error)
FormatFirst is similar to `Format` but only gets and stringifies the first item.
func (vc *ValueContext) GetFarOffset() (offset uint32, err error)
GetFarOffset returns the offset if the value is not embedded [within the pointer itself] or an error if an embedded value.
func (vc *ValueContext) IfdPath() string
IfdPath returns the path of the IFD containing this tag.
func (vc *ValueContext) RawValueOffset() []byte
RawValueOffset returns the uninterpreted value-offset. This is used for embedded values (values small enough to fit within the offset bytes rather than needing to be stored elsewhere and referred to by an actual offset).
func (vc *ValueContext) ReadAscii() (value string, err error)
ReadAscii parses the encoded NUL-terminated ASCII string from the value- context.
func (vc *ValueContext) ReadAsciiNoNul() (value string, err error)
ReadAsciiNoNul parses the non-NUL-terminated encoded ASCII string from the value-context.
func (vc *ValueContext) ReadBytes() (value []byte, err error)
ReadBytes parses the encoded byte-array from the value-context.
func (vc *ValueContext) ReadDoubles() (value []float64, err error)
ReadDoubles parses the list of encoded, doubles from the value-context.
func (vc *ValueContext) ReadFloats() (value []float32, err error)
ReadFloats parses the list of encoded, floats from the value-context.
func (vc *ValueContext) ReadLongs() (value []uint32, err error)
ReadLongs parses the list of encoded, unsigned longs from the value-context.
func (vc *ValueContext) ReadRationals() (value []Rational, err error)
ReadRationals parses the list of encoded, unsigned rationals from the value- context.
func (vc *ValueContext) ReadRawEncoded() (rawBytes []byte, err error)
ReadRawEncoded returns the encoded bytes for the value that we represent.
func (vc *ValueContext) ReadShorts() (value []uint16, err error)
ReadShorts parses the list of encoded shorts from the value-context.
func (vc *ValueContext) ReadSignedLongs() (value []int32, err error)
ReadSignedLongs parses the list of encoded, signed longs from the value-context.
func (vc *ValueContext) ReadSignedRationals() (value []SignedRational, err error)
ReadSignedRationals parses the list of encoded, signed rationals from the value-context.
func (vc *ValueContext) SetUndefinedValueType(tagType TagTypePrimitive)
SetUndefinedValueType sets the effective type if this is an unknown-type tag.
func (vc *ValueContext) SizeInBytes() int
SizeInBytes returns the number of bytes that this value requires. The underlying call will panic if the type is UNDEFINED. It is the responsibility of the caller to preemptively check that.
func (vc *ValueContext) TagId() uint16
TagId returns the ID of the tag that we represent.
func (vc *ValueContext) UnitCount() uint32
UnitCount returns the embedded unit-count.
func (vc *ValueContext) ValueOffset() uint32
ValueOffset returns the value-offset decoded as a `uint32`.
func (vc *ValueContext) Values() (values interface{}, err error)
Values knows how to resolve the given value. This value is always a list (undefined-values aside), so we're named accordingly.
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 `Undefined()`.
ValueEncoder knows how to encode values of every type to bytes.
type ValueEncoder struct {
// contains filtered or unexported fields
}
func NewValueEncoder(byteOrder binary.ByteOrder) *ValueEncoder
NewValueEncoder returns a new ValueEncoder.
func (ve *ValueEncoder) Encode(value interface{}) (ed EncodedData, err error)
Encode returns bytes for the given value, infering type from the actual value. This does not support `TypeAsciiNoNull` (all strings are encoded as `TypeAscii`).