如何使用PDFBox将多个PDF模板块合并至同一页面?
用PDFBox实现PDF页面内容叠加(同一页面)
当然可以实现,PDFMergerUtility是用来将整页追加为新页面的工具,要实现同一页面内的内容叠加,需要借助PDPageContentStream将行模板的页面内容绘制到主模板的目标页面上,同时还要处理表单字段的合并(避免字段名冲突)。以下是具体实现方案:
核心思路
- 加载并分别处理两个模板的表单字段
- 将行模板的页面内容绘制到主页面的指定坐标,通过循环调整坐标实现重复叠加
- 合并行模板的表单字段到主文档(需重命名字段避免冲突)
- 保存最终文档
Kotlin 实现代码
import org.apache.pdfbox.pdmodel.PDDocument import org.apache.pdfbox.pdmodel.PDPage import org.apache.pdfbox.pdmodel.PDPageContentStream import org.apache.pdfbox.pdmodel.common.PDRectangle import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm import org.apache.pdfbox.pdmodel.interactive.form.PDField import java.io.File fun main() { val mainTemplatePath = "template.pdf" val lineTemplatePath = "line_template.pdf" val outputPath = "invoice.pdf" // 加载主模板和行模板,use块自动管理资源关闭 PDDocument.load(File(mainTemplatePath)).use { mainDoc -> PDDocument.load(File(lineTemplatePath)).use { lineDoc -> // 处理主模板表单字段(示例:修改字段值) val mainAcroForm = mainDoc.documentCatalog.acroForm mainAcroForm.getField("main_title")?.value = "XX公司发票" mainAcroForm.getField("customer_name")?.value = "张三" // 处理行模板表单字段(示例:修改字段值) val lineAcroForm = lineDoc.documentCatalog.acroForm lineAcroForm.getField("item_name")?.value = "办公用品" lineAcroForm.getField("item_price")?.value = "100.00" // 获取主模板的目标页面(取第一页) val mainPage = mainDoc.pageTree[0] val mainPageSize = mainPage.mediaBox // 获取行模板的页面(取第一页) val linePage = lineDoc.pageTree[0] val linePageSize = linePage.mediaBox // 定义行项的起始坐标与间距(PDF坐标原点在页面左下角) val startX = 60f // 左侧偏移量 var currentY = mainPageSize.height - 200f // 起始Y坐标(从顶部往下偏移) val lineSpacing = linePageSize.height + 15f // 行项之间的垂直间距 // 重复叠加3次行模板内容 repeat(3) { index -> // 创建内容流,将行页面绘制到主页面指定位置 PDPageContentStream(mainDoc, mainPage, PDPageContentStream.AppendMode.APPEND, true, true).use { contentStream -> // drawPage参数:目标页面,绘制位置的X、Y(对应行页面左下角坐标) contentStream.drawPage(linePage, startX, currentY - linePageSize.height) } // 合并行模板的表单字段到主文档,重命名避免冲突 lineAcroForm.fields.forEach { field -> val newFieldName = "${field.partialName}_$index" val clonedField = field.clone() as PDField clonedField.partialName = newFieldName mainAcroForm.fields.add(clonedField) } // 更新下一行的Y坐标,实现垂直排列 currentY -= lineSpacing } // 保存最终文档 mainDoc.save(outputPath) } } }
关键注意事项
- 坐标系统:PDF的坐标原点在页面左下角,
drawPage方法的X、Y参数对应行页面左下角在主页面的位置,需根据实际排版调整 - 字段重命名:行模板的表单字段必须重命名后再添加到主文档的AcroForm,否则会出现字段名冲突,导致表单无法正常显示或编辑
- 资源管理:使用
use块自动关闭文档和内容流,避免资源泄漏 - 内容流模式:采用
AppendMode.APPEND确保行模板内容不会覆盖主页面原有内容
内容的提问来源于stack exchange,提问作者Arek Jaworski
相关产品推荐
相关产品推荐

