示例#1
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: lstat: %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)
	ex, err := exif.Decode(r)
	if err != nil {
		return defaultTime()
	}
	ct, err = ex.DateTime()
	if err != nil {
		return defaultTime()
	}
	return ct, nil
}
示例#2
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 {
		log.Fatalf("exif.Decode: %v", err)
	}
	fmt.Printf("exif.Decode = %#v\n", ex)
	ct, err := ex.DateTime()
	fmt.Printf("exif.DateTime = %v, %v\n", ct, err)
}
示例#3
0
func main() {
	flag.Parse()
	fname := flag.Arg(0)

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

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

	x.Walk(Walker{})
}
示例#4
0
func ExampleDecode() {
	fname := "sample1.jpg"

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

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

	camModel, _ := x.Get("Model")
	date, _ := x.Get("DateTimeOriginal")
	fmt.Println(camModel.StringVal())
	fmt.Println(date.StringVal())

	focal, _ := x.Get("FocalLength")
	numer, denom := focal.Rat2(0) // retrieve first (only) rat. value
	fmt.Printf("%v/%v", numer, denom)
}
示例#5
0
// TODO(mpl): move this test to the goexif lib if/when we contribute
// back the DateTime stuff to upstream.
func TestDateTime(t *testing.T) {
	f, err := os.Open(path.Join(datadir, "f1-exif.jpg"))
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()
	ex, err := exif.Decode(f)
	if err != nil {
		t.Fatal(err)
	}
	got, err := ex.DateTime()
	if err != nil {
		t.Fatal(err)
	}
	exifTimeLayout := "2006:01:02 15:04:05"
	want, err := time.Parse(exifTimeLayout, "2012:11:04 05:42:02")
	if err != nil {
		t.Fatal(err)
	}
	if got != want {
		t.Fatalf("Creation times differ; got %v, want: %v\n", got, want)
	}
}
示例#6
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)
	if err != nil {
		imageDebug("No valid EXIF.")
	} else {
		tag, err := ex.Get(exif.Orientation)
		if err != nil {
			imageDebug("No \"Orientation\" tag in EXIF.")
		} else {
			orient := tag.Int(0)
			switch orient {
			// those are the orientations that require
			// a rotation of ±90
			case leftSideTop, rightSideTop, rightSideBottom, leftSideBottom:
				swapDimensions = true
			}
		}
	}
	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
}
示例#7
0
func indexEXIF(wholeRef blob.Ref, header []byte, mm *mutationMap) {
	ex, err := exif.Decode(bytes.NewReader(header))
	if err != nil {
		return
	}
	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 {
			log.Printf("Ignoring invalid EXIF file. Caught panic: %v", e)
		}
	}()

	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.Ncomp)
		if tag.Format() == tiff.StringVal {
			numComp = 1
		}
		var val bytes.Buffer
		val.WriteString(keyEXIFTag.Val(tagFmt, numComp, ""))
		if tag.Format() == tiff.StringVal {
			str := tag.StringVal()
			if containsUnsafeRawStrByte(str) {
				val.WriteString(urle(str))
			} else {
				val.WriteString(str)
			}
		} else {
			for i := 0; i < int(tag.Ncomp); i++ {
				if i > 0 {
					val.WriteByte('|')
				}
				switch tagFmt {
				case "int":
					fmt.Fprintf(&val, "%d", tag.Int(i))
				case "rat":
					n, d := tag.Rat2(i)
					fmt.Fprintf(&val, "%d/%d", n, d)
				case "float":
					fmt.Fprintf(&val, "%v", tag.Float(i))
				default:
					panic("shouldn't get here")
				}
			}
		}
		valStr := val.String()
		mm.Set(key, valStr)
		return nil
	}))

	longTag, err := ex.Get(exif.FieldName("GPSLongitude"))
	if err != nil {
		return
	}
	ewTag, err := ex.Get(exif.FieldName("GPSLongitudeRef"))
	if err != nil {
		return
	}
	latTag, err := ex.Get(exif.FieldName("GPSLatitude"))
	if err != nil {
		return
	}
	nsTag, err := ex.Get(exif.FieldName("GPSLatitudeRef"))
	if err != nil {
		return
	}
	long := tagDegrees(longTag)
	lat := tagDegrees(latTag)
	if ewTag.StringVal() == "W" {
		long *= -1.0
	}
	if nsTag.StringVal() == "S" {
		lat *= -1.0
	}
	mm.Set(keyEXIFGPS.Key(wholeRef), keyEXIFGPS.Val(fmt.Sprint(lat), fmt.Sprint(long)))
}
示例#8
0
// Decode decodes an image from r using the provided decoding options.
// The string returned is the format name returned by image.Decode.
// If opts is nil, the defaults are used.
func Decode(r io.Reader, opts *DecodeOpts) (image.Image, string, error) {
	var buf bytes.Buffer
	tr := io.TeeReader(io.LimitReader(r, 2<<20), &buf)
	angle := 0
	flipMode := FlipDirection(0)
	if opts.useEXIF() {
		ex, err := exif.Decode(tr)
		if err != nil {
			imageDebug("No valid EXIF; will not rotate or flip.")
			return image.Decode(io.MultiReader(&buf, r))
		}
		tag, err := ex.Get(exif.Orientation)
		if err != nil {
			imageDebug("No \"Orientation\" tag in EXIF; will not rotate or flip.")
			return image.Decode(io.MultiReader(&buf, r))
		}
		orient := tag.Val[1]
		switch orient {
		case 1:
			// do nothing
		case 2:
			flipMode = 2
		case 3:
			angle = 180
		case 4:
			angle = 180
			flipMode = 2
		case 5:
			angle = -90
			flipMode = 2
		case 6:
			angle = -90
		case 7:
			angle = 90
			flipMode = 2
		case 8:
			angle = 90
		}
	} else {
		if opts.forcedRotate() {
			var ok bool
			angle, ok = opts.Rotate.(int)
			if !ok {
				return nil, "", fmt.Errorf("Rotate should be an int, not a %T", opts.Rotate)
			}
		}
		if opts.forcedFlip() {
			var ok bool
			flipMode, ok = opts.Flip.(FlipDirection)
			if !ok {
				return nil, "", fmt.Errorf("Flip should be a FlipDirection, not a %T", opts.Flip)
			}
		}
	}

	im, err := jpeg.Decode(io.MultiReader(&buf, r))
	if err != nil {
		return nil, "", err
	}
	return flip(rotate(im, angle), flipMode), "jpeg", nil
}
示例#9
0
// Decode decodes an image from r using the provided decoding options.
// The Config returned is similar to the one from the image package,
// with the addition of the Modified field which indicates if the
// image was actually flipped or rotated.
// If opts is nil, the defaults are used.
func Decode(r io.Reader, opts *DecodeOpts) (image.Image, Config, error) {
	var c Config
	var buf bytes.Buffer
	tr := io.TeeReader(io.LimitReader(r, 2<<20), &buf)
	angle := 0
	flipMode := FlipDirection(0)
	if opts.useEXIF() {
		ex, err := exif.Decode(tr)
		maybeRescale := func() (image.Image, Config, error) {
			im, format, err := image.Decode(io.MultiReader(&buf, r))
			if err == nil && opts.wantRescale(im.Bounds()) {
				im = rescale(im, opts)
				c.Modified = true
			}
			c.Format = format
			c.setBounds(im)
			return im, c, err
		}
		if err != nil {
			imageDebug("No valid EXIF; will not rotate or flip.")
			return maybeRescale()
		}
		tag, err := ex.Get(exif.Orientation)
		if err != nil {
			imageDebug("No \"Orientation\" tag in EXIF; will not rotate or flip.")
			return maybeRescale()
		}
		orient := tag.Int(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
		}
	} else {
		if opts.forcedRotate() {
			var ok bool
			angle, ok = opts.Rotate.(int)
			if !ok {
				return nil, c, fmt.Errorf("Rotate should be an int, not a %T", opts.Rotate)
			}
		}
		if opts.forcedFlip() {
			var ok bool
			flipMode, ok = opts.Flip.(FlipDirection)
			if !ok {
				return nil, c, fmt.Errorf("Flip should be a FlipDirection, not a %T", opts.Flip)
			}
		}
	}

	im, format, err := image.Decode(io.MultiReader(&buf, r))
	if err != nil {
		return nil, c, err
	}
	rescaled := false
	if opts.wantRescale(im.Bounds()) {
		im = rescale(im, opts)
		rescaled = true
	}
	im = flip(rotate(im, angle), flipMode)
	modified := true
	if angle == 0 && flipMode == 0 && !rescaled {
		modified = false
	}

	c.Format = format
	c.Modified = modified
	c.setBounds(im)
	return im, c, nil
}
示例#10
0
// Decode decodes an image from r using the provided decoding options.
// The Config returned is similar to the one from the image package,
// with the addition of the Modified field which indicates if the
// image was actually flipped or rotated.
// If opts is nil, the defaults are used.
func Decode(r io.Reader, opts *DecodeOpts) (image.Image, Config, error) {
	var c Config
	var buf bytes.Buffer
	tr := io.TeeReader(io.LimitReader(r, 2<<20), &buf)
	angle := 0
	flipMode := FlipDirection(0)
	if opts.useEXIF() {
		ex, err := exif.Decode(tr)
		if err != nil {
			imageDebug("No valid EXIF; will not rotate or flip.")
			im, format, err := image.Decode(io.MultiReader(&buf, r))
			c.Format = format
			c.setBounds(im)
			return im, c, err
		}
		tag, err := ex.Get(exif.Orientation)
		if err != nil {
			imageDebug("No \"Orientation\" tag in EXIF; will not rotate or flip.")
			im, format, err := image.Decode(io.MultiReader(&buf, r))
			c.Format = format
			c.setBounds(im)
			return im, c, err
		}
		orient := tag.Int(0)
		switch orient {
		case 1:
			// do nothing
		case 2:
			flipMode = 2
		case 3:
			angle = 180
		case 4:
			angle = 180
			flipMode = 2
		case 5:
			angle = -90
			flipMode = 2
		case 6:
			angle = -90
		case 7:
			angle = 90
			flipMode = 2
		case 8:
			angle = 90
		}
	} else {
		if opts.forcedRotate() {
			var ok bool
			angle, ok = opts.Rotate.(int)
			if !ok {
				return nil, c, fmt.Errorf("Rotate should be an int, not a %T", opts.Rotate)
			}
		}
		if opts.forcedFlip() {
			var ok bool
			flipMode, ok = opts.Flip.(FlipDirection)
			if !ok {
				return nil, c, fmt.Errorf("Flip should be a FlipDirection, not a %T", opts.Flip)
			}
		}
	}

	im, err := jpeg.Decode(io.MultiReader(&buf, r))
	if err != nil {
		return nil, c, err
	}
	im = flip(rotate(im, angle), flipMode)
	modified := true
	if angle == 0 && flipMode == 0 {
		modified = false
	}
	c.Format = "jpeg"
	c.Modified = modified
	c.setBounds(im)
	return im, c, nil
}