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

使用iText库替换PDF页脚文本及链接失败问题求助

问题:iText 5.5.13.3替换PDF链接URI与显示文本失效

问题背景

使用iTextpdf 5.5.13.3和bcprov-jdk15on 1.69尝试替换PDF中的超链接URI和显示文本,Maven依赖配置如下:

<itextpdf.version>5.5.13.3</itextpdf.version>
<bcprov-jdk15on.version>1.69</bcprov-jdk15on.version>

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>${itextpdf.version}</version>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>${bcprov-jdk15on.version}</version>
</dependency>

原实现代码尝试修改链接注释的Action字典中的URI和T键,但实际运行时出现:有时仅URI替换成功,显示文本无变化;有时两者都无法替换的情况。

核心原因

  • 显示文本与链接注释分离:页面上的可见链接文本属于PDF页面内容流,并非存储在链接注释的Action字典的T键中——T键仅为鼠标悬停时的工具提示文本,不是用户看到的显示文本。
  • 链接动作类型不匹配:原代码仅处理URI类型的Action,若PDF中使用了其他类型的链接动作(如GoToR、JavaScript动作),会导致匹配失败。
  • URI存储位置多样:部分PDF的URI直接存储在链接注释字典的URI键中,而非嵌套在Action字典内,原代码未覆盖这种情况。

解决方案

1. 全面替换链接URI

修改代码以覆盖多种URI存储方式:

  • 检查链接注释的Action字典中的URI键
  • 检查链接注释字典本身的URI键(部分PDF的写法)

2. 替换链接区域的显示文本

通过解析页面内容流定位链接区域内的文本,再用新文本覆盖原位置(需注意字体、字号保持一致)。

完整修改代码

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;

public class PdfLinkReplacer {

    public void parsePdf(final Path path) {
        try {
            PdfReader.unethicalreading = true;
            Path tempPath = Files.createTempFile("_pdfwriting", ".pdf");
            PdfReader reader = new PdfReader(Files.newInputStream(path));
            PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(tempPath.toFile()));
            BaseFont baseFont = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);

            int numPages = reader.getNumberOfPages();
            for (int i = 1; i <= numPages; i++) {
                PdfDictionary pageDict = reader.getPageN(i);
                // 处理所有链接注释
                PdfArray annotsArray = pageDict.getAsArray(PdfName.ANNOTS);
                if (annotsArray != null) {
                    for (int j = 0; j < annotsArray.size(); j++) {
                        PdfDictionary annotDict = annotsArray.getAsDict(j);
                        if (annotDict != null && PdfName.LINK.equals(annotDict.getAsName(PdfName.SUBTYPE))) {
                            PdfString newUri = new PdfString("https://new-url-example.com");
                            String newDisplayText = "新链接文本";

                            // 情况1:URI在Action字典中
                            PdfDictionary actionDict = annotDict.getAsDict(PdfName.A);
                            if (actionDict != null && PdfName.URI.equals(actionDict.getAsName(PdfName.S))) {
                                actionDict.put(PdfName.URI, newUri);
                            }
                            // 情况2:URI直接在注释字典中
                            else if (annotDict.getAsString(PdfName.URI) != null) {
                                annotDict.put(PdfName.URI, newUri);
                            }

                            // 获取链接注释的位置区域,用于替换显示文本
                            PdfArray rectArray = annotDict.getAsArray(PdfName.RECT);
                            if (rectArray != null) {
                                float[] rect = rectArray.asFloatArray();
                                float x1 = rect[0];
                                float y1 = rect[1];
                                float x2 = rect[2];
                                float y2 = rect[3];

                                // 获取页面内容层,覆盖原文本
                                PdfContentByte content = stamper.getOverContent(i);
                                // 用白色矩形覆盖原文本(避免残留)
                                content.setColorFill(BaseColor.WHITE);
                                content.rectangle(x1, y1, x2 - x1, y2 - y1);
                                content.fill();

                                // 写入新文本
                                content.setColorFill(BaseColor.BLUE);
                                content.setFontAndSize(baseFont, 10); // 需匹配原文本的字号,可根据实际调整
                                // 计算文本居中位置
                                float textWidth = baseFont.getWidthPoint(newDisplayText, 10);
                                float textX = x1 + (x2 - x1 - textWidth) / 2;
                                float textY = y1 + (y2 - y1 - baseFont.getAscentPoint(newDisplayText, 10)) / 2 + baseFont.getAscentPoint(newDisplayText, 10);
                                content.beginText();
                                content.setTextMatrix(textX, textY);
                                content.showText(newDisplayText);
                                content.endText();
                            }
                        }
                    }
                }
            }

            stamper.close();
            reader.close();

            Files.copy(tempPath, path, StandardCopyOption.REPLACE_EXISTING);
            Files.deleteIfExists(tempPath);
        } catch (IOException | DocumentException e) {
            e.printStackTrace();
        }
    }
}

注意事项

  • 字体匹配:代码中使用了Helvetica字体,若原PDF使用其他字体,需替换为对应字体,避免显示异常。
  • 字号调整:需根据原链接文本的实际字号调整setFontAndSize的参数。
  • 复杂PDF:对于包含文本绕排、多行链接文本的PDF,需要更复杂的内容流解析逻辑(如使用PdfTextExtractor提取文本位置后精准替换)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 00:22:54