PHPのGDライブラリを用いて文字列を描画する

# はじめに
本稿では、PHPを用いてサーバサイドで文字列を含む画像を生成し、クライアントに出力する方法について説明します。

# 文字列の描画手順
1. フォント(.ttfファイル。後述)、フォントのサイズ、描画する文字列の角度を指定する
1. 描画領域の座標系を取得する(詳しくは後述)
1. 文字列を描画する
1. 生成した画像を出力する

## フォントの指定方法について
PHPのimagettfbbox()関数、imagettftext()関数のフォントに指定方法には癖があり、環境によって異なるようです。  
そこで、PHPスクリプトのパスをもとにした絶対パスで指定するのが無難なようです。
```php
$font_file_name = realpath('./') . '/NotoSerifJP-Regular.ttf';
```
ここで```realpath('./')```がスクリプトの絶対パスとなり、スクリプトと同じディレクトリに.ttfファイルが存在する場合は上記のようになります。

## 描画領域の座標系について
まず、実際に文字列を描画する前にimagettfbbox()関数を用いてキャンバスのサイズを計算する必要があります。  
以下のように、フォントサイズ、フォント名、実際に描画する文字列、文字列を回転させる角度を指定します。
```php
$bounding_box = imagettfbbox(
    $font_size,
    $text_angle,
    $font_file_name,
    $text
    //PHP8.0では引数にarray $options = []が追加されている
);
```
imagettfbbox()関数の戻り値となるバウンディングボックスでは、描画対象となる画像の４隅の座標(すなわち８つの数値)が得られます。  
バウンディングボックスの数値は最初の２つが文字列の左下のX座標とY座標、以下同様に右下、左上、右上の座標となります。  
ここで癖となるのが座標系にはマイナスの数値も入ってくるということです。  
そこで、X座標、Y座標それぞれの最大値と最小値を計算し、その差分がキャンバスのサイズとなります。  
```php
$max_x = max($bounding_box[0], $bounding_box[2], $bounding_box[4], $bounding_box[6]);
$min_x = min($bounding_box[0], $bounding_box[2], $bounding_box[4], $bounding_box[6]);
$max_y = max($bounding_box[1], $bounding_box[3], $bounding_box[5], $bounding_box[7]);
$min_y = min($bounding_box[1], $bounding_box[3], $bounding_box[5], $bounding_box[7]);
//画像サイズ
$canvas_width = $max_x - $min_x;
$canvas_height = $max_y - $min_y;
```
さらにテキストに角度を指定した場合はややこしくなりますが、これはひとまず角度０°(つまり水平)の文字列を描画してから説明したいと思います。

# 文字列を描画する
まず、前項で計算した画像サイズから空のイメージを生成します。
```php
$image = imagecreatetruecolor($canvas_width, $canvas_height);
```
次に、文字列の色となる前景色と、必要に応じて背景色を生成します。  
ここで空のイメージが必要になります。
```php
$background_color = imagecolorallocate($image, 255, 255, 255); //背景色: FFFFFF
$foreground_color = imagecolorallocate($image, 0, 0, 255); //前景色：: 0000FF
```
さらに、必要に応じて背景色で塗りつぶします。
```php
imagefilledrectangle($image, 0, 0, $canvas_width, $canvas_height, $background_color);
```
ここまでやってようやく文字列を描画することができます。
```php
imagettftext(
    $image, //あらかじめ生成した空のイメージ
    $font_size,
    $text_angle, //0°(水平)を基準として任意の角度に回転させることができる
    $min_x * -1, //テキストの開始位置のX座標：別記
    $min_y * -1, //テキストの開始位置のY座標：別記
    $foreground_color, //文字色
    $font_file_name, //.ttfファイルのパス
    $text //描画する文字列
);
```

# 生成した画像を出力する
画像をWebクライアントに出力する方法については以前の記事で紹介しています。

Webサーバ上で生成した画像データをWebクライアントに出力する #PHP - Qiita https://qiita.com/kk_outlaw/items/8f856f28c10097ee3e06

ここで上述のサンプルと違って注意しなければならないのは、最後に画像を保持しているメモリを解放しなければならないところです(下記コードの最後のimagedestroy()関数)。  
```php
//画像を出力する前に明示的にレスポンスヘッダを出力する
header("Content-Type: image/png");
header('Content-Disposition: inline; filename="logo.png"');

//PNGイメージでストリーム出力
imagepng($image, null, -1, -1);

//後処理
imagedestroy($image);
```

## 動作確認
それではGitHub上にアップしたサンプルコードを動かしてみます。  
https://github.com/kk-outlaw/php_gd_string

コマンドラインからPHPのビルトインサーバーを起動します。  
```
>php -S localhost:8080 gd_string.php
```
Webブラウザでlocalhost:8080にアクセスします。  
以下のように画像が表示されれば成功です。  
このサンプルではフォントベースラインの影響を受けず(小文字のyの下部が見切れていない)、日本語も上部が(ギリギリ)見切れていないことがわかります。  
![horizontal_sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3893745/9e84c024-6aac-dc81-a1dd-42147ff9d093.png)

## 応用例
また、本稿を応用したお遊びプログラムもあります。  
(というかこのお遊びがやりたくて文字列の描画方法を調べたので順番が逆で、未完成な部分もあります。)

タンパベイ・レイズ風ロゴジェネレータ 
https://ss1.xrea.com/kevintheoutlaw.s1008.xrea.com/rays_logo/
https://github.com/kk-outlaw/rays_logo

# 終わりに
記事が長くなってしまいましたので、文字列を斜めに描画する方法と注意点については、改めて説明したいと思います。