PAYATHON 2026

如何使用 PHP 和 FPDI 将 PDF 上下拆成两页并去除多余留白?

数据小许

把两个输出页面的高度分别设为对应区域的高度,再按分割位置将下半部分的模板向上移动,就能去掉整页画布造成的留白,无需再用白色矩形遮盖内容。

为什么当前代码会留下半页空白?

两次 AddPage() 都使用了原页面的完整高度:

$pdf->AddPage($size['orientation'], [$size['width'], $size['height']]);

后面的 Rect() 只绘制白色矩形,不会改变 PDF 页面的尺寸。因此,即使只显示半页内容,输出的页面仍是完整大小。

严格上下等分时,假设原页面宽度为 W、高度为 H,输出的两页都应为 W × (H / 2)。如果按固定位置 splitY 分割,上页高度应为 splitY,下页高度应为 H - splitY。

如何修改拆分代码?

下面的写法适用于以 FPDF 为后端的 setasign\Fpdi\Fpdi。在现有 keepInvoice() 方法中,保留文件读取、setSourceFile() 和响应部分,将原来的循环替换为以下代码:

$pdf->SetMargins(0, 0, 0);
$pdf->SetAutoPageBreak(false, 0);

for ($page = 1; $page <= $pageCount; $page++) {
    $tplId = $pdf->importPage($page);
    $size = $pdf->getTemplateSize($tplId);

    $width = $size['width'];
    $height = $size['height'];

    // Split the original page into two equal parts.
    $splitY = $height / 2;
    $topHeight = $splitY;
    $bottomHeight = $height - $splitY;

    // Choose the orientation for each new page's dimensions.
    $topOrientation = $width > $topHeight ? 'L' : 'P';
    $bottomOrientation = $width > $bottomHeight ? 'L' : 'P';

    // Top part: keep the template at its original position.
    $pdf->AddPage($topOrientation, [$width, $topHeight]);
    $pdf->useTemplate($tplId, 0, 0, $width, $height);

    // Bottom part: move the template upward by the split distance.
    $pdf->AddPage($bottomOrientation, [$width, $bottomHeight]);
    $pdf->useTemplate($tplId, 0, -$splitY, $width, $height);
}

新页面的边界会限制可见区域:

  • 上页从原页面顶部开始显示,高度为 topHeight。
  • 下页将原页面向上移动 splitY,使分割线对齐新页面顶部,高度为 bottomHeight。

两次 useTemplate() 都使用原模板的完整宽高,以保持内容比例。不要把模板高度也改成半页高度,否则会改变模板缩放,无法得到预期的裁切结果。

原来的 SetFillColor() 和两次 Rect() 可以删除。

如果客户信息和发票的分界线在 123 处怎么办?

如果要沿业务内容的分界线拆分,而不是严格等分,将示例中的:

$splitY = $height / 2;

替换为:

$splitY = 123;

if ($splitY <= 0 || $splitY >= $height) {
    throw new \InvalidArgumentException(
        'The split position must be inside the page.'
    );
}

其余计算保持不变。此时上页高度为 123,下页高度为 $height - 123。只有原页面高度恰好为 246 时,两页才一样高。

123 使用 PDF 实例的当前单位。默认构造的 FPDF 后端实例通常使用毫米;如果初始化时更改了单位,需要按实际单位设置分割位置。尺寸或布局不同的页面也未必适合共用同一个固定值。

还需要注意什么?

拆分后的宽高比例可能与原页面不同,因此代码根据每个新页面的尺寸重新选择 P 或 L,不要直接沿用原页面的 $size['orientation']。

这种方法能去掉输出页面尺寸过大造成的空白。如果原 PDF 的客户信息或发票区域内部本来就有白边,按半页拆分后仍会保留,需要进一步确定实际内容边界才能裁掉。

示例只替换页面拆分逻辑。响应部分的 Output() 参数顺序应与实际使用的 PDF 后端一致:FPDF 后端返回字符串可使用 $pdf->Output('S');如果使用的是 TCPDF 后端,则需要遵循对应的 Output() 签名。

备注:内容仅供参考。