commit - c3f5671b4eb04fba2cae977039bcf15265cb3b0e
commit + df2198f2b6e1bfef0e54dd9281760d87ba6a6b65
blob - /dev/null
blob + 3d99627da02e3c450ffdf52760ecf75f8a5db911 (mode 644)
--- /dev/null
+++ config/_default/markup.toml
+[goldmark.parser]
+wrapStandAloneImageWithinParagraph = false
+
+[goldmark.parser.attribute]
+block = true
blob - /dev/null
blob + 173240eb4e4cd63b3c7b26ba44b212440ce249c7 (mode 644)
--- /dev/null
+++ layouts/_markup/render-image.html
+{{- $url := urls.Parse .Destination -}}
+{{- $image := false -}}
+
+{{- if not $url.IsAbs -}}
+ {{- $path := strings.TrimPrefix "./" $url.Path -}}
+
+ {{- $image = or
+ (.PageInner.Resources.Get $path)
+ (resources.Get $path)
+ -}}
+{{- end -}}
+
+{{- $attributes := merge
+ (dict "alt" .PlainText)
+ .Attributes
+-}}
+
+{{- with .Title -}}
+ {{- $attributes = merge
+ (dict "title" .)
+ $attributes
+ -}}
+{{- end -}}
+
+{{- partial "image.html" (dict
+ "image" $image
+ "src" .Destination
+ "page" .PageInner
+ "attributes" $attributes
+) -}}
blob - cbf1202e44f25e6afa8801855925e80f45231689
blob + 9c54707dfa43337e4b7d38c5bdf40fac5f45a796
--- layouts/_partials/image.html
+++ layouts/_partials/image.html
{{- $image := .image -}}
+{{- $src := .src -}}
{{- $page := .page -}}
{{- $attributes := .attributes | default dict -}}
-{{- if not $image -}}
- {{- errorf "image.html: image is required" -}}
-{{- end -}}
-{{- if not (reflect.IsImageResource $image) -}}
- {{- errorf "image.html: %q is not an image resource" $image.Name -}}
-{{- end -}}
+{{/* Hugo image resource */}}
+{{- if $image -}}
-{{/* Processable raster image */}}
+ {{- if not (reflect.IsImageResource $image) -}}
+ {{- errorf "image.html: %q is not an image resource" $image.Name -}}
+ {{- end -}}
-{{- if reflect.IsImageResourceProcessable $image -}}
- {{/* Configuration: site -> page -> invocation */}}
+ {{/* Processable raster image */}}
- {{- $config := site.Params.radium.images -}}
+ {{- if reflect.IsImageResourceProcessable $image -}}
- {{- with $page -}}
- {{- with .Params.radium.images -}}
- {{- $config = merge $config . -}}
+ {{/* Configuration: site -> page -> invocation */}}
+
+ {{- $config := site.Params.radium.images -}}
+
+ {{- with $page -}}
+ {{- with .Params.radium.images -}}
+ {{- $config = merge $config . -}}
+ {{- end -}}
{{- end -}}
- {{- end -}}
- {{- $widths := .widths | default $config.widths -}}
- {{- $sizes := .sizes | default $config.sizes -}}
- {{- $mode := .mode | default $config.mode | lower -}}
+ {{- $widths := .widths | default $config.widths -}}
+ {{- $sizes := .sizes | default $config.sizes -}}
+ {{- $mode := .mode | default $config.mode | lower -}}
- {{/* Normalize configured widths */}}
+ {{/* Normalize configured widths */}}
- {{- $normalized := slice -}}
+ {{- $normalized := slice -}}
- {{- range $widths -}}
- {{- $width := int . -}}
+ {{- range $widths -}}
+ {{- $width := int . -}}
- {{- if gt $width 0 -}}
- {{- $normalized = $normalized | append $width -}}
+ {{- if gt $width 0 -}}
+ {{- $normalized = $normalized | append $width -}}
+ {{- end -}}
{{- end -}}
- {{- end -}}
- {{- $widths = collections.Sort (collections.Uniq $normalized) -}}
+ {{- $widths = collections.Sort (collections.Uniq $normalized) -}}
- {{- if not $widths -}}
- {{- errorf "image.html: width ladder is empty" -}}
- {{- end -}}
+ {{- if not $widths -}}
+ {{- errorf "image.html: width ladder is empty" -}}
+ {{- end -}}
- {{/*
- Build the actual ladder.
+ {{/*
+ Never upscale.
- Never upscale configured variants, but always include
- the original width.
+ Always include the original width:
- 1300 + [480 768 1024 1440 1920]
- -> [480 768 1024 1300]
+ 1300 + [480 768 1024 1440 1920]
+ -> [480 768 1024 1300]
- 4000 + [480 768 1024 1440 1920]
- -> [480 768 1024 1440 1920 4000]
- */}}
+ 4000 + [480 768 1024 1440 1920]
+ -> [480 768 1024 1440 1920 4000]
+ */}}
- {{- $targets := slice -}}
+ {{- $targets := slice -}}
- {{- range $widths -}}
- {{- if le . $image.Width -}}
- {{- $targets = $targets | append . -}}
+ {{- range $widths -}}
+ {{- if le . $image.Width -}}
+ {{- $targets = $targets | append . -}}
+ {{- end -}}
{{- end -}}
- {{- end -}}
- {{- $targets = $targets | append $image.Width -}}
- {{- $targets = collections.Sort (collections.Uniq $targets) -}}
+ {{- $targets = $targets | append $image.Width -}}
+ {{- $targets = collections.Sort (collections.Uniq $targets) -}}
- {{/*
- src gets the largest configured width that does not
- exceed the original.
+ {{/*
+ src uses the largest configured size not exceeding
+ the original.
- The original itself remains available through srcset.
- */}}
+ The full original resolution remains available in srcset.
+ */}}
- {{- $fallbackWidth := $image.Width -}}
- {{- $maxWidth := index $widths (sub (len $widths) 1) -}}
+ {{- $fallbackWidth := $image.Width -}}
+ {{- $maxWidth := index $widths (sub (len $widths) 1) -}}
- {{- if gt $image.Width $maxWidth -}}
- {{- $fallbackWidth = $maxWidth -}}
- {{- end -}}
+ {{- if gt $image.Width $maxWidth -}}
+ {{- $fallbackWidth = $maxWidth -}}
+ {{- end -}}
- {{/* Compression mode */}}
+ {{/* Compression mode */}}
- {{- if eq $mode "auto" -}}
+ {{- if eq $mode "auto" -}}
- {{- if eq $image.MediaType.Type "image/jpeg" -}}
- {{- $mode = "lossy" -}}
+ {{- if eq $image.MediaType.Type "image/jpeg" -}}
- {{- else if in
- (slice "image/png" "image/bmp")
- $image.MediaType.Type
- -}}
- {{- $mode = "lossless" -}}
+ {{- $mode = "lossy" -}}
- {{- else -}}
- {{- errorf
- "image.html: cannot determine compression mode of %q (%s); specify mode"
- $image.Name
+ {{- else if in
+ (slice "image/png" "image/bmp")
$image.MediaType.Type
-}}
+
+ {{- $mode = "lossless" -}}
+
+ {{- else -}}
+
+ {{- errorf
+ "image.html: cannot determine compression mode of %q (%s); specify mode"
+ $image.Name
+ $image.MediaType.Type
+ -}}
+
+ {{- end -}}
+
+ {{- else if not (in (slice "lossy" "lossless") $mode) -}}
+
+ {{- errorf
+ "image.html: invalid mode %q; expected auto, lossy, or lossless"
+ $mode
+ -}}
+
{{- end -}}
- {{- else if not (in (slice "lossy" "lossless") $mode) -}}
- {{- errorf
- "image.html: invalid mode %q; expected auto, lossy, or lossless"
- $mode
- -}}
+ {{/* WebP variants */}}
- {{- end -}}
-
-
- {{/* WebP variants */}}
-
- {{- $webp := partial "image/variants.html" (dict
- "image" $image
- "widths" $targets
- "format" "webp"
- "mode" $mode
- ) -}}
-
- {{- $fallback := index
- $webp.variants
- (printf "%d" $fallbackWidth)
- -}}
-
-
- {{/* Width / height */}}
-
- {{- $defaults := dict "alt" "" -}}
-
- {{- $hasWidth := isset $attributes "width" -}}
- {{- $hasHeight := isset $attributes "height" -}}
-
- {{- if and (not $hasWidth) (not $hasHeight) -}}
-
- {{- $defaults = merge $defaults (dict
- "width" $fallback.Width
- "height" $fallback.Height
- ) -}}
-
- {{- else if and $hasWidth (not $hasHeight) -}}
-
- {{- $width := int $attributes.width -}}
- {{- $height := int (math.Round (mul
- $fallback.Height
- (div (float $width) $fallback.Width)
- )) -}}
-
- {{- $defaults = merge $defaults (dict
- "height" $height
- ) -}}
-
- {{- else if and $hasHeight (not $hasWidth) -}}
-
- {{- $height := int $attributes.height -}}
- {{- $width := int (math.Round (mul
- $fallback.Width
- (div (float $height) $fallback.Height)
- )) -}}
-
- {{- $defaults = merge $defaults (dict
- "width" $width
- ) -}}
-
- {{- end -}}
-
-
- {{/* User attributes override defaults.
- Generated resource attributes override everything. */}}
-
- {{- $attributes = merge
- $defaults
- $attributes
- (dict
- "src" $fallback.RelPermalink
- "srcset" $webp.srcset
- "sizes" $sizes
- )
- -}}
-
-
- {{/* Lossy: AVIF + WebP fallback */}}
-
- {{- if eq $mode "lossy" -}}
-
- {{- $avif := partial "image/variants.html" (dict
+ {{- $webp := partial "image/variants.html" (dict
"image" $image
"widths" $targets
- "format" "avif"
- "mode" "lossy"
+ "format" "webp"
+ "mode" $mode
) -}}
- <picture>
- <source{{ partial "attrs.html" (dict
- "type" "image/avif"
- "srcset" $avif.srcset
- "sizes" $sizes
- ) }}>
- <img{{ partial "attrs.html" $attributes }}>
- </picture>
+ {{- $fallback := index
+ $webp.variants
+ (printf "%d" $fallbackWidth)
+ -}}
- {{- else -}}
- <img{{ partial "attrs.html" $attributes }}>
+ {{/* Default attributes */}}
- {{- end -}}
+ {{- $defaults := dict "alt" "" -}}
-
-{{/* Non-processable image */}}
-
-{{- else -}}
-
- {{/* SVG can still be minified. */}}
-
- {{- if eq $image.MediaType.Type "image/svg+xml" -}}
- {{- $image = resources.Minify $image -}}
- {{- end -}}
-
- {{- $defaults := dict "alt" "" -}}
-
-
- {{/*
- Some non-processable formats have readable dimensions.
- SVG and ICO, for example, don't.
- */}}
-
- {{- if reflect.IsImageResourceWithMeta $image -}}
-
{{- $hasWidth := isset $attributes "width" -}}
{{- $hasHeight := isset $attributes "height" -}}
{{- if and (not $hasWidth) (not $hasHeight) -}}
{{- $defaults = merge $defaults (dict
- "width" $image.Width
- "height" $image.Height
+ "width" $fallback.Width
+ "height" $fallback.Height
) -}}
{{- else if and $hasWidth (not $hasHeight) -}}
{{- $width := int $attributes.width -}}
- {{- $height := int (math.Round (mul
- $image.Height
- (div (float $width) $image.Width)
+
+ {{- $height := cast.ToInt (math.Round (math.Div
+ (math.Mul (cast.ToFloat $width) $fallback.Height)
+ $fallback.Width
)) -}}
{{- $defaults = merge $defaults (dict
{{- else if and $hasHeight (not $hasWidth) -}}
{{- $height := int $attributes.height -}}
- {{- $width := int (math.Round (mul
- $image.Width
- (div (float $height) $image.Height)
+
+ {{- $width := cast.ToInt (math.Round (math.Div
+ (math.Mul (cast.ToFloat $height) $fallback.Width)
+ $fallback.Height
)) -}}
{{- $defaults = merge $defaults (dict
{{- end -}}
+
+ {{/*
+ Priority:
+ defaults
+ -> caller attributes
+ -> generated resource attributes
+ */}}
+
+ {{- $attributes = merge
+ $defaults
+ $attributes
+ (dict
+ "src" $fallback.RelPermalink
+ "srcset" $webp.srcset
+ "sizes" $sizes
+ )
+ -}}
+
+
+ {{/* Lossy: AVIF + WebP */}}
+
+ {{- if eq $mode "lossy" -}}
+
+ {{- $avif := partial "image/variants.html" (dict
+ "image" $image
+ "widths" $targets
+ "format" "avif"
+ "mode" "lossy"
+ ) -}}
+
+ <picture>
+ <source{{ partial "attrs.html" (dict
+ "type" "image/avif"
+ "srcset" $avif.srcset
+ "sizes" $sizes
+ ) }}>
+ <img{{ partial "attrs.html" $attributes }}>
+ </picture>
+
+ {{/* Lossless: WebP */}}
+
+ {{- else -}}
+
+ <img{{ partial "attrs.html" $attributes }}>
+
+ {{- end -}}
+
+
+ {{/* Non-processable image resource */}}
+
+ {{- else -}}
+
+ {{- if eq $image.MediaType.Type "image/svg+xml" -}}
+ {{- $image = resources.Minify $image -}}
+ {{- end -}}
+
+ {{- $defaults := dict "alt" "" -}}
+
+
+ {{/* Add intrinsic dimensions when Hugo knows them */}}
+
+ {{- if reflect.IsImageResourceWithMeta $image -}}
+
+ {{- $hasWidth := isset $attributes "width" -}}
+ {{- $hasHeight := isset $attributes "height" -}}
+
+ {{- if and (not $hasWidth) (not $hasHeight) -}}
+
+ {{- $defaults = merge $defaults (dict
+ "width" $image.Width
+ "height" $image.Height
+ ) -}}
+
+ {{- else if and $hasWidth (not $hasHeight) -}}
+
+ {{- $width := int $attributes.width -}}
+
+ {{- $height := cast.ToInt (math.Round (math.Div
+ (math.Mul (cast.ToFloat $width) $image.Height)
+ $image.Width
+ )) -}}
+
+ {{- $defaults = merge $defaults (dict
+ "height" $height
+ ) -}}
+
+ {{- else if and $hasHeight (not $hasWidth) -}}
+
+ {{- $height := int $attributes.height -}}
+
+ {{- $width := cast.ToInt (math.Round (math.Div
+ (math.Mul (cast.ToFloat $height) $image.Width)
+ $image.Height
+ )) -}}
+
+ {{- $defaults = merge $defaults (dict
+ "width" $width
+ ) -}}
+
+ {{- end -}}
+
+ {{- end -}}
+
+ {{- $attributes = merge
+ $defaults
+ $attributes
+ (dict "src" $image.RelPermalink)
+ -}}
+
+ <img{{ partial "attrs.html" $attributes }}>
+
{{- end -}}
+
+{{/* Plain URL/path */}}
+
+{{- else if $src -}}
+
{{- $attributes = merge
- $defaults
+ (dict "alt" "")
$attributes
- (dict "src" $image.RelPermalink)
+ (dict "src" $src)
-}}
<img{{ partial "attrs.html" $attributes }}>
+
+{{- else -}}
+
+ {{- errorf "image.html: image or src is required" -}}
+
{{- end -}}