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

如何通过编程替换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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 12:53:06