使用Java poi编辑word.docx文档

4,054 阅读5分钟

前置了解

  这里仅说明以docx后缀的word文件,doc文件似乎用的是另一个api,不是很了解。

  首先在使用poi之前,需要了解word.docx的存储方式。右键word文档,我们可以发现选项中是有打开压缩包等选项的。解压缩后文件夹格式如下:

    -word
      -_rels
        - .rels
      -customXml
        ...
      -docProps
        ...
      -media
        - 1.png
        - 2.png
        - ...
      -word
        ...
        - document.xml
        ...
      [Content_Types].xml

  其中我们需要关注的主要是media文件夹,它下面是该word文档中所有的图片;以及word文件夹下的document.xml,该xml文件统领全局,包括了word文档中的文字、格式、图片的引用等等。

使用poi

  如果使用maven的话,可以添加如下引用:

  <dependency>
		  	<groupId>org.apache.poi</groupId>
		  	<artifactId>poi-scratchpad</artifactId>
		  	<version>3.17</version>
  </dependency>

  如果需要处理word中的数学公式,那么还需要添加:

  <dependency>
		    <groupId>org.apache.poi</groupId>
		    <artifactId>ooxml-schemas</artifactId>
		    <version>1.0</version>
  </dependency>

document

  首先是获取文档的内容,XWPFDocument document = new XWPFDocument(new FileInputStream(filePath));

  接下来说下document的整体格式。

  此时我们获取的document,很大程度上就是对应了之前word文件夹下的document.xml文件,我们之后也会很多的使用xml的方式来处理word文档。用文本编辑器打开document.xml,这个文件是经过混淆的,我这里使用sublime的插件来进行重新排版。

  从document.xml文件可以看到,根节点w:document下紧跟的是w:body,body节点下几乎包括了所有的文件信息。body下是三种节点:

1.<w:sectPr>:该节点下定义了该word文档的整体信息,包括页眉页脚的引用,你可以在解压后的word文件夹下找到页眉页脚的定义。另外也包括了页面的大小。这一部分我在使用过程中不需要获取,具体内容也不是很了解。可以在复制新的document时使用document方法直接整体set进去(如果不需要获取格式内容的话,接下来的段落、表格及它们内部的节点同理,格式部分直接用原来的设置);

2.<w:p>:段落,对应XWPFParagraph。段落下主要也有三个节点

  1.<w:pPr>与sectPr同理,格式部分内容,接下来的各部分的格式部分就不在重复了,基本上每一个节点都拥有一个属性,对应poi中的一个类

  2.<w:run>run标签,对应XWPFRun,是段落中格式相同的一部分内容,主要包括了属性部分,图片部分,文字部分。也就是说,一个段落中如果有多个格式的文字,或是图片,那么该段落节点下相应的就会有多个run标签。

--------1.文字部分:在text标签下,设置和获取都很简单,相应对象中有直接的方法

--------2.图片部分:<w:drawing>标签,对应XWPFPictureData。需要注意的是,word中每一张图片都对应一个Id:

<pic:blipFill>
	<a:blip cstate="print" r:embed="rId15"/>
	<a:stretch>
		<a:fillRect/>
	</a:stretch>
</pic:blipFill>

    找到其中的该部分内容,其中我们可以得到该图片的id为rId15,我们可以直接通过XWPFPictureData picture = document.getPictureDataByID(id);来获取指定的图片,获取id的方法放到文章末尾。这个picture拥有getBytes方法,之后想怎么做就看你了=。=另外网上有同学说可能有另一种格式来保存图片,不过我在使用过程中未遇到该种情况,所以不细述。

  3.<m:oMath>公式部分,需要添加文章开头的依赖,对应CTOMath。如果不需要获取公式的话可以跳过。如果大家需要用到公式的话,很可能就需要将他用pandoc将其转成latex格式。我在使用过程中遇到的问题是部门老大的mac无法读取word中的公式部分,以及在使用pandoc转文件格式时会忽略其中的公式,而在windows下是能够正常通过word查看的。最后发现是该部分标签放在的run标签下,这导致了这两个问题。所以我当时需要做的很简单,遍历run标签内元素,遇到公式,将run标签内的公式部分删去,获取父段落,添加到与run标签同级。遍历部分与获取图片的id方法一样,在最后说。

3.<w:tab>表格,对应XWPFTable。表格内除了格式属性外,就是每一行,下面是列,单元格,我们在阅读word文档时可以发现,每一个单元格的内容后都有一个回车符号,这是因为单元格下就是使用<w:p>段落来存储的,所以知道了上面知识,表格也就简单了。

遍历

接下来说下遍历的方法。如果仅仅需要段落、图片或是表格的一种,那么XWPFDocument下有直接的方法来获取;如果需求相对复杂的话,使用XWPFDocument:getBodyElements获取所有元素。通过instanceof判断格式。对于具体的,可以通过:

target.getCTR().setRPr(source.getCTR().getRPr());
XmlCursor cursor = source.getCTR().newCursor();
cursor.selectPath("./*");

while (cursor.toNextSelection()) {
    XmlObject xmlObject = cursor.getObject();
    if (xmlObject instanceof CTText) {
        CTText ctText = (CTText) xmlObject;
        target.setText(ctText.getStringValue());
    } else if (xmlObject instanceof XmlAnyTypeImpl) {
        CTOMath ctoMath = paragraph.getCTP().addNewOMath();
        ctoMath.set(xmlObject);
    }
}

来进行,上述是我复制run标签并删除run内部的段落的部分代码,可以简单参考下。其中公式在run内部时的class类型是XmlAnyTypeImpl。cursor.selectPath("./*");的意思是获取所有子元素。

End

  那么文档部分就简单讲到这了,poi中具体的方法还是需要大家自己探索了。祝大家新年快乐!