【過去Blogからの移行記事】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 で組み込む、とした場合は次のようにします。
1. 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.*" // ←追記
},
}
2. 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
3. ひとまず 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;
4. 当該ページへアクセス
「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ディレクトリは予め作成してください。
1. 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');
}
}
2. 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 に作成します。
1. IndexController
元々 emptyPJ の Mymodule では極力シンプルな作りにする目的でルーティングを固定してしまっているので、IndexController で複数のリクエスト先アクションを動的に扱えるよう、routes の type を Literal から Segment に変更します。
- 編集対象: emptyPJ/module/Mymodule/config/module.config.php
2. 新しく追加するアクション名を 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;
}
// ~~省略~~
3. phtml
- 配置先: 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に文字を書き込んでいくことが出来るはずです。
今回の記事は以上です。
Discussion