整理项目资料或生成报告时,经常需要将多个 PDF 文件合并成一个。有时需要保留所有页面,有时只需要提取不同文件中的指定页面。如果 PDF 数据来自内存或网络,也可能需要直接通过流进行合并。
本文将介绍如何使用 Java 实现三种 PDF 合并方式:合并完整文档、合并多个 PDF 的指定页面,以及通过输入流合并 PDF。
首先,在 Java 项目的 pom.xml 中添加以下 Maven 仓库和依赖:
<repositories>
<repository>
<id>com.e-iceblue</id>
<name>e-iceblue</name>
<url>https://repo.e-iceblue.cn/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spire.pdf</artifactId>
<version>12.9.0</version>
</dependency>
</dependencies>
下面的示例使用三个 PDF 文件:
pdfs/
├── 01-cover.pdf
├── 02-report.pdf
└── 03-appendix.pdf
它们分别表示封面、报告正文和附录。每个代码示例都可以独立运行,并生成不同的输出文件。
如果需要将多个 PDF 文件的所有页面按顺序合并,可以使用 PdfDocument.mergeFiles() 方法。
该方法支持直接传入 PDF 文件路径数组,并将合并结果保存到指定位置。
完整代码如下:
import com.spire.pdf.PdfDocument;
public class MergePDF {
public static void main(String[] args) {
// 指定需要合并的 PDF 文件
String[] files = {
"pdfs/01-cover.pdf",
"pdfs/02-report.pdf",
"pdfs/03-appendix.pdf"
};
// 指定输出文件路径
String outputFile = "merged.pdf";
// 合并 PDF 文件
PdfDocument.mergeFiles(files, outputFile);
System.out.println("PDF 合并完成:" + outputFile);
}
}
这里的核心方法是:
PdfDocument.mergeFiles(files, outputFile)
它接收两个参数:
files:包含多个 PDF 文件路径的字符串数组。
outputFile:合并后 PDF 文件的保存路径。
程序会按照数组中的顺序合并文件。因此,生成的 merged.pdf 将依次包含封面、报告正文和附录的所有页面。
如果需要调整合并顺序,只需改变数组中文件路径的排列顺序,无须修改 PDF 文件本身。
这种方式适用于需要完整保留各个源文档的情况。
有时并不需要合并所有页面。例如,一份报告可能只需要封面、正文中的部分章节以及附录首页。
这种情况下,可以先加载多个 PDF 文件,再通过以下两个方法将指定页面插入到新文档中:
insertPage():从源 PDF 中插入指定页面。
insertPageRange():从源 PDF 中插入连续的页面范围。
假设现在需要合并以下内容:
源文件 | 需要合并的页面 |
|---|---|
01-cover.pdf | 第 1 页 |
02-report.pdf | 第 2~4 页 |
03-appendix.pdf | 第 1 页 |
最终生成的 PDF 将包含 5 页,并按照上述顺序排列。
完整代码如下:
import com.spire.pdf.PdfDocument;
public class MergeSelectedPDFPages {
public static void main(String[] args) {
// 创建源 PDF 文档对象
PdfDocument cover = new PdfDocument();
PdfDocument report = new PdfDocument();
PdfDocument appendix = new PdfDocument();
// 创建用于保存合并结果的 PDF 文档
PdfDocument mergedPdf = new PdfDocument();
try {
// 加载源 PDF 文件
cover.loadFromFile("pdfs/01-cover.pdf");
report.loadFromFile("pdfs/02-report.pdf");
appendix.loadFromFile("pdfs/03-appendix.pdf");
// 检查源 PDF 是否包含所需页面
if (cover.getPages().getCount() < 1
|| report.getPages().getCount() < 4
|| appendix.getPages().getCount() < 1) {
throw new IllegalArgumentException(
"源 PDF 的页数不足");
}
// 插入封面的第 1 页
mergedPdf.insertPage(cover, 0);
// 插入报告正文的第 2~4 页
mergedPdf.insertPageRange(report, 1, 3);
// 插入附录的第 1 页
mergedPdf.insertPage(appendix, 0);
// 保存合并结果
mergedPdf.saveToFile("merged-selected.pdf");
System.out.println("指定页面合并完成");
} finally {
// 释放 PDF 文档资源
mergedPdf.close();
cover.close();
report.close();
appendix.close();
}
}
}
这段代码需要注意两个地方。
1. PDF 页面的索引从 0 开始
在代码中,第 1 页对应索引 0,第 2 页对应索引 1,依此类推。
例如:
mergedPdf.insertPage(cover, 0);
表示从 cover 文档中获取第 1 页,并将其添加到合并文档中。
2. insertPageRange() 用于插入连续页面
下面这行代码:
mergedPdf.insertPageRange(report, 1, 3);
表示从 report 文档中插入索引为 1、2、3 的页面,也就是实际的第 2~4 页。结束索引包含在范围内。
由于页面是按照方法调用的先后顺序添加的,因此可以通过调整 insertPage() 和 insertPageRange() 的执行顺序来控制最终 PDF 的页面排列。
另外,插入的页面索引不能超过源 PDF 的实际页数。示例在执行插入操作前检查了页数,避免因页面不存在而产生异常。
如果只需要某几个不连续的页面,也可以多次调用 insertPage(),分别指定对应的页面索引。
除了直接通过文件路径合并 PDF,还可以使用输入流。
例如,在 Web 应用中,PDF 数据可能来自文件上传、网络请求或内存中的字节数组。此时不一定需要先将这些数据保存为临时 PDF 文件,再进行合并。
PdfDocument.mergeFiles() 提供了一个接收 InputStream[] 参数的重载方法,可以直接合并多个 PDF 输入流。
下面以 FileInputStream 为例,演示如何通过流合并三个 PDF 文件。
import com.spire.pdf.FileFormat;
import com.spire.pdf.PdfDocument;
import com.spire.pdf.PdfDocumentBase;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class MergePDFByStream {
public static void main(String[] args)
throws IOException {
// 创建三个 PDF 输入流
try (InputStream stream1 = new FileInputStream(
"pdfs/01-cover.pdf");
InputStream stream2 = new FileInputStream(
"pdfs/02-report.pdf");
InputStream stream3 = new FileInputStream(
"pdfs/03-appendix.pdf")) {
// 将输入流放入数组
InputStream[] streams = {
stream1,
stream2,
stream3
};
// 合并 PDF 输入流
PdfDocumentBase mergedPdf =
PdfDocument.mergeFiles(streams);
try {
// 保存合并后的 PDF
mergedPdf.save(
"merged-stream.pdf",
FileFormat.PDF);
System.out.println("PDF 流合并完成");
} finally {
// 释放合并后的文档资源
mergedPdf.close();
}
}
}
}
与前面的文件路径合并方式相比,这里的区别主要在于输入数据的形式。
首先,使用 FileInputStream 打开各个 PDF 文件,得到对应的输入流。
然后,将这些输入流放入 InputStream[] 数组,再调用:
PdfDocument.mergeFiles(streams);
该方法返回一个 PdfDocumentBase 对象,表示合并后的 PDF 文档。
最后,通过 save() 方法将结果保存为 merged-stream.pdf。
这里使用了 Java 的 try-with-resources 语法来管理输入流。无论合并成功还是发生异常,输入流都会在退出代码块时自动关闭。合并后的 PDF 对象也会在 finally 中释放资源。
需要注意,使用输入流合并并不意味着一定完全在内存中处理,也不代表不会产生输出文件。本例从本地文件创建输入流,并将结果保存到了磁盘。
如果输入 PDF 数据本身已经存储在内存中,也可以使用 ByteArrayInputStream 来创建输入流,无需先把原始数据写入临时文件。
完整文件合并和流合并都按照输入数组中的顺序处理文档。
指定页面合并则按照页面插入的顺序生成结果。如果希望调整最终页面顺序,需要在合并时明确指定。
如果源 PDF 包含 A4、A3 或其他尺寸的页面,合并后的 PDF 也可能保留这些不同尺寸。
合并操作不会自动将所有页面转换成统一大小。如有统一页面尺寸的要求,需要额外进行处理。
如果源 PDF 设置了打开密码,直接合并可能失败。对于已知密码的 PDF,可以使用 loadFromFile(filePath, password) 加载文档,再根据需要合并其中的页面。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。