diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2023-01-04 18:24:36 +0100 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2023-05-16 18:01:29 +0200 |
commit | 241b21b0fd34d91fccb2ce69874110dceae6f926 (patch) | |
tree | d4e0118eac7e9c42f065815447a70805f8d6ad3e /resources/images/config.go | |
parent | 6aededf6b42011c3039f5f66487a89a8dd65e0e7 (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.go | 175 |
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 } |