|
| 1 | +{{- /* Based on https://www.veriphor.com/articles/link-and-image-render-hooks/#image-render-hook */}} |
| 2 | + |
| 3 | +{{- /* Last modified: 2023-12-09T16:29:48-08:00 */}} |
| 4 | + |
| 5 | +{{- /* |
| 6 | +Copyright 2023 Veriphor LLC |
| 7 | + |
| 8 | +Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| 9 | +use this file except in compliance with the License. You may obtain a copy of |
| 10 | +the License at |
| 11 | + |
| 12 | +https://www.apache.org/licenses/LICENSE-2.0 |
| 13 | + |
| 14 | +Unless required by applicable law or agreed to in writing, software |
| 15 | +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 16 | +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 17 | +License for the specific language governing permissions and limitations under |
| 18 | +the License. |
| 19 | +*/}} |
| 20 | + |
| 21 | +{{- /* |
| 22 | +This render hook resolves internal destinations by looking for a matching: |
| 23 | + |
| 24 | + 1. Page resource (an image in the current page bundle) |
| 25 | + 2. Section resource (an image in the current section) |
| 26 | + 3. Global resource (an image in the assets directory) |
| 27 | + |
| 28 | +It skips the section resource lookup if the current page is a leaf bundle, and |
| 29 | +captures external destinations as resources for local hosting. |
| 30 | + |
| 31 | +You must place global resources in the assets directory. If you have placed |
| 32 | +your resources in the static directory, and you are unable or unwilling to move |
| 33 | +them, you must mount the static directory to the assets directory by including |
| 34 | +both of these entries in your site configuration: |
| 35 | + |
| 36 | + [[module.mounts]] |
| 37 | + source = 'assets' |
| 38 | + target = 'assets' |
| 39 | + |
| 40 | + [[module.mounts]] |
| 41 | + source = 'static' |
| 42 | + target = 'assets' |
| 43 | + |
| 44 | +By default, if this render hook is unable to resolve a destination, it passes |
| 45 | +the destination through without modification. To emit a warning or error, set |
| 46 | +the error level in your site configuration: |
| 47 | + |
| 48 | + [params.render_hooks.image] |
| 49 | + errorLevel = 'warning' # ignore (default), warning, or error (fails the build) |
| 50 | + |
| 51 | +Image render hooks are also used to: |
| 52 | + |
| 53 | + - Resize, crop, rotate, filter, and convert images |
| 54 | + - Build responsive images using srcset and sizes attributes |
| 55 | + - Wrap images inside of a picture element |
| 56 | + - Transform standalone images into figure elements |
| 57 | + |
| 58 | +To perform any of these operations, you can "hook" into this render hook with a |
| 59 | +partial template, after the render hook has captured the resource. |
| 60 | + |
| 61 | +@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. |
| 62 | +@context {string} Destination The image destination. |
| 63 | +@context {bool} IsBlock Returns true if a standalone image is not wrapped within a paragraph element. |
| 64 | +@context {int} Ordinal The zero-based ordinal of the image on the page. |
| 65 | +@context {page} Page A reference to the page containing the image. |
| 66 | +@context {string} PlainText The image description as plain text. |
| 67 | +@context {string} Text The image description. |
| 68 | +@context {string} Title The image title. |
| 69 | + |
| 70 | +@returns {template.html} |
| 71 | +*/}} |
| 72 | + |
| 73 | +{{- /* Initialize. */}} |
| 74 | +{{- $renderHookName := "image" }} |
| 75 | + |
| 76 | +{{- /* Verify minimum required version. */}} |
| 77 | +{{- $minHugoVersion := "0.114.0" }} |
| 78 | +{{- if lt hugo.Version $minHugoVersion }} |
| 79 | + {{- errorf "The %q render hook requires Hugo v%s or later." $renderHookName $minHugoVersion }} |
| 80 | +{{- end }} |
| 81 | + |
| 82 | +{{- /* Error level when unable to resolve destination: ignore, warning, or error. */}} |
| 83 | +{{- $errorLevel := or site.Params.render_hooks.image.errorLevel "ignore" | lower }} |
| 84 | + |
| 85 | +{{- /* Validate error level. */}} |
| 86 | +{{- if not (in (slice "ignore" "warning" "error") $errorLevel) }} |
| 87 | + {{- errorf "The %q render hook is misconfigured. The errorLevel %q is invalid. Please check your site configuration." $renderHookName $errorLevel }} |
| 88 | +{{- end }} |
| 89 | + |
| 90 | +{{- /* Determine content path for warning and error messages. */}} |
| 91 | +{{- $contentPath := "" }} |
| 92 | +{{- with .Page.File }} |
| 93 | + {{- $contentPath = .Path }} |
| 94 | +{{- else }} |
| 95 | + {{- $contentPath = .Path }} |
| 96 | +{{- end }} |
| 97 | + |
| 98 | +{{- /* Parse destination. */}} |
| 99 | +{{- $u := urls.Parse .Destination }} |
| 100 | + |
| 101 | +{{- /* Set common message. */}} |
| 102 | +{{- $msg := printf "The %q render hook was unable to resolve the destination %q in %s" $renderHookName $u.String $contentPath }} |
| 103 | + |
| 104 | +{{- /* Get image resource. */}} |
| 105 | +{{- $r := "" }} |
| 106 | +{{- if $u.IsAbs }} |
| 107 | + {{- with try (resources.GetRemote $u.String) }} |
| 108 | + {{- with .Err }} |
| 109 | + {{- if eq $errorLevel "warning" }} |
| 110 | + {{- warnf "%s. See %s" .Err $contentPath }} |
| 111 | + {{- else if eq $errorLevel "error" }} |
| 112 | + {{- errorf "%s. See %s" .Err $contentPath }} |
| 113 | + {{- end }} |
| 114 | + {{- else with .Value }} |
| 115 | + {{- /* Destination is a remote resource. */}} |
| 116 | + {{- $r = . }} |
| 117 | + {{- end }} |
| 118 | + {{- else }} |
| 119 | + {{- if eq $errorLevel "warning" }} |
| 120 | + {{- warnf $msg }} |
| 121 | + {{- else if eq $errorLevel "error" }} |
| 122 | + {{- errorf $msg }} |
| 123 | + {{- end }} |
| 124 | + {{- end }} |
| 125 | +{{- else }} |
| 126 | + {{- with .Page.Resources.Get (strings.TrimPrefix "./" $u.Path) }} |
| 127 | + {{- /* Destination is a page resource. */}} |
| 128 | + {{- $r = . }} |
| 129 | + {{- else }} |
| 130 | + {{- with (and (ne .Page.BundleType "leaf") (.Page.CurrentSection.Resources.Get (strings.TrimPrefix "./" $u.Path))) }} |
| 131 | + {{- /* Destination is a section resource, and current page is not a leaf bundle. */}} |
| 132 | + {{- $r = . }} |
| 133 | + {{- else }} |
| 134 | + {{- with resources.Get $u.Path }} |
| 135 | + {{- /* Destination is a global resource. */}} |
| 136 | + {{- $r = . }} |
| 137 | + {{- else }} |
| 138 | + {{- if eq $errorLevel "warning" }} |
| 139 | + {{- warnf $msg }} |
| 140 | + {{- else if eq $errorLevel "error" }} |
| 141 | + {{- errorf $msg }} |
| 142 | + {{- end }} |
| 143 | + {{- end }} |
| 144 | + {{- end }} |
| 145 | + {{- end }} |
| 146 | +{{- end }} |
| 147 | + |
| 148 | +{{- /* Determine id attribute. */}} |
| 149 | +{{- $id := printf "h-rh-i-%d" .Ordinal }} |
| 150 | +{{- with .Attributes.id }} |
| 151 | + {{- $id = . }} |
| 152 | +{{- end }} |
| 153 | + |
| 154 | +{{/* |
| 155 | + Upstream template converts every image to WebP here, but that duplicates work |
| 156 | + already done by the responsive-image pipeline (widths array) and adds |
| 157 | + significant CPU/memory pressure for no practical benefit — the images are |
| 158 | + already compact PNGs at ≤1400px. Skip the conversion entirely and serve the |
| 159 | + original file. |
| 160 | +*/}} |
| 161 | + |
| 162 | +{{- if ne $r.MediaType.SubType "svg" }} |
| 163 | +{{- /* Render image element. */ -}} |
| 164 | +<img |
| 165 | + src="{{ $r.RelPermalink }}" |
| 166 | + width="{{ string $r.Width }}" |
| 167 | + height="{{ string $r.Height }}" |
| 168 | + decoding="{{ site.Params.thulite_images.defaults.decoding }}" |
| 169 | + fetchpriority="{{ site.Params.thulite_images.defaults.fetchpriority }}" |
| 170 | + loading="{{ site.Params.thulite_images.defaults.loading }}" |
| 171 | + alt="{{ .PlainText }}" |
| 172 | + {{- with .Title -}}title="{{ . }}"{{- end }} |
| 173 | + id="{{ $id }}" |
| 174 | +> |
| 175 | +{{- else }} |
| 176 | +<img |
| 177 | + src="{{ $r.RelPermalink }}" |
| 178 | + decoding="{{ site.Params.thulite_images.defaults.decoding }}" |
| 179 | + fetchpriority="{{ site.Params.thulite_images.defaults.fetchpriority }}" |
| 180 | + loading="{{ site.Params.thulite_images.defaults.loading }}" |
| 181 | + alt="{{ .PlainText }}" |
| 182 | + {{- with .Title -}}title="{{ . }}"{{- end }} |
| 183 | + id="{{ $id }}" |
| 184 | + class="markdown-svg" |
| 185 | +> |
| 186 | +{{- end }} |
| 187 | + |
| 188 | +{{- /**/ -}} |
0 commit comments