玩转PDF贼稳,产品要做一个三方合同签署,我方了!

Posted 牧小农

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了玩转PDF贼稳,产品要做一个三方合同签署,我方了!相关的知识,希望对你有一定的参考价值。

一、前言

事情是这个样子的,小农的公司,之前有个功能需要签署来进行一系列的操作,于是我们引入了一个三方平台的签署——上上签,但是有一个比较尴尬的点就是,它不支持合同在浏览器上和附件一起预览的,我们想要的是需要将附件拼接在合同主文件中一起展示,但是它不支持,于是我们就开了一个需求会。。。

产品说,我们要做一个线上合同签署的功能,不依靠第三方来完成,可以浏览器上预览和下载合同,小农,你这边能做吗?

我一听,这个啊,这个有点难度啊(我需要时间),不太好做,之前我们接入的第三方就没有完全完成浏览器预览的功能,相当于我们做一个和这个第三方一模一样的东西,而且还要比它那个兼容更多的功能,不太好做(确实有点不太好做),加上之前也没有做过,心里没有底。

产品说,这个没有办法(你做也得做,不做也得做),是领导要求的(上面要求的,你只能做),你看下完成这些功能大概需要多久?

于是只能硬着头皮上了,于是给了一个大概的时间后,就开始研究了,什么是快乐星球,如果你想知道的话,那我就带你研究,what???等等,跑偏了,回来回来。

二、那我就带你研究

研究什么?什么是快乐星球[手动狗头],咳咳,洗脑了,请你立即停止你的傻*行为。

我们知道,如果是想要操作PDF的话(因为签署合同一般都是用的PDF,同志们为你们解疑了,掌声可以响起来了),所以一般都是用iText(PDF 操作类库)操作类库??? ,咳咳,你怎么回事?

我们一般都是要使用 Adobe工具设计表单和iText 来进行内容操作,这也是我们今天需要讲解的主体,知道了用什么,我们来讲一下我们的需求是什么?工作后的小伙伴有没有觉得很可怕,“我们来讲一下需求”,首先需要实现的是 通过PDF模板填充我们对应的甲乙方基本数据后,生成PDF文件,然后再将数据库的特定数据拼接在PDF里面一起,通过甲乙方先后签署后,然后让该合同进行生效操作!可以预览和下载。

要求就是这么个要求,听着倒是不难,主要是之前没有做过,心里不太有谱,但是做完之后,不得不呼自己真是个天才啊,我可真聪明,想起小农从实习的时候就是做的PDF,如今工作这么久了还是在做PDF的,真是漂(cao)亮(dan),那就做呗,谁怕谁啊!

三、Adobe工具

工欲善其事必先利其器,首先如果想要填充PDF模板里面的内容的话,我们需要使用到Adobe Acrobat Peo DC这个工具

下载地址:

链接:https://pan.baidu.com/s/1JdeKr7-abc4bajhVxoiYWg
提取码:6h0i
解压密码:www.downza.cn

1、打开PDF文件

当我们下载好Adobe Acrobat Peo DC后,用它打开PDF文件,然后点击 准备表单

2、添加文本域

点击添加文本域

3、填写变量名

这个变量名就是我们填充数据的参数,要一一对应

4、填写完成后,如下所示

3、开始安排

别担心小伙伴们,项目都给你们准备好了
项目地址:https://github.com/muxiaonong/other/tree/master/pdf_sign_demo

jar文件:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.5</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>layout</artifactId>
            <version>7.1.15</version>
        </dependency>

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
        </dependency>

        <!--itext生成word文档,需要下面dependency-->
        <dependency>
            <groupId>com.lowagie</groupId>
            <artifactId>iText-rtf</artifactId>
            <version>2.1.4</version>
        </dependency>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

根据PDF模板生成文件和拼接产品的数据

    /**
     * 根据PDF模板生成PDF文件
     * @return
     */
    @GetMapping("generatePdf")
    public String generatePdf() throws Exception
//        File file = ResourceUtils.getFile("classpath:"+SAVE_PATH);
        File pdfFile = new File(ResourceUtils.getURL("classpath:").getPath()+SAVE_PATH);
        try 
            PdfReader pdfReader;
            PdfStamper pdfStamper;
            ByteArrayOutputStream baos;

            Document document = new Document();
//

            PdfSmartCopy pdfSmartCopy = new PdfSmartCopy(document,
                    new FileOutputStream(pdfFile));

            document.open();

            File file = ResourceUtils.getFile("classpath:"+templatePath);
            pdfReader = new PdfReader(file.getPath());
            int n = pdfReader.getNumberOfPages();
            log.info("页数:"+n);
            baos = new ByteArrayOutputStream();
            pdfStamper = new PdfStamper(pdfReader, baos);

            for(int i = 1; i <= n; i++) 
                AcroFields acroFields = pdfStamper.getAcroFields();

                //key statement 1
                acroFields.setGenerateAppearances(true);

                //acroFields.setExtraMargin(5, 5);
                acroFields.setField("customerAddress", "上海市浦东新区田子路520弄1号楼");
                acroFields.setField("customerCompanyName", "上海百度有限公司");
                acroFields.setField("customerName", "张三");
                acroFields.setField("customerPhone", "15216667777");
                acroFields.setField("customerMail", "123456789@sian.com");

                acroFields.setField("vendorAddress", "上海市浦东新区瑟瑟发抖路182号");
                acroFields.setField("vendorCompanyName", "牧小农科技技术有限公司");
                acroFields.setField("vendorName", "王五");
                acroFields.setField("vendorPhone", "15688886666");
                acroFields.setField("vendorMail", "123567@qq.com");

                acroFields.setField("effectiveStartTime", "2021年05月25");
                acroFields.setField("effectiveEndTime", "2022年05月25");

                //true代表生成的PDF文件不可编辑
                pdfStamper.setFormFlattening(true);

                pdfStamper.close();

                pdfReader = new PdfReader(baos.toByteArray());


                pdfSmartCopy.addPage(pdfSmartCopy.getImportedPage(pdfReader, i));
                pdfSmartCopy.freeReader(pdfReader);
                pdfReader.close();
            
            pdfReader.close();
            document.close();
         catch(DocumentException dex) 
            dex.printStackTrace();
         catch(IOException ex) 
            ex.printStackTrace();
        
        //创建PDF文件
        createPdf();


        File file3 = new File(ResourceUtils.getURL("classpath:").getPath()+TEMP_PATH);
        File file1 = new File(ResourceUtils.getURL("classpath:").getPath()+outputFileName);

        List<File> files = new ArrayList<>();
        files.add(pdfFile);
        files.add(file3);

        try 
            PdfUtil pdfUtil = new PdfUtil();
            pdfUtil.mergeFileToPDF(files,file1);
         catch (Exception e) 
            e.printStackTrace();
        

        //如果你是上传文件服务器上,这里可以上传文件
//        String url = fileServer.uploadPdf(File2byte(file1));

        //删除总文件
        //如果是你本地预览就不要删除了,删了就看不到了
//        if(file1.exists())
//            file1.delete();
//        
        //删除模板文件
        if(pdfFile.exists())
            System.gc();
            pdfFile.delete();
        
        //删除产品文件
        if(file3.exists())
            file3.delete();
        
        return "success";
    

创建PDF附件信息拼接到主文件中:

/**
     * 创建PDF附件信息
     */
    public static void createPdf() 
        Document doc = null;
        try 
            doc = new Document();
            PdfWriter.getInstance(doc, new FileOutputStream(ResourceUtils.getURL("classpath:").getPath()+TEMP_PATH));
            doc.open();
            BaseFont bfChi = BaseFont.createFont("STSong-Light","UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            Font fontChi = new Font(bfChi, 8, Font.NORMAL);

            PdfPTable table = new PdfPTable(5);

            Font fontTitle = new Font(bfChi, 15, Font.NORMAL);
            PdfPCell cell = new PdfPCell(new Paragraph("*货运*运输服务协议-附件1 运输费用报价",fontTitle));


            cell.setColspan(5);
            table.addCell(cell);
//			"序号"
            table.addCell(new Paragraph("序号",fontChi));
            table.addCell(new Paragraph("品类",fontChi));
            table.addCell(new Paragraph("名称",fontChi));
            table.addCell(new Paragraph("计算方式",fontChi));
            table.addCell(new Paragraph("费率",fontChi));

            table.addCell(new Paragraph("1",fontChi));
            table.addCell(new Paragraph("货运",fontChi));
            table.addCell(new Paragraph("费率1.0",fontChi));
            table.addCell(new Paragraph("算",fontChi));
            table.addCell(new Paragraph("0~100万-5.7%,上限:500元,下限:20元",fontChi));

            table.addCell(new Paragraph("2",fontChi));
            table.addCell(new Paragraph("货运",fontChi));
            table.addCell(new Paragraph("费率1.0",fontChi));
            table.addCell(new Paragraph("倒",fontChi));
            table.addCell(new Paragraph("100万~200万-5.6%,无上限、下限",fontChi));

            table.addCell(new Paragraph("3",fontChi));
            table.addCell(new Paragraph("货运",fontChi));
            table.addCell(new Paragraph("费率1.0",fontChi));
            table.addCell(new Paragraph("算",fontChi));
            table.addCell(new Paragraph("200万~300万-5.5%,无上限、下限",fontChi));


            doc.add(table);

//			doc.add(new Paragraph("Hello World,看看中文支持不........aaaaaaaaaaaaaaaaa",fontChi));
         catch (FileNotFoundException e) 
            e.printStackTrace();
         catch (DocumentException e) 
            e.printStackTrace();
         catch (IOException e) 
            e.printStackTrace();
         finally 
            doc.close();
        
    

合同签署:

/**
     * 签署合同
     * @return
     * @throws IOException
     * @throws DocumentException
     */
    @GetMapping("addContent")
    public String addContent() throws IOException, DocumentException 

        BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        Font font = new Font(baseFont);

        //这里可以填写本地地址,也可以是服务器上的文件地址
        PdfReader reader = new PdfReader(ResourceUtils.getURL("classpath:").getPath()+outputFileName);
        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(ResourceUtils.getURL("classpath:").getPath()+endPdf));

//
        PdfContentByte over = stamper.getOverContent(1);
        ColumnText columnText = new ColumnText(over);

        PdfContentByte over1 = stamper.getOverContent(1);
        ColumnText columnText1 = new ColumnText(over1);

        PdfContentByte over2 = stamper.getOverContent(1);
        ColumnText columnText2 = new ColumnText(over2);


        PdfContentByte over3 = stamper.getOverContent(1);
        ColumnText columnText3 = new ColumnText(over3);
        // llx 和 urx  最小的值决定离左边的距离. lly 和 ury 最大的值决定离下边的距离
        // llx 左对齐
        // lly 上对齐
        // urx 宽带
        // ury 高度
        columnText.setSimpleColumn(29, 117, 221, 16);
        Paragraph elements = new Paragraph(0, new Chunk("上海壹站供应链有限公司"));

        columnText1.setSimpleColumn(26, 75, 221, 16);
        Paragraph elements1 = new Paragraph(0, new Chunk("2021年03月03日

以上是关于玩转PDF贼稳,产品要做一个三方合同签署,我方了!的主要内容,如果未能解决你的问题,请参考以下文章

使用第三方电子合同平台签电子合同该怎么签?

PDF电子签名。如何与合法签名的人签名紧密?

如何调用第三方电子签章接口

建筑建材企业如何高效进行合同签署?数商云B2B系统推动企业合同签约规范化

建筑建材企业如何高效进行合同签署?数商云B2B系统推动企业合同签约规范化

2018年中国第三方电子签名行业研究报告