summaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/github.com/rwcarlsen/goexif/exif/exif.go
diff options
context:
space:
mode:
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.go619
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
-}