summaryrefslogtreecommitdiff
path: root/vendor/github.com/dsoprea/go-exif/v3/utility.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/dsoprea/go-exif/v3/utility.go')
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/utility.go237
1 files changed, 237 insertions, 0 deletions
diff --git a/vendor/github.com/dsoprea/go-exif/v3/utility.go b/vendor/github.com/dsoprea/go-exif/v3/utility.go
new file mode 100644
index 000000000..f0b5e6383
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-exif/v3/utility.go
@@ -0,0 +1,237 @@
+package exif
+
+import (
+ "fmt"
+ "io"
+ "math"
+
+ "github.com/dsoprea/go-logging"
+ "github.com/dsoprea/go-utility/v2/filesystem"
+
+ "github.com/dsoprea/go-exif/v3/common"
+ "github.com/dsoprea/go-exif/v3/undefined"
+)
+
+var (
+ utilityLogger = log.NewLogger("exif.utility")
+)
+
+// 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"`
+}
+
+// String returns a string representation.
+func (et ExifTag) String() string {
+ return fmt.Sprintf(
+ "ExifTag<"+
+ "IFD-PATH=[%s] "+
+ "TAG-ID=(0x%02x) "+
+ "TAG-NAME=[%s] "+
+ "TAG-TYPE=[%s] "+
+ "VALUE=[%v] "+
+ "VALUE-BYTES=(%d) "+
+ "CHILD-IFD-PATH=[%s]",
+ et.IfdPath, et.TagId, et.TagName, et.TagTypeName, et.FormattedFirst,
+ len(et.ValueBytes), et.ChildIfdPath)
+}
+
+// GetFlatExifData returns a simple, flat representation of all tags.
+func GetFlatExifData(exifData []byte, so *ScanOptions) (exifTags []ExifTag, med *MiscellaneousExifData, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ sb := rifs.NewSeekableBufferWithBytes(exifData)
+
+ exifTags, med, err = getFlatExifDataUniversalSearchWithReadSeeker(sb, so, false)
+ log.PanicIf(err)
+
+ return exifTags, med, nil
+}
+
+// RELEASE(dustin): GetFlatExifDataUniversalSearch is a kludge to allow univeral tag searching in a backwards-compatible manner. For the next release, undo this and simply add the flag to GetFlatExifData.
+
+// GetFlatExifDataUniversalSearch returns a simple, flat representation of all
+// tags.
+func GetFlatExifDataUniversalSearch(exifData []byte, so *ScanOptions, doUniversalSearch bool) (exifTags []ExifTag, med *MiscellaneousExifData, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ sb := rifs.NewSeekableBufferWithBytes(exifData)
+
+ exifTags, med, err = getFlatExifDataUniversalSearchWithReadSeeker(sb, so, doUniversalSearch)
+ log.PanicIf(err)
+
+ return exifTags, med, nil
+}
+
+// RELEASE(dustin): GetFlatExifDataUniversalSearchWithReadSeeker is a kludge to allow using a ReadSeeker in a backwards-compatible manner. For the next release, drop this and refactor GetFlatExifDataUniversalSearch to take a ReadSeeker.
+
+// GetFlatExifDataUniversalSearchWithReadSeeker returns a simple, flat
+// representation of all tags given a ReadSeeker.
+func GetFlatExifDataUniversalSearchWithReadSeeker(rs io.ReadSeeker, so *ScanOptions, doUniversalSearch bool) (exifTags []ExifTag, med *MiscellaneousExifData, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ exifTags, med, err = getFlatExifDataUniversalSearchWithReadSeeker(rs, so, doUniversalSearch)
+ log.PanicIf(err)
+
+ return exifTags, med, nil
+}
+
+// getFlatExifDataUniversalSearchWithReadSeeker returns a simple, flat
+// representation of all tags given a ReadSeeker.
+func getFlatExifDataUniversalSearchWithReadSeeker(rs io.ReadSeeker, so *ScanOptions, doUniversalSearch bool) (exifTags []ExifTag, med *MiscellaneousExifData, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ headerData := make([]byte, ExifSignatureLength)
+ if _, err = io.ReadFull(rs, headerData); err != nil {
+ if err == io.EOF {
+ return nil, nil, err
+ }
+
+ log.Panic(err)
+ }
+
+ eh, err := ParseExifHeader(headerData)
+ log.PanicIf(err)
+
+ im, err := exifcommon.NewIfdMappingWithStandard()
+ log.PanicIf(err)
+
+ ti := NewTagIndex()
+
+ if doUniversalSearch == true {
+ ti.SetUniversalSearch(true)
+ }
+
+ ebs := NewExifReadSeeker(rs)
+ ie := NewIfdEnumerate(im, ti, ebs, eh.ByteOrder)
+
+ exifTags = make([]ExifTag, 0)
+
+ visitor := func(ite *IfdTagEntry) (err error) {
+ // This encodes down to base64. Since this an example tool and we do not
+ // expect to ever decode the output, we are not worried about
+ // specifically base64-encoding it in order to have a measure of
+ // control.
+ valueBytes, err := ite.GetRawBytes()
+ if err != nil {
+ if err == exifundefined.ErrUnparseableValue {
+ return nil
+ }
+
+ log.Panic(err)
+ }
+
+ value, err := ite.Value()
+ if err != nil {
+ if err == exifcommon.ErrUnhandledUndefinedTypedTag {
+ value = exifundefined.UnparseableUnknownTagValuePlaceholder
+ } else if log.Is(err, exifcommon.ErrParseFail) == true {
+ utilityLogger.Warningf(nil,
+ "Could not parse value for tag [%s] (%04x) [%s].",
+ ite.IfdPath(), ite.TagId(), ite.TagName())
+
+ return nil
+ } else {
+ log.Panic(err)
+ }
+ }
+
+ et := ExifTag{
+ IfdPath: ite.IfdPath(),
+ TagId: ite.TagId(),
+ TagName: ite.TagName(),
+ UnitCount: ite.UnitCount(),
+ TagTypeId: ite.TagType(),
+ TagTypeName: ite.TagType().String(),
+ Value: value,
+ ValueBytes: valueBytes,
+ ChildIfdPath: ite.ChildIfdPath(),
+ }
+
+ et.Formatted, err = ite.Format()
+ log.PanicIf(err)
+
+ et.FormattedFirst, err = ite.FormatFirst()
+ log.PanicIf(err)
+
+ exifTags = append(exifTags, et)
+
+ return nil
+ }
+
+ med, err = ie.Scan(exifcommon.IfdStandardIfdIdentity, eh.FirstIfdOffset, visitor, nil)
+ log.PanicIf(err)
+
+ return exifTags, med, nil
+}
+
+// GpsDegreesEquals returns true if the two `GpsDegrees` are identical.
+func GpsDegreesEquals(gi1, gi2 GpsDegrees) bool {
+ if gi2.Orientation != gi1.Orientation {
+ return false
+ }
+
+ degreesRightBound := math.Nextafter(gi1.Degrees, gi1.Degrees+1)
+ minutesRightBound := math.Nextafter(gi1.Minutes, gi1.Minutes+1)
+ secondsRightBound := math.Nextafter(gi1.Seconds, gi1.Seconds+1)
+
+ if gi2.Degrees < gi1.Degrees || gi2.Degrees >= degreesRightBound {
+ return false
+ } else if gi2.Minutes < gi1.Minutes || gi2.Minutes >= minutesRightBound {
+ return false
+ } else if gi2.Seconds < gi1.Seconds || gi2.Seconds >= secondsRightBound {
+ return false
+ }
+
+ return true
+}