summaryrefslogtreecommitdiffstats
path: root/resources/images/config.go
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2023-01-04 18:24:36 +0100
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2023-05-16 18:01:29 +0200
commit241b21b0fd34d91fccb2ce69874110dceae6f926 (patch)
treed4e0118eac7e9c42f065815447a70805f8d6ad3e /resources/images/config.go
parent6aededf6b42011c3039f5f66487a89a8dd65e0e7 (diff)
Create a struct with all of Hugo's config options
Primary motivation is documentation, but it will also hopefully simplify the code. Also, * Lower case the default output format names; this is in line with the custom ones (map keys) and how it's treated all the places. This avoids doing `stringds.EqualFold` everywhere. Closes #10896 Closes #10620
Diffstat (limited to 'resources/images/config.go')
-rw-r--r--resources/images/config.go175
1 files changed, 111 insertions, 64 deletions
diff --git a/resources/images/config.go b/resources/images/config.go
index 09a7016c1..a3ca0c359 100644
--- a/resources/images/config.go
+++ b/resources/images/config.go
@@ -19,16 +19,16 @@ import (
"strconv"
"strings"
- "github.com/gohugoio/hugo/identity"
+ "github.com/gohugoio/hugo/common/maps"
+ "github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/media"
+ "github.com/mitchellh/mapstructure"
"errors"
"github.com/bep/gowebp/libwebp/webpoptions"
"github.com/disintegration/gift"
-
- "github.com/mitchellh/mapstructure"
)
var (
@@ -47,12 +47,12 @@ var (
}
imageFormatsBySubType = map[string]Format{
- media.JPEGType.SubType: JPEG,
- media.PNGType.SubType: PNG,
- media.TIFFType.SubType: TIFF,
- media.BMPType.SubType: BMP,
- media.GIFType.SubType: GIF,
- media.WEBPType.SubType: WEBP,
+ media.Builtin.JPEGType.SubType: JPEG,
+ media.Builtin.PNGType.SubType: PNG,
+ media.Builtin.TIFFType.SubType: TIFF,
+ media.Builtin.BMPType.SubType: BMP,
+ media.Builtin.GIFType.SubType: GIF,
+ media.Builtin.WEBPType.SubType: WEBP,
}
// Add or increment if changes to an image format's processing requires
@@ -121,66 +121,83 @@ func ImageFormatFromMediaSubType(sub string) (Format, bool) {
const (
defaultJPEGQuality = 75
defaultResampleFilter = "box"
- defaultBgColor = "ffffff"
+ defaultBgColor = "#ffffff"
defaultHint = "photo"
)
-var defaultImaging = Imaging{
- ResampleFilter: defaultResampleFilter,
- BgColor: defaultBgColor,
- Hint: defaultHint,
- Quality: defaultJPEGQuality,
-}
-
-func DecodeConfig(m map[string]any) (ImagingConfig, error) {
- if m == nil {
- m = make(map[string]any)
+var (
+ defaultImaging = map[string]any{
+ "resampleFilter": defaultResampleFilter,
+ "bgColor": defaultBgColor,
+ "hint": defaultHint,
+ "quality": defaultJPEGQuality,
}
- i := ImagingConfig{
- Cfg: defaultImaging,
- CfgHash: identity.HashString(m),
- }
+ defaultImageConfig *config.ConfigNamespace[ImagingConfig, ImagingConfigInternal]
+)
- if err := mapstructure.WeakDecode(m, &i.Cfg); err != nil {
- return i, err
+func init() {
+ var err error
+ defaultImageConfig, err = DecodeConfig(defaultImaging)
+ if err != nil {
+ panic(err)
}
+}
- if err := i.Cfg.init(); err != nil {
- return i, err
+func DecodeConfig(in map[string]any) (*config.ConfigNamespace[ImagingConfig, ImagingConfigInternal], error) {
+ if in == nil {
+ in = make(map[string]any)
}
- var err error
- i.BgColor, err = hexStringToColor(i.Cfg.BgColor)
- if err != nil {
- return i, err
- }
+ buildConfig := func(in any) (ImagingConfigInternal, any, error) {
+ m, err := maps.ToStringMapE(in)
+ if err != nil {
+ return ImagingConfigInternal{}, nil, err
+ }
+ // Merge in the defaults.
+ maps.MergeShallow(m, defaultImaging)
+
+ var i ImagingConfigInternal
+ if err := mapstructure.Decode(m, &i.Imaging); err != nil {
+ return i, nil, err
+ }
+
+ if err := i.Imaging.init(); err != nil {
+ return i, nil, err
+ }
+
+ i.BgColor, err = hexStringToColor(i.Imaging.BgColor)
+ if err != nil {
+ return i, nil, err
+ }
- if i.Cfg.Anchor != "" && i.Cfg.Anchor != smartCropIdentifier {
- anchor, found := anchorPositions[i.Cfg.Anchor]
+ if i.Imaging.Anchor != "" && i.Imaging.Anchor != smartCropIdentifier {
+ anchor, found := anchorPositions[i.Imaging.Anchor]
+ if !found {
+ return i, nil, fmt.Errorf("invalid anchor value %q in imaging config", i.Anchor)
+ }
+ i.Anchor = anchor
+ }
+
+ filter, found := imageFilters[i.Imaging.ResampleFilter]
if !found {
- return i, fmt.Errorf("invalid anchor value %q in imaging config", i.Anchor)
+ return i, nil, fmt.Errorf("%q is not a valid resample filter", filter)
}
- i.Anchor = anchor
- } else {
- i.Cfg.Anchor = smartCropIdentifier
- }
- filter, found := imageFilters[i.Cfg.ResampleFilter]
- if !found {
- return i, fmt.Errorf("%q is not a valid resample filter", filter)
+ i.ResampleFilter = filter
+
+ return i, nil, nil
}
- i.ResampleFilter = filter
- if strings.TrimSpace(i.Cfg.Exif.IncludeFields) == "" && strings.TrimSpace(i.Cfg.Exif.ExcludeFields) == "" {
- // Don't change this for no good reason. Please don't.
- i.Cfg.Exif.ExcludeFields = "GPS|Exif|Exposure[M|P|B]|Contrast|Resolution|Sharp|JPEG|Metering|Sensing|Saturation|ColorSpace|Flash|WhiteBalance"
+ ns, err := config.DecodeNamespace[ImagingConfig](in, buildConfig)
+ if err != nil {
+ return nil, fmt.Errorf("failed to decode media types: %w", err)
}
+ return ns, nil
- return i, nil
}
-func DecodeImageConfig(action, config string, defaults ImagingConfig, sourceFormat Format) (ImageConfig, error) {
+func DecodeImageConfig(action, config string, defaults *config.ConfigNamespace[ImagingConfig, ImagingConfigInternal], sourceFormat Format) (ImageConfig, error) {
var (
c ImageConfig = GetDefaultImageConfig(action, defaults)
err error
@@ -268,8 +285,8 @@ func DecodeImageConfig(action, config string, defaults ImagingConfig, sourceForm
}
if c.FilterStr == "" {
- c.FilterStr = defaults.Cfg.ResampleFilter
- c.Filter = defaults.ResampleFilter
+ c.FilterStr = defaults.Config.Imaging.ResampleFilter
+ c.Filter = defaults.Config.ResampleFilter
}
if c.Hint == 0 {
@@ -277,8 +294,8 @@ func DecodeImageConfig(action, config string, defaults ImagingConfig, sourceForm
}
if c.AnchorStr == "" {
- c.AnchorStr = defaults.Cfg.Anchor
- c.Anchor = defaults.Anchor
+ c.AnchorStr = defaults.Config.Imaging.Anchor
+ c.Anchor = defaults.Config.Anchor
}
// default to the source format
@@ -288,13 +305,13 @@ func DecodeImageConfig(action, config string, defaults ImagingConfig, sourceForm
if c.Quality <= 0 && c.TargetFormat.RequiresDefaultQuality() {
// We need a quality setting for all JPEGs and WEBPs.
- c.Quality = defaults.Cfg.Quality
+ c.Quality = defaults.Config.Imaging.Quality
}
if c.BgColor == nil && c.TargetFormat != sourceFormat {
if sourceFormat.SupportsTransparency() && !c.TargetFormat.SupportsTransparency() {
- c.BgColor = defaults.BgColor
- c.BgColorStr = defaults.Cfg.BgColor
+ c.BgColor = defaults.Config.BgColor
+ c.BgColorStr = defaults.Config.Imaging.BgColor
}
}
@@ -389,22 +406,43 @@ func (i ImageConfig) GetKey(format Format) string {
return k
}
-type ImagingConfig struct {
+type ImagingConfigInternal struct {
BgColor color.Color
Hint webpoptions.EncodingPreset
ResampleFilter gift.Resampling
Anchor gift.Anchor
- // Config as provided by the user.
- Cfg Imaging
+ Imaging ImagingConfig
+}
+
+func (i *ImagingConfigInternal) Compile(externalCfg *ImagingConfig) error {
+ var err error
+ i.BgColor, err = hexStringToColor(externalCfg.BgColor)
+ if err != nil {
+ return err
+ }
+
+ if externalCfg.Anchor != "" && externalCfg.Anchor != smartCropIdentifier {
+ anchor, found := anchorPositions[externalCfg.Anchor]
+ if !found {
+ return fmt.Errorf("invalid anchor value %q in imaging config", i.Anchor)
+ }
+ i.Anchor = anchor
+ }
+
+ filter, found := imageFilters[externalCfg.ResampleFilter]
+ if !found {
+ return fmt.Errorf("%q is not a valid resample filter", filter)
+ }
+ i.ResampleFilter = filter
+
+ return nil
- // Hash of the config map provided by the user.
- CfgHash string
}
-// Imaging contains default image processing configuration. This will be fetched
+// ImagingConfig contains default image processing configuration. This will be fetched
// from site (or language) config.
-type Imaging struct {
+type ImagingConfig struct {
// Default image quality setting (1-100). Only used for JPEG images.
Quality int
@@ -426,7 +464,7 @@ type Imaging struct {
Exif ExifConfig
}
-func (cfg *Imaging) init() error {
+func (cfg *ImagingConfig) init() error {
if cfg.Quality < 0 || cfg.Quality > 100 {
return errors.New("image quality must be a number between 1 and 100")
}
@@ -436,6 +474,15 @@ func (cfg *Imaging) init() error {
cfg.ResampleFilter = strings.ToLower(cfg.ResampleFilter)
cfg.Hint = strings.ToLower(cfg.Hint)
+ if cfg.Anchor == "" {
+ cfg.Anchor = smartCropIdentifier
+ }
+
+ if strings.TrimSpace(cfg.Exif.IncludeFields) == "" && strings.TrimSpace(cfg.Exif.ExcludeFields) == "" {
+ // Don't change this for no good reason. Please don't.
+ cfg.Exif.ExcludeFields = "GPS|Exif|Exposure[M|P|B]|Contrast|Resolution|Sharp|JPEG|Metering|Sensing|Saturation|ColorSpace|Flash|WhiteBalance"
+ }
+
return nil
}