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

Java中使用PdfBox实现带ID的PDF文本占位符与任意长度填充

嘿,我来帮你搞定PdfBox里的占位符问题!这其实是PDF生成/编辑里很常见的需求,我分几个部分给你讲清楚,代码示例都是可直接运行的那种:

一、基础版:普通文本占位符替换

如果你是从头创建PDF模板,最简单的方式就是先在内容里插入特定标记(比如{{fullName}}、{{email}})作为占位符,之后再替换成实际内容。如果是编辑已有的PDF模板,思路是先遍历PDF的内容流,匹配占位符后替换。

示例1:创建带占位符的PDF模板

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;

import java.io.File;
import java.io.IOException;

public class PdfPlaceholderTemplate {
    public static void main(String[] args) throws IOException {
        try (PDDocument document = new PDDocument()) {
            PDPage page = new PDPage();
            document.addPage(page);

            try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
                // 设置字体和字号
                contentStream.setFont(PDType1Font.HELVETICA_BOLD, 12);
                // 添加带占位符的文本
                contentStream.beginText();
                contentStream.newLineAtOffset(50, 750);
                contentStream.showText("姓名: {{fullName}}");
                contentStream.newLineAtOffset(0, -20);
                contentStream.showText("邮箱: {{email}}");
                contentStream.endText();
            }

            document.save("template.pdf");
        }
    }
}

示例2:替换模板中的占位符

PdfBox没有直接的“一键替换文本”API,所以我们需要先提取PDF内容,替换占位符后重新生成内容流,这里给你一种实用的实现方式:

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.text.PDFTextStripper;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PdfPlaceholderReplacer {
    public static void main(String[] args) throws IOException {
        // 定义占位符和对应的值
        Map<String, String> placeholderMap = new HashMap<>();
        placeholderMap.put("fullName", "张三");
        placeholderMap.put("email", "zhangsan@example.com");

        try (PDDocument document = PDDocument.load(new File("template.pdf"))) {
            PDPage page = document.getPage(0);
            // 提取原有文本
            PDFTextStripper stripper = new PDFTextStripper();
            String pageText = stripper.getText(document);

            // 替换占位符
            Pattern pattern = Pattern.compile("\\{\\{(.*?)\\}\\}");
            Matcher matcher = pattern.matcher(pageText);
            String replacedText = matcher.replaceAll(matchResult -> placeholderMap.getOrDefault(matchResult.group(1), matchResult.group(0)));

            // 清空原有内容,写入新内容
            try (PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.OVERWRITE, true, true)) {
                contentStream.setFont(PDType1Font.HELVETICA_BOLD, 12);
                contentStream.beginText();
                contentStream.newLineAtOffset(50, 750);
                // 拆分多行文本逐行写入
                String[] lines = replacedText.split("\n");
                for (String line : lines) {
                    contentStream.showText(line);
                    contentStream.newLineAtOffset(0, -20);
                }
                contentStream.endText();
            }

            document.save("filled_template.pdf");
        }
    }
}
二、进阶版:带ID的占位符(类似HTML标签)

如果你需要给占位符加类似HTML的ID,方便精准定位替换,可以自定义结构化的占位符格式,比如{{id:"userName"}}或者[#userId#]。核心思路是用正则表达式匹配并提取ID,再根据ID映射对应的值。

示例:带ID的占位符替换

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.text.PDFTextStripper;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PdfIdPlaceholderReplacer {
    public static void main(String[] args) throws IOException {
        // 定义ID和对应的值
        Map<String, String> idValueMap = new HashMap<>();
        idValueMap.put("userName", "李四");
        idValueMap.put("userAge", "30");

        try (PDDocument document = PDDocument.load(new File("id_template.pdf"))) {
            PDPage page = document.getPage(0);
            PDFTextStripper stripper = new PDFTextStripper();
            String pageText = stripper.getText(document);

            // 匹配带ID的占位符:{{id:"xxx"}}
            Pattern pattern = Pattern.compile("\\{\\{id:\"(.*?)\"\\}\\}");
            Matcher matcher = pattern.matcher(pageText);
            String replacedText = matcher.replaceAll(matchResult -> idValueMap.getOrDefault(matchResult.group(1), matchResult.group(0)));

            // 重新写入内容
            try (PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.OVERWRITE, true, true)) {
                contentStream.setFont(PDType1Font.HELVETICA_BOLD, 12);
                contentStream.beginText();
                contentStream.newLineAtOffset(50, 750);
                String[] lines = replacedText.split("\n");
                for (String line : lines) {
                    contentStream.showText(line);
                    contentStream.newLineAtOffset(0, -20);
                }
                contentStream.endText();
            }

            document.save("filled_id_template.pdf");
        }
    }
}

你可以根据自己的习惯修改占位符格式,比如改成[id:userName],只要调整正则表达式就行,灵活性很高。

三、任意长度文本的填充处理

当文本长度不确定时,直接替换可能会导致内容超出页面或者排版混乱。这时候可以用PdfBox的文本处理工具实现自动换行的文本填充,确保内容在预设区域内合理展示。

示例:任意长度文本填充

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
import org.apache.pdfbox.text.PDFTextStripper;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PdfLongTextFiller {
    public static void main(String[] args) throws IOException {
        Map<String, String> idValueMap = new HashMap<>();
        // 模拟超长文本
        idValueMap.put("userDesc", "这是一段非常长的用户描述文本,用来测试PdfBox的任意长度文本填充功能。这段文本会自动换行,不会超出预设的文本区域,确保排版美观。你可以添加更多内容来测试它的换行效果,比如继续写一些无关的内容,看看是否能自动调整行数。");

        try (PDDocument document = PDDocument.load(new File("long_text_template.pdf"))) {
            PDPage page = document.getPage(0);
            PDFTextStripper stripper = new PDFTextStripper();
            String pageText = stripper.getText(document);

            Pattern pattern = Pattern.compile("\\{\\{id:\"userDesc\"\\}\\}");
            Matcher matcher = pattern.matcher(pageText);
            String replacedText = matcher.replaceAll(idValueMap.get("userDesc"));

            try (PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.OVERWRITE, true, true)) {
                // 设置文本区域:x起始坐标,y起始坐标,宽度,行间距
                float x = 50;
                float currentY = 700;
                float maxWidth = 500;
                float lineSpacing = 15;

                contentStream.setFont(PDType1Font.HELVETICA, 12);
                contentStream.setNonStrokingColor(new PDColor(new float[]{0,0,0}, PDDeviceRGB.INSTANCE));

                // 拆分超长文本为多行
                String[] words = replacedText.split(" ");
                StringBuilder currentLine = new StringBuilder();
                for (String word : words) {
                    String tempLine = currentLine + " " + word;
                    float lineWidth = PDType1Font.HELVETICA.getStringWidth(tempLine) / 1000 * 12;
                    if (lineWidth <= maxWidth) {
                        currentLine.append(" ").append(word);
                    } else {
                        // 写入当前行
                        contentStream.beginText();
                        contentStream.newLineAtOffset(x, currentY);
                        contentStream.showText(currentLine.toString().trim());
                        contentStream.endText();
                        currentY -= lineSpacing;
                        currentLine = new StringBuilder(word);
                    }
                }
                // 写入最后一行
                contentStream.beginText();
                contentStream.newLineAtOffset(x, currentY);
                contentStream.showText(currentLine.toString().trim());
                contentStream.endText();
            }

            document.save("filled_long_text.pdf");
        }
    }
}

如果需要更精准的占位符位置(比如模板里的占位符在特定坐标),可以通过PDFTextStripper的自定义子类记录每个占位符的坐标,然后直接在对应位置填充文本,这样即使文本长度变化,也能准确替换。

内容的提问来源于stack exchange,提问作者dzanab

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:20:58