Hugo-Images management

Posted by Blog - uTIcARdI on January 25, 2019

I introduced why I migrate my blog to github and Hugo here. Most of articles have been moved to here in 30 days. Next, I just want to talk about something about posts’ images management, versioning, theme customization/something about SEO.

Before start

All images will be compressed before I upload them for post. There are many tools could do this, such as tinypng, UPNG. My choice is Tinypng because it supports variety formats and its API.

img compression (20M to 5.1M)
img compression (20M to 5.1M)

Tinypng’s APIs can be called by Python-Requests in this way:

import os
from os import path
import requests
from requests.auth import HTTPBasicAuth

def picopt(root_path, apikey):
    for f in os.listdir(root_path):
        fullurl = os.sep.join([root_path, f])
        if path.isdir(fullurl):
            picopt(fullurl, apikey)
        else:
            if fullurl.endswith(('.DS_Store', '-opt.png', '-opt.jpg', '-opt.jpeg')):
                print('ignore ' + fullurl)
                continue
            print('processing ' + fullurl)

            with open(fullurl, 'rb') as imgf:
                content = imgf.read()
            res = requests.post(
                auth=HTTPBasicAuth("api:%s" %(apikey), ''), url='https://api.tinify.com/shrink', data=content)
            print(res.json())
            optimgurl = res.json()['output']['url']
            # print(optimgurl)
            res = requests.get(
                auth=HTTPBasicAuth("api:%s" %(apikey), ''), url=optimgurl)

            filename, filetypestr = fullurl.rsplit('.', 1)
            optfullurl = '-opt.'.join([filename, filetypestr])
            with open(optfullurl, 'wb') as fd:
                for chunk in res.iter_content(chunk_size=128):
                    fd.write(chunk)
                print('optimized img file: ' + fullurl)

if __name__ == "__main__":
    devkey = ''
    postimgsfolder = ''
    picopt(postimgsfolder, devkey)

Manage the image in post with markdown

Refer to this Guide, it’s the most easiest way:

  1. Save the image file to folder static/img
  2. Link image in post file with the markdown:

    • For image without link:

      ![Minion](/img/home-psb-bg.jpeg "alt wording")
      
    • For image with link:

      [![Minion](/img/home-psb-bg.jpeg "alt wording")](/img/home-psb-bg.jpeg)
      

    Yes, it’s /img/ in the link which was mentioned in here:

    This union filesystem will be served from your site root. So a file /static/me.png will be accessible as /me.png

    Load images from the post folder is possible in Hugo future version.

From my understanding, as the guide described, extra work should be needed for images resize with HTTP parameters, I didn’t dig deeper on this.

Manage the image in post with html code

  • For image without link:

    <img src="/img/home-psb-bg.jpeg" alt="alt wording" style="height:200px;">
    
  • For images with link:

    <a href="/img/home-psb-bg.jpeg">
    <img src="/img/home-psb-bg.jpeg" alt="alt wording" style="height:200px;">
    </a>
    

Manage the image with shortcode

Hugo provides a figure shortcode with sample:

{{< figure src="/media/spf13.jpg" title="Steve Francia" >}}

Note:
A sample about show shortcode on Hugo post.

The thing is Shortcode and Image Processing module enable Hugo to create responsive images:

  1. Customize the behavior and style to view the image
  2. Resize and crop the image to proper size and quality for different view

Make a long story short, shared my customization as an example

  1. Image defination with shortcode in the markdown file:

    ...
    postWithImg: true
    ...
    {{< imgproc imgPath="2018/12/5-opt.png" alt="XCode-Export Apple Developer Profile-3" max-height="250"  >}}
    
  2. Post images related settings in site configuration file:

    ...
    [params]
    postimgfoler = "/img/postimgs" #full url: assertsDir + /img/postimgs
    ...
    
  3. layout/shortcodes/

     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
    
    {{ $img := resources.Get (printf "%s/%s" ( .Site.Params.postimgfoler ) (.Get "imgPath")) }}
    
    {{ $imgpath := (printf "%s/%s/%s" ( "assets" ) (.Site.Params.postimgfoler ) (.Get "imgPath")) }}
    
    {{ $pagehgt := (printf "x%s q20" (.Get "max-height")) }}
    {{ .Scratch.Set "image" ($img.Resize $pagehgt) }}
    {{ $pageimage := .Scratch.Get "image" }}
    
    {{ $imgData :=  imageConfig $imgpath }}
    {{ $fanhgt := string ( $imgData.Height ) }}
    {{ if gt  $imgData.Height  550 }}
    {{ $fanhgt = "550" }}
    {{ end }}
    
    {{ $fanhgt := (printf "x%s q20" ($fanhgt)) }}
    {{ .Scratch.Set "image" ($img.Resize $fanhgt) }}
    {{ $fanimage := .Scratch.Get "image" }}
    
    <div align="center">
        <figure style="padding: 0.8rem; margin: 2rem 0; border: thin #c0c0c0 solid; border-radius: 10px; width: {{$pageimage.Width}}px; max-width: 88%">
        <a href="{{ $fanimage.RelPermalink }}" data-fancybox data-caption={{ .Get "alt" }} >
            <img src="{{ $pageimage.RelPermalink }}" class="img-responsive" alt={{ .Get "alt" }} style="display: block;margin: 0.5rem;"/>
        </a>
        <figcaption class="text-capitalize">
            <small>{{ .Get "alt" }}</small>
        </figcaption>
        </figure>
    </div>

    • Line1: Different to markdown and html part, the images were saved to /assets/ folder so that resources.Get function could load the image file by path. The printf function joins two parameters to one string for resources.Get function.
    • Line 3: Initial the full url of the image for imageConfig function (Line 9)
    • Line 5-7: Generate an image from original image by the max-height value which was defined in the markdown file, quality to 20
    • Line 9-10: Get the hight info of the original image file with imageConfig
    • Line 11-17: Check the height of the original image file, and generate a new file with quality value: 20. The height of new image will be 550px if the value of the origin is larger than 550px.
    • Line 21-23: Call fancybox and bootstrap for image (Refer to layouts/partials/head.html for jQuery and fancybox files)

    Note:

  4. A live demo.

Lazy load images

Update @ Mar-09-2019

This part was refer to Lazy load images with zero Javascript

  1. Add the class img-lazyload in Hugo shortcode for lazy load

            <img data-src="{{ $pageimage.RelPermalink }}" class="img-responsive img-lazyload" alt={{ .Get "alt" }} style="height: {{$pageimage.Height}}px;"/>

  2. JS code

    Added following code in footer.html

    {{ if .Params.postWithImg }}
    <script>
        let lazyImages = [...document.querySelectorAll('.img-lazyload')]
        let inAdvance = 20
    
        function lazyLoad() {
            lazyImages.forEach(image => {
                if ((image.offsetTop < window.innerHeight + window.pageYOffset + inAdvance) && ! image.classList.contains('img-lazyload-loaded') ) {
                    image.src = image.dataset.src
                    image.onload = () => {
                        image.classList.add('img-lazyload-loaded');
                        image.style.height="";
                    }
                }
            })
            // if all loaded removeEventListener
        }
        lazyLoad()
        window.addEventListener('scroll', _.throttle(lazyLoad, 8))
        window.addEventListener('resize', _.throttle(lazyLoad, 8))
    </script>
    {{end}}
    

  3. Link lodash

    Add the link in head.html, otherwise it will run into the error: ReferenceError: _ is not defined

        <!-- JS/CSS for postimg -->
        {{ if .Params.postWithImg }}
        <link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.6/jquery.fancybox.min.css">
        <script src="//cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.6/jquery.fancybox.min.js"></script>
        <script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/0.10.0/lodash.min.js"></script>
        {{end}}
  4. Demo


To Do: With shortcodes and img process module, I’d like to try and process the post images like this: Responsive images | MDN.


Hugo-Images management


donation

Scan the QR code using WeChat

comments powered by Disqus