1. 概述

在本文中,我们将重点讨论如何使用 docx4j 库创建 .docx 文档。

Docx4j 是一个 Java 库,用于创建和处理 Office OpenXML 文件,这意味着它只能处理 .docx 文件类型,而旧版本的 Microsoft Word 使用的是 .doc 扩展名(二进制文件)。

请注意,微软 Office 从 2007 版开始支持 OpenXML 格式。

2. Maven 依赖

<dependency>
    <groupId>org.docx4j</groupId>
    <artifactId>docx4j</artifactId>
    <version>6.1.2</version>
</dependency>
<dependency> 
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
    <version>2.3.1</version>
</dependency>

需要依赖 JAXB,因为 docx4j 在底层使用该库来序列化/反序列化 docx 文件中的 XML 部分。

3. 创建 Docx 文档

3.1. 文本元素和样式

让我们先来看看如何创建一个简单的 docx 文件 ,并插入一个文本段落:

WordprocessingMLPackage wordPackage = WordprocessingMLPackage.createPackage();
MainDocumentPart mainDocumentPart = wordPackage.getMainDocumentPart();
mainDocumentPart.addStyledParagraphOfText("Title", "Hello World!");
mainDocumentPart.addParagraphOfText("Welcome To Baeldung");
File exportFile = new File("welcome.docx");
wordPackage.save(exportFile);

下面是生成的 welcome.docx 文件:

im1

要创建一个新文档,我们必须使用 WordprocessingMLPackage,它表示 OpenXML 格式的 docx 文件,而 MainDocumentPart class则表示 document.xml 部分。

为了理清思路,我们解压 welcome.docx 文件(是的你没有看错,可以使用压缩工具打开),并打开 word/document.xml 文件,看看 XML 表示格式是什么样子的:

<w:body>
    <w:p>
        <w:pPr>
            <w:pStyle w:val="Title"/>
        </w:pPr>
        <w:r>
            <w:t>Hello World!</w:t>
        </w:r>
    </w:p>
    <w:p>
        <w:r>
            <w:t>Welcome To Baeldung!</w:t>
        </w:r>
    </w:p>
</w:body>

正如我们所看到的,每个句子都由段落 (p) 内的运行块 (run/r) 文本 (t) 表示,这就是 addParagraphOfText() 方法的作用。

addStyledParagraphOfText() 方法的作用远不止于此,它还创建了一个段落属性 (pPr),用于保存应用于段落的样式。

简单地说,段落声明独立的运行块,每个运行块包含一些文本元素:

p-r-t

要创建美观的文档,我们需要完全控制这些元素(段落、运行块和文本)。

因此,让我们来了解一下如何使用 runProperties (RPr) 对象为我们的内容设计风格:

ObjectFactory factory = Context.getWmlObjectFactory();
P p = factory.createP();
R r = factory.createR();
Text t = factory.createText();
t.setValue("Welcome To Baeldung");
r.getContent().add(t);
p.getContent().add(r);
RPr rpr = factory.createRPr();       
BooleanDefaultTrue b = new BooleanDefaultTrue();
rpr.setB(b);
rpr.setI(b);
rpr.setCaps(b);
Color green = factory.createColor();
green.setVal("green");
rpr.setColor(green);
r.setRPr(rpr);
mainDocumentPart.getContent().add(p);
File exportFile = new File("welcome.docx");
wordPackage.save(exportFile);

结果是这样的

im2a

在使用 createP()、createR() 和 createText() 分别创建了一个段落、一个运行块和一个文本元素后,我们声明了一个新的 runProperties 对象 (RPr),以便为文本元素添加一些样式。

rpr 对象用于设置格式属性:粗体 (B)、斜体 (I) 和大写 (Caps),这些属性通过 setRPr() 方法应用于文本运行块。

3.2. 使用图片

Docx4j 提供了一种在 Word 文档中添加图像的简便方法:

File image = new File("image.jpg" );
byte[] fileContent = Files.readAllBytes(image.toPath());
BinaryPartAbstractImage imagePart = BinaryPartAbstractImage
  .createImagePart(wordPackage, fileContent);
Inline inline = imagePart.createImageInline(
  "Baeldung Image (filename hint)", "Alt Text", 1, 2, false);
P Imageparagraph = addImageToParagraph(inline);
mainDocumentPart.getContent().add(Imageparagraph);

下面是 addImageToParagraph() 方法的实现过程:

private static P addImageToParagraph(Inline inline) {
    ObjectFactory factory = new ObjectFactory();
    P p = factory.createP();
    R r = factory.createR();
    p.getContent().add(r);
    Drawing drawing = factory.createDrawing();
    r.getContent().add(drawing);
    drawing.getAnchorOrInline().add(inline);
    return p;
}

首先,我们创建了包含要添加到主文档部分的图片的文件,然后,我们将代表图片的字节数组与 wordMLPackage 对象连接起来。

创建图像部分后,我们需要使用 createImageInline() 方法创建一个 Inline 对象。

addImageToParagraph() 方法将 Inline 对象嵌入到绘图中,以便将其添加到运行块中。

最后,像文本段落一样,包含图片的段落被添加到 mainDocumentPart 中。

下面是生成的文档结果:

im3a

3.3. 创建表格

Docx4j 还能轻松操作表格 (Tbl)、行 (Tr) 和列 (Tc)。

让我们看看如何创建一个 3×3 表格并添加一些内容:

int writableWidthTwips = wordPackage.getDocumentModel()
  .getSections().get(0).getPageDimensions().getWritableWidthTwips();
int columnNumber = 3;
Tbl tbl = TblFactory.createTable(3, 3, writableWidthTwips/columnNumber);     
List<Object> rows = tbl.getContent();
for (Object row : rows) {
    Tr tr = (Tr) row;
    List<Object> cells = tr.getContent();
    for(Object cell : cells) {
        Tc td = (Tc) cell;
        td.getContent().add(p);
    }
}

给定了行和列,createTable() 方法创建一个新的 Tbl 对象,第三个参数指的是列宽,单位是 twips(一种距离测量方法--1/1440 英寸)。

创建后,我们可以遍历 tbl 对象的内容,并在每个单元格中添加 Paragraph 对象。

最终结果:

im4a

4. 读取 Docx 文档

学会了如何使用 docx4j 创建文档,那么让我们来看看如何读取现有 docx 文件并打印其内容:

File doc = new File("helloWorld.docx");
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage
  .load(doc);
MainDocumentPart mainDocumentPart = wordMLPackage
  .getMainDocumentPart();
String textNodesXPath = "//w:t";
List<Object> textNodes= mainDocumentPart
  .getJAXBNodesViaXPath(textNodesXPath, true);
for (Object obj : textNodes) {
    Text text = (Text) ((JAXBElement) obj).getValue();
    String textValue = text.getValue();
    System.out.println(textValue);
}

在本例中,我们使用 load() 方法,基于现有的 helloWorld.docx 文件创建了一个 WordprocessingMLPackage 对象。

然后,我们使用 XPath 表达式 (//w:t) 获取所有文本节点。

getJAXBNodesViaXPath() 方法返回一个 JAXBElement 对象列表。

因此,mainDocumentPart 对象内的所有文本元素都会打印到控制台中。

注:我们可以随时解压 docx 文件,以便更好地了解 XML 结构,这有助于分析问题,并更好地了解如何解决问题。

5. 总结

在本文中,我们学习了 docx4j 如何使 MSWord 文档中的复杂操作(如创建段落、表格、文档部分和添加图片)变得更容易。

最后本文的代码片段可以一如既往地在 [GitHub】(https://github.com/eugenp/tutorials/tree/master/libraries-data-io) 上找到。