如何使用 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() 签名。
备注:内容仅供参考。
