...

Package exif

import "github.com/dsoprea/go-exif/v3"
Overview
Index
Examples
Subdirectories

Overview ▾

Package exif parses raw EXIF information given a block of raw EXIF data. It can also construct new EXIF information, and provides tools for doing so. This package is not involved with the parsing of particular file-formats.

The EXIF data must first be extracted and then provided to us. Conversely, when constructing new EXIF data, the caller is responsible for packaging this in whichever format they require.

Index ▾

Constants
Variables
func BuildExifHeader(byteOrder binary.ByteOrder, firstIfdOffset uint32) (headerBytes []byte, err error)
func Collect(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, exifData []byte) (eh ExifHeader, index IfdIndex, err error)
func GetFlatExifData(exifData []byte, so *ScanOptions) (exifTags []ExifTag, med *MiscellaneousExifData, err error)
func GetFlatExifDataUniversalSearch(exifData []byte, so *ScanOptions, doUniversalSearch bool) (exifTags []ExifTag, med *MiscellaneousExifData, err error)
func GetFlatExifDataUniversalSearchWithReadSeeker(rs io.ReadSeeker, so *ScanOptions, doUniversalSearch bool) (exifTags []ExifTag, med *MiscellaneousExifData, err error)
func GpsDegreesEquals(gi1, gi2 GpsDegrees) bool
func LoadStandardTags(ti *TagIndex) (err error)
func SearchAndExtractExif(data []byte) (rawExif []byte, err error)
func SearchAndExtractExifN(data []byte, n int) (rawExif []byte, err error)
func SearchAndExtractExifWithReader(r io.Reader) (rawExif []byte, err error)
func SearchFileAndExtractExif(filepath string) (rawExif []byte, err error)
type BuilderTag
    func NewBuilderTag(ifdPath string, tagId uint16, typeId exifcommon.TagTypePrimitive, value *IfdBuilderTagValue, byteOrder binary.ByteOrder) *BuilderTag
    func NewChildIfdBuilderTag(ifdPath string, tagId uint16, value *IfdBuilderTagValue) *BuilderTag
    func NewStandardBuilderTag(ifdPath string, it *IndexedTag, byteOrder binary.ByteOrder, value interface{}) *BuilderTag
    func (bt *BuilderTag) SetValue(byteOrder binary.ByteOrder, value interface{}) (err error)
    func (bt *BuilderTag) String() string
    func (bt *BuilderTag) Value() (value *IfdBuilderTagValue)
type ByteWriter
    func NewByteWriter(b *bytes.Buffer, byteOrder binary.ByteOrder) (bw *ByteWriter)
    func (bw ByteWriter) WriteFourBytes(value []byte) (err error)
    func (bw ByteWriter) WriteUint16(value uint16) (err error)
    func (bw ByteWriter) WriteUint32(value uint32) (err error)
type ExifBlobSeeker
type ExifHeader
    func ParseExifHeader(data []byte) (eh ExifHeader, err error)
    func Visit(rootIfdIdentity *exifcommon.IfdIdentity, ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, exifData []byte, visitor TagVisitorFn, so *ScanOptions) (eh ExifHeader, furthestOffset uint32, err error)
    func (eh ExifHeader) String() string
type ExifReadSeeker
    func NewExifReadSeeker(rs io.ReadSeeker) *ExifReadSeeker
    func NewExifReadSeekerWithBytes(exifData []byte) *ExifReadSeeker
    func (edbs *ExifReadSeeker) GetReadSeeker(initialOffset int64) (rs io.ReadSeeker, err error)
type ExifTag
    func (et ExifTag) String() string
type GpsDegrees
    func NewGpsDegreesFromRationals(refValue string, rawCoordinate []exifcommon.Rational) (gd GpsDegrees, err error)
    func (d GpsDegrees) Decimal() float64
    func (d GpsDegrees) Raw() []exifcommon.Rational
    func (d GpsDegrees) String() string
type GpsInfo
    func (gi *GpsInfo) S2CellId() s2.CellID
    func (gi *GpsInfo) String() string
type Ifd
    func FindIfdFromRootIfd(rootIfd *Ifd, ifdPath string) (ifd *Ifd, err error)
    func (ifd *Ifd) ByteOrder() binary.ByteOrder
    func (ifd *Ifd) ChildIfdIndex() map[string]*Ifd
    func (ifd *Ifd) ChildWithIfdPath(iiChild *exifcommon.IfdIdentity) (childIfd *Ifd, err error)
    func (ifd *Ifd) Children() []*Ifd
    func (ifd *Ifd) DumpTags() []*IfdTagEntry
    func (ifd *Ifd) DumpTree() []string
    func (ifd *Ifd) Entries() []*IfdTagEntry
    func (ifd *Ifd) EntriesByTagId() map[uint16][]*IfdTagEntry
    func (ifd *Ifd) EnumerateTagsRecursively(visitor ParsedTagVisitor) (err error)
    func (ifd *Ifd) FindTagWithId(tagId uint16) (results []*IfdTagEntry, err error)
    func (ifd *Ifd) FindTagWithName(tagName string) (results []*IfdTagEntry, err error)
    func (ifd *Ifd) GpsInfo() (gi *GpsInfo, err error)
    func (ifd *Ifd) IfdIdentity() *exifcommon.IfdIdentity
    func (ifd *Ifd) NextIfd() *Ifd
    func (ifd *Ifd) Offset() uint32
    func (ifd *Ifd) ParentTagIndex() int
    func (ifd *Ifd) PrintIfdTree()
    func (ifd *Ifd) PrintTagTree(populateValues bool)
    func (ifd *Ifd) String() string
    func (ifd *Ifd) Thumbnail() (data []byte, err error)
type IfdBuilder
    func GetOrCreateIbFromRootIb(rootIb *IfdBuilder, fqIfdPath string) (ib *IfdBuilder, err error)
    func NewIfdBuilder(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exifcommon.IfdIdentity, byteOrder binary.ByteOrder) (ib *IfdBuilder)
    func NewIfdBuilderFromExistingChain(rootIfd *Ifd) (firstIb *IfdBuilder)
    func NewIfdBuilderWithExistingIfd(ifd *Ifd) (ib *IfdBuilder)
    func (ib *IfdBuilder) Add(bt *BuilderTag) (err error)
    func (ib *IfdBuilder) AddChildIb(childIb *IfdBuilder) (err error)
    func (ib *IfdBuilder) AddStandard(tagId uint16, value interface{}) (err error)
    func (ib *IfdBuilder) AddStandardWithName(tagName string, value interface{}) (err error)
    func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, includeTagIds []uint16, excludeTagIds []uint16) (err error)
    func (ib *IfdBuilder) ChildWithTagId(childIfdTagId uint16) (childIb *IfdBuilder, err error)
    func (ib *IfdBuilder) DeleteAll(tagId uint16) (n int, err error)
    func (ib *IfdBuilder) DeleteFirst(tagId uint16) (err error)
    func (ib *IfdBuilder) DeleteN(tagId uint16, n int) (err error)
    func (ib *IfdBuilder) DumpToStrings() (lines []string)
    func (ib *IfdBuilder) Find(tagId uint16) (position int, err error)
    func (ib *IfdBuilder) FindN(tagId uint16, maxFound int) (found []int, err error)
    func (ib *IfdBuilder) FindTag(tagId uint16) (bt *BuilderTag, err error)
    func (ib *IfdBuilder) FindTagWithName(tagName string) (bt *BuilderTag, err error)
    func (ib *IfdBuilder) IfdIdentity() *exifcommon.IfdIdentity
    func (ib *IfdBuilder) NewBuilderTagFromBuilder(childIb *IfdBuilder) (bt *BuilderTag)
    func (ib *IfdBuilder) NextIb() (nextIb *IfdBuilder, err error)
    func (ib *IfdBuilder) PrintIfdTree()
    func (ib *IfdBuilder) PrintTagTree()
    func (ib *IfdBuilder) Replace(tagId uint16, bt *BuilderTag) (err error)
    func (ib *IfdBuilder) ReplaceAt(position int, bt *BuilderTag) (err error)
    func (ib *IfdBuilder) Set(bt *BuilderTag) (err error)
    func (ib *IfdBuilder) SetNextIb(nextIb *IfdBuilder) (err error)
    func (ib *IfdBuilder) SetStandard(tagId uint16, value interface{}) (err error)
    func (ib *IfdBuilder) SetStandardWithName(tagName string, value interface{}) (err error)
    func (ib *IfdBuilder) SetThumbnail(data []byte) (err error)
    func (ib *IfdBuilder) String() string
    func (ib *IfdBuilder) Tags() (tags []*BuilderTag)
    func (ib *IfdBuilder) Thumbnail() []byte
type IfdBuilderTagValue
    func NewIfdBuilderTagValueFromBytes(valueBytes []byte) *IfdBuilderTagValue
    func NewIfdBuilderTagValueFromIfdBuilder(ib *IfdBuilder) *IfdBuilderTagValue
    func (ibtv IfdBuilderTagValue) Bytes() []byte
    func (ibtv IfdBuilderTagValue) Ib() *IfdBuilder
    func (ibtv IfdBuilderTagValue) IsBytes() bool
    func (ibtv IfdBuilderTagValue) IsIb() bool
    func (ibtv IfdBuilderTagValue) String() string
type IfdByteEncoder
    func NewIfdByteEncoder() (ibe *IfdByteEncoder)
    func (ibe *IfdByteEncoder) EncodeToExif(ib *IfdBuilder) (data []byte, err error)
    func (ibe *IfdByteEncoder) EncodeToExifPayload(ib *IfdBuilder) (data []byte, err error)
    func (ibe *IfdByteEncoder) Journal() [][3]string
    func (ibe *IfdByteEncoder) PrintJournal()
    func (ibe *IfdByteEncoder) TableSize(entryCount int) uint32
type IfdEnumerate
    func NewIfdEnumerate(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ebs ExifBlobSeeker, byteOrder binary.ByteOrder) *IfdEnumerate
    func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error)
    func (ie *IfdEnumerate) FurthestOffset() uint32
    func (ie *IfdEnumerate) Scan(iiRoot *exifcommon.IfdIdentity, ifdOffset uint32, visitor TagVisitorFn, so *ScanOptions) (med *MiscellaneousExifData, err error)
type IfdIndex
type IfdTagEntry
    func (ite *IfdTagEntry) ChildFqIfdPath() string
    func (ite *IfdTagEntry) ChildIfdName() string
    func (ite *IfdTagEntry) ChildIfdPath() string
    func (ite *IfdTagEntry) Format() (phrase string, err error)
    func (ite *IfdTagEntry) FormatFirst() (phrase string, err error)
    func (ite *IfdTagEntry) GetRawBytes() (rawBytes []byte, err error)
    func (ite *IfdTagEntry) IfdIdentity() *exifcommon.IfdIdentity
    func (ite *IfdTagEntry) IfdPath() string
    func (ite *IfdTagEntry) IsThumbnailOffset() bool
    func (ite *IfdTagEntry) IsThumbnailSize() bool
    func (ite *IfdTagEntry) SetChildIfd(ii *exifcommon.IfdIdentity)
    func (ite *IfdTagEntry) String() string
    func (ite *IfdTagEntry) TagId() uint16
    func (ite *IfdTagEntry) TagName() string
    func (ite *IfdTagEntry) TagType() exifcommon.TagTypePrimitive
    func (ite *IfdTagEntry) UnitCount() uint32
    func (ite *IfdTagEntry) Value() (value interface{}, err error)
type IndexedTag
    func (it *IndexedTag) DoesSupportType(tagType exifcommon.TagTypePrimitive) bool
    func (it *IndexedTag) GetEncodingType(value interface{}) exifcommon.TagTypePrimitive
    func (it *IndexedTag) Is(ifdPath string, id uint16) bool
    func (it *IndexedTag) IsName(ifdPath, name string) bool
    func (it *IndexedTag) String() string
type MiscellaneousExifData
    func (med *MiscellaneousExifData) UnknownTags() map[exifcommon.BasicTag]exifcommon.BasicTag
type ParsedTagVisitor
type QueuedIfd
type ScanOptions
type TagIndex
    func NewTagIndex() *TagIndex
    func (ti *TagIndex) Add(it *IndexedTag) (err error)
    func (ti *TagIndex) FindFirst(id uint16, typeId exifcommon.TagTypePrimitive, ifdIdentities []*exifcommon.IfdIdentity) (it *IndexedTag, err error)
    func (ti *TagIndex) Get(ii *exifcommon.IfdIdentity, id uint16) (it *IndexedTag, err error)
    func (ti *TagIndex) GetWithName(ii *exifcommon.IfdIdentity, name string) (it *IndexedTag, err error)
    func (ti *TagIndex) SetUniversalSearch(flag bool)
    func (ti *TagIndex) UniversalSearch() bool
type TagVisitorFn

Package files

data_layer.go error.go exif.go gps.go ifd_builder.go ifd_builder_encode.go ifd_enumerate.go ifd_tag_entry.go package.go tags.go tags_data.go testing_common.go utility.go

Constants

const (
    // ExifAddressableAreaStart is the absolute offset in the file that all
    // offsets are relative to.
    ExifAddressableAreaStart = uint32(0x0)

    // ExifDefaultFirstIfdOffset is essentially the number of bytes in addition
    // to `ExifAddressableAreaStart` that you have to move in order to escape
    // the rest of the header and get to the earliest point where we can put
    // stuff (which has to be the first IFD). This is the size of the header
    // sequence containing the two-character byte-order, two-character fixed-
    // bytes, and the four bytes describing the first-IFD offset.
    ExifDefaultFirstIfdOffset = uint32(2 + 2 + 4)
)
const (

    // ThumbnailFqIfdPath is the fully-qualified IFD path that the thumbnail
    // must be found in.
    ThumbnailFqIfdPath = "IFD1"

    // ThumbnailOffsetTagId returns the tag-ID of the thumbnail offset.
    ThumbnailOffsetTagId = 0x0201

    // ThumbnailSizeTagId returns the tag-ID of the thumbnail size.
    ThumbnailSizeTagId = 0x0202
)
const (

    // TagGpsVersionId is the ID of the GPS version tag.
    TagGpsVersionId = 0x0000

    // TagLatitudeId is the ID of the GPS latitude tag.
    TagLatitudeId = 0x0002

    // TagLatitudeRefId is the ID of the GPS latitude orientation tag.
    TagLatitudeRefId = 0x0001

    // TagLongitudeId is the ID of the GPS longitude tag.
    TagLongitudeId = 0x0004

    // TagLongitudeRefId is the ID of the GPS longitude-orientation tag.
    TagLongitudeRefId = 0x0003

    // TagTimestampId is the ID of the GPS time tag.
    TagTimestampId = 0x0007

    // TagDatestampId is the ID of the GPS date tag.
    TagDatestampId = 0x001d

    // TagAltitudeId is the ID of the GPS altitude tag.
    TagAltitudeId = 0x0006

    // TagAltitudeRefId is the ID of the GPS altitude-orientation tag.
    TagAltitudeRefId = 0x0005
)
const (
    // ExifSignatureLength is the number of bytes in the EXIF signature (which
    // customarily includes the first IFD offset).
    ExifSignatureLength = 8
)
const (
    // Tag-ID + Tag-Type + Unit-Count + Value/Offset.
    IfdTagEntrySize = uint32(2 + 2 + 4 + 4)
)

Variables

var (
    // ErrTagNotFound indicates that the tag was not found.
    ErrTagNotFound = errors.New("tag not found")

    // ErrTagNotKnown indicates that the tag is not registered with us as a
    // known tag.
    ErrTagNotKnown = errors.New("tag is not known")
)
var (
    ExifBigEndianSignature    = [4]byte{'M', 'M', 0x00, 0x2a}
    ExifLittleEndianSignature = [4]byte{'I', 'I', 0x2a, 0x00}
)
var (
    ErrNoExif          = errors.New("no exif data")
    ErrExifHeaderError = errors.New("exif header error")
)
var (
    ErrTagEntryNotFound = errors.New("tag entry not found")
    ErrChildIbNotFound  = errors.New("child IB not found")
)
var (
    // ErrNoThumbnail means that no thumbnail was found.
    ErrNoThumbnail = errors.New("no thumbnail")

    // ErrNoGpsTags means that no GPS info was found.
    ErrNoGpsTags = errors.New("no gps tags")

    // ErrTagTypeNotValid means that the tag-type is not valid.
    ErrTagTypeNotValid = errors.New("tag type invalid")

    // ErrOffsetInvalid means that the file offset is not valid.
    ErrOffsetInvalid = errors.New("file offset invalid")
)
var (
    // ErrGpsCoordinatesNotValid means that some part of the geographic data was
    // unparseable.
    ErrGpsCoordinatesNotValid = errors.New("GPS coordinates not valid")
)
var (
    // ValidGpsVersions is the list of recognized EXIF GPS versions/signatures.
    ValidGpsVersions = [][4]byte{

        {2, 0, 0, 0},

        {2, 2, 0, 0},

        {2, 3, 0, 0},
    }
)

func BuildExifHeader

func BuildExifHeader(byteOrder binary.ByteOrder, firstIfdOffset uint32) (headerBytes []byte, err error)

BuildExifHeader constructs the bytes that go at the front of the stream.

Example

Code:

headerBytes, err := BuildExifHeader(exifcommon.TestDefaultByteOrder, 0x11223344)
log.PanicIf(err)

eh, err := ParseExifHeader(headerBytes)
log.PanicIf(err)

fmt.Printf("%v\n", eh)

Output:

ExifHeader<BYTE-ORDER=[BigEndian] FIRST-IFD-OFFSET=(0x11223344)>

func Collect

func Collect(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, exifData []byte) (eh ExifHeader, index IfdIndex, err error)

Collect recursively builds a static structure of all IFDs and tags.

func GetFlatExifData

func GetFlatExifData(exifData []byte, so *ScanOptions) (exifTags []ExifTag, med *MiscellaneousExifData, err error)

GetFlatExifData returns a simple, flat representation of all tags.

func GetFlatExifDataUniversalSearch

func GetFlatExifDataUniversalSearch(exifData []byte, so *ScanOptions, doUniversalSearch bool) (exifTags []ExifTag, med *MiscellaneousExifData, err error)

GetFlatExifDataUniversalSearch returns a simple, flat representation of all tags.

func GetFlatExifDataUniversalSearchWithReadSeeker

func GetFlatExifDataUniversalSearchWithReadSeeker(rs io.ReadSeeker, so *ScanOptions, doUniversalSearch bool) (exifTags []ExifTag, med *MiscellaneousExifData, err error)

GetFlatExifDataUniversalSearchWithReadSeeker returns a simple, flat representation of all tags given a ReadSeeker.

func GpsDegreesEquals

func GpsDegreesEquals(gi1, gi2 GpsDegrees) bool

GpsDegreesEquals returns true if the two `GpsDegrees` are identical.

func LoadStandardTags

func LoadStandardTags(ti *TagIndex) (err error)

LoadStandardTags registers the tags that all devices/applications should support.

func SearchAndExtractExif

func SearchAndExtractExif(data []byte) (rawExif []byte, err error)

SearchAndExtractExif searches for an EXIF blob in the byte-slice.

func SearchAndExtractExifN

func SearchAndExtractExifN(data []byte, n int) (rawExif []byte, err error)

SearchAndExtractExifN searches for an EXIF blob in the byte-slice, but skips the given number of EXIF blocks first. This is a forensics tool that helps identify multiple EXIF blocks in a file.

func SearchAndExtractExifWithReader

func SearchAndExtractExifWithReader(r io.Reader) (rawExif []byte, err error)

SearchAndExtractExifWithReader searches for an EXIF blob using an `io.Reader`. We can't know how much long the EXIF data is without parsing it, so this will likely grab up a lot of the image-data, too.

func SearchFileAndExtractExif

func SearchFileAndExtractExif(filepath string) (rawExif []byte, err error)

SearchFileAndExtractExif returns a slice from the beginning of the EXIF data to the end of the file (it's not practical to try and calculate where the data actually ends).

type BuilderTag

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

func NewBuilderTag

func NewBuilderTag(ifdPath string, tagId uint16, typeId exifcommon.TagTypePrimitive, value *IfdBuilderTagValue, byteOrder binary.ByteOrder) *BuilderTag

func NewChildIfdBuilderTag

func NewChildIfdBuilderTag(ifdPath string, tagId uint16, value *IfdBuilderTagValue) *BuilderTag

func NewStandardBuilderTag

func NewStandardBuilderTag(ifdPath string, it *IndexedTag, byteOrder binary.ByteOrder, value interface{}) *BuilderTag

NewStandardBuilderTag constructs a `BuilderTag` instance. The type is looked up. `ii` is the type of IFD that owns this tag.

func (*BuilderTag) SetValue

func (bt *BuilderTag) SetValue(byteOrder binary.ByteOrder, value interface{}) (err error)

Example

Code:

testImageFilepath := getTestImageFilepath()

rawExif, err := SearchFileAndExtractExif(testImageFilepath)
log.PanicIf(err)

im, err := exifcommon.NewIfdMappingWithStandard()
log.PanicIf(err)

ti := NewTagIndex()

_, index, err := Collect(im, ti, rawExif)
log.PanicIf(err)

// Create builder.

rootIb := NewIfdBuilderFromExistingChain(index.RootIfd)

// Find tag to update.

exifBt, err := rootIb.FindTagWithName("ExifTag")
log.PanicIf(err)

ucBt, err := exifBt.value.Ib().FindTagWithName("UserComment")
log.PanicIf(err)

// Update the value. Since this is an "undefined"-type tag, we have to use
// its type-specific struct.

// TODO(dustin): !! Add an example for setting a non-unknown value, too.
uc := exifundefined.Tag9286UserComment{
    EncodingType:  exifundefined.TagUndefinedType_9286_UserComment_Encoding_ASCII,
    EncodingBytes: []byte("TEST COMMENT"),
}

err = ucBt.SetValue(rootIb.byteOrder, uc)
log.PanicIf(err)

// Encode.

ibe := NewIfdByteEncoder()

// This returns the raw bytes that you will be looking for, but there's no
// use for them at this point in the example.
_, err = ibe.EncodeToExif(rootIb)
log.PanicIf(err)

func (*BuilderTag) String

func (bt *BuilderTag) String() string

func (*BuilderTag) Value

func (bt *BuilderTag) Value() (value *IfdBuilderTagValue)

type ByteWriter

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

func NewByteWriter

func NewByteWriter(b *bytes.Buffer, byteOrder binary.ByteOrder) (bw *ByteWriter)

func (ByteWriter) WriteFourBytes

func (bw ByteWriter) WriteFourBytes(value []byte) (err error)

func (ByteWriter) WriteUint16

func (bw ByteWriter) WriteUint16(value uint16) (err error)

func (ByteWriter) WriteUint32

func (bw ByteWriter) WriteUint32(value uint32) (err error)

type ExifBlobSeeker

type ExifBlobSeeker interface {
    GetReadSeeker(initialOffset int64) (rs io.ReadSeeker, err error)
}

type ExifHeader

type ExifHeader struct {
    ByteOrder      binary.ByteOrder
    FirstIfdOffset uint32
}

func ParseExifHeader

func ParseExifHeader(data []byte) (eh ExifHeader, err error)

ParseExifHeader parses the bytes at the very top of the header.

This will panic with ErrNoExif on any data errors so that we can double as an EXIF-detection routine.

func Visit

func Visit(rootIfdIdentity *exifcommon.IfdIdentity, ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, exifData []byte, visitor TagVisitorFn, so *ScanOptions) (eh ExifHeader, furthestOffset uint32, err error)

Visit recursively invokes a callback for every tag.

func (ExifHeader) String

func (eh ExifHeader) String() string

type ExifReadSeeker

ExifReadSeeker knows how to retrieve data from the EXIF blob relative to the beginning of the blob (so, absolute position (0) is the first byte of the EXIF data).

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

func NewExifReadSeeker

func NewExifReadSeeker(rs io.ReadSeeker) *ExifReadSeeker

func NewExifReadSeekerWithBytes

func NewExifReadSeekerWithBytes(exifData []byte) *ExifReadSeeker

func (*ExifReadSeeker) GetReadSeeker

func (edbs *ExifReadSeeker) GetReadSeeker(initialOffset int64) (rs io.ReadSeeker, err error)

Fork creates a new ReadSeeker instead that wraps a BouncebackReader to maintain its own position in the stream.

type ExifTag

ExifTag is one simple representation of a tag in a flat list of all of them.

type ExifTag struct {
    // IfdPath is the fully-qualified IFD path (even though it is not named as
    // such).
    IfdPath string `json:"ifd_path"`

    // TagId is the tag-ID.
    TagId uint16 `json:"id"`

    // TagName is the tag-name. This is never empty.
    TagName string `json:"name"`

    // UnitCount is the recorded number of units constution of the value.
    UnitCount uint32 `json:"unit_count"`

    // TagTypeId is the type-ID.
    TagTypeId exifcommon.TagTypePrimitive `json:"type_id"`

    // TagTypeName is the type name.
    TagTypeName string `json:"type_name"`

    // Value is the decoded value.
    Value interface{} `json:"value"`

    // ValueBytes is the raw, encoded value.
    ValueBytes []byte `json:"value_bytes"`

    // Formatted is the human representation of the first value (tag values are
    // always an array).
    FormattedFirst string `json:"formatted_first"`

    // Formatted is the human representation of the complete value.
    Formatted string `json:"formatted"`

    // ChildIfdPath is the name of the child IFD this tag represents (if it
    // represents any). Otherwise, this is empty.
    ChildIfdPath string `json:"child_ifd_path"`
}

func (ExifTag) String

func (et ExifTag) String() string

String returns a string representation.

type GpsDegrees

GpsDegrees is a high-level struct representing geographic data.

type GpsDegrees struct {
    // Orientation describes the N/E/S/W direction that this position is
    // relative to.
    Orientation byte

    // Degrees is a simple float representing the underlying rational degrees
    // amount.
    Degrees float64

    // Minutes is a simple float representing the underlying rational minutes
    // amount.
    Minutes float64

    // Seconds is a simple float representing the underlying ration seconds
    // amount.
    Seconds float64
}

func NewGpsDegreesFromRationals

func NewGpsDegreesFromRationals(refValue string, rawCoordinate []exifcommon.Rational) (gd GpsDegrees, err error)

NewGpsDegreesFromRationals returns a GpsDegrees struct given the EXIF-encoded information. The refValue is the N/E/S/W direction that this position is relative to.

func (GpsDegrees) Decimal

func (d GpsDegrees) Decimal() float64

Decimal calculates and returns the simplified float representation of the component degrees.

func (GpsDegrees) Raw

func (d GpsDegrees) Raw() []exifcommon.Rational

Raw returns a Rational struct that can be used to *write* coordinates. In practice, the denominator are typically (1) in the original EXIF data, and, that being the case, this will best preserve precision.

func (GpsDegrees) String

func (d GpsDegrees) String() string

String provides returns a descriptive string.

type GpsInfo

GpsInfo encapsulates all of the geographic information in one place.

type GpsInfo struct {
    Latitude, Longitude GpsDegrees
    Altitude            int
    Timestamp           time.Time
}

func (*GpsInfo) S2CellId

func (gi *GpsInfo) S2CellId() s2.CellID

S2CellId returns the cell-ID of the geographic location on the earth.

func (*GpsInfo) String

func (gi *GpsInfo) String() string

String returns a descriptive string.

type Ifd

Ifd represents a single, parsed IFD.

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

func FindIfdFromRootIfd

func FindIfdFromRootIfd(rootIfd *Ifd, ifdPath string) (ifd *Ifd, err error)

FindIfdFromRootIfd returns the given `Ifd` given the root-IFD and path of the desired IFD.

func (*Ifd) ByteOrder

func (ifd *Ifd) ByteOrder() binary.ByteOrder

Offset returns the offset of the IFD in the stream.

func (*Ifd) ChildIfdIndex

func (ifd *Ifd) ChildIfdIndex() map[string]*Ifd

ChildWithIfdPath returns a map of all child IFDs of this IFD.

func (*Ifd) ChildWithIfdPath

func (ifd *Ifd) ChildWithIfdPath(iiChild *exifcommon.IfdIdentity) (childIfd *Ifd, err error)

ChildWithIfdPath returns an `Ifd` struct for the given child of the current IFD.

func (*Ifd) Children

func (ifd *Ifd) Children() []*Ifd

Children returns a flat list of all child IFDs of this IFD.

func (*Ifd) DumpTags

func (ifd *Ifd) DumpTags() []*IfdTagEntry

DumpTags prints the IFD hierarchy.

func (*Ifd) DumpTree

func (ifd *Ifd) DumpTree() []string

DumpTree returns a list of strings describing the IFD hierarchy.

func (*Ifd) Entries

func (ifd *Ifd) Entries() []*IfdTagEntry

Entries returns a flat list of all tags for this IFD.

func (*Ifd) EntriesByTagId

func (ifd *Ifd) EntriesByTagId() map[uint16][]*IfdTagEntry

EntriesByTagId returns a map of all tags for this IFD.

func (*Ifd) EnumerateTagsRecursively

func (ifd *Ifd) EnumerateTagsRecursively(visitor ParsedTagVisitor) (err error)

EnumerateTagsRecursively calls the given visitor function for every tag and IFD in the current IFD, recursively.

Example

Code:

testImageFilepath := getTestImageFilepath()

rawExif, err := SearchFileAndExtractExif(testImageFilepath)
log.PanicIf(err)

im, err := exifcommon.NewIfdMappingWithStandard()
log.PanicIf(err)

ti := NewTagIndex()

_, index, err := Collect(im, ti, rawExif)
log.PanicIf(err)

cb := func(ifd *Ifd, ite *IfdTagEntry) error {

    // Something useful.

    return nil
}

err = index.RootIfd.EnumerateTagsRecursively(cb)
log.PanicIf(err)

func (*Ifd) FindTagWithId

func (ifd *Ifd) FindTagWithId(tagId uint16) (results []*IfdTagEntry, err error)

FindTagWithId returns a list of tags (usually just zero or one) that match the given tag ID. This is efficient.

func (*Ifd) FindTagWithName

func (ifd *Ifd) FindTagWithName(tagName string) (results []*IfdTagEntry, err error)

FindTagWithName returns a list of tags (usually just zero or one) that match the given tag name. This is not efficient (though the labor is trivial).

Example

Code:

testImageFilepath := getTestImageFilepath()

rawExif, err := SearchFileAndExtractExif(testImageFilepath)
log.PanicIf(err)

im, err := exifcommon.NewIfdMappingWithStandard()
log.PanicIf(err)

ti := NewTagIndex()

_, index, err := Collect(im, ti, rawExif)
log.PanicIf(err)

tagName := "Model"

rootIfd := index.RootIfd

// We know the tag we want is on IFD0 (the first/root IFD).
results, err := rootIfd.FindTagWithName(tagName)
log.PanicIf(err)

// This should never happen.
if len(results) != 1 {
    log.Panicf("there wasn't exactly one result")
}

ite := results[0]

valueRaw, err := ite.Value()
log.PanicIf(err)

value := valueRaw.(string)
fmt.Println(value)

Output:

Canon EOS 5D Mark III

func (*Ifd) GpsInfo

func (ifd *Ifd) GpsInfo() (gi *GpsInfo, err error)

GpsInfo parses and consolidates the GPS info. This can only be called on the GPS IFD.

Example

Code:

filepath := getTestGpsImageFilepath()

rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)

im, err := exifcommon.NewIfdMappingWithStandard()
log.PanicIf(err)

ti := NewTagIndex()

_, index, err := Collect(im, ti, rawExif)
log.PanicIf(err)

ifd, err := index.RootIfd.ChildWithIfdPath(exifcommon.IfdGpsInfoStandardIfdIdentity)
log.PanicIf(err)

gi, err := ifd.GpsInfo()
log.PanicIf(err)

fmt.Printf("%s\n", gi)

Output:

GpsInfo<LAT=(26.58667) LON=(-80.05361) ALT=(0) TIME=[2018-04-29 01:22:57 +0000 UTC]>

func (*Ifd) IfdIdentity

func (ifd *Ifd) IfdIdentity() *exifcommon.IfdIdentity

IfdIdentity returns IFD identity that this struct represents.

func (*Ifd) NextIfd

func (ifd *Ifd) NextIfd() *Ifd

NextIfd returns the Ifd struct for the next IFD in the chain.

func (*Ifd) Offset

func (ifd *Ifd) Offset() uint32

Offset returns the offset of the IFD in the stream.

func (*Ifd) ParentTagIndex

func (ifd *Ifd) ParentTagIndex() int

ParentTagIndex returns the position of this IFD's tag in its parent IFD (*if* there is a parent).

func (*Ifd) PrintIfdTree

func (ifd *Ifd) PrintIfdTree()

PrintIfdTree prints the IFD hierarchy.

func (*Ifd) PrintTagTree

func (ifd *Ifd) PrintTagTree(populateValues bool)

PrintTagTree prints the IFD hierarchy.

func (*Ifd) String

func (ifd *Ifd) String() string

String returns a description string.

func (*Ifd) Thumbnail

func (ifd *Ifd) Thumbnail() (data []byte, err error)

Thumbnail returns the raw thumbnail bytes. This is typically directly readable by any standard image viewer.

Example

Code:

testImageFilepath := getTestImageFilepath()

rawExif, err := SearchFileAndExtractExif(testImageFilepath)
log.PanicIf(err)

im, err := exifcommon.NewIfdMappingWithStandard()
log.PanicIf(err)

ti := NewTagIndex()

_, index, err := Collect(im, ti, rawExif)
log.PanicIf(err)

// This returns the raw bytes that you will be looking for, but there's no
// use for them at this point in the example.
_, err = index.RootIfd.nextIfd.Thumbnail()
log.PanicIf(err)

type IfdBuilder

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

func GetOrCreateIbFromRootIb

func GetOrCreateIbFromRootIb(rootIb *IfdBuilder, fqIfdPath string) (ib *IfdBuilder, err error)

GetOrCreateIbFromRootIb returns an IB representing the requested IFD, even if an IB doesn't already exist for it. This function may call itself recursively.

func NewIfdBuilder

func NewIfdBuilder(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exifcommon.IfdIdentity, byteOrder binary.ByteOrder) (ib *IfdBuilder)

func NewIfdBuilderFromExistingChain

func NewIfdBuilderFromExistingChain(rootIfd *Ifd) (firstIb *IfdBuilder)

NewIfdBuilderFromExistingChain creates a chain of IB instances from an IFD chain generated from real data.

func NewIfdBuilderWithExistingIfd

func NewIfdBuilderWithExistingIfd(ifd *Ifd) (ib *IfdBuilder)

NewIfdBuilderWithExistingIfd creates a new IB using the same header type information as the given IFD.

func (*IfdBuilder) Add

func (ib *IfdBuilder) Add(bt *BuilderTag) (err error)

func (*IfdBuilder) AddChildIb

func (ib *IfdBuilder) AddChildIb(childIb *IfdBuilder) (err error)

AddChildIb adds a tag that branches to a new IFD.

func (*IfdBuilder) AddStandard

func (ib *IfdBuilder) AddStandard(tagId uint16, value interface{}) (err error)

AddStandard quickly and easily composes and adds the tag using the information already known about a tag. Only works with standard tags.

func (*IfdBuilder) AddStandardWithName

func (ib *IfdBuilder) AddStandardWithName(tagName string, value interface{}) (err error)

AddStandardWithName quickly and easily composes and adds the tag using the information already known about a tag (using the name). Only works with standard tags.

func (*IfdBuilder) AddTagsFromExisting

func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, includeTagIds []uint16, excludeTagIds []uint16) (err error)

AddTagsFromExisting does a verbatim copy of the entries in `ifd` to this builder. It excludes child IFDs. These must be added explicitly via `AddChildIb()`.

func (*IfdBuilder) ChildWithTagId

func (ib *IfdBuilder) ChildWithTagId(childIfdTagId uint16) (childIb *IfdBuilder, err error)

func (*IfdBuilder) DeleteAll

func (ib *IfdBuilder) DeleteAll(tagId uint16) (n int, err error)

func (*IfdBuilder) DeleteFirst

func (ib *IfdBuilder) DeleteFirst(tagId uint16) (err error)

func (*IfdBuilder) DeleteN

func (ib *IfdBuilder) DeleteN(tagId uint16, n int) (err error)

func (*IfdBuilder) DumpToStrings

func (ib *IfdBuilder) DumpToStrings() (lines []string)

func (*IfdBuilder) Find

func (ib *IfdBuilder) Find(tagId uint16) (position int, err error)

func (*IfdBuilder) FindN

func (ib *IfdBuilder) FindN(tagId uint16, maxFound int) (found []int, err error)

func (*IfdBuilder) FindTag

func (ib *IfdBuilder) FindTag(tagId uint16) (bt *BuilderTag, err error)

func (*IfdBuilder) FindTagWithName

func (ib *IfdBuilder) FindTagWithName(tagName string) (bt *BuilderTag, err error)

func (*IfdBuilder) IfdIdentity

func (ib *IfdBuilder) IfdIdentity() *exifcommon.IfdIdentity

func (*IfdBuilder) NewBuilderTagFromBuilder

func (ib *IfdBuilder) NewBuilderTagFromBuilder(childIb *IfdBuilder) (bt *BuilderTag)

func (*IfdBuilder) NextIb

func (ib *IfdBuilder) NextIb() (nextIb *IfdBuilder, err error)

func (*IfdBuilder) PrintIfdTree

func (ib *IfdBuilder) PrintIfdTree()

func (*IfdBuilder) PrintTagTree

func (ib *IfdBuilder) PrintTagTree()

func (*IfdBuilder) Replace

func (ib *IfdBuilder) Replace(tagId uint16, bt *BuilderTag) (err error)

func (*IfdBuilder) ReplaceAt

func (ib *IfdBuilder) ReplaceAt(position int, bt *BuilderTag) (err error)

func (*IfdBuilder) Set

func (ib *IfdBuilder) Set(bt *BuilderTag) (err error)

Set will add a new entry or update an existing entry.

func (*IfdBuilder) SetNextIb

func (ib *IfdBuilder) SetNextIb(nextIb *IfdBuilder) (err error)

func (*IfdBuilder) SetStandard

func (ib *IfdBuilder) SetStandard(tagId uint16, value interface{}) (err error)

SetStandard quickly and easily composes and adds or replaces the tag using the information already known about a tag. Only works with standard tags.

func (*IfdBuilder) SetStandardWithName

func (ib *IfdBuilder) SetStandardWithName(tagName string, value interface{}) (err error)

SetStandardWithName quickly and easily composes and adds or replaces the tag using the information already known about a tag (using the name). Only works with standard tags.

Example

ExampleIfdBuilder_SetStandardWithName establishes a chain of `IfdBuilder` structs from an existing chain of `Ifd` structs, navigates to the IB representing IFD0, updates the ProcessingSoftware tag to a different value, encodes down to a new EXIF block, reparses, and validates that the value for that tag is what we set it to.

Code:

testImageFilepath := getTestImageFilepath()

rawExif, err := SearchFileAndExtractExif(testImageFilepath)
log.PanicIf(err)

// Boilerplate.

im, err := exifcommon.NewIfdMappingWithStandard()
log.PanicIf(err)

ti := NewTagIndex()

// Load current IFDs.

_, index, err := Collect(im, ti, rawExif)
log.PanicIf(err)

ib := NewIfdBuilderFromExistingChain(index.RootIfd)

// Read the IFD whose tag we want to change.

// Standard:
// - "IFD0"
// - "IFD0/Exif0"
// - "IFD0/Exif0/Iop0"
// - "IFD0/GPSInfo0"
//
// If the numeric indices are not included, (0) is the default. Note that
// this isn't strictly necessary in our case since IFD0 is the first IFD anyway, but we're putting it here to show usage.
ifdPath := "IFD0"

childIb, err := GetOrCreateIbFromRootIb(ib, ifdPath)
log.PanicIf(err)

// There are a few functions that allow you to surgically change the tags in an
// IFD, but we're just gonna overwrite a tag that has an ASCII value.

tagName := "ProcessingSoftware"

err = childIb.SetStandardWithName(tagName, "alternative software")
log.PanicIf(err)

// Encode the in-memory representation back down to bytes.

ibe := NewIfdByteEncoder()

updatedRawExif, err := ibe.EncodeToExif(ib)
log.PanicIf(err)

// Reparse the EXIF to confirm that our value is there.

_, index, err = Collect(im, ti, updatedRawExif)
log.PanicIf(err)

// This isn't strictly necessary for the same reason as above, but it's here
// for documentation.
childIfd, err := FindIfdFromRootIfd(index.RootIfd, ifdPath)
log.PanicIf(err)

results, err := childIfd.FindTagWithName(tagName)
log.PanicIf(err)

for _, ite := range results {
    valueRaw, err := ite.Value()
    log.PanicIf(err)

    stringValue := valueRaw.(string)
    fmt.Println(stringValue)
}

Output:

alternative software

Example (Timestamp)

Code:

// Check initial value.

filepath := getTestGpsImageFilepath()

rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)

im, err := exifcommon.NewIfdMappingWithStandard()
log.PanicIf(err)

ti := NewTagIndex()

_, index, err := Collect(im, ti, rawExif)
log.PanicIf(err)

rootIfd := index.RootIfd

// Update the value.

rootIb := NewIfdBuilderFromExistingChain(rootIfd)

exifIb, err := rootIb.ChildWithTagId(exifcommon.IfdExifStandardIfdIdentity.TagId())
log.PanicIf(err)

t := time.Date(2020, 06, 7, 1, 30, 0, 0, time.UTC)

err = exifIb.SetStandardWithName("DateTimeDigitized", t)
log.PanicIf(err)

// Encode to bytes.

ibe := NewIfdByteEncoder()

updatedRawExif, err := ibe.EncodeToExif(rootIb)
log.PanicIf(err)

// Decode from bytes.

_, updatedIndex, err := Collect(im, ti, updatedRawExif)
log.PanicIf(err)

updatedRootIfd := updatedIndex.RootIfd

// Test.

updatedExifIfd, err := updatedRootIfd.ChildWithIfdPath(exifcommon.IfdExifStandardIfdIdentity)
log.PanicIf(err)

results, err := updatedExifIfd.FindTagWithName("DateTimeDigitized")
log.PanicIf(err)

ite := results[0]

phrase, err := ite.FormatFirst()
log.PanicIf(err)

fmt.Printf("%s\n", phrase)

Output:

2020:06:07 01:30:00

Example (UpdateGps)

Code:

// Check initial value.

filepath := getTestGpsImageFilepath()

rawExif, err := SearchFileAndExtractExif(filepath)
log.PanicIf(err)

im, err := exifcommon.NewIfdMappingWithStandard()
log.PanicIf(err)

ti := NewTagIndex()

_, index, err := Collect(im, ti, rawExif)
log.PanicIf(err)

rootIfd := index.RootIfd

gpsIfd, err := rootIfd.ChildWithIfdPath(exifcommon.IfdGpsInfoStandardIfdIdentity)
log.PanicIf(err)

initialGi, err := gpsIfd.GpsInfo()
log.PanicIf(err)

fmt.Printf("Original:\n%s\n\n", initialGi.Latitude.String())

// Update the value.

rootIb := NewIfdBuilderFromExistingChain(rootIfd)

gpsIb, err := rootIb.ChildWithTagId(exifcommon.IfdGpsInfoStandardIfdIdentity.TagId())
log.PanicIf(err)

updatedGi := GpsDegrees{
    Degrees: 11,
    Minutes: 22,
    Seconds: 33,
}

raw := updatedGi.Raw()

err = gpsIb.SetStandardWithName("GPSLatitude", raw)
log.PanicIf(err)

// Encode to bytes.

ibe := NewIfdByteEncoder()

updatedRawExif, err := ibe.EncodeToExif(rootIb)
log.PanicIf(err)

// Decode from bytes.

_, updatedIndex, err := Collect(im, ti, updatedRawExif)
log.PanicIf(err)

updatedRootIfd := updatedIndex.RootIfd

// Test.

updatedGpsIfd, err := updatedRootIfd.ChildWithIfdPath(exifcommon.IfdGpsInfoStandardIfdIdentity)
log.PanicIf(err)

recoveredUpdatedGi, err := updatedGpsIfd.GpsInfo()
log.PanicIf(err)

fmt.Printf("Updated, written, and re-read:\n%s\n", recoveredUpdatedGi.Latitude.String())

Output:

Original:
Degrees<O=[N] D=(26) M=(35) S=(12)>

Updated, written, and re-read:
Degrees<O=[N] D=(11) M=(22) S=(33)>

func (*IfdBuilder) SetThumbnail

func (ib *IfdBuilder) SetThumbnail(data []byte) (err error)

SetThumbnail sets thumbnail data.

NOTES:

func (*IfdBuilder) String

func (ib *IfdBuilder) String() string

func (*IfdBuilder) Tags

func (ib *IfdBuilder) Tags() (tags []*BuilderTag)

func (*IfdBuilder) Thumbnail

func (ib *IfdBuilder) Thumbnail() []byte

type IfdBuilderTagValue

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

func NewIfdBuilderTagValueFromBytes

func NewIfdBuilderTagValueFromBytes(valueBytes []byte) *IfdBuilderTagValue

func NewIfdBuilderTagValueFromIfdBuilder

func NewIfdBuilderTagValueFromIfdBuilder(ib *IfdBuilder) *IfdBuilderTagValue

func (IfdBuilderTagValue) Bytes

func (ibtv IfdBuilderTagValue) Bytes() []byte

func (IfdBuilderTagValue) Ib

func (ibtv IfdBuilderTagValue) Ib() *IfdBuilder

func (IfdBuilderTagValue) IsBytes

func (ibtv IfdBuilderTagValue) IsBytes() bool

IsBytes returns true if the bytes are populated. This is always the case when we're loaded from a tag in an existing IFD.

func (IfdBuilderTagValue) IsIb

func (ibtv IfdBuilderTagValue) IsIb() bool

func (IfdBuilderTagValue) String

func (ibtv IfdBuilderTagValue) String() string

type IfdByteEncoder

IfdByteEncoder converts an IB to raw bytes (for writing) while also figuring out all of the allocations and indirection that is required for extended data.

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

func NewIfdByteEncoder

func NewIfdByteEncoder() (ibe *IfdByteEncoder)

func (*IfdByteEncoder) EncodeToExif

func (ibe *IfdByteEncoder) EncodeToExif(ib *IfdBuilder) (data []byte, err error)

EncodeToExif calls EncodeToExifPayload and then packages the result into a complete EXIF block.

Example

Code:

// Construct an IFD.

im, err := exifcommon.NewIfdMappingWithStandard()
log.PanicIf(err)

ti := NewTagIndex()
ib := NewIfdBuilder(im, ti, exifcommon.IfdStandardIfdIdentity, exifcommon.TestDefaultByteOrder)

err = ib.AddStandardWithName("ProcessingSoftware", "asciivalue")
log.PanicIf(err)

err = ib.AddStandardWithName("DotRange", []uint8{0x11})
log.PanicIf(err)

err = ib.AddStandardWithName("SubfileType", []uint16{0x2233})
log.PanicIf(err)

err = ib.AddStandardWithName("ImageWidth", []uint32{0x44556677})
log.PanicIf(err)

err = ib.AddStandardWithName("WhitePoint", []exifcommon.Rational{{Numerator: 0x11112222, Denominator: 0x33334444}})
log.PanicIf(err)

err = ib.AddStandardWithName("ShutterSpeedValue", []exifcommon.SignedRational{{Numerator: 0x11112222, Denominator: 0x33334444}})
log.PanicIf(err)

// Encode it.

ibe := NewIfdByteEncoder()

exifData, err := ibe.EncodeToExif(ib)
log.PanicIf(err)

// Parse it so we can see it.

_, index, err := Collect(im, ti, exifData)
log.PanicIf(err)

for i, ite := range index.RootIfd.Entries() {
    value, err := ite.Value()
    log.PanicIf(err)

    fmt.Printf("%d: %s [%v]\n", i, ite, value)
}

Output:

0: IfdTagEntry<TAG-IFD-PATH=[IFD] TAG-ID=(0x000b) TAG-TYPE=[ASCII] UNIT-COUNT=(11)> [asciivalue]
1: IfdTagEntry<TAG-IFD-PATH=[IFD] TAG-ID=(0x0150) TAG-TYPE=[BYTE] UNIT-COUNT=(1)> [[17]]
2: IfdTagEntry<TAG-IFD-PATH=[IFD] TAG-ID=(0x00ff) TAG-TYPE=[SHORT] UNIT-COUNT=(1)> [[8755]]
3: IfdTagEntry<TAG-IFD-PATH=[IFD] TAG-ID=(0x0100) TAG-TYPE=[LONG] UNIT-COUNT=(1)> [[1146447479]]
4: IfdTagEntry<TAG-IFD-PATH=[IFD] TAG-ID=(0x013e) TAG-TYPE=[RATIONAL] UNIT-COUNT=(1)> [[{286335522 858997828}]]
5: IfdTagEntry<TAG-IFD-PATH=[IFD] TAG-ID=(0x9201) TAG-TYPE=[SRATIONAL] UNIT-COUNT=(1)> [[{286335522 858997828}]]

func (*IfdByteEncoder) EncodeToExifPayload

func (ibe *IfdByteEncoder) EncodeToExifPayload(ib *IfdBuilder) (data []byte, err error)

EncodeToExifPayload is the base encoding step that transcribes the entire IB structure to its on-disk layout.

func (*IfdByteEncoder) Journal

func (ibe *IfdByteEncoder) Journal() [][3]string

func (*IfdByteEncoder) PrintJournal

func (ibe *IfdByteEncoder) PrintJournal()

PrintJournal prints a hierarchical representation of the steps taken during encoding.

func (*IfdByteEncoder) TableSize

func (ibe *IfdByteEncoder) TableSize(entryCount int) uint32

type IfdEnumerate

IfdEnumerate is the main enumeration type. It knows how to parse the IFD containers in the EXIF blob.

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

func NewIfdEnumerate

func NewIfdEnumerate(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ebs ExifBlobSeeker, byteOrder binary.ByteOrder) *IfdEnumerate

NewIfdEnumerate returns a new instance of IfdEnumerate.

func (*IfdEnumerate) Collect

func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error)

Collect enumerates the different EXIF blocks (called IFDs) and builds out an index struct for referencing all of the parsed data.

func (*IfdEnumerate) FurthestOffset

func (ie *IfdEnumerate) FurthestOffset() uint32

FurthestOffset returns the furthest offset visited in the EXIF blob. This *does not* account for the locations of any undefined tags since we always evaluate the furthest offset, whether or not the user wants to know it.

We are not willing to incur the cost of actually parsing those tags just to know their length when there are still undefined tags that are out there that we still won't have any idea how to parse, thus making this an approximation regardless of how clever we get.

func (*IfdEnumerate) Scan

func (ie *IfdEnumerate) Scan(iiRoot *exifcommon.IfdIdentity, ifdOffset uint32, visitor TagVisitorFn, so *ScanOptions) (med *MiscellaneousExifData, err error)

Scan enumerates the different EXIF blocks (called IFDs). `rootIfdName` will be "IFD" in the TIFF standard.

type IfdIndex

IfdIndex collects a bunch of IFD and tag information stored in several different ways in order to provide convenient lookups.

type IfdIndex struct {
    RootIfd *Ifd
    Ifds    []*Ifd
    Tree    map[int]*Ifd
    Lookup  map[string]*Ifd
}

type IfdTagEntry

IfdTagEntry refers to a tag in the loaded EXIF block.

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

func (*IfdTagEntry) ChildFqIfdPath

func (ite *IfdTagEntry) ChildFqIfdPath() string

ChildFqIfdPath returns the complete path of the child IFD along with the numeric suffixes differentiating sibling occurrences of the same type. "0" indices are omitted.

func (*IfdTagEntry) ChildIfdName

func (ite *IfdTagEntry) ChildIfdName() string

ChildIfdName returns the name of the child IFD

func (*IfdTagEntry) ChildIfdPath

func (ite *IfdTagEntry) ChildIfdPath() string

ChildIfdPath returns the path of the child IFD.

func (*IfdTagEntry) Format

func (ite *IfdTagEntry) Format() (phrase string, err error)

Format returns the tag's value as a string.

func (*IfdTagEntry) FormatFirst

func (ite *IfdTagEntry) FormatFirst() (phrase string, err error)

FormatFirst returns the same as Format() but only the first item.

func (*IfdTagEntry) GetRawBytes

func (ite *IfdTagEntry) GetRawBytes() (rawBytes []byte, err error)

GetRawBytes renders a specific list of bytes from the value in this tag.

func (*IfdTagEntry) IfdIdentity

func (ite *IfdTagEntry) IfdIdentity() *exifcommon.IfdIdentity

IfdIdentity returns the IfdIdentity associated with this tag.

func (*IfdTagEntry) IfdPath

func (ite *IfdTagEntry) IfdPath() string

IfdPath returns the fully-qualified path of the IFD that owns this tag.

func (*IfdTagEntry) IsThumbnailOffset

func (ite *IfdTagEntry) IsThumbnailOffset() bool

IsThumbnailOffset returns true if the tag has the IFD and tag-ID of a thumbnail offset.

func (*IfdTagEntry) IsThumbnailSize

func (ite *IfdTagEntry) IsThumbnailSize() bool

IsThumbnailSize returns true if the tag has the IFD and tag-ID of a thumbnail size.

func (*IfdTagEntry) SetChildIfd

func (ite *IfdTagEntry) SetChildIfd(ii *exifcommon.IfdIdentity)

SetChildIfd sets child-IFD information (if we represent a child IFD).

func (*IfdTagEntry) String

func (ite *IfdTagEntry) String() string

String returns a stringified representation of the struct.

func (*IfdTagEntry) TagId

func (ite *IfdTagEntry) TagId() uint16

TagId returns the ID of the tag that we represent. The combination of (IfdPath(), TagId()) is unique.

func (*IfdTagEntry) TagName

func (ite *IfdTagEntry) TagName() string

TagName returns the name of the tag. This is determined else and set after the parse (since it's not actually stored in the stream). If it's empty, it is because it is an unknown tag (nonstandard or otherwise unavailable in the tag-index).

func (*IfdTagEntry) TagType

func (ite *IfdTagEntry) TagType() exifcommon.TagTypePrimitive

TagType is the type of value for this tag.

func (*IfdTagEntry) UnitCount

func (ite *IfdTagEntry) UnitCount() uint32

UnitCount returns the unit-count of the tag's value.

func (*IfdTagEntry) Value

func (ite *IfdTagEntry) Value() (value interface{}, err error)

Value returns the specific, parsed, typed value from the tag.

type IndexedTag

IndexedTag describes one index lookup result.

type IndexedTag struct {
    // Id is the tag-ID.
    Id uint16

    // Name is the tag name.
    Name string

    // IfdPath is the proper IFD path of this tag. This is not fully-qualified.
    IfdPath string

    // SupportedTypes is an unsorted list of allowed tag-types.
    SupportedTypes []exifcommon.TagTypePrimitive
}

func (*IndexedTag) DoesSupportType

func (it *IndexedTag) DoesSupportType(tagType exifcommon.TagTypePrimitive) bool

DoesSupportType returns true if this tag can be found/decoded with this type.

func (*IndexedTag) GetEncodingType

func (it *IndexedTag) GetEncodingType(value interface{}) exifcommon.TagTypePrimitive

GetEncodingType returns the largest type that this tag's value can occupy.

func (*IndexedTag) Is

func (it *IndexedTag) Is(ifdPath string, id uint16) bool

Is returns true if this tag matched the given tag ID.

func (*IndexedTag) IsName

func (it *IndexedTag) IsName(ifdPath, name string) bool

IsName returns true if this tag matches the given tag name.

func (*IndexedTag) String

func (it *IndexedTag) String() string

String returns a descriptive string.

type MiscellaneousExifData

MiscellaneousExifData is reports additional data collected during the parse.

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

func (*MiscellaneousExifData) UnknownTags

func (med *MiscellaneousExifData) UnknownTags() map[exifcommon.BasicTag]exifcommon.BasicTag

UnknownTags returns the unknown tags encountered during the scan.

type ParsedTagVisitor

ParsedTagVisitor is a callback used if wanting to visit through all tags and child IFDs from the current IFD and going down.

type ParsedTagVisitor func(*Ifd, *IfdTagEntry) error

type QueuedIfd

QueuedIfd is one IFD that has been identified but yet to be processed.

type QueuedIfd struct {
    IfdIdentity *exifcommon.IfdIdentity

    Offset uint32
    Parent *Ifd

    // ParentTagIndex is our tag position in the parent IFD, if we had a parent
    // (if `ParentIfd` is not nil and we weren't an IFD referenced as a sibling
    // instead of as a child).
    ParentTagIndex int
}

type ScanOptions

ScanOptions tweaks parser behavior/choices.

type ScanOptions struct {
}

type TagIndex

TagIndex is a tag-lookup facility.

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

func NewTagIndex

func NewTagIndex() *TagIndex

NewTagIndex returns a new TagIndex struct.

func (*TagIndex) Add

func (ti *TagIndex) Add(it *IndexedTag) (err error)

Add registers a new tag to be recognized during the parse.

func (*TagIndex) FindFirst

func (ti *TagIndex) FindFirst(id uint16, typeId exifcommon.TagTypePrimitive, ifdIdentities []*exifcommon.IfdIdentity) (it *IndexedTag, err error)

FindFirst looks for the given tag-ID in each of the given IFDs in the given order. If `fqIfdPaths` is `nil` then use a default search order. This defies the standard, which requires each tag to exist in certain IFDs. This is a contingency to make recommendations for malformed data.

Things *can* end badly here, in that the same tag-ID in different IFDs might describe different data and different ata-types, and our decode might then produce binary and non-printable data.

func (*TagIndex) Get

func (ti *TagIndex) Get(ii *exifcommon.IfdIdentity, id uint16) (it *IndexedTag, err error)

Get returns information about the non-IFD tag given a tag ID. `ifdPath` must not be fully-qualified.

func (*TagIndex) GetWithName

func (ti *TagIndex) GetWithName(ii *exifcommon.IfdIdentity, name string) (it *IndexedTag, err error)

GetWithName returns information about the non-IFD tag given a tag name.

func (*TagIndex) SetUniversalSearch

func (ti *TagIndex) SetUniversalSearch(flag bool)

SetUniversalSearch enables a fallback to matching tags under *any* IFD.

func (*TagIndex) UniversalSearch

func (ti *TagIndex) UniversalSearch() bool

UniversalSearch enables a fallback to matching tags under *any* IFD.

type TagVisitorFn

TagVisitorFn is called for each tag when enumerating through the EXIF.

type TagVisitorFn func(ite *IfdTagEntry) (err error)

Subdirectories

Name Synopsis
..
command
exif-read-tool This tool dumps EXIF information from images.
common
undefined