基于IText7 PDF模板填充?

引入依赖

<dependency><groupId>com.itextpdf</groupId><artifactId>itext7-core</artifactId><version>8.0.1</version><type>pom</type>
</dependency>
<dependency><groupId>com.itextpdf</groupId><artifactId>bouncy-castle-adapter</artifactId><version>8.0.1</version>
</dependency>

模板填充工具

import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.forms.fields.PdfFormCreator;
import com.itextpdf.forms.fields.PdfFormField;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.*;
import com.itextpdf.kernel.pdf.annot.PdfAnnotation;
import com.itextpdf.kernel.pdf.annot.PdfWidgetAnnotation;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.muchenx.util.ImageCompressUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;/*** PDF 模板填充工具类(itext7)* <p>* 支持文本 及 图片 填充*/
@Slf4j
public final class PDFTemplateFillHandler {/*** 待填充的PDF模板文档*/private final transient PdfDocument templateDocument;/*** 填充后PDF文档数据*/private final transient ByteArrayOutputStream destByteOutStream;/*** 字体*/private transient PdfFont font;private PDFTemplateFillHandler(PdfDocument templateDocument, ByteArrayOutputStream destByteOutStream) {this.templateDocument = templateDocument;this.destByteOutStream = destByteOutStream;try {this.font = loadFont();} catch (IOException e) {log.warn("加载指定字体异常:{}", e.getMessage());}}public static PDFTemplateFillHandler loadTemplate(InputStream templateFileStream) {try {ByteArrayOutputStream dest = new ByteArrayOutputStream();PdfDocument pdfDocument = new PdfDocument(new PdfReader(templateFileStream), new PdfWriter(dest));return new PDFTemplateFillHandler(pdfDocument, dest);} catch (IOException e) {throw new RuntimeException(e);}}/*** 填充及返回填充后的数据** @param fillData - 待填充数据* @return - 填充后bytes数据*/public byte[] fill(Map<String, Object> fillData) {try {PdfAcroForm form = PdfAcroForm.getAcroForm(templateDocument, true);fillData.forEach((keyword, value) -> {Optional.ofNullable(form.getField(keyword)).ifPresent(templateFormField -> {if (value instanceof byte[]) {PdfArray pos = templateFormField.getWidgets().get(0).getRectangle();float x = pos.getAsNumber(0).floatValue();float y = pos.getAsNumber(1).floatValue();float width = pos.getAsNumber(2).floatValue() - x;float height = pos.getAsNumber(3).floatValue() - y;Rectangle rectangle = new Rectangle(x, y, width, height);PdfWidgetAnnotation widget = new PdfWidgetAnnotation(rectangle);PdfFormField formField = PdfFormCreator.createFormField(widget, templateDocument);PdfPage annotationPage = findAnnotationPage(keyword);if (annotationPage != null) {doFillFieldImage(annotationPage, formField, (byte[]) value);}} else {templateFormField.setValue(String.valueOf(value));if (font != null) {templateFormField.setFont(templateFormField.getFont());}}form.partialFormFlattening(keyword);});});form.flattenFields();} finally {if (templateDocument != null) {templateDocument.close();}}return destByteOutStream.toByteArray();}/*** 图片填充** @param newPage   - 当前页* @param formField - 表单文本域* @param imgBytes  - 图片文件字节数组*/private void doFillFieldImage(PdfPage newPage, PdfFormField formField, byte[] imgBytes) {Rectangle rtl = formField.getWidgets().get(0).getRectangle().toRectangle(); // 获取表单域的xy坐标PdfCanvas canvas = new PdfCanvas(newPage);ImageData img = ImageDataFactory.create(imgBytes);if (Float.compare(img.getWidth(), rtl.getWidth()) <= 0 && Float.compare(img.getHeight(), rtl.getHeight()) <= 0) {// 不处理canvas.addImageAt(img, rtl.getX(), rtl.getY(), true);} else {// 压缩图片。计算得到图片放缩的最大比例float scale = Math.max(img.getWidth() / rtl.getWidth(), img.getHeight() / rtl.getHeight());int imgWidth = Math.round(img.getWidth() / scale);int imgHeight = Math.round(img.getHeight() / scale);// 压缩图片byte[] compressImgBytes;try {compressImgBytes = ImageCompressUtils.resizeByThumbnails(imgBytes, imgWidth, imgHeight);} catch (IOException e) {throw new RuntimeException(e);}img = ImageDataFactory.create(compressImgBytes);canvas.addImageAt(img, rtl.getX(), rtl.getY(), true);}}/*** 根据表单域关键字查找当前关键字所在页对象(PdfPage)** @param keyword - 关键字* @return - page object*/private PdfPage findAnnotationPage(String keyword) {int pages = templateDocument.getNumberOfPages();for (int index = 1; index <= pages; index++) {PdfPage page = templateDocument.getPage(index);for (PdfAnnotation annotation : page.getAnnotations()) {PdfString title = annotation.getPdfObject().getAsString(PdfName.T);if (title != null && keyword.equals(String.valueOf(title))) {return page;}}}return null;}/*** 获取模板文件表单域关键字位置信息*/private Map<Integer, Map<String, float[]>> getFormKeywordsPos() {int pages = templateDocument.getNumberOfPages();Map<Integer, Map<String, float[]>> maps = new HashMap<>(pages);for (int index = 1; index <= pages; index++) {maps.putIfAbsent(index, new HashMap<>());PdfPage page = templateDocument.getPage(index);// 获取当前页的表单域int finalIndex = index;page.getAnnotations().forEach(anno -> {PdfString title = anno.getTitle();PdfArray rectangle = anno.getRectangle();float x = rectangle.getAsNumber(0).floatValue();float y = rectangle.getAsNumber(1).floatValue();float width = rectangle.getAsNumber(2).floatValue() - x;float height = rectangle.getAsNumber(3).floatValue() - y;maps.get(finalIndex).put(title.getValue(), new float[]{x, y, width, height});});}return maps;}/*** 加载字体*/private PdfFont loadFont() throws IOException {Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath*:/font/*.ttc");if (resources.length == 0) {return null;}PdfFontFactory.register(resources[0].getURL().getPath(), "SimSun");return PdfFontFactory.createRegisteredFont("SimSun", PdfEncodings.IDENTITY_H,PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED);}
}

填充实例

LocalDate now = LocalDate.now();
// 图片文件
ByteArrayOutputStream catOutStream = new ByteArrayOutputStream();
File img = ResourceUtils.getFile("classpath:cat.png");
InputStream catInStream = Files.newInputStream(img.toPath());
IOUtils.copy(catInStream, catOutStream);
Map<String, Object> data = new HashMap<>();
data.put("username", "喵星人");
data.put("gender", "女");
data.put("nation", "狗");
data.put("school", "狗族大学");
data.put("describe", "千百年来,地球上一直住着一种外星生物,名叫喵星人。它们从遥远的喵星来到地球,化身为猫科动物,分散在世界每个角落。萌萌的外表,加上机智聪明的头脑,轻易就得到人类的宠爱。");
data.put("describe1", "千百年来,地球上一直住着一种外星生物,名叫喵星人。它们从遥远的喵星来到地球,化身为猫科动物,分散在世界每个角落。萌萌的外表,加上机智聪明的头脑,轻易就得到人类的宠爱。");
data.put("content", "千百年来,地球上一直住着一种外星生物,名叫喵星人。它们从遥远的喵星来到地球,化身为猫科动物,分散在世界每个角落。萌萌的外表,加上机智聪明的头脑,轻易就得到人类的宠爱。");
data.put("sign", catOutStream.toByteArray());
data.put("avatar", catOutStream.toByteArray());
data.put("year", String.valueOf(now.getYear()));
data.put("month", String.valueOf(now.getMonthValue()));
data.put("day", String.valueOf(now.getDayOfMonth()));String homePath = FileSystemView.getFileSystemView().getHomeDirectory().getAbsolutePath();
File template = ResourceUtils.getFile("classpath:fill_template.pdf");
byte[] filledDataBytes = PDFTemplateFillHandler.loadTemplate(Files.newInputStream(template.toPath())).fill(data);
IOUtils.write(filledDataBytes, Files.newOutputStream(Paths.get(homePath + "/" + System.nanoTime() + ".pdf")));

基于IText7 的 PDF表单域模板填充

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/200696.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mybatis XML 配置文件

我们刚开始就有说Mybatis 的开发有两种方式: 1.注释 2.XML 注解和 XML 的方式是可以共存的 我们前面说的都是注释的方式,接下来是XML方式 XML的方式分为三步 : 1.配置数据库(配在 application.yml 里面) 这个跟注释的配置是一样的,username应该都是一样的,password记得写…

1. 小游戏(贪心)

题干&#xff1a; 谷同学很喜欢玩计算机游戏&#xff0c;特别是战略游戏&#xff0c;但是有时他不能尽快找到解所以常常感到很沮丧。现在面临如下问题&#xff1a;他必须在一个中世纪的城堡里设防&#xff0c;城堡里的道路形成一棵无向树。要在结点上安排最少的士兵使得他们可以…

MySql配置主从服务器复制数据库数据

主从服务器MySql版本尽可能一致。如果不一致的情况此文没考虑。 主服务器必须开启二进制日志文件。查看是否开启&#xff1a; SHOW VARIABLES LIKE log_bin; 开启方法&#xff0c;只需要在配置文件添加如下配置&#xff1a; [mysqld] log-binmysql-bin 1、MySql主服务器…

6.Eclipse里下载Subclipse插件

方法一&#xff1a;从Eclipse Marketplace里面下载 具体操作&#xff1a;打开Eclipse --> Help --> Eclipse Marketplace --> 在Find中输入subclipse搜索 --> 找到subclipse点击install 方法二&#xff1a;从Install New Software里下载 具体操作&#xff1a;打开…

高压电网过电压在线监测系统的研究

摘要 由于高压输电线路纵横延伸几十甚至几百千米&#xff0c;处在不同的环境中。因此高压输电线路受所处地理环境和气候影响很大&#xff0c;每年电网停电事故主要由线路事故引起。传统输电线路检查主要依靠运行维护人员周期性巡视&#xff0c;虽能发现设备隐患&#xff0c;但…

Qt与Sqlite3

操作流程&#xff1a; (1)与数据库连接 (2)进行增删改查操作 (3)关闭数据库 示例&#xff1a; 参考&#xff1a;Qt 操作SQLite数据库_qt sqlite数据库操作_houxian1103的博客-CSDN博客 再谈QSqlQuery::exec: database not open问题的解决_qt database not open-CSDN博客…

JavaScript中的递归函数

再此之前呢&#xff0c;我们要先知道什么是递归函数&#xff1f; 一.什么是递归函数&#xff1f; 递归函数&#xff1a;是一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法&#xff0c;也是一种循环运算的一种算法模式。这种技术可以用来解决一些复杂的问题&…

OSPF浅析

一、预习&#xff1a; 1、优点&#xff1a; 是一种典型的链路状态路由协议&#xff0c;协议号89&#xff0c;把大型网络分隔为多个较小、可管理的单元&#xff1a;Area a.减少LSA泛洪范围&#xff0c;有效地把拓朴变化 控制在区域内&#xff0c;达到网络优化的目的…

nodejs微信小程序+python+PHP本科生优秀作业交流网站的设计与实现-计算机毕业设计推荐

通过软件的需求分析已经获得了系统的基本功能需求&#xff0c;根据需求&#xff0c;将本科生优秀作业交流网站功能模块主要分为管理员模块。管理员添加系统首页、个人中心、用户管理、作业分类管理、作业分享管理、论坛交流、投诉举报、系统管理等操作。 随着信息化社会的形成…

【面试HOT200】二叉树的构建二叉搜索树篇

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了秋招面试的&#xff0c;整理期间苛求每个知识点&#xff0c;平衡理解简易度与深入程度。 &#x1f970;来源&#xff1a;材料主要源于【CodeTopHot200】进行的&#xff0c;每个知识点的修正和深入主要参…

Leetcode 77 组合

题意理解&#xff1a; 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 如&#xff1a;n3,k2,则有&#xff1a;12 13 23 一般&#xff0c;我们使用回溯法来解决组合问题。 组合问题没有顺序要求&#xff0c;所以 12 21 是同一个组合&#xff08;如…

【Linux知识点汇总】01 Linux常见版本

常见的Linux发行版&#xff08;Distribution&#xff0c;简称为distro&#xff09;有很多&#xff0c;它们在用途、包管理系统、默认桌面环境等方面可能有所不同 CentOS 特点&#xff1a; 企业级稳定性&#xff0c;是Red Hat Enterprise Linux&#xff08;RHEL&#xff09;的开…

黑苹果配置清单

手里的MacBookPro已经快沦为电子垃圾了&#xff0c;平时用MacOS比较多&#xff0c;Window用的比较少&#xff0c;而苹果电脑的价格不管是MacBookPro还是MacMini丐版的便宜但是面对现在Window动不动就64g内存的情况就显得微不足道了&#xff0c;高配的价格直接把我劝退&#xff…

PostgreSql HOT 技术

摘自唐成的《PostgreSQL修炼之道&#xff1a;从小工到专家&#xff08;第2版&#xff09;》。 一、概述 因为多版本的原因&#xff0c;当 PostgreSQL 中更新一行时&#xff0c;实际上原数据行并不会被删除&#xff0c;只是插入了一个新行。如果表上有索引&#xff0c;而更新的…

Leetcode题库(数据库合集)_ 难度:中等

目录 难度&#xff1a;中等1.股票的资本损益2. 当选者3. 页面推荐4. 2016年的投资5. 买下所有产品的人6. 电影评分6. 确认率7. 按分类统计薪水8. 餐馆营业额的变化增长8. 即时食物配送 ①9. 至少有5名直系下属的经理10. 游戏玩法分析11. 好友申请&#xff1a;谁有最多的好友12.…

Oracle官网 账号及密码 -- 笔记

亲测有效: 账户&#xff1a;3028064308qq.com 密码&#xff1a;OraclePassword123! 引用: Oracle官网 账号及密码_oracle账号-CSDN博客 如有失效,请帮忙留言,及时删除!

Qlik 成为网络犯罪的焦点

研究人员警告说&#xff0c;Cactus 勒索软件组织正在利用 Qlik Sense 数据可视化、探索和监控解决方案中的关键漏洞来获得对企业网络的初始访问权限。 今年八月下旬&#xff0c;Qlik Sense 开发人员 针对影响 Windows 版本平台的两个关键漏洞发布了补丁 。 其中一个漏洞 CVE-…

图数据库知识点9 | 大数据框架与图数据架构异同

开门见山&#xff0c;直奔主题&#xff0c;接续前面的知识点&#xff1a; 【图数据库知识点1|图数据库与关系型数据库的区别&#xff1f;】 【图数据库知识点2 | 图思维方式】 【图数据库知识点3 | 图数据库解决了什么问题&#xff1f;】 【图数据库知识点4 | 图计算与图数…

基于Echarts的大数据可视化模板:智慧交通管理

目录 引言智慧交通管理的重要性ECharts在智慧交通中的作用智慧交通管理系统架构系统总体架构数据收集与处理Echarts与大数据可视化Echarts库以及其在大数据可视化领域的应用优势开发过程和所选设计方案模板如何满足管理的特定需求模板功能与特性深入解析模板提供的各项功能模板…

javaScript(二):javaScript基础语法

文章目录 1、javaScript变量定义2、JavaScript五种数据类型3、JavaScript常用运算符3.1、算术运算符3.2、赋值运算符3.3、比较运算符3.4、逻辑运算符 1、javaScript变量定义 定义变量 ES5定义变量 var 变量名 变量值;ES6定义变量 let 变量名 变量值; const 常量名 常量值;…