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

news2024/12/25 10:18:03

这篇文章,主要介绍使用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);

        // 在合并PDF
        String 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.coloradmin.cn/o/1060250.html

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

相关文章

LVGL_基础控件label

LVGL_基础控件label 1、创建一个基础对象 /* 创建一个基础对象 label */ lv_obj_t * label lv_label_create(lv_scr_act()); // 创建一个label部件(对象),他的父对象是活动屏幕对象2、设置显示内容 char * text "www.100ask.net"; // 要显示的文字 /* 展示文…

K8S网络原理

文章目录 一、Kubernetes网络模型设计原则IP-per-Pod模型 二、Kubernetes的网络实现容器到容器的通信Pod之间的通信同一个Node内Pod之间的通信不同Node上Pod之间的通信 CNI网络模型CNM模型CNI模型在Kubernetes中使用网络插件 开源的网络组件FlannelFlannel实现图Flannel特点 Op…

视频批量剪辑工具,自定义视频速率,批量剪辑工具助力创意无限”

在视频制作的世界里&#xff0c;每一个细节都至关重要。今天&#xff0c;让我们来探索一项强大且创新的功能——自定义视频速率。利用它&#xff0c;你可以轻松地调整视频播放速度&#xff0c;赋予你的作品独特的个性和风格。 首先第一步&#xff0c;我们要打开好简单批量智剪…

智慧公厕有什么?

智慧公厕作为一种新形态的公共厕所&#xff0c;把智慧化的技术融入到公共厕所的日常使用与管理当中&#xff0c;赋予公共厕所更良好的信息化、数字化、科技化、联网化。 那么&#xff0c;智慧公厕有什么&#xff1f;本文从设施、技术、服务三方面进行快速了解。 首先&#xf…

vue实现轮播图详解

vue实现轮播图详解 目录 vue实现轮播图详解1 引言2 vue实现轮播图2.1 Vant组件引入2.1.1 vant组件引入2.2.2 使用van-swipe组件 2.2 vue代码实现2.2.1 功能需求2.2.2 实现思路2.2.3 代码实现2.2.4 实现效果 3 总结 1 引言 在互联网日渐内卷的情况下&#xff0c;越来越注重用户…

【重拾C语言】四、循环程序设计(后判断条件循环、先判断条件循环、多重循环;典例:计算平均成绩、打印素数、百钱百鸡问题)

目录 前言 四、循环程序设计 4.1 计算平均成绩——循环程序 4.1.1 后判断条件的循环 a. 语法 b. 典例 4.1.2 先判断条件的循环 a. 语法 b. 典例 4.1.3 for语句 a. 语法 b. 典例 4.2 计算全班每人平均成绩—多重循环 4.2.1 打印100以内素数 4.2.2 百钱百…

批量png图片格式转eps格式

问题描述&#xff1a; 在利用Latex排版论文格式时&#xff0c;当插入图片的格式要求为eps格式 &#xff0c;当然也适用于其它文件格式转换 解决方法&#xff1a; 推荐一格好用的免费在线格式转换工具&#xff1a;https://cdkm.com/cn/ 操作步骤&#xff1a; step1:打开网址 ste…

Access注入---Cookie注入

Access注入----Cookie注入Access数据库&#xff08;微软&#xff09; 逐渐淘汰 &#xff08;没有库的概念&#xff0c;是表的集合&#xff09;Access没有系统自带库Cookie注入&#xff08;头注入HEAD注入的&#xff09;php中产生Cookie注入的可能性小&#xff0c;但ASP产生Cook…

CCF CSP认证 历年题目自练Day21

题目一 试题编号&#xff1a; 201909-1 试题名称&#xff1a; 小明种苹果 时间限制&#xff1a; 2.0s 内存限制&#xff1a; 512.0MB 题目分析&#xff08;个人理解&#xff09; 先看输入&#xff0c;第一行输入苹果的棵树n和每一次掉的苹果数m还是先如何存的问题&#xf…

船用法兰铸钢止回阀

声明 本文是学习GB-T 586-2015 船用法兰铸钢止回阀. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了法兰连接尺寸和密封面按 CB/T 4196、GB/T 2501 的船用法兰铸钢止回阀(以下简 称止回阀)的分类和标记、要求、试验方法、检验规…

计算机竞赛 行人重识别(person reid) - 机器视觉 深度学习 opencv python

文章目录 0 前言1 技术背景2 技术介绍3 重识别技术实现3.1 数据集3.2 Person REID3.2.1 算法原理3.2.2 算法流程图 4 实现效果5 部分代码6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习行人重识别(person reid)系统 该项目…

RabbitMQ-第四种交换机类型

接上文 RabbitMQ-主题模式 1 第四种交换机类型 header:它是根据头部信息来决定的&#xff0c;在我们发送的消息中是可以携带一些头部信息的&#xff0c;类似与HTTP&#xff0c;我们可以根据这些头部信息来决定路由到哪一个消息队列中。 修改配置类内容 Configuration public…

win10自动更新后vpn不能使用

win10自动更新后vpn连接报错&#xff1a;不能建立到远程计算机的连接&#xff0c;请更改网络设置。 查看事件查看器&#xff1a; 错误日志如下&#xff1a; CoId{7E9C11AE-F6AF-0000-BC96-9C7EAFF6D901}: 用户 win10\myname 已进行名为 vpn1 的拨号连接&#xff0c;该连接已失…

写一个宏,可以将一个整数的二进制位的奇数位和偶数位交换。

题目要求&#xff1a; 写一个宏&#xff0c;可以将一个整数的二进制位的奇数位和偶数位交换。 思考内容&#xff1a; 怎么确定一个二进制数位的奇数位和偶数位?且这个位上的是0还是1&#xff1f; 假设&#xff1a; 数字 13 的二进制数位 0000 0000 0000 0000 0000 0000 00…

微信小程序 table表格 固定表头和首列 右侧表格可以左右滚动

(一) 1.左侧一列固定不动 2.右侧表格内容可以左右滚动 3.单元格内容平均分配 4.每一行行高可以由内容撑开 通过 js 设置左侧一列行高与右侧表格内容行高保持一致 1.1 效果图 1.2 tabble.wxml <view classtable><!-- 左侧固定 --><view classtable_left_colum…

代码随想录 Day10 栈与队列 LeetCode T239 滑动窗口的最大值 T347 前K个高频元素

简要介绍一下单调队列和优先级队列的不同 元素顺序的处理&#xff1a;单调队列中&#xff0c;元素的顺序是单调的&#xff0c;也就是说&#xff0c;队列中的元素按照特定的单调性&#xff08;递增或递减&#xff09;排列。这种特性使得单调队列在处理一些问题时非常高效&#…

【LeetCode热题100】--114.二叉树展开为链表

114.二叉树展开为链表 方法一&#xff1a;对二叉树进行先序遍历&#xff0c;得到各个节点被访问到的顺序&#xff0c;利用数组存储下来&#xff0c;然后在先序遍历之后更新每个节点的左右节点的信息&#xff0c;将二叉树展开为链表 /*** Definition for a binary tree node.* …

【ONE·Linux || 多线程(二)】

总言 多线程&#xff1a;生产者消费者模型与两种实现方式&#xff08;条件变量、信号量&#xff09;、线程池。 文章目录 总言4、生产者消费者模型4.1、基本概念4.2、基于BlockingQueue的生产者消费者模型&#xff08;理解条件变量&#xff09;4.2.1、单生产者单消费者模式&am…

【算法训练-数组 三】【数组矩阵】螺旋矩阵、搜索二维矩阵

废话不多说&#xff0c;喊一句号子鼓励自己&#xff1a;程序员永不失业&#xff0c;程序员走向架构&#xff01;本篇Blog的主题是螺旋矩阵&#xff0c;使用【二维数组】这个基本的数据结构来实现 螺旋矩阵【EASY】 二维数组的结构特性入手 题干 解题思路 根据题目示例 mat…

WEB3 创建React前端Dapp环境并整合solidity项目,融合项目结构便捷前端拿取合约 Abi

好 各位 经过我们上文 WEB3 solidity 带着大家编写测试代码 操作订单 创建/取消/填充操作 我们自己写了一个测试订单业务的脚本 没想到运行的还挺好的 那么 今天开始 我们就可以开始操作我们前端 Dapp 的一个操作了 在整个过程中 确实是没有我们后端的操作 或者说 我们自己就…