HugoOGP/Twitter Cardを自動生成したい

はじめに

Hugo v0.90.0からimages.Textにより画像に文字を合成した画像を生成できるようになりましたこれによりQiitaとかみたいに画像中にタイトルとかを記述したOGP/Twitter Cardを自動生成できるようになりました

この記事では最終的に次のような画像が生成されるようにします

準備

まずHugo0.90.0以上にアップデートしてください文字を合成する前の元画像を用意しますTwitter CardLarge Summary Imageの推奨サイズが800 x 418 pxらしいのでそのサイズで用意しました1

画像生成

テキストと書いた画像を生成する場合次のようにしたら良いですこの例ではGoogle FontsGitHubからresources.GetRemoteを使ってNotoSansJP-Bold.otfを読み取りフォントをNotoSansJP BoldにしていますOGPの元画像としてstatic/images/ogp/ogp.pngにある画像を読み込んでいますstatic/にあるファイルにアクセスするためにconfig.tomlassetDir = "static"と設定してます(images.Text ... (dict ...))は見ての通りテキストの位置や大きさなどを設定しています最後に$img.RelPermalinkで画像のリンクを取得します

static/images/ogp/...にある画像を参照することでhugoを実行した時に生成される画像はimages/ogp/...になるので都合がいいので僕はこうしてます

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{{ $font := resources.GetRemote "https://github.com/google/fonts/raw/main/ofl/notosansjp/NotoSansJP-Bold.otf" }}
{{ $img := resources.Get "images/ogp/ogp.png" }}
{{ $img = $img | images.Filter
  (images.Text "テキスト" (dict
    "color" "#ff3300"
    "size" 32
    "x" 50 "y" 200
    "font" $font
  ))
}}
<img src="{{ $img.RelPermalink }}" />

英文のようにスペースが入る文章なら改行処理を行ってくれますが和文のようにスペースが入らない文章では最初に改行が入ってそのままはみ出してしまいます

英文の場合 和文の場合

そこで長いタイトルの場合はみ出す分を切り取りこのようにします

実際のソースコードを使って説明します記事のタイトル以外にブログ名やカテゴリについても書きます

4–8行目ではタイトルが長い場合に切り取っています標準のタイトル文字数を22としてタイトルに英字を入れたことを考慮して手動で文字数を設定できるようにしています5行目

date.Date.Formatで形式を指定しますcategoriestagsはループでカンマ区切りの1つのStringにまとめます

$title$categoriesとかを複数の(images.Text ...)使って同時に書きます

 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
{{ $font := resources.GetRemote "https://github.com/google/fonts/raw/main/ofl/notosansjp/NotoSansJP-Bold.otf" }}
{{ $img := resources.Get "images/ogp/ogp-post.png" }}
{{ $title := .Title }}
{{ $strlen := 22 }}
{{ if .Params.strlen }}{{ $strlen = .Params.strlen }}{{ end }}
{{ if gt (strings.RuneCount $title) (add $strlen 1) }}
  {{ $title = printf "%s…" (substr $title 0 $strlen) }}
{{ end }}
{{ $date := .Date.Format "2006 年 1 月 2 日" }}
{{ $categories := "" }}
{{ range .Params.categories }}{{ $categories = printf "%s, %s" $categories . }}{{ end }}
{{ $categories = substr $categories 2 }}
{{ $tags := "" }}
{{ range .Params.tags }}{{ $tags = printf "%s, %s" $tags . }}{{ end }}
{{ $tags = substr $tags 2 }}
{{ $img = $img | images.Filter
  (images.Text .Site.Title (dict
    "color" "#333"
    "size" 32
    "x" 90 "y" 19
    "font" $font
  ))
  (images.Text $title (dict
    "color" "#333"
    "size" 32
    "x" 25 "y" 135
    "font" $font
  ))
  (images.Text $date (dict
    "color" "#666"
    "size" 24
    "x" 80 "y" 230
    "font" $font
  ))
  (images.Text $categories (dict
    "color" "#666"
    "size" 24
    "x" 80 "y" 280
    "font" $font
  ))
  (images.Text $tags (dict
    "color" "#666"
    "size" 24
    "x" 80 "y" 330
    "font" $font
  ))
}}

このように画像が出力されますタイトルに英数字が入っているため文字数の割に幅が狭いのでstrlen: 34と設定しています

OGP/Twitter Cardの設定

このブログではこんな感じでOGP/Twitter Cardを設定しています

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@Daiji256" />
<meta name="twitter:title" content="{{ .Title }} - {{ .Site.Title }}" />
<meta name="twitter:image" content="{{ $img.RelPermalink | absURL }}" />
<meta property="og:url" content="{{ .Permalink }}" />
<meta property="og:type" content="article" />
<meta property="og:title" content="{{ .Title }} - {{ .Site.Title }}" />
<meta property="og:description" content="{{ .Summary }}" />
<meta property="og:site_name" content="{{ .Site.Title }}" />
<meta property="og:image" content="{{ $img.RelPermalink | absURL }}" />

おまけ

ホームカテゴリ/タグのページのような投稿日やカテゴリとかと無縁なページでは以下のように画像を生成するようにしました

元の画像 出力画像

このようにホーム等のページでは.Params.tagsが設定されないのでそれを判定して2種類の画像生成を分岐するようにしました

1
2
3
4
5
{{ if or .Params.tags .Params.categories }}
  <!-- カテゴリタグがある場合 -->
{{ else }}
  <!-- カテゴリタグがない場合 -->
{{ end }}

追記

こんな風にちゃんとできてました!

文献

  1. Hugo releases/v0.90.0 (GitHub)

  1. OGPの推奨サイズは1200 x 630 pxらしいです ↩︎