javapackage top.zhoudeshui.utils;
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.concurrent.*;
public class WPSConvertUtils {
private static final Logger logger = LoggerFactory.getLogger(WPSConvertUtils.class);
private static final int WD_FORMAT_PDF = 17;
private static final int XL_TYPE_PDF = 0;
private static final int PP_SAVE_AS_PDF = 32;
private static final ExecutorService executorService = Executors.newSingleThreadExecutor();
public static CompletableFuture<String> convertToPDF(String inputFilePath, String outputDirOrFilePath) {
File inputFile = new File(inputFilePath);
if (!inputFile.exists()) {
logger.warn("原文件不存在: {}", inputFilePath);
return CompletableFuture.completedFuture(null);
}
String kind = getFileSuffix(inputFile.getName());
// 检查文件是否为PDF
if ("pdf".equalsIgnoreCase(kind)) {
try {
Path source = new File(inputFilePath).toPath();
Path target = new File(outputDirOrFilePath).toPath();
// 检查源文件是否存在
if (!Files.exists(source)) {
logger.error("Source file does not exist: {}", source);
return CompletableFuture.completedFuture(null);
}
// 如果目标是目录,创建一个以源文件名命名的新文件
if (Files.isDirectory(target)) {
target = target.resolve(source.getFileName());
}
// 确保目标目录存在
Path targetParent = target.getParent();
if (targetParent != null && !Files.exists(targetParent)) {
Files.createDirectories(targetParent);
}
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
logger.info("PDF 文件已复制: {} -> {}", source, target);
return CompletableFuture.completedFuture(target.toString());
} catch (IOException e) {
logger.error("Error copying PDF file", e);
return CompletableFuture.completedFuture(null);
}
}
String baseName = inputFile.getName().substring(0, inputFile.getName().lastIndexOf("."));
File outputDirOrFile = new File(outputDirOrFilePath);
String outputFilePath;
// 检查输出路径是否包含.pdf
if (outputDirOrFilePath.toLowerCase().endsWith(".pdf")) {
// 输出路径已经包含.pdf,直接使用该文件名
File pdfFile = new File(outputDirOrFilePath);
// 确保文件的父目录存在,如果不存在则创建
if (!pdfFile.getParentFile().exists() && !pdfFile.getParentFile().mkdirs()) {
logger.error("创建目录失败,请检查目录权限!");
return CompletableFuture.completedFuture(null);
}
outputFilePath = outputDirOrFilePath;
} else {
// 输出路径不包含.pdf,使用源文件的基本名称作为输出文件名
if (!outputDirOrFile.exists() && !outputDirOrFile.mkdirs()) {
logger.error("创建目录失败,请检查目录权限!");
return CompletableFuture.completedFuture(null);
}
outputFilePath = new File(outputDirOrFile, baseName + ".pdf").getAbsolutePath();
}
// 提交任务到线程池并返回 CompletableFuture
return CompletableFuture.supplyAsync(() -> processTask(inputFilePath, outputFilePath, kind), executorService)
.thenApply(result -> {
if (result) {
logger.info("转换成功: {} -> {}", inputFilePath, outputFilePath);
return outputFilePath;
} else {
logger.error("转换失败: {}", inputFilePath);
return null;
}
});
}
private static boolean processTask(String inputFilePath, String outputFilePath, String kind) {
switch (kind.toLowerCase()) {
case "doc":
case "docx":
case "txt":
return wordToPDF(inputFilePath, outputFilePath);
case "ppt":
case "pptx":
case "pptm":
case "ppsx":
return pptToPDF(inputFilePath, outputFilePath);
case "xls":
case "xlsx":
return exToPDF(inputFilePath, outputFilePath);
case "png":
case "jpg":
case "jpeg":
case "gif":
return imageToPDF(inputFilePath, outputFilePath);
default:
logger.warn("不支持的文件格式: {}", kind);
return false;
}
}
private static String getFileSuffix(String fileName) {
int splitIndex = fileName.lastIndexOf(".");
return splitIndex == -1 ? "" : fileName.substring(splitIndex + 1);
}
private static boolean wordToPDF(String inputFile, String pdfFile) {
ActiveXComponent app = null;
try {
ComThread.InitSTA();
app = new ActiveXComponent("KWPS.Application");
app.setProperty("Visible", new Variant(false));
app.setProperty("AutomationSecurity", new Variant(3)); // 禁用宏
Dispatch docs = app.getProperty("Documents").toDispatch();
Dispatch doc = Dispatch.call(docs, "Open", inputFile, false, true).toDispatch();
Dispatch.call(doc, "ExportAsFixedFormat", pdfFile, WD_FORMAT_PDF);
Dispatch.call(doc, "Close", false);
logger.info("Word 文件转换成功: {} -> {}", inputFile, pdfFile);
return true;
} catch (Exception e) {
logger.error("Word 文件转换失败: {}", inputFile, e);
return false;
} finally {
if (app != null) {
app.invoke("Quit", 0);
app.safeRelease(); // 释放 ActiveXComponent 资源
}
ComThread.Release();
}
}
private static boolean exToPDF(String inputFile, String pdfFile) {
ActiveXComponent app = null;
try {
ComThread.InitSTA();
app = new ActiveXComponent("KET.Application");
app.setProperty("Visible", new Variant(false));
app.setProperty("AutomationSecurity", new Variant(3)); // 禁用宏
Dispatch excels = app.getProperty("Workbooks").toDispatch();
Dispatch excel = Dispatch.invoke(excels, "Open", Dispatch.Method, new Object[]{inputFile, new Variant(false), new Variant(false)}, new int[9]).toDispatch();
Dispatch.invoke(excel, "ExportAsFixedFormat", Dispatch.Method, new Object[]{new Variant(XL_TYPE_PDF), pdfFile}, new int[1]);
Dispatch.call(excel, "Close", new Variant(false));
logger.info("Excel 文件转换成功: {} -> {}", inputFile, pdfFile);
return true;
} catch (Exception e) {
logger.error("Excel 文件转换失败: {}", inputFile, e);
return false;
} finally {
if (app != null) {
app.invoke("Quit");
app.safeRelease(); // 释放 ActiveXComponent 资源
}
ComThread.Release();
}
}
private static boolean pptToPDF(String inputFile, String pdfFile) {
ActiveXComponent app = null;
try {
ComThread.InitSTA();
app = new ActiveXComponent("KWPP.Application");
Dispatch ppts = app.getProperty("Presentations").toDispatch();
Dispatch ppt = Dispatch.call(ppts, "Open", inputFile, true, false).toDispatch();
Dispatch.invoke(ppt, "SaveAs", Dispatch.Method, new Object[]{pdfFile, new Variant(PP_SAVE_AS_PDF)}, new int[1]);
Dispatch.call(ppt, "Close");
logger.info("PPT 文件转换成功: {} -> {}", inputFile, pdfFile);
return true;
} catch (Exception e) {
logger.error("PPT 文件转换失败: {}", inputFile, e);
return false;
} finally {
if (app != null) {
app.invoke("Quit");
app.safeRelease(); // 释放 ActiveXComponent 资源
}
ComThread.Release();
}
}
/**
* 将图片转换为PDF
*
* @param inputFile 图片文件路径
* @param pdfFile 输出PDF文件路径
* @return 是否转换成功
*/
public static boolean imageToPDF(String inputFile, String pdfFile) {
try (PDDocument document = new PDDocument()) {
PDPage page = new PDPage(PDRectangle.A4); // 创建一个A4页面
document.addPage(page);
// 加载图片并创建一个PDF图像对象
File file = new File(inputFile);
BufferedImage bufferedImage = ImageIO.read(file);
PDImageXObject pdImage = LosslessFactory.createFromImage(document, bufferedImage);
// 计算图片缩放比例以适应页面
float originalWidth = pdImage.getWidth();
float originalHeight = pdImage.getHeight();
float scaleX = PDRectangle.A4.getWidth() / originalWidth;
float scaleY = PDRectangle.A4.getHeight() / originalHeight;
float scale = Math.min(scaleX, scaleY); // 取较小的缩放比例以完全适应页面
// 开始在页面上绘制内容
try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
// 将图片绘制到页面上,居中并按比例缩放
float x = (PDRectangle.A4.getWidth() - originalWidth * scale) / 2;
float y = (PDRectangle.A4.getHeight() - originalHeight * scale) / 2;
contentStream.drawImage(pdImage, x, y, originalWidth * scale, originalHeight * scale);
}
// 保存PDF文件
document.save(pdfFile);
logger.info("图片转换成功: {} -> {}", inputFile, pdfFile);
return true;
} catch (IOException e) {
logger.error("图片转换失败: {}", inputFile, e);
return false;
}
}
}
本文作者:周得水
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!