MikeHaertl / phpwkhtmltopdf
一个简洁的 PHP 包装器,围绕 wkhtmltopdf,拥有易于使用和干净的 OOP 接口
Requires
- php: >=5.0.0
- mikehaertl/php-shellcommand: ^1.5.0
- mikehaertl/php-tmpfile: ^1.2.1
Requires (Dev)
- phpunit/phpunit: >4.0 <9.4
README
PHP WkHtmlToPdf 提供了一个简单且干净的接口,用于使用 wkhtmltopdf 创建 PDF 和图像。**必须安装并使系统中的 wkhtmltopdf
和 - 选项性 - wkhtmltoimage
命令正常工作**。下面部分有详细说明。
对于 Windows 系统,请确保在二进制选项中设置到 wkhtmltopdf.exe 的路径。或者,您可以将 wkhtmltopdf 的 "bin" 目录添加到系统 PATH 变量中,以便在 Windows CMD 中使用 wkhtmltopdf 命令。
安装
通过 composer 安装该包
composer require mikehaertl/phpwkhtmltopdf
请确保,在您的代码库中包含 composer 的 自动加载器。
示例
单页 PDF
<?php use mikehaertl\wkhtmlto\Pdf; // You can pass a filename, a HTML string, an URL or an options array to the constructor $pdf = new Pdf('/path/to/page.html'); // On some systems you may have to set the path to the wkhtmltopdf executable // $pdf->binary = 'C:\...'; if (!$pdf->saveAs('/path/to/page.pdf')) { $error = $pdf->getError(); // ... handle error here }
带有目录和封面页的多页 PDF
<?php use mikehaertl\wkhtmlto\Pdf; $pdf = new Pdf; $pdf->addPage('/path/to/page.html'); $pdf->addPage('<html>....</html>'); $pdf->addPage('http://www.example.com'); // Add a cover (same sources as above are possible) $pdf->addCover('/path/to/mycover.html'); // Add a Table of contents $pdf->addToc(); // Save the PDF if (!$pdf->saveAs('/path/to/report.pdf')) { $error = $pdf->getError(); // ... handle error here } // ... or send to client for inline display if (!$pdf->send()) { $error = $pdf->getError(); // ... handle error here } // ... or send to client as file download if (!$pdf->send('report.pdf')) { $error = $pdf->getError(); // ... handle error here } // ... or you can get the raw pdf as a string $content = $pdf->toString();
创建图像
<?php use mikehaertl\wkhtmlto\Image; // You can pass a filename, a HTML string, an URL or an options array to the constructor $image = new Image('/path/to/page.html'); $image->saveAs('/path/to/page.png'); // ... or send to client for inline display if (!$image->send()) { $error = $image->getError(); // ... handle error here } // ... or send to client as file download if (!$image->send('page.png')) { $error = $image->getError(); // ... handle error here }
设置选项
wkhtmltopdf
命令行接受不同类型的选项
- 全局选项(例如,设置文档的 DPI 或默认页面选项)
- 页面选项(例如,为页面提供自定义 CSS 文件)
- 目录选项(例如,设置目录标题)
请参阅 wkhtmltopdf -H
以获取完整说明。所有选项都作为数组传递,例如
<?php $options = array( 'no-outline', // option without argument 'encoding' => 'UTF-8', // option with argument // Option with 2 arguments 'cookie' => array('name'=>'value'), // Repeatable options with single argument 'run-script' => array( '/path/to/local1.js', '/path/to/local2.js', ), // Repeatable options with 2 arguments 'replace' => array( 'number' => $page++, // Replace '[number]' 'title' => $pageTitle, // Replace '[title]' ), );
可以将选项传递给多个 PDF 方法
<?php $pdf = new Pdf($globalOptions); // Set global PDF options $pdf->setOptions($globalOptions); // Set global PDF options (alternative) $pdf->addPage($page, $pageOptions); // Add page with options $pdf->addCover($page, $pageOptions); // Add cover with options $pdf->addToc($tocOptions); // Add TOC with options
请注意,您也可以在全局 PDF 选项中使用页面选项。如果添加页面时覆盖了它们,
wkhtmltopdf
将应用于所有页面。
对于 wkhtmltoimage
,只有一个选项集
<?php $image = new Image($options); // Set image options $image->setOptions($options); // Set image options (alternative)
包装器选项
包装器本身通过以下特殊选项进行配置,这些选项可以传递给构造函数,作为对象属性设置,或通过 setOptions()
binary
:到wkhtmltopdf
命令的完整路径。默认为wkhtmltopdf
,这假设该命令位于您的 shell 的搜索路径中。commandOptions
:传递给 https://github.com/mikehaertl/php-shellcommand 的选项。tmpDir
:临时目录的路径。默认为 PHP 临时目录。ignoreWarnings
:如果 PDF 文件仍然创建,则是否忽略任何错误。默认为false
。version9
:是否使用较旧版本 wkhtmltopdf 的命令行语法。
除了上述的 binary
、commandOptions
、tmpDir
和 ignoreWarnings
选项外,Image
类还有一个 type
选项
type
:图像类型。默认为png
。您还可以使用jpg
或bmp
。
commandOptions
可以用来为 wkhtmltopdf
设置环境变量。例如,如果您想传递 UTF-8 编码的参数,您可能需要设置 LANG
环境变量。
<?php $pdf = new Pdf(array( 'binary' => '/obscure/path/to/wkhtmltopdf', 'ignoreWarnings' => true, 'commandOptions' => array( 'useExec' => true, // Can help on Windows systems 'procEnv' => array( // Check the output of 'locale -a' on your system to find supported languages 'LANG' => 'en_US.utf-8', ), ), ));
传递字符串
一些选项如 header-html
通常期望一个 URL 或一个文件名。使用我们的库,您也可以传递一个字符串。类将尝试检测参数是一个 URL、一个文件名还是一些 HTML 或 XML 内容。为了使检测更容易,您可以将内容放在 <html>
标签中。
如果这不起作用,您也可以最后传递我们的 File
助手实例
<?php use mikehaertl\tmp\File; $options = [ 'header-html' => new File('Complex content', '.html'), ];
错误处理
send()
、saveAs()
和toString()
在出错时将返回false
。在这种情况下,详细错误信息可通过getError()
获取。
<?php if (!$pdf->send()) { throw new Exception('Could not create PDF: '.$pdf->getError()); } $content = $pdf->toString(); if ($content === false) { throw new Exception('Could not create PDF: '.$pdf->getError()); }
已知问题
在Windows下使用
如果您使用双引号("
)或百分号(%
)作为选项值,它们可能会被转换为空格。在这种情况下,您可以在命令中禁用参数转义。对于Windows,还有两个有趣的proc_open()
选项您可能希望使用。
<?php $pdf = new Pdf(array( 'commandOptions' => array( 'escapeArgs' => false, 'procOptions' => array( // This will bypass the cmd.exe which seems to be recommended on Windows 'bypass_shell' => true, // Also worth a try if you get unexplainable errors 'suppress_errors' => true, ), ), ... ));
但这样一来,您就必须自己注意适当的参数转义。在某些情况下,可能需要在您的参数值周围添加额外的双引号。
我还发现一些选项在Windows上不起作用(使用wkhtmltopdf 0.11 rc2进行测试),如下例中使用的user-style-sheet
选项。
下载问题
关于使用send()
时损坏的PDF或图像的许多报告。这通常是由执行额外压缩的web服务器(Apache、Nginx等)引起的。这将打乱由本库添加的Content-Length
头。让浏览器显示进度条很有用。
要修复此问题,有两个选项
-
在您的Web服务器中将下载URL排除在压缩之外。例如,如果您的脚本名为
pdf.php
,那么对于Apache中的mod_deflate,您可以在配置中尝试添加以下内容SetEnvIfNoCase REQUEST_URI ^/pdf.php$ no-gzip dont-vary
对于Nginx,有类似解决方案可以禁用特定位置的
gzip
。 -
在发送文件时抑制
Content-Length
头(自2.5.0版起可用)<?php $pdf->send('name.pdf', false, array( 'Content-Length' => false, )); $image->send('name.png', false, array( 'Content-Length' => false, ));
wkhtmltopdf的安装
建议您从他们的网站上下载最新的wkhtmltopdf
http://wkhtmltopdf.org/downloads.html
这些版本应该可以即装即用。
如果您无法这样做,您可能会遇到动态链接版本的wkhtmltopdf
问题。例如,在Ubuntu 12.04 LTS上安装wkhtmltopdf包时,您会遇到这种情况。虽然它会工作,但为了使用所有功能,它需要一个通常在无头Web服务器上不可用的X服务器。
因此,我们提供了两种基于Xvfb的解决方案。您可以选择使用
- 内置的Xvfb支持或
- 一个独立的Xvfb服务器。
两者都需要在系统上安装Xvfb包,并且两者也有一些缺点。
内置Xvfb支持
这会将每个对wkhtmltopdf
的调用包裹在xvfb-run中。xvfb-run
将在没有完整X会话的所有开销的情况下在X环境中运行任何给定的命令。这种解决方案的缺点是,对于每个创建的PDF,仍会启动一个新的会话,这将给您的CPU带来相当多的额外负载。因此,此设置仅适用于低频率站点。
要使用内置支持,您必须在commandOptions
中设置enableXvfb
。还有一些选项您可以设置。
<?php $pdf = new Pdf(array( // Explicitly tell wkhtmltopdf that we're using an X environment 'use-xserver', // Enable built in Xvfb support in the command 'commandOptions' => array( 'enableXvfb' => true, // Optional: Set your path to xvfb-run. Default is just 'xvfb-run'. // 'xvfbRunBinary' => '/usr/bin/xvfb-run', // Optional: Set options for xfvb-run. The following defaults are used. // 'xvfbRunOptions' => '--server-args="-screen 0, 1024x768x24"', ), ));
独立Xvfb
最好启动一个Xvfb进程一次,并为您所有的PHP请求重用它(感谢Larry Williamson的原始想法)。这要求您对机器有root权限,因为您必须为此进程添加启动脚本。我们提供了一个Ubuntu的示例init脚本(感谢eusonlito)。您可以将它放入/etc/init.d/xvfb
,并使用update-rc.d xvfb defaults 10
将其添加到启动文件中。
如果您的系统基于systemd,以下配置可能有所帮助(感谢nkm):[链接](https://gist.github.com/nkm/91006178753df6f503c1)。
如果您的Xvfb
进程正在运行,您只需告诉类使用这个X显示进行渲染。这通过一个环境变量来完成。
<?php $pdf = new Pdf(array( 'use-xserver', 'commandOptions' => array( // You can change ':0' to whatever display you pick in your daemon script 'procEnv' => array( 'DISPLAY' => ':0' ), ), ));
完整示例
对我来说,wkhtmltopdf
似乎在智能缩放关闭的情况下能产生最佳结果。但这样我遇到了缩放问题,后来我把所有边距都设置为零,并通过CSS添加了边距后问题就解决了。您也可以在CSS中使用cm
或in
,这对于打印样式来说更合适。
<?php use mikehaertl\wkhtmlto\Pdf; // Create a new Pdf object with some global PDF options $pdf = new Pdf(array( 'no-outline', // Make Chrome not complain 'margin-top' => 0, 'margin-right' => 0, 'margin-bottom' => 0, 'margin-left' => 0, // Default page options 'disable-smart-shrinking', 'user-style-sheet' => '/path/to/pdf.css', )); // Add a page. To override above page defaults, you could add // another $options array as second argument. $pdf->addPage('/path/to/demo.html'); if (!$pdf->send()) { $error = $pdf->getError(); // ... handle error here }
demo.html
<!DOCTYPE html> <html> <head> </head> <body> <div id="print-area"> <div id="header"> This is an example header. </div> <div id="content"> <h1>Demo</h1> <p>This is example content</p> </div> <div id="footer"> This is an example footer. </div> </div> </body> </html>
pdf.css
/* Define page size. Requires print-area adjustment! */ body { margin: 0; padding: 0; width: 21cm; height: 29.7cm; } /* Printable area */ #print-area { position: relative; top: 1cm; left: 1cm; width: 19cm; height: 27.6cm; font-size: 10px; font-family: Arial; } #header { height: 3cm; background: #ccc; } #footer { position: absolute; bottom: 0; width: 100%; height: 3cm; background: #ccc; }
链接
还可以查看我的php-pdftk
包装器[php-pdftk](https://github.com/mikehaertl/php-pdftk),它将pdftk
的全部功能引入PHP。