如何通过编程替换PDF文件中的指定文本并保留原格式(支持Java/Python)
如何通过编程替换PDF文件中的指定文本并保留原格式(支持Java/Python)
我完全理解你现在的困扰——想通过代码批量替换PDF里的特定文本,但试了Apache PDFBox的代码后得到了空白文档,甚至参考了一些方案也没解决问题。下面我先帮你分析原有代码的问题,再分别给出Java和Python的可行解决方案。
一、你的Java代码问题分析
你写的PDFBox代码出现空白文档,主要有这几个核心问题:
- 使用了
PDPageContentStream.AppendMode.OVERWRITE模式:这个模式会直接清空原页面的所有内容,相当于把原页面擦除后重新绘制,自然会输出空白文档。 - 全局收集所有
TextPosition:你把所有页面的文本位置都存在同一个列表里,遍历单个页面时会处理所有页面的文本,逻辑完全混乱。 - 没有覆盖原文本:直接在原文本位置写入新内容,会导致新旧文本重叠,而且原文本还会留在页面上。
修正后的Java代码(基于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.PDFont; import org.apache.pdfbox.text.PDFTextStripper; import org.apache.pdfbox.text.TextPosition; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class ReplaceTextPreserveFont { public static void replaceTextInPDF(String inputFilePath, String outputFilePath, String targetText, String replacementText) throws IOException { // 用try-with-resources自动关闭文档,避免资源泄漏 try (PDDocument document = PDDocument.load(new File(inputFilePath))) { // 按页面存储文本位置,确保每个页面只处理自己的文本 Map<PDPage, List<TextPosition>> pageTextPositions = new HashMap<>(); PDFTextStripper stripper = new PDFTextStripper() { @Override protected void writeString(String text, List<TextPosition> textPositionsList) throws IOException { // 获取当前处理的页面 PDPage currentPage = document.getPages().get(getCurrentPageNo() - 1); pageTextPositions.computeIfAbsent(currentPage, k -> new ArrayList<>()).addAll(textPositionsList); super.writeString(text, textPositionsList); } }; stripper.getText(document); for (Map.Entry<PDPage, List<TextPosition>> entry : pageTextPositions.entrySet()) { PDPage page = entry.getKey(); List<TextPosition> textPositions = entry.getValue(); // 使用APPEND模式,保留原页面所有内容,只追加新绘制内容 try (PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true, true)) { for (TextPosition textPosition : textPositions) { String text = textPosition.getUnicode(); // 精确匹配目标文本(如果需要模糊匹配可以用contains) if (text.equals(targetText)) { PDFont font = textPosition.getFont(); float fontSize = textPosition.getFontSize(); float x = textPosition.getXDirAdj(); float y = textPosition.getYDirAdj(); // 计算原文本的宽度,用于绘制覆盖矩形 float textWidth = font.getStringWidth(targetText) * fontSize / 1000; // 用白色矩形覆盖原文本,避免新旧文本重叠 contentStream.setNonStrokingColor(255, 255, 255); contentStream.addRect(x, y - fontSize, textWidth, fontSize); contentStream.fill(); // 写入替换文本,保持和原文本一致的格式 contentStream.setNonStrokingColor(0, 0, 0); // 黑色文本,和原文本一致 contentStream.beginText(); contentStream.setFont(font, fontSize); contentStream.newLineAtOffset(x, y); contentStream.showText(replacementText); contentStream.endText(); } } } } document.save(outputFilePath); System.out.println("文本替换完成,已保存至: " + outputFilePath); } } public static void main(String[] args) { String inputFilePath = "G:\\Document.pdf"; String outputFilePath = "G:\\Updated_Document.pdf"; String targetText = "Jesse"; String replacementText = "James"; try { replaceTextInPDF(inputFilePath, outputFilePath, targetText, replacementText); } catch (IOException e) { e.printStackTrace(); } } }
代码改进说明
- 改用
APPEND模式:保留原页面的所有内容,只在上面追加新的绘制内容,不会清空原页面。 - 按页面存储文本位置:确保每个页面只处理自己的文本,逻辑清晰不混乱。
- 先覆盖原文本:用白色矩形把原文本区域完全盖住,再写入新文本,彻底避免重叠问题。
- 自动关闭资源:使用
try-with-resources语法,确保文档和流自动关闭,避免内存泄漏。
二、Python解决方案(基于PyMuPDF)
如果用Python的话,PyMuPDF(fitz库)处理文本替换会更简单,对格式的保留也更友好。先安装依赖库:
pip install pymupdf
Python代码示例
import fitz # PyMuPDF def replace_text_in_pdf(input_path, output_path, target_text, replacement_text): # 打开PDF文档 doc = fitz.open(input_path) for page in doc: # 查找当前页面中所有匹配目标文本的位置 text_instances = page.search_for(target_text) for inst in text_instances: # 用白色矩形覆盖原文本,消除重叠 page.draw_rect(inst, color=(1, 1, 1), fill=(1, 1, 1)) # 获取原文本的字体、大小、颜色,保证替换后格式一致 font, font_size, text_color = None, None, None blocks = page.get_text("dict")["blocks"] for block in blocks: if "lines" in block: for line in block["lines"]: for span in line["spans"]: if span["text"] == target_text: font = span["font"] font_size = span["size"] text_color = span["color"] break # 插入替换文本,对齐原位置 page.insert_text( (inst.x0, inst.y0), # 文本左上角坐标,和原文本对齐 replacement_text, fontname=font, fontsize=font_size, color=fitz.sRGB_to_pdf(text_color) ) # 保存修改后的文档 doc.save(output_path) doc.close() print(f"文本替换完成,已保存至: {output_path}") if __name__ == "__main__": input_file = "G:\\Document.pdf" output_file = "G:\\Updated_Document.pdf" target = "Jesse" replacement = "James" replace_text_in_pdf(input_file, output_file, target, replacement)
代码说明
search_for方法:快速定位当前页面中所有匹配目标文本的位置,返回矩形坐标。- 覆盖原文本:用白色矩形填充原文本区域,彻底消除新旧文本重叠的问题。
- 继承原格式:自动获取原文本的字体、大小和颜色,确保替换后的文本和原文本格式完全一致。
insert_text方法:精准对齐原文本位置写入新内容,保证布局不变。
注意事项
- 扫描版PDF不适用:如果你的PDF是扫描生成的图片格式,以上方法都无法直接替换文本,需要先通过OCR工具将其转换为可编辑的文本PDF。
- 复杂布局适配:对于多列文本、嵌套表格等复杂布局,可能需要微调坐标或文本匹配逻辑,确保替换位置准确。
- 字体一致性:如果原文档使用了特殊字体,确保运行代码的环境中安装了该字体,否则可能会出现字体替换导致的格式变形。
备注:内容来源于stack exchange,提问作者pranavs
相关产品推荐
相关产品推荐

