Java实现自动生成PDF+电子签章

        最近因工作需要,调研了一下java怎么用代码方式生成带有电子签名和签章的PDF文件,发现网上已经有很多例子了,也参考了很多。发现其中有些细节都没有提到,因此写个博客记录一下。

        本文会比较全面,从电子签章封面图片的制作,到电子签名证书的生成,再到绘制PDF以及最后对PDF文件加上电子签章等步骤。

        经过测试,生成后的PDF电子签章在Windows系统中支持WPS、福昕、Adobe Acrobat等软件识别。Mac系统WPS无电子证书功能,福昕可以识别,Adobe Acrobat需要花钱就没测。移动端也只有福昕可以识别到电子签章。


相关工具类jar包版本:
1.PDF工具jar包:com.itextpdf:itextpdf:5.5.13.3
2.解决pdf中文字体问题:com.itextpdf:font-asian:7.2.3
3.pdf富文本拓展:com.itextpdf.tool:xmlworker:5.5.13 (好像没用到^o^)
4.hutool工具类jar: cn.hutool.core.io:5.8.25

        一、电子签章封面图的制作

        注意:已经有封面图(电子公章图片)的朋友们可以略过;

        参考视频:在WPS里如果制作电子印章_哔哩哔哩_bilibili

        本人制作的效果图:

                

        二、本地生成自测用电子证书

        在后面的加签过程中,我们需要用到 .p12格式的证书文件,这里可以用本地自带的jdk生成测试用的。

        网上有非常多的方式方法和博客可以参考,这里就不再赘述了。

        三、绘制自定义样式的PDF文件并生成带有电子签章的PDF文件

        我用的方法是与前端同学约定好入参字段及格式,做成简易版的PDF自定义样式。大家也可以用富文本直接做入参,但是有些样式可能不好调试,看大家的使用习惯吧。

        Controller层代码

package com.xxxxx.user.controller.pdf.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import com.xxxxx.common.annotation.Anonymous;
import com.xxxxx.common.response.BizResponseUtil;
import com.xxxxx.user.base.BaseController;
import com.xxxxx.user.controller.pdf.constants.Constant;
import com.xxxxx.user.controller.pdf.pojo.param.MatchItem;
import com.xxxxx.user.controller.pdf.pojo.param.PdfParam;
import com.xxxxx.user.controller.pdf.pojo.param.SignatureInfo;
import com.xxxxx.user.controller.pdf.util.PDFUtil;
import com.tencent.ssv.techinfra.spring.boot.response.pojo.Response;
import jakarta.validation.Valid;
import java.io.File;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * PDF制作 控制器
 */
@Slf4j
@RestController
@RequestMapping("/pdfMake")
public class PdfMakeController extends BaseController {


    /**
     * 制作pdf
     */
    @PostMapping("/createPdf")
    public Response<Void> createPdf(@Valid @RequestBody PdfParam param) {
        String tempFilePath = null;
        try {
            //1.生成临时pdf文件名(年月日时分秒微秒)
            String tempFileName = DateUtil.format(DateUtil.date(), "yyyyMMddHHmmssSSS") + "_temp" + Constant.PDF_SUFFIX;
            //2.临时文件路径:区分操作系统类型
            if (FileUtil.isWindows()) {
                tempFilePath = Constant.PDF_PATH_WINDOWS + "/" + tempFileName;
            } else {
                tempFilePath = Constant.PDF_PATH_LINUX + "/" + tempFileName;
            }
            //3.创建临时文件及其父目录,如果这个文件存在,直接返回这个文件
            File tempFile = FileUtil.touch(tempFilePath);
            //4.生成不带电子签章的PDF文件
            PDFUtil.createTempPDF(tempFile, param);
            // 5-根据关键字获取需要签章的位置
            MatchItem matchItem = PDFUtil.getKeyWordsByPath(tempFilePath, Constant.INSCRIBE_NAME);
            // 6-获取和封装签章所需信息
            SignatureInfo signatureInfo = PDFUtil.getSignatureInfo();
            // 7-加盖电子签章,数字签名,并将图片插入到pdf中
            PDFUtil.sign(tempFilePath, matchItem, signatureInfo);
            return BizResponseUtil.success();
        } catch (Exception e) {
            log.error("pdf制作失败:{}", e);
            return BizResponseUtil.fail("PdfMakeFailed", "pdf制作失败");
        } finally {
            try {
                // 8-删除临时pdf文件
                // 8.1-调用GC,强制删除
                //显式调用gc会造成性能黑洞,但是临时文件又没有存在的必要,仍需要删除,可以考虑做个定时器每日0点扫描文件夹删除
//                System.gc();
                // 8.2-删除临时文件
                FileUtil.del(tempFilePath);
            } catch (Exception e) {
                log.error("删除临时文件失败:{}", e);
            }
        }
    }

}

        PDFUtil相关代码

package com.xxxxx.user.controller.pdf.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.xxxxx.user.controller.pdf.constants.Constant;
import com.xxxxx.user.controller.pdf.controller.PdfMakeController;
import com.xxxxx.user.controller.pdf.helper.ContentEventHelper;
import com.xxxxx.user.controller.pdf.listener.CustomRenderListener;
import com.xxxxx.user.controller.pdf.pojo.param.ContentStyle;
import com.xxxxx.user.controller.pdf.pojo.param.ItemTable;
import com.xxxxx.user.controller.pdf.pojo.param.ItemTableRow;
import com.xxxxx.user.controller.pdf.pojo.param.MatchItem;
import com.xxxxx.user.controller.pdf.pojo.param.PDFColor;
import com.xxxxx.user.controller.pdf.pojo.param.PDFFont;
import com.xxxxx.user.controller.pdf.pojo.param.PdfParam;
import com.xxxxx.user.controller.pdf.pojo.param.SignatureInfo;
import com.itextpdf.text.Chunk;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.text.pdf.parser.PdfReaderContentParser;
import com.itextpdf.text.pdf.security.BouncyCastleDigest;
import com.itextpdf.text.pdf.security.DigestAlgorithms;
import com.itextpdf.text.pdf.security.ExternalDigest;
import com.itextpdf.text.pdf.security.ExternalSignature;
import com.itextpdf.text.pdf.security.MakeSignature;
import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
import com.itextpdf.text.pdf.security.PrivateKeySignature;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.util.LinkedList;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;

@Slf4j
public class PDFUtil {

    /**
     * 生成不带电子签章的PDF文件
     *
     * @param tempFile 临时文件
     * @param param 参数
     */
    public static void createTempPDF(File tempFile, PdfParam param) {
        //将tempFile转为输出流
        try (OutputStream tempOutputStream = new FileOutputStream(tempFile)) { // 创建输出流
            // 1-创建文本对象 Document
            Document document = new Document(PageSize.A4, Constant.DOCUMENT_MARGIN_LEFT, Constant.DOCUMENT_MARGIN_RIGHT,
                    Constant.DOCUMENT_MARGIN_TOP, Constant.DOCUMENT_MARGIN_BOTTOM);
            // 2-初始化 pdf输出对象 PdfWriter
            PdfWriter pdfWriter = PdfWriter.getInstance(document, tempOutputStream);
            // 2.1-设置背景图
            pdfWriter.setPageEvent(new ContentEventHelper());
            // 3-打开 Document
            document.open();
            // 4-往 Document 添加内容
            // 4.1-自定义的标题
            Chunk inviteTile1 = new Chunk(Constant.INVITE_TITLE_PREFIX, PDFFont.content1Font);
            Chunk inviteTile2 = new Chunk(param.getInviteTitle(), PDFFont.contentBlueFont);
            Chunk inviteTile3 = new Chunk(Constant.INVITE_TITLE_SUFFIX, PDFFont.content1Font);
    
Java中,可以使用一些开源库来实现PDF文件添加电子印章的功能。其中,常用的开源库包括iText和PDFBox。 一种常见的实现方式是使用iText库。首先,需要使用iText加载要进行操作的PDF文件。然后,通过创建一个PdfReader对象,读取PDF文件的内容。接下来,可以使用PdfStamper类来创建一个新的PDF文件,并在新文件中添加电子印章。可以通过调用PdfStamper的addAnnotation()方法,在指定的位置添加一个电子印章。 在添加电子印章时,需要创建一个PdfSignatureAppearance对象,并设置印章的相关属性,例如印章的位置、大小、图片等。可以通过调用PdfSignatureAppearance的setSignatureGraphic()方法来设置印章的图片。此外,还需要创建一个数字证书,并通过调用PdfSignatureAppearance的setCrypto()方法来设置数字证书的相关信息。 完成设置后,需要调用PdfStamper的close()方法来保存并关闭新的PDF文件。现在,新的PDF文件中就成功添加了电子印章。 除了iText,另一个常用的库是PDFBox。使用PDFBox实现类似的功能也是可以的。使用PDFBox可以加载PDF文件,并使用AcroForms类来进行表单操作。可以通过AcroForms类的addSignatureField()方法来添加电子印章的表单域,并设置相应的属性。然后,通过创建一个PDDocument对象来保存并关闭新的PDF文件。 总结起来,使用Java语言,可以通过一些开源库(如iText和PDFBox)来实现PDF文件进行电子印章的添加。具体的实现步骤是,加载PDF文件,创建一个新的PDF文件,设置电子印章的相关属性,并将电子印章添加到新的PDF文件中。最后,保存并关闭并且新的PDF文件就成功添加了电子印章。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值