原文标题:Creating Word documents with Docx4j
原文链接:http://blog.iprofs.nl/2012/09/06/creating-word-documents-with-docx4j/
原文作者:lvdpal
发表时间:2012年9月6日
注:第一次翻译博客;由于个人水平不高,而且英语仅有四级水平,所以错误很多,请大家见谅!!!只是国内关于docx4j的博客极少,感觉这篇入门博客挺不错,勉强翻译过来,希望对大家有所帮助。
一段时间之前,我为一个想要在word和excel中作报表的客户提供服务。我以前的项目中生成过PDF文件和CSV文件,但从来没有处理过docx和xlsx文件。这些天了解到MS Office是基于XML的。我不禁想知道是否有一些库来帮助我生成这些文件。经过一番在线搜索,结果表明确实有:Docx4j。我开始去试用这个新库看它能做些什么。
在官方网站上你可以了解到docx4j可以读取、更新、创建docx、xlsx和pptx文档。我只需要生成文档,所以我没有尝试读取和更新文档,但原理是一样的。Docx4j已经自带了不少示例程序,但我发现其中的一些例子同时展示了多样东西或者没有完全展示我需要了解的。所以有不少东西我不得不自己解决。幸运的是docx4j严格遵循Office Open XML标准,所以还不算太难。总而言这,我对这个库所提供的功能非常满意。
在这个博客中我会展示一些我试用docx4j生成docx文档时创建的例子:
在真正示例开始之前有几处提示。首先,在使用docx创建文档的时候,看看你使用word软件创建文档时生成的xml。当你使用一个ZIP压缩工具(例如7zip)打开一个docx文件时会找到这些XML文件。看一下都是些什么样的文件以及它们包含了哪些信息。通常情况下,如果你想得到什么东西,可以通过查看Word软件自己生成了什么而从中得到很多帮助。
其次,去看一下 the Office Open Specification。
现在让我们来看一些代码。
最简单的例子就是创建一个新word文档并向其中添加一些文本内容。
运行下面代码:
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage(); wordMLPackage.getMainDocumentPart().addParagraphOfText("Hello Word!"); wordMLPackage.save(new java.io.File("src/main/files/HelloWord1.docx"));
这里发生的就是一个word文档由一个带有文档部件的包组成。Docx4j提供一个创建包的工具方法(第1行);然后你可以从这个包中获取主文档部件并向其中添加一个文本段落(第2行);最后将这个包保存(第3行)。就这么简单,当然,如果你想做不仅仅是向文档中添加一些文本内容,那么你需要做更多的工作。
然而即使添加带样式的文本也并不需要做过多的工作,不是向文档添加一个普通的文本段落,而是只需要添加一个带样式的段落。
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage(); wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Title", "Hello Word!"); wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Subtitle","This is a subtitle!"); wordMLPackage.save(new java.io.File("src/main/files/HelloWord2.docx"));
当我们想添加一个表格时,情况开始变得复杂一些。直到现在我们的代码都如此简单以至于不需要拆分到多个方法中,所以我仅仅只展示了代码中相关的数行。但这个示例我们需要第二个方法,所以我会展示整个类。
public class AddingATable { private static WordprocessingMLPackage wordMLPackage; private static ObjectFactory factory; public static void main (String[] args) throws Docx4JException { wordMLPackage = WordprocessingMLPackage.createPackage(); factory = Context.getWmlObjectFactory(); Tbl table = factory.createTbl(); Tr tableRow = factory.createTr(); addTableCell(tableRow, "Field 1"); addTableCell(tableRow, "Field 2"); table.getContent().add(tableRow); wordMLPackage.getMainDocumentPart().addObject(table); wordMLPackage.save(new java.io.File("src/main/files/HelloWord4.docx")); } private static void addTableCell(Tr tableRow, String content) { Tc tableCell = factory.createTc(); tableCell.getContent().add( wordMLPackage.getMainDocumentPart().createParagraphOfText(content)); tableRow.getContent().add(tableCell); } }
然后我们创建了表格,创建了行并且在行中添加了两个单元格。最后我们将行添加到表格中,将表格添加到主文档部件并且将包保存。
现在我们拥有了一个表格,让我们给它添加一些边框。这需要深入研究一下表格的结构。
public class TableWithBorders { private static WordprocessingMLPackage wordMLPackage; private static ObjectFactory factory; public static void main (String[] args) throws Docx4JException { wordMLPackage = WordprocessingMLPackage.createPackage(); factory = Context.getWmlObjectFactory(); Tbl table = createTableWithContent(); addBorders(table); wordMLPackage.getMainDocumentPart().addObject(table); wordMLPackage.save(new java.io.File( "src/main/files/HelloWord5.docx") ); } private static void addBorders(Tbl table) { table.setTblPr(new TblPr()); CTBorder border = new CTBorder(); border.setColor("auto"); tborder.setSz(new BigInteger("4")); border.setSpace(new BigInteger("0")); border.setVal(STBorder.SINGLE); TblBorders borders = new TblBorders(); borders.setBottom(border); borders.setLeft(border); borders.setRight(border); borders.setTop(border); borders.setInsideH(border); borders.setInsideV(border); table.getTblPr().setTblBorders(borders); } private static Tbl createTableWithContent() { Tbl table = factory.createTbl(); Tr tableRow = factory.createTr(); addTableCell(tableRow, "Field 1"); addTableCell(tableRow, "Field 2"); table.getContent().add(tableRow); return table; } private static void addTableCell(Tr tableRow, String content) { Tc tableCell = factory.createTc(); tableCell.getContent().add( wordMLPackage.getMainDocumentPart(). createParagraphOfText(content)); tableRow.getContent().add(tableCell); } }
接下来的问题--给表格添加样式让我忙活了一阵子,我们的客户想要在表格中使用粗体文本并且需要多种大小的字体。
这个示例程序有点复杂,注释和代码交织在一起。
public class TableWithStyledContent { private static WordprocessingMLPackage wordMLPackage; private static ObjectFactory factory; /** * 跟前面的做的一样, 我们再一次创建了一个表格, 并添加了三个单元格, 其中有两个 * 单元带有样式. 在新方法中我们传进表格行, 单元格内容, 是否为粗体及字体大小作 * 为参数. 你需要注意, 因为the Office Open specification规范定义这个属性是半个 * 点(half-point)大小, 因此字体大小需要是你想在Word中显示大小的两倍, */ public static void main (String[] args) throws Docx4JException { wordMLPackage = WordprocessingMLPackage.createPackage(); factory = Context.getWmlObjectFactory(); Tbl table = factory.createTbl(); Tr tableRow = factory.createTr(); addRegularTableCell(tableRow, "Normal text"); addStyledTableCell(tableRow, "Bold text", true, null); addStyledTableCell(tableRow, "Bold large text", true, "40"); table.getContent().add(tableRow); addBorders(table); wordMLPackage.getMainDocumentPart().addObject(table); wordMLPackage.save(new java.io.File("src/main/files/HelloWord6.docx") ); } /** * 本方法创建单元格, 添加样式后添加到表格行中 */ private static void addStyledTableCell(Tr tableRow, String content, boolean bold, String fontSize) { Tc tableCell = factory.createTc(); addStyling(tableCell, content, bold, fontSize); tableRow.getContent().add(tableCell); } /** * 这里我们添加实际的样式信息, 首先创建一个段落, 然后创建以单元格内容作为值的文本对象; * 第三步, 创建一个被称为运行块的对象, 它是一块或多块拥有共同属性的文本的容器, 并将文本对象添加 * 到其中. 随后我们将运行块R添加到段落内容中. * 直到现在我们所做的还没有添加任何样式, 为了达到目标, 我们创建运行块属性对象并给它添加各种样式. * 这些运行块的属性随后被添加到运行块. 最后段落被添加到表格的单元格中. */ private static void addStyling(Tc tableCell, String content, boolean bold, String fontSize) { P paragraph = factory.createP(); Text text = factory.createText(); text.setValue(content); R run = factory.createR(); run.getContent().add(text); paragraph.getContent().add(run); RPr runProperties = factory.createRPr(); if (bold) { addBoldStyle(runProperties); } if (fontSize != null && !fontSize.isEmpty()) { setFontSize(runProperties, fontSize); } run.setRPr(runProperties); tableCell.getContent().add(paragraph); } /** * 本方法为可运行块添加字体大小信息. 首先创建一个"半点"尺码对象, 然后设置fontSize * 参数作为该对象的值, 最后我们分别设置sz和szCs的字体大小. * Finally we'll set the non-complex and complex script font sizes, sz and szCs respectively. */ private static void setFontSize(RPr runProperties, String fontSize) { HpsMeasure size = new HpsMeasure(); size.setVal(new BigInteger(fontSize)); runProperties.setSz(size); runProperties.setSzCs(size); } /** * 本方法给可运行块属性添加粗体属性. BooleanDefaultTrue是设置b属性的Docx4j对象, 严格 * 来说我们不需要将值设置为true, 因为这是它的默认值. */ private static void addBoldStyle(RPr runProperties) { BooleanDefaultTrue b = new BooleanDefaultTrue(); b.setVal(true); runProperties.setB(b); } /** * 本方法像前面例子中一样再一次创建了普通的单元格 */ private static void addRegularTableCell(Tr tableRow, String content) { Tc tableCell = factory.createTc(); tableCell.getContent().add( wordMLPackage.getMainDocumentPart().createParagraphOfText( content)); tableRow.getContent().add(tableCell); } /** * 本方法给表格添加边框 */ private static void addBorders(Tbl table) { table.setTblPr(new TblPr()); CTBorder border = new CTBorder(); border.setColor("auto"); border.setSz(new BigInteger("4")); border.setSpace(new BigInteger("0")); border.setVal(STBorder.SINGLE); TblBorders borders = new TblBorders(); borders.setBottom(border); borders.setLeft(border); borders.setRight(border); borders.setTop(border); borders.setInsideH(border); borders.setInsideV(border); table.getTblPr().setTblBorders(borders); } }
有时你想要一个三列的表格,其中第一列将多行组合在一起。
package com.zyh.sample.docx4j; public class TableWithMergedCells { private static WordprocessingMLPackage wordMLPackage; private static ObjectFactory factory; /** * 创建一个带边框的表格并添加四个带内容的行, 然后将表格添加到文档并保存 */ public static void main (String[] args) throws Docx4JException { wordMLPackage = WordprocessingMLPackage.createPackage(); factory = Context.getWmlObjectFactory(); Tbl table = factory.createTbl(); addBorders(table); addTableRowWithMergedCells("Heading 1", "Heading 1.1", "Field 1", table); addTableRowWithMergedCells(null, "Heading 1.2", "Field 2", table); addTableRowWithMergedCells("Heading 2", "Heading 2.1", "Field 3", table); addTableRowWithMergedCells(null, "Heading 2.2", "Field 4", table); wordMLPackage.getMainDocumentPart().addObject(table); wordMLPackage.save(new java.io.File( "src/main/files/HelloWord9.docx") ); } /** * 本方法创建一行, 并向其中添加合并列, 然后添加再两个普通的单元格. 随后将该行添加到表格 */ private static void addTableRowWithMergedCells(String mergedContent, String field1Content, String field2Content, Tbl table) { Tr tableRow1 = factory.createTr(); addMergedColumn(tableRow1, mergedContent); addTableCell(tableRow1, field1Content); addTableCell(tableRow1, field2Content); table.getContent().add(tableRow1); } /** * 本方法添加一个合并了其它行单元格的列单元格. 如果传进来的内容是null, 传空字符串和一个为null的合并值. */ private static void addMergedColumn(Tr row, String content) { if (content == null) { addMergedCell(row, "", null); } else { addMergedCell(row, content, "restart"); } } /** * 我们创建一个单元格和单元格属性对象. * 也创建了一个纵向合并对象. 如果合并值不为null, 将它设置到合并对象中. 然后将该对象添加到 * 单元格属性并将属性添加到单元格中. 最后设置单元格内容并将单元格添加到行中. * * 如果合并值为'restart', 表明要开始一个新行. 如果为null, 继续按前面的行处理, 也就是合并单元格. */ private static void addMergedCell(Tr row, String content, String vMergeVal) { Tc tableCell = factory.createTc(); TcPr tableCellProperties = new TcPr(); VMerge merge = new VMerge(); if(vMergeVal != null){ merge.setVal(vMergeVal); } tableCellProperties.setVMerge(merge); tableCell.setTcPr(tableCellProperties); if(content != null) { tableCell.getContent().add( wordMLPackage.getMainDocumentPart(). createParagraphOfText(content)); } row.getContent().add(tableCell); } /** * 本方法为给定的行添加一个单元格, 并以给定的段落作为内容 */ private static void addTableCell(Tr tr, String content) { Tc tc1 = factory.createTc(); tc1.getContent().add( wordMLPackage.getMainDocumentPart().createParagraphOfText(content)); tr.getContent().add(tc1); } /** * 本方法为表格添加边框 */ private static void addBorders(Tbl table) { table.setTblPr(new TblPr()); CTBorder border = new CTBorder(); border.setColor("auto"); border.setSz(new BigInteger("4")); border.setSpace(new BigInteger("0")); border.setVal(STBorder.SINGLE); TblBorders borders = new TblBorders(); borders.setBottom(border); borders.setLeft(border); borders.setRight(border); borders.setTop(border); borders.setInsideH(border); borders.setInsideV(border); table.getTblPr().setTblBorders(borders); } }
作为本博客的最后一个示例, 我会展示怎样为表格设置列宽。
public class SettingColumnWidthForTable { private static WordprocessingMLPackage wordMLPackage; private static ObjectFactory factory; /** * 创建一个带边框的表格并添加一行. 然后添加两个带内容的单元格并给定宽度. */ public static void main (String[] args) throws Docx4JException { wordMLPackage = WordprocessingMLPackage.createPackage(); factory = Context.getWmlObjectFactory(); Tbl table = factory.createTbl(); addBorders(table); Tr tr = factory.createTr(); addTableCellWithWidth(tr, "Field 1", 2500); addTableCellWithWidth(tr, "Field 2", 0); table.getContent().add(tr); wordMLPackage.getMainDocumentPart().addObject(table); wordMLPackage.save(new java.io.File( "src/main/files/HelloWord13.docx") ); } /** * 本方法创建一个单元格并将给定的内容添加进去. * 如果给定的宽度大于0, 将这个宽度设置到单元格. * 最后, 将单元格添加到行中. */ private static void addTableCellWithWidth(Tr row, String content, int width){ Tc tableCell = factory.createTc(); tableCell.getContent().add( wordMLPackage.getMainDocumentPart().createParagraphOfText( content)); if (width > 0) { setCellWidth(tableCell, width); } row.getContent().add(tableCell); } /** * 本方法创建一个单元格属性集对象和一个表格宽度对象. 将给定的宽度设置到宽度对象然后将其添加到 * 属性集对象. 最后将属性集对象设置到单元格中. */ private static void setCellWidth(Tc tableCell, int width) { TcPr tableCellProperties = new TcPr(); TblWidth tableWidth = new TblWidth(); tableWidth.setW(BigInteger.valueOf(width)); tableCellProperties.setTcW(tableWidth); tableCell.setTcPr(tableCellProperties); } /** * 本方法为表格添加边框 */ private static void addBorders(Tbl table) { table.setTblPr(new TblPr()); CTBorder border = new CTBorder(); border.setColor("auto"); border.setSz(new BigInteger("4")); border.setSpace(new BigInteger("0")); border.setVal(STBorder.SINGLE); TblBorders borders = new TblBorders(); borders.setBottom(border); borders.setLeft(border); borders.setRight(border); borders.setTop(border); borders.setInsideH(border); borders.setInsideV(border); table.getTblPr().setTblBorders(borders); } }
在这篇文章中,我展示了如何创建一个文档、如何为其应用样式以及添加带样式的表格。以后的文章我会展示更多的关于docx文档的示例(比如添加目录、添加图片、添加页眉和页脚等)以及一些关于xlsx文档的示例。