Hugo のサムネ作成、画像追加、画像最適化を自動化するシェル芸
CMS ではなく Markdown で記事を作成していると画像周りがどうにもダルいのでスクリプト化しました。
自分用のオレオレ処理ですが、貼っておけば何かの役に立つかもしれないので残しておきます。
準備
画像の編集をするのに ImageMagick が必要です。
また、画像の最適化では
を使っています。それぞれ Mac であれば Homebrew で入れられるかと思います。
構成
build 用のファイルなどを除くとざっくりこんな感じのディレクトリ構成で Hugo を使っています。
▸ content/post/
▸ data/
▸ layouts/
▸ static/images/
▸ static_src/images/
▸ themes/
addImage.sh*
config.toml
createBanner.sh*
createImageWithText.sh*
imageOptim.sh*
static/images/
以下に post の slug と同じディレクトリを作成し、その中に記事の画像をまとめて入れています。
画像は圧縮などの最適化を行ってから使用するので、元データは static_src/images/
にバックアップしています。
スクリプト
スクリプトは以下の 4 つを使用しています。
addImage.sh
createBanner.sh
createImageWithText.sh
imageOptim.sh
createImageWithText.sh
createImageWithText.sh
は以前作った画像の上に黒帯背景で白文字を描画するためのスクリプトです。
サムネイルで楽をするために使っています。
addImage.sh
addImage.sh
は画像を Hugo の構成内に追加するときに叩くスクリプトです。
addImage.sh src title name
のフォーマットで、
src
: 元画像title
: Hugo の slugname
: 画像の名前(拡張子は不要)
です。
src
に指定した画像を static/images/
static_src/images/
に入れた上で、 static/images/
の方には最適化を行います。
なお、 blog に吐いたデータだけでなく作成用のリポジトリ自体も GitHub に上げているので、 static_src/images/
に入れるバックアップ用の画像も convert
の -strip
オプションで Exif 情報を削っています。
#!/bin/bash
set -e
set -u
usage="Usage: addImage.sh src title name"
if [ ! $# -eq 3 ]; then
echo $usage
exit 1
fi
src="$1"
title="$2"
name="$3"
ext="${src##*.}"
dstDir="static/images/${title}"
dst="${dstDir}/${name}.${ext}"
backupDir="static_src/images/${title}"
backup="${backupDir}/${name}.${ext}"
mkdir -p "$dstDir"
mkdir -p "$backupDir"
convert "$src" -strip "$backup"
convert "$src" -strip "$dst"
./imageOptim.sh "$dst"
本当は引数ではなくオプションを取るようにしたほうが汎用性が高いのですが、自分でゴリゴリ使う用なので及第点としています。
cp
ではなく -strip
付きの convert
で src
から画像を複製しているので、 src
はファイルパスだけでなく URL でも取れるようになっています。
imageOptim.sh
imageOptim.sh
は画像の最適化を行うスクリプトです。
拡張子をファイル名から判別して
で圧縮しています。対応しているのは jpg
png
です。
mogrify -resize '2560x1440>' "$1"
で 2560x1440
から縦横のどちらかでもはみ出たら、アスペクト比を保ちつつ収まるサイズに縮小しています。
#!/bin/bash
set -e
set -u
usage="Usage: imageOptim.sh src"
function resize () {
if [ ! $# -eq 1 ]; then
echo "resize() needs 1 args"
exit 1
fi
mogrify -resize '2560x1440>' "$1"
}
if [ ! $# -eq 1 ]; then
echo $usage
exit 1
fi
src="$1"
ext="${src##*.}"
beforeSize=$(wc -c <"$src")
echo "optimize image: $src"
if [ $ext = 'jpg' ]; then
resize "$src"
jpegoptim --strip-all --max=90 "$src"
elif [ $ext = 'png' ]; then
resize "$src"
pngquant --ext ".${ext}" --force --speed 1 "$src"
else
echo "unknown filetype $ext"
exit 1
fi
afterSize=$(wc -c <"$src")
echo "optimize image: optimized ${beforeSize} to ${afterSize} ($(expr 100 \* $afterSize / $beforeSize)%)"
createBanner.sh
createBanner.sh
は src 画像からサムネイルを作って画像追加までを行う、最もオレオレなスクリプトです。
一時ファイルの処理が甘いのですが、使い方の参考にはなると思うので気にせず載せます。
#!/bin/bash
set -e
set -u
usage="createBanner.sh src.jpg 'title' 'text'"
if [ ! $# -eq 3 ]; then
echo $usage
exit 1
fi
srcImage="$1"
title="$2"
text="$3"
ext="${srcImage##*.}"
# conf
aspectX=16
aspectY=9
name="banner"
size=($(identify -format "%w %h" "$srcImage"))
srcW=${size[0]}
srcH=${size[1]}
tmp1=$(mktemp "/tmp/${0##*/}.${srcImage//\//_}.$(date).tmp1.tmp.${ext}")
# TODO trap rm
if [ $(expr $srcH \* $aspectX) -gt $(expr $srcW \* $aspectY) ]; then
# crop height
w=$(expr $srcW - $srcW % $aspectX)
h=$(expr $w / 16 \* 9)
echo $w
echo $h
echo $tmp1
convert "$srcImage" -gravity center -crop "${w}x${h}+0+0" "$tmp1"
elif [ $(expr $srcH \* $aspectX) -lt $(expr $srcW \* $aspectY) ]; then
# crop width
h=$(expr $srcH - $srcH % $aspectY)
w=$(expr $h / 9 \* 16)
echo $w
echo $h
echo $tmp1
convert "$srcImage" -gravity center -crop "${w}x${h}+0+0" "$tmp1"
fi
tmp2=$(mktemp "/tmp/${0##*/}.${srcImage//\//_}.$(date).tmp2.tmp.${ext}")
# TODO trap rm
echo "srcImage = $srcImage"
echo "text = $text"
echo "tmp1 = $tmp1"
echo "tmp2 = $tmp2"
./createImageWithText.sh "$tmp1" "$text" "$tmp2"
./addImage.sh "$tmp2" "$title" "$name"
rm -f "$tmp1"
rm -f "$tmp2"
中央らへんのごちゃごちゃと計算しているところがややこしいですね。
アスペクト比を 16:9
にするためにどちらが長いかを計算してはみ出たところを中央から crop
しています。
16:9
にしたものを $tmp1
に保存し、 createImageWithText.sh
で文字を入れてサムネ化したものを $tmp2
に保存。
生成された文字入り画像 $tmp2
を元画像として addImage.sh
で画像を追加しています。
以上
CMS ではアップロードするだけでいいのに Hugo では色々と自分で作業しなければいけない画像の追加が自動化できたので、とりあえず満足です。
Shougo/deoplete.nvim や Shougo/denite.nvim のソースとして画像を取得できるようにすると便利そうな気配がするので、気が向いたらやります。
addImage.sh
の出力を image の path にして pbcopy
するくらいでも良いかもしれないですね。