如何用Apache PDFBox将PDF页眉页脚转为静态内容并合并文件
解决Apache PDFBox中动态页眉页脚转静态并合并PDF的问题
为什么你的扁平化代码无效
你当前的代码仅处理AcroForm表单域,但动态页眉页脚(尤其是阅读器自动生成的页码)不属于表单域范畴——它们通常是PDF页面内容流中的动态变量(如PageNumber/PageCount)、阅读器内置分页逻辑,或是通过JavaScript动态渲染的内容,和AcroForm完全无关,因此pdAcroForm.flatten()对这类内容没有作用。
核心解决思路
要将动态页眉页脚转为静态内容,本质是把阅读器渲染时才会计算出的实际文本,提前以静态文本形式写入PDF页面,同时保留原有的字体、位置和样式。具体分为两步:
- 解析单页PDF,提取动态页眉页脚的实际文本值、字体、位置等属性;
- 在页面上绘制静态文本覆盖原动态内容,生成静态PDF后再合并,最后添加新页码。
具体实现步骤与代码示例
1. 获取动态页码的实际文本值
针对常见的动态页码逻辑,直接计算当前页的实际页码文本:
private static String getStaticPageText(PDPage page, PDDocument doc) throws IOException { int currentPageNum = doc.getPages().indexOf(page) + 1; int totalPageCount = doc.getNumberOfPages(); // 按你的PDF原有格式调整模板,比如原页眉是"Page X of Y"就对应修改 return String.format("第%d页,共%d页", currentPageNum, totalPageCount); }
2. 提取原动态文本的样式与位置
解析页面资源和内容流,获取原文本的字体、字号、坐标等参数(示例为简化处理,实际需根据PDF格式调整):
private static Map<String, Object> getTextStyle(PDPage page) throws IOException { Map<String, Object> styleMap = new HashMap<>(); PDResources resources = page.getResources(); // 从页面资源中获取原字体(需根据你的PDF实际字体名称调整) PDType1Font font = (PDType1Font) resources.getFont(COSName.getPDFName("F1")); styleMap.put("font", font); styleMap.put("fontSize", 12f); // 原页眉页脚的坐标(PDF坐标系原点在左下角,需根据实际位置调整) styleMap.put("x", 100f); styleMap.put("y", 50f); styleMap.put("color", Color.BLACK); return styleMap; }
3. 将动态内容转为静态文本
使用PDPageContentStream在原有页面内容之上绘制静态文本,覆盖动态内容:
private static void convertDynamicToStatic(PDDocument doc) throws IOException { for (PDPage page : doc.getPages()) { String staticText = getStaticPageText(page, doc); Map<String, Object> style = getTextStyle(page); PDPageContentStream contentStream = new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.APPEND, true, true); PDType1Font font = (PDType1Font) style.get("font"); float fontSize = (float) style.get("fontSize"); float x = (float) style.get("x"); float y = (float) style.get("y"); Color color = (Color) style.get("color"); contentStream.setNonStrokingColor(color); contentStream.beginText(); contentStream.setFont(font, fontSize); contentStream.newLineAtOffset(x, y); contentStream.showText(staticText); contentStream.endText(); contentStream.close(); } }
4. 合并静态PDF并添加新页码
先将单个PDF转为静态,再合并,最后添加左侧页脚的新页码:
public static void mergeAndAddNewNumbers(List<String> pdfPaths, String outputPath) throws IOException { try (PDDocument mergedDoc = new PDDocument()) { // 处理每个PDF为静态后合并 for (String path : pdfPaths) { try (PDDocument singleDoc = PDDocument.load(new File(path))) { convertDynamicToStatic(singleDoc); for (PDPage page : singleDoc.getPages()) { mergedDoc.addPage(page); } } } // 添加左侧页脚的新页码 int totalMergedPages = mergedDoc.getNumberOfPages(); for (int i = 0; i < totalMergedPages; i++) { PDPage page = mergedDoc.getPage(i); try (PDPageContentStream contentStream = new PDPageContentStream(mergedDoc, page, PDPageContentStream.AppendMode.APPEND, true, true)) { contentStream.setNonStrokingColor(Color.GRAY); contentStream.beginText(); contentStream.setFont(PDType1Font.HELVETICA, 10); // 左侧页脚坐标,按需调整 contentStream.newLineAtOffset(50, 30); contentStream.showText(String.format("第%d页,共%d页", i+1, totalMergedPages)); contentStream.endText(); } } mergedDoc.save(new File(outputPath)); } }
注意事项
- 实际解析内容流时,可能需要用PDFBox的
PDFStreamEngine遍历指令,处理更复杂的字体引用、文本矩阵变换; - 若动态页码由JavaScript生成,需使用PDFBox的
JavaScriptEngine执行脚本获取实际值; - 测试时需根据你的PDF格式调整坐标、字体名称等参数,确保静态文本与原动态内容完全重合。
内容的提问来源于stack exchange,提问作者DougFairhurstAssociatescom
相关产品推荐
相关产品推荐

