PAYATHON 2026

pdf-lib 如何将裁剪后的页面切片堆叠回单页?

数据小陈

结论

setCropBox() 只设置页面的可视区域,不会删除页面内容流中的其他内容。后续代码通过 copyPages() 和 embedPdf() 导入页面时,仍然处理整页,因此 CropBox 隐藏的内容也可能被带入。

要把切片组合到一页上,可以使用 PDFDocument.embedPage(page, boundingBox),为每个切片指定边界,再将嵌入页面绘制到目标页。boundingBox 使用 PDF 页面坐标系,原点在左下角。pdf-lib 的 API 文档列出了 embedPage 的可选 boundingBox 参数。pdf-lib API 文档

按切片边界嵌入页面

下面假设每个切片都是原始页面上的一个矩形,边界格式为 { left, bottom, right, top }。请将 chunkBoxes 替换为裁剪 UI 实际生成的坐标,并确保坐标相对于原始页面,而非缩放或裁剪后的预览图。

import { PDFDocument } from 'pdf-lib';

export async function stackChunks(
  inputBytes: Uint8Array,
  outputBytes: Uint8Array,
  sourcePageIndex: number,
  chunkBoxes: Array<{
    left: number;
    bottom: number;
    right: number;
    top: number;
  }>,
) {
  const sourceDoc = await PDFDocument.load(inputBytes);
  const sourcePage = sourceDoc.getPage(sourcePageIndex);
  const { width: pageWidth, height: pageHeight } = sourcePage.getSize();

  const outputDoc = await PDFDocument.create();
  const outputPage = outputDoc.addPage([pageWidth, pageHeight]);

  const margin = 30;
  const spacing = 10;
  const targetWidth = pageWidth - margin * 2;
  let currentY = pageHeight - margin;

  for (const box of chunkBoxes) {
    const chunkWidth = box.right - box.left;
    const chunkHeight = box.top - box.bottom;

    if (chunkWidth <= 0 || chunkHeight <= 0) {
      throw new Error('切片边界无效');
    }

    const embeddedChunk = await outputDoc.embedPage(sourcePage, box);
    const scale = targetWidth / chunkWidth;
    const drawWidth = chunkWidth * scale;
    const drawHeight = chunkHeight * scale;

    if (currentY - drawHeight < margin) {
      throw new Error('切片总高度超出目标页面;请减小缩放比例或增加目标页');
    }

    outputPage.drawPage(embeddedChunk, {
      x: margin,
      y: currentY - drawHeight,
      width: drawWidth,
      height: drawHeight,
    });

    currentY -= drawHeight + spacing;
  }

  return outputDoc.save();
}

代码会把所有切片绘制到同一张目标页面,因此 chunkBoxes 的顺序决定了它们从页面顶部开始的堆叠顺序。drawPage() 的 x、y 使用左下角坐标;代码通过 currentY - drawHeight 将切片放在当前垂直位置下方。

如果现有的裁剪页保留了对应的 CropBox,可以读取并转换为嵌入边界:

const crop = croppedPage.getCropBox();
const box = {
  left: crop.x,
  bottom: crop.y,
  right: crop.x + crop.width,
  top: crop.y + crop.height,
};

然后将 box 传给 embedPage()。如果裁剪边界只保存在前端预览坐标中,需要先换算为 PDF 页面坐标,并确认切片宽高、页面旋转和坐标原点都与 PDF 页面一致。

关于“删除”被裁掉的内容

使用 boundingBox 可以在嵌入时限制显示范围,适合将切片排版到新页面,但不会从源 PDF 中物理删除矩形外的内容。如果需要彻底清除敏感内容,不能只依赖 CropBox 或嵌入裁剪;应采用能够移除或重建对应页面内容的处理方案,并检查最终文件。

备注:内容仅供参考。