PHPでPDFを生成する場合、多くの便利なライブラリがあります。そういったライブラリを用いて既にあるPDFテンプレートの上に文字、図形、画像などを載せる目的を達成する場合、特に便利なのがTCPDFとFPDIの併用です。
TCPDFとFPDIのインストールと簡易な使い方は次です。
# composerでインストール composer require setasign/fpdi composer require tecnickcom/tcpdf
<?php // 必要なライブラリを読み込み require_once __DIR__ . '/vendor/autoload.php'; // 名前空間の設定 use setasign\Fpdi\Tcpdf\Fpdi; // FPDIの初期化 $pdf = new Fpdi(); // ページを追加 $pdf->AddPage(); // テンプレートとなるPDFファイルを指定 $pdf->setSourceFile(__DIR__ .'/you.pdf'); // テンプレートPDFの1ページ目をインポート $tplIdx = $pdf->importPage(1); // インポートしたページをテンプレートとして使用し、10,10の位置に幅100mmで配置 $pdf->useTemplate($tplIdx, 10, 10, 100); // テンプレートの上にテキストを書き込む $pdf->SetFont(TCPDF_FONTS::addTTFfont(__DIR__ . '/ipag.ttf')); // フォントを設定 $pdf->SetTextColor(255, 0, 0); // テキストの色を設定 $pdf->SetXY(20, 20); // テキストを書き始める位置を設定 $pdf->Write(0, '浜松太郎'); // テキストを書き込む // PDFを新しいPDFファイルとして保存 $pdf->Output(__DIR__.'/new_pdf.pdf', 'F');
FPDIの文字列を埋め込む機能は自在に動作させられて便利なのですが、その自由度に比例するかのように引数が複雑です。この記事では FPDI の MultiCell メソッドを使って任意の範囲に文字を適切な大きさで収める方法を紹介します。
MultiCellメソッドを使用するとPDF内の任意の位置に文字列の描画範囲(セル)を指定し、その範囲内でテキストをうまく配置することが可能です。MultiCellメソッドは次の様に使用できます。
$w = 25;
$h = 10;
$txt = '浜松太郎';
// $w,$h,$txt以外の値はデフォルト値です
$fpdi->MultiCell(
$w, // セルの幅
$h, // セルの高さ
$txt, // 埋め込む文字列
$border = 0, // 枠線。0はなし。1はあり
$align = 'J', // セルの中の横方向の文字の位置。L,C,R,J。それぞれ左揃え、中央揃え、右揃え、両端揃え
$fill = false, // セルの中を塗りつぶすか否か
$ln = 1, // このメソッド実行後のカーソルがどこに行くか。1は次の行の先頭に移動する
$x = null, // セルの左上のx座標。nullは最後のカーソル位置を参照
$y = null, // セルの左上のy座標。nullは最後のカーソル位置を参照
$resetH = true, // 最後に呼ばれたメソッド記憶されている高さをリセットするか
$stretch = 0, // フォントを拡大縮小するか。0はしない
$isHtml = false, // $textがHTMLか否か。通常は使わない。もしHTMLを書きたいならwriteHTMLCellやwriteHTMLを使う
$autoPadding = true, // 行の幅を考慮して自動的にパディングの大きさを調整するかしないか
$maxH = 0, // セルの最大高さ。0なら無効になる
$valign = 'T',// セルの中の縦方向の文字の位置。T,M,B。それぞれ上揃え、中央揃え、下揃え
$fitCell = false // フォントサイズを小さくして、セル内のすべてのテキストを収めようとするか否か
);
各引数の意味はコメントの通りです。より詳しく正確な内容を得るならば次の公式ドキュメントとライブラリ内のソースコードがおすすめです。
classes-TCPDF · TCPDF#MultiCell()
TCPDF/tcpdf.php at main · tecnickcom/TCPDF#L5860
MultiCellメソッドを使うと文字列を特定の範囲に任意のサイズで配置することができます。文字列が指定した範囲からはみ出しそうになったらいい感じに文字列を自動的に改行して範囲内に収まる様に頑張ってくれます。お題にある任意の範囲に文字を適切な大きさで収める目的ならば引数は次の様に指定できます。
$fpdi->MultiCell(
$w,
$h,
$txt,
$border = 0,
$align = 'J',
$fill = false,
$ln = 1,
$x = null,
$y = null,
$resetH = true,
$stretch = 0,
$isHtml = false,
$autoPadding = true,
$maxH = 0,
$valign = 'T',
$fitCell = true // 最後尾のここを true にするだけでいい感じに範囲内に収める様に折り返したり文字が小さくなったりします
);
最後の fitCell のみが範囲内収まる様にするか否かを決定づけます。シンプルな引数の値の変更だけでやりたいことが実現できるのですが、ここで引数の多さがめんどくささを大きくしてしまいます。名前付き引数を使える PHP8 .0以降ならば毎回 fitCell 設定が冗長に感じるだけで済むのですが、メンテ等によってそれより前のバージョンの PHP を触る場合、引数の指定は引数の定義順でしかできないという制限が大きく働きます。こうなるとfitCellより手前の多数の引数も毎回指定する必要が出てくるためものすごく冗長になってしまいます。この解決にラッパーメソッドないし関数を作る方法があります。
前述した様にMultiCellメソッドの使用には多くの引数が必要です。しかしながら実際に文書を生成する時は一部のパラメーターしか変更しないことがほとんどです。このためラッパーメソッドを作成し引数を減らすことでより簡易ににMultiCellメソッドを扱えます。これは例えば次の様にできます。
class AppFPDI
{
/** 折り返したありでテキストをセットする */
public function setTextWrap(string $text, float $x, float $y, float $w, float $h, string $align = 'L', string $valign = 'M'): void
{
$this->fpdi->MultiCell(
$w, // 幅
$h, // 高さ
$text, // 文字列
self::DEBUG, // 枠線。どの範囲に文字列が入るか否かを確認することができるなどのデバッグモードを定義し、そのフラグと紐づけている
$align, // 横整列
$fill = false, // セルの中を塗りつぶすか否か。ここでは文字列を埋め込みたいだけなので塗りつぶさない
$ln = 1, // このメソッド実行後のカーソルがどこに行くか。1は次の行の先頭に移動する
$x, // x座標
$y, // y座標
$resetH = true, // 最後に呼ばれたメソッド記憶されている高さをリセットするか
$stretch = 0, // フォントを拡大縮小するか。0はしない
$isHtml = false, // $textがHTMLか否か。通常は使わない。もしHTMLを書きたいならwriteHTMLCellやwriteHTMLを使う
$autoPadding = true, // 行の幅を考慮して自動的にパディングの大きさを調整するかしないか
$maxH = 0, // セルの最大高さ。0なら無効になる
$valign,// 縦整列
$fitCell = true // フォントサイズを小さくして、セル内のすべてのテキストを収めようとするか否か
);
}
}
// 使用例
$w = 25;
$h = 10;
$txt = '浜松太郎';
$appFpdi->setTextWrap($txt, 0, 0, $w, $h);
このメソッドでは埋め込む文字列、セルの位置、セルのサイズ、文字の揃えのみを指定できるようにし、自分のユースケースに合う様にたまに変更するだけの揃えにデフォルト値、他の部分を固定値としています。こんな感じにするとFPDIの持つ自由な機能を活かしつつ、何度も文字列を埋め込む処理を作りやすく、読みやすくできます。