🖨️
wkhtmltopdfをlinuxにインストールして、PHPで実行するまで
かつてはHTMLからPDFへの変換は「mPDF」を利用していましたが、最近はQT Webkitエンジンを使ってよりブラウザに近い精度で変換する「wkhtmltopdf」を使用しています。本記事ではAmazon LinuxやCent OS等での環境にインストールする方法を記載します。
ダウンロードする
wkhtmltopdfのダウンロードページ よりサーバに応じたRPMパッケージをダウンロードします。
必要なパッケージがわからない場合
このときに必要なパッケージがわからない場合は、下記のようなコマンドで確認します。
OSのバージョンを確認する
cat /etc/redhat-release
ビット数を確認する
arch
インストールする
ダウンロードしたRPMパッケージを適当なフォルダにアップロードして、インストールのコマンドを実行します。
rpm -ivh wkhtmltox-0.12.6-1.centos7.x86_64.rpm
色々と足りないと言われるので、足りないと言われたものをyumでインストールします。(ググるとコンパイルなどの記述が出てきて沼るのでご注意ください。)
yum install -y libXext libXrender xorg-x11-fonts-75dpi xorg-x11-fonts-Type1
そのままだと日本語環境で文字化けするので、フォントをインストールします。
yum install -y ipa-gothic-fonts ipa-mincho-fonts ipa-pgothic-fonts ipa-pmincho-fonts
足りないものがインストールできたら、再度インストールのコマンドを実行します。
rpm -ivh wkhtmltox-0.12.6-1.centos7.x86_64.rpm
実行する
今回は mikehaertl/phpwkhtmltopdf
でPHP経由で実行します。まずはcomposerでインストールします。
composer require mikehaertl/phpwkhtmltopdf
サンプルに従って実行します。
<?php
require_once 'autoload.php';
use mikehaertl\wkhtmlto\Pdf;
$main = '<!DOCTYPE><html><head></head><body></body></html>'
$pdf = new Pdf([
'encoding' => 'utf-8',
'commandOptions' => array(
'useExec' => true, // エラーメッセージが出てこない場合のための記述
'procEnv' => array(
'LANG' => 'ja_JP.utf-8', // 出力のロケールチェック
),
),
]);
$pdf->addPage($main);
if (!$pdf->send()) {
echo($pdf->getError());
}
実務向け
CSSも使えますが、いくつか制約があります。simplehtmldomと組み合わせて実行します。
- flexが効かないのでfloatで代用する
- メディアクエリーが効かないので、メディアクエリは削除する
- 画像は数が多いと、メモリー不足により読み込みできないので、事前にbase64エンコードする
- 画像は相対パスだとエラーになるので、絶対パスにする(CSSも同様)
<?php
require_once 'autoload.php';
require_once 'simplehtmldom_1_9_1/simple_html_dom.php';
use mikehaertl\wkhtmlto\Pdf;
class html2Pdf
{
/**
* HTMLオブジェクトから要素を削除
*
* @param bool $html HTMLオブジェクト
* @param bool $selector 削除するセレクタ
*/
public function removeNode($html, $selector)
{
$html = str_get_html($html);
foreach ($html->find($selector) as $node){
$node->outertext = '';
}
return $html;
}
/**
* HTMLオブジェクトの画像をbase64に変換
*
* @param bool $html HTMLオブジェクト
* @param bool $selector 画像のセレクタ
*/
public function insertImage($html, $selector)
{
$html = str_get_html($html);
foreach ($html->find($selector) as $node){
$src = $node->src;
$base64 = base64_encode(file_get_contents($src));
$pathinfo = pathinfo($src);
$file_ext = strtolower($pathinfo['extension']);
if ($file_ext == 'jpeg') {
$file_ext = 'jpg';
}
$node->src = 'data: ' . $file_ext . ';base64,' . $base64;
}
return $html;
}
/**
* 親要素を削除する
*
* @param bool $html HTMLオブジェクト
* @param bool $selector 削除するセレクタ
*/
public function unwrap($html, $selector)
{
$html = str_get_html($html);
foreach ($html->find($selector) as $node){
$text = $node->innertext;
$node->outertext = $text;
}
return $html;
}
/**
* DIVで囲む
*
* @param bool $html HTMLオブジェクト
* @param bool $selector 囲むセレクタ
* @param bool $start 開始タグ
* @param bool $end 終了タグ
*/
public function insertDivider($html, $selector, $start, $end)
{
$html = str_get_html($html);
foreach ($html->find($selector) as $node){
$node->outertext = $start.$node->outertext.$end;
}
return $html;
}
/**
* PDFで出力する
*/
public function generate($res)
{
// URLを取得
$base = (empty($_SERVER["HTTPS"]) ? "http://" : "https://") . $_SERVER["HTTP_HOST"];
// HTMLを調整
$html = str_get_html($res);
$title = $html->find('title', 0)->innertext;
$main = $html->find('main', 0)->innertext; // 必要なセレクタのみ取得
$main = $this->removeNode($main, '.only-display'); // 不要なセレクタを削除
$main = str_replace('src="/', 'src="'.$base.'/', $main); // 画像を絶対パスに変更
$main = $this->insertImage($main, 'img'); // 画像をbase64エンコード
// CSSを調整
$printStyle = file_get_contents(__DIR__.'/css/pdf.css'); // 印刷用CSSを読み込み
$printStyle = str_replace('background: url(../', 'background:url('.$base.'/', $printStyle); // 背景画像を絶対パスに変更
$printStyle = preg_replace('/^\@media.*?^\}$/ms', '', $printStyle); // メディアクエリを削除
// HTMLとCSSを統合
$main = "<!doctype><html lang=\"ja\"><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"><style>{$printStyle}</style></head><body>{$main}</body></html>";
// PDFを出力
$pdf = new Pdf([
'encoding' => 'utf-8',
'commandOptions' => array(
'useExec' => true, // エラーメッセージが出てこない場合のための記述
'procEnv' => array(
'LANG' => 'ja_JP.utf-8', // 出力のロケールチェック
),
),
]);
$pdf->addPage($main);
if (!$pdf->send()) {
echo($pdf->getError());
}
}
}
PHPなどでHTMLのPDF化を試みている人口が少なく、偉大な先人たちに感謝します。
Discussion