【itext7】使用itext7将多个PDF文件、图片合并成一个PDF文件,图片旋转、图片缩放

这篇文章,主要介绍使用itext7将多个PDF文件、图片合并成一个PDF文件,图片旋转、图片缩放。

目录

一、itext7合并PDF

1.1、引入依赖

1.2、合并PDF介绍

1.3、采用字节数组方式读取PDF文件

1.4、合并多个PDF文件

1.5、合并图片到PDF文件

1.6、旋转图片

1.7、完整案例代码

(1)PDFUtil工具类

(2)测试类代码

(3)合并效果


一、itext7合并PDF

1.1、引入依赖

我这里使用的是itext-core7.1.16版本,只需要引入一个itext-core依赖即可,因为这个依赖里面已经给我们引入了itext所需要的依赖。

<!-- 引入 itext7-core 依赖 -->
<dependency><groupId>com.itextpdf</groupId><artifactId>itext7-core</artifactId><version>7.1.16</version><type>pom</type>
</dependency>

1.2、合并PDF介绍

最简单的合并方式,那就是读取两个PDF文件,然后将其合并成一个新的PDF文件,保存到服务器上面之后,在将这个新的PDF文件和下一个待合并的PDF文件进行合并,以此类推,最终可以得到一个完整的PDF文件,但是这种方式缺点在于,每一次合并之后,都需要新生成一个PDF文件,并且下一次合并之后,还要再读取这个PDF文件,这就会导致多次读取文件的过程,效率不是很理想。

这篇文章,我主要是将PDF作为字节数组读取到内存里面,然后在内存中合并两个PDF的字节数据,这样可以减少读取和生成PDF文件的次数,执行效率方面也就会更加好一些了,合并两个PDF字节数组的方法如下所示:

/*** 基于内存中的字节数组进行PDF文档的合并* @param firstPdf 第一个PDF文档* @param secondPdf 第二个PDF文档*/
private static byte[] mergePdfBytes(byte[] firstPdf, byte[] secondPdf) throws IOException {if (firstPdf != null && secondPdf != null) {// 创建字节数组,基于内存进行合并ByteArrayOutputStream baos = new ByteArrayOutputStream();PdfDocument destDoc = new PdfDocument(new PdfWriter(baos));// 合并的pdf文件对象PdfDocument firstDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(firstPdf)));PdfDocument secondDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(secondPdf)));// 合并对象PdfMerger merger = new PdfMerger(destDoc);merger.merge(firstDoc, 1, firstDoc.getNumberOfPages());merger.merge(secondDoc, 1, secondDoc.getNumberOfPages());// 关闭文档流merger.close();firstDoc.close();secondDoc.close();destDoc.close();return baos.toByteArray();}return null;
}

1.3、采用字节数组方式读取PDF文件

合并PDF文件的时候,有些PDF文件可能是网络上的,也有些是本地磁盘上的,所以这里需要做下判断,如果是网络上的PDF文件,则需要首先访问网络,再将其保存到字节数组里面,如果是本地磁盘文件,则需要读取本地文件。

/*** 将pdf文档转换成字节数组* @param pdf PDF文档路径* @return 返回对应PDF文档的字节数组*/
private static byte[] getPdfBytes(String pdf) throws Exception {ByteArrayOutputStream out = new ByteArrayOutputStream();InputStream is;if (pdf.startsWith("http://") || pdf.startsWith("https://")) {is = new URL(pdf).openStream();} else {is = new FileInputStream(pdf);}byte[] data = new byte[2048];int len;while ((len = is.read(data)) != -1) {out.write(data, 0, len);}return out.toByteArray();
}

1.4、合并多个PDF文件

合并PDF时候,直接传递需要合并的PDF文件路径就可以啦,调用下面方法,就可以完成合并。

/*** 将给定List集合中的pdf文档,按照顺序依次合并,生成最终的目标PDF文档* @param pdfPathLists 待合并的PDF文档路径集合,可以是本地PDF文档,也可以是网络上的PDF文档* @param destPath 目标合并生成的PDF文档路径*/
public static boolean mergeMultiplePdfs(List<String> pdfPathLists, String destPath) {try {int size = pdfPathLists.size();byte[] pdfData = getPdfBytes(pdfPathLists.get(0));for (int i = 1; i < size; i++) {pdfData = mergePdfBytes(pdfData, getPdfBytes(pdfPathLists.get(i)));}if (pdfData != null) {FileOutputStream fis = new FileOutputStream(destPath);fis.write(pdfData);fis.close();}return true;} catch (Exception e) {logger.error("合并PDF异常:", e);}return false;
}

1.5、合并图片到PDF文件

如何将图片也一起合并到PDF文件里面呢???这里我是将图片直接添加到PDF文件的空白页面中实现的,一张图片占据一个页面,当然,你也可以设置显示在相同页面,超过之后页面高度之后,图片会自动显示到下一个页面。

/*** 将给定集合中的图片合并到一个pdf文档里面* @param imagePathList 图片路径集合* @param destPath 合并之后的PDF文档*/
public static boolean mergeImagesToPdf(List<String> imagePathList, String destPath) {try {PdfDocument pdfDocument = new PdfDocument(new PdfWriter(destPath));Document document = new Document(pdfDocument);if (imagePathList != null && imagePathList.size() > 0) {int size = imagePathList.size();for (int i = 0; i < size; i++) {String imgPath = imagePathList.get(i);ImageData imageData;if (imgPath.startsWith("http://") || imgPath.startsWith("https://")) {imageData = ImageDataFactory.create(new URL(imgPath));} else {imageData = ImageDataFactory.create(imgPath);}Image image = new Image(imageData);/*设置旋转的弧度值,默认是逆时针旋转的。弧度、角度换算公式:1° = PI / 180°1 rad = 180° / PI*/image.setRotationAngle(- Math.PI / 2); // 顺时针旋转90°// 设置图片自动缩放,即:图片宽高自适应image.setAutoScale(true);document.add(image);if (i != size - 1) {// 最后一页不需要新增空白页document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));}}}pdfDocument.close();return true;} catch (Exception e) {logger.error("合并图片到PDF异常:", e);}return false;
}

1.6、旋转图片

在某些需求下,你可以想某个图片竖向摆放、某些图片横向摆放,那么这个时候,就可以调用itext7中【Image】图片对象的【setRotationAngle()】方法,对其进行旋转,需要注意的是:setRotationAngle方法设置的旋转弧度,而不是旋转角度,并且它是逆时针旋转的。弧度和角度之间有一个转换公式,如下所示:

1.7、完整案例代码

(1)PDFUtil工具类

package com.gitcode.itext.util;import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.utils.PdfMerger;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.AreaBreak;
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.property.AreaBreakType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.*;
import java.net.URL;
import java.util.List;/*** @version 1.0.0* @Date: 2023/10/04 10:07* @Author ZhuYouBin* @Description: PDF工具类【基于 itext7 组件实现】*/
public class PDFUtil {private static final Logger logger = LoggerFactory.getLogger(PDFUtil.class);/*** 将给定List集合中的pdf文档,按照顺序依次合并,生成最终的目标PDF文档* @param pdfPathLists 待合并的PDF文档路径集合,可以是本地PDF文档,也可以是网络上的PDF文档* @param destPath 目标合并生成的PDF文档路径*/public static boolean mergeMultiplePdfs(List<String> pdfPathLists, String destPath) {try {int size = pdfPathLists.size();byte[] pdfData = getPdfBytes(pdfPathLists.get(0));for (int i = 1; i < size; i++) {pdfData = mergePdfBytes(pdfData, getPdfBytes(pdfPathLists.get(i)));}if (pdfData != null) {FileOutputStream fis = new FileOutputStream(destPath);fis.write(pdfData);fis.close();}return true;} catch (Exception e) {logger.error("合并PDF异常:", e);}return false;}/*** 将给定集合中的图片合并到一个pdf文档里面* @param imagePathList 图片路径集合* @param destPath 合并之后的PDF文档*/public static boolean mergeImagesToPdf(List<String> imagePathList, String destPath) {try {PdfDocument pdfDocument = new PdfDocument(new PdfWriter(destPath));Document document = new Document(pdfDocument);if (imagePathList != null && imagePathList.size() > 0) {int size = imagePathList.size();for (int i = 0; i < size; i++) {String imgPath = imagePathList.get(i);ImageData imageData;if (imgPath.startsWith("http://") || imgPath.startsWith("https://")) {imageData = ImageDataFactory.create(new URL(imgPath));} else {imageData = ImageDataFactory.create(imgPath);}Image image = new Image(imageData);/*设置旋转的弧度值,默认是逆时针旋转的。弧度、角度换算公式:1° = PI / 180°1 rad = 180° / PI*/image.setRotationAngle(- Math.PI / 2); // 顺时针旋转90°// 设置图片自动缩放,即:图片宽高自适应image.setAutoScale(true);document.add(image);if (i != size - 1) {// 最后一页不需要新增空白页document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));}}}pdfDocument.close();return true;} catch (Exception e) {logger.error("合并图片到PDF异常:", e);}return false;}/*** 基于内存中的字节数组进行PDF文档的合并* @param firstPdf 第一个PDF文档* @param secondPdf 第二个PDF文档*/private static byte[] mergePdfBytes(byte[] firstPdf, byte[] secondPdf) throws IOException {if (firstPdf != null && secondPdf != null) {// 创建字节数组,基于内存进行合并ByteArrayOutputStream baos = new ByteArrayOutputStream();PdfDocument destDoc = new PdfDocument(new PdfWriter(baos));// 合并的pdf文件对象PdfDocument firstDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(firstPdf)));PdfDocument secondDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(secondPdf)));// 合并对象PdfMerger merger = new PdfMerger(destDoc);merger.merge(firstDoc, 1, firstDoc.getNumberOfPages());merger.merge(secondDoc, 1, secondDoc.getNumberOfPages());// 关闭文档流merger.close();firstDoc.close();secondDoc.close();destDoc.close();return baos.toByteArray();}return null;}/*** 将pdf文档转换成字节数组* @param pdf PDF文档路径* @return 返回对应PDF文档的字节数组*/private static byte[] getPdfBytes(String pdf) throws Exception {ByteArrayOutputStream out = new ByteArrayOutputStream();InputStream is;if (pdf.startsWith("http://") || pdf.startsWith("https://")) {is = new URL(pdf).openStream();} else {is = new FileInputStream(pdf);}byte[] data = new byte[2048];int len;while ((len = is.read(data)) != -1) {out.write(data, 0, len);}return out.toByteArray();}
}

(2)测试类代码

package com.gitcode.itext;import com.gitcode.itext.util.PDFUtil;import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;/*** @version 1.0.0* @Date: 2023/10/4 11:12* @Author ZhuYouBin* @Description:*/
public class ImageDemo {public static void main(String[] args) throws FileNotFoundException {// 图片合并之后生成的PDF路径String imagePath = "F:\\pdf-demo\\imagePath.pdf";List<String> imageList = new ArrayList<>();imageList.add("F:\\pdf-demo\\01.jpg");imageList.add("F:\\pdf-demo\\02.jpg");// 先合并图片PDFUtil.mergeImagesToPdf(imageList, imagePath);// 在合并PDFString destPath = "F:\\pdf-demo\\merge.pdf";List<String> pdfPath = new ArrayList<>();pdfPath.add("F:\\pdf-demo\\demo01.pdf");pdfPath.add("F:\\pdf-demo\\demo02.pdf");pdfPath.add(imagePath);PDFUtil.mergeMultiplePdfs(pdfPath, destPath);}
}

(3)合并效果

到此,itext7合并PDF文件就介绍完啦。

综上,这篇文章结束了,主要介绍使用itext7将多个PDF文件、图片合并成一个PDF文件,图片旋转、图片缩放。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

1797_GNU pdf阅读器evince

全部学习汇总&#xff1a; GreyZhang/g_GNU: After some years I found that I do need some free air, so dive into GNU again! (github.com) 近段时间经历了很多事情&#xff0c;终于想找一点技术上的自由气氛。或许&#xff0c;没有什么比GNU的一些软件探索更适合填充这样的…

Leetcode---114双周赛

题目列表 2869. 收集元素的最少操作次数 2870. 使数组为空的最少操作次数 2871. 将数组分割成最多数目的子数组 2872. 可以被 K 整除连通块的最大数目 一、收集元素的最小操作次数 直接模拟&#xff0c;倒序遍历即可&#xff0c;代码如下 class Solution { public:int mi…

博客之站项目测试报告

项目背景项目功能测试计划Bug总结升级自动化测试正常登录流程 项目背景 1&#xff1a;博客之站系统是采用前后端分离的方式来实现&#xff1b;使用MySQL、Redis数据库储存相关数据&#xff1b;同时部署到云服务器上。 2&#xff1a;包含注册页、登录页、博客列表页、个人列表页…

CCF CSP认证 历年题目自练 Day22

CCF CSP认证 历年题目自练 Day22 题目一 试题编号&#xff1a; 201912-1 试题名称&#xff1a; 报数 时间限制&#xff1a; 1.0s 内存限制&#xff1a; 512.0MB 题目分析&#xff08;个人理解&#xff09; 每一个人都要报多少个数字&#xff0c;我选择字典存储&#xff0…

堆--数组中第K大元素

如果对于堆不是太认识&#xff0c;请点击&#xff1a;堆的初步认识-CSDN博客 解题思路&#xff1a; /*** <h3>求数组中第 K 大的元素</h3>* <p>* 解体思路* <ol>* 1.向小顶堆放入前k个元素* 2.剩余元素* 若 < 堆顶元素, 则略过* …

【GO 编程语言】面向对象

指针与结构体 文章目录 指针与结构体一、OOP 思想二、继承三、方法四、接口实现五、多态六、空接口七、接口继承八、接口断言九、Type别名 一、OOP 思想 Go语言不是面向对象的语言&#xff0c;这里只是通过一些方法来模拟面向对象&#xff0c;从而更好的来理解面向对象思想 面…

Win11 安装 Vim

安装包&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1Ru7HhTSotz9mteHug-Yhpw?pwd6666 提取码&#xff1a;6666 双击安装包&#xff0c;一直下一步。 配置环境变量&#xff1a; 先配置系统变量中的path&#xff1a; 接着配置用户变量&#xff1a; 在 cmd 中输入…

小谈设计模式(19)—备忘录模式

小谈设计模式&#xff08;19&#xff09;—备忘录模式 专栏介绍专栏地址专栏介绍 备忘录模式主要角色发起人&#xff08;Originator&#xff09;备忘录&#xff08;Memento&#xff09;管理者&#xff08;Caretaker&#xff09; 应用场景结构实现步骤Java程序实现首先&#xff…

基于Vue+ELement实现增删改查案例与表单验证(附源码)

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《ELement》。&#x1f3af;&#x1f3af; &#x1…

打开MySQL数据库

在命令行里输入mysql --version就可以查看&#xff1a; mysql -uroot -p之前设置的密码&#xff08;不用输入&#xff09;就可登录成功&#xff1a;

karmada v1.7.0安装指导

前言 安装心得 经过多种方式操作&#xff0c;发现二进制方法安装太复杂&#xff0c;证书生成及其手工操作太多了&#xff0c;没有安装成功&#xff1b;helm方式的安装&#xff0c;v1.7.0的chart包执行安装会报错&#xff0c;手工修复了报错并修改了镜像地址&#xff0c;还是各…

Redis-数据过期策略

数据过期策略 惰性删除策略优点&#xff1a;对cpu比较友好&#xff0c;在用到该key的时候才去进行判断&#xff0c;对于很多用不到key不用浪费时间去检查是否过期缺点&#xff1a;对内存不友好&#xff0c;如果一个key过期了&#xff0c;但是我们又一直没有用到该key&#xff0…

竞赛选题 深度学习 opencv python 公式识别(图像识别 机器视觉)

文章目录 0 前言1 课题说明2 效果展示3 具体实现4 关键代码实现5 算法综合效果6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的数学公式识别算法实现 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学…

1800_vim的宏录制功能尝试

全部学习信息汇总&#xff1a; GreyZhang/editors_skills: Summary for some common editor skills I used. (github.com) 最近5年多来&#xff0c;我emacs的编辑器用的还是比较多的。我的配置基本上是一个spacemacs&#xff0c;然后根据自己的需求增加了一丁点儿的其他配置。而…

数控车床中滚珠螺母的维护保养方法

滚珠螺母是一种高精度的机械部件&#xff0c;广泛应用于各种机械设备中&#xff0c;包括数控机床、精密轴承座、滚珠丝杆等&#xff0c;滚珠螺母作为数控机床中的进给系统的重要组件&#xff0c;其维护保养方法对于机床的精度和使用寿命具有重要影响。以下为数控机床滚珠螺母维…

李沐深度学习记录4:12.权重衰减/L2正则化

权重衰减从零开始实现 #高维线性回归 %matplotlib inline import torch from torch import nn from d2l import torch as d2l#整个流程是&#xff0c;1.生成标准数据集&#xff0c;包括训练数据和测试数据 # 2.定义线性模型训练 # 模型初始化&#xff08;函…

基于Kylin的数据统计分析平台架构设计与实现

目录 1 前言 2 关键模块 2.1 数据仓库的搭建 2.2 ETL 2.3 Kylin数据分析系统 2.4 数据可视化系统 2.5 报表模块 3 最终成果 4 遇到问题 1 前言 这是在TP-LINK公司云平台部门做的一个项目&#xff0c;总体包括云上数据统计平台的架构设计和组件开发&#xff0c;在此只做…

1.2 数据模型

思维导图&#xff1a; 前言&#xff1a; **1.2.1 什么是模型** - **定义**&#xff1a;模型是对现实世界中某个对象特征的模拟和抽象。例如&#xff0c;一张地图、建筑设计沙盘或精致的航模飞机都可以视为具体的模型。 - **具体模型与现实生活**&#xff1a;具体模型可以很容…

2023/9/27 -- ARM

【汇编语言相关语法】 1.汇编语言的组成部分 1.伪操作&#xff1a;不参与程序的执行&#xff0c;但是用于告诉编译器程序该怎么编译 .text .global .end .if .else .endif .data2.汇编指令 编译器将一条汇编指令编译成一条机器码&#xff0c;在内存里一条指令占4字节内…

【C++ 学习 ㉕】- 万字详解 unordered_map 和 unordered_set(哈希表的查找和容器的模拟实现)

目录 一、unordered_map 的基本介绍 二、unordered_set 的基本介绍 三、相关练习 3.1 - 在长度 2N 的数组中找出重复 N 次的元素 3.2 - 存在重复元素 3.3 - 两句话中的不常见单词 四、哈希表的查找 4.1 - 哈希表的基本概念 4.2 - 哈希函数的构造方法 4.3 - 处理冲突的…