URLに入力した文字列を動的にOGP表示させるだけのWebサービスをつくってみた

2021/04/01に公開

新年度ですね。
今日せねばならなかったシステムの対応が上流工程の遅れによりまだしばらく触れなさそうな状況になってしまいましたので、腹いせにどうでもいいサービスを作ってみつつZennデビューさせていただく次第でございます。

つくったもの

水曜どうでしょうみたいな文字を出したい欲が唐突に出てきたわけです。
https://designpocket.jp/static/font/sns/how-do-you-like-wednesday/default.aspx

フォント高いですよね。

今回はフリーフォントを使って以下のようなものを作りました。

https://kakizome.uko.jp/?t=ここを\nキャンプ地\nとする&s=60

横書きやないかというツッコミは無しで

動的に文字入りの画像を生成する

PHP GDでttfフォントを扱いたければソースコンパイルが必要らしい

PHPerなので、GDでやってみます。
手元の開発用PCはMac、設置予定のサーバーはCentOSです。
参考にさせていただいたのはこのへん:https://www.softel.co.jp/blogs/tech/archives/5212

コードを拝借してさらっと実行してみたところ、imagettftext() が無いとのこと。

# php -S localhost:5000
# [Thu Apr  1] [::1]:51656 [200]: /img.php - Uncaught Error: Call to undefined function imagettftext() in /(略)/img.php:6

オプションとしてfreetype2をコンパイル時に含めておかなきゃいけないそうです。

https://php-tips.com/2009/06/17/macosx-serverのデフォルトphpでgdを有効にする/

確認してみると確かに無い。。面倒くさそうだ・・・

php -r "print_r( gd_info() );"
Array
(
    [GD Version] => bundled (2.1.0 compatible)
    [FreeType Support] =>
    [GIF Read Support] => 1
    [GIF Create Support] => 1
    [JPEG Support] => 1
    [PNG Support] => 1
    [WBMP Support] => 1
    [XPM Support] =>
    [XBM Support] => 1
    [WebP Support] =>
    [BMP Support] => 1
    [JIS-mapped Japanese Font Support] =>
)

phpbrewを導入

Homebrewからも入れられるようだけど、phar実行タイプを使います。

https://github.com/phpbrew/phpbrew/blob/master/README.ja.md

php8をGDつきでインストール

フラグ周りで途中で引っかかる可能性があるので対処しておきます。

https://gayou.hateblo.jp/entry/2019/11/30/191642

その他必要そうなものも先に揃えておきます。
(もしかしたらbrewから直接入れてれば依存pkgも全部やってくれたか…?)

brew install gd mhash curl libzip

とりあえず、以下のphpbrewで途中でエラーが出て「なんたら_CFLAGSが〜〜〜」と言われたら、あらかじめbrewでいろいろインストールしておくとよいです。

$ phpbrew install 8.0.3 +default +gd

Macbook Pro 16 (6core) でビルド9分かかりました。
さっそく使ってみましょう。と、その前に、php.iniを編集してGDを有効化する必要があります。

$ phpbrew switch php-8.0.3
$ phpbrew config
(エディタで extension=gd の行頭のセミコロン消して有効化)
..
.
$ php -r "print_r( gd_info() );"
Array
(
    [GD Version] => bundled (2.1.0 compatible)
    [FreeType Support] => 1
    [FreeType Linkage] => with freetype
    [GIF Read Support] => 1
    [GIF Create Support] => 1
    [JPEG Support] => 1
    [PNG Support] => 1
    [WBMP Support] => 1
    [XPM Support] =>
    [XBM Support] => 1
    [WebP Support] =>
    [BMP Support] => 1
    [TGA Read Support] => 1
    [JIS-mapped Japanese Font Support] =>
)

デフォルトでFreeType入ってるのは神。もはや無償の愛。

実装

再びこちらのサイトを参考にしつつ、細部を調整して実装してみました。
https://www.softel.co.jp/blogs/tech/archives/5212

img.php
<?php

// 文字列とサイズをパラメータから取得
$text = (empty(trim($_GET['t']))) ? '404' : (string)trim($_GET['t']);
$size = (empty($_GET['s'])) ? 100 : (int)$_GET['s'];

// Twitterの横長OGP
$card_x = 600;
$card_y = 314;
$im = imagecreatetruecolor($card_x, $card_y);

// カード背景色塗り潰し
// リソース, 左上x, 左上y, 右下x, 右下y, 色
imagefilledrectangle($im, 0, 0, $card_x, $card_y, 0xffffff);

// 文字数から位置決めする
// 改行で文字列を切って行ごとに描画
$lines = explode('\n', $text);
foreach ($lines as $n => $line) {
  $length = mb_strlen($line);
  $xpos = ($card_x - ($length * $size)) / 2;
  $ypos = ($card_y + $size) / 2 - ((count($lines) - 1) * $size / 2) + ($n * $size);
  // 文字列ここで書く
  // リソース, フォントサイズ, 角度, x, y, 色, 文字
  imagettftext($im, $size, 0, $xpos, $ypos, 0x000000, 'chikarayowaku.ttf', $line);
}

// PNG画像として出力
header('Content-Type: image/png;');
imagepng($im);
imagedestroy($im);

Webフォントは「再配布」許可のものを選ぶこと

「851チカラヨワク」というものを利用させていただきました。
ダウンロードし、phpファイルと同じディレクトリに置いておきます。

https://pm85122.onamae.jp/851ch-yw.html

TwitterのOGPカードサイズは2種類

でっかく表示させるタイプの場合は「600x314」となるようです。

https://afila0.com/twitter-card-image-size/

改行コードごとにループを回して上下左右位置を決める

横方向は、行ごとに文字列長が異なるので、都度センタリングさせます。
縦方向は、総行数から1行目の基準位置を決め、そこから行数*フォントサイズで計算します。


とりあえずこれで、テキスト文字列とフォントサイズを指定して表示させることができるようになりました。
/img.php?t=テキスト&s=100

OGP化する

通常のhtml出力ですが、メタタグをつけることによってOGPが表示できるようになります。

https://kaikoku.blam.co.jp/client/digimaguild/knowledge/sns-pr/498

Twitterでネタ的なことがしたかったので、必要なタグは以下の通りとなります。

<meta property="og:url" content=" ページの URL" />
<meta property="og:type" content=" ページの種類" />
<meta property="og:title" content=" ページの タイトル" />
<meta property="og:description" content=" ページの説明文" />
<meta property="og:site_name" content="サイト名" />
<meta property="og:image" content=" サムネイル画像の URL" />
<meta name="twitter:image" content=" サムネイル画像の URL" />
<meta name="twitter:card" content="カードの種類" />
<meta name="twitter:site" content="@ユーザー名" />

とりあえず、何かしらのhtmlが必要ですね。
「HTML テンプレ」で検索してこちらをお借りしました。
くだらないことに使ってすみません。

https://f-tpl.com/tpl_091/

index.phpをつくる

上記のテンプレートのうち、index.htmlのみを編集します。
クレジット以外の不要部分は削除し、スタイルシートを適宜インラインで上書き適用させています。
サービス名は「かきぞめ」にしました。年度始やしええやろ別に

index.php
<?php
	// 文字列とサイズをパラメータから取得
	$text = (empty($_GET['t'])) ? 'いらっしゃい' : (string)trim($_GET['t']);
	$size = (empty($_GET['s'])) ? 100 : (int)$_GET['s'];
	$text_noreturn = str_replace('\n', ' ', $text); // 改行ないやつ
	$text_nospace = str_replace('\n', '', $text); // 空白もないやつ
	$get_params = '?t=' . $text . '&s=' . $size;
?>

<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="description" content="<?= $text_noreturn ?>">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title><?= $text_nospace ?> | かきぞめ</title>
<meta property="og:url" content="https://kakizome.uko.jp<?= $get_params ?>" />
<meta property="og:type" content="article" />
<meta property="og:title" content="<?= $text_nospace ?>" />
<meta property="og:description" content="書いた文字がOGPになるだけのサービス" />
<meta property="og:site_name" content="かきぞめ" />
<meta property="og:image" content="https://kakizome.uko.jp/img.php<?= $get_params ?>" />
<meta name="twitter:image" content="https://kakizome.uko.jp/img.php<?= $get_params ?>" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@ukokq" />
<link rel="stylesheet" media="all" href="css/style.css">
</head>

<body id="top">

<section id="sec01" style="padding-top: 30px;">
	<header>
		<h2><span>KAKIZOME</span></h2>
	</header>
	<div class="innerS" style="text-align: center; padding-bottom: 40px;">
		<?= $text_noreturn ?>
	</div>
</section>

<div id="mainImg" style="text-align: center; height: initial;">
	<img src="img.php<?= $get_params ?>" alt="<?= $text_noreturn ?>" style="height: auto; width: initial; margin: 0 auto; max-width: 100%; position: initial;">
</div>

<footer id="footer">
	このHTMLは<a href="https://f-tpl.com/tpl_091/" target="_blank">無料ホームページテンプレート.com さんの テンプレートNo.091</a>をお借りしています。<br>
	Copyright(c) 2016 Sample Inc. All Rights Reserved. Design by <a href="http://f-tpl.com" target="_blank">http://f-tpl.com</a><!-- ←クレジット表記を外す場合はシリアルキーが必要です http://f-tpl.com/credit/ -->
</footer>

</body>
</html>

デプロイする

以下は割愛です。サーバー持ってない場合はHerokuとかで良いと思います。

  • サブドメイン取得
  • nginxでリバースプロキシさせてphp-fpmで動かすようにする
  • Let's Encryptでhttps化
  • nginxのconfにhttp2を書き加えて再起動

おわり。2021年度もはりきっていきましょう。

https://kakizome.uko.jp/?t=おつかれ\nちゃん&s=50

おまけ参考:Node.js版

コンパイルという文字を見たら発疹が出たので途中でNode.jsに浮気しようとしました。
ただし、よく使っているバックエンドは nginx & php-fpm の構成のため、これも一旦考え直しで。
Node.jsさらっと動かせる環境用意されてればexpressを追加してこの方法も良しですね。

https://qiita.com/ampersand/items/805e75b5a54797923885

Discussion