docx4j -- 使用Java处理word2007(.docx)文档

前面的话


最近的项目中很多地方需要处理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的官方站点有时可能需要挂代理才能访问。

官方介绍:

docx4j is a Java library for creating and manipulating Microsoft Open XML (Word docx, Powerpoint pptx, and Excel xlsx) files.
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能做什么

  1. 打开已存在docx(从文件系统、SMB/CIFS、使用VFS的WebDAV),pptx,xlsx
  2. 创建新的docx、pptx、xlsx
  3. 编程式地操作上面打开的文档(很显示)
docx4j特殊的功能支持:

  1. 模版替换;CustomXML绑定
  2. 生产/消费Word2007的xmlPackage(pkg)格式
  3. 作为docx保存docx到文件系统(zipped)或者保存到JCR(unzipped)
  4. 应用转换,包括常见过滤器
  5. 作为HTML或者PDF导出
  6. 比较文档、段落或者sdt(内容控件)之间的差异
  7. 字体支持(字体替换及使用任何文档中嵌入的字体)


具体的使用技巧请看前面提到的几篇博客以及docx4j的入门指南,这里仅列出几个自己了解而前面博客没有提到的使用技巧:合并docx文档和转换PDF。


合并多个docx文档


现在所做的项目中,需要合并多个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


在做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......


你可能感兴趣的:(Java,开源项目)