記事タイトルが埋め込まれたOGP画像を作る
あけましておめでとうございます。Kalsarikannintのフロントエンド担当・daiです。
QiitaとかZennとかイマドキのサイトをSNSなどでシェアしたとき、表示されるOGP画像に記事のタイトルが埋め込まれてるのをよく見かけると思います。
Kalsarikannintもイマドキの技術ブログも目指したい(願望) ので、この年末年始休みを使って、以下の各ページを参考に、WordPressで構築されているこのブログでもそれを実装してみたので、その方法などを紹介していきます!
参考:
つくったもの
最終的には、 functions.php
に以下のコードを追記することで、記事を保存したタイミングで /wp-content/uploads/ogp/ogp_{記事ID}.jpg
として記事タイトルが埋め込まれた画像を生成することができるようになりました。
// functions.php への記載内容
/**
* Create OPG Image
*
* タイトルを利用したOGP画像を生成する
*/
function create_ogp_image($post_id, $post) {
$file_name = 'ogp_'.$post_id.'.jpg';
$title = $post->post_title;
$base_image = get_template_directory().'/images/ogp_template.jpg';
$image = imagecreatefromjpeg($base_image);
$color = imagecolorallocate($image, 0, 0, 0);
$fontfile = get_template_directory().'/fonts/ZenKakuGothicNew-Black.ttf';
$size = 36;
$breakpoint = 20;
if (mb_strwidth($title, 'UTF-8') > 120) {
// 全体が60文字(= 20文字 * 3行)以上ならフォントサイズ変更 + 改行位置も変更
$size = 20;
$breakpoint = 35;
}
if (mb_strwidth($title, 'UTF-8') > $breakpoint * 2) {
$max_count = mb_strwidth($title, 'UTF-8') / $breakpoint * 2;
// 禁則処理に該当する文字
$specialChars = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'ー', 'ァ', 'ィ', 'ゥ', 'ェ', 'ォ', 'ッ', 'ャ', 'ュ', 'ョ', 'ヮ', '、', '。', '(', ')', '「', '」', '【', '】', 'ぁ', 'ぃ', 'ぅ', 'ぇ', 'ぉ', 'っ', 'ゃ', 'ゅ', 'ょ', 'ゎ'];
$last_pos = 0;
for ($count = 0; $count < $max_count; $count++) {
for ($pos = $breakpoint + $last_pos; $pos > $last_pos; $pos--) {
$char = mb_substr($title, $pos + 1, 1, 'UTF-8');
if (!in_array($char, $specialChars)) {
// 禁則処理に触れない = 改行OK
$title = mb_substr($title, 0, $pos + 1)."\\n".mb_substr($title, $pos + 1);
$last_pos = $pos - 1;
break;
} else if ($pos === $last_pos) {
// 禁則処理だらけなら強制改行しちゃお!
$title = mb_substr($title, $last_pos, $last_pos + $breakpoint)."\\n".mb_substr($title, $last_pos + 1);
$last_pos = $pos;
break;
}
}
}
}
$angle = 0;
$x = 100;
$y = 300 + $size;
// 文字列挿入
imagettftext($image, $size, $angle, $x, $y, $color, $fontfile, $title);
// OGP画像の保存場所がなければ作る
if (!file_exists('wp-content/uploads/ogp')) {
mkdir('wp-content/uploads/ogp', 0777);
}
// ファイル名を指定して画像出力
imagejpeg($image, 'wp-content/uploads/ogp/'.$file_name);
}
add_action('save_post', 'create_ogp_image', 10, 2);
加えて、 functions.php
へ以下のようなコードも追記し、記事ページでは上記で生成されたOGP画像を og:image
として指定するように設定しています。
// functions.php への記載内容
add_action('wp_head', function() {
$og_image = get_template_directory_uri().'/images/ogp_image.jpg';
if (is_single()) {
// 個別記事ページ
$og_image = get_site_url().'/wp-content/uploads/ogp/ogp_'.get_the_ID().'.jpg';
}
echo '<meta property="og:image" content="'.$og_image.'" />';
});
上記の記述を追記するほかに、以下のファイルを用意する必要があります。
- フォントファイル(テーマファイルディレクトリ配下の
fonts
ディレクトリを作成し保存) - テンプレートになる
ogp_templete.jpg
ファイル(テーマファイルディレクトリ配下のimages
ディレクトリへ保存) - 記事ページ以外のOGP画像となる
ogp_image.jpg
ファイル(同上)
OGP画像の生成方法
イマドキの環境を使っていれば、サーバーサイドでNode.jsを動かして、canvasを使って画像と文字の合成を行ったりするようですが、このサイトはCMSにWordPressを使っていることもあり、ごくごく一般的なレンタルサーバーで運用しているので、PHPのGDというライブラリを使って実装していきます。
GDライブラリがめちゃくちゃ優秀で、画像と文字の合成くらいなら、少ない記述であっという間に実装できてしまいました。
function createimage($title, $file_name) {
$base_image = './images/ogp_templete.jpg';
$image = imagecreatefromjpeg($base_image);
$color = imagecolorallocate($image, 0, 0, 0);
$fontfile = './fonts/ZenKakuGothicNew-Black.ttf';
$fontsize = 36;
$angle = 0;
$x = 100;
$y = 300 + $fontsize;
// 合体っ!
imagettftext($image, $fontsize, $angle, $x, $y,$color, $fontfile, $title);
// 保存
imagejpeg($image, './images/'.$file_name);
}
createimage("Hello World", "hw.jpg")
用意するのは、上記の関数が書かれたPHPファイルと、ベースとなる画像、それからフォントファイルです。フォントファイルはGoogleFontsから「Download」をクリックすると取得できます。
また、このままだと長いタイトルが来たときに画像の右端を突き抜けてしまうので、文字数に応じた改行処理を入れる必要があります。さらに、日本語には禁則処理といって改行してはいけない / しないほうがいい箇所があるので、それも力技で実装していきます。
禁則処理については、次のページを参考にしました:PHPで禁則処理を強引にやる | 無趣味の戯言
WordPressへの組み込み
とりあえずPHPだけでどうにかできることは分かったので、次はWordPressへの組み込みを考えていきます。
WordPressには、JavaScriptでいうところの「イベント」のような機能として「アクションフック」があり、記事を保存したタイミングで save_post
フックが発火するようになっています。
ということで、 functions.php
に save_post
フックをトリガーにしたOGP画像生成の関数を追記していきます。
function create_ogp_image($post_id, $post) {
// 画像を生成する処理を記載
}
// save_post フックで create_ogp_image 関数をコールする
add_action('save_post', 'create_ogp_image', 10, 2);
add_action()
関数は、以下の引数を取ります。第三・第四引数は省略可能ですが、今回は第四引数で 2 を与えて投稿の内容も取得したかったので、第三・第四引数も指定しています。
- 第一引数:トリガーとなるフック名
- 第二引数:実行する関数名
- 第三引数:プライオリティ(実行の優先順位)(初期値:10)
- 第四引数:実行する関数が受け取る引数の数(利用するフックに応じて選択できる値が変わる)(初期値:1)
create_ogp_image()
の中身は長くなるので、記事冒頭のコードからご覧ください…。
これで、記事を保存したタイミングで、 /wp-content/uploads/ogp/
に ogp_記事ID.jpg
の画像ファイルが生成されるようになりました。
この記事でいうと、以下の画像が該当します。
headへのmetaタグの出力
画像が生成できるようになったので、次に、 <head>
内にOGP画像をお知らせするタグを埋め込んでいきます。
こちらも同じく functions.php
に記述していきます。
add_action('wp_head', function() {
$og_image = get_template_directory_uri().'/images/ogp_image.jpg';
if (is_single()) {
// 個別記事ページ
$og_image = get_site_url().'/wp-content/uploads/ogp/ogp_'.get_the_ID().'.jpg';
}
echo '<meta property="og:image" content="'.$og_image.'" />';
});
wp_head
フックが呼ばれたときに、上記処理を実行しています。今回はコールバック関数を直接記述するパターンでやってみました。関数名考えたくないときにはこのパターンもおすすめです。(どっちかで統一しろ)
実際には、 og:title
や og:url
などもページの種類ごとに吐き出させているのですが、上記のコードでは og:image
に焦点を当てて、それ以外は消しています。OGPの詳細は以下のページを参照してください。
以上が、WordPressで記事のタイトル入りOGP画像を自動生成する一連の流れになります。
サーバーサイドでNode.jsを動かすことなく、PHPのGDライブラリだけでもここまで柔軟に合成処理が行えるのはなかなか魅力的だと思います。実際に実装してみると意外とシンプルなので、テーマやプラグインのカスタマイズに慣れている方であれば、すぐに組み込むことができるかと思います。ぜひ、自分好みに拡張・応用しながら、技術ブログやWordPressサイトでイマドキなOGP画像を作ってみてください!