前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >itext实现合同尾部签章部分自动添加,定位签名

itext实现合同尾部签章部分自动添加,定位签名

作者头像
老梁
发布2019-09-10 17:32:44
1.4K0
发布2019-09-10 17:32:44
举报

使用的pom

代码语言:javascript
复制
<!-- pdf处理 start-->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext-asian</artifactId>
    <version>5.2.0</version>
</dependency>
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.itextpdf/kernel -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>kernel</artifactId>
    <version>7.1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.itextpdf/layout -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>layout</artifactId>
    <version>7.1.2</version>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.49</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.itextpdf/forms -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>forms</artifactId>
    <version>7.1.4</version>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdk15on</artifactId>
    <version>1.49</version>
</dependency>
<!-- pdf处理 end-->

效果图

原理

  1. 通过itext中List添加固定文本
  2. 添加指定标记比如★☆用以之后替换成其他文本或图片
  3. 可以不用标记,反正就是算好位置
  4. 至于签章这块位置的选定,根据文档最后一行位置判定,我的判定方法就是文档最后一页最后一行离尾部距离小于一定值,签章的整块内容移到新的一页

过程

  1. 核心利用了com.itextpdf.text.pdf.parser.RenderListener这个类,它会遍历这个文档的内容
  2. 写个继承这个类的方法,实现方法如下
代码语言:javascript
复制
@Override
public void renderText(TextRenderInfo textInfo) {
    Float bound = textInfo.getBaseline().getBoundingRectange();
    String text = textInfo.getText();
    float y = bound.y - this.fixHeight;
    for (String keyWord : findText) {
        if (null != text && text.contains(keyWord)) {
            ReplaceRegion region = new ReplaceRegion(keyWord);
            region.setH(bound.height == 0 ? defaultH : bound.height);
            if ((text.contains("☆") && keyWord.equals("☆"))) {
                region.setW(20f);
                int i = text.indexOf("☆");
                region.setX(bound.x + 13 * i);
            } else if (text.contains("★") && keyWord.equals("★")) {
                region.setW(15f);
                region.setX(bound.x);
            } else if ((text.contains("△") && keyWord.equals("△"))) {
                region.setW(15f);
                int i = text.indexOf("△");
                region.setX(bound.x + 11 * i);
            } else if ((text.contains("▲") && keyWord.equals("▲"))) {
                region.setW(15f);
                region.setX(bound.width + 15 * 4.5f);
            } else {
                region.setW(bound.width);
                region.setX(bound.x);
            }
            region.setY(y);
            result.put(keyWord, region);
        }
    }
    //判断最后一行是否小于某个值
    if (y < heightSign) {
        signY.put("endY", 0f);
    } else {
        signY.put("endY", y);
    }
}
  1. 这里我进行了很多微调,此方法肯定存在很多改进的地方,由于时间紧急,我对itext的研究也不深,勉强实现需求
代码语言:javascript
复制
...
PdfReader reader = new PdfReader(pdfBytes);
//内容解析器
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
for (int i = 1; i <= numberOfPages; i++) {
    parser.processContent(i, listener);
    Map<String, ReplaceRegion> res = listener.getResult(i);
    if (res.size() > 0) {
        pdfReplacer.setPageNum(i);
        result = res;
    }
}
...
  1. 通过上面的步骤找到最后一行位置,找到指定特殊字符的位置
  2. 添加尾部签章部分通过
代码语言:javascript
复制
PdfReader reader = new PdfReader(basePath + "_temp2.pdf");
PdfWriter writer = new PdfWriter(basePath + "_temp3.pdf");
PdfDocument pdf = new PdfDocument(reader, writer);
int numberOfPages = pdf.getNumberOfPages();
float height = pdf.getDefaultPageSize().getHeight();
if (endY == 0f) {
    numberOfPages++;
    endY = height - 140f;
    pdf.addNewPage();
}else{
    endY = endY - 60f;
}

Document doc = new Document(pdf);
PdfFont font = PdfFontFactory.createFont("C:/Windows/Fonts/simsun.ttc,1", PdfEncodings.IDENTITY_H,true);
com.itextpdf.layout.element.List list = new com.itextpdf.layout.element.List().setPageNumber(numberOfPages).setFixedPosition(80,endY,400f).setListSymbol("").setSymbolIndent(22f).setFont(font).setFontSize(14);
list.add(new ListItem("甲方法定代表人:☆            乙方法定代表人: △"))
    .add(new ListItem("联系电话:                    联系电话:"))
    .add(new ListItem("身份证号码:                  身份证号码:"))
    .add(new ListItem("★                            ▲"));
doc.add(list);

pdf.close();
  1. 整个过程会出现很多中间临时文件,所以说还是可以有很多改进的地方。
  2. 替换方法,用来替换日期,和覆盖特殊符号
代码语言:javascript
复制
textReplacer = new PdfReplacer(basePath + "_temp3.pdf");
textReplacer.replaceText(replaceStr, destStr.toString());
replaceRegion = textReplacer.toPdf(basePath + "_temp4.pdf");
PdfReplacer textReplacer2 = new PdfReplacer(basePath + "_temp4.pdf");
String dateRecord = sysconfig.getProperties().getProperty(dateSign);
String text = DateUtil.format2str("yyyy 年  MM 月  dd 日");
textReplacer2.setFont(14);
String dateFontPath = sysconfig.getProperties().getProperty("date_font_path");
if (dateFontPath.lastIndexOf("ttf") > 0) {
    textReplacer2.setFont(new com.itextpdf.text.Font(BaseFont.createFont(dateFontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED)));
} else {
    dateFontPath = dateFontPath + ",1";
    textReplacer2.setFont(new com.itextpdf.text.Font(BaseFont.createFont(dateFontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED)));
}
log.info("字体路径{}", dateFontPath);
textReplacer2.replaceText(dateRecord, text);
  1. 签章方法
代码语言:javascript
复制
public static byte[] sign(String password, InputStream inputStream, String signPdfSrc, String signImage,
                          float x, float y,int page) {
    File signPdfSrcFile = new File(signPdfSrc);
    PdfReader reader = null;
    ByteArrayOutputStream signPDFData = null;
    PdfStamper stp = null;
    try {
        BouncyCastleProvider provider = new BouncyCastleProvider();
        Security.addProvider(provider);
        KeyStore ks = KeyStore.getInstance("PKCS12", new BouncyCastleProvider());
        // 私钥密码 为Pkcs生成证书是的私钥密码 123456
        ks.load(inputStream, password.toCharArray());
        String alias = (String) ks.aliases().nextElement();
        PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());
        Certificate[] chain = ks.getCertificateChain(alias);
        reader = new PdfReader(signPdfSrc);
        signPDFData = new ByteArrayOutputStream();
        // 临时pdf文件
        File temp = new File(signPdfSrcFile.getParent(), System.currentTimeMillis() + ".pdf");
        stp = PdfStamper.createSignature(reader, signPDFData, '\0', temp, true);
        stp.setFullCompression();
        PdfSignatureAppearance sap = stp.getSignatureAppearance();
        sap.setReason("数字签名,不可改变");
        // 使用png格式透明图片
        Image image = Image.getInstance(signImage);
        sap.setImageScale(0);
        sap.setSignatureGraphic(image);
        sap.setRenderingMode(RenderingMode.GRAPHIC);
        int size = 120;
        // 是对应x轴和y轴坐标
        float lly = y - 50;
        sap.setVisibleSignature(new Rectangle(x, lly, x + size, lly+size), page,
                UUID.randomUUID().toString().replaceAll("-", ""));
        stp.getWriter().setCompressionLevel(5);
        ExternalDigest digest = new BouncyCastleDigest();
        ExternalSignature signature = new PrivateKeySignature(key, DigestAlgorithms.SHA512, provider.getName());
        MakeSignature.signDetached(sap, digest, signature, chain, null, null, null, 0, CryptoStandard.CADES);
        stp.close();
        reader.close();
        return signPDFData.toByteArray();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {

        if (signPDFData != null) {
            try {
                signPDFData.close();
            } catch (IOException e) {
            }
        }

        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (IOException e) {
            }
        }
    }
    return null;
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-11-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 使用的pom
  • 效果图
  • 原理
  • 过程
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档