前置了解
这里仅说明以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中具体的方法还是需要大家自己探索了。祝大家新年快乐!