Beispiel #1
0
func ExampleDecode() {
	fname := "sample1.jpg"

	f, err := os.Open(fname)
	if err != nil {
		log.Fatal(err)
	}

	// Optionally register camera makenote data parsing - currently Nikon and
	// Canon are supported.
	exif.RegisterParsers(mknote.All...)

	x, err := exif.Decode(f)
	if err != nil {
		log.Fatal(err)
	}

	camModel, _ := x.Get(exif.Model) // normally, don't ignore errors!
	fmt.Println(camModel.StringVal())

	focal, _ := x.Get(exif.FocalLength)
	numer, denom, _ := focal.Rat2(0) // retrieve first (only) rat. value
	fmt.Printf("%v/%v", numer, denom)

	// Two convenience functions exist for date/time taken and GPS coords:
	tm, _ := x.DateTime()
	fmt.Println("Taken: ", tm)

	lat, long, _ := x.LatLong()
	fmt.Println("lat, long: ", lat, ", ", long)
}
Beispiel #2
0
// FileTime returns the best guess of the file's creation time (or modtime).
// If the file doesn't have its own metadata indication the creation time (such as in EXIF),
// FileTime uses the modification time from the file system.
// It there was a valid EXIF but an error while trying to get a date from it,
// it logs the error and tries the other methods.
func FileTime(f io.ReaderAt) (time.Time, error) {
	var ct time.Time
	defaultTime := func() (time.Time, error) {
		if osf, ok := f.(*os.File); ok {
			fi, err := osf.Stat()
			if err != nil {
				return ct, fmt.Errorf("Failed to find a modtime: stat: %v", err)
			}
			return fi.ModTime(), nil
		}
		return ct, errors.New("All methods failed to find a creation time or modtime.")
	}

	size, ok := findSize(f)
	if !ok {
		size = 256 << 10 // enough to get the EXIF
	}
	r := io.NewSectionReader(f, 0, size)
	var tiffErr error
	ex, err := exif.Decode(r)
	if err != nil {
		tiffErr = err
		if exif.IsShortReadTagValueError(err) {
			return ct, io.ErrUnexpectedEOF
		}
		if exif.IsCriticalError(err) || exif.IsExifError(err) {
			return defaultTime()
		}
	}
	ct, err = ex.DateTime()
	if err != nil {
		return defaultTime()
	}
	// If the EXIF file only had local timezone, but it did have
	// GPS, then lookup the timezone and correct the time.
	if ct.Location() == time.Local {
		if exif.IsGPSError(tiffErr) {
			log.Printf("Invalid EXIF GPS data: %v", tiffErr)
			return ct, nil
		}
		if lat, long, err := ex.LatLong(); err == nil {
			if loc := lookupLocation(latlong.LookupZoneName(lat, long)); loc != nil {
				if t, err := exifDateTimeInLocation(ex, loc); err == nil {
					return t, nil
				}
			}
		} else if !exif.IsTagNotPresentError(err) {
			log.Printf("Invalid EXIF GPS data: %v", err)
		}
	}
	return ct, nil
}
Beispiel #3
0
// DecodeConfig returns the image Config similarly to
// the standard library's image.DecodeConfig with the
// addition that it also checks for an EXIF orientation,
// and sets the Width and Height as they would visibly
// be after correcting for that orientation.
func DecodeConfig(r io.Reader) (Config, error) {
	var c Config
	var buf bytes.Buffer
	tr := io.TeeReader(io.LimitReader(r, 2<<20), &buf)
	swapDimensions := false

	ex, err := exif.Decode(tr)
	// trigger a retry when there isn't enough data for reading exif data from a tiff file
	if exif.IsShortReadTagValueError(err) {
		return c, io.ErrUnexpectedEOF
	}
	if err != nil {
		imageDebug(fmt.Sprintf("No valid EXIF, error: %v.", err))
	} else {
		tag, err := ex.Get(exif.Orientation)
		if err != nil {
			imageDebug(`No "Orientation" tag in EXIF.`)
		} else {
			orient, err := tag.Int(0)
			if err == nil {
				switch orient {
				// those are the orientations that require
				// a rotation of ±90
				case leftSideTop, rightSideTop, rightSideBottom, leftSideBottom:
					swapDimensions = true
				}
			} else {
				imageDebug(fmt.Sprintf("EXIF Error: %v", err))
			}
		}
	}
	conf, format, err := image.DecodeConfig(io.MultiReader(&buf, r))
	if err != nil {
		imageDebug(fmt.Sprintf("Image Decoding failed: %v", err))
		return c, err
	}
	c.Format = format
	if swapDimensions {
		c.Width, c.Height = conf.Height, conf.Width
	} else {
		c.Width, c.Height = conf.Width, conf.Height
	}
	return c, err
}
Beispiel #4
0
// exifOrientation parses the  EXIF data in r and returns the stored
// orientation as the angle and flip necessary to transform the image.
func exifOrientation(r io.Reader) (int, FlipDirection) {
	var (
		angle    int
		flipMode FlipDirection
	)
	ex, err := exif.Decode(r)
	if err != nil {
		imageDebug("No valid EXIF; will not rotate or flip.")
		return 0, 0
	}
	tag, err := ex.Get(exif.Orientation)
	if err != nil {
		imageDebug(`No "Orientation" tag in EXIF; will not rotate or flip.`)
		return 0, 0
	}
	orient, err := tag.Int(0)
	if err != nil {
		imageDebug(fmt.Sprintf("EXIF error: %v", err))
		return 0, 0
	}
	switch orient {
	case topLeftSide:
		// do nothing
	case topRightSide:
		flipMode = 2
	case bottomRightSide:
		angle = 180
	case bottomLeftSide:
		angle = 180
		flipMode = 2
	case leftSideTop:
		angle = -90
		flipMode = 2
	case rightSideTop:
		angle = -90
	case rightSideBottom:
		angle = 90
		flipMode = 2
	case leftSideBottom:
		angle = 90
	}
	return angle, flipMode
}
Beispiel #5
0
func showEXIF(file string) {
	f, err := os.Open(file)
	if err != nil {
		panic(err.Error())
	}
	defer f.Close()
	ex, err := exif.Decode(f)
	if err != nil {
		if exif.IsCriticalError(err) {
			log.Fatalf("exif.Decode, critical error: %v", err)
		}
		log.Printf("exif.Decode, warning: %v", err)
	}
	fmt.Printf("%v\n", ex)
	if exif.IsExifError(err) {
		// the error happened while decoding the EXIF sub-IFD, so as DateTime is
		// part of it, we have to assume (until there's a better "decode effort"
		// strategy in goexif) that it's not usable.
		return
	}
	ct, err := ex.DateTime()
	fmt.Printf("exif.DateTime = %v, %v\n", ct, err)
}
Beispiel #6
0
func main() {
	flag.Parse()
	fnames := flag.Args()

	if *mnote {
		exif.RegisterParsers(mknote.All...)
	}

	for _, name := range fnames {
		f, err := os.Open(name)
		if err != nil {
			log.Printf("err on %v: %v", name, err)
			continue
		}

		x, err := exif.Decode(f)
		if err != nil {
			log.Printf("err on %v: %v", name, err)
			continue
		}

		if *thumb {
			data, err := x.JpegThumbnail()
			if err != nil {
				log.Fatal("no thumbnail present")
			}
			if _, err := os.Stdout.Write(data); err != nil {
				log.Fatal(err)
			}
			return
		}

		fmt.Printf("\n---- Image '%v' ----\n", name)
		x.Walk(Walker{})
	}
}
Beispiel #7
0
func indexEXIF(wholeRef blob.Ref, reader io.Reader, mm *mutationMap) (err error) {
	var tiffErr error
	ex, err := exif.Decode(reader)
	if err != nil {
		tiffErr = err
		if exif.IsCriticalError(err) {
			return
		}
		log.Printf("Non critical TIFF decoding error: %v", err)
	}
	defer func() {
		// The EXIF library panics if you access a field past
		// what the file contains.  Be paranoid and just
		// recover here, instead of crashing on an invalid
		// EXIF file.
		if e := recover(); e != nil {
			err = errEXIFPanic
		}
	}()

	err = ex.Walk(exifWalkFunc(func(name exif.FieldName, tag *tiff.Tag) error {
		tagFmt := tagFormatString(tag)
		if tagFmt == "" {
			return nil
		}
		key := keyEXIFTag.Key(wholeRef, fmt.Sprintf("%04x", tag.Id))
		numComp := int(tag.Count)
		if tag.Format() == tiff.StringVal {
			numComp = 1
		}
		var val bytes.Buffer
		val.WriteString(keyEXIFTag.Val(tagFmt, numComp, ""))
		if tag.Format() == tiff.StringVal {
			str, err := tag.StringVal()
			if err != nil {
				log.Printf("Invalid EXIF string data: %v", err)
				return nil
			}
			if containsUnsafeRawStrByte(str) {
				val.WriteString(urle(str))
			} else {
				val.WriteString(str)
			}
		} else {
			for i := 0; i < int(tag.Count); i++ {
				if i > 0 {
					val.WriteByte('|')
				}
				switch tagFmt {
				case "int":
					v, err := tag.Int(i)
					if err != nil {
						log.Printf("Invalid EXIF int data: %v", err)
						return nil
					}
					fmt.Fprintf(&val, "%d", v)
				case "rat":
					n, d, err := tag.Rat2(i)
					if err != nil {
						log.Printf("Invalid EXIF rat data: %v", err)
						return nil
					}
					fmt.Fprintf(&val, "%d/%d", n, d)
				case "float":
					v, err := tag.Float(i)
					if err != nil {
						log.Printf("Invalid EXIF float data: %v", err)
						return nil
					}
					fmt.Fprintf(&val, "%v", v)
				default:
					panic("shouldn't get here")
				}
			}
		}
		valStr := val.String()
		mm.Set(key, valStr)
		return nil
	}))
	if err != nil {
		return
	}

	if exif.IsGPSError(tiffErr) {
		log.Printf("Invalid EXIF GPS data: %v", tiffErr)
		return nil
	}
	if lat, long, err := ex.LatLong(); err == nil {
		mm.Set(keyEXIFGPS.Key(wholeRef), keyEXIFGPS.Val(fmt.Sprint(lat), fmt.Sprint(long)))
	} else if !exif.IsTagNotPresentError(err) {
		log.Printf("Invalid EXIF GPS data: %v", err)
	}
	return nil
}