如何用Java从姓名生成类手写签名并添加至PDF文件
Java实现PDF数字签名+类手写签名添加方案
一、PDF数字签名实现(基于iText 7)
我之前做过不少PDF处理的需求,iText是这块的老牌工具库,数字签名功能支持得相当完善,给你整理了具体步骤:
- 引入依赖(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>
- 核心代码实现
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




