Skip to content

Commit

Permalink
Add image.Exif
Browse files Browse the repository at this point in the history
Note that we will probably need to add some metadata cache for this to scale.

Fixes #4600
  • Loading branch information
bep committed Aug 31, 2019
1 parent 8a8d4a6 commit 2814339
Show file tree
Hide file tree
Showing 12 changed files with 483 additions and 2 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ require (
github.com/pkg/errors v0.8.1
github.com/rogpeppe/go-internal v1.3.0
github.com/russross/blackfriday v1.5.3-0.20190124082335-a477dd164691
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
github.com/sanity-io/litter v1.1.0
github.com/spf13/afero v1.2.2
github.com/spf13/cast v1.3.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,8 @@ github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNue
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday v1.5.3-0.20190124082335-a477dd164691 h1:auJkuUc4uOuZNoH9jGLvqVaDLiuCOh/LY+Qw5NBFo4I=
github.com/russross/blackfriday v1.5.3-0.20190124082335-a477dd164691/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/sanity-io/litter v1.1.0 h1:BllcKWa3VbZmOZbDCoszYLk7zCsKHz5Beossi8SUcTc=
github.com/sanity-io/litter v1.1.0/go.mod h1:CJ0VCw2q4qKU7LaQr3n7UOSHzgEMgcGco7N/SkZQPjw=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
Expand Down
49 changes: 49 additions & 0 deletions resources/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import (
_ "image/png"
"os"
"strings"
"sync"

"github.com/gohugoio/hugo/resources/images/exif"

"github.com/gohugoio/hugo/resources/internal"

Expand Down Expand Up @@ -48,12 +51,56 @@ var (
type imageResource struct {
*images.Image

// When a image is processed in a chain, this holds the reference to the
// original (first).
root *imageResource

exifInit sync.Once
exifInitErr error
exif *exif.Exif

baseResource
}

// ImageData contains image related data, typically Exif.
type ImageData map[string]interface{}

func (i *imageResource) Exif() (*exif.Exif, error) {
return i.root.getExif()
}

func (i *imageResource) getExif() (*exif.Exif, error) {

i.exifInit.Do(func() {
supportsExif := i.Format == images.JPEG || i.Format == images.TIFF
if !supportsExif {
return
}

f, err := i.root.ReadSeekCloser()
if err != nil {
i.exifInitErr = err
return
}
defer f.Close()

x, err := i.getSpec().imaging.DecodeExif(f)
if err != nil {
i.exifInitErr = err
return
}

i.exif = x

})

return i.exif, i.exifInitErr
}

func (i *imageResource) Clone() resource.Resource {
gr := i.baseResource.Clone().(baseResource)
return &imageResource{
root: i.root,
Image: i.WithSpec(gr),
baseResource: gr,
}
Expand All @@ -74,6 +121,7 @@ func (i *imageResource) cloneWithUpdates(u *transformationUpdate) (baseResource,
}

return &imageResource{
root: i.root,
Image: img,
baseResource: base,
}, nil
Expand Down Expand Up @@ -217,6 +265,7 @@ func (i *imageResource) clone(img image.Image) *imageResource {

return &imageResource{
Image: image,
root: i.root,
baseResource: spec,
}
}
Expand Down
26 changes: 26 additions & 0 deletions resources/image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,32 @@ func TestSVGImageContent(t *testing.T) {
c.Assert(content.(string), qt.Contains, `<svg height="100" width="100">`)
}

func TestImageExif(t *testing.T) {
c := qt.New(t)
image := fetchImage(c, "sunset.jpg")

x, err := image.Exif()
c.Assert(err, qt.IsNil)
c.Assert(x, qt.Not(qt.IsNil))

c.Assert(x.Date.Format("2006-01-02"), qt.Equals, "2017-10-27")

// Malaga: https://goo.gl/taazZy
c.Assert(x.Lat, qt.Equals, float64(36.59744166666667))
c.Assert(x.Long, qt.Equals, float64(-4.50846))

v, found := x.Values["LensModel"]
c.Assert(found, qt.Equals, true)
lensModel, ok := v.(string)
c.Assert(ok, qt.Equals, true)
c.Assert(lensModel, qt.Equals, "smc PENTAX-DA* 16-50mm F2.8 ED AL [IF] SDM")

resized, _ := image.Resize("300x200")
x2, _ := resized.Exif()
c.Assert(x2, qt.Equals, x)

}

func TestImageOperationsGolden(t *testing.T) {
c := qt.New(t)
c.Parallel()
Expand Down
30 changes: 30 additions & 0 deletions resources/images/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ func DecodeConfig(m map[string]interface{}) (Imaging, error) {
i.ResampleFilter = filter
}

if strings.TrimSpace(i.Exif.IncludeFields) == "" && strings.TrimSpace(i.Exif.ExcludeFields) == "" {
// Don't change this for no good reason. Please don't.
i.Exif.ExcludeFields = "GPS|Exif|Exposure[M|P|B]|Contrast|Resolution|Sharp|JPEG|Metering|Sensing|Saturation|ColorSpace|Flash|WhiteBalance"
}

return i, nil
}

Expand Down Expand Up @@ -279,4 +284,29 @@ type Imaging struct {

// The anchor to use in Fill. Default is "smart", i.e. Smart Crop.
Anchor string

Exif ExifConfig
}

type ExifConfig struct {

// Regexp matching the Exif fields you want from the (massive) set of Exif info
// available. As we cache this info to disk, this is for performance and
// disk space reasons more than anything.
// If you want it all, put ".*" in this config setting.
// Note that if neither this or ExcludeFields is set, Hugo will return a small
// default set.
IncludeFields string

// Regexp matching the Exif fields you want to exclude. This may be easier to use
// than IncludeFields above, depending on what you want.
ExcludeFields string

// Hugo extracts the "photo taken" date/time into .Date by default.
// Set this to true to turn it off.
DisableDate bool

// Hugo extracts the "photo taken where" (GPS latitude and longitude) into
// .Long and .Lat. Set this to true to turn it off.
DisableLatLong bool
}
10 changes: 10 additions & 0 deletions resources/images/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ func TestDecodeConfig(t *testing.T) {
})
c.Assert(err, qt.IsNil)
c.Assert(imaging.Anchor, qt.Equals, "smart")

imaging, err = DecodeConfig(map[string]interface{}{
"exif": map[string]interface{}{
"disableLatLong": true,
},
})
c.Assert(err, qt.IsNil)
c.Assert(imaging.Exif.DisableLatLong, qt.Equals, true)
c.Assert(imaging.Exif.ExcludeFields, qt.Equals, "GPS|Exif|Exposure[M|P|B]|Contrast|Resolution|Sharp|JPEG|Metering|Sensing|Saturation|ColorSpace|Flash|WhiteBalance")

}

func TestDecodeImageConfig(t *testing.T) {
Expand Down
Loading

0 comments on commit 2814339

Please sign in to comment.