August 8, 2021

Ox-hugo friendly image resize in Hugo

I write content to this site with Org Mode, and export to Hugo with ox-hugo. Main images are in the org directory, and ox-hugo copies them to page bundle directories. I wanted to optimize image management and presentation and I had the following thoughts:

  • Main images are high (enough) resolution and thus future-safe
  • Images are visible in native resolution, without resizing by browser
  • Some images behave as links to the original image
  • Minimize additional code in org file, i.e., be org friendly

I think it’s criminal to use high resolution images on the website and order browser to do resizing. Sometimes I see multiple megabyte images on a single page used as little thumbnails. It’s horrible waste of bandwidth and processing power. Lastly, in advance resized images can be higher quality than quickly scaled by browser.

So, currently my page width of content is 800px. Often I want to have 100% width image on the page, so I need to do resizing. In the future, I may change page width, and thus I must be prepared for that with the main images. In addition, sometimes I want to provide possibility for user to open the original version of an image.

The simplest solution for me was to use “big enough” main images in org directory and override Hugo’s figure shortcode to automatically create resized images when needed.

Example 1 - Low resolution image without resizing or configuration 🔗

The width of original image is 400px. Hugo doesn’t create resized version but uses the original image.

[[./files/film.jpg]]

Example 2 - High resolution image with resizing 🔗

This is the most common scenario for me. The width of original image is 2600px. Hugo creates 800px width version of the image and uses it on the web page. With the optional imgzoom macro, image is configured to be a link to the original image.

{{{imgzoom}}}
#+caption: /"EAT ME"/
[[./files/self-portrait.jpg]]
“EAT ME”

“EAT ME”

Example 3 - High resolution image with all configurations 🔗

This example demonstrates use of all supported features.

{{{imgzoom}}}
#+caption: /"DRINK ME"/
#+attr_html: :class fancy
#+attr_html: :target _blank
#+attr_html: :attr by me :attrlink /g
#+attr_html: :alt Caption not used as alt text
#+attr_html: :width 50% :height 50%
[[./files/self-portrait.jpg]]
Caption not used as alt text

“DRINK ME” by me

Implementation 🔗

Org Mode macro 🔗

As seen in the previous examples, there is an Org Mode macro to active link creation. Of course, attr_html could be directly used but this makes my life easier.

#+macro: imgzoom #+attr_html: :link t

Hugo figure shortcode 🔗

Following figure template in /layouts/shortcodes/figure.html overrides Hugo’s default figure template.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<!-- Web page content width -->
{{ $maxWidth := 800 }}

{{ $original := .Page.Resources.GetMatch (.Get "src") }}

{{ if $original }}
    {{ if gt $original.Width $maxWidth }}
        {{ .Scratch.Set "preview" ($original.Resize (print $maxWidth "x")).RelPermalink }}
    {{ end }}
{{ end }}

{{ if (.Scratch.Get "preview") }}
    {{ if .Get "link" }}
        {{ .Scratch.Set "link" (.Get "src") }}
    {{ end }}
{{ else }}
    {{ .Scratch.Set "preview" (.Get "src") }}
{{ end }}

<!-- Replace caption  -->
{{ $caption := replaceRE "^Figure [0-9]+: " "" (.Get "caption") }}

<figure{{ with .Get "class" }} class="{{ . }}"{{ end }}>
    {{- if .Scratch.Get "link" -}}
    <a href="{{ .Scratch.Get "link" }}" {{ with .Get "target" }} target="{{ . }}"{{ end }}>
    {{- end -}}
    <img src="{{ .Scratch.Get "preview" }}"
    {{- if or (.Get "alt") $caption }}
         alt="{{ with .Get "alt" }}{{ . }}{{ else }}{{ $caption | markdownify | plainify }}{{ end }}"
    {{- end -}}
    {{- with .Get "width" }} width="{{ . }}"{{ end -}}
    {{- with .Get "height" }} height="{{ . }}"{{ end -}}
    />
    {{- if .Scratch.Get "link" }}</a>{{ end -}}
    {{- if or $caption (.Get "attr") -}}

    <figcaption>
        {{- if or $caption (.Get "attr") -}}<p>
            {{- $caption | markdownify -}}
            {{- with .Get "attrlink" }}
                <a href="{{ . }}">
            {{- end -}}
            {{- .Get "attr" | markdownify -}}
            {{- if .Get "attrlink" }}</a>{{ end }}</p>
        {{- end }}
    </figcaption>
    {{- end }}
</figure>

Figure template checks width of image. If width is more than 800px, it creates resized 800px version and uses it. Otherwise, it uses the original image.

It supports those optional figure parameters that I found useful for me. As a bonus feature, it removes Figure n: prefix from caption text.