MikeHaertl/phpwkhtmltopdf

一个简洁的 PHP 包装器,围绕 wkhtmltopdf,拥有易于使用和干净的 OOP 接口

2.5.0 2021-03-01 19:41 UTC

README

GitHub Tests Packagist Version Packagist Downloads GitHub license Packagist PHP Version Support

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 的命令行语法。

除了上述的 binarycommandOptionstmpDirignoreWarnings 选项外,Image 类还有一个 type 选项

  • type:图像类型。默认为 png。您还可以使用 jpgbmp

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头。让浏览器显示进度条很有用。

要修复此问题,有两个选项

  1. 在您的Web服务器中将下载URL排除在压缩之外。例如,如果您的脚本名为pdf.php,那么对于Apache中的mod_deflate,您可以在配置中尝试添加以下内容

    SetEnvIfNoCase REQUEST_URI ^/pdf.php$ no-gzip dont-vary
    

    对于Nginx,有类似解决方案可以禁用特定位置的gzip

  2. 在发送文件时抑制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中使用cmin,这对于打印样式来说更合适。

<?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。