URI: 
       resources: Support output image format in image operations - hugo - [fork] hugo port for 9front
  HTML git clone git@git.drkhsh.at/hugo.git
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
   DIR README
   DIR LICENSE
       ---
   DIR commit e5856e61d88ef5149582851b00e06b5b93dce9f8
   DIR parent 34dc06b032741abac342d7a2a77510ded9b72ae8
  HTML Author: J. Ansorg <github@joachim-ansorg.de>
       Date:   Sat, 21 Sep 2019 16:50:27 +0200
       
       resources: Support output image format in image operations
       
       The image format is defined as the image extension of the known formats,
       excluding the dot.
       All of 'img.Resize "600x jpeg"', 'img.Resize "600x jpg"',
       and 'img.Resize "600x png"' are valid format definitions.
       If the target format is defined in the operation definition string,
       then the converted image will be stored in this format. Permalinks and
       media type are updated correspondingly.
       Unknown image extensions in the operation definition have not effect.
       
       See #6298
       Diffstat:
         M media/mediaType.go                  |       7 +++++--
         M resources/image.go                  |      24 +++++++++++++-----------
         M resources/image_test.go             |      40 +++++++++++++++++++++++++++++++
         M resources/images/config.go          |       6 +++++-
         M resources/images/image.go           |      32 ++++++++++++++++++++++++++++++-
         M resources/resource.go               |       4 ++++
         M resources/resource_metadata.go      |       2 ++
       
       7 files changed, 100 insertions(+), 15 deletions(-)
       ---
   DIR diff --git a/media/mediaType.go b/media/mediaType.go
       @@ -140,8 +140,11 @@ var (
                YAMLType       = Type{MainType: "application", SubType: "yaml", Suffixes: []string{"yaml", "yml"}, Delimiter: defaultDelimiter}
        
                // Common image types
       -        PNGType = Type{MainType: "image", SubType: "png", Suffixes: []string{"png"}, Delimiter: defaultDelimiter}
       -        JPGType = Type{MainType: "image", SubType: "jpg", Suffixes: []string{"jpg", "jpeg"}, Delimiter: defaultDelimiter}
       +        PNGType  = Type{MainType: "image", SubType: "png", Suffixes: []string{"png"}, Delimiter: defaultDelimiter}
       +        JPGType  = Type{MainType: "image", SubType: "jpg", Suffixes: []string{"jpg", "jpeg"}, Delimiter: defaultDelimiter}
       +        GIFType  = Type{MainType: "image", SubType: "gif", Suffixes: []string{"gif"}, Delimiter: defaultDelimiter}
       +        TIFFType = Type{MainType: "image", SubType: "tiff", Suffixes: []string{"tif", "tiff"}, Delimiter: defaultDelimiter}
       +        BMPType  = Type{MainType: "image", SubType: "bmp", Suffixes: []string{"bmp"}, Delimiter: defaultDelimiter}
        
                OctetType = Type{MainType: "application", SubType: "octet-stream"}
        )
   DIR diff --git a/resources/image.go b/resources/image.go
       @@ -43,8 +43,6 @@ import (
                "github.com/gohugoio/hugo/resources/images"
        
                // Blind import for image.Decode
       -
       -        // Blind import for image.Decode
                _ "golang.org/x/image/webp"
        )
        
       @@ -220,17 +218,13 @@ func (i *imageResource) Filter(filters ...interface{}) (resource.Image, error) {
                }
        
                conf.Key = internal.HashString(gfilters)
       +        conf.TargetFormat = i.Format
        
                return i.doWithImageConfig(conf, func(src image.Image) (image.Image, error) {
                        return i.Proc.Filter(src, gfilters...)
                })
        }
        
       -func (i *imageResource) isJPEG() bool {
       -        name := strings.ToLower(i.getResourcePaths().relTargetDirFile.file)
       -        return strings.HasSuffix(name, ".jpg") || strings.HasSuffix(name, ".jpeg")
       -}
       -
        // Serialize image processing. The imaging library spins up its own set of Go routines,
        // so there is not much to gain from adding more load to the mix. That
        // can even have negative effect in low resource scenarios.
       @@ -260,7 +254,7 @@ func (i *imageResource) doWithImageConfig(conf images.ImageConfig, f func(src im
                                return nil, nil, &os.PathError{Op: errOp, Path: errPath, Err: err}
                        }
        
       -                if i.Format == images.PNG {
       +                if conf.TargetFormat == images.PNG {
                                // Apply the colour palette from the source
                                if paletted, ok := src.(*image.Paletted); ok {
                                        tmp := image.NewPaletted(converted.Bounds(), paletted.Palette)
       @@ -271,6 +265,8 @@ func (i *imageResource) doWithImageConfig(conf images.ImageConfig, f func(src im
        
                        ci := i.clone(converted)
                        ci.setBasePath(conf)
       +                ci.Format = conf.TargetFormat
       +                ci.setMediaType(conf.TargetFormat.MediaType())
        
                        return ci, converted, nil
                })
       @@ -282,11 +278,14 @@ func (i *imageResource) decodeImageConfig(action, spec string) (images.ImageConf
                        return conf, err
                }
        
       -        iconf := i.Proc.Cfg
       +        // default to the source format
       +        if conf.TargetFormat == 0 {
       +                conf.TargetFormat = i.Format
       +        }
        
       -        if conf.Quality <= 0 && i.isJPEG() {
       +        if conf.Quality <= 0 && conf.TargetFormat.RequiresDefaultQuality() {
                        // We need a quality setting for all JPEGs
       -                conf.Quality = iconf.Quality
       +                conf.Quality = i.Proc.Cfg.Quality
                }
        
                return conf, nil
       @@ -339,6 +338,9 @@ func (i *imageResource) getImageMetaCacheTargetPath() string {
        
        func (i *imageResource) relTargetPathFromConfig(conf images.ImageConfig) dirFile {
                p1, p2 := helpers.FileAndExt(i.getResourcePaths().relTargetDirFile.file)
       +        if conf.TargetFormat != i.Format {
       +                p2 = conf.TargetFormat.DefaultExtension()
       +        }
        
                h, _ := i.hash()
                idStr := fmt.Sprintf("_hu%s_%d", h, i.size())
   DIR diff --git a/resources/image_test.go b/resources/image_test.go
       @@ -133,6 +133,46 @@ func TestImageTransformBasic(t *testing.T) {
                c.Assert(filled, eq, filledAgain)
        }
        
       +func TestImageTransformFormat(t *testing.T) {
       +        c := qt.New(t)
       +
       +        image := fetchSunset(c)
       +
       +        fileCache := image.(specProvider).getSpec().FileCaches.ImageCache().Fs
       +
       +        assertExtWidthHeight := func(img resource.Image, ext string, w, h int) {
       +                c.Helper()
       +                c.Assert(img, qt.Not(qt.IsNil))
       +                c.Assert(helpers.Ext(img.RelPermalink()), qt.Equals, ext)
       +                c.Assert(img.Width(), qt.Equals, w)
       +                c.Assert(img.Height(), qt.Equals, h)
       +        }
       +
       +        c.Assert(image.RelPermalink(), qt.Equals, "/a/sunset.jpg")
       +        c.Assert(image.ResourceType(), qt.Equals, "image")
       +        assertExtWidthHeight(image, ".jpg", 900, 562)
       +
       +        imagePng, err := image.Resize("450x png")
       +        c.Assert(err, qt.IsNil)
       +        c.Assert(imagePng.RelPermalink(), qt.Equals, "/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_450x0_resize_linear.png")
       +        c.Assert(imagePng.ResourceType(), qt.Equals, "image")
       +        assertExtWidthHeight(imagePng, ".png", 450, 281)
       +        c.Assert(imagePng.Name(), qt.Equals, "sunset.jpg")
       +        c.Assert(imagePng.MediaType().String(), qt.Equals, "image/png")
       +
       +        assertFileCache(c, fileCache, path.Base(imagePng.RelPermalink()), 450, 281)
       +
       +        imageGif, err := image.Resize("225x gif")
       +        c.Assert(err, qt.IsNil)
       +        c.Assert(imageGif.RelPermalink(), qt.Equals, "/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_225x0_resize_linear.gif")
       +        c.Assert(imageGif.ResourceType(), qt.Equals, "image")
       +        assertExtWidthHeight(imageGif, ".gif", 225, 141)
       +        c.Assert(imageGif.Name(), qt.Equals, "sunset.jpg")
       +        c.Assert(imageGif.MediaType().String(), qt.Equals, "image/gif")
       +
       +        assertFileCache(c, fileCache, path.Base(imageGif.RelPermalink()), 225, 141)
       +}
       +
        // https://github.com/gohugoio/hugo/issues/4261
        func TestImageTransformLongFilename(t *testing.T) {
                c := qt.New(t)
   DIR diff --git a/resources/images/config.go b/resources/images/config.go
       @@ -187,7 +187,8 @@ func DecodeImageConfig(action, config string, defaults Imaging) (ImageConfig, er
                                } else {
                                        return c, errors.New("invalid image dimensions")
                                }
       -
       +                } else if f, ok := ImageFormatFromExt("." + part); ok {
       +                        c.TargetFormat = f
                        }
                }
        
       @@ -212,6 +213,9 @@ func DecodeImageConfig(action, config string, defaults Imaging) (ImageConfig, er
        
        // ImageConfig holds configuration to create a new image from an existing one, resize etc.
        type ImageConfig struct {
       +        // This defines the output format of the output image. It defaults to the source format
       +        TargetFormat Format
       +
                Action string
        
                // If set, this will be used as the key in filenames etc.
   DIR diff --git a/resources/images/image.go b/resources/images/image.go
       @@ -23,6 +23,7 @@ import (
                "io"
                "sync"
        
       +        "github.com/gohugoio/hugo/media"
                "github.com/gohugoio/hugo/resources/images/exif"
        
                "github.com/disintegration/gift"
       @@ -59,7 +60,7 @@ type Image struct {
        }
        
        func (i *Image) EncodeTo(conf ImageConfig, img image.Image, w io.Writer) error {
       -        switch i.Format {
       +        switch conf.TargetFormat {
                case JPEG:
        
                        var rgba *image.RGBA
       @@ -250,6 +251,35 @@ const (
                BMP
        )
        
       +// RequiresDefaultQuality returns if the default quality needs to be applied to images of this format
       +func (f Format) RequiresDefaultQuality() bool {
       +        return f == JPEG
       +}
       +
       +// DefaultExtension returns the default file extension of this format, starting with a dot.
       +// For example: .jpg for JPEG
       +func (f Format) DefaultExtension() string {
       +        return f.MediaType().FullSuffix()
       +}
       +
       +// MediaType returns the media type of this image, e.g. image/jpeg for JPEG
       +func (f Format) MediaType() media.Type {
       +        switch f {
       +        case JPEG:
       +                return media.JPGType
       +        case PNG:
       +                return media.PNGType
       +        case GIF:
       +                return media.GIFType
       +        case TIFF:
       +                return media.TIFFType
       +        case BMP:
       +                return media.BMPType
       +        default:
       +                panic(fmt.Sprintf("%d is not a valid image format", f))
       +        }
       +}
       +
        type imageConfig struct {
                config       image.Config
                configInit   sync.Once
   DIR diff --git a/resources/resource.go b/resources/resource.go
       @@ -220,6 +220,10 @@ func (l *genericResource) MediaType() media.Type {
                return l.mediaType
        }
        
       +func (l *genericResource) setMediaType(mediaType media.Type) {
       +        l.mediaType = mediaType
       +}
       +
        func (l *genericResource) Name() string {
                return l.name
        }
   DIR diff --git a/resources/resource_metadata.go b/resources/resource_metadata.go
       @@ -18,6 +18,7 @@ import (
                "strconv"
        
                "github.com/gohugoio/hugo/hugofs/glob"
       +        "github.com/gohugoio/hugo/media"
                "github.com/gohugoio/hugo/resources/resource"
        
                "github.com/pkg/errors"
       @@ -42,6 +43,7 @@ type metaAssignerProvider interface {
        type metaAssigner interface {
                setTitle(title string)
                setName(name string)
       +        setMediaType(mediaType media.Type)
                updateParams(params map[string]interface{})
        }