結論(先に解決策)
PHP の GD で OG 画像などを生成したとき、日本語が表示されない(空白・豆腐・文字化け)、または
imagettftext(): Could not find/open font
imagettfbbox(): Could not find/open font
が出る——原因はほぼ次の2つです。
- 日本語に対応した TTF/OTF フォントを指定していない(英語フォントだと日本語グリフが無く描画されない)
- フォントパスが相対パスになっている(GD のフォント指定は絶対パスが必要)
解決は 日本語フォント(例: IPAexGothic)を同梱し、__DIR__ 基準の絶対パスで渡すこと。
// ✅ __DIR__ 起点で絶対パスにする
$font = __DIR__ . '/assets/fonts/ipaexg.ttf'; // 日本語対応 TTF を同梱
imagettftext($img, 40, 0, 50, 100, $color, $font, '東京都の人口データ');
症状
- 画像は生成されるが日本語部分だけ何も描かれない/英数字は出る
-
□□□(豆腐)になる - 次のエラーで生成が止まる
Warning: imagettftext(): Could not find/open font in ...
Warning: imagettfbbox(): Could not find/open font in ...
ローカルでは出たのに本番(別サーバー)に上げたら消える、CLI からは動くのにWeb から呼ぶと font が見つからない、というのも典型です。
原因
原因1: フォント自体が日本語を持っていない
GD は指定された TTF/OTF のグリフ(字形)しか描けません。Arial や DejaVu Sans のような欧文フォントには日本語のグリフが含まれないため、日本語は描画されません(エラーにならず「空白」になることが多く、気づきにくい)。
→ 日本語対応フォント(IPAexGothic / Noto Sans JP / Migu など)を使う必要があります。
原因2: パスが相対で、解決に失敗している
imagettftext() / imagettfbbox() のフォント引数は絶対パスが前提です。相対パスを渡すと「現在の作業ディレクトリ(CWD)」基準で解決されますが、この CWD は呼び出し経路で変わります。
| 実行経路 | CWD の例 | 相対パスの解決先 |
|---|---|---|
| CLI でスクリプト直叩き | スクリプトのある場所 | たまたま当たる |
| Web(PHP-FPM)から | docroot やサーバー既定 | ずれる → not found |
| cron から | ホームや /
|
ずれる → not found |
「ローカルの CLI では動くのに本番 Web で Could not find/open font」の正体はこれです。CWD に依存する相対パスをやめ、スクリプト位置を起点にした絶対パスにすれば経路によらず固定されます。
補足: 古い GD では先頭にスラッシュやドライブレターが無いパスを「システムフォントディレクトリ基準」と解釈する挙動もあり、ますます直感に反します。絶対パスで渡せば全部回避できます。
解決
1. 日本語フォントをプロジェクトに同梱する
サーバーに日本語フォントがある保証はありません。リポジトリに TTF を含めて配置するのが確実です(ライセンスは要確認。IPAex フォントは再配布可で OG 生成によく使われます)。
project/
├── lib/og.php
└── assets/fonts/ipaexg.ttf ← 同梱
2. DIR 基準の絶対パスで渡す
function render_og(string $text): void {
$img = imagecreatetruecolor(1200, 630);
$color = imagecolorallocate($img, 255, 255, 255);
// __DIR__ で「このファイルの場所」を起点にする → CWD に依存しない
$font = __DIR__ . '/../assets/fonts/ipaexg.ttf';
if (!is_file($font)) {
// 早期に気づけるようにしておく
throw new RuntimeException("font not found: {$font}");
}
imagettftext($img, 40, 0, 50, 320, $color, $font, $text);
// ...
}
ポイントは __DIR__。imagettftext の前に is_file() で存在チェックを入れておくと、「空白で生成される」よりも早く・確実に原因に気づけます。
3. 文字幅の計算(imagettfbbox)も同じフォントを絶対パスで
中央寄せなどで imagettfbbox() を使う場合も、同じ絶対パスのフォントを渡します。ここだけ相対にして失敗するミスが多いです。
$bbox = imagettfbbox(40, 0, $font, $text); // $font は上と同じ絶対パス
$width = $bbox[2] - $bbox[0];
$x = (int)((1200 - $width) / 2);
imagettftext($img, 40, 0, $x, 320, $color, $font, $text);
補足
- そもそも
imagettftext自体がundefinedの場合は、GD 拡張(と FreeType サポート)が無効です。php -m | grep -i gdやphpinfo()の "FreeType Support => enabled" を確認してください。 - 本番が共有ホスティングの場合、システムフォントの場所や有無に依存しない**「同梱 +
__DIR__絶対パス」**が最も再現性が高いです。 - 一言でいえば 「日本語フォントを自分で持ち、
__DIR__で絶対パス指定する」。これで CLI・Web・cron のどの経路から呼んでも同じ結果になります。