commit df2198f2b6e1bfef0e54dd9281760d87ba6a6b65 from: Anton Kasimov date: Fri Aug 21 15:25:03 2026 UTC Поддержка вывода image для markdown commit - c3f5671b4eb04fba2cae977039bcf15265cb3b0e commit + df2198f2b6e1bfef0e54dd9281760d87ba6a6b65 blob - /dev/null blob + 3d99627da02e3c450ffdf52760ecf75f8a5db911 (mode 644) --- /dev/null +++ config/_default/markup.toml @@ -0,0 +1,5 @@ +[goldmark.parser] +wrapStandAloneImageWithinParagraph = false + +[goldmark.parser.attribute] +block = true blob - /dev/null blob + 173240eb4e4cd63b3c7b26ba44b212440ce249c7 (mode 644) --- /dev/null +++ layouts/_markup/render-image.html @@ -0,0 +1,30 @@ +{{- $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 @@ -1,258 +1,166 @@ {{- $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 ) -}} - - - - + {{- $fallback := index + $webp.variants + (printf "%d" $fallbackWidth) + -}} - {{- else -}} - + {{/* 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 @@ -262,9 +170,10 @@ {{- 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 @@ -273,14 +182,135 @@ {{- 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" + ) -}} + + + + + + + {{/* Lossless: WebP */}} + + {{- else -}} + + + + {{- 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) + -}} + + + {{- end -}} + +{{/* Plain URL/path */}} + +{{- else if $src -}} + {{- $attributes = merge - $defaults + (dict "alt" "") $attributes - (dict "src" $image.RelPermalink) + (dict "src" $src) -}} + +{{- else -}} + + {{- errorf "image.html: image or src is required" -}} + {{- end -}}