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
相关产品推荐
相关产品推荐

