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

 更新时间:2025年07月29日 11:02:17   作者:代码萌新知  
这篇文章主要介绍了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模版填充表单内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot项目实战之加载和读取资源文件

    SpringBoot项目实战之加载和读取资源文件

    在项目的开发中,我们知道的是SpringBoot框架大大减少了我们的配置文件,但是还是留下了一个application.properties文件让我们可以进行一些配置,下面这篇文章主要给大家介绍了关于SpringBoot项目实战之加载和读取资源文件的相关资料,需要的朋友可以参考下
    2021-10-10
  • 浅谈Timer和TimerTask与线程的关系

    浅谈Timer和TimerTask与线程的关系

    下面小编就为大家带来一篇浅谈Timer和TimerTask与线程的关系。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • SpringSecurity 自定义认证登录的项目实践

    SpringSecurity 自定义认证登录的项目实践

    本文主要介绍了SpringSecurity 自定义认证登录的项目实践,以手机验证码登录为例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-08-08
  • Java中Prime算法的原理与实现详解

    Java中Prime算法的原理与实现详解

    Prime算法是一种穷举查找算法来从一个连通图中构造一棵最小生成树。本文主要为大家介绍了Java中Prime算法的原理与实现,感兴趣的可以学习一下
    2022-07-07
  • JAVA数组中五种常见排序方法整理汇总

    JAVA数组中五种常见排序方法整理汇总

    本文给大家分享五种常用的Java数组排序方法整理,每种方法结合示例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2025-05-05
  • SpringBoot使用OpenCV的超详细步骤

    SpringBoot使用OpenCV的超详细步骤

    最近有个项⽬需要对图⽚图像进⾏处理,使⽤到了开源框架OpenCV,所以下面这篇文章主要给大家介绍了关于SpringBoot使用OpenCV的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-04-04
  • java实现俄罗斯方块游戏

    java实现俄罗斯方块游戏

    这篇文章主要为大家详细介绍了java实现俄罗斯方块游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-02-02
  • 详解Java中CountDownLatch异步转同步工具类

    详解Java中CountDownLatch异步转同步工具类

    今天给大家带来的是关于Java的相关知识,文章围绕着CountDownLatch异步转同步工具类展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下
    2021-06-06
  • springBoot启动报错log4j冲突的解决方案

    springBoot启动报错log4j冲突的解决方案

    这篇文章主要介绍了springBoot启动报错log4j冲突的解决方案,具有很好的参考价值,希望对大家有所帮助。
    2021-07-07
  • Java简单实现session保存到redis的方法示例

    Java简单实现session保存到redis的方法示例

    这篇文章主要介绍了Java简单实现session保存到redis的方法,结合实例形式分析了Java将session存入redis缓存服务器的相关设置、实现技巧与操作注意事项,需要的朋友可以参考下
    2018-05-05

最新评论