本篇文章会讲解如何使用jacob以及jodconverter将word无格式损毁转换为pdf。
使用java将word转换为pdf大体上有三种方式:
- 使用poi,也就是apache开源框架poi,但是它的缺点很明显,使用它转换pdf格式损毁比较严重,效率还很低,它在windows和linux平台都可以使用
- 使用jodconverter,使用它将word转换为pdf效果上要比poi好很多,但是它依赖于服务器端安装的OpenOffice或者libreoffice,它在windows和linux平台都可以使用,前提是宿主机都安装了OpenOffice或者libreoffice软件
- 使用jacob,使用它能实现100%无格式损毁转换word到pdf,但是它不能跨平台,只能在windows平台使用。
一、jacob转换方法
使用jacob转换依赖于windows系统上安装的wps或者word,在运行程序前必须安装这两个软件中的其中一个。
jacob项目已经迁移到github,github地址为,https://github.com/freemansoft/jacob-project ,现在star数量感人。。。打开release下载最新版(最新版的版本号是 1.20 ),并解压得到如下三个文件:
jacob.jar
jacob-1.20-x64.dll
jacob-1.20-x86.dll
1. 将jacob.jar安装到本地maven仓库
适用如下命令将jacob安装到本地maven仓库以方便使用
mvn install:install-file -Dfile=./jacob.jar -DgroupId=com.kdyzm -DartifactId=jacob -Dversion=1.0 -Dpackaging=jar
2. 将jacob-1.20-x64.dll拷贝到jre/bin目录
由于jacob依赖于本地安装的wps或者office,这就需要jni调用,所以需要将dll文件放到jire/bin目录下
3. 安装wps或者office
4.编码转换
private int word2PDF(String inputFile, String pdfFile) {
try {
// 打开Word应用程序
ActiveXComponent app = new ActiveXComponent("KWPS.Application");
log.info("开始转化{}为{}...",inputFile,pdfFile);
long date = new Date().getTime();
// 设置Word不可见
app.setProperty("Visible", new Variant(false));
// 禁用宏
app.setProperty("AutomationSecurity", new Variant(3));
// 获得Word中所有打开的文档,返回documents对象
Dispatch docs = app.getProperty("Documents").toDispatch();
// 调用Documents对象中Open方法打开文档,并返回打开的文档对象Document
Dispatch doc = Dispatch.call(docs, "Open", inputFile, false, true).toDispatch();
/***
*
* 调用Document对象的SaveAs方法,将文档保存为pdf格式
*
* Dispatch.call(doc, "SaveAs", pdfFile, wdFormatPDF word保存为pdf格式宏,值为17 )
*
*/
Dispatch.call(doc, "ExportAsFixedFormat", pdfFile, wdFormatPDF);// word保存为pdf格式宏,值为17
// 关闭文档
long date2 = new Date().getTime();
int time = (int) ((date2 - date) / 1000);
Dispatch.call(doc, "Close", false);
// 关闭Word应用程序
app.invoke("Quit", 0);
return time;
} catch (Exception e) {
// TODO: handle exception
log.error("",e);
return -1;
}
}
另外,完整代码如下所示
/**
* @author kdyzm
*/
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.Objects;
import java.util.Properties;
/***
* office文件转换为PDF文件
*
* @author leo.li
*
*/
public class Main2 {
private String office_path = "D:\\ProgramFiles\\WPS Office\\11.1.0.10132\\office6";
private String pdf_path = "./pdfs";
private static final int wdFormatPDF = 17;
private static final int xlTypePDF = 0;
private static final int ppSaveAsPDF = 32;
private static final Logger log= LoggerFactory.getLogger(Main2.class);
// 8 代表word保存成html
public static final int WORD_HTML = 8;
// 1 代表html保存成word
public static final int HTML_WORD = 1;
public static void main(String[] args) throws IOException {
Properties properties = new Properties();
properties.load(new FileReader(new File("config.properties")));
String sourceDirStr = properties.get("source.dir").toString();
String outputDirStr = properties.get("output.dir").toString();
File sourceDir = new File(sourceDirStr);
Arrays.stream(Objects.requireNonNull(sourceDir.listFiles())).forEach(file->{
String absolutePath = file.getAbsolutePath();
String name = file.getName().substring(0,file.getName().indexOf("."));
log.info(absolutePath);
String targetName=outputDirStr+"/"+name+".pdf";
log.info(targetName);
new Main2().word2PDF(absolutePath,targetName);
});
}
public static void wordToHtml(String docfile, String htmlfile)
{
// 启动word应用程序(Microsoft Office Word 2003)
ActiveXComponent app = new ActiveXComponent("Word.Application");
System.out.println("*****正在转换...*****");
try
{
// 设置word应用程序不可见
app.setProperty("Visible", new Variant(false));
// documents表示word程序的所有文档窗口,(word是多文档应用程序)
Dispatch docs = app.getProperty("Documents").toDispatch();
// 打开要转换的word文件
Dispatch doc = Dispatch.invoke(docs, "Open", Dispatch.Method, new Object[] { docfile, new Variant(false), new Variant(true) }, new int[1]).toDispatch();
// 作为html格式保存到临时文件
Dispatch.invoke(doc, "SaveAs", Dispatch.Method, new Object[] { htmlfile, new Variant(WORD_HTML) }, new int[1]);
// 关闭word文件
Dispatch.call(doc, "Close", new Variant(false));
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
//关闭word应用程序
app.invoke("Quit", new Variant[] {});
}
System.out.println("*****转换完毕********");
}
/**
* 转换office文件为PDF
*
* @param inputFileName
* @return
*/
public boolean office2pdf(String inputFileName, String username) {
String officeUserPath = String.format(office_path + "/%s/%s", username, inputFileName);
inputFileName = inputFileName.substring(0, inputFileName.lastIndexOf("."));
String pdfUserPath = String.format(pdf_path + "/%s/%s", username, inputFileName + ".pdf");
int time = convert2PDF(officeUserPath, pdfUserPath);
boolean result = false;
if (time == -4) {
log.info("转化失败,未知错误...");
} else if (time == -3) {
result = true;
log.info("原文件就是PDF文件,无需转化...");
} else if (time == -2) {
log.info("转化失败,文件不存在...");
} else if (time == -1) {
log.info("转化失败,请重新尝试...");
} else if (time < -4) {
log.info("转化失败,请重新尝试...");
} else {
result = true;
log.info("转化成功,用时: " + time + "s...");
}
return result;
}
/***
* 判断需要转化文件的类型(Excel、Word、ppt)
*
* @param inputFile
* @param pdfFile
*/
private int convert2PDF(String inputFile, String pdfFile) {
String kind = getFileSufix(inputFile);
File file = new File(inputFile);
if (!file.exists()) {
log.info("文件不存在");
return -2;
}
if (kind.equals("pdf")) {
log.info("原文件就是PDF文件");
return -3;
}
if (kind.equals("doc") || kind.equals("docx") || kind.equals("txt")) {
return word2PDF(inputFile, pdfFile);
} else if (kind.equals("ppt") || kind.equals("pptx")) {
return ppt2PDF(inputFile, pdfFile);
} else if (kind.equals("xls") || kind.equals("xlsx")) {
return Ex2PDF(inputFile, pdfFile);
} else {
return -4;
}
}
/***
* 判断文件类型
*
* @param fileName
* @return
*/
public static String getFileSufix(String fileName) {
int splitIndex = fileName.lastIndexOf(".");
return fileName.substring(splitIndex + 1);
}
/***
*
* Word转PDF
*
* @param inputFile
* @param pdfFile
* @return
*/
private int word2PDF(String inputFile, String pdfFile) {
try {
// 打开Word应用程序
ActiveXComponent app = new ActiveXComponent("KWPS.Application");
log.info("开始转化{}为{}...",inputFile,pdfFile);
long date = new Date().getTime();
// 设置Word不可见
app.setProperty("Visible", new Variant(false));
// 禁用宏
app.setProperty("AutomationSecurity", new Variant(3));
// 获得Word中所有打开的文档,返回documents对象
Dispatch docs = app.getProperty("Documents").toDispatch();
// 调用Documents对象中Open方法打开文档,并返回打开的文档对象Document
Dispatch doc = Dispatch.call(docs, "Open", inputFile, false, true).toDispatch();
/***
*
* 调用Document对象的SaveAs方法,将文档保存为pdf格式
*
* Dispatch.call(doc, "SaveAs", pdfFile, wdFormatPDF word保存为pdf格式宏,值为17 )
*
*/
Dispatch.call(doc, "ExportAsFixedFormat", pdfFile, wdFormatPDF);// word保存为pdf格式宏,值为17
// 关闭文档
long date2 = new Date().getTime();
int time = (int) ((date2 - date) / 1000);
Dispatch.call(doc, "Close", false);
// 关闭Word应用程序
app.invoke("Quit", 0);
return time;
} catch (Exception e) {
// TODO: handle exception
log.error("",e);
return -1;
}
}
/***
*
* Excel转化成PDF
*
* @param inputFile
* @param pdfFile
* @return
*/
private int Ex2PDF(String inputFile, String pdfFile) {
try {
ComThread.InitSTA(true);
ActiveXComponent ax = new ActiveXComponent("KET.Application");
log.info("开始转化Excel为PDF...");
long date = new Date().getTime();
ax.setProperty("Visible", false);
ax.setProperty("AutomationSecurity", new Variant(3)); // 禁用宏
Dispatch excels = ax.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(0), // PDF格式=0
pdfFile, new Variant(xlTypePDF) // 0=标准 (生成的PDF图片不会变模糊) 1=最小文件
// (生成的PDF图片糊的一塌糊涂)
}, new int[1]);
// 这里放弃使用SaveAs
/*
* Dispatch.invoke(excel,"SaveAs",Dispatch.Method,new Object[]{ outFile, new
* Variant(57), new Variant(false), new Variant(57), new Variant(57), new
* Variant(false), new Variant(true), new Variant(57), new Variant(true), new
* Variant(true), new Variant(true) },new int[1]);
*/
long date2 = new Date().getTime();
int time = (int) ((date2 - date) / 1000);
Dispatch.call(excel, "Close", new Variant(false));
if (ax != null) {
ax.invoke("Quit", new Variant[] {});
ax = null;
}
ComThread.Release();
return time;
} catch (Exception e) {
// TODO: handle exception
return -1;
}
}
/***
* ppt转化成PDF
*
* @param inputFile
* @param pdfFile
* @return
*/
private int ppt2PDF(String inputFile, String pdfFile) {
log.info("开始转化PPT为PDF...");
try {
ComThread.InitSTA(true);
ActiveXComponent app = new ActiveXComponent("KWPP.Application");
// app.setProperty("Visible", false);
long date = new Date().getTime();
Dispatch ppts = app.getProperty("Presentations").toDispatch();
Dispatch ppt = Dispatch.call(ppts, "Open", inputFile, true, // ReadOnly
// false, // Untitled指定文件是否有标题
false// WithWindow指定文件是否可见
).toDispatch();
Dispatch.invoke(ppt, "SaveAs", Dispatch.Method, new Object[] { pdfFile, new Variant(ppSaveAsPDF) },
new int[1]);
log.info("PPT");
Dispatch.call(ppt, "Close");
long date2 = new Date().getTime();
int time = (int) ((date2 - date) / 1000);
app.invoke("Quit");
return time;
} catch (Exception e) {
// TODO: handle exception
return -1;
}
}
// 删除多余的页,并转换为PDF
public static void interceptPPT(String inputFile, String pdfFile) {
ActiveXComponent app = null;
try {
ComThread.InitSTA(true);
app = new ActiveXComponent("KWPP.Application");
ActiveXComponent presentations = app.getPropertyAsComponent("Presentations");
ActiveXComponent presentation = presentations.invokeGetComponent("Open", new Variant(inputFile),
new Variant(false));
int count = Dispatch.get(presentations, "Count").getInt();
System.out.println("打开文档数:" + count);
ActiveXComponent slides = presentation.getPropertyAsComponent("Slides");
int slidePages = Dispatch.get(slides, "Count").getInt();
System.out.println("ppt幻灯片总页数:" + slidePages);
// 总页数的20%取整+1 最多不超过5页
int target = (int) (slidePages * 0.5) + 1 > 5 ? 5 : (int) (slidePages * 0.5) + 1;
// 删除指定页数
while (slidePages > target) {
// 选中指定页幻灯片
Dispatch slide = Dispatch.call(presentation, "Slides", slidePages).toDispatch();
Dispatch.call(slide, "Select");
Dispatch.call(slide, "Delete");
slidePages--;
System.out.println("当前ppt总页数:" + slidePages);
}
Dispatch.invoke(presentation, "SaveAs", Dispatch.Method, new Object[] { pdfFile, new Variant(32) },
new int[1]);
Dispatch.call(presentation, "Save");
Dispatch.call(presentation, "Close");
presentation = null;
app.invoke("Quit");
app = null;
ComThread.Release();
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
}
5.修改文档属性
pdf的文档属性,也就是pdf的metadata数据,包含了标题、作者等信息,这里的标题和文件名不一样,谷歌浏览器能识别并打开pdf文件,会使用pdf的标题文档属性作为tab页的title,如果不修改,会出现诡异的问题:tab页的title和文件内容标题不一致。我转换了word到pdf,保留了metadata信息,结果tab页的title竟然是“大头”两个字。。
修改元数据方法:不会使用jacob修改,这里使用apache pdfbox 进行修改。
首先引入pom依赖
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.22</version>
</dependency>
然后几行代码就可以完成转换:
private static void changeMetadata(File destFile,String title) throws IOException {
PDDocument doc = PDDocument.load(destFile);
PDDocumentInformation documentInformation = doc.getDocumentInformation();
documentInformation.setTitle(title);
documentInformation.setAuthor("kdyzm");
doc.save(destFile);
}
参考文档:https://pdfbox.apache.org/1.8/cookbook/workingwithmetadata.html 、https://stackoverflow.com/questions/3491026/how-to-edit-pdf-properties-in-java
二、jodconverter转换方法
joconverter转换word到pdf是最简单的一种方法,只需要一行代码就可以搞定。
1.引入jodconverter依赖
上maven中央仓库搜索jodconverter
关键字,将jodconverter-local
和jodconverter-core
引入到项目中,我使用的是4.3.0版本
<!-- https://mvnrepository.com/artifact/org.jodconverter/jodconverter-local -->
<dependency>
<groupId>org.jodconverter</groupId>
<artifactId>jodconverter-local</artifactId>
<version>4.3.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.jodconverter/jodconverter-core -->
<dependency>
<groupId>org.jodconverter</groupId>
<artifactId>jodconverter-core</artifactId>
<version>4.3.0</version>
</dependency>
2. 安装openoffice或者libreoffice
jodconverter依赖于服务端的这两个软件之一,但是和jacob不同的是,这两个软件都可以安装到linux中,有了容器技术之后,即使实现这种方式的转换也不再变得麻烦,总之jodconverter是poi和jacob之间的折中方案了。
另外,wps也能安装到linux中了,但是jacob还不支持,jacob还只能在windows环境中使用。
3.编码转换
jodconverter是最简单的转换方式,代码就三行,简单来看,真正去做转换的代码只有一行,可谓大道至简。
import org.jodconverter.core.office.OfficeException;
import org.jodconverter.local.JodConverter;
import org.jodconverter.local.office.LocalOfficeManager;
import java.io.File;
/**
* @author kdyzm
*/
public class Main1 {
public static void main(String[] args) throws OfficeException {
LocalOfficeManager manager = LocalOfficeManager.builder().officeHome("D:\\Program Files (x86)\\OpenOffice 4").install().build();
manager.start();
JodConverter.convert(new File("./mmmp.docx")).to(new File("./mmmp.pdf")).execute();
}
}
注意:本文归作者所有,未经作者允许,不得转载