HUGO v0.90.0 で追加された images.Text でブログカードと OGP 画像を自動生成する

Posts

HUGO の v0.90.0 から images.Text を使って、画像と文字を合成した画像を生成できるようになりました。
images.Text を使うと、記事ファイル内のテキスト情報を埋め込んだ画像を自動で生成できそうです。

この記事では、記事ファイルの Frontmatter の情報と images.Text を使って、生成したブログカードの画像を一覧に表示したり、OGP 画像として設定することを試してみました。

動作を確認した環境

HUGO v0.90.0

img.Text の使い方を確認する

img.Text は、Image Filters の引数として渡すための関数です。
参考:ドキュメントのサンプル
img.Text に渡されたテキストリソースと指定した画像を、Image Filters を使って合成します。

{{ $img := resources.Get "/images/background.png"}}
{{ $img = $img.Filter (images.Text "Hugo rocks!" (dict
    "color" "#ffffff"
    "size" 60
    "linespacing" 2
    "x" 10
    "y" 20
))}}

実装のイメージ

画像ファイルは、Frontmatter の image で指定し、テキストには title を指定します。

posts/ramen.md
---
title: 福岡の美味しいラーメン
image: ramen.jpg
---

## 福岡といえばとんこつ

記事や画像ファイルは、次のように配置しました。

$ tree content
content
└── post
    ├── _index.md  # 一覧ページ
    ├── gourmet.jpg
    ├── ramen.jpg
    ├── ramen.md  # 記事ページ
    ├── udon.jpg
    └── udon.md  # 記事ページ

以降の説明では、hugo new theme THEME_NAME で生成した空のテンプレートに実装することを想定しています。

一覧ページに記事情報から生成したブログカードを表示する

リストテンプレートの実装

それぞれの記事から、ブログカードの画像を作成し表示するリストページを作ります。

layouts/_default/list.html
{{- define "main" }}
<h1>{{ .Title }}</h1>
{{ $font := resources.Get "https://github.com/google/fonts/raw/main/ofl/notosansjp/NotoSansJP-Black.otf" }}
{{ range .RegularPages }}
<div>
  {{- $img := .Page.Parent.Resources.GetMatch (.Params.image) }}
  {{- $title := .Title }}
  {{- with $img }}
    {{- $options := images.Text $title (dict "color" "#eee" "size" 30 "y" 120 "font" $font) }}
    {{- $img = $img.Fill "600x315 Center"  | images.Filter $options }}
    <div><img src="{{ $img.RelPermalink }}" alt="ブログカード" ></div>
  {{- end }}
  <div>
    <a href="{{ .Permalink }}">{{ .Date.Format "2006-01-02" }} | {{ .Title }}</a>
  </div>
</div>
{{ end -}}
{{ end -}}
  • 6 行目: Frontmatter の image.Params.image)で指定されたパスから画像を取得する
  • 7, 9 行目:テキスト情報には記事のタイトル(.Title)を使う
  • 10 行目:images.Text で生成したテキストを元の画像に合成する

動作確認

http://localhost:1313/posts/ を確認してみます。

一覧ページの画面例

それぞれの記事のタイトルを埋め込んだブログカードの画像が一覧に表示されていました。

生成した画像を OGP 画像に設定する

head 内に表示するテンプレートの実装

同様に、生成した画像のパスを OGP のメタ情報として指定します。

layouts/partials/head.html
<meta property="og:type" content="article">
<meta property="og:title" content="{{ .Title }}">
<meta property="og:description" content="{{ .Description }}">
<meta property="og:site_name" content="{{ .Site.Title }}">
{{- $font := resources.Get "https://github.com/google/fonts/raw/main/ofl/notosansjp/NotoSansJP-Black.otf" }}
{{- $img := "" }}
{{- if .IsPage }}{{- $img = .Page.Parent.Resources.GetMatch (.Params.image) }}
{{ else if .IsSection }}{{- $img = .Page.Resources.GetMatch (.Params.image) }}
{{ end }}
{{- $title := .Title }}
{{- with $img }}
  {{- $option := images.Text $title (dict "color" "#eee" "size" 30 "y" 120 "font" $font) }}
  {{- $img = $img.Fill "600x315 Center"  | images.Filter $option }}
  <meta property="og:image" content="{{ $img.RelPermalink }}">
{{ end -}}
  • 7, 8 行目:記事ページと一覧ページでは、リソースにアクセスする方法が違います。そのため、それぞれに合った方法で画像を取得します。

動作確認

ローカルで OGP 情報を確認できる Chrome 拡張 Social Share Preview を使って、プレビューしてみます。

一覧ページの OGP のプレビュー(http://localhost:1313/posts/
一覧ページの OGP のプレビューの画面例

記事ページの OGP のプレビュー(http://localhost:1313/posts/udon/
記事ページの OGP のプレビューの画面例

それぞれ、記事の情報に合わせた OGP 画像を設定できました。

気になったこと

今回、実装してみて気になったのは、次の点です。

  • テキストが長い場合、画像の幅に合わせてサイズを縮小してくれない
    • Frontmatter からフォントサイズや位置情報を渡せば解決しそう
  • フォントファイルを取得するときに、キャッシュが効いているか