ZF3でTCPDFを使って任意のテキストを入れたPDFを出力する
※本稿では、Zendframework 3 のことを ZF3 とか zf3 と呼んでいます。 ※2017年6月18日 2:13AM に投稿した本稿(当初postId=4172088349873447690)を、 少し修正しようとして操作を誤り、翌日1:15AM頃に削除してしまいました。 当初の記事内容に若干の修正を加えて再アップします。 (提示したPdfManagerクラスの汎用性を少し改善した)
空のZF3プロジェクトへTCPDFを導入
php で TCPDF を使いたい場合、 composer を活用すれば簡単に導入ができます。参考記事: TCPDF + FPDIで既存PDFに日本語文章を追加
上記リンク先を参考にしつつ、
仮に先日書いた記事 ZF3で空のプロジェクトを作成する自分なりの手順 で作った空のプロジェクト「emptyPJ」へ composer で組み込む、とした場合は次のようにします。
- composer.json に下記を追記
- composer.jsonの中身
{ "require": { "php": "^5.6 || ^7.0", "zendframework/zend-component-installer": "^1.0", "zendframework/zend-mvc": "^3.1", // ←行末にコロン "tecnickcom/tcpdf": "6.2.*", // ←追記 "setasign/fpdi": "1.6.*", // ←追記 "setasign/fpdi-tcpdf": "1.6.*" // ←追記 }, } - composer update する
- Windows上 Git Bash でのコマンド実行例
hoge@host MINGW64 ~/work/emptyPj $ php composer.phar update Loading composer repositories with package information Updating dependencies (including require-dev) Package operations: 3 installs, 0 updates, 0 removals - Installing setasign/fpdi (1.6.2): Loading from cache - Installing tecnickcom/tcpdf (6.2.12): Loading from cache - Installing setasign/fpdi-tcpdf (1.6.1): Loading from cache setasign/fpdi suggests installing setasign/fpdf (FPDI will extend this class but as it is also possible to use "tecnickcom/tcpdf" as a n alternative there's no fixed dependency configured.) setasign/fpdi suggests installing setasign/fpdi-fpdf (Use this package to automatically evaluate dependencies to FPDF.) Writing lock file Generating autoload files
- ひとまず IndexController の indexAction で new FPDI() してみる。
- ※ FPDIクラスは、TCPDFクラスを継承したもの。(上述のcomposer update でインストールされたもの達によって下準備済み)
- emptyPJ/module/Mymodule/src/Mymodule/Controller/IndexController.php
// ~~省略~~ public function indexAction() { $pdf = new \FPDI(); return new ViewModel(array('version'=>$pdf->getPdfVersion())); } // ~~省略~~ - emptyPJ/module/Mymodule/view/mymodule/index/index.phtml
<?php echo 'index/index: PDF version:'.$this->version; - 当該ページへアクセスして「index/index: PDF version:1.3」のような表示になったらひとまず動作できている。
以後、上述した emptyPJ を編集していく形で説明を続けます。
※PDF出力の実装は別のアクションで行うので、
※上記まででindexAction() や index.phtml へ加えた変更は破棄してください。
既存のPDFテンプレートに任意のテキストを入れて出力する
背景にセットするPDFテンプレートを用意する
ひとまず、次のような2ページに渡るPDFテンプレート(A4サイズ:縦)を用意します。
各ページの異なるところはページ番号だけです。
これを例えば、emptyPJ/data/pdf_tamplate/ 配下に sample.pdf というファイル名で配置します。
※pdf_tamplateディレクトリは予め作成してください。
PDFファイルにあれこれ手を加えるModelクラスを用意する
次に、PDFファイルを好きに扱うためのModelクラスを作ります。
※Modelディレクトリは予め作成してください。
- emptyPJ/module/Mymodule/src/Mymodule/Model/MyPdf.php
<?php namespace Mymodule\Model; /** * PDFクラス * @see TCPDF */ class MyPDF extends \FPDI { /** 日本語フォント */ const FONT_JP = 'kozminproregular'; /** * ヘッダー * @see TCPDF::Header() */ public function Header() { $this->SetFont(self::FONT_JP, '', 8); $outputDate = 'date: '.date('Y-n-j'); $this->Cell(0, 8, $outputDate, 0, 0, 'R'); } /** * フッター * @see TCPDF::Footer() */ public function Footer() { $this->Cell(0, 8, $this->getAliasNumPage() . '/' . $this->getAliasNbPages(), 0, 0, 'C'); } }- emptyPJ/module/Mymodule/src/Mymodule/Model/PdfManager.php
<?php namespace Mymodule\Model; use Mymodule\Model\MyPdf; class PdfManager { const DEFAULT_PDF_TMPL_PATH = '/data/pdf_template/dummy.pdf'; /** @var string */ const ORIENTATION_PORTRAIT = 'P'; /** @var string */ const ORIENTATION_LANDSCAPE = 'L'; /** * 用紙の向き * 縦(デフォルト) * 横 * @var array */ const ORIENTATION_ARRAY = array( self::ORIENTATION_PORTRAIT, self::ORIENTATION_LANDSCAPE ); /** @var string */ const PAGE_FORMAT_A4 = 'A4'; /** @var string */ const PAGE_FORMAT_A3 = 'A3'; /** * 用紙フォーマット * A4: 210x297 mm (デフォルト) * A3: 297x420 mm * @var array */ const PAGE_FORMAT_ARRAY = array( self::PAGE_FORMAT_A4, self::PAGE_FORMAT_A3 ); /** @var string */ const ENCODING_UTF8 = 'UTF-8'; /** @var TCPDF */ protected $pdfObj; /** @ver string */ protected $templatePath; /** * @param string $pageOrientation default: ORIENTATION_PORTRAIT * @param string $pageFormat * @param string $encode * @param string $tmplPath */ public function __construct( $pageOrientation = self::ORIENTATION_PORTRAIT, $pageFormat = self::PAGE_FORMAT_A4, $encode = self::ENCODING_UTF8, $tmplPath = null ) { $pageOrientation = in_array($pageOrientation, self::ORIENTATION_ARRAY) ? $pageOrientation : self::ORIENTATION_PORTRAIT; $pageFormat = in_array($pageFormat, self::PAGE_FORMAT_ARRAY) ? $pageFormat : self::PAGE_FORMAT_A4; $encode = empty($encode) ? self::ENCODING_UTF8 : $encode; if ( ! (isset($this->templatePath) && is_null($tmplPath)) ) { $this->templatePath = is_null($tmplPath) ? getcwd().self::DEFAULT_PDF_TMPL_PATH : $tmplPath; } $this->pdfObj = new MyPDF( $pageOrientation, PDF_UNIT, $pageFormat, true, $encode, false ); $this->pdfObj->SetFont(MyPDF::FONT_JP, '', 8); $this->pdfObj->setFontSubsetting(true); } /** * @return object TCPDF */ public function getPdfObj() { return $this->pdfObj; } /** * 出力するPDFデータを作成する */ public function create() { // 背景に既存のPDFを使うのでマージンを無効にする $this->pdfObj->SetAutoPageBreak(false, 0); $this->pdfObj->SetMargins(0, 0, 0); $this->pdfObj->setPrintHeader(false); $this->pdfObj->setPrintFooter(false); $this->pdfObj->AddPage(); // 背景PDFセット $this->pdfObj->setSourceFile($this->templatePath); // 背景PDFページ設定 $this->pdfObj->useTemplate($this->pdfObj->importPage(1)); } }- ここで書いたcreate()メソッドがPDFの中身を作り込んでいく処理になりますが、この時点ではテンプレートPDFを背景にセットするのみです。後ほど、create()メソッドの中身にテキスト追加処理を書いていきます。
PDFを出力するためのアクションを用意する
次に、PDFを出力するための新しいアクションを IndexController に作成します。
- 元々 emptyPJ の Mymodule では極力シンプルな作りにする目的でルーティングを固定してしまっているので、IndexController で複数のリクエスト先アクションを動的に扱えるよう、routes の type を Literal から Segment に変更します。
- emptyPJ/module/Mymodule/config/module.config.php
- 新しく追加するアクション名を printPdfAction() とします。
- emptyPJ/module/Mymodule/src/Mymodule/Controller/IndexController.php
// ~~省略~~ use Mymodule\Model\PdfManager; // ←追加 class IndexController extends AbstractActionController { // ~~省略~~ public function printPdfAction() // ←この関数ブロックを追加 { $pdfMgr = new PdfManager(); // PDFデータを作成する $pdfMgr->create(); // view にPDFをアタッチする $view = new ViewModel(array('pdf' => $pdfMgr->getPdfObj())); $view->setTerminal(true); return $view; } // ~~省略~~ - emptyPJ/module/Mymodule/view/mymodule/index/print-pdf.phtml
<?php if (isset($this->pdf)) { $this->pdf->Output("output.pdf", 'I'); } else { echo 'Error'; } - テンプレートPDFの1ページ目のみの出力が得られれば、新しく作成したModelクラスがひとまず動作できているということです。
PDFファイルにあれこれ手を加えてみる
最後に、先ほど作成した ModelクラスでPDFに対してあれこれ編集して出力を見てみます。
emptyPJ/module/Mymodule/src/Mymodule/Model/PdfManager.phpを次のように編集
// ~~省略~~
// 定数・変数の定義領域に下記を追加定義
// ページ内で行を順次処理する場合: テキスト描画時に行処理を始めるカーソル位置
const ROW_START_X = 20;
const ROW_START_Y = 28;
const LINE_HEIGHT = 6.5;
// ページ内で行を順次処理する場合: 現在のカーソル位置
protected $curX = 0;
protected $curY = 0;
// ~~省略~~
// create()メソッド部分を変更するとともに処理の追加を施す
/**
* 出力するPDFデータを作成する
*/
public function create()
{
if (!empty($this->templatePath)) {
/*
* 背景PDFセット
*/
if (file_exists($this->templatePath)) {
$this->pdfObj->setSourceFile($this->templatePath);
} else {
throw new \Exception('The specified PDF template file does not exist.');
}
// 背景に既存のPDFを使うのでマージンを無効にする
$this->pdfObj->SetAutoPageBreak(false, 0);
$this->pdfObj->SetMargins(0, 0, 0);
// 上記と同様の理由で、ヘッダーやフッターを差し込まない
$this->pdfObj->setPrintHeader(false);
$this->pdfObj->setPrintFooter(false);
}
// テキスト色を赤にセット
$this->pdfObj->SetTextColor(255, 0, 0);
// コンテンツを作成
$this->makeContents();
}
/**
* PDFに出力するコンテンツを作成する
*/
protected function makeContents()
{
// ページごとにコンテンツを生成
$this->add1stPage();
$this->add2ndPage();
}
/** 1ページめのコンテンツを生成 */
public function add1stPage()
{
$this->pdfObj->AddPage();
// create()で setSourceFile()が行われていればページ背景をセット
if (!is_null($this->pdfObj->currentFilename)) {
//背景PDFページ設定
$this->pdfObj->useTemplate($this->pdfObj->importPage(1));
}
// 表のセルを埋めるように表示
$this->curY = self::ROW_START_Y;
$this->rowProcessing("報告書A", "鈴木", "", "", false, false);
$this->rowProcessing("報告書B", "塚本", "", "", false, false);
$this->rowProcessing("報告書C", "藤田", "", "", false, false);
$this->rowProcessing("報告書D", "浜野", "", "", false, false);
// テキトーな文言をテキトーな位置に表示
$this->pdfObj->Text(50, 100, "ポケモンゲットだぜ!!");
}
/** 2ページめのコンテンツを生成 */
public function add2ndPage()
{
$this->pdfObj->AddPage();
// create()で setSourceFile()が行われていればページ背景をセット
if (!is_null($this->pdfObj->currentFilename)) {
//背景PDFページ設定
$this->pdfObj->useTemplate($this->pdfObj->importPage(2));
}
// テキトーな文言をテキトーな位置に表示
$this->pdfObj->Text(50, 100, "スライムナイトは仲間になりたそうにこちらを見ている");
// 表のセルを埋めるように表示
$this->curY = self::ROW_START_Y;
$this->rowProcessing("鬼平犯科帳", "池波", "", "", false, false);
$this->rowProcessing("修造語録", "松岡", "", "", false, false);
}
/**
* 表に対して1行ずつのテキスト表示処理
*/
public function rowProcessing($docName, $assign, $expTime, $startDate, $stMake = false, $stSend = false)
{
$this->curX = self::ROW_START_X;
$this->curY += self::LINE_HEIGHT;
// 資料名
$this->pdfObj->Text($this->curX, $this->curY, $docName);
// 担当
$this->curX += 60;
$this->pdfObj->Text($this->curX, $this->curY, $assign);
// 期限
// ~~処理~~
// 着手日
// ~~処理~~
// 作業完了
// ~~処理~~
// 提出済み
// ~~処理~~
}
以上によって、printPdfActionへアクセスした時に得られるPDFの中身は次のようになりました。
※TCPDFによって追加されたテキストの色を赤にしたのは、わかりやすさのためです。
テンプレートPDFに当て込む書式は様々あると思いますが、
PdfManagerクラスの create()メソッド以降の処理を適宜書き換えることで、
好きなようにPDFに文字を書き込んでいくことが出来るはずです。
今回の記事は以上です。




コメント
コメントを投稿