如何从 HTML 准备好在 PHP 中进行双面打印的模板批量生成 PDF?

How to bulk generate PDF from HTML templates ready for double-sided printing in PHP?

我为此苦恼了一段时间,感到无助。 Prestashop 使用 tcpdf to generate invoices and delivery slips from HTML templates filled using Smarty. We are working on updating the invoice design and found tcpdf to be lacking in CSS support. After some research we settled for wkhtmltopdf 作为将 HTML/CSS 模板转换为 PDF 的正确工具。

问题

该商店具有将多张发票导出为单个 PDF 的功能。使用 TCPDF 我能够通过在生成文件之前具有奇数页的每张发票后插入空白页来使批处理文件准备好进行双面打印。但是现在我们切换到 wkhtmltopdf 我无法获得相同的结果。

关键问题是虽然 wkhtmltopdf 允许使用多个 HTML 模板,但似乎没有可靠的方法 determine the number of pages 它们每个都将在文件之前生成。页眉和页脚模板可以接收发票最终的页数,但它们与主要内容是分开的,因此我无法相应地插入分页符。

我也尝试计算 height of the content / PDF page height,但是一旦我开始导出多个模板(使用单个模板就可以正常工作),就会有 various issues。这种方法也不是很好,因为在内容本身中插入空白页会导致页脚出现在新页面上,这不是我想要的。

我最好的尝试

我认为可以解决这些问题的唯一方法效率很低。每次将模板添加到批次时,我都可以使用我在 SO 上找到的 wrapper for wkhtmltopdf, get the temporary file name, determine how many pages it has using pdfinfo and add a blank HTML template to the main instance accordingly. Here's a draft of a function to get the number of pages of the last template added (from a class that extends the wrapper, based on some other pdfinfo questions 的单独实例预先生成它:

/**
* Return the number of pages in the last invoice template added
* $complete === true => return the length of the entire document
*/
public function getNumPages($complete = false)
{
    if (!$complete) {
        // Generate PDF of last template added

        $tmpPdf = new WKPdf($this->_options);
        $tmpPdf->addPage($this->content, Array(
            'footer-html' => $this->footer
        ));

        /**
           The createPdf method is protected so I need to get 
           the content as string here to force the wrapper to 
           call wkhtmltopdf.
        */
        $tmpPdf->toString();
        $document = $tmpPdf->getPdfFilename();
    } else {

        // Generate entire batch
        $this->createPdf();
        $document = $this->getPdfFilename();
    }

    // Use pdfinfo to get the PDF page count
    $cmd = 'pdfinfo';
    exec("$cmd \"$document\"", $output);

    $pagecount = 0;
    foreach($output as $op)
    {
        // Extract the number
        if(preg_match("/Pages:\s*(\d+)/i", $op, $matches) === 1)
        {
            $pagecount = intval($matches[1]);
            break;
        }
    }

    return $pagecount;
}

这非常低效 - 生成一批 25 张发票大约需要 80 秒,因为我必须调用 wkhtmltopdf 25 次来创建临时 PDF 文件,以便我可以调用 pdfinfo 25 次以获得各自的长度并在必要时插入空白页,然后生成最终文档。

TCPDF 的优势在于它可以即时为您提供页数,类似的功能大约需要 5 秒来生成包含 25 张发票的批处理文件。

有人对如何加快速度有任何想法吗?或者一个更好的主意完全做到这一点。我已经考虑过包括 dompdf 在内的这一代的各种工具,但 wkhtmltopdf 是最强大的。批次生成实际上只由商店管理员从后台使用,所以也许他们可以耐心等待。但还是。

不幸的是wkhtmltopdf是库,它是用C语言编写的,我们不能像PHP库那样动态添加一页。

Citate from your comment: Due to number of items ordered or ammount of customer data each invoice can be anywhere from 1 to 3 pages long.

因此我们无法预先计算页数并将其写入数据库。

我认为你只有一种可能性/解决方案:你必须在每张发票后面写一个空白页,并且在生成整个 PDF 后你必须使用免费 PHP 类库 FPDI。结合 FPDI 甚至可以编辑 PDF 文档。

通过 PDF 编辑,您可以删除所有不需要的以奇数页码开头的空白页(如 35 等)。在 FPDI 中,您可以检测页码。它比您现在使用的解决方案快得多。

您可以使用 FPFI 在 内容长度 上检测到的空白(或空白)页面如下:

<?php
require('fpdf.php');
require_once('setasign/Fpdi/autoload.php');

class Pdf extends \setasign\Fpdi\Fpdi
{
    private $pdfReader;
    function Header()
    {
        if(is_null($this->pdfReader))
        {
            $readerId = $this->getPdfReaderId('blank-pages.pdf');
            $this->pdfReader = $this->getPdfReader($readerId);
        }

        $page_fpdi = $this->pdfReader->getPage($this->PageNo());
        $this->content = $page_fpdi->getContentStream();

        $this->Cell(0, 15, 'page content length: '.strlen($this->content));
    }

    protected function _putimages(){}
}

$pdf = new Pdf();
$pdf->SetFont('Arial', '', 12);
$pdf->AddPage(); //page content length: 70 // page with 'Hello World!' string
$pdf->AddPage(); //page content length: 30 // empty page
$pdf->AddPage(); //page content length: 30 // empty page
$pdf->Output();
?>

我的 blank-pages.pdf 我使用 FPDF 生成了以下代码:

<?php
require('fpdf.php');

$pdf = new FPDF();
$pdf->AddPage();
$pdf->SetFont('Arial','B',16);
$pdf->Cell(40,10,'Hello World!');
$pdf->AddPage();
$pdf->AddPage();
$pdf->Output();
?>