You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用Java从姓名生成类手写签名并添加至PDF文件

Java实现PDF数字签名+类手写签名添加方案

一、PDF数字签名实现(基于iText 7)

我之前做过不少PDF处理的需求,iText是这块的老牌工具库,数字签名功能支持得相当完善,给你整理了具体步骤:

  1. 引入依赖(Maven项目为例)
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext7-core</artifactId>
    <version>7.2.5</version>
</dependency>
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>sign</artifactId>
    <version>7.2.5</version>
</dependency>
  1. 核心代码实现
import com.itextpdf.kernel.pdf.*;
import com.itextpdf.signatures.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;

public class PdfSignerDemo {
    public static void main(String[] args) throws Exception {
        // 1. 定义待签名PDF和输出文件路径
        String src = "input.pdf";
        String dest = "signed_output.pdf";
        
        // 2. 加载PKCS12格式的数字证书(提前准备好证书文件和密码)
        KeyStore ks = KeyStore.getInstance("PKCS12");
        ks.load(new FileInputStream("your_certificate.p12"), "your_cert_password".toCharArray());
        String alias = ks.aliases().nextElement();
        PrivateKey privateKey = (PrivateKey) ks.getKey(alias, "your_cert_password".toCharArray());
        Certificate[] certChain = ks.getCertificateChain(alias);
        
        // 3. 初始化PDF读取、写入和签名器
        PdfReader reader = new PdfReader(src);
        PdfWriter writer = new PdfWriter(dest);
        PdfDocument pdfDoc = new PdfDocument(reader, writer);
        PdfSigner signer = new PdfSigner(reader, writer, false);
        
        // 4. 设置签名外观(位置、原因、地点等)
        PdfSignatureAppearance appearance = signer.getSignatureAppearance();
        appearance.setReason("正式文档签署")
                  .setLocation("上海")
                  .setReuseAppearance(false);
        // 签名框位置:左下角x=100, y=100,宽200高100,第一页
        appearance.setPageRect(new Rectangle(100, 100, 200, 100))
                  .setPageNumber(1);
        
        // 5. 执行签名操作
        IExternalSignature signature = new PrivateKeySignature(privateKey, DigestAlgorithms.SHA256, null);
        IExternalDigest digest = new BouncyCastleDigest();
        signer.signDetached(digest, signature, certChain, null, null, null, 0, PdfSigner.CryptoStandard.CMS);
        
        // 关闭资源
        pdfDoc.close();
        reader.close();
        writer.close();
    }
}

小提示:数字证书可以从正规CA机构申请,测试的话也可以用OpenSSL工具自签一个PKCS12格式的证书。

二、类手写签名生成与添加

我之前也踩过单纯用手写字体的坑,确实太规整,完全不像真的手写。后来摸索出两种优化方案,效果都不错:

方案1:手写字体+随机变形生成自然签名图片

用Java2D绘制文字时,给每个字符加随机偏移、倾斜和旋转,模拟手写的不规则感:

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import javax.imageio.ImageIO;
import java.util.Random;

public class HandwrittenSignatureGenerator {
    public static void main(String[] args) throws Exception {
        String userName = "李四";
        int imgWidth = 220;
        int imgHeight = 80;
        
        // 创建透明背景的图片缓冲区
        BufferedImage signatureImg = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = signatureImg.createGraphics();
        
        // 开启抗锯齿,让文字边缘更平滑
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        
        // 加载手写字体(把下载的手写字体文件放到项目路径)
        Font handwrittenFont = Font.createFont(Font.TRUETYPE_FONT, new FileInputStream("handwritten_style.ttf"))
                                   .deriveFont(Font.PLAIN, 45f);
        g2d.setFont(handwrittenFont);
        g2d.setColor(new Color(0, 0, 0, 255)); // 黑色不透明
        
        Random random = new Random();
        int startX = 25;
        int baseY = 55;
        
        // 逐个字符绘制,添加随机变形
        for (char c : userName.toCharArray()) {
            // 随机偏移x、y坐标(±2像素)
            int offsetX = random.nextInt(5) - 2;
            int offsetY = random.nextInt(5) - 2;
            // 随机倾斜角度(-5°到5°)
            double tiltAngle = (random.nextInt(10) - 5) * Math.PI / 180;
            
            // 旋转画布绘制当前字符,再旋转回来
            g2d.rotate(tiltAngle, startX + offsetX, baseY + offsetY);
            g2d.drawString(String.valueOf(c), startX + offsetX, baseY + offsetY);
            g2d.rotate(-tiltAngle, startX + offsetX, baseY + offsetY);
            
            // 随机调整字符间距
            startX += g2d.getFontMetrics().charWidth(c) + random.nextInt(4) + 1;
        }
        
        g2d.dispose();
        // 保存为PNG图片
        ImageIO.write(signatureImg, "PNG", new FileOutputStream("user_signature.png"));
    }
}

方案2:将手写签名添加到PDF(可结合数字签名)

如果只是单纯添加签名图片到PDF,用iText的PdfCanvas就能实现:

import com.itextpdf.kernel.pdf.*;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.io.image.ImageDataFactory;

public class AddSignatureToPdf {
    public static void main(String[] args) throws Exception {
        String srcPdf = "input.pdf";
        String destPdf = "pdf_with_signature.pdf";
        String signatureImgPath = "user_signature.png";
        
        PdfReader reader = new PdfReader(srcPdf);
        PdfWriter writer = new PdfWriter(destPdf);
        PdfDocument pdfDoc = new PdfDocument(reader, writer);
        
        // 获取要添加签名的页面(这里是第一页)
        PdfPage targetPage = pdfDoc.getPage(1);
        PdfCanvas canvas = new PdfCanvas(targetPage);
        
        // 加载签名图片并设置位置大小(x=100, y=100,宽度180)
        ImageData signatureData = ImageDataFactory.create(signatureImgPath);
        canvas.addImage(signatureData, 100, 100, 180, false);
        
        pdfDoc.close();
        reader.close();
        writer.close();
    }
}

如果要和数字签名结合,只需要在数字签名的代码里,把生成的签名图片设置为签名外观:

// 在设置签名外观的部分添加这两行
appearance.setSignatureGraphic(ImageDataFactory.create("user_signature.png"));
appearance.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);

这样生成的签名既有手写的自然感,又能完成PDF的数字签名验证,亲测比单纯用手写字体效果好太多~

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

火山引擎 最新活动