You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 20:37:05