最近的项目中很多地方需要处理Microsoft的word2007文档(即.docx文档),本来打算用Apache的POI项目,但经过试用发现其对word2007的支持并不好(其实可以说很差),网络上大家也都在讨论这个问题,说POI其实只是个半成品(仅仅是针对office2007这一块来说),并不推荐使用。
所以我又多方查找解决方案,最终找到了docx4j项目,docx4j主要用于word2007,同时也可以用于office 2007中的PowerPoint和Excel;试用之后感觉使用挺方便,只是文档较少,中文文档就更是凤毛麟角了。
在调研的过程中,Google帮了非常大的忙,因为国内少有人用docx4j(也可能是有人用但没人愿意分享),导致相关资料奇缺,百度对此无能为力;但是通过Google搜索还是能找到不少英文的相关博客,跟着这些博客以及官方示例,磕磕绊绊地也可以使用了,而且基本满足了项目的需求,这让我惊喜不已;有鉴于此,我觉得有必要花些时间推荐一下docx4j,使自己不至于一直是技术分享中的索取者。。。
我在使用过程中“翻译”了几篇外国人写的博客,这些博客质量很高(当然,不是说翻译的质量高),它们可以指引你使用docx4j,目录如下:
将这几篇博客称之为翻译实在是很惭愧,因为我的英文水平很菜,看这几篇翻译过来的博客可能还不如去看原文,而且我确实也在博客中留下了原文地址;虽然如此,但我觉得还是有必要将其“翻译”过来,因为在国内关于docx4j的资料很少见,即使只将代码放在这里对大家可能也是有所帮助。
官方网站:http://www.docx4java.org/trac/docx4j
下载地址:http://www.docx4java.org/downloads.html
入门指南:Getting Started guide(PDF)(HTML)
得益于天朝伟大的GFW,docx4j的官方站点有时可能需要挂代理才能访问。
官方介绍:
具体的使用技巧请看前面提到的几篇博客以及docx4j的入门指南,这里仅列出几个自己了解而前面博客没有提到的使用技巧:合并docx文档和转换PDF。
现在所做的项目中,需要合并多个docx文档,这让我纠结了很长一段时间;其实在docx4j的基础上,作者还提供了合并多个docx文档的lib,但那是需要商业授权的,所以没法使用,但后来在docx4j的forum中看到了其他人提供的解决方案,详情如下:
public InputStream mergeDocx(final List streams)
throws Docx4JException, IOException {
WordprocessingMLPackage target = null;
final File generated = File.createTempFile("generated", ".docx");
int chunkId = 0;
Iterator 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时让我为难了好长一阵子,因为中文导致乱码,官方示例中是有这一部分内容的,但由于注释太少,所以一直没有注意到,后来才发现示例的作者将字体相关的两句代码注释掉了:
// 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);
}
}
其实这样转换好像还不够完美,比如页眉页脚、目录什么的,都会出乱子;由于项目中word文档较为复杂,最终没有采用docx4j做PDF转换,换成了jacob......