PHPでPDF帳票を出力する方法（TCPDF + FPDI）

PHPでPDFを出力する場合、[PDFlib](http://www.pdflib.jp/) や [FPDF](http://www.fpdf.org/)  、[haru](http://php.net/manual/ja/book.haru.php) などさまざまなソリューションがありますが、たとえば、請求書や納品書といった、レイアウトが複雑なデザインの帳票を出力したいときには、 **既存のPDFをテンプレートとして活用し、そこに必要な文字だけ配置していく** ことでソースコードをシンプルに保つことができます。

既存のPDFをテンプレートとして読み込むことができ、かつ無償で活用できるライブラリとして、[TCPDF](http://www.tcpdf.org/) と [FPDI](http://www.setasign.com/products/fpdi/about/) の組み合わせをご紹介します。

# テンプレートとなるPDFを作成する

PDFの読み込みを行うFPDIは、有償のパーサーを別途導入しない場合、PDF 1.5以上のファイルをパースすることができません。テンプレートとなるPDFファイルは PDF 1.4以下の圧縮形式で作成する必要があります。

また、PHP上から文字を配置する場合、座標を使用するため（左から10mm、上から20mmの位置から…といった具合）、Illustratorなどのソフトを使用したほうが効率的です。

![ss1.jpg](https://qiita-image-store.s3.amazonaws.com/0/644/60180c19-b090-d9ae-ce14-86a0e1d51284.jpeg)

たとえば、上の例ではIllustratorを使用して領収書のひな形を作成し、ドキュメント上に　X : 42mm / Y : 108mm　の位置に　幅 : 56mm × 高さ : 10mm　のグレーのボックスを配置しています。ここにPHPを使って宛名を差し込んでいきます。

**IllustratorからPDF 1.4互換のファイルを作成する**

Illustrator CCの場合、「ファイルの種類」で「Adobe PDF」を選択し、続いて表示されるダイアログ上の、「互換性のある形式」で「Acrobat 5（PDF 1.4）」を選択します。

# ライブラリをダウンロードする

## TCPDF
1. [TCPDFをダウンロードします](http://www.tcpdf.org/download.php)。ドキュメントなどが必要でなければ tcpdf_min_* を選択すればよいでしょう。
2. ダウンロードしたファイルは解凍して、PHPのinclude_path上に ```tcpdf``` というフォルダ名で配置しました。Symfony2で使用する場合は、libフォルダなどを作成し、autoload.phpに以下のように追記します。

```php:autoload.php
$loader = require __DIR__.'/../vendor/autoload.php';

AnnotationRegistry::registerLoader(array($loader, 'loadClass'));

// include_pathを追加する
set_include_path(get_include_path() . PATH_SEPARATOR . __DIR__ . '/../lib');

return $loader;
```

## フォント
1. 日本語のフォントを使用したいので、今回は [MigMixフォント](http://mix-mplus-ipa.sourceforge.jp/migmix/) を使用させてもらいます。MigMix 2Pをダウンロードしたら、解凍して ```migmix-2p-bold.ttf``` と ```migmix-2p-regular.ttf``` を ```tcpdf/fonts/``` フォルダに配置します。
2. ```tcpdf/fonts/``` フォルダにはPHP実行ユーザに書き込み権限が必要ですので、パーミッションを変更しておきましょう。

## FPDI
1. [FPDIをダウンロードします](http://www.setasign.com/products/fpdi/downloads/)。
2. ダウンロードしたファイルは解凍して、TCPDFを配置した ```tcpdf``` フォルダ内に配置しましょう。この時点で ```tcpdf``` フォルダ内は以下のような配置になっているはずです。

```
lib/
    tcpdf/
        fonts/
            migmix-2p-bold.ttf
            migmix-2p-regular.ttf
        fpdi.php
        …（ほか、FPDIのファイル群）
        tcpdf.php
        …（ほか、TCPDFのファイル群）
```

# ライブラリを使用して、PDFを出力する

準備はここまでです。
あとは、PHPからテンプレートとなるPDFを読み込み、文字列を追加して出力します。
以下はSymfony2のコントローラですが、もちろんほかのフレームワークでも同様の書き方でPDFを出力可能かと思います。

```php:PdfController.php
<?php

namespace Awesome\DemoBundle\Controller;

// include_pathからTCPDFとFPDIを読み込む
require_once('tcpdf/tcpdf.php');
require_once('tcpdf/fpdi.php');

use Doctrine\ORM\EntityManager;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class DeliveriesPdfController extends Controller
{
    public function listAction(Request $request)
    {
        $receipt = new \FPDI();

        // PDFの余白(上左右)を設定
        $receipt->SetMargins(0, 0, 0);

        // ヘッダーの出力を無効化
        $receipt->setPrintHeader(false);

        // フッターの出力を無効化
        $receipt->setPrintFooter(false);

        // フォントを登録
        $fontPathRegular = $this->getLibPath() . '/tcpdf/fonts/migmix-2p-regular.ttf';
        $regularFont = $receipt->addTTFfont($fontPathRegular, '', '', 32);

        $fontPathBold = $this->getLibPath() . '/tcpdf/fonts/migmix-2p-bold.ttf';
        $boldFont = $receipt->addTTFfont($fontPathBold, '', '', 32);

        // ページを追加
        $receipt->AddPage();

        // テンプレートを読み込み
        $receipt->setSourceFile($this->getLibPath() . '/tcpdf/tpl/receipt.pdf');

        // 読み込んだPDFの1ページ目のインデックスを取得
        $tplIdx = $receipt->importPage(1);

        // 読み込んだPDFの1ページ目をテンプレートとして使用
        $receipt->useTemplate($tplIdx, null, null, null, null, true);

        // 書き込む文字列のフォントを指定
        $receipt->SetFont($regularFont, '', 11);

        // 書き込む文字列の文字色を指定
        $receipt->SetTextColor(255, 0, 0);

        // X : 42mm / Y : 108mm の位置に
        $receipt->SetXY(42, 108);

        // 文字列を書き込む
        $receipt->Write(0, '山田 太郎');

        $response = new Response(
            // Output関数の第一引数にはファイル名、第二引数には出力タイプを指定する
            // 今回は文字列で返してほしいので、ファイル名はnull、出力タイプは S = String を選択する
            $receipt->Output(null, 'S'),
            200,
            array('content-type' => 'application/pdf')
        );

        // レスポンスヘッダーにContent-Dispositionをセットし、ファイル名をreceipt.pdfに指定
        $response->headers->set('Content-Disposition', 'attachment; filename="receipt.pdf"');

        return $response;
    }

    /**
     * @return string
     */
    private function getLibPath()
    {
        return realpath(sprintf('%s/../lib', $this->get('kernel')->getRootDir()));
    }
}
```

これで、文字列が追加されたPDFが得られます。

![ss2.jpg](https://qiita-image-store.s3.amazonaws.com/0/644/160301b2-b408-ece2-ca5f-941281ac3d7e.jpeg)

もちろんこのまま ```$receipt->AddPage();``` を続ければ、領収書をまとめてPDFとして出力することもできます。

**以下のページを参考にさせていただきました :**
[TCPDFとFPDIの使い方メモ](http://cmf.ohtanz.com/blog/archives/2463)