HugoでOGP/Twitter Cardを自動生成したい
はじめに
Hugo v0.90.0から、images.Textにより画像に文字を合成した画像を生成できるようになりました。これにより、Qiitaとかみたいに画像中にタイトルとかを記述したOGP/Twitter Cardを自動生成できるようになりました。
この記事では最終的に次のような画像が生成されるようにします。
準備
まずHugoを0.90.0以上にアップデートしてください。文字を合成する前の元画像を用意します。Twitter CardのLarge Summary Imageの推奨サイズが800 x 418 pxらしいのでそのサイズで用意しました1。
画像生成
テキストと書いた画像を生成する場合次のようにしたら良いです。この例ではGoogle FontsのGitHubからresources.GetRemoteを使ってNotoSansJP-Bold.otfを読み取りフォントをNotoSansJP Boldにしています。OGPの元画像として、static/images/ogp/ogp.pngにある画像を読み込んでいます。static/にあるファイルにアクセスするためにconfig.tomlでassetDir = "static"と設定してます。(images.Text ... (dict ...))は見ての通りテキストの位置や大きさなどを設定しています。最後に$img.RelPermalinkで画像のリンクを取得します。
static/images/ogp/...にある画像を参照することで、hugoを実行した時に生成される画像はimages/ogp/...になるので都合がいいので僕はこうしてます。
{{ $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で形式を指定します。categoriesとtagsはループでカンマ区切りの1つのStringにまとめます。
$title、$categoriesとかを複数の(images.Text ...)使って、同時に書きます。
{{ $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を設定しています。
<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種類の画像生成を分岐するようにしました。
{{ if or .Params.tags .Params.categories }}
<!-- カテゴリ、タグがある場合 -->
{{ else }}
<!-- カテゴリ、タグがない場合 -->
{{ end }}
文献
脚注
-
OGPの推奨サイズは1200 x 630 pxらしいです。 ↩



