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

1、前言

事情是这个样子的,小农的公司,以前有个功能须要签署来进行一系列的操做,因而咱们引入了一个三方平台的签署——上上签,可是有一个比较尴尬的点就是,它不支持合同在浏览器上和附件一块儿预览的,咱们想要的是须要将附件拼接在合同主文件中一块儿展现,可是它不支持,因而咱们就开了一个需求会。。。java

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

我一听,这个啊,这个有点难度啊(我须要时间),不太好作,以前咱们接入的第三方就没有彻底完成浏览器预览的功能,至关于咱们作一个和这个第三方如出一辙的东西,并且还要比它那个兼容更多的功能,不太好作(确实有点不太好作),加上以前也没有作过,内心没有底。github

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

因而只能硬着头皮上了,因而给了一个大概的时间后,就开始研究了,什么是快乐星球,若是你想知道的话,那我就带你研究,what???等等,跑偏了,回来回来。 在这里插入图片描述spring

2、那我就带你研究

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

咱们知道,若是是想要操做PDF的话(由于签署合同通常都是用的PDF,同志们为大家解疑了,掌声能够响起来了),因此通常都是用iText(PDF 操做类库)操做类库??? ,咳咳,你怎么回事? 在这里插入图片描述apache

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

要求就是这么个要求,听着却是不难,主要是以前没有作过,内心不太有谱,可是作完以后,不得不呼本身真是个天才啊,我可真聪明,想起小农从实习的时候就是作的PDF,现在工做这么久了仍是在作PDF的,真是漂(cao)亮(dan),那就作呗,谁怕谁啊!浏览器

3、Adobe工具

工欲善其事必先利其器,首先若是想要填充PDF模板里面的内容的话,咱们须要使用到Adobe Acrobat Peo DC这个工具tomcat

下载地址:

连接:pan.baidu.com/s/1JdeKr7-a…

提取码:6h0i

一、打开PDF文件

当咱们下载好Adobe Acrobat Peo DC后,用它打开PDF文件,而后点击 准备表单 在这里插入图片描述

二、添加文本域

点击添加文本域 在这里插入图片描述

三、填写变量名

这个变量名就是咱们填充数据的参数,要一一对应 在这里插入图片描述

四、填写完成后,以下所示

在这里插入图片描述

三、开始安排

别担忧小伙伴们,项目都给大家准备好了 项目地址:github.com/muxiaonong/…

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日"));

        columnText2.setSimpleColumn(800, 120, 200, 16);
        Paragraph elements2 = new Paragraph(0, new Chunk("壹汇(江苏)供应链管理有限公司芜湖分公司"));

        columnText3.setSimpleColumn(800, 74, 181, 16);
        Paragraph elements3 = new Paragraph(0, new Chunk("2022年03月03日"));

// acroFields.setField("customerSigntime", "2021年03月03日");
// acroFields.setField("vendorSigntime", "2021年03月09日");
        // 设置字体,若是不设置添加的中文将没法显示
        elements.setFont(font);
        columnText.addElement(elements);
        columnText.go();

        elements1.setFont(font);
        columnText1.addElement(elements1);
        columnText1.go();

        elements2.setFont(font);
        columnText2.addElement(elements2);
        columnText2.go();

        elements3.setFont(font);
        columnText3.addElement(elements3);
        columnText3.go();

        stamper.close();

        File tempFile = new File(ResourceUtils.getURL("classpath:").getPath()+"签署测试.pdf");

        //若是是你要上传到服务器上,填写服务器的地址
// String url = fileServer.uploadPdf(File2byte(tempFile));
// log.info("url:"+url);

        //若是是上传服务器后,要删除信息
        //本地不要删除,不然没有文件
// if(tempFile.exists()){
// tempFile.delete();
// }

        return "success";
    }

复制代码

PDF工具类:

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.stereotype.Component;

import java.io.*;
import java.util.List;

/*** * pdf 相关操做 * @author mxn */
@Slf4j
@Component
public class PdfUtil {

    /** * 合并PDF文件 * @param files 文件列表 * @param output 输出的PDF文件 */
    public void mergeFileToPDF(List<File> files, File output) {
        Document document = null;
        PdfCopy copy = null;
        OutputStream os = null;
        try {
            os = new FileOutputStream(output);
            document = new Document();
            copy = new PdfCopy(document, os);
            document.open();
            for (File file : files) {
                if (!file.exists()) {
                    continue;
                }
                String fileName = file.getName();
                if (fileName.endsWith(".pdf")) {
                    PdfContentByte cb = copy.getDirectContent();
                    PdfOutline root = cb.getRootOutline();
                    new PdfOutline(root, new PdfDestination(PdfDestination.XYZ), fileName
                            .substring(0, fileName.lastIndexOf(".")));
                    // 不使用reader来维护文件,不然删除不掉文件,一直被占用
                    try (InputStream is = new FileInputStream(file)) {
                        PdfReader reader = new PdfReader(is);
                        int n = reader.getNumberOfPages();
                        for (int j = 1; j <= n; j++) {
                            document.newPage();
                            PdfImportedPage page = copy.getImportedPage(reader, j);
                            copy.addPage(page);
                        }
                    } catch(Exception e) {
                        log.warn("error to close file : {}" + file.getCanonicalPath(), e);
// e.printStackTrace();
                    }
                } else {
                    log.warn("file may not be merged to pdf. name:" + file.getCanonicalPath());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (document != null) {
                document.close();
            }
            if (copy != null) {
                copy.close();
            }
            if (os != null) {
                IOUtils.closeQuietly(os);
            }
        }
    }


    /** * 将文件转换成byte数组 * @param file * @return * **/
    public static byte[] File2byte(File file){
        byte[] buffer = null;
        try {
            FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] b = new byte[1024];
            int n;
            while ((n = fis.read(b)) != -1) {
                bos.write(b, 0, n);
            }
            fis.close();
            bos.close();
            buffer = bos.toByteArray();
        }catch (FileNotFoundException e){
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }
        return buffer;
    }
}
复制代码

演示

当咱们编写完成以后,就来到了最关键的地方,测试了,内心还有点小激动,应该不会有BUG的

在这里插入图片描述

首先咱们输入http://localhost:8080/generatePdf,生成填充模板,生成新的PDF文件并合并文件,生成完成以后咱们会在项目的class目录下看到这个文件

在这里插入图片描述

打开咱们的文件,就能够看到,对应的数据信息,到这里有惊无险,就查最后一步签署合同了 在这里插入图片描述

到这里若是可以把签署的信息,填写到合同签署的位置上,那咱们就能够说大功告成了,咱们输入签署的地址http://localhost:8080/addContent,当咱们在目录下看到 签署测试.PDF的时候就说明咱们大功告成了

在这里插入图片描述 在这里插入图片描述 咱们能够看到对应的签署信息已经被咱们添加上去了,除了没有第三方认证,该有的功能都有了,太优秀了啊!

这个时候我不免想起了,李白那句 “仰天大笑出门去,我辈岂是蓬蒿人”,天晴了,雨停了,我以为我又行了,我都已经联想到产品小姐姐崇拜的小眼神了,魅力放光芒,请你不要再迷恋哥!别光喝酒啊,吃点菜啊,几个菜喝成这样 在这里插入图片描述

总结

这个功能,花费了小农三天的时候,若是这个功能已经可以在公司项目中正常使用了,以上就是这个功能的详细代码和说明,若是有疑问或者也在作这个功能的小伙伴能够留言,小农看到了会第一时间回复

若是你们以为不错,记得一键三连~

点赞过百,我就是怀双胞胎也出下一篇

我是牧小农,怕什么真理无穷,进一步有进一步的欢喜,你们加油!

相关文章
相关标签/搜索