【PHP】PDFテンプレートの穴埋めを少しでも楽にする

  • 2020年3月5日
  • 2020年3月6日
  • PHP

 PDFはPortable Document Formatの略称で、ファイル形式の一つです。PDFファイルはどの環境でも同じ様に見れる(アス比、マスの幅など)ことが利点の一つで多く使われています。電子書籍でもPDF形式で配布されており、電子書籍用ソフトの取り込み可能なファイル形式にもよく含まれています。
Portable Document Format – Wikipedia
 そんなわけでPDF需要はそれなりにあります。webサービスでは主に印刷、FAXに関連して現れます。webサービスではよく特定のデータ形式の値を特定の場所にあてはめて、あてはめたものをユーザに見せて……ということをします。PDFも例にもれず、値の穴埋めをして出力することが求められやすいです。そしてその穴埋めを元であるテンプレートにPDFファイルを渡されることが少なくありません。
 PDFファイル上の特定の場所に特定の図形や文字列を埋め込む方法の一つにFPDI, TCPDFとい二つのライブラリを組み合わせる方法があります。TCPDFはPDFファイルを作成できるライブラリであり、FPDIはPDFを読み取り、TCPDFのテンプレートとして使用できるようにするライブラリです(FPDI本来の目的はFPDFというまた別のPDFライブラリの拡張)。FPDIとTCPDFの連携にそれほど悩む必要はありません。FPDIの作者が適切なバージョンの組み合わせでパッケージをcomposerでダウンロードできるようにしてくれています。
Setasign/FPDI-TCPDF: A kind of metadata package for Composer with fixed dependencies for the latest versions of FPDI and TCPDF.
 FPDI-TCPDFでPDFを描く方法は大体次の通りです。

// 初期処理
// 全くの空のPDFを用意
$pdf = new \FPDI($orientation = 'P', $unit = 'mm', $format = 'A4', $unicode = true, $encoding = 'UTF-8');

$pdf->SetMargins(0, 0, 0); // マージンを設定
$pdf->setPrintHeader(false);// 印刷時のヘッダを設定
$pdf->setPrintFooter(false);// 印刷時のフッタを設定
$pdf->SetAutoPageBreak(false);// 自動改ページの有効無効を設定
$pdf->AddPage($orientation, $format);// 書く対象のPDFに新たなページをセット

$pdf->setSourceFile("hoge.pdf"); // テンプレートファイルを設定
$tplIdx = $pdf->importPage(1); // テンプレートファイルからページを読み込み
$pdf->useTemplate($tplIdx, 0, 0); // 読み込んだテンプレートを使うと宣言

$font = TCPDF_FONTS::addTTFfont($this->_font_root() . 'ipag.ttf');// フォントを用意
$pdf->SetFont($font, 'B', 11); // 書く対象のPDFで扱うフォントを設定

$pdf->SetXY(X座標, Y座標);
$pdf->Cell(幅, 高さ, 入力したい文字列, (bool)外枠の有無, セルを作った後のXY座標定義, 揃え);

 ページをセットしたら箱の左上の座標をsetXYで、箱の大きさをCellで指定して箱の中に文字列を打ち込む感じです。画像は箱の外枠を”有”にした場合です。箱があってその中に文字列を入れているのが分かりやすいと思います。

 これを工夫せずにやると座標と箱の大きさを調べるのが尋常でなく手間です。

※この記事を書くにあたって調べ直していたら\TCPDF::Writeメソッドが存在していることに気づきました。もしかしたらこちらの方がCellより楽に文字列を書けるかもしれませんが、この記事ではCellメソッドでの記述で話を進めます。

 まずPDF上の座標を簡単に取得できる環境を整えます。解像度の大きい画像にするのがおすすめです。この機能は多くのPDF閲覧用ソフトに入っています。例えば、Adobe Acrobatの”PDF を書き出し”、XChange-Viewerの”イメージへエクスポート”、です。
 画像化したら適当な画像編集ソフトで文字列を入れるべき場所の座標を数値として取得(大体テキストファイルへメモ)していきます。都度、対象範囲の左上のピクセルと右下のピクセルを記録します。
 

 記録したらコーディングの時間です。次の様にCellメソッドを左上座標、右下座標で呼べるようにラッピングします。

	/**
	 * 文字の入ったcellをPDF内に作る
	 * @param string $value 入力したい文字列
	 * @param string $x_start Cell左端
	 * @param string $y_start Cell上端
	 * @param string $x_end Cell右端
	 * @param string $y_end Cell下端
	 * @param int $fontsize フォントサイズ
	 * @param string $align 整列種類
	 */
	protected function set_str_cell($value, $x_start, $y_start, $x_end, $y_end, $fontsize = 16, $align = self::ALIGN_LEFT)
	{
		// $this->_pdfには上述のコードの様な感じで初期化したTCPDFインスタンスが入ります。
		// $this->_scaleには画像の座標とTCPDF上の座標の比をあらかじめ格納します。
		// この比の探索は未だに効率が微妙な手作業二分探索ぐらいしかできていません。

		// scale倍して左上の座標をセット
		$this->_pdf->SetXY($x_start * $this->_scale, $y_start * $this->_scale); 
		 // 枠の大きさでよくフォントサイズが変わるのでここで扱えるようにする
		$this->_pdf->SetFontSize($fontsize);
		// 左上座標と右下座標から箱の大きさを決定。Cellメソッドを呼び出し
		$this->_pdf->Cell(($x_end - $x_start) * $this->_scale, ($y_end - $y_start) * $this->_scale, $value, false, 0, $align);
	}

 こうなるとあとは簡単です。画像から得た座標に従ってだーっとCellメソッドのラッピングメソッドset_str_cellを呼び出します。

		$this->set_str_cell($post->name_kana, 5066, 7793, 12419, 8402, 14);
		$this->set_str_cell($post->name, 5066, 8416, 12419, 9784);
		$this->set_str_cell($post->zip_pre(), 5926, 10620, 6574, 10988, 12);
		$this->set_str_cell($post->zip_post(), 7422, 10620, 8286, 10988, 12);
		$this->set_str_cell($post->full_address(), 5288, 11416, 20928, 12930);
		$this->set_str_cell($post->birthday_year_digit(0), 14776, 8762, 15384, 9784, 16, self::ALIGN_CENTER);
		$this->set_str_cell($post->birthday_year_digit(1), 15385, 8762, 15993, 9784, 16, self::ALIGN_CENTER);
		$this->set_str_cell($post->birthday_year_digit(2), 15994, 8762, 16602, 9784, 16, self::ALIGN_CENTER);
		$this->set_str_cell($post->birthday_year_digit(3), 16603, 8762, 17211, 9784, 16, self::ALIGN_CENTER);
		$this->set_str_cell($post->birthday_month_digit(0), 17718, 8762, 18328, 9784, 16, self::ALIGN_CENTER);
		$this->set_str_cell($post->birthday_month_digit(1), 18329, 8762, 18939, 9784, 16, self::ALIGN_CENTER);
		$this->set_str_cell($post->birthday_day_digit(0), 19441, 8762, 20051, 9784, 16, self::ALIGN_CENTER);
		$this->set_str_cell($post->birthday_day_digit(1), 20052, 8762, 20661, 9784, 16, self::ALIGN_CENTER);

 PDFの出力結果を見ながらいちいち座標を調整するよりずっと楽です。

>株式会社シーポイントラボ

株式会社シーポイントラボ

TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:〒432-8003
   静岡県浜松市中央区和地山3-1-7
   浜松イノベーションキューブ 315
※ご来社の際はインターホンで「316」をお呼びください

CTR IMG