From 4a2cbe887bb6d789462f0c35d5090e713a3c7b32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=20Sim=C3=B3?= Date: Thu, 12 Jun 2025 15:31:17 +0200 Subject: [PATCH] render-image.html to solve image resources address in multilingual sites with page bundles --- layouts/_default/_markup/render-image.html | 181 +++++++++++++++++++-- 1 file changed, 165 insertions(+), 16 deletions(-) diff --git a/layouts/_default/_markup/render-image.html b/layouts/_default/_markup/render-image.html index 148cbaf..ee974e4 100644 --- a/layouts/_default/_markup/render-image.html +++ b/layouts/_default/_markup/render-image.html @@ -1,19 +1,168 @@ -{{- if .Page.Site.Params.BookPortableLinks -}} - {{- template "portable-image" . -}} -{{- else -}} - {{ .Text }} -{{- end -}} +{{- /* Last modified: 2025-04-25T11:05:22-07:00 */}} -{{- define "portable-image" -}} - {{- $isRemote := or (in .Destination "://") (strings.HasPrefix .Destination "//") }} - {{- if not $isRemote }} - {{- $path := print .Page.File.Dir .Destination }} - {{- if strings.HasPrefix .Destination "/" }} - {{- $path = print "/static" .Destination }} - {{- end }} - {{- if not (fileExists $path) }} - {{- warnf "Image '%s' not found in '%s'" .Destination .Page.File }} +{{- /* +Copyright 2025 Veriphor, LLC +https://www.veriphor.com/articles/link-and-image-render-hooks/ + +Licensed under the Apache License, Version 2.0 (the "License"); you may not +use this file except in compliance with the License. You may obtain a copy of +the License at + +https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations under +the License. +*/}} + +{{- /* +This render hook resolves internal destinations by looking for a matching: + + 1. Page resource (an image in the current page bundle) + 2. Section resource (an image in the current section) + 3. Global resource (an image in the assets directory) + +It skips the section resource lookup if the current page is a leaf bundle, and +captures external destinations as resources for local hosting. + +You must place global resources in the assets directory. If you have placed +your resources in the static directory, and you are unable or unwilling to move +them, you must mount the static directory to the assets directory by including +both of these entries in your site configuration: + + [[module.mounts]] + source = 'assets' + target = 'assets' + + [[module.mounts]] + source = 'static' + target = 'assets' + +By default, if this render hook is unable to resolve a destination, it passes +the destination through without modification. To emit a warning or error, set +the error level in your site configuration: + + [params.render_hooks.image] + errorLevel = 'warning' # ignore (default), warning, or error (fails the build) + +Image render hooks are also used to: + + - Resize, crop, rotate, filter, and convert images + - Build responsive images using srcset and sizes attributes + - Wrap images inside of a picture element + - Transform standalone images into figure elements + +To perform any of these operations, you can “hook” into this render hook with a +partial template, after the render hook has captured the resource. + +@context {map} Attributes The Markdown attributes, available if (a) markup.goldmark.parser.attribute.block is true, and (b) markup.goldmark.parser.wrapStandAloneImageWithinParagraph is false in site configuration. +@context {string} Destination The image destination. +@context {bool} IsBlock Returns true if a standalone image is not wrapped within a paragraph element. +@context {int} Ordinal The zero-based ordinal of the image on the page. +@context {page} Page A reference to the page containing the image. +@context {string} PlainText The image description as plain text. +@context {string} Text The image description. +@context {string} Title The image title. + +@returns {template.html} +*/}} + +{{- /* Initialize. */}} +{{- $renderHookName := "image" }} + +{{- /* Verify minimum required version. */}} +{{- $minHugoVersion := "0.147.0" }} +{{- if lt hugo.Version $minHugoVersion }} + {{- errorf "The %q render hook requires Hugo v%s or later." $renderHookName $minHugoVersion }} +{{- end }} + +{{- /* Error level when unable to resolve destination: ignore, warning, or error. */}} +{{- $errorLevel := or site.Params.render_hooks.image.errorLevel "ignore" | lower }} + +{{- /* Validate error level. */}} +{{- if not (in (slice "ignore" "warning" "error") $errorLevel) }} + {{- errorf "The %q render hook is misconfigured. The errorLevel %q is invalid. Please check your site configuration." $renderHookName $errorLevel }} +{{- end }} + +{{- /* Determine content path for warning and error messages. */}} +{{- $contentPath := "" }} +{{- with .Page.File }} + {{- $contentPath = .Path }} +{{- else }} + {{- $contentPath = .Path }} +{{- end }} + +{{- /* Parse destination. */}} +{{- $u := urls.Parse .Destination }} + +{{- /* Set common message. */}} +{{- $msg := printf "The %q render hook was unable to resolve the destination %q in %s" $renderHookName $u.String $contentPath }} + +{{- /* Get image resource. */}} +{{- $r := "" }} +{{- if $u.IsAbs }} + {{- with try (resources.GetRemote $u.String) }} + {{- with .Err }} + {{- if eq $errorLevel "warning" }} + {{- warnf "%s. See %s" . $contentPath }} + {{- else if eq $errorLevel "error" }} + {{- errorf "%s. See %s" . $contentPath }} + {{- end }} + {{- else with .Value }} + {{- /* Destination is a remote resource. */}} + {{- $r = . }} + {{- else }} + {{- if eq $errorLevel "warning" }} + {{- warnf $msg }} + {{- else if eq $errorLevel "error" }} + {{- errorf $msg }} + {{- end }} {{- end }} {{- end }} - {{ .Text }} -{{- end -}} +{{- else }} + {{- with .PageInner.Resources.Get (strings.TrimPrefix "./" $u.Path) }} + {{- /* Destination is a page resource. */}} + {{- $r = . }} + {{- else with (and (ne .Page.BundleType "leaf") (.Page.CurrentSection.Resources.Get (strings.TrimPrefix "./" $u.Path))) }} + {{- /* Destination is a section resource, and current page is not a leaf bundle. */}} + {{- $r = . }} + {{- else with resources.Get $u.Path }} + {{- /* Destination is a global resource. */}} + {{- $r = . }} + {{- else }} + {{- if eq $errorLevel "warning" }} + {{- warnf $msg }} + {{- else if eq $errorLevel "error" }} + {{- errorf $msg }} + {{- end }} + {{- end }} +{{- end }} + +{{- /* Determine id attribute. */}} +{{- $id := printf "h-rh-i-%d" .Ordinal }} +{{- with .Attributes.id }} + {{- $id = . }} +{{- end }} + +{{- /* Initialize attributes. */}} +{{- $attrs := merge .Attributes (dict "id" $id "alt" .PlainText "title" (.Title | transform.HTMLEscape) "src" $u.String) }} + +{{- /* Merge attributes from resource. */}} +{{- with $r }} + {{- $attrs = merge $attrs (dict "src" .RelPermalink) }} + {{- if not (eq .MediaType.SubType "svg") }} + {{- $attrs = merge $attrs (dict "height" (string .Height) "width" (string .Width)) }} + {{- end }} +{{- end }} + +{{- /* Render image element. */ -}} + +{{- /**/ -}} \ No newline at end of file