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

如何使用Apache PDFBox为已签名PDF表单字段文本添加下划线?

解决方案:Apache PDFBox 为已签署PDF表单字段添加下划线

1. 可以实现,核心思路是增量添加视觉元素(避免破坏签名有效性)

签署后的PDF不能修改原有字段内容(否则签名会失效),因此我们通过添加额外的绘制层或批注来实现下划线,而非修改字段的默认外观(DA)。

方法1:直接绘制下划线(通过内容流)

这种方式是在字段文本的对应位置绘制一条线,步骤如下:

  • 定位目标字段的位置和文本信息
  • 计算下划线的坐标(基于文本宽度、字体大小和字段对齐方式)
  • 以增量模式打开页面内容流,绘制下划线

示例代码:

import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.interactive.form.*;
import java.io.File;
import java.awt.Color;

public class AddUnderlineToSignedField {
    public static void main(String[] args) throws Exception {
        // 加载已签署的PDF文档
        PDDocument document = PDDocument.load(new File("signed_document.pdf"));
        PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
        
        // 获取目标表单字段(替换为你的字段名称)
        PDTextField targetField = (PDTextField) acroForm.getField("your_target_field");
        PDAnnotationWidget widget = targetField.getWidgets().get(0);
        PDPage page = widget.getPage();
        PDRectangle fieldRect = widget.getRectangle();
        
        // 获取字段文本和字体信息
        String fieldText = targetField.getValue();
        PDDefaultAppearanceString daString = new PDDefaultAppearanceString(targetField.getDefaultAppearance());
        PDFont font = daString.getFont();
        float fontSize = daString.getFontSize();
        
        // 计算文本宽度和下划线位置(左对齐示例,按需调整对齐方式)
        float textWidth = font.getStringWidth(fieldText) / 1000 * fontSize;
        float padding = 2; // 内边距
        float xStart = fieldRect.getLowerLeftX() + padding;
        float xEnd = xStart + textWidth;
        float yPos = fieldRect.getLowerLeftY() + fontSize / 2 - 1; // 下划线在基线下方,调整-1控制偏移
        
        // 以增量模式打开内容流,避免破坏原有签名
        try (PDPageContentStream contentStream = new PDPageContentStream(
                document, page, PDPageContentStream.AppendMode.APPEND, true, true)) {
            // 设置下划线样式
            contentStream.setStrokingColor(Color.BLACK);
            contentStream.setLineWidth(0.5f);
            
            // 绘制下划线
            contentStream.moveTo(xStart, yPos);
            contentStream.lineTo(xEnd, yPos);
            contentStream.stroke();
        }
        
        // 增量保存文档(不会修改原有签名内容)
        document.save("signed_document_with_underline.pdf");
        document.close();
    }
}

方法2:使用文本批注(PDAnnotationTextMarkup)

如果直接绘制线不符合需求,可以使用PDF的下划线批注,注意要正确设置批注的QuadPoints坐标:

示例代码:

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDRectangle;
import org.apache.pdfbox.pdmodel.color.PDColor;
import org.apache.pdfbox.pdmodel.color.PDDeviceRGB;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationTextMarkup;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDQuadPoint;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDAnnotationWidget;
import org.apache.pdfbox.pdmodel.interactive.form.PDTextField;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class AddUnderlineAnnotToSignedField {
    public static void main(String[] args) throws Exception {
        PDDocument document = PDDocument.load(new File("signed_document.pdf"));
        PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
        
        PDTextField targetField = (PDTextField) acroForm.getField("your_target_field");
        PDAnnotationWidget widget = targetField.getWidgets().get(0);
        PDPage page = widget.getPage();
        PDRectangle fieldRect = widget.getRectangle();
        
        // 计算下划线位置(同方法1)
        String fieldText = targetField.getValue();
        PDDefaultAppearanceString daString = new PDDefaultAppearanceString(targetField.getDefaultAppearance());
        PDFont font = daString.getFont();
        float fontSize = daString.getFontSize();
        float textWidth = font.getStringWidth(fieldText) / 1000 * fontSize;
        float padding = 2;
        float xStart = fieldRect.getLowerLeftX() + padding;
        float xEnd = xStart + textWidth;
        float yBaseline = fieldRect.getLowerLeftY() + fontSize / 2;
        
        // 创建下划线批注
        PDAnnotationTextMarkup underlineAnnot = new PDAnnotationTextMarkup(PDAnnotationTextMarkup.SUB_TYPE_UNDERLINE);
        underlineAnnot.setRectangle(fieldRect);
        underlineAnnot.setColor(new PDColor(new float[]{0, 0, 0}, PDDeviceRGB.INSTANCE));
        
        // 设置QuadPoints(左上、右上、左下、右下,对应下划线区域)
        List<PDQuadPoint> quadPoints = new ArrayList<>();
        quadPoints.add(new PDQuadPoint(
                xStart, yBaseline + 1,
                xEnd, yBaseline + 1,
                xStart, yBaseline - 1,
                xEnd, yBaseline - 1
        ));
        underlineAnnot.setQuadPoints(quadPoints);
        
        // 添加批注到页面
        page.getAnnotations().add(underlineAnnot);
        
        document.save("signed_document_with_underline_annot.pdf");
        document.close();
    }
}

2. 替代方案说明

如果上述方法遇到问题(比如字段是多行、复杂布局),可以考虑:

  • 预签署时设置字段默认外观:如果允许在签署前调整,直接修改字段的DA字符串,添加下划线指令。例如在DA中加入/U 1(PDF的文本状态参数,U表示下划线,1为开启),但注意签署后不能修改DA,否则签名失效。
  • 提取字段位置后生成覆盖层:将字段位置导出,生成一个包含下划线的PDF层,然后通过PDFBox合并到原文档中(同样使用增量更新)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 15:55:06