前面的话
最近的项目中很多地方需要处理Microsoft的word2007文档(即.docx文档),本来打算用Apache的POI项目,但经过试用发现其对word2007的支持并不好(其实可以说很差),网络上大家也都在讨论这个问题,说POI其实只是个半成品(仅仅是针对office2007这一块来说),并不推荐使用。
所以我又多方查找解决方案,最终找到了docx4j项目,docx4j主要用于word2007,同时也可以用于office 2007中的PowerPoint和Excel;试用之后感觉使用挺方便,只是文档较少,中文文档就更是凤毛麟角了。
在调研的过程中,Google帮了非常大的忙,因为国内少有人用docx4j(也可能是有人用但没人愿意分享),导致相关资料奇缺,百度对此无能为力;但是通过Google搜索还是能找到不少英文的相关博客,跟着这些博客以及官方示例,磕磕绊绊地也可以使用了,而且基本满足了项目的需求,这让我惊喜不已;有鉴于此,我觉得有必要花些时间推荐一下docx4j,使自己不至于一直是技术分享中的索取者。。。
我在使用过程中“翻译”了几篇外国人写的博客,这些博客质量很高(当然,不是说翻译的质量高),它们可以指引你使用docx4j,目录如下:
- 使用Docx4j创建word文档
- 向Docx4j生成的word文档添加图片和布局--第一部分
- 向Docx4j生成的word文档中添加布局--第二部分
- 使用docx4j编程式地创建复杂的Word(.docx)文档
将这几篇博客称之为翻译实在是很惭愧,因为我的英文水平很菜,看这几篇翻译过来的博客可能还不如去看原文,而且我确实也在博客中留下了原文地址;虽然如此,但我觉得还是有必要将其“翻译”过来,因为在国内关于docx4j的资料很少见,即使只将代码放在这里对大家可能也是有所帮助。
docx4j介绍
官方网站:http://www.docx4java.org/trac/docx4j
下载地址:http://www.docx4java.org/downloads.html
入门指南:Getting Started guide(PDF)(HTML)
得益于天朝伟大的GFW,docx4j的官方站点有时可能需要挂代理才能访问。
官方介绍:
It is similar to Microsoft's OpenXML SDK, but for Java.
docx4j uses JAXB to create the in-memory object representation.
It is available under the Apache License (v2).
docx4j was created by Plutext Pty Ltd in 2008 - using OpenXML4J for the OPC piece. Plutext still drives the project, but since then docx4j has benefited from contributions from many individuals. The contributors are listed in docx4j's pom.xml.
docx4j能做什么
- 打开已存在docx(从文件系统、SMB/CIFS、使用VFS的WebDAV),pptx,xlsx
- 创建新的docx、pptx、xlsx
- 编程式地操作上面打开的文档(很显示)
- 模版替换;CustomXML绑定
- 生产/消费Word2007的xmlPackage(pkg)格式
- 作为docx保存docx到文件系统(zipped)或者保存到JCR(unzipped)
- 应用转换,包括常见过滤器
- 作为HTML或者PDF导出
- 比较文档、段落或者sdt(内容控件)之间的差异
- 字体支持(字体替换及使用任何文档中嵌入的字体)
具体的使用技巧请看前面提到的几篇博客以及docx4j的入门指南,这里仅列出几个自己了解而前面博客没有提到的使用技巧:合并docx文档和转换PDF。
合并多个docx文档
现在所做的项目中,需要合并多个docx文档,这让我纠结了很长一段时间;其实在docx4j的基础上,作者还提供了合并多个docx文档的lib,但那是需要商业授权的,所以没法使用,但后来在docx4j的forum中看到了其他人提供的解决方案,详情如下:
public InputStream mergeDocx(final List<InputStream> streams)
throws Docx4JException, IOException {
WordprocessingMLPackage target = null;
final File generated = File.createTempFile("generated", ".docx");
int chunkId = 0;
Iterator<InputStream> it = streams.iterator();
while (it.hasNext()) {
InputStream is = it.next();
if (is != null) {
if (target == null) {
// Copy first (master) document
OutputStream os = new FileOutputStream(generated);
os.write(IOUtils.toByteArray(is));
os.close();
target = WordprocessingMLPackage.load(generated);
} else {
// Attach the others (Alternative input parts)
insertDocx(target.getMainDocumentPart(),
IOUtils.toByteArray(is), chunkId++);
}
}
}
if (target != null) {
target.save(generated);
return new FileInputStream(generated);
} else {
return null;
}
}
// 插入文档
private void insertDocx(MainDocumentPart main, byte[] bytes, int chunkId) {
try {
AlternativeFormatInputPart afiPart = new AlternativeFormatInputPart(
new PartName("/part" + chunkId + ".docx"));
afiPart.setContentType(new ContentType(CONTENT_TYPE));
afiPart.setBinaryData(bytes);
Relationship altChunkRel = main.addTargetPart(afiPart);
CTAltChunk chunk = Context.getWmlObjectFactory().createCTAltChunk();
chunk.setId(altChunkRel.getId());
main.addObject(chunk);
} catch (Exception e) {
e.printStackTrace();
}
}
docx文档转换为PDF
在做docx转换PDF时让我为难了好长一阵子,因为中文导致乱码,官方示例中是有这一部分内容的,但由于注释太少,所以一直没有注意到,后来才发现示例的作者将字体相关的两句代码注释掉了:
// Set up font mapper
// Mapper fontMapper = new BestMatchingMapper();
// wordMLPackage.setFontMapper(fontMapper);
在将这段代码加上之后,中文乱码没有了,但是好像除了“宋体”以外的其它字体还会乱码,比如:华文行楷、隶书之类的,要解决这些问题,需要多做点工作:
Mapper fontMapper = new IdentityPlusMapper();
fontMapper.getFontMappings().put("华文行楷", PhysicalFonts.getPhysicalFonts().get("STXingkai"));
// 其它中文字体
mlPackage.setFontMapper(fontMapper);
// 然后再创建转换器
PdfConversion conversion = new Conversion(mlPackage);
完整方法代码:
/**
* docx文档转换为PDF
* @param docx docx文档
* @param pdfPath PDF文档存储路径
* @throws Exception 可能为Docx4JException, FileNotFoundException, IOException等
*/
public void convertDocxToPDF(File docx, String pdfPath) throws Exception {
OutputStream os = null;
try {
WordprocessingMLPackage mlPackage = WordprocessingMLPackage.load(docx);
// Mapper fontMapper = new BestMatchingMapper();
Mapper fontMapper = new IdentityPlusMapper();
fontMapper.getFontMappings().put("华文行楷", PhysicalFonts.getPhysicalFonts().get("STXingkai"));
fontMapper.getFontMappings().put("华文仿宋", PhysicalFonts.getPhysicalFonts().get("STFangsong"));
fontMapper.getFontMappings().put("隶书", PhysicalFonts.getPhysicalFonts().get("LiSu"));
mlPackage.setFontMapper(fontMapper);
PdfConversion conversion = new org.docx4j.convert.out.pdf.viaXSLFO.Conversion(mlPackage);
os = new FileOutputStream(pdfPath);
conversion.output(os, new PdfSettings());
} finally {
IOUtils.closeQuietly(os);
}
}
另外,docx4j还支持通过iTtext将docx文档转换为PDF,不过好像只能支持iText2.X版本,新版本不能用!
其实这样转换好像还不够完美,比如页眉页脚、目录什么的,都会出乱子;由于项目中word文档较为复杂,最终没有采用docx4j做PDF转换,换成了jacob......