Daiji Blog

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

公開:
更新:

はじめに

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

QiitaのOGP

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

この記事の方法で生成されるOGP

準備

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

OGPの元画像

画像生成

テキストと書いた画像を生成する場合次のようにしたら良いですこの例では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/...になるので都合がいいので僕はこうしてます

{{ $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 }}" />

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

英文の場合和文の場合
英文が改行されて表示されているOGP和文が改行されずに表示されているOGP

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

和文のはみ出した部分を切り取り表示したOGP

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

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

date.Date.Formatで形式を指定しますcategoriestagsはループでカンマ区切りの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

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 }}" />

おまけ

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

元の画像出力画像
ホームなどのOGPのための元画像ホームのOGP

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

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

文献

  1. Hugo releases/v0.90.0

脚注

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