Laravel HTML转换PDF

首先,HTML转换成PDF使用一个叫wkhtmltopdf的工具,地址为:http://wkhtmltopdf.org/index.html,安装之后会提供一个命令行工具,这个命令行工具可配置的参数非常多:

wkhtmltopdf --page-width 100 --page-height 100 http://blog.ifeeline.com i.pdf
Loading pages (1/6)
Counting pages (2/6)
Resolving links (4/6)
Loading headers and footers (5/6)
Printing pages (6/6)
Done

在Linux下安装,可能会缺少中文字体,最简单的方式是在Windows下拷贝字体上传到Linux。Windows下字体位置:控制面板\所有控制面板项\字体,拷贝:
window_fonts
然后上传到Linux:

mkdir zh_cn
cd zh_cn
#安装字体
fc-cache -fv
#查看字体是否安装
fc-list | grep simsun

为了可以在PHP中使用这个命令行工具,有一个PHP包对其进行了封装,地址:https://github.com/KnpLabs/snappy:

require __DIR__ . '/vendor/autoload.php';

use Knp\Snappy\Pdf;

$snappy = new Pdf('/usr/local/bin/wkhtmltopdf');

// or you can do it in two steps
$snappy = new Pdf();
$snappy->setBinary('/usr/local/bin/wkhtmltopdf');

// Display the resulting pdf in the browser
// by setting the Content-type header to pdf
$snappy = new Pdf('/usr/local/bin/wkhtmltopdf');
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="file.pdf"');
echo $snappy->getOutput('http://www.github.com');

// Merge multiple urls into one pdf
// by sending an array of urls to getOutput()
$snappy = new Pdf('/usr/local/bin/wkhtmltopdf');
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="file.pdf"');
echo $snappy->getOutput(array('http://www.github.com','http://www.knplabs.com','http://www.php.net'));

// .. or simply save the PDF to a file
$snappy = new Pdf('/usr/local/bin/wkhtmltopdf');
$snappy->generateFromHtml('<h1>Bill</h1><p>You owe me money, dude.</p>', '/tmp/bill-123.pdf');

// Pass options to snappy
// Type wkhtmltopdf -H to see the list of options
$snappy = new Pdf('/usr/local/bin/wkhtmltopdf');
$snappy->setOption('disable-javascript', true);
$snappy->setOption('no-background', true);
$snappy->setOption('allow', array('/path1', '/path2'));
$snappy->setOption('cookie', array('key' => 'value', 'key2' => 'value2'));
$snappy->setOption('cover', 'pathToCover.html');
// .. or pass a cover as html
$snappy->setOption('cover', '<h1>Bill cover</h1>');
$snappy->setOption('toc', true);
$snappy->setOption('cache-dir', '/path/to/cache/dir');

从例子可以看到,这个snappy工具包,主要实现了把wkhtmltopdf参数传递wkhtmltopdf命令行工具,然后调用命令行工具产生PDF而已。

每次调用这个工具都要设置一版数据显然很麻烦,所以为了在Laravel框架中有效的使用,就需要把这个工具包继续做一次封装基础到框架,可以使用laravel-snappy工具,地址:https://github.com/barryvdh/laravel-snappy。

Laravel集成第三方工具,基本套路是:
1 添加配置文件
2 添加ServiceProvider
3 添加Facade
ServiceProvider一般会读取配置文件中的配置(如果需要配置),然后把实例对象注入Laravel容器,接着就可以直接从容器中获取实例对象进行操作,也可以直接使用Facade,它实际也是从容器中获取实例对象。

以下是操作过程:

composer require barryvdh/laravel-snappy

#添加ServiceProvider
vi app/config/app.php
Barryvdh\Snappy\ServiceProvider::class,

#添加Facade
'PDF' => Barryvdh\Snappy\Facades\SnappyPdf::class,
'Image' => Barryvdh\Snappy\Facades\SnappyImage::class,

#添加配置(可以直接拷贝)
php artisan vendor:publish

#使用
$snappy = App::make('snappy.pdf');
//To file
$snappy->generateFromHtml('<h1>Bill</h1><p>You owe me money, dude.</p>', '/tmp/bill-123.pdf');
$snappy->generate('http://www.github.com', '/tmp/github.pdf'));
//Or output:
return new Response(
    $snappy->getOutputFromHtml($html),
    200,
    array(
        'Content-Type'          => 'application/pdf',
        'Content-Disposition'   => 'attachment; filename="file.pdf"'
    )
);

$pdf = App::make('snappy.pdf.wrapper');
$pdf->loadHTML('<h1>Test</h1>');
return $pdf->inline();

// Facade操作
$pdf = PDF::loadView('pdf.invoice', $data);
return $pdf->download('invoice.pdf');

return PDF::loadFile('http://www.github.com')->inline('github.pdf');

PDF::loadHTML($html)->setPaper('a4')->setOrientation('landscape')->setOption('margin-bottom', 0)->save('myfile.pdf')

Facade的操作方式还是很便利的。比如PDF::loadFile(‘http://www.github.com’),这样直接抓取一个网页,可以调用inline或stream方法直接输出到浏览器(实际是output方法的封装,output方法返回pdf的字符流,而output又是调用snappy提供的方法),也可以调用download方法下载,调用save方法保存。

这里需要特别指出的是,可以使用loadView方法直接load一个Blade模板文件,第二参数是模板使用到的变量,这对于动态产生PDF输出提供了一个绝佳实现。

最后,看下配置文件:

<?php
return array(
    'pdf' => array(
        'enabled' => true,
        'binary'  => '/usr/local/bin/wkhtmltopdf',
        'timeout' => false,
        'options' => array(),
        'env'     => array(),
    ),
    'image' => array(
        'enabled' => true,
        'binary'  => '/usr/local/bin/wkhtmltoimage',
        'timeout' => false,
        'options' => array(),
        'env'     => array(),
    ),
);

这里的enabled表示对应命令是否启用,binary表示wkhtmltopdf命令的位置,timeout表示是否设置超时,options就是要传递到wkhtmltopdf命令行的参数,env是执行命令行的环境变量。options的可用值,就是wkhtmltopdf命令的可用值,这个可以查看Knp\Snappy\Pdf的configure方法得到一个列表:

 protected function configure()
    {
        $this->addOptions(array(
            'ignore-load-errors'           => null, // old v0.9
            'lowquality'                   => true,
            'collate'                      => null,
            'no-collate'                   => null,
            'cookie-jar'                   => null,
            'copies'                       => null,
            'dpi'                          => null,
            'extended-help'                => null,
            'grayscale'                    => null,
            'help'                         => null,
            'htmldoc'                      => null,
            'image-dpi'                    => null,
            'image-quality'                => null,
            'manpage'                      => null,
            'margin-bottom'                => null,
            'margin-left'                  => null,
            'margin-right'                 => null,
            'margin-top'                   => null,
            'orientation'                  => null,
            'output-format'                => null,
            'page-height'                  => null,
            'page-size'                    => null,
            'page-width'                   => null,
            'no-pdf-compression'           => null,
            'quiet'                        => null,
            'read-args-from-stdin'         => null,
            'title'                        => null,
            'use-xserver'                  => null,
            'version'                      => null,
            'dump-default-toc-xsl'         => null,
            'dump-outline'                 => null,
            'outline'                      => null,
            'no-outline'                   => null,
            'outline-depth'                => null,
            'allow'                        => null,
            'background'                   => null,
            'no-background'                => null,
            'checkbox-checked-svg'         => null,
            'checkbox-svg'                 => null,
            'cookie'                       => null,
            'custom-header'                => null,
            'custom-header-propagation'    => null,
            'no-custom-header-propagation' => null,
            'debug-javascript'             => null,
            'no-debug-javascript'          => null,
            'default-header'               => null,
            'encoding'                     => null,
            'disable-external-links'       => null,
            'enable-external-links'        => null,
            'disable-forms'                => null,
            'enable-forms'                 => null,
            'images'                       => null,
            'no-images'                    => null,
            'disable-internal-links'       => null,
            'enable-internal-links'        => null,
            'disable-javascript'           => null,
            'enable-javascript'            => null,
            'javascript-delay'             => null,
            'load-error-handling'          => null,
            'load-media-error-handling'    => null,
            'disable-local-file-access'    => null,
            'enable-local-file-access'     => null,
            'minimum-font-size'            => null,
            'exclude-from-outline'         => null,
            'include-in-outline'           => null,
            'page-offset'                  => null,
            'password'                     => null,
            'disable-plugins'              => null,
            'enable-plugins'               => null,
            'post'                         => null,
            'post-file'                    => null,
            'print-media-type'             => null,
            'no-print-media-type'          => null,
            'proxy'                        => null,
            'radiobutton-checked-svg'      => null,
            'radiobutton-svg'              => null,
            'run-script'                   => null,
            'disable-smart-shrinking'      => null,
            'enable-smart-shrinking'       => null,
            'stop-slow-scripts'            => null,
            'no-stop-slow-scripts'         => null,
            'disable-toc-back-links'       => null,
            'enable-toc-back-links'        => null,
            'user-style-sheet'             => null,
            'username'                     => null,
            'window-status'                => null,
            'zoom'                         => null,
            'footer-center'                => null,
            'footer-font-name'             => null,
            'footer-font-size'             => null,
            'footer-html'                  => null,
            'footer-left'                  => null,
            'footer-line'                  => null,
            'no-footer-line'               => null,
            'footer-right'                 => null,
            'footer-spacing'               => null,
            'header-center'                => null,
            'header-font-name'             => null,
            'header-font-size'             => null,
            'header-html'                  => null,
            'header-left'                  => null,
            'header-line'                  => null,
            'no-header-line'               => null,
            'header-right'                 => null,
            'header-spacing'               => null,
            'replace'                      => null,
            'disable-dotted-lines'         => null,
            'cover'                        => null,
            'toc'                          => null,
            'toc-depth'                    => null,
            'toc-font-name'                => null,
            'toc-l1-font-size'             => null,
            'toc-header-text'              => null,
            'toc-header-font-name'         => null,
            'toc-header-font-size'         => null,
            'toc-level-indentation'        => null,
            'disable-toc-links'            => null,
            'toc-text-size-shrink'         => null,
            'xsl-style-sheet'              => null,
            'viewport-size'                => null,
            'redirect-delay'               => null, // old v0.9
            'cache-dir'                    => null,
        ));
    }

大概浏览一下,可以传递cookie,cookie-jar,这个在抓取一个需要验证登录的网络时就非常有意义。可以设置外边距,可以设置页大小,也可以指定页的宽度和高度,可以传递用户名密码,可以POST数据等等,实际就是一个脚本解析器和html渲染工工具(可以看成一个浏览器),只有像浏览器一样渲染html和执行js,才能获取到渲染结果(我们看到的模样),然后才能按照这个模样转换成PDF(或者图片)。

关于打印尺寸,参考:http://doc.qt.io/qt-4.8/qprinter.html#PaperSize-enum

————————————————————————————-
以上内容是PHP中调用命令行工具的封装。也可以直接使用对应的PHP扩展,地址:https://github.com/mreiferson/php-wkhtmltox,例子:

foreach (range(1, 4) as $i) {
    wkhtmltox_convert('pdf', 
        array('out' => '/tmp/test'.$i.'.pdf', 'imageQuality' => '95'), // global settings
        array(
            array('page' => 'http://www.visionaryrenesis.com/'),
            array('page' => 'http://www.google.com/')
            )); // object settings
}

第二参数是一个配置数组,比如imageQuality,对应的命令行参数是image-quality,详细的配置参考:http://wkhtmltopdf.org/libwkhtmltox/pagesettings.html, 可以把配置写入文件,然后直接代入。

#安装wkhtmltox
wget http://download.gna.org/wkhtmltopdf/0.12/0.12.2.1/wkhtmltox-0.12.2.1_linux-centos7-amd64.rpm

yum install xorg-x11-fonts-75dpi.noarch
yum install xorg-x11-fonts-Type1

rpm -i wkhtmltox-0.12.2.1_linux-centos7-amd64.rpm

rpm -ql wkhtmltox-0.12.2.1-1.x86_64
/usr/local/bin/wkhtmltoimage
/usr/local/bin/wkhtmltopdf
/usr/local/include/wkhtmltox/dllbegin.inc
/usr/local/include/wkhtmltox/dllend.inc
/usr/local/include/wkhtmltox/image.h
/usr/local/include/wkhtmltox/pdf.h
/usr/local/lib/libwkhtmltox.so
/usr/local/lib/libwkhtmltox.so.0
/usr/local/lib/libwkhtmltox.so.0.12
/usr/local/lib/libwkhtmltox.so.0.12.2
/usr/local/share/man/man1/wkhtmltoimage.1.gz
/usr/local/share/man/man1/wkhtmltopdf.1.gz

##安装PHP扩展(https://github.com/mreiferson/php-wkhtmltox)
cd phpwkhtmltox
phpize
./configure –with-php-config=/usr/local/php/bin/php-config #此处按照各自系统php安装路径不同而定
make && make install

通过简单设置,也可以直接输出PDF文档。
————————————————————————————-