Help us understand the problem. What is going on with this article?

【PHP】TCPDFで差し込み印刷をする

PHPを使って、html形式などをPDF形式で出力できるTCPDF。これを使って差し込み印刷ができる機能が必要になったので、作ってみました。

ポイント

  • 表や書状などのテンプレートを用意して画像化し、PDFの背景画像とする。
  • 背景画像を元にhtmlを構築し、データの出力位置、印字を調整する。
  • 差し込み部分は変数で渡す。
  • PDFを複数枚印刷できるようにループさせる。

以上の流れとなります。

テンプレートの画像化

テンプレートを画像化する際、必ず指定サイズ(A4など)に収まるようにして選んでください。表を画像化する場合は一旦PDF化して、それを画像化するとちょうどA4のサイズの背景画像を作成できます。

PowerPointで原稿を作成し、PDF形式で保存。それをreneePDF Aideなどで画像変換すると一番手っ取り早いと思います。

※縁取りするためにあえて灰色のレイヤーを敷いています。
haikei.png

プログラム

プログラムはこちらです(かなりミスがあったのでプログラムを修正しています。現在は動作確認済)。TCPDFはライブラリなので、外部からインストールする必要があります。

参考リンク

PHP
    require_once("{$_SERVER['DOCUMENT_ROOT']}/tcpdf/tcpdf.php"); //tcPDFライブラリ読込
        //日付指定の定数
    define("DATEY",date('Y'));
    define("DATEM",date('m'));
    define("DATED",date('d'));
        //tcPDFの設定用定数
    define("MY_PDF_PAGE_ORIENTATION"   , "P");  // P:Portrait(縦向き), L:Landscape(横向き)
    define("MY_PDF_FONT_NAME"          , "kozminproregular");  // 使用するフォント
    define("MY_PDF_FONT_SIZE"          , 10); //デフォルトのフォントサイズ
    define("MY_PDF_UNIT"               , "mm"); //用紙の単位
    define("MY_PDF_PAGE_FORMAT"        , "A4"); //用紙のサイズ
    define("MY_PDF_IMAGE_SCALE_RATIO"  , 1); //比率
    define("MY_PDF_MARGIN_HEADER"      , 0); //ヘッダに対するマージン 
    define("MY_PDF_MARGIN_FOOTER"      , 0); //フッタに対するマージン
    define("MY_PDF_MARGIN_TOP"         , 10); //全体に対する上余白
    define("MY_PDF_MARGIN_LEFT"        , 15); //全体に対する左余白
    define("MY_PDF_MARGIN_RIGHT"       , 15); //全体に対する右余白
    define("MY_PDF_MARGIN_BOTTOM"      , 20); //全体に対する下余白
    define("BACKGROUND_IMAGE_PATH"     , "img/background.png"); //表を画像化したもの(背景画像)
       //ヘッダの制御(注1)
    class MYPDF extends TCPDF {
        public function Header(){
        $this->SetAutoPageBreak(false, 0); //ページを表示させない
        $this->Image(BACKGROUND_IMAGE_PATH, 0, 0, 210, 297, '', '', '', false, 300, '', false, false, 0); //背景画像を設定
        }
    }
    //対象のデータ(これが差し込み用のデータとなります)
    $lists = [
        "桜井","藤原","野田","桑田","清水","深瀬","斎藤","大橋","草野","山口","森内","片岡","川上"
    ];

     //オブジェクト生成
     $pdf = new MYPDF(MY_PDF_PAGE_ORIENTATION, MY_PDF_UNIT, MY_PDF_PAGE_FORMAT, true, 'UTF-8', false);
     //必要ページ分のpdfデータ準備////////////////////////////////
     foreach ($lists as $data ){
                        $con = new Contents($data);
            // ページ文の追記
            $pdf -> SetTitle('PDFの差し込み印刷');
            $pdf -> SetMargins(MY_PDF_MARGIN_LEFT, MY_PDF_MARGIN_TOP, MY_PDF_MARGIN_RIGHT);
            $pdf -> setPrintHeader(true); //ページもヘッダで制御する
            $pdf -> setPrintFooter(false); //フッタは使わない
            $pdf -> setImageScale(MY_PDF_IMAGE_SCALE_RATIO);
            $pdf -> SetFont(MY_PDF_FONT_NAME, "", 10);
            $pdf -> AddPage($data); //ページの始まり
                        $html = $con -> build_contents(); //差し込み用のテーブル作成
            // PDFに変換するHTML 
            $pdf -> writeHTML($html, true, false, true, false, '');
            $pdf -> lastPage(); //ページの終わり
    }
    $fileData = $pdf-> Output(null,'S'); // デバッグ
    $data = null;
    $fileName = "テスト.pdf";
    $data = $pdf->Output(null,'S');     // PDFドキュメントを文字列として返却

    if ($data != null) {
        // ブラウザにプレビュー表示
        header('Content-Type: application/pdf');
        header('Content-Disposition: inline; filename="'.basename($fileName).'"');
        // ダウンロードする場合は下のコメントアウトを外す
        /*
        header('Content-Type: application/octet-stream', false);
        header('Content-Disposition: attachment; filename="'.basename($fileName).'"');
        */
    echo $data;
    }

     //差し込み印刷部分のテーブル制御クラス
    class Contents
    {
        private $data;
        public function __construct($data){
            $this -> data = $data;
        }

        //コンテンツの構築
        public function build_contents(){
            /*中身は後述*/
        }
        //関数の文字列埋め込み処理用
        private function c($v){ return $v; }
    }

注意点

ページ番号の調整をヘッダで指定しているのは、背景をヘッダに設定しているためです。もし、フッタでページ番号を調整しようとするとフッタ分のマージンができてしまい、大幅に印字がずれてしまうことになります。

差し込み印刷部分のレイアウト構築について

まず、注意しておくとCSSはあまり使えません。特に位置を制御するCSSは全くないといっていいと思います。ただ、paddingプロパティだけは使えるので、それで全コンテンツの位置を調整します。また、文字を装飾するcssはほとんど使用可能です。

コンテンツ内の構成はテーブルタグを使って作成しますので、構築中はテーブルの線を見えるようにしておくとやりやすいです。そしてmargin※やpositionが使えない以上、微妙な位置の調整などはpaddingやtext-alignなどを駆使することになります。また、文字の入らない空白セルには を代入することを忘れないでください。なお、差し込みのデータ部分は変数で渡します。

※marginは別途制御できるメソッドがあるので、それを駆使します。

php
public function build_contents(){
    $data = $this -> data;
    return $html = <<< EOM
    <style>
    /*コンテンツに必要なCSS情報はここに記述*/
    .year{
        letter-spacing: 1.5em;
    text-align: right;
    }
    .name{
        font-size: 20px;
    text-align: left;
    }
    .title{
        text-align: center;
        font-size: 20px;
    }
    </style>
        <table width="100%" border="1"><!-- デバッグが終わったら0にする -->
-->
          <tr>
               <td width="33%" height="20px">&nbsp;</td>
               <td width="33%" class="title">&nbsp;</td>
               <td width="34%">&nbsp;</td>
          </tr>
          <tr>
               <td class="name">&nbsp;<u>{$data} </u></td><!-- 参加者氏名 -->
               <td>&nbsp;</td>
               <td>&nbsp;</td>
          </tr>
          <tr>
               <td>&nbsp;</td>
               <td>&nbsp;</td>
               <td class="year">{$this -> c(DATEY)} {$this -> c(DATEM)}{$this -> c(DATED)}</td><!-- 日付-->
          </tr> 
        </table>
EOM;

}

プレビュー結果

プログラムを読み込むと、このようにブラウザにプレビューされます。データが代入されているか、そして位置やフォントを調節していきます(テストサンプルなので、フォントや配置は割といい加減です…)。

pdf_border.jpg

調節が終わったところで、テーブルタグのborderプロパティを"0"にし、境界線を非表示にします。

pdf_nonB.jpg

複数枚指定した場合、実際はこのように文字(変数$dataの値)や日付(PHPから取得した定数)が順番に代入されていくので、差し込み印刷ができます。
sasikomi.jpg

これで差し込み印刷の準備完了です。PDFをダウンロードする場合は、コメントアウトしているヘッダを表示してください。

印刷する場合の注意

ブラウザの印刷プレビューだと恐ろしく時間がかかります(枚数が多いと、下手したらPCがフリーズします)。ですので、複数枚綴りの1PDFファイルで保存しておいて、PDFの専用ビューアを使って、1ページずつ印刷するといいでしょう。

実務で使う場合

結局は、コンテンツに対する上下左右の余白など最終的にはプリンタの性能に依存しますが、コンテンツそのものはテーブルで作成する限り中身が崩れたりはしないので、それなりの品質を維持できるはずです。

BRSF
職業、PG・SE・DBエンジニア。オープン環境のwebプログラムをメインにシステム構築担当。使用言語はPHP(cakePHP、Laravel含)jQuery、JavaScript、ExcelVBA、Perl、Ruby、Python。現在Vue、React、Angular強化中。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした