工作有需要 Word 文档转换 PDF、图片 的场景,我们来看看 Java 开发中怎么解决这个问题的。
Word 转 PDF 分为商用 Aspose 方案和开源 Apache POI+iText 方案。
这种方式在目前来看应该是最好的,无论是转换的速度还是成功的概率,还支持的文件类型。
由于 Aspose 并非开源软件,不会在 Maven 公开依赖,故我们要手动加入到 Maven 管理中去。
<dependency>
<groupId>com.asposegroupId>
<artifactId>aspose-wordsartifactId>
<version>15.8version>
<scope>systemscope>
<systemPath>${project.basedir}/jar/aspose-words-15.8.0-jdk16.jarsystemPath>
dependency>
因为是手动添加的包,MANIFEST.MF 也要加入,不然启动程序的时候不知道要加入这个 jar 包。增加一个manifestEntries
节点:
<manifestEntries>
<Class-Path>lib/aspose-words-15.8.0-jdk16.jarClass-Path>
manifestEntries>
新增于 pom.xml 的
位置如图:
拷贝 jar 包,除了 runtime 的还有刚新家的 system 包,新增一个copy-dependencies2
:
<execution>
<id>copy-dependencies2id>
<phase>packagephase>
<goals>
<goal>copy-dependenciesgoal>
goals>
<configuration>
<outputDirectory>${project.build.directory}/liboutputDirectory>
<includeScope>systemincludeScope>
configuration>
execution>
import com.aspose.words.Document;
import com.aspose.words.ImageSaveOptions;
import com.aspose.words.License;
import com.aspose.words.SaveFormat;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
/**
* ...
* ...
*/
public class AsposeUtil {
/**
* Word 转 PDF
*
* @param wordPath Word 路径
* @param pdfPath PDF 路径
*/
public static void word2pdf(String wordPath, String pdfPath) {
AsposeUtil.getLicense();
try (FileOutputStream os = new FileOutputStream(pdfPath)) {
long old = System.currentTimeMillis();
//设置一个字体目录(必须设置,否则生成的pdf乱码)下面这行代码不加的话在windows系统下生成的pdf不存在乱码问题,但是在linux系统下会乱码,linux下乱码解决方案请看后面的解决方案
//FontSettings.setFontsFolder("/usr/share/fonts/chinese", false);
new Document(wordPath).save(os, SaveFormat.PDF);
System.out.println("word2pdf共耗时:" + (System.currentTimeMillis() - old) / 1000.0 + "秒");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void word2img(String wordPath, String outputDir) {
AsposeUtil.getLicense();
try {
long old = System.currentTimeMillis();
Document doc = new Document(wordPath);
// 创建图像保存选项对象
ImageSaveOptions options = new ImageSaveOptions(SaveFormat.JPEG);
options.setPageCount(doc.getPageCount()); // 设置要转换的页数
// options.setResolution(300); // 设置图像分辨率,默认为96dpi
// 逐页转换并保存为图像
for (int pageIndex = 0; pageIndex < doc.getPageCount(); pageIndex++) {
String outputFileName = outputDir + "image_" + (pageIndex + 1) + ".png";
options.setPageIndex(pageIndex);
doc.save(outputFileName, options);
}
System.out.println("word2pdf共耗时:" + (System.currentTimeMillis() - old) / 1000.0 + "秒");
} catch (Exception e) {
e.printStackTrace();
}
}
private static final byte[] LICENSE = ("\n" +
" \n" +
" \n" +
" Aspose.Total for Java \n" +
" Aspose.Words for Java \n" +
" \n" +
" Enterprise \n" +
" 20991231 \n" +
" 20991231 \n" +
" 8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7 \n" +
" \n" +
" sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU= \n" +
"").getBytes();
/**
* 判断是否有授权文件 如果没有则会认为是试用版,转换的文件会有水印
*/
public static void getLicense() {
try (InputStream is = new ByteArrayInputStream(LICENSE)) {
new License().setLicense(is);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
<dependency>
<groupId>fr.opensagres.xdocreportgroupId>
<artifactId>org.apache.poi.xwpf.converter.pdfartifactId>
<version>1.0.6version>
dependency>
转换程序
import fr.opensagres.xdocreport.utils.StringUtils;
import org.apache.poi.xwpf.converter.pdf.PdfConverter;
import org.apache.poi.xwpf.converter.pdf.PdfOptions;
import org.apache.poi.xwpf.usermodel.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
/**
* @author Rocca
*/
public class WordPdfUtils {
/**
* 将word文档, 转换成pdf, 中间替换掉变量
*
* @param source 源为word文档, 必须为docx文档
* @param target 目标输出
* @param params 需要替换的变量
*/
public static void wordConverterToPdf(InputStream source, OutputStream target, Map<String, String> params) {
wordConverterToPdf(source, target, null, params);
}
/**
* 将word文档, 转换成pdf, 中间替换掉变量
*
* @param source 源为word文档, 必须为docx文档
* @param target 目标输出
* @param params 需要替换的变量
* @param options PdfOptions.create().fontEncoding( "windows-1250" ) 或者其他
*/
public static void wordConverterToPdf(InputStream source, OutputStream target, PdfOptions options, Map<String, String> params) {
long old = System.currentTimeMillis();
try {
XWPFDocument doc = new XWPFDocument(source);
paragraphReplace(doc.getParagraphs(), params);
for (XWPFTable table : doc.getTables()) {
for (XWPFTableRow row : table.getRows()) {
for (XWPFTableCell cell : row.getTableCells())
paragraphReplace(cell.getParagraphs(), params);
}
}
PdfConverter.getInstance().convert(doc, target, options);
System.out.println("word2pdf共耗时:" + (System.currentTimeMillis() - old) / 1000.0 + "秒");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 替换段落中内容
*/
private static void paragraphReplace(List<XWPFParagraph> paragraphs, Map<String, String> params) {
for (XWPFParagraph p : paragraphs) {
for (XWPFRun r : p.getRuns()) {
String content = r.getText(r.getTextPosition());
if (StringUtils.isNotEmpty(content) && params.containsKey(content)) r.setText(params.get(content), 0);
}
}
}
}
上述的 Aspose.Word 并不支持 PDF 转图片。要使用 Aspose PDF 转图片须使用他家的另外一个产品 Aspose.Pdf。另外有趣的是,Aspose.Word 可以直接转为图片,但由于当前需求是得到了 Pdf 加盖章和签名之后转换图片的,并不能从 Word 直接转图片。而且感觉 Word 转图片也比较慢。
我感觉 PDF 转图片比较简单,不用 Aspose 也行,——于是使用了 Apache 的 Pdfbox。
<dependency>
<groupId>org.apache.pdfboxgroupId>
<artifactId>pdfboxartifactId>
<version>3.0.0version>
dependency>
你可以调整 DPI 分辨率,跟图片格式,下面例子是 gif 的。
/**
* PDF 转图片
*
* @param pdfFile PDF 文件
*/
public static void pdf2Img(String pdfFile, String outputDir) {
long old = System.currentTimeMillis();
try (PDDocument document = Loader.loadPDF(new File(pdfFile))) {
PDFRenderer renderer = new PDFRenderer(document);
for (int i = 0; i < document.getNumberOfPages(); ++i) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ImageIO.write(renderer.renderImageWithDPI(i, DPI), "gif", out);
// 将字节数组写入到文件
try (FileOutputStream fos = new FileOutputStream(outputDir + FileHelper.SEPARATOR + "img-" + i + ".gif")) {
fos.write(out.toByteArray());
}
}
System.out.println("pdf2img共耗时:" + (System.currentTimeMillis() - old) / 1000.0 + "秒");
} catch (IOException e) {
e.printStackTrace();
}
}
一般一份 PDF 是多页的,于是也会输出多张图片。所以你可以修改里面的文件名生成规则。