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

2025-07-29 20:50

本文主要是介绍java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)》:本文主要介绍java中pdf模版填充表单踩坑的相关资料,OpenPDF、iText、PDFBox是三...

准备Pdf模版

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

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

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

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

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

方法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, HttpServlewww.chinasem.cntResponse 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 resourcesphp = 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)遇到China编程的问题

由于业务需要下载协议和预览协议,移动端在线预览和下载时出现乱码情况,暂时没有解决问题。于是又试了方法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; fiwww.chinasem.cnlename=" +
                    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)这php里报空指针异常,但是data里每个都有值,针对每个值一一排查发现竟然有的值能填充成功有的值不行,一次排查后发现跟模版的表单有问题,最终针对pdf模版的表单域删除重新创建就可以了。但是上线到测试服后才发现又有问题联想浏览器预览的pdf表单填的都是空值,下载又可以了。我不死心又拿着新建表单域的pdf模板去试方法2和方法3发现两个方法的毛病解决了,所以最终我使用的方法2。至于方法1和方法4的问题实在没有时间来验证和排查了。

总结

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

这篇关于java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1155527

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

一篇文章彻底搞懂macOS如何决定java环境

《一篇文章彻底搞懂macOS如何决定java环境》MacOS作为一个功能强大的操作系统,为开发者提供了丰富的开发工具和框架,下面:本文主要介绍macOS如何决定java环境的相关资料,文中通过代码... 目录方法一:使用 which命令方法二:使用 Java_home工具(Apple 官方推荐)那问题来了,

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

Python版本信息获取方法详解与实战

《Python版本信息获取方法详解与实战》在Python开发中,获取Python版本号是调试、兼容性检查和版本控制的重要基础操作,本文详细介绍了如何使用sys和platform模块获取Python的主... 目录1. python版本号获取基础2. 使用sys模块获取版本信息2.1 sys模块概述2.1.1

Java中的.close()举例详解

《Java中的.close()举例详解》.close()方法只适用于通过window.open()打开的弹出窗口,对于浏览器的主窗口,如果没有得到用户允许是不能关闭的,:本文主要介绍Java中的.... 目录当你遇到以下三种情况时,一定要记得使用 .close():用法作用举例如何判断代码中的 input