exifcommon

package
v3.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 12, 2022 License: MIT Imports: 15 Imported by: 65

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
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})
)
View Source
var (

	// EncodeDefaultByteOrder is the default byte-order for encoding operations.
	EncodeDefaultByteOrder = binary.BigEndian

	// Default byte order for tests.
	TestDefaultByteOrder = binary.BigEndian
)
View Source
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")
)
View Source
var (
	ErrChildIfdNotMapped = errors.New("no child-IFD for that tag-ID under parent")
)
View Source
var (
	// ErrNotFarValue indicates that an offset-based lookup was attempted for a
	// non-offset-based (embedded) value.
	ErrNotFarValue = errors.New("not a far value")
)
View Source
var (
	ErrParseFail = errors.New("parse failure")
)
View Source
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",
	}
)

Functions

func DumpBytes

func DumpBytes(data []byte)

DumpBytes prints a list of hex-encoded bytes.

func DumpBytesClause

func DumpBytesClause(data []byte)

DumpBytesClause prints a list like DumpBytes(), but encapsulated in "[]byte { ... }".

func DumpBytesClauseToString

func DumpBytesClauseToString(data []byte) string

DumpBytesClauseToString returns a comma-separated list of hex-encoded bytes.

func DumpBytesToString

func DumpBytesToString(data []byte) string

DumpBytesToString returns a stringified list of hex-encoded bytes.

func ExifFullTimestampString

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
originalPhrase := "2018:11:30 13:01:49"

timestamp, err := ParseExifFullTimestamp(originalPhrase)
log.PanicIf(err)

restoredPhrase := ExifFullTimestampString(timestamp)
fmt.Printf("To EXIF timestamp: [%s]\n", restoredPhrase)
Output:

To EXIF timestamp: [2018:11:30 13:01:49]

func FormatFromBytes

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

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

func GetModuleRootPath() string

func GetTestAssetsPath

func GetTestAssetsPath() string

func IsTime

func IsTime(v interface{}) bool

IsTime returns true if the value is a `time.Time`.

func LoadStandardIfds

func LoadStandardIfds(im *IfdMapping) (err error)

LoadStandardIfds loads the standard IFDs into the mapping.

func ParseExifFullTimestamp

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
originalPhrase := "2018:11:30 13:01:49"

timestamp, err := ParseExifFullTimestamp(originalPhrase)
log.PanicIf(err)

fmt.Printf("To Go timestamp: [%s]\n", timestamp.Format(time.RFC3339))
Output:

To Go timestamp: [2018-11-30T13:01:49Z]

func TranslateStringToType

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

Types

type BasicTag

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
}

BasicTag describes a single tag for any purpose.

type EncodedData

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
}

EncodedData encapsulates the compound output of an encoding operation.

type IfdIdentity

type IfdIdentity struct {
	// contains filtered or unexported fields
}

IfdIdentity represents a single IFD path and provides access to various information and representations.

Only global instances can be used for equality checks.

func NewIfdIdentity

func NewIfdIdentity(ifdTag IfdTag, parts ...IfdIdentityPart) (ii *IfdIdentity)

NewIfdIdentity returns a new IfdIdentity struct.

func NewIfdIdentityFromString

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 (*IfdIdentity) Equals

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 (*IfdIdentity) IfdTag

func (ii *IfdIdentity) IfdTag() IfdTag

IfdTag returns the tag struct behind this IFD.

func (*IfdIdentity) Index

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 (*IfdIdentity) LeafPathPart

func (ii *IfdIdentity) LeafPathPart() IfdIdentityPart

LeafPathPart returns the last right-most path-part, which represents the current IFD.

func (*IfdIdentity) Name

func (ii *IfdIdentity) Name() string

Name returns the simple name of this IFD.

func (*IfdIdentity) NewChild

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 (*IfdIdentity) NewSibling

func (ii *IfdIdentity) NewSibling(index int) (iiSibling *IfdIdentity)

NewSibling creates an IfdIdentity for an IFD that is a sibling to the current one.

func (*IfdIdentity) String

func (ii *IfdIdentity) String() string

String returns a fully-qualified IFD path.

func (*IfdIdentity) TagId

func (ii *IfdIdentity) TagId() uint16

TagId returns the tag-ID of the IFD.

func (*IfdIdentity) UnindexedString

func (ii *IfdIdentity) UnindexedString() string

UnindexedString returns a non-fully-qualified IFD path.

type IfdIdentityPart

type IfdIdentityPart struct {
	Name  string
	Index int
}

IfdIdentityPart represents one component in an IFD path.

func (IfdIdentityPart) String

func (iip IfdIdentityPart) String() string

String returns a fully-qualified IFD path.

func (IfdIdentityPart) UnindexedString

func (iip IfdIdentityPart) UnindexedString() string

UnindexedString returned a non-fully-qualified IFD path.

type IfdMapping

type IfdMapping struct {
	// contains filtered or unexported fields
}

IfdMapping describes all of the IFDs that we currently recognize.

func NewIfdMapping

func NewIfdMapping() (ifdMapping *IfdMapping)

NewIfdMapping returns a new IfdMapping struct.

func NewIfdMappingWithStandard

func NewIfdMappingWithStandard() (ifdMapping *IfdMapping, err error)

NewIfdMappingWithStandard retruns a new IfdMapping struct preloaded with the standard IFDs.

func (*IfdMapping) Add

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 (*IfdMapping) DumpLineages

func (im *IfdMapping) DumpLineages() (output []string, err error)

DumpLineages returns a slice of strings representing all mappings.

func (*IfdMapping) FqPathPhraseFromLineage

func (im *IfdMapping) FqPathPhraseFromLineage(lineage []IfdTagIdAndIndex) (fqPathPhrase string)

FqPathPhraseFromLineage returns the fully-qualified IFD path from the slice.

func (*IfdMapping) Get

func (im *IfdMapping) Get(parentPlacement []uint16) (childIfd *MappedIfd, err error)

Get returns the node given the path slice.

func (*IfdMapping) GetChild

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 (*IfdMapping) GetWithPath

func (im *IfdMapping) GetWithPath(pathPhrase string) (mi *MappedIfd, err error)

GetWithPath returns the node given the path string.

func (*IfdMapping) PathPhraseFromLineage

func (im *IfdMapping) PathPhraseFromLineage(lineage []IfdTagIdAndIndex) (pathPhrase string)

PathPhraseFromLineage returns the non-fully-qualified IFD path from the slice.

func (*IfdMapping) ResolvePath

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 (*IfdMapping) StripPathPhraseIndices

func (im *IfdMapping) StripPathPhraseIndices(pathPhrase string) (strippedPathPhrase string, err error)

StripPathPhraseIndices returns a non-fully-qualified path-phrase (no indices).

type IfdTag

type IfdTag struct {
	// contains filtered or unexported fields
}

IfdTag describes a single IFD tag and its parent (if any).

func NewIfdTag

func NewIfdTag(parentIfdTag *IfdTag, tagId uint16, name string) IfdTag

func (IfdTag) Name

func (it IfdTag) Name() string

Name returns the simple name of this IFD.

func (IfdTag) ParentIfd

func (it IfdTag) ParentIfd() *IfdTag

ParentIfd returns the IfdTag of this IFD's parent.

func (IfdTag) String

func (it IfdTag) String() string

String returns a descriptive string.

func (IfdTag) TagId

func (it IfdTag) TagId() uint16

TagId returns the tag-ID of this IFD.

type IfdTagIdAndIndex

type IfdTagIdAndIndex struct {
	Name  string
	TagId uint16
	Index int
}

IfdTagIdAndIndex represents a specific part of the IFD path.

This is a legacy type.

func (IfdTagIdAndIndex) String

func (itii IfdTagIdAndIndex) String() string

String returns a descriptive string.

type MappedIfd

type MappedIfd struct {
	ParentTagId uint16
	Placement   []uint16
	Path        []string

	Name     string
	TagId    uint16
	Children map[uint16]*MappedIfd
}

MappedIfd is one node in the IFD-mapping.

func (*MappedIfd) PathPhrase

func (mi *MappedIfd) PathPhrase() string

PathPhrase returns a non-fully-qualified IFD path.

func (*MappedIfd) String

func (mi *MappedIfd) String() string

String returns a descriptive string.

type Parser

type Parser struct {
}

Parser knows how to parse all well-defined, encoded EXIF types.

func (*Parser) ParseAscii

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 (*Parser) ParseAsciiNoNul

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 (*Parser) ParseBytes

func (p *Parser) ParseBytes(data []byte, unitCount uint32) (value []uint8, err error)

ParseBytesknows how to parse a byte-type value.

func (*Parser) ParseDoubles

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 (*Parser) ParseFloats

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 (*Parser) ParseLongs

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 (*Parser) ParseRationals

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 (*Parser) ParseShorts

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 (*Parser) ParseSignedLongs

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 (*Parser) ParseSignedRationals

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.

type Rational

type Rational struct {
	// Numerator is the numerator of the rational value.
	Numerator uint32

	// Denominator is the numerator of the rational value.
	Denominator uint32
}

Rational describes an unsigned rational value.

type SignedRational

type SignedRational struct {
	// Numerator is the numerator of the rational value.
	Numerator int32

	// Denominator is the numerator of the rational value.
	Denominator int32
}

SignedRational describes a signed rational value.

type TagTypePrimitive

type TagTypePrimitive uint16

TagTypePrimitive is a type-alias that let's us easily lookup type properties.

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

func GetTypeByName(typeName string) (tagType TagTypePrimitive, found bool)

GetTypeByName returns the `TagTypePrimitive` for the given type name. Returns (0) if not valid.

func (TagTypePrimitive) IsValid

func (tagType TagTypePrimitive) IsValid() bool

IsValid returns true if tagType is a valid type.

func (TagTypePrimitive) Size

func (tagType TagTypePrimitive) Size() int

Size returns the size of one atomic unit of the type.

func (TagTypePrimitive) String

func (typeType TagTypePrimitive) String() string

String returns the name of the type

type ValueContext

type ValueContext struct {
	// contains filtered or unexported fields
}

ValueContext embeds all of the parameters required to find and extract the actual tag value.

func NewValueContext

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 (*ValueContext) AddressableData

func (vc *ValueContext) AddressableData() io.ReadSeeker

AddressableData returns the block of data that we can dereference into.

func (*ValueContext) ByteOrder

func (vc *ValueContext) ByteOrder() binary.ByteOrder

ByteOrder returns the byte-order of numbers.

func (*ValueContext) Format

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 (*ValueContext) FormatFirst

func (vc *ValueContext) FormatFirst() (value string, err error)

FormatFirst is similar to `Format` but only gets and stringifies the first item.

func (*ValueContext) GetFarOffset

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 (*ValueContext) IfdPath

func (vc *ValueContext) IfdPath() string

IfdPath returns the path of the IFD containing this tag.

func (*ValueContext) RawValueOffset

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 (*ValueContext) ReadAscii

func (vc *ValueContext) ReadAscii() (value string, err error)

ReadAscii parses the encoded NUL-terminated ASCII string from the value- context.

func (*ValueContext) ReadAsciiNoNul

func (vc *ValueContext) ReadAsciiNoNul() (value string, err error)

ReadAsciiNoNul parses the non-NUL-terminated encoded ASCII string from the value-context.

func (*ValueContext) ReadBytes

func (vc *ValueContext) ReadBytes() (value []byte, err error)

ReadBytes parses the encoded byte-array from the value-context.

func (*ValueContext) ReadDoubles

func (vc *ValueContext) ReadDoubles() (value []float64, err error)

ReadDoubles parses the list of encoded, doubles from the value-context.

func (*ValueContext) ReadFloats

func (vc *ValueContext) ReadFloats() (value []float32, err error)

ReadFloats parses the list of encoded, floats from the value-context.

func (*ValueContext) ReadLongs

func (vc *ValueContext) ReadLongs() (value []uint32, err error)

ReadLongs parses the list of encoded, unsigned longs from the value-context.

func (*ValueContext) ReadRationals

func (vc *ValueContext) ReadRationals() (value []Rational, err error)

ReadRationals parses the list of encoded, unsigned rationals from the value- context.

func (*ValueContext) ReadRawEncoded

func (vc *ValueContext) ReadRawEncoded() (rawBytes []byte, err error)

ReadRawEncoded returns the encoded bytes for the value that we represent.

func (*ValueContext) ReadShorts

func (vc *ValueContext) ReadShorts() (value []uint16, err error)

ReadShorts parses the list of encoded shorts from the value-context.

func (*ValueContext) ReadSignedLongs

func (vc *ValueContext) ReadSignedLongs() (value []int32, err error)

ReadSignedLongs parses the list of encoded, signed longs from the value-context.

func (*ValueContext) ReadSignedRationals

func (vc *ValueContext) ReadSignedRationals() (value []SignedRational, err error)

ReadSignedRationals parses the list of encoded, signed rationals from the value-context.

func (*ValueContext) SetUndefinedValueType

func (vc *ValueContext) SetUndefinedValueType(tagType TagTypePrimitive)

SetUndefinedValueType sets the effective type if this is an unknown-type tag.

func (*ValueContext) SizeInBytes

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 (*ValueContext) TagId

func (vc *ValueContext) TagId() uint16

TagId returns the ID of the tag that we represent.

func (*ValueContext) UnitCount

func (vc *ValueContext) UnitCount() uint32

UnitCount returns the embedded unit-count.

func (*ValueContext) ValueOffset

func (vc *ValueContext) ValueOffset() uint32

ValueOffset returns the value-offset decoded as a `uint32`.

func (*ValueContext) Values

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()`.

type ValueEncoder

type ValueEncoder struct {
	// contains filtered or unexported fields
}

ValueEncoder knows how to encode values of every type to bytes.

func NewValueEncoder

func NewValueEncoder(byteOrder binary.ByteOrder) *ValueEncoder

NewValueEncoder returns a new ValueEncoder.

func (*ValueEncoder) Encode

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

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL