
在使用 Apache PDFBox 合并 PDF 时,若直接调用 addPage() 添加来自其他文档的页面(如 PDPage 对象),会导致目标 PDF 生成全空白页;正确做法是使用 importPage() 将源页面内容完整复制到当前文档上下文中。
在使用 apache pdfbox 合并 pdf 时,若直接调用 `addpage()` 添加来自其他文档的页面(如 `pdpage` 对象),会导致目标 pdf 生成全空白页;正确做法是使用 `importpage()` 将源页面内容完整复制到当前文档上下文中。
当使用 PDDocument.addPage(PDPage) 方法添加一个从其他 PDDocument 实例获取的页面对象(例如 doccgv.getPage(0))时,PDFBox 并不会自动复制该页面所依赖的资源(如字体、图像、流内容、资源字典等)。由于这些资源仍绑定在原始文档的上下文(COSDocument/ScratchFile)中,而目标文档无权访问,最终渲染时页面内容缺失,仅显示为空白。
✅ 正确做法:使用 PDDocument.importPage(PDPage)
该方法会深度克隆页面及其全部依赖资源,将其安全迁移至当前文档的资源空间中,确保内容可正常渲染。
以下是修复后的核心代码示例(关键修改已高亮):
// 创建最终文档
PDDocument document = new PDDocument();
// 加载固定模板页(CGV)
File cgvFile = new File(context.repertoire_advendio + "conf/CGV.pdf");
PDDocument doccgv = Loader.loadPDF(cgvFile);
PDPage cgvTemplate = doccgv.getPage(0); // 注意:仅获取引用,不立即导入
// 遍历待合并的发票 PDF
File inputDir = new File(context.repertoire_output_docone);
for (String filename : inputDir.list()) {
if (!filename.startsWith("fact_") && !filename.startsWith("agence_fact_")) {
continue;
}
PDDocument sourceDoc = Loader.loadPDF(new File(inputDir, filename));
// 逐页导入(非 addPage!)
for (int i = 0; i < sourceDoc.getNumberOfPages(); i++) {
PDPage sourcePage = sourceDoc.getPage(i);
// ✅ 关键:使用 importPage 复制页面内容
PDPage importedPage = document.importPage(sourcePage);
document.addPage(importedPage);
// ✅ 同样,固定页也必须导入(不能复用原 doccgv 的 page 引用)
PDPage importedCGV = document.importPage(cgvTemplate);
document.addPage(importedCGV);
}
sourceDoc.close(); // 及时关闭源文档,释放资源
}
doccgv.close();
// 保存并关闭
String outputPath = context.repertoire_output_docone
+ "output/docone_" + context.id_legal_entity
+ "_" + context.input_invoice_date.replace("-", "") + ".pdf";
document.save(outputPath);
document.close();⚠️ 重要注意事项:
- importPage() 返回的是一个新创建的、属于当前文档的 PDPage 实例,必须显式调用 document.addPage() 才能加入页面序列;
- 每个需跨文档复用的页面(包括 cgvTemplate)都必须单独 importPage() —— 不可多次 addPage(cgvpage),否则仍为空白;
- 始终在操作完成后调用 sourceDoc.close() 和 doccgv.close(),避免文件句柄泄漏与内存占用过高;
- 若需批量导入大量页面,建议启用 Loader.loadPDF(..., true) 启用内存映射(适用于大文件),或通过 MemoryUsageSetting.setupTempFileOnly() 控制临时存储策略。
总结:addPage() 仅适用于向当前文档添加本就属于该文档的页面(如 new PDPage() 或 document.getPage(i));而跨文档页面复用场景下,importPage() 是唯一可靠且符合 PDFBox 设计语义的解决方案。遵循此原则,即可彻底规避“页面数正确但内容全空”的典型陷阱。
立即学习“Java免费学习笔记(深入)”;









