java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > java pdf模版填充表单

java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)

作者:代码萌新知

这篇文章主要介绍了java中pdf模版填充表单踩坑的相关资料,OpenPDF、iText、PDFBox是三种常用的 PDF 处理库,它们各自具有独特的优势和特点,同时也存在一些局限性和差异,文中通过代码介绍的非常详细,需要的朋友可以参考下

准备Pdf模版

Adobe Acrobat Dc打开pdf模版,点击准备表单出现自动识别的表单域,如果需要自定义表单域可点击顶栏标记位置自定义表单位置。

双击表单域,出现表单设置框,可设置表单字段名称(与代码相关),位置、以及外观字体以及大小等(默认值),最后点击保存

方法1:itextpdf7填充表单

(1)加入依赖

<dependency>
   <groupId>com.itextpdf</groupId>
   <artifactId>itext7-core</artifactId>
   <version>7.1.19</version>
   <type>pom</type>
</dependency>
<dependency>
   <groupId>com.itextpdf</groupId>
   <artifactId>itext-asian</artifactId>
   <version>5.2.0</version>
</dependency>
   <groupId>com.itextpdf</groupId>
   <artifactId>font-asian</artifactId>
   <version>7.1.19</version>
   <scope>pom</scope>
</dependency>

(2)代码

public void exportContractPdf(EnterpriseInfoVo params, HttpServletResponse response) {
       String inputFileName = "template/test.pdf";//模版路径
       ByteArrayOutputStream baos = new ByteArrayOutputStream();
       ClassPathResource classPathResource = new ClassPathResource(inputFileName);
       try (InputStream inputStream = classPathResource.getInputStream();PdfReader reader = new PdfReader(inputStream);
           PdfWriter writer = new PdfWriter(baos);
           PdfDocument pdfDoc = new PdfDocument(reader, writer)) {
           PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
           ClassPathResource fontResource = new ClassPathResource("ttf/STSong.ttf"); //字体路径
           InputStream fontStream = fontResource.getInputStream();
           ByteArrayOutputStream buffer = new ByteArrayOutputStream();
           byte[] data1= new byte[1024];
           int nRead;
           while ((nRead = fontStream.read(data1, 0, data1.length)) != -1) {
               buffer.write(data1, 0, nRead);
           }
           buffer.flush();
           byte[] fontBytes = buffer.toByteArray();
           PdfFont font = PdfFontFactory.createFont(
                   fontBytes,
                   PdfEncodings.IDENTITY_H,
                   true
           );
           form.getFormFields().values().forEach(field -> field.setFont(font));
           Map<String, Object> data = new HashMap<>();
           data.put("param1", params.getParam1());
           data.put("param2",params.getParam2());
           for (Map.Entry<String, Object> entry : data.entrySet()) {
               PdfFormField field = form.getField(entry.getKey());
               if (field != null) {
                   field.setValue(entry.getValue().toString());
               }
           }
           form.flattenFields();
           pdfDoc.close();
            response.setContentType("application/pdf");
       response.setHeader("Content-Disposition", "attachment; filename=" +
               Optional.ofNullable(enterpriseInfoVo.getContract_id())
                       .orElse(URLEncoder.encode(enterpriseInfoVo.getFirm_name(), StandardCharsets.UTF_8.name())) + ".pdf");
       response.getOutputStream().write(baos.toByteArray());

       } catch (Exception e) {
           //异常处理
       }

   }

(3)遇到的问题

本来这个方法没有任何问题,但是 由于突然要将系统迁移到另一个服务器,部署上去后 PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
这行报错 com/itextpdf/io/font/cmap/UniJIS-UTF16-H was not found.该路径下没有这文件但是font-asian有这个文件,本地运行正常,按照网上的方法加入itext-asian依赖,或者自己导入resource资源都试过了,还是不行。研究了很久正式服就是走不通,但是之前服务器的正式服能就走,捣鼓很久一无所获,所以决定换方法2。

方法2:pdfbox填充表单

(1)加入依赖

<dependency>
   <groupId>org.apache.pdfbox</groupId>
   <artifactId>pdfbox</artifactId>
   <version>2.0.24</version>
</dependency>

(2)代码

public void exportContractPdf( EnterpriseInfoVo params, HttpServletResponse response) throws ParseException {
   String inputFileName = "template/test.pdf";
   try (PDDocument document = PDDocument.load(new ClassPathResource(inputFileName).getInputStream());
        ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
       PDAcroForm form = document.getDocumentCatalog().getAcroForm();
       InputStream fontStream = new ClassPathResource("ttf/STSong.ttf").getInputStream();
       PDType0Font font = PDType0Font.load(document, fontStream, false);
       PDResources resources = new PDResources();
       resources.put(COSName.getPDFName("F1"), font);
       form.setDefaultResources(resources);
       Map<String, Object> data = new HashMap<>();
       data.put("param1", params.getParam1());
       data.put("param2", params.getParam2());
       for (PDField field : form.getFields()) {
           if (data.containsKey(field.getFullyQualifiedName())) {
               if (field instanceof PDTextField) { 
                   PDTextField textField = (PDTextField) field;
                   textField.setDefaultAppearance("/F1 10 Tf 0 g");  // 可能导致文件过大
                   String fullyQualifiedName = field.getFullyQualifiedName();
                   Object o = data.get(fullyQualifiedName);
                   textField.setValue(o.toString()); // 设置字段值
                   textField.setReadOnly(true); // 将字段设置为只读
               }
           }
       }
       form.flatten();
       document.save(baos);
        response.setContentType("application/pdf");
           response.setHeader("Content-Disposition", "attachment; filename=" +
                   Optional.ofNullable(params.getId())
                           .orElse(URLEncoder.encode(enterpriseInfoVo.getName(), StandardCharsets.UTF_8.name())) + ".pdf");
           response.getOutputStream().write(baos.toByteArray());

   } catch (Exception e) {
       //异常处理
   }

}

(3)遇到的问题

由于业务需要下载协议和预览协议,移动端在线预览和下载时出现乱码情况,暂时没有解决问题。于是又试了方法3。

方法3:openpdf填充表单

(1)加入依赖

<dependency>
  <groupId>com.github.librepdf</groupId>
  <artifactId>openpdf</artifactId>
   <version>1.3.26</version>
</dependency>

(2)代码

public void exportContractPdf(EnterpriseInfoVo params, HttpServletResponse response) throws ParseException {
       String inputFileName = "template/test.pdf";
       try (InputStream templateStream = new ClassPathResource(inputFileName).getInputStream();
            ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
           com.lowagie.text.pdf.PdfReader reader = new com.lowagie.text.pdf.PdfReader(templateStream);
           PdfStamper stamper = new PdfStamper(reader, baos);
           AcroFields form = stamper.getAcroFields();
           BaseFont font = BaseFont.createFont(
                   new ClassPathResource("ttf/STSong.ttf").getPath(),
                   BaseFont.IDENTITY_H,
                   BaseFont.EMBEDDED
           );
           Map<String, Object> data = new HashMap<>();
           data.put("param1", params.getParam1());
            data.put("param2", params.getParam2());  
           for (Object fieldName : form.getFields().keySet()) {
               if (data.containsKey(fieldName.toString())) {
                   form.addSubstitutionFont(font);               
//                    form.setFieldProperty(fieldName.toString(), "textfont", font, null); //这两行代码加入后直接从200k激增到7.5M
//                    form.setFieldProperty(fieldName.toString(),"textsize",Float.valueOf(10f),null);
                   Object value = data.get(fieldName);
                   form.setField(fieldName.toString(), value.toString());
                   form.setFieldProperty(fieldName.toString(), "setfflags", PdfFormField.FF_READ_ONLY, null);
               }
           }
           stamper.setFormFlattening(true);
           stamper.close();
           reader.close();
           response.setContentType("application/pdf");
           response.setHeader("Content-Disposition", "attachment; filename=" +
                   Optional.ofNullable(params.getId())
                           .orElse(URLEncoder.encode(params.getName(), StandardCharsets.UTF_8.name())) + ".pdf");
           response.getOutputStream().write(baos.toByteArray());

       } catch (Exception e) {
           //异常处理
       }
   }

(3)遇到的问题

刚开始导出模版出现填充文字大小不一,字体样式不统一的情况,所以使用了//                    form.setFieldProperty(fieldName.toString(), "textfont", font, null); //这两行代码加入后直接从200k激增到7.5M
//                    form.setFieldProperty(fieldName.toString(),"textsize",Float.valueOf(10f),null);这两行代码的加入直接导致一页的pdf文件从200k到7.5M,查找原因说是字体文件重复嵌入,试了很多方法总是出现这样那样的问题,所以到方法4。

方法4:itextpdf5填充表单

(1)加入依赖

<dependency>
   <groupId>com.itextpdf</groupId>
   <artifactId>itextpdf</artifactId>
   <version>5.5.13.2</version>
</dependency>
<dependency>
   <groupId>com.itextpdf.tool</groupId>
   <artifactId>xmlworker</artifactId>
   <version>5.5.13.2</version>
</dependency>
<dependency>
   <groupId>com.itextpdf</groupId>
   <artifactId>itext-asian</artifactId>
   <version>5.2.0</version>
</dependency>

<dependency>
   <groupId>wiki.xsx</groupId>
   <artifactId>x-easypdf-pdfbox</artifactId>
   <version>2.11.6</version>
</dependency>

<dependency>
   <groupId>org.xhtmlrenderer</groupId>
   <artifactId>flying-saucer-pdf-itext5</artifactId>
   <version>9.1.22</version>
   <exclusions>
       <exclusion>
           <artifactId>itextpdf</artifactId>
           <groupId>com.itextpdf</groupId>
       </exclusion>
   </exclusions>
</dependency>

(2)代码

   public void exportContractPdf(EnterpriseInfoVo params, HttpServletResponse response) {
        String inputFileName = "template/test.pdf";
             try {     
            response.setContentType("application/pdf");
            response.setHeader("Content-Disposition", "attachment; filename=" +
                    Optional.ofNullable(params.getId())
                            .orElse(URLEncoder.encode(params.getName(), StandardCharsets.UTF_8.name())) + ".pdf");
            Map<String, String> data = new HashMap<>();
            data.put("param1", params.getParam1());
            data.put("param2", params.getParam2());      
            XEasyPdfHandler.Document
                    .load( new ClassPathResource(inputFileName).getInputStream())
                    .formFiller()
                    .setFontPath(getTempFontPath("ttf/STSong.ttf"))
                    .enableCompress()
                    .enableFixForm()
                    .enableReadOnly()
                    .fill(data)
                    .finish(response.getOutputStream());

        } catch (Exception e) {
           //异常处理
        }
    }

(3)遇到的问题

刚开始出现.fill(data)这里报空指针异常,但是data里每个都有值,针对每个值一一排查发现竟然有的值能填充成功有的值不行,一次排查后发现跟模版的表单有问题,最终针对pdf模版的表单域删除重新创建就可以了。但是上线到测试服后才发现又有问题联想浏览器预览的pdf表单填的都是空值,下载又可以了。我不死心又拿着新建表单域的pdf模板去试方法2和方法3发现两个方法的毛病解决了,所以最终我使用的方法2。至于方法1和方法4的问题实在没有时间来验证和排查了。

总结

到此这篇关于java中pdf模版填充表单踩坑实战的文章就介绍到这了,更多相关java pdf模版填充表单内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文