PDF模板填充,基于IText5(改进版)

原版:PDF模板填充,基于IText5-CSDN博客

\import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;/*** PDF模板填充,基于itext-pdf-v5*/
public class PDFTemplateFiller {/*** 模板*/private transient PdfReader reader;/*** 字体*/private BaseFont baseFont;/*** 是否添加页码*/private boolean addPageNumber;/*** 页码字体大小* {@link #addPageNumber}为true时有效*/private float pageNumberFontSize = 14F;/*** 页码左边样式字符* {@link #addPageNumber}为true时有效*/private String pageNumberLeft = "";/*** 页码邮编样式字符* {@link #addPageNumber}为true时有效*/private String pageNumberRight = "";/*** 填充模板,返回填充后文件** @param fillData - 待填充数据* @return - 填充后文件字节数据*/public byte[] fill(Map<String, Object> fillData) throws DocumentException, IOException {ByteArrayOutputStream out = new ByteArrayOutputStream();PdfStamper stamper = new PdfStamper(reader, out);AcroFields form = stamper.getAcroFields();// 设置字体form.addSubstitutionFont(baseFont);// 执行填充doFill(stamper, form, fillData);// 清除表单域可编辑状态stamper.setFormFlattening(true);stamper.close();reader.close();// 是否需要添加页码,则需要重新将填充后的PDF文档加载进reader中byte[] filedData = out.toByteArray();if (addPageNumber) {this.reader = new PdfReader(filedData);return pageNumber();}return filedData;}/*** 添加页码** @return* @throws DocumentException*/public byte[] pageNumber() throws DocumentException {ByteArrayOutputStream out = new ByteArrayOutputStream();Document document = new Document();PdfWriter writer = PdfWriter.getInstance(document, out);document.open();int pages = reader.getNumberOfPages();for (int i = 1; i <= pages; i++) {document.newPage();doAadPageNumber(writer, i);PdfImportedPage page = writer.getImportedPage(reader, i);writer.getDirectContent().addTemplate(page, 0, 0);}document.close();reader.close();return out.toByteArray();}/*** 是否添加页码(默认:否)** @param isAddPageNumber - true-添加/false-不添加*/public PDFTemplateFiller pageNumber(boolean isAddPageNumber) {this.addPageNumber = isAddPageNumber;return this;}/*** <pre>* 设置字体(默认:STSong-Light)* 通过 {@link com.itextpdf.text.pdf.BaseFont#createFont(java.lang.String, java.lang.String, boolean)}创建* </pre>** @param baseFont - 待设值字体*/public PDFTemplateFiller font(BaseFont baseFont) {this.baseFont = baseFont;return this;}/*** 页码样式设置* <p>* {@link #addPageNumber}为true时有效** @param pageNumberLeft  - 页码左边样式字符* @param pageNumberRight - 页码邮编样式字符*/public PDFTemplateFiller pageNumberStyle(String pageNumberLeft, String pageNumberRight) {this.pageNumberLeft = pageNumberLeft;this.pageNumberRight = pageNumberRight;return this;}/*** 根据表单域字段名查找其对应所在页码** @param form      - 表单对象* @param fieldName - 表单域属性名* @return - 找到返回具体页码(起始页为1);否则返回-1*/private int findPageNumber(AcroFields form, String fieldName) {List<AcroFields.FieldPosition> positions = form.getFieldPositions(fieldName);if (positions == null || positions.isEmpty()) {return -1;}return positions.get(0).page;}/*** 填充文本*/private void doFill(PdfStamper stamper, AcroFields form, Map<String, Object> fillData) throws DocumentException, IOException {for (Map.Entry<String, Object> entry : fillData.entrySet()) {if (entry.getValue() instanceof byte[]) {doFillImage(stamper, entry.getKey(), (byte[]) entry.getValue());} else {form.setField(entry.getKey(), String.valueOf(entry.getValue()));}}}/*** 填充图片*/private void doFillImage(PdfStamper stamper, String fieldName, byte[] image) throws DocumentException, IOException {AcroFields form = stamper.getAcroFields();List<AcroFields.FieldPosition> positions = form.getFieldPositions(fieldName);if (positions != null && !positions.isEmpty()) {AcroFields.FieldPosition position = positions.get(0);Rectangle rectangle = position.position;com.itextpdf.text.Image img = com.itextpdf.text.Image.getInstance(image);// 根据域的大小缩放图片img.scaleToFit(rectangle.getWidth(), rectangle.getHeight());img.setAbsolutePosition(rectangle.getLeft(), rectangle.getBottom());stamper.getOverContent(position.page).addImage(img);}}/*** 添加页码*/private void doAadPageNumber(PdfWriter writer, int pageNumber) {PdfContentByte contentByte = writer.getDirectContent();contentByte.beginText();contentByte.setFontAndSize(baseFont, pageNumberFontSize);Rectangle rectangle = writer.getPageSize();// 页码的 横轴 坐标 居中float x = (rectangle.getLeft() + rectangle.getRight()) / 2;contentByte.showTextAligned(Element.ALIGN_CENTER, String.format("%s%d%s", pageNumberLeft, pageNumber, pageNumberRight), x, 20, 0);contentByte.endText();}/*** 合并多个PDF文件到一个PDF中** @param pdfs - 待合并的PDF 文件* @return byte - 合并后的PDF流*/public static byte[] merge(File... pdfs) throws Exception {PdfReader[] array = new PdfReader[pdfs.length];for (int i = 0; i < pdfs.length; i++) {array[i] = new PdfReader(pdfs[i].getAbsolutePath());}return merge(array);}/*** 合并多个PDF文件到一个PDF中** @param pdfs - 待合并的PDF 文件* @return byte - 合并后的PDF流*/public static byte[] merge(byte[]... pdfs) throws Exception {PdfReader[] array = new PdfReader[pdfs.length];for (int i = 0; i < pdfs.length; i++) {array[i] = new PdfReader(pdfs[i]);}return merge(array);}/*** 合并多个PDF文件到一个PDF中** @param pdfs - 待合并的PDF 文件* @return byte - 合并后的PDF流*/public static byte[] merge(PdfReader... pdfs) throws Exception {ByteArrayOutputStream out = new ByteArrayOutputStream();Document document = new Document();PdfCopy copy = new PdfCopy(document, out);document.open();for (PdfReader reader : pdfs) {for (int i = 1, pages = reader.getNumberOfPages(); i <= pages; i++) {copy.addPage(copy.getImportedPage(reader, i));}reader.close();}copy.close();document.close();return out.toByteArray();}public static PDFTemplateFiller load(InputStream pdfTemplate) throws IOException, DocumentException {return new PDFTemplateFiller(new PdfReader(pdfTemplate));}public static PDFTemplateFiller load(File pdfTemplate) throws IOException, DocumentException {return new PDFTemplateFiller(new PdfReader(pdfTemplate.getAbsolutePath()));}public static PDFTemplateFiller load(byte[] pdfTemplate) throws IOException, DocumentException {return new PDFTemplateFiller(new PdfReader(pdfTemplate));}public static PDFTemplateFiller load(ByteArrayOutputStream pdfTemplate) throws IOException, DocumentException {return load(pdfTemplate.toByteArray());}private PDFTemplateFiller(PdfReader pdfReader) throws DocumentException, IOException {this.reader = pdfReader;this.baseFont = getDefaultFont();this.addPageNumber = false;}/*** 获取默认的字体(宋体)*/private BaseFont getDefaultFont() throws DocumentException, IOException {return BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);}}

测试demo

public static void main(String[] args) throws Exception {String templatePath = "/path/模板.pdf";byte[] avatar = IOUtils.toByteArray(Files.newInputStream(Paths.get("/path/avatar.jpg")));Map<String, Object> fillData = new HashMap<>();fillData.put("name", "Ian");fillData.put("gender", "男");fillData.put("addr", "贵州省贵阳市");fillData.put("avatar", avatar);fillData.put("avatar1", avatar);String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));byte[] bytes = PDFTemplateFiller.load(Files.newInputStream(Paths.get(templatePath))).pageNumber(true).pageNumberStyle("-< ", " >-").fill(fillData);write(bytes, "/path/" + date + ".pdf");File dir = new File("/path/pdfdata/");File[] files = dir.listFiles((file, name) -> name.endsWith(".pdf"));assert files != null;byte[] bytes1 = PDFTemplateFiller.merge(files);write(bytes1, "/path/merged" + date +".pdf");}static void write(byte[] bytes, String dest) throws IOException {IOUtils.write(bytes, Files.newOutputStream(Paths.get(dest)));}

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

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

相关文章

MySQL数据库学习二

2 MySQL InnoDB 锁的基本类型 https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html 官网把锁分成了 8 类。所以我们把前面的两个行级别的锁&#xff08;Shared and ExclusiveLocks&#xff09;&#xff0c;和两个表级别的锁&#xff08;Intention Locks&#xff09;…

记录:开始学习网络安全

本文持续更新学习进度 背景 在私企干了5年虚拟化、云原生相关的运维&#xff0c;学到了很多&#xff0c;但不成体系。老板是清华毕业法国留学在德勤干过&#xff0c;最后回国创业的野路子。我工作是为了更好的生活&#xff0c;我挺担心老板因为家庭变故或者炒个原油宝&#x…

使用Collectors.toMap()报NullPointerException

使用Collectors.toMap()报NullPointerException 文章目录 使用Collectors.toMap()报NullPointerException问题代码问题分析 记录下最近工作中在使用 stream().collect(Collectors.toMap())出现的空指针问题 问题代码 Test public void test2() {List<Student> studentL…

游泳耳机到底怎么选?一文说全游泳耳机哪个好!

对于一个热爱游泳的人&#xff0c;每次游泳都希望找到一种好的方式来打发时间&#xff0c;同时也希望能够得到一些锻炼。在尝试了各种游泳装备之后&#xff0c;发现游泳耳机是一个非常不错的选择&#xff0c;不过市面上的游泳耳机鱼龙混杂&#xff0c;所以游泳耳机的选择需要综…

深度学习|2.11 向量化vectorization

2.11 向量化的作用 向量化可以使得向量中的每一个维度的数据进行并行计算&#xff0c;从而加快了神经网络的计算速度。 验证 其他

基于Python的新闻推荐平台:网络爬虫与推荐算法实现

项目设计目的 项目旨在开发一个基于Python的新闻推荐平台&#xff0c;通过网络爬虫实时抓取新闻数据&#xff0c;并利用推荐算法为用户提供个性化的新闻推荐服务。通过该平台&#xff0c;用户可以快速获取自己感兴趣的新闻内容&#xff0c;提高用户的阅读体验和粘性。 功能需…

超市订单管理系统

比较简单的超市订单管理系统

4.28 构建onnx结构模型-Unfold

前言 构建onnx方式通常有两种&#xff1a; 1、通过代码转换成onnx结构&#xff0c;比如pytorch —> onnx 2、通过onnx 自定义结点&#xff0c;图&#xff0c;生成onnx结构 本文主要是简单学习和使用两种不同onnx结构&#xff0c; 下面以 Unfold 结点进行分析 方式 方法…

电子书推荐|VMware 替代与升级攻略:技术路线、产品对比与用户实践

在进行 VMware 国产化替代时&#xff0c;您是否会遇到以下问题&#xff1a; 如何实现 VMware 整体架构/部分组件替换&#xff1f;是否可以不仅“为替换而替换”&#xff0c;而是同时实现架构的升级&#xff0c;带来更多业务价值&#xff1f;哪些国产方案具备 VMware 同等能力&…

vue3+ts+vite自定义组件上传npm流程

1. 创建项目 npm create vite 这里踩坑点&#xff1a; 运行vite生成的vue项目时报错“SyntaxError: Unexpected token ?? at “ 是因为node版本过低 电脑为windows11系统&#xff0c;我当时使用的版本node版本是14.21.3&#xff0c;如下图&#xff0c;后边安装了nvm版本…

PyTorch中常用的工具(3)TensorBoard

文章目录 前言3 可视化工具3.1 TensorBoard 前言 在训练神经网络的过程中需要用到很多的工具&#xff0c;最重要的是数据处理、可视化和GPU加速。本章主要介绍PyTorch在这些方面常用的工具模块&#xff0c;合理使用这些工具可以极大地提高编程效率。 由于内容较多&#xff0c…

【解决】电脑上的WIFI图标不见了咋整?

相信不少同学都遇到过这种情况&#xff1a;电脑上的wifi图标莫名不见了&#xff0c;甚至有时候还是在使用的中途突然断网消失的。 遇到这种情况一般有两种解决方案&#xff1a; 1. 在开机状态下长按电源键30秒以上 这种办法应该是给主板放电&#xff0c;一般应用在wifi6上面。…

计算机视觉与自然语言处理(Open AI)

1.语音识别技术 语音识别是将语音转换为文本的技术&#xff0c; 是自然语言处理的一个分支。通过特征的提取、模式的匹配将语音信号变为文本或命令&#xff0c;以实现机器识别和理解语音。 按照应用场景的不同&#xff0c;可以大致分为三类&#xff1b; • 电信级系统应用&…

树莓派(linux)使用Motion动作捕捉或实时获取视频

测试摄像头 查看系统是否识别了摄像头 $ lsusb 测试摄像头抓图&#xff08;拍照&#xff09; 安装 fswebcam sudo apt-get install fswebcam 抓拍一张图,存放与当前目录&#xff0c;并保存为 jpg 格式。 fswebcam /dev/video0 ./img1.jpg 查看摄像头效果 安装 luvcview …

Web前端第9章思维导图

本章内容是关于CSS样式属性&#xff0c;包含CSS单位、CSS字体样式、CSS文本样式、CSS颜色与背景、CSS列表样式、CSS盒模型。重点在于CSS盒模型、CSS文本样式、CSS字体样式。 1. CSS单位 绝对单位 磅&#xff08;pt&#xff09;&#xff0c;pica&#xff08;pc&#xff09;、c…

自动医疗检查仓:未来医疗的新篇章

自动医疗检查仓:未来医疗的新篇章 随着科技的飞速发展,医疗行业正经历着前所未有的变革。其中,自动医疗检查仓作为近年来备受瞩目的创新技术,正在逐渐改变我们对医疗服务的认知和体验。本文将对自动医疗检查仓进行深入剖析,从其技术原理、应用场景到未来发展趋势等方面展…

深度学习核心技术与实践之自然语言处理篇

非书中全部内容&#xff0c;只是写了些自认为有收获的部分。 自然语言处理简介 NLP的难点 &#xff08;1&#xff09;语言有很多复杂的情况&#xff0c;比如歧义、省略、指代、重复、更正、倒序、反语等 &#xff08;2&#xff09;歧义至少有如下几种&#xff1a; …

十大排序总结之——冒泡排序、插入排序

同样&#xff0c;这两几乎也是被淘汰了的算法&#xff0c;尽管它们是稳定的&#xff0c;但是时间复杂度没人喜欢&#xff0c;了解一下就好&#xff0c;没啥好说的&#xff0c;注意最后一句话就行了 一&#xff0c;冒泡排序 1. 算法步骤 共n-1趟&#xff0c;谁两敢冒泡就换了…

十四:爬虫-Redis基础

1、背景 随着互联网大数据时代的来临&#xff0c;传统的关系型数据库已经不能满足中大型网站日益增长的访问量和数据量。这个时候就需要一种能够快速存取数据的组件来缓解数据库服务I/O的压力&#xff0c;来解决系统性能上的瓶颈。 2、redis是什么 Redis 全称 Remote Dictio…

HarmonyOS4.0系统性深入开发10卡片事件能力说明

卡片事件能力说明 ArkTS卡片中提供了postCardAction()接口用于卡片内部和提供方应用间的交互&#xff0c;当前支持router、message和call三种类型的事件&#xff0c;仅在卡片中可以调用。 接口定义&#xff1a;postCardAction(component: Object, action: Object): void 接口…