1
0
Fork 0
mirror of https://github.com/vbatts/freezing-octo-hipster.git synced 2025-07-03 07:48:29 +00:00

go*: one go module for the repo, no more nested

Signed-off-by: Vincent Batts <vbatts@hashbangbash.com>
This commit is contained in:
Vincent Batts 2024-04-26 19:34:55 +00:00
parent a788e9ac95
commit 4ab3be9bc6
Signed by: vbatts
GPG key ID: E30EFAA812C6E5ED
632 changed files with 153930 additions and 133148 deletions

24
vendor/github.com/rwcarlsen/goexif/LICENSE generated vendored Normal file
View file

@ -0,0 +1,24 @@
Copyright (c) 2012, Robert Carlsen & Contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

4
vendor/github.com/rwcarlsen/goexif/exif/README.md generated vendored Normal file
View file

@ -0,0 +1,4 @@
To regenerate the regression test data, run `go generate` inside the exif
package directory and commit the changes to *regress_expected_test.go*.

655
vendor/github.com/rwcarlsen/goexif/exif/exif.go generated vendored Normal file
View file

@ -0,0 +1,655 @@
// Package exif implements decoding of EXIF data as defined in the EXIF 2.2
// specification (http://www.exif.org/Exif2-2.PDF).
package exif
import (
"bufio"
"bytes"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"math"
"strconv"
"strings"
"time"
"github.com/rwcarlsen/goexif/tiff"
)
const (
jpeg_APP1 = 0xE1
exifPointer = 0x8769
gpsPointer = 0x8825
interopPointer = 0xA005
)
// A decodeError is returned when the image cannot be decoded as a tiff image.
type decodeError struct {
cause error
}
func (de decodeError) Error() string {
return fmt.Sprintf("exif: decode failed (%v) ", de.cause.Error())
}
// IsShortReadTagValueError identifies a ErrShortReadTagValue error.
func IsShortReadTagValueError(err error) bool {
de, ok := err.(decodeError)
if ok {
return de.cause == tiff.ErrShortReadTagValue
}
return false
}
// A TagNotPresentError is returned when the requested field is not
// present in the EXIF.
type TagNotPresentError FieldName
func (tag TagNotPresentError) Error() string {
return fmt.Sprintf("exif: tag %q is not present", string(tag))
}
func IsTagNotPresentError(err error) bool {
_, ok := err.(TagNotPresentError)
return ok
}
// Parser allows the registration of custom parsing and field loading
// in the Decode function.
type Parser interface {
// Parse should read data from x and insert parsed fields into x via
// LoadTags.
Parse(x *Exif) error
}
var parsers []Parser
func init() {
RegisterParsers(&parser{})
}
// RegisterParsers registers one or more parsers to be automatically called
// when decoding EXIF data via the Decode function.
func RegisterParsers(ps ...Parser) {
parsers = append(parsers, ps...)
}
type parser struct{}
type tiffErrors map[tiffError]string
func (te tiffErrors) Error() string {
var allErrors []string
for k, v := range te {
allErrors = append(allErrors, fmt.Sprintf("%s: %v\n", stagePrefix[k], v))
}
return strings.Join(allErrors, "\n")
}
// IsCriticalError, given the error returned by Decode, reports whether the
// returned *Exif may contain usable information.
func IsCriticalError(err error) bool {
_, ok := err.(tiffErrors)
return !ok
}
// IsExifError reports whether the error happened while decoding the EXIF
// sub-IFD.
func IsExifError(err error) bool {
if te, ok := err.(tiffErrors); ok {
_, isExif := te[loadExif]
return isExif
}
return false
}
// IsGPSError reports whether the error happened while decoding the GPS sub-IFD.
func IsGPSError(err error) bool {
if te, ok := err.(tiffErrors); ok {
_, isGPS := te[loadExif]
return isGPS
}
return false
}
// IsInteroperabilityError reports whether the error happened while decoding the
// Interoperability sub-IFD.
func IsInteroperabilityError(err error) bool {
if te, ok := err.(tiffErrors); ok {
_, isInterop := te[loadInteroperability]
return isInterop
}
return false
}
type tiffError int
const (
loadExif tiffError = iota
loadGPS
loadInteroperability
)
var stagePrefix = map[tiffError]string{
loadExif: "loading EXIF sub-IFD",
loadGPS: "loading GPS sub-IFD",
loadInteroperability: "loading Interoperability sub-IFD",
}
// Parse reads data from the tiff data in x and populates the tags
// in x. If parsing a sub-IFD fails, the error is recorded and
// parsing continues with the remaining sub-IFDs.
func (p *parser) Parse(x *Exif) error {
if len(x.Tiff.Dirs) == 0 {
return errors.New("Invalid exif data")
}
x.LoadTags(x.Tiff.Dirs[0], exifFields, false)
// thumbnails
if len(x.Tiff.Dirs) >= 2 {
x.LoadTags(x.Tiff.Dirs[1], thumbnailFields, false)
}
te := make(tiffErrors)
// recurse into exif, gps, and interop sub-IFDs
if err := loadSubDir(x, ExifIFDPointer, exifFields); err != nil {
te[loadExif] = err.Error()
}
if err := loadSubDir(x, GPSInfoIFDPointer, gpsFields); err != nil {
te[loadGPS] = err.Error()
}
if err := loadSubDir(x, InteroperabilityIFDPointer, interopFields); err != nil {
te[loadInteroperability] = err.Error()
}
if len(te) > 0 {
return te
}
return nil
}
func loadSubDir(x *Exif, ptr FieldName, fieldMap map[uint16]FieldName) error {
r := bytes.NewReader(x.Raw)
tag, err := x.Get(ptr)
if err != nil {
return nil
}
offset, err := tag.Int64(0)
if err != nil {
return nil
}
_, err = r.Seek(offset, 0)
if err != nil {
return fmt.Errorf("exif: seek to sub-IFD %s failed: %v", ptr, err)
}
subDir, _, err := tiff.DecodeDir(r, x.Tiff.Order)
if err != nil {
return fmt.Errorf("exif: sub-IFD %s decode failed: %v", ptr, err)
}
x.LoadTags(subDir, fieldMap, false)
return nil
}
// Exif provides access to decoded EXIF metadata fields and values.
type Exif struct {
Tiff *tiff.Tiff
main map[FieldName]*tiff.Tag
Raw []byte
}
// Decode parses EXIF data from r (a TIFF, JPEG, or raw EXIF block)
// and returns a queryable Exif object. After the EXIF data section is
// called and the TIFF structure is decoded, each registered parser is
// called (in order of registration). If one parser returns an error,
// decoding terminates and the remaining parsers are not called.
//
// The error can be inspected with functions such as IsCriticalError
// to determine whether the returned object might still be usable.
func Decode(r io.Reader) (*Exif, error) {
// EXIF data in JPEG is stored in the APP1 marker. EXIF data uses the TIFF
// format to store data.
// If we're parsing a TIFF image, we don't need to strip away any data.
// If we're parsing a JPEG image, we need to strip away the JPEG APP1
// marker and also the EXIF header.
header := make([]byte, 4)
n, err := io.ReadFull(r, header)
if err != nil {
return nil, fmt.Errorf("exif: error reading 4 byte header, got %d, %v", n, err)
}
var isTiff bool
var isRawExif bool
var assumeJPEG bool
switch string(header) {
case "II*\x00":
// TIFF - Little endian (Intel)
isTiff = true
case "MM\x00*":
// TIFF - Big endian (Motorola)
isTiff = true
case "Exif":
isRawExif = true
default:
// Not TIFF, assume JPEG
assumeJPEG = true
}
// Put the header bytes back into the reader.
r = io.MultiReader(bytes.NewReader(header), r)
var (
er *bytes.Reader
tif *tiff.Tiff
sec *appSec
)
switch {
case isRawExif:
var header [6]byte
if _, err := io.ReadFull(r, header[:]); err != nil {
return nil, fmt.Errorf("exif: unexpected raw exif header read error")
}
if got, want := string(header[:]), "Exif\x00\x00"; got != want {
return nil, fmt.Errorf("exif: unexpected raw exif header; got %q, want %q", got, want)
}
fallthrough
case isTiff:
// Functions below need the IFDs from the TIFF data to be stored in a
// *bytes.Reader. We use TeeReader to get a copy of the bytes as a
// side-effect of tiff.Decode() doing its work.
b := &bytes.Buffer{}
tr := io.TeeReader(r, b)
tif, err = tiff.Decode(tr)
er = bytes.NewReader(b.Bytes())
case assumeJPEG:
// Locate the JPEG APP1 header.
sec, err = newAppSec(jpeg_APP1, r)
if err != nil {
return nil, err
}
// Strip away EXIF header.
er, err = sec.exifReader()
if err != nil {
return nil, err
}
tif, err = tiff.Decode(er)
}
if err != nil {
return nil, decodeError{cause: err}
}
er.Seek(0, 0)
raw, err := ioutil.ReadAll(er)
if err != nil {
return nil, decodeError{cause: err}
}
// build an exif structure from the tiff
x := &Exif{
main: map[FieldName]*tiff.Tag{},
Tiff: tif,
Raw: raw,
}
for i, p := range parsers {
if err := p.Parse(x); err != nil {
if _, ok := err.(tiffErrors); ok {
return x, err
}
// This should never happen, as Parse always returns a tiffError
// for now, but that could change.
return x, fmt.Errorf("exif: parser %v failed (%v)", i, err)
}
}
return x, nil
}
// LoadTags loads tags into the available fields from the tiff Directory
// using the given tagid-fieldname mapping. Used to load makernote and
// other meta-data. If showMissing is true, tags in d that are not in the
// fieldMap will be loaded with the FieldName UnknownPrefix followed by the
// tag ID (in hex format).
func (x *Exif) LoadTags(d *tiff.Dir, fieldMap map[uint16]FieldName, showMissing bool) {
for _, tag := range d.Tags {
name := fieldMap[tag.Id]
if name == "" {
if !showMissing {
continue
}
name = FieldName(fmt.Sprintf("%v%x", UnknownPrefix, tag.Id))
}
x.main[name] = tag
}
}
// Get retrieves the EXIF tag for the given field name.
//
// If the tag is not known or not present, an error is returned. If the
// tag name is known, the error will be a TagNotPresentError.
func (x *Exif) Get(name FieldName) (*tiff.Tag, error) {
if tg, ok := x.main[name]; ok {
return tg, nil
}
return nil, TagNotPresentError(name)
}
// Walker is the interface used to traverse all fields of an Exif object.
type Walker interface {
// Walk is called for each non-nil EXIF field. Returning a non-nil
// error aborts the walk/traversal.
Walk(name FieldName, tag *tiff.Tag) error
}
// Walk calls the Walk method of w with the name and tag for every non-nil
// EXIF field. If w aborts the walk with an error, that error is returned.
func (x *Exif) Walk(w Walker) error {
for name, tag := range x.main {
if err := w.Walk(name, tag); err != nil {
return err
}
}
return nil
}
// DateTime returns the EXIF's "DateTimeOriginal" field, which
// is the creation time of the photo. If not found, it tries
// the "DateTime" (which is meant as the modtime) instead.
// The error will be TagNotPresentErr if none of those tags
// were found, or a generic error if the tag value was
// not a string, or the error returned by time.Parse.
//
// If the EXIF lacks timezone information or GPS time, the returned
// time's Location will be time.Local.
func (x *Exif) DateTime() (time.Time, error) {
var dt time.Time
tag, err := x.Get(DateTimeOriginal)
if err != nil {
tag, err = x.Get(DateTime)
if err != nil {
return dt, err
}
}
if tag.Format() != tiff.StringVal {
return dt, errors.New("DateTime[Original] not in string format")
}
exifTimeLayout := "2006:01:02 15:04:05"
dateStr := strings.TrimRight(string(tag.Val), "\x00")
// TODO(bradfitz,mpl): look for timezone offset, GPS time, etc.
timeZone := time.Local
if tz, _ := x.TimeZone(); tz != nil {
timeZone = tz
}
return time.ParseInLocation(exifTimeLayout, dateStr, timeZone)
}
func (x *Exif) TimeZone() (*time.Location, error) {
// TODO: parse more timezone fields (e.g. Nikon WorldTime).
timeInfo, err := x.Get("Canon.TimeInfo")
if err != nil {
return nil, err
}
if timeInfo.Count < 2 {
return nil, errors.New("Canon.TimeInfo does not contain timezone")
}
offsetMinutes, err := timeInfo.Int(1)
if err != nil {
return nil, err
}
return time.FixedZone("", offsetMinutes*60), nil
}
func ratFloat(num, dem int64) float64 {
return float64(num) / float64(dem)
}
// Tries to parse a Geo degrees value from a string as it was found in some
// EXIF data.
// Supported formats so far:
// - "52,00000,50,00000,34,01180" ==> 52 deg 50'34.0118"
// Probably due to locale the comma is used as decimal mark as well as the
// separator of three floats (degrees, minutes, seconds)
// http://en.wikipedia.org/wiki/Decimal_mark#Hindu.E2.80.93Arabic_numeral_system
// - "52.0,50.0,34.01180" ==> 52deg50'34.0118"
// - "52,50,34.01180" ==> 52deg50'34.0118"
func parseTagDegreesString(s string) (float64, error) {
const unparsableErrorFmt = "Unknown coordinate format: %s"
isSplitRune := func(c rune) bool {
return c == ',' || c == ';'
}
parts := strings.FieldsFunc(s, isSplitRune)
var degrees, minutes, seconds float64
var err error
switch len(parts) {
case 6:
degrees, err = strconv.ParseFloat(parts[0]+"."+parts[1], 64)
if err != nil {
return 0.0, fmt.Errorf(unparsableErrorFmt, s)
}
minutes, err = strconv.ParseFloat(parts[2]+"."+parts[3], 64)
if err != nil {
return 0.0, fmt.Errorf(unparsableErrorFmt, s)
}
minutes = math.Copysign(minutes, degrees)
seconds, err = strconv.ParseFloat(parts[4]+"."+parts[5], 64)
if err != nil {
return 0.0, fmt.Errorf(unparsableErrorFmt, s)
}
seconds = math.Copysign(seconds, degrees)
case 3:
degrees, err = strconv.ParseFloat(parts[0], 64)
if err != nil {
return 0.0, fmt.Errorf(unparsableErrorFmt, s)
}
minutes, err = strconv.ParseFloat(parts[1], 64)
if err != nil {
return 0.0, fmt.Errorf(unparsableErrorFmt, s)
}
minutes = math.Copysign(minutes, degrees)
seconds, err = strconv.ParseFloat(parts[2], 64)
if err != nil {
return 0.0, fmt.Errorf(unparsableErrorFmt, s)
}
seconds = math.Copysign(seconds, degrees)
default:
return 0.0, fmt.Errorf(unparsableErrorFmt, s)
}
return degrees + minutes/60.0 + seconds/3600.0, nil
}
func parse3Rat2(tag *tiff.Tag) ([3]float64, error) {
v := [3]float64{}
for i := range v {
num, den, err := tag.Rat2(i)
if err != nil {
return v, err
}
v[i] = ratFloat(num, den)
if tag.Count < uint32(i+2) {
break
}
}
return v, nil
}
func tagDegrees(tag *tiff.Tag) (float64, error) {
switch tag.Format() {
case tiff.RatVal:
// The usual case, according to the Exif spec
// (http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf,
// sec 4.6.6, p. 52 et seq.)
v, err := parse3Rat2(tag)
if err != nil {
return 0.0, err
}
return v[0] + v[1]/60 + v[2]/3600.0, nil
case tiff.StringVal:
// Encountered this weird case with a panorama picture taken with a HTC phone
s, err := tag.StringVal()
if err != nil {
return 0.0, err
}
return parseTagDegreesString(s)
default:
// don't know how to parse value, give up
return 0.0, fmt.Errorf("Malformed EXIF Tag Degrees")
}
}
// LatLong returns the latitude and longitude of the photo and
// whether it was present.
func (x *Exif) LatLong() (lat, long float64, err error) {
// All calls of x.Get might return an TagNotPresentError
longTag, err := x.Get(FieldName("GPSLongitude"))
if err != nil {
return
}
ewTag, err := x.Get(FieldName("GPSLongitudeRef"))
if err != nil {
return
}
latTag, err := x.Get(FieldName("GPSLatitude"))
if err != nil {
return
}
nsTag, err := x.Get(FieldName("GPSLatitudeRef"))
if err != nil {
return
}
if long, err = tagDegrees(longTag); err != nil {
return 0, 0, fmt.Errorf("Cannot parse longitude: %v", err)
}
if lat, err = tagDegrees(latTag); err != nil {
return 0, 0, fmt.Errorf("Cannot parse latitude: %v", err)
}
ew, err := ewTag.StringVal()
if err == nil && ew == "W" {
long *= -1.0
} else if err != nil {
return 0, 0, fmt.Errorf("Cannot parse longitude: %v", err)
}
ns, err := nsTag.StringVal()
if err == nil && ns == "S" {
lat *= -1.0
} else if err != nil {
return 0, 0, fmt.Errorf("Cannot parse longitude: %v", err)
}
return lat, long, nil
}
// String returns a pretty text representation of the decoded exif data.
func (x *Exif) String() string {
var buf bytes.Buffer
for name, tag := range x.main {
fmt.Fprintf(&buf, "%s: %s\n", name, tag)
}
return buf.String()
}
// JpegThumbnail returns the jpeg thumbnail if it exists. If it doesn't exist,
// TagNotPresentError will be returned
func (x *Exif) JpegThumbnail() ([]byte, error) {
offset, err := x.Get(ThumbJPEGInterchangeFormat)
if err != nil {
return nil, err
}
start, err := offset.Int(0)
if err != nil {
return nil, err
}
length, err := x.Get(ThumbJPEGInterchangeFormatLength)
if err != nil {
return nil, err
}
l, err := length.Int(0)
if err != nil {
return nil, err
}
return x.Raw[start : start+l], nil
}
// MarshalJson implements the encoding/json.Marshaler interface providing output of
// all EXIF fields present (names and values).
func (x Exif) MarshalJSON() ([]byte, error) {
return json.Marshal(x.main)
}
type appSec struct {
marker byte
data []byte
}
// newAppSec finds marker in r and returns the corresponding application data
// section.
func newAppSec(marker byte, r io.Reader) (*appSec, error) {
br := bufio.NewReader(r)
app := &appSec{marker: marker}
var dataLen int
// seek to marker
for dataLen == 0 {
if _, err := br.ReadBytes(0xFF); err != nil {
return nil, err
}
c, err := br.ReadByte()
if err != nil {
return nil, err
} else if c != marker {
continue
}
dataLenBytes := make([]byte, 2)
for k, _ := range dataLenBytes {
c, err := br.ReadByte()
if err != nil {
return nil, err
}
dataLenBytes[k] = c
}
dataLen = int(binary.BigEndian.Uint16(dataLenBytes)) - 2
}
// read section data
nread := 0
for nread < dataLen {
s := make([]byte, dataLen-nread)
n, err := br.Read(s)
nread += n
if err != nil && nread < dataLen {
return nil, err
}
app.data = append(app.data, s[:n]...)
}
return app, nil
}
// reader returns a reader on this appSec.
func (app *appSec) reader() *bytes.Reader {
return bytes.NewReader(app.data)
}
// exifReader returns a reader on this appSec with the read cursor advanced to
// the start of the exif's tiff encoded portion.
func (app *appSec) exifReader() (*bytes.Reader, error) {
if len(app.data) < 6 {
return nil, errors.New("exif: failed to find exif intro marker")
}
// read/check for exif special mark
exif := app.data[:6]
if !bytes.Equal(exif, append([]byte("Exif"), 0x00, 0x00)) {
return nil, errors.New("exif: failed to find exif intro marker")
}
return bytes.NewReader(app.data[6:]), nil
}

309
vendor/github.com/rwcarlsen/goexif/exif/fields.go generated vendored Normal file
View file

@ -0,0 +1,309 @@
package exif
type FieldName string
// UnknownPrefix is used as the first part of field names for decoded tags for
// which there is no known/supported EXIF field.
const UnknownPrefix = "UnknownTag_"
// Primary EXIF fields
const (
ImageWidth FieldName = "ImageWidth"
ImageLength FieldName = "ImageLength" // Image height called Length by EXIF spec
BitsPerSample FieldName = "BitsPerSample"
Compression FieldName = "Compression"
PhotometricInterpretation FieldName = "PhotometricInterpretation"
Orientation FieldName = "Orientation"
SamplesPerPixel FieldName = "SamplesPerPixel"
PlanarConfiguration FieldName = "PlanarConfiguration"
YCbCrSubSampling FieldName = "YCbCrSubSampling"
YCbCrPositioning FieldName = "YCbCrPositioning"
XResolution FieldName = "XResolution"
YResolution FieldName = "YResolution"
ResolutionUnit FieldName = "ResolutionUnit"
DateTime FieldName = "DateTime"
ImageDescription FieldName = "ImageDescription"
Make FieldName = "Make"
Model FieldName = "Model"
Software FieldName = "Software"
Artist FieldName = "Artist"
Copyright FieldName = "Copyright"
ExifIFDPointer FieldName = "ExifIFDPointer"
GPSInfoIFDPointer FieldName = "GPSInfoIFDPointer"
InteroperabilityIFDPointer FieldName = "InteroperabilityIFDPointer"
ExifVersion FieldName = "ExifVersion"
FlashpixVersion FieldName = "FlashpixVersion"
ColorSpace FieldName = "ColorSpace"
ComponentsConfiguration FieldName = "ComponentsConfiguration"
CompressedBitsPerPixel FieldName = "CompressedBitsPerPixel"
PixelXDimension FieldName = "PixelXDimension"
PixelYDimension FieldName = "PixelYDimension"
MakerNote FieldName = "MakerNote"
UserComment FieldName = "UserComment"
RelatedSoundFile FieldName = "RelatedSoundFile"
DateTimeOriginal FieldName = "DateTimeOriginal"
DateTimeDigitized FieldName = "DateTimeDigitized"
SubSecTime FieldName = "SubSecTime"
SubSecTimeOriginal FieldName = "SubSecTimeOriginal"
SubSecTimeDigitized FieldName = "SubSecTimeDigitized"
ImageUniqueID FieldName = "ImageUniqueID"
ExposureTime FieldName = "ExposureTime"
FNumber FieldName = "FNumber"
ExposureProgram FieldName = "ExposureProgram"
SpectralSensitivity FieldName = "SpectralSensitivity"
ISOSpeedRatings FieldName = "ISOSpeedRatings"
OECF FieldName = "OECF"
ShutterSpeedValue FieldName = "ShutterSpeedValue"
ApertureValue FieldName = "ApertureValue"
BrightnessValue FieldName = "BrightnessValue"
ExposureBiasValue FieldName = "ExposureBiasValue"
MaxApertureValue FieldName = "MaxApertureValue"
SubjectDistance FieldName = "SubjectDistance"
MeteringMode FieldName = "MeteringMode"
LightSource FieldName = "LightSource"
Flash FieldName = "Flash"
FocalLength FieldName = "FocalLength"
SubjectArea FieldName = "SubjectArea"
FlashEnergy FieldName = "FlashEnergy"
SpatialFrequencyResponse FieldName = "SpatialFrequencyResponse"
FocalPlaneXResolution FieldName = "FocalPlaneXResolution"
FocalPlaneYResolution FieldName = "FocalPlaneYResolution"
FocalPlaneResolutionUnit FieldName = "FocalPlaneResolutionUnit"
SubjectLocation FieldName = "SubjectLocation"
ExposureIndex FieldName = "ExposureIndex"
SensingMethod FieldName = "SensingMethod"
FileSource FieldName = "FileSource"
SceneType FieldName = "SceneType"
CFAPattern FieldName = "CFAPattern"
CustomRendered FieldName = "CustomRendered"
ExposureMode FieldName = "ExposureMode"
WhiteBalance FieldName = "WhiteBalance"
DigitalZoomRatio FieldName = "DigitalZoomRatio"
FocalLengthIn35mmFilm FieldName = "FocalLengthIn35mmFilm"
SceneCaptureType FieldName = "SceneCaptureType"
GainControl FieldName = "GainControl"
Contrast FieldName = "Contrast"
Saturation FieldName = "Saturation"
Sharpness FieldName = "Sharpness"
DeviceSettingDescription FieldName = "DeviceSettingDescription"
SubjectDistanceRange FieldName = "SubjectDistanceRange"
LensMake FieldName = "LensMake"
LensModel FieldName = "LensModel"
)
// Windows-specific tags
const (
XPTitle FieldName = "XPTitle"
XPComment FieldName = "XPComment"
XPAuthor FieldName = "XPAuthor"
XPKeywords FieldName = "XPKeywords"
XPSubject FieldName = "XPSubject"
)
// thumbnail fields
const (
ThumbJPEGInterchangeFormat FieldName = "ThumbJPEGInterchangeFormat" // offset to thumb jpeg SOI
ThumbJPEGInterchangeFormatLength FieldName = "ThumbJPEGInterchangeFormatLength" // byte length of thumb
)
// GPS fields
const (
GPSVersionID FieldName = "GPSVersionID"
GPSLatitudeRef FieldName = "GPSLatitudeRef"
GPSLatitude FieldName = "GPSLatitude"
GPSLongitudeRef FieldName = "GPSLongitudeRef"
GPSLongitude FieldName = "GPSLongitude"
GPSAltitudeRef FieldName = "GPSAltitudeRef"
GPSAltitude FieldName = "GPSAltitude"
GPSTimeStamp FieldName = "GPSTimeStamp"
GPSSatelites FieldName = "GPSSatelites"
GPSStatus FieldName = "GPSStatus"
GPSMeasureMode FieldName = "GPSMeasureMode"
GPSDOP FieldName = "GPSDOP"
GPSSpeedRef FieldName = "GPSSpeedRef"
GPSSpeed FieldName = "GPSSpeed"
GPSTrackRef FieldName = "GPSTrackRef"
GPSTrack FieldName = "GPSTrack"
GPSImgDirectionRef FieldName = "GPSImgDirectionRef"
GPSImgDirection FieldName = "GPSImgDirection"
GPSMapDatum FieldName = "GPSMapDatum"
GPSDestLatitudeRef FieldName = "GPSDestLatitudeRef"
GPSDestLatitude FieldName = "GPSDestLatitude"
GPSDestLongitudeRef FieldName = "GPSDestLongitudeRef"
GPSDestLongitude FieldName = "GPSDestLongitude"
GPSDestBearingRef FieldName = "GPSDestBearingRef"
GPSDestBearing FieldName = "GPSDestBearing"
GPSDestDistanceRef FieldName = "GPSDestDistanceRef"
GPSDestDistance FieldName = "GPSDestDistance"
GPSProcessingMethod FieldName = "GPSProcessingMethod"
GPSAreaInformation FieldName = "GPSAreaInformation"
GPSDateStamp FieldName = "GPSDateStamp"
GPSDifferential FieldName = "GPSDifferential"
)
// interoperability fields
const (
InteroperabilityIndex FieldName = "InteroperabilityIndex"
)
var exifFields = map[uint16]FieldName{
/////////////////////////////////////
////////// IFD 0 ////////////////////
/////////////////////////////////////
// image data structure for the thumbnail
0x0100: ImageWidth,
0x0101: ImageLength,
0x0102: BitsPerSample,
0x0103: Compression,
0x0106: PhotometricInterpretation,
0x0112: Orientation,
0x0115: SamplesPerPixel,
0x011C: PlanarConfiguration,
0x0212: YCbCrSubSampling,
0x0213: YCbCrPositioning,
0x011A: XResolution,
0x011B: YResolution,
0x0128: ResolutionUnit,
// Other tags
0x0132: DateTime,
0x010E: ImageDescription,
0x010F: Make,
0x0110: Model,
0x0131: Software,
0x013B: Artist,
0x8298: Copyright,
// Windows-specific tags
0x9c9b: XPTitle,
0x9c9c: XPComment,
0x9c9d: XPAuthor,
0x9c9e: XPKeywords,
0x9c9f: XPSubject,
// private tags
exifPointer: ExifIFDPointer,
/////////////////////////////////////
////////// Exif sub IFD /////////////
/////////////////////////////////////
gpsPointer: GPSInfoIFDPointer,
interopPointer: InteroperabilityIFDPointer,
0x9000: ExifVersion,
0xA000: FlashpixVersion,
0xA001: ColorSpace,
0x9101: ComponentsConfiguration,
0x9102: CompressedBitsPerPixel,
0xA002: PixelXDimension,
0xA003: PixelYDimension,
0x927C: MakerNote,
0x9286: UserComment,
0xA004: RelatedSoundFile,
0x9003: DateTimeOriginal,
0x9004: DateTimeDigitized,
0x9290: SubSecTime,
0x9291: SubSecTimeOriginal,
0x9292: SubSecTimeDigitized,
0xA420: ImageUniqueID,
// picture conditions
0x829A: ExposureTime,
0x829D: FNumber,
0x8822: ExposureProgram,
0x8824: SpectralSensitivity,
0x8827: ISOSpeedRatings,
0x8828: OECF,
0x9201: ShutterSpeedValue,
0x9202: ApertureValue,
0x9203: BrightnessValue,
0x9204: ExposureBiasValue,
0x9205: MaxApertureValue,
0x9206: SubjectDistance,
0x9207: MeteringMode,
0x9208: LightSource,
0x9209: Flash,
0x920A: FocalLength,
0x9214: SubjectArea,
0xA20B: FlashEnergy,
0xA20C: SpatialFrequencyResponse,
0xA20E: FocalPlaneXResolution,
0xA20F: FocalPlaneYResolution,
0xA210: FocalPlaneResolutionUnit,
0xA214: SubjectLocation,
0xA215: ExposureIndex,
0xA217: SensingMethod,
0xA300: FileSource,
0xA301: SceneType,
0xA302: CFAPattern,
0xA401: CustomRendered,
0xA402: ExposureMode,
0xA403: WhiteBalance,
0xA404: DigitalZoomRatio,
0xA405: FocalLengthIn35mmFilm,
0xA406: SceneCaptureType,
0xA407: GainControl,
0xA408: Contrast,
0xA409: Saturation,
0xA40A: Sharpness,
0xA40B: DeviceSettingDescription,
0xA40C: SubjectDistanceRange,
0xA433: LensMake,
0xA434: LensModel,
}
var gpsFields = map[uint16]FieldName{
/////////////////////////////////////
//// GPS sub-IFD ////////////////////
/////////////////////////////////////
0x0: GPSVersionID,
0x1: GPSLatitudeRef,
0x2: GPSLatitude,
0x3: GPSLongitudeRef,
0x4: GPSLongitude,
0x5: GPSAltitudeRef,
0x6: GPSAltitude,
0x7: GPSTimeStamp,
0x8: GPSSatelites,
0x9: GPSStatus,
0xA: GPSMeasureMode,
0xB: GPSDOP,
0xC: GPSSpeedRef,
0xD: GPSSpeed,
0xE: GPSTrackRef,
0xF: GPSTrack,
0x10: GPSImgDirectionRef,
0x11: GPSImgDirection,
0x12: GPSMapDatum,
0x13: GPSDestLatitudeRef,
0x14: GPSDestLatitude,
0x15: GPSDestLongitudeRef,
0x16: GPSDestLongitude,
0x17: GPSDestBearingRef,
0x18: GPSDestBearing,
0x19: GPSDestDistanceRef,
0x1A: GPSDestDistance,
0x1B: GPSProcessingMethod,
0x1C: GPSAreaInformation,
0x1D: GPSDateStamp,
0x1E: GPSDifferential,
}
var interopFields = map[uint16]FieldName{
/////////////////////////////////////
//// Interoperability sub-IFD ///////
/////////////////////////////////////
0x1: InteroperabilityIndex,
}
var thumbnailFields = map[uint16]FieldName{
0x0201: ThumbJPEGInterchangeFormat,
0x0202: ThumbJPEGInterchangeFormatLength,
}

BIN
vendor/github.com/rwcarlsen/goexif/exif/sample1.jpg generated vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

270
vendor/github.com/rwcarlsen/goexif/mknote/fields.go generated vendored Normal file
View file

@ -0,0 +1,270 @@
package mknote
import "github.com/rwcarlsen/goexif/exif"
// Useful resources used in creating these tables:
// http://www.exiv2.org/makernote.html
// http://www.exiv2.org/tags-canon.html
// http://www.exiv2.org/tags-nikon.html
// Known Maker Note fields
const (
// common fields
ISOSpeed exif.FieldName = "ISOSpeed"
ColorMode exif.FieldName = "ColorMode"
Quality exif.FieldName = "Quality"
Sharpening exif.FieldName = "Sharpening"
Focus exif.FieldName = "Focus"
FlashSetting exif.FieldName = "FlashSetting"
FlashDevice exif.FieldName = "FlashDevice"
WhiteBalanceBias exif.FieldName = "WhiteBalanceBias"
WB_RBLevels exif.FieldName = "WB_RBLevels"
ProgramShift exif.FieldName = "ProgramShift"
ExposureDiff exif.FieldName = "ExposureDiff"
ISOSelection exif.FieldName = "ISOSelection"
DataDump exif.FieldName = "DataDump"
Preview exif.FieldName = "Preview"
FlashComp exif.FieldName = "FlashComp"
ISOSettings exif.FieldName = "ISOSettings"
ImageBoundary exif.FieldName = "ImageBoundary"
FlashExposureComp exif.FieldName = "FlashExposureComp"
FlashBracketComp exif.FieldName = "FlashBracketComp"
ExposureBracketComp exif.FieldName = "ExposureBracketComp"
ImageProcessing exif.FieldName = "ImageProcessing"
CropHiSpeed exif.FieldName = "CropHiSpeed"
ExposureTuning exif.FieldName = "ExposureTuning"
SerialNumber exif.FieldName = "SerialNumber"
ImageAuthentication exif.FieldName = "ImageAuthentication"
ActiveDLighting exif.FieldName = "ActiveDLighting"
VignetteControl exif.FieldName = "VignetteControl"
ImageAdjustment exif.FieldName = "ImageAdjustment"
ToneComp exif.FieldName = "ToneComp"
AuxiliaryLens exif.FieldName = "AuxiliaryLens"
LensType exif.FieldName = "LensType"
Lens exif.FieldName = "Lens"
FocusDistance exif.FieldName = "FocusDistance"
DigitalZoom exif.FieldName = "DigitalZoom"
FlashMode exif.FieldName = "FlashMode"
ShootingMode exif.FieldName = "ShootingMode"
AutoBracketRelease exif.FieldName = "AutoBracketRelease"
LensFStops exif.FieldName = "LensFStops"
ContrastCurve exif.FieldName = "ContrastCurve"
ColorHue exif.FieldName = "ColorHue"
SceneMode exif.FieldName = "SceneMode"
HueAdjustment exif.FieldName = "HueAdjustment"
NEFCompression exif.FieldName = "NEFCompression"
NoiseReduction exif.FieldName = "NoiseReduction"
LinearizationTable exif.FieldName = "LinearizationTable"
RawImageCenter exif.FieldName = "RawImageCenter"
SensorPixelSize exif.FieldName = "SensorPixelSize"
SceneAssist exif.FieldName = "SceneAssist"
RetouchHistory exif.FieldName = "RetouchHistory"
ImageDataSize exif.FieldName = "ImageDataSize"
ImageCount exif.FieldName = "ImageCount"
DeletedImageCount exif.FieldName = "DeletedImageCount"
ShutterCount exif.FieldName = "ShutterCount"
ImageOptimization exif.FieldName = "ImageOptimization"
SaturationText exif.FieldName = "SaturationText"
VariProgram exif.FieldName = "VariProgram"
ImageStabilization exif.FieldName = "ImageStabilization"
AFResponse exif.FieldName = "AFResponse"
HighISONoiseReduction exif.FieldName = "HighISONoiseReduction"
ToningEffect exif.FieldName = "ToningEffect"
PrintIM exif.FieldName = "PrintIM"
CaptureData exif.FieldName = "CaptureData"
CaptureVersion exif.FieldName = "CaptureVersion"
CaptureOffsets exif.FieldName = "CaptureOffsets"
ScanIFD exif.FieldName = "ScanIFD"
ICCProfile exif.FieldName = "ICCProfile"
CaptureOutput exif.FieldName = "CaptureOutput"
Panorama exif.FieldName = "Panorama"
ImageType exif.FieldName = "ImageType"
FirmwareVersion exif.FieldName = "FirmwareVersion"
FileNumber exif.FieldName = "FileNumber"
OwnerName exif.FieldName = "OwnerName"
CameraInfo exif.FieldName = "CameraInfo"
CustomFunctions exif.FieldName = "CustomFunctions"
ModelID exif.FieldName = "ModelID"
PictureInfo exif.FieldName = "PictureInfo"
ThumbnailImageValidArea exif.FieldName = "ThumbnailImageValidArea"
SerialNumberFormat exif.FieldName = "SerialNumberFormat"
SuperMacro exif.FieldName = "SuperMacro"
OriginalDecisionDataOffset exif.FieldName = "OriginalDecisionDataOffset"
WhiteBalanceTable exif.FieldName = "WhiteBalanceTable"
LensModel exif.FieldName = "LensModel"
InternalSerialNumber exif.FieldName = "InternalSerialNumber"
DustRemovalData exif.FieldName = "DustRemovalData"
ProcessingInfo exif.FieldName = "ProcessingInfo"
MeasuredColor exif.FieldName = "MeasuredColor"
VRDOffset exif.FieldName = "VRDOffset"
SensorInfo exif.FieldName = "SensorInfo"
ColorData exif.FieldName = "ColorData"
// Nikon-specific fields
Nikon_Version exif.FieldName = "Nikon.Version"
Nikon_WhiteBalance exif.FieldName = "Nikon.WhiteBalance"
Nikon_ColorSpace exif.FieldName = "Nikon.ColorSpace"
Nikon_LightSource exif.FieldName = "Nikon.LightSource"
Nikon_Saturation exif.FieldName = "Nikon_Saturation"
Nikon_ShotInfo exif.FieldName = "Nikon.ShotInfo" // A sub-IFD
Nikon_VRInfo exif.FieldName = "Nikon.VRInfo" // A sub-IFD
Nikon_PictureControl exif.FieldName = "Nikon.PictureControl" // A sub-IFD
Nikon_WorldTime exif.FieldName = "Nikon.WorldTime" // A sub-IFD
Nikon_ISOInfo exif.FieldName = "Nikon.ISOInfo" // A sub-IFD
Nikon_AFInfo exif.FieldName = "Nikon.AFInfo" // A sub-IFD
Nikon_ColorBalance exif.FieldName = "Nikon.ColorBalance" // A sub-IFD
Nikon_LensData exif.FieldName = "Nikon.LensData" // A sub-IFD
Nikon_SerialNO exif.FieldName = "Nikon.SerialNO" // usually starts with "NO="
Nikon_FlashInfo exif.FieldName = "Nikon.FlashInfo" // A sub-IFD
Nikon_MultiExposure exif.FieldName = "Nikon.MultiExposure" // A sub-IFD
Nikon_AFInfo2 exif.FieldName = "Nikon.AFInfo2" // A sub-IFD
Nikon_FileInfo exif.FieldName = "Nikon.FileInfo" // A sub-IFD
Nikon_AFTune exif.FieldName = "Nikon.AFTune" // A sub-IFD
Nikon3_0x000a exif.FieldName = "Nikon3.0x000a"
Nikon3_0x009b exif.FieldName = "Nikon3.0x009b"
Nikon3_0x009f exif.FieldName = "Nikon3.0x009f"
Nikon3_0x00a3 exif.FieldName = "Nikon3.0x00a3"
// Canon-specific fiends
Canon_CameraSettings exif.FieldName = "Canon.CameraSettings" // A sub-IFD
Canon_ShotInfo exif.FieldName = "Canon.ShotInfo" // A sub-IFD
Canon_AFInfo exif.FieldName = "Canon.AFInfo"
Canon_TimeInfo exif.FieldName = "Canon.TimeInfo"
Canon_0x0000 exif.FieldName = "Canon.0x0000"
Canon_0x0003 exif.FieldName = "Canon.0x0003"
Canon_0x00b5 exif.FieldName = "Canon.0x00b5"
Canon_0x00c0 exif.FieldName = "Canon.0x00c0"
Canon_0x00c1 exif.FieldName = "Canon.0x00c1"
)
var makerNoteCanonFields = map[uint16]exif.FieldName{
0x0000: Canon_0x0000,
0x0001: Canon_CameraSettings,
0x0002: exif.FocalLength,
0x0003: Canon_0x0003,
0x0004: Canon_ShotInfo,
0x0005: Panorama,
0x0006: ImageType,
0x0007: FirmwareVersion,
0x0008: FileNumber,
0x0009: OwnerName,
0x000c: SerialNumber,
0x000d: CameraInfo,
0x000f: CustomFunctions,
0x0010: ModelID,
0x0012: PictureInfo,
0x0013: ThumbnailImageValidArea,
0x0015: SerialNumberFormat,
0x001a: SuperMacro,
0x0026: Canon_AFInfo,
0x0035: Canon_TimeInfo,
0x0083: OriginalDecisionDataOffset,
0x00a4: WhiteBalanceTable,
0x0095: LensModel,
0x0096: InternalSerialNumber,
0x0097: DustRemovalData,
0x0099: CustomFunctions,
0x00a0: ProcessingInfo,
0x00aa: MeasuredColor,
0x00b4: exif.ColorSpace,
0x00b5: Canon_0x00b5,
0x00c0: Canon_0x00c0,
0x00c1: Canon_0x00c1,
0x00d0: VRDOffset,
0x00e0: SensorInfo,
0x4001: ColorData,
}
// Nikon version 3 Maker Notes fields (used by E5400, SQ, D2H, D70, and newer)
var makerNoteNikon3Fields = map[uint16]exif.FieldName{
0x0001: Nikon_Version,
0x0002: ISOSpeed,
0x0003: ColorMode,
0x0004: Quality,
0x0005: Nikon_WhiteBalance,
0x0006: Sharpening,
0x0007: Focus,
0x0008: FlashSetting,
0x0009: FlashDevice,
0x000a: Nikon3_0x000a,
0x000b: WhiteBalanceBias,
0x000c: WB_RBLevels,
0x000d: ProgramShift,
0x000e: ExposureDiff,
0x000f: ISOSelection,
0x0010: DataDump,
0x0011: Preview,
0x0012: FlashComp,
0x0013: ISOSettings,
0x0016: ImageBoundary,
0x0017: FlashExposureComp,
0x0018: FlashBracketComp,
0x0019: ExposureBracketComp,
0x001a: ImageProcessing,
0x001b: CropHiSpeed,
0x001c: ExposureTuning,
0x001d: SerialNumber,
0x001e: Nikon_ColorSpace,
0x001f: Nikon_VRInfo,
0x0020: ImageAuthentication,
0x0022: ActiveDLighting,
0x0023: Nikon_PictureControl,
0x0024: Nikon_WorldTime,
0x0025: Nikon_ISOInfo,
0x002a: VignetteControl,
0x0080: ImageAdjustment,
0x0081: ToneComp,
0x0082: AuxiliaryLens,
0x0083: LensType,
0x0084: Lens,
0x0085: FocusDistance,
0x0086: DigitalZoom,
0x0087: FlashMode,
0x0088: Nikon_AFInfo,
0x0089: ShootingMode,
0x008a: AutoBracketRelease,
0x008b: LensFStops,
0x008c: ContrastCurve,
0x008d: ColorHue,
0x008f: SceneMode,
0x0090: Nikon_LightSource,
0x0091: Nikon_ShotInfo,
0x0092: HueAdjustment,
0x0093: NEFCompression,
0x0094: Nikon_Saturation,
0x0095: NoiseReduction,
0x0096: LinearizationTable,
0x0097: Nikon_ColorBalance,
0x0098: Nikon_LensData,
0x0099: RawImageCenter,
0x009a: SensorPixelSize,
0x009b: Nikon3_0x009b,
0x009c: SceneAssist,
0x009e: RetouchHistory,
0x009f: Nikon3_0x009f,
0x00a0: Nikon_SerialNO,
0x00a2: ImageDataSize,
0x00a3: Nikon3_0x00a3,
0x00a5: ImageCount,
0x00a6: DeletedImageCount,
0x00a7: ShutterCount,
0x00a8: Nikon_FlashInfo,
0x00a9: ImageOptimization,
0x00aa: SaturationText,
0x00ab: VariProgram,
0x00ac: ImageStabilization,
0x00ad: AFResponse,
0x00b0: Nikon_MultiExposure,
0x00b1: HighISONoiseReduction,
0x00b3: ToningEffect,
0x00b7: Nikon_AFInfo2,
0x00b8: Nikon_FileInfo,
0x00b9: Nikon_AFTune,
0x0e00: PrintIM,
0x0e01: CaptureData,
0x0e09: CaptureVersion,
0x0e0e: CaptureOffsets,
0x0e10: ScanIFD,
0x0e1d: ICCProfile,
0x0e1e: CaptureOutput,
}

70
vendor/github.com/rwcarlsen/goexif/mknote/mknote.go generated vendored Normal file
View file

@ -0,0 +1,70 @@
// Package mknote provides makernote parsers that can be used with goexif/exif.
package mknote
import (
"bytes"
"github.com/rwcarlsen/goexif/exif"
"github.com/rwcarlsen/goexif/tiff"
)
var (
// Canon is an exif.Parser for canon makernote data.
Canon = &canon{}
// NikonV3 is an exif.Parser for nikon makernote data.
NikonV3 = &nikonV3{}
// All is a list of all available makernote parsers
All = []exif.Parser{Canon, NikonV3}
)
type canon struct{}
// Parse decodes all Canon makernote data found in x and adds it to x.
func (_ *canon) Parse(x *exif.Exif) error {
m, err := x.Get(exif.MakerNote)
if err != nil {
return nil
}
mk, err := x.Get(exif.Make)
if err != nil {
return nil
}
if val, err := mk.StringVal(); err != nil || val != "Canon" {
return nil
}
// Canon notes are a single IFD directory with no header.
// Reader offsets need to be w.r.t. the original tiff structure.
buf := bytes.NewReader(append(make([]byte, m.ValOffset), m.Val...))
buf.Seek(int64(m.ValOffset), 0)
mkNotesDir, _, err := tiff.DecodeDir(buf, x.Tiff.Order)
if err != nil {
return err
}
x.LoadTags(mkNotesDir, makerNoteCanonFields, false)
return nil
}
type nikonV3 struct{}
// Parse decodes all Nikon makernote data found in x and adds it to x.
func (_ *nikonV3) Parse(x *exif.Exif) error {
m, err := x.Get(exif.MakerNote)
if err != nil {
return nil
} else if bytes.Compare(m.Val[:6], []byte("Nikon\000")) != 0 {
return nil
}
// Nikon v3 maker note is a self-contained IFD (offsets are relative
// to the start of the maker note)
mkNotes, err := tiff.Decode(bytes.NewReader(m.Val[10:]))
if err != nil {
return err
}
x.LoadTags(mkNotes.Dirs[0], makerNoteNikon3Fields, false)
return nil
}

BIN
vendor/github.com/rwcarlsen/goexif/tiff/sample1.tif generated vendored Normal file

Binary file not shown.

445
vendor/github.com/rwcarlsen/goexif/tiff/tag.go generated vendored Normal file
View file

@ -0,0 +1,445 @@
package tiff
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"math/big"
"strings"
"unicode"
"unicode/utf8"
)
// Format specifies the Go type equivalent used to represent the basic
// tiff data types.
type Format int
const (
IntVal Format = iota
FloatVal
RatVal
StringVal
UndefVal
OtherVal
)
var ErrShortReadTagValue = errors.New("tiff: short read of tag value")
var formatNames = map[Format]string{
IntVal: "int",
FloatVal: "float",
RatVal: "rational",
StringVal: "string",
UndefVal: "undefined",
OtherVal: "other",
}
// DataType represents the basic tiff tag data types.
type DataType uint16
const (
DTByte DataType = 1
DTAscii DataType = 2
DTShort DataType = 3
DTLong DataType = 4
DTRational DataType = 5
DTSByte DataType = 6
DTUndefined DataType = 7
DTSShort DataType = 8
DTSLong DataType = 9
DTSRational DataType = 10
DTFloat DataType = 11
DTDouble DataType = 12
)
var typeNames = map[DataType]string{
DTByte: "byte",
DTAscii: "ascii",
DTShort: "short",
DTLong: "long",
DTRational: "rational",
DTSByte: "signed byte",
DTUndefined: "undefined",
DTSShort: "signed short",
DTSLong: "signed long",
DTSRational: "signed rational",
DTFloat: "float",
DTDouble: "double",
}
// typeSize specifies the size in bytes of each type.
var typeSize = map[DataType]uint32{
DTByte: 1,
DTAscii: 1,
DTShort: 2,
DTLong: 4,
DTRational: 8,
DTSByte: 1,
DTUndefined: 1,
DTSShort: 2,
DTSLong: 4,
DTSRational: 8,
DTFloat: 4,
DTDouble: 8,
}
// Tag reflects the parsed content of a tiff IFD tag.
type Tag struct {
// Id is the 2-byte tiff tag identifier.
Id uint16
// Type is an integer (1 through 12) indicating the tag value's data type.
Type DataType
// Count is the number of type Type stored in the tag's value (i.e. the
// tag's value is an array of type Type and length Count).
Count uint32
// Val holds the bytes that represent the tag's value.
Val []byte
// ValOffset holds byte offset of the tag value w.r.t. the beginning of the
// reader it was decoded from. Zero if the tag value fit inside the offset
// field.
ValOffset uint32
order binary.ByteOrder
intVals []int64
floatVals []float64
ratVals [][]int64
strVal string
format Format
}
// DecodeTag parses a tiff-encoded IFD tag from r and returns a Tag object. The
// first read from r should be the first byte of the tag. ReadAt offsets should
// generally be relative to the beginning of the tiff structure (not relative
// to the beginning of the tag).
func DecodeTag(r ReadAtReader, order binary.ByteOrder) (*Tag, error) {
t := new(Tag)
t.order = order
err := binary.Read(r, order, &t.Id)
if err != nil {
return nil, errors.New("tiff: tag id read failed: " + err.Error())
}
err = binary.Read(r, order, &t.Type)
if err != nil {
return nil, errors.New("tiff: tag type read failed: " + err.Error())
}
err = binary.Read(r, order, &t.Count)
if err != nil {
return nil, errors.New("tiff: tag component count read failed: " + err.Error())
}
// There seems to be a relatively common corrupt tag which has a Count of
// MaxUint32. This is probably not a valid value, so return early.
if t.Count == 1<<32-1 {
return t, errors.New("invalid Count offset in tag")
}
valLen := typeSize[t.Type] * t.Count
if valLen == 0 {
return t, errors.New("zero length tag value")
}
if valLen > 4 {
binary.Read(r, order, &t.ValOffset)
// Use a bytes.Buffer so we don't allocate a huge slice if the tag
// is corrupt.
var buff bytes.Buffer
sr := io.NewSectionReader(r, int64(t.ValOffset), int64(valLen))
n, err := io.Copy(&buff, sr)
if err != nil {
return t, errors.New("tiff: tag value read failed: " + err.Error())
} else if n != int64(valLen) {
return t, ErrShortReadTagValue
}
t.Val = buff.Bytes()
} else {
val := make([]byte, valLen)
if _, err = io.ReadFull(r, val); err != nil {
return t, errors.New("tiff: tag offset read failed: " + err.Error())
}
// ignore padding.
if _, err = io.ReadFull(r, make([]byte, 4-valLen)); err != nil {
return t, errors.New("tiff: tag offset read failed: " + err.Error())
}
t.Val = val
}
return t, t.convertVals()
}
func (t *Tag) convertVals() error {
r := bytes.NewReader(t.Val)
switch t.Type {
case DTAscii:
if len(t.Val) <= 0 {
break
}
nullPos := bytes.IndexByte(t.Val, 0)
if nullPos == -1 {
t.strVal = string(t.Val)
} else {
// ignore all trailing NULL bytes, in case of a broken t.Count
t.strVal = string(t.Val[:nullPos])
}
case DTByte:
var v uint8
t.intVals = make([]int64, int(t.Count))
for i := range t.intVals {
err := binary.Read(r, t.order, &v)
if err != nil {
return err
}
t.intVals[i] = int64(v)
}
case DTShort:
var v uint16
t.intVals = make([]int64, int(t.Count))
for i := range t.intVals {
err := binary.Read(r, t.order, &v)
if err != nil {
return err
}
t.intVals[i] = int64(v)
}
case DTLong:
var v uint32
t.intVals = make([]int64, int(t.Count))
for i := range t.intVals {
err := binary.Read(r, t.order, &v)
if err != nil {
return err
}
t.intVals[i] = int64(v)
}
case DTSByte:
var v int8
t.intVals = make([]int64, int(t.Count))
for i := range t.intVals {
err := binary.Read(r, t.order, &v)
if err != nil {
return err
}
t.intVals[i] = int64(v)
}
case DTSShort:
var v int16
t.intVals = make([]int64, int(t.Count))
for i := range t.intVals {
err := binary.Read(r, t.order, &v)
if err != nil {
return err
}
t.intVals[i] = int64(v)
}
case DTSLong:
var v int32
t.intVals = make([]int64, int(t.Count))
for i := range t.intVals {
err := binary.Read(r, t.order, &v)
if err != nil {
return err
}
t.intVals[i] = int64(v)
}
case DTRational:
t.ratVals = make([][]int64, int(t.Count))
for i := range t.ratVals {
var n, d uint32
err := binary.Read(r, t.order, &n)
if err != nil {
return err
}
err = binary.Read(r, t.order, &d)
if err != nil {
return err
}
t.ratVals[i] = []int64{int64(n), int64(d)}
}
case DTSRational:
t.ratVals = make([][]int64, int(t.Count))
for i := range t.ratVals {
var n, d int32
err := binary.Read(r, t.order, &n)
if err != nil {
return err
}
err = binary.Read(r, t.order, &d)
if err != nil {
return err
}
t.ratVals[i] = []int64{int64(n), int64(d)}
}
case DTFloat: // float32
t.floatVals = make([]float64, int(t.Count))
for i := range t.floatVals {
var v float32
err := binary.Read(r, t.order, &v)
if err != nil {
return err
}
t.floatVals[i] = float64(v)
}
case DTDouble:
t.floatVals = make([]float64, int(t.Count))
for i := range t.floatVals {
var u float64
err := binary.Read(r, t.order, &u)
if err != nil {
return err
}
t.floatVals[i] = u
}
}
switch t.Type {
case DTByte, DTShort, DTLong, DTSByte, DTSShort, DTSLong:
t.format = IntVal
case DTRational, DTSRational:
t.format = RatVal
case DTFloat, DTDouble:
t.format = FloatVal
case DTAscii:
t.format = StringVal
case DTUndefined:
t.format = UndefVal
default:
t.format = OtherVal
}
return nil
}
// Format returns a value indicating which method can be called to retrieve the
// tag's value properly typed (e.g. integer, rational, etc.).
func (t *Tag) Format() Format { return t.format }
func (t *Tag) typeErr(to Format) error {
return &wrongFmtErr{typeNames[t.Type], formatNames[to]}
}
// Rat returns the tag's i'th value as a rational number. It returns a nil and
// an error if this tag's Format is not RatVal. It panics for zero deminators
// or if i is out of range.
func (t *Tag) Rat(i int) (*big.Rat, error) {
n, d, err := t.Rat2(i)
if err != nil {
return nil, err
}
return big.NewRat(n, d), nil
}
// Rat2 returns the tag's i'th value as a rational number represented by a
// numerator-denominator pair. It returns an error if the tag's Format is not
// RatVal. It panics if i is out of range.
func (t *Tag) Rat2(i int) (num, den int64, err error) {
if t.format != RatVal {
return 0, 0, t.typeErr(RatVal)
}
return t.ratVals[i][0], t.ratVals[i][1], nil
}
// Int64 returns the tag's i'th value as an integer. It returns an error if the
// tag's Format is not IntVal. It panics if i is out of range.
func (t *Tag) Int64(i int) (int64, error) {
if t.format != IntVal {
return 0, t.typeErr(IntVal)
}
return t.intVals[i], nil
}
// Int returns the tag's i'th value as an integer. It returns an error if the
// tag's Format is not IntVal. It panics if i is out of range.
func (t *Tag) Int(i int) (int, error) {
if t.format != IntVal {
return 0, t.typeErr(IntVal)
}
return int(t.intVals[i]), nil
}
// Float returns the tag's i'th value as a float. It returns an error if the
// tag's Format is not IntVal. It panics if i is out of range.
func (t *Tag) Float(i int) (float64, error) {
if t.format != FloatVal {
return 0, t.typeErr(FloatVal)
}
return t.floatVals[i], nil
}
// StringVal returns the tag's value as a string. It returns an error if the
// tag's Format is not StringVal. It panics if i is out of range.
func (t *Tag) StringVal() (string, error) {
if t.format != StringVal {
return "", t.typeErr(StringVal)
}
return t.strVal, nil
}
// String returns a nicely formatted version of the tag.
func (t *Tag) String() string {
data, err := t.MarshalJSON()
if err != nil {
return "ERROR: " + err.Error()
}
if t.Count == 1 {
return strings.Trim(fmt.Sprintf("%s", data), "[]")
}
return fmt.Sprintf("%s", data)
}
func (t *Tag) MarshalJSON() ([]byte, error) {
switch t.format {
case StringVal, UndefVal:
return nullString(t.Val), nil
case OtherVal:
return []byte(fmt.Sprintf("unknown tag type '%v'", t.Type)), nil
}
rv := []string{}
for i := 0; i < int(t.Count); i++ {
switch t.format {
case RatVal:
n, d, _ := t.Rat2(i)
rv = append(rv, fmt.Sprintf(`"%v/%v"`, n, d))
case FloatVal:
v, _ := t.Float(i)
rv = append(rv, fmt.Sprintf("%v", v))
case IntVal:
v, _ := t.Int(i)
rv = append(rv, fmt.Sprintf("%v", v))
}
}
return []byte(fmt.Sprintf(`[%s]`, strings.Join(rv, ","))), nil
}
func nullString(in []byte) []byte {
rv := bytes.Buffer{}
rv.WriteByte('"')
for _, b := range in {
if unicode.IsPrint(rune(b)) {
rv.WriteByte(b)
}
}
rv.WriteByte('"')
rvb := rv.Bytes()
if utf8.Valid(rvb) {
return rvb
}
return []byte(`""`)
}
type wrongFmtErr struct {
From, To string
}
func (e *wrongFmtErr) Error() string {
return fmt.Sprintf("cannot convert tag type '%v' into '%v'", e.From, e.To)
}

153
vendor/github.com/rwcarlsen/goexif/tiff/tiff.go generated vendored Normal file
View file

@ -0,0 +1,153 @@
// Package tiff implements TIFF decoding as defined in TIFF 6.0 specification at
// http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
package tiff
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"io/ioutil"
)
// ReadAtReader is used when decoding Tiff tags and directories
type ReadAtReader interface {
io.Reader
io.ReaderAt
}
// Tiff provides access to a decoded tiff data structure.
type Tiff struct {
// Dirs is an ordered slice of the tiff's Image File Directories (IFDs).
// The IFD at index 0 is IFD0.
Dirs []*Dir
// The tiff's byte-encoding (i.e. big/little endian).
Order binary.ByteOrder
}
// Decode parses tiff-encoded data from r and returns a Tiff struct that
// reflects the structure and content of the tiff data. The first read from r
// should be the first byte of the tiff-encoded data and not necessarily the
// first byte of an os.File object.
func Decode(r io.Reader) (*Tiff, error) {
data, err := ioutil.ReadAll(r)
if err != nil {
return nil, errors.New("tiff: could not read data")
}
buf := bytes.NewReader(data)
t := new(Tiff)
// read byte order
bo := make([]byte, 2)
if _, err = io.ReadFull(buf, bo); err != nil {
return nil, errors.New("tiff: could not read tiff byte order")
}
if string(bo) == "II" {
t.Order = binary.LittleEndian
} else if string(bo) == "MM" {
t.Order = binary.BigEndian
} else {
return nil, errors.New("tiff: could not read tiff byte order")
}
// check for special tiff marker
var sp int16
err = binary.Read(buf, t.Order, &sp)
if err != nil || 42 != sp {
return nil, errors.New("tiff: could not find special tiff marker")
}
// load offset to first IFD
var offset int32
err = binary.Read(buf, t.Order, &offset)
if err != nil {
return nil, errors.New("tiff: could not read offset to first IFD")
}
// load IFD's
var d *Dir
prev := offset
for offset != 0 {
// seek to offset
_, err := buf.Seek(int64(offset), 0)
if err != nil {
return nil, errors.New("tiff: seek to IFD failed")
}
if buf.Len() == 0 {
return nil, errors.New("tiff: seek offset after EOF")
}
// load the dir
d, offset, err = DecodeDir(buf, t.Order)
if err != nil {
return nil, err
}
if offset == prev {
return nil, errors.New("tiff: recursive IFD")
}
prev = offset
t.Dirs = append(t.Dirs, d)
}
return t, nil
}
func (tf *Tiff) String() string {
var buf bytes.Buffer
fmt.Fprint(&buf, "Tiff{")
for _, d := range tf.Dirs {
fmt.Fprintf(&buf, "%s, ", d.String())
}
fmt.Fprintf(&buf, "}")
return buf.String()
}
// Dir provides access to the parsed content of a tiff Image File Directory (IFD).
type Dir struct {
Tags []*Tag
}
// DecodeDir parses a tiff-encoded IFD from r and returns a Dir object. offset
// is the offset to the next IFD. The first read from r should be at the first
// byte of the IFD. ReadAt offsets should generally be relative to the
// beginning of the tiff structure (not relative to the beginning of the IFD).
func DecodeDir(r ReadAtReader, order binary.ByteOrder) (d *Dir, offset int32, err error) {
d = new(Dir)
// get num of tags in ifd
var nTags int16
err = binary.Read(r, order, &nTags)
if err != nil {
return nil, 0, errors.New("tiff: failed to read IFD tag count: " + err.Error())
}
// load tags
for n := 0; n < int(nTags); n++ {
t, err := DecodeTag(r, order)
if err != nil {
return nil, 0, err
}
d.Tags = append(d.Tags, t)
}
// get offset to next ifd
err = binary.Read(r, order, &offset)
if err != nil {
return nil, 0, errors.New("tiff: falied to read offset to next IFD: " + err.Error())
}
return d, offset, nil
}
func (d *Dir) String() string {
s := "Dir{"
for _, t := range d.Tags {
s += t.String() + ", "
}
return s + "}"
}