PDFBox 3.0.1复制含图片PDF页面生成新文档时出现损坏问题
PDFBox 3.0.1拆分含图片PDF生成损坏文件的问题解决
问题描述
需要拆分每页含图片的多页PDF,生成若干不超过指定大小(7485760字节)的PDF。原代码在PDFBox 3.0.0中运行正常,但升级到3.0.1后生成的PDF损坏:仅首页显示图片,后续页面无图,打开时弹出警告:There was an error processing a page. There was a problem reading this document (18)。纯文本PDF无此问题,且确认是用于获取单页大小的ByteArrayOutputStream保存操作导致的,移除该操作则恢复正常。
原问题代码:
long currentSize = 0L; try (PDDocument document = Loader.loadPDF(file)) { List<PDDocument> documentList = new ArrayList<>(); for (int i = 0; i < document.getPages().getCount(); i++) { PDDocument sizeCheck = new PDDocument(); sizeCheck.addPage(document.getPage(i)); ByteArrayOutputStream baos = new ByteArrayOutputStream(); sizeCheck.save(baos); long pageSize = baos.size(); if (currentSize==0L || (currentSize+pageSize) > 7485760L) { PDDocument tempDoc = new PDDocument(); tempDoc.addPage(document.getPage(i)); documentList.add(tempDoc); currentSize=pageSize; } else { documentList.get(documentList.size() - 1).addPage(document.getPage(i)); currentSize+=pageSize; } } for (PDDocument doc : documentList) { File temp = Files.createTempFile(UUID.randomUUID(), ".pdf").toFile(); doc.save(temp); doc.close(); } }
原因分析
PDFBox 3.0.1的内部实现变更导致:直接将原文档的页面添加到临时sizeCheck文档并保存时,会修改原页面关联的图片资源的引用状态(比如资源被标记为已被其他文档占用、或资源的内部指针被修改)。后续将同一页面添加到目标拆分文档时,无法正确读取图片资源,从而导致页面损坏。而3.0.0版本没有这个资源引用的变更逻辑,因此不会出现问题。
解决方案
核心思路是避免临时文档操作影响原页面的资源,可以通过复制页面副本到临时文档的方式实现:
修改后的代码
long currentSize = 0L; try (PDDocument document = Loader.loadPDF(file)) { List<PDDocument> documentList = new ArrayList<>(); for (int i = 0; i < document.getPages().getCount(); i++) { PDPage originalPage = document.getPage(i); // 创建临时文档并导入原页面的副本,而非直接引用原页面 try (PDDocument sizeCheck = new PDDocument()) { PDPage pageCopy = sizeCheck.importPage(originalPage); ByteArrayOutputStream baos = new ByteArrayOutputStream(); sizeCheck.save(baos); long pageSize = baos.size(); if (currentSize == 0L || (currentSize + pageSize) > 7485760L) { PDDocument tempDoc = new PDDocument(); tempDoc.importPage(originalPage); documentList.add(tempDoc); currentSize = pageSize; } else { documentList.get(documentList.size() - 1).importPage(originalPage); currentSize += pageSize; } } // 自动关闭sizeCheck临时文档,释放资源 } for (PDDocument doc : documentList) { File temp = Files.createTempFile(UUID.randomUUID(), ".pdf").toFile(); doc.save(temp); doc.close(); } }
关键修改点
- 使用
PDDocument.importPage()替代直接addPage():importPage()会创建原页面的副本及关联资源的独立副本,临时文档的保存操作不会影响原文档的页面资源。 - 给临时
sizeCheck文档添加try-with-resources声明,确保及时关闭释放资源,避免内存泄漏。 - 目标拆分文档中同样使用
importPage()添加页面,确保每个拆分文档的资源都是独立的,避免跨文档的资源引用冲突。
其他可选方案
- 若对页面大小的估算精度要求不高,可以通过计算页面中图片资源的字节数加上固定的PDF页面结构开销来估算,无需创建临时文档保存,从根源避免问题。
- 临时回退到PDFBox 3.0.0版本,但不建议长期使用,需跟进PDFBox官方是否修复该兼容性问题。
内容的提问来源于stack exchange,提问作者ab572210
相关产品推荐
相关产品推荐

