如何使用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
相关产品推荐
相关产品推荐

