场景说明
由于政企业务需要,文件要用到国产化的OFD,关于PDF文件转OFD文件的内容网上少之又少,要么收费要么文章内容不全,自己写了个PDF转OFD工具类,贡献给大家,若用到了点个赞就行了。话不多说,直接实操走起。
目录
3.1.Maven配置文件settings.xml加入(能直接引入spire.pdf.free包可忽略)
1.Maven依赖
注意:用到了免费版spire.pdf.free-5.1.0.jar,收费版会有水印的。
限制:带有图片pdf可以转前3页,纯文本pdf可以转前10页。
解决:具体解决限制页数问题,工具类中有具体思路。
<repositories>
<repository>
<id>com.e-iceblue</id>
<name>e-iceblue</name>
<url>https://repo.e-iceblue.cn/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<!-- pdf 转 ofd ,这个引入免费包需要Maven配置文件-->
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spire.pdf.free</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.ofdrw</groupId>
<artifactId>ofdrw-full</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.ofdrw</groupId>
<artifactId>ofdrw-tool</artifactId>
<version>2.3.1</version>
</dependency>
<!--ofd合并签名-->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.70</version>
</dependency>
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.29</version> <!-- 请根据需要调整版本号 -->
</dependency>
</dependencies>
2.PDF转OFD工具类
package com.jsite.modules.flow.util;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfReader;
import com.spire.pdf.FileFormat;
import com.spire.pdf.PdfDocument;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.ofdrw.tool.merge.OFDMerger;
import org.springframework.util.StringUtils;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
/**
* PDF文件转OFD文件工具类
*
* @author
* @date 2024/3/29 15:27
* @describe
*/
public class PDFToOFDUtil {
/**
* 测试方法
*
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// 记录程序开始时间(以纳秒为单位)
long startTime = System.nanoTime();
//1.源pfd文件
String inputFilePath = "D:\\data\\PDF\\12222\\202445045.pdf";
//2.源pfd文件分隔成按页文件合集
List<String> listPfd = splitPDFByPageCount(inputFilePath, 3);
//3.PDF文件合集转换为OFD文件合集
List<String> listOfd = new ArrayList<>();
for (String s : listPfd) {
String replace = s.replace("pdf", "ofd");
//用的冰蓝免费的jar包,pdf每次转换带图片可转前3页,纯文本可转前10页,所以我们每次转换按照情况实现最大化就行。
transPdfToOfd(s, replace);
listOfd.add(replace);
}
//4.OFD文件合集合并一个完整版ofd文件(最终实现源pfd转ofd)
String outputFilePathOfd = inputFilePath.replace("pdf", "ofd");
mergeOfdFiles(listOfd, outputFilePathOfd);
System.out.println("完整版ofd文件路径:" + outputFilePathOfd);
//5.删除多余文件
listPfd.addAll(listOfd);
for (String relFilePath : listPfd) {
Files.deleteIfExists(Paths.get(relFilePath));
}
long endTime = System.nanoTime();
double durationInSeconds = (endTime - startTime) / 1_000_000_000.0;
// 输出运行时间
System.out.println("程序运行时间: " + durationInSeconds + " 秒");
}
/**
* @Description pdf文档 转 ofd文档
* @date 2024/3/29 15:27
* @Param pdfPath pdf文件全路径
* @Param ofdPath ofd 输出全路径
**/
public static void transPdfToOfd(String pdfPath, String ofdPath) {
if (StringUtils.isEmpty(ofdPath) || StringUtils.isEmpty(pdfPath)) {
throw new RuntimeException("pdf或ofd文档地址不能为空");
}
byte[] bytes = getBytesFromFile(new File(pdfPath));
transPdfToOfd(bytes, ofdPath);
}
/**
* @Description pdf byte数组转ofd文档输出
* @date 2024/3/29 15:27
* @Param pdfBytes 字节数组
* @Param ofdPath ofd文档输出全路径
**/
public static void transPdfToOfd(byte[] pdfBytes, String ofdPath) {
if (pdfBytes == null || pdfBytes.length <= 0) {
throw new RuntimeException("pdf转ofd转化失败,pdf文件错误,pdf文件内容不能为空");
}
if (StringUtils.isEmpty(ofdPath)) {
throw new RuntimeException("ofd文档输出地址不能为空");
}
long startTime = LocalDateTime.now().atOffset(ZoneOffset.of("+8")).toInstant().toEpochMilli();
//构建PDF内容
PdfDocument pdf = new PdfDocument();
pdf.loadFromBytes(pdfBytes);
pdf.saveToFile(ofdPath, FileFormat.OFD);
long endTime = LocalDateTime.now().atOffset(ZoneOffset.of("+8")).toInstant().toEpochMilli();
}
/**
* @return byte[]
* @Description 把一个文件转化为byte字节数组
* @date 2024/3/29 15:27
* @Param file 文件
**/
public static byte[] getBytesFromFile(File file) {
byte[] data;
try (InputStream inputStream = Files.newInputStream(file.toPath());
BufferedInputStream bis = new BufferedInputStream(inputStream);
ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
int len;
byte[] buffer = new byte[1024];
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
data = bos.toByteArray();
} catch (Exception e) {
throw new RuntimeException(e);
}
return data;
}
/**
* 使用iTextPDF合并PDF文件
*
* @param pdfFiles 要合并的PDF文件路径
* @param mergedPdf 合并后的PDF文件路径
* @throws IOException
* @throws DocumentException
*/
public static void mergePdfFiles(List<String> pdfFiles, String mergedPdf) throws IOException, DocumentException {
Document document = new Document();
PdfCopy copy = new PdfCopy(document, new FileOutputStream(mergedPdf));
document.open();
PdfReader reader;
for (String file : pdfFiles) {
reader = new PdfReader(file);
int n = reader.getNumberOfPages();
for (int page = 0; page < n; ) {
copy.addPage(copy.getImportedPage(reader, ++page));
}
copy.freeReader(reader);
reader.close();
}
document.close();
}
/**
* OFD合并
*
* @param ofdFiles
* @param mergeOfd
* @throws IOException
* @throws DocumentException
*/
public static void mergeOfdFiles(List<String> ofdFiles, String mergeOfd) {
// 1. 提供合并文件输出位置。
Path dst = Paths.get(mergeOfd);
// 3. 创建合并对象
try (OFDMerger ofdMerger = new OFDMerger(dst)) {
for (String ofdFile : ofdFiles) {
Path d1Path = Paths.get(ofdFile);
// 4. 添加合并文档和页面。
ofdMerger.add(d1Path);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 将 PDF 文件按指定页数分隔,并保存到与输入文件同级的输出目录中。
*
* @param inputFilePath 输入 PDF 文件的路径
* @param pagesPerFile 每个 PDF 文件包含的页数比如3页为一个文件
* @return 分隔后的 PDF 文件路径列表
*/
public static List<String> splitPDFByPageCount(String inputFilePath, int pagesPerFile) {
// PDF 文件路径列表
List<String> listPdf = new ArrayList<>();
try {
// 加载 PDF 文件
File inputFile = new File(inputFilePath);
PDDocument document = PDDocument.load(inputFile);
// 获取输入文件的父目录
String outputDirPath = inputFile.getParent();
// 创建输出目录
File outputDir = new File(outputDirPath);
if (!outputDir.exists()) {
outputDir.mkdirs();
}
// 获取总页数
int totalPages = document.getNumberOfPages();
// 计算文件数量
int numFiles = (int) Math.ceil((double) totalPages / pagesPerFile);
// 遍历每个文件
for (int fileIndex = 0; fileIndex < numFiles; fileIndex++) {
PDDocument singleFileDoc = new PDDocument();
// 计算每个文件的起始页和结束页
int startPage = fileIndex * pagesPerFile;
int endPage = Math.min(startPage + pagesPerFile, totalPages);
// 添加页到当前文档
for (int pageIndex = startPage; pageIndex < endPage; pageIndex++) {
PDPage page = document.getPage(pageIndex);
singleFileDoc.addPage(page);
}
// 保存分隔出的 PDF 文件
String outputFileName = outputDirPath + "/output-part-" + (fileIndex + 1) + ".pdf";
singleFileDoc.save(outputFileName);
singleFileDoc.close();
listPdf.add(outputFileName);
}
// 关闭原始文档
document.close();
System.out.println("PDF 按页数分隔完成,文件已保存到:" + outputDirPath);
} catch (IOException e) {
e.printStackTrace();
}
return listPdf;
}
}
2.1.生成OFD文件效果展示
3.可能出现的问题及解决方案
3.1.Maven配置文件settings.xml加入(能直接引入spire.pdf.free包可忽略)
<mirror>
<id>aliyunmaven</id>
<!-- *是不能下载com.e-iceblue的依赖的,需要加入!com.e-iceblue -->
<mirrorOf>*,!com.e-iceblue</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
3.2.jar包冲突签名问题解决方案
bcpkix-jdk15on 与 bcpkix-jdk14 ,文件签名时引用冲突了,排除 bcpkix-jdk14引用。
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>2.1.7</version>
<exclusions>
<exclusion>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk14</artifactId>
</exclusion>
<exclusion>
<groupId>bouncycastle</groupId>
<artifactId>bcprov-jdk14</artifactId>
</exclusion>
</exclusions>
</dependency>
4.其他OFD优质文章参考
4.1.启发文章:
4.2.这个更多有关OFD操作免费开源的文章【推荐】
4.3.有关OFD文件前端预览,业务没要求没实现,哈哈。其他思路是转图片或者PDF预览。