diff options
author | Christopher Speller <crspeller@gmail.com> | 2016-05-12 15:08:58 -0400 |
---|---|---|
committer | Christopher Speller <crspeller@gmail.com> | 2016-05-12 16:37:29 -0400 |
commit | 84d2482ddbff9564c9ad75b2d30af66e3ddfd44d (patch) | |
tree | 8bfa567d2b6381f4a996ada2deff8a16aa85a3ac /Godeps/_workspace/src/github.com/rwcarlsen/goexif/exif/exif.go | |
parent | d1efb66ad7b017f0fbfe6f0c20843b30f396e504 (diff) | |
download | chat-84d2482ddbff9564c9ad75b2d30af66e3ddfd44d.tar.gz chat-84d2482ddbff9564c9ad75b2d30af66e3ddfd44d.tar.bz2 chat-84d2482ddbff9564c9ad75b2d30af66e3ddfd44d.zip |
Updating go depencancies. Switching to go1.6 vendoring (#2949)
Diffstat (limited to 'Godeps/_workspace/src/github.com/rwcarlsen/goexif/exif/exif.go')
-rw-r--r-- | Godeps/_workspace/src/github.com/rwcarlsen/goexif/exif/exif.go | 619 |
1 files changed, 0 insertions, 619 deletions
diff --git a/Godeps/_workspace/src/github.com/rwcarlsen/goexif/exif/exif.go b/Godeps/_workspace/src/github.com/rwcarlsen/goexif/exif/exif.go deleted file mode 100644 index b420729da..000000000 --- a/Godeps/_workspace/src/github.com/rwcarlsen/goexif/exif/exif.go +++ /dev/null @@ -1,619 +0,0 @@ -// 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 { - 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-encoded data from r and returns a queryable Exif -// object. After the exif data section is called and the tiff structure -// 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 := r.Read(header) - if err != nil { - return nil, err - } - if n < len(header) { - return nil, errors.New("exif: short read on header") - } - - var isTiff bool - switch string(header) { - case "II*\x00": - // TIFF - Little endian (Intel) - isTiff = true - case "MM\x00*": - // TIFF - Big endian (Motorola) - isTiff = true - default: - // Not TIFF, assume JPEG - } - - // Put the header bytes back into the reader. - r = io.MultiReader(bytes.NewReader(header), r) - var ( - er *bytes.Reader - tif *tiff.Tiff - ) - - if 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()) - } else { - // Locate the JPEG APP1 header. - var sec *appSec - 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. - // For now, just always return the time.Local timezone. - return time.ParseInLocation(exifTimeLayout, dateStr, time.Local) -} - -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 -} |