(二)、JAVA基于OPENXML的word文档插入、合并、替换操作系列之基础篇-图片在word结构中的存放、插入图片
准备工作
在上一篇的《一个word文档的真实面目》中,我列出了一个word文档被解压后的全部结构,如果没看到的可以通过传送门了解一下。
本篇笔记的内容是后续word插入、替换、合并的基础,如果不考虑带图片的内容,请忽略本篇。
word中图片存放处理
先来看张word解压后与原图的对比, 相信你一眼就能看到media
这个玩意儿,对,你猜的没错,word中的图片它最终会存储media目录下面,那word是怎么找它呢,这就是接下来的内容,让我来一层一层扒开它。
在上一篇中我提到了document.xml
将是本系列的重点,那么现在就要从它开起拔起了,以下贴一段上面那个word的document.xml的片段(限于篇幅,这里就只放一段了)
<!-- 上面部分省略.... -->
<w:p>
<w:pPr>
<w:spacing w:after="450"/>
<w:ind w:left="120"/>
<w:jc w:val="center"/>
</w:pPr>
<w:r>
<w:drawing>
<wp:inline distT="0" distB="0" distL="0" distR="0">
<wp:extent cx="8466666" cy="4496254"/>
<wp:effectExtent l="0" t="0" r="0" b="0"/>
<wp:docPr id="0" name="" descr=""/>
<wp:cNvGraphicFramePr>
<a:graphicFrameLocks noChangeAspect="true"/>
</wp:cNvGraphicFramePr>
<a:graphic>
<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:pic>
<pic:nvPicPr>
<pic:cNvPr id="1" name=""/>
<pic:cNvPicPr/>
</pic:nvPicPr>
<pic:blipFill>
<!-- 这里定义资源ID引用 -->
<a:blip r:embed="rId5"/>
<a:stretch>
<a:fillRect/>
</a:stretch>
</pic:blipFill>
<pic:spPr>
<a:xfrm>
<a:off x="0" y="0"/>
<a:ext cx="8466666" cy="4496254"/>
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst/>
</a:prstGeom>
</pic:spPr>
</pic:pic>
</a:graphicData>
</a:graphic>
</wp:inline>
</w:drawing>
</w:r>
</w:p>
<!-- 下面部分省略.... -->
不知道诸位有没有发现点什么(markdown的代码块中怎么加粗没找到办法,各位自己看吧),<w:drawing>
这个玩意在上一篇的《关于word文档WordprocessingML的表达方式》中提过,它就是用来在word中放图片的,这个标签里面又定义了许多的其他子标签,它们是用来定位这张图片的大小、位置、在word的布局等等信息的,在本篇中不作重点介绍,有兴趣的自行研究。
那么问题来了,既然是放图片的, 那在这段代码中了没看到跟实际图片有关联的地方啊,它是怎么加载图片的呢,稍等莫急,我们重点来看一下这段 <a:blip r:embed="rId5"/>
,这个又是什么意思呢,先卖个关子,下面隆重介绍一下第二个重点要关注的文件document.xml.rels
,在上面的图中我已经框出来了,没印象回过头再去看一下,打开它后是这样的:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Target="styles.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"/>
<Relationship Id="rId2" Target="settings.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings"/>
<Relationship Id="rId3" Target="numbering.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering"/>
<Relationship Id="rId4" Target="media/document_image_rId4.png" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"/>
<!-- 这里定义资源ID与具体资源位置的关联 -->
<Relationship Id="rId5" Target="media/document_image_rId5.png" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"/>
</Relationships>
你们发现了什么? 别告诉我你看到了姑娘,我不信,除非你给我买包好烟,哈哈。
在<a:blip r:embed="rId5"/>
中引用的 r:embed
值就是一个资源ID,而这个资源就是在 document.xml.rels
中定义的,这里说的是资源,而不是图片,大家应该看到了文件中定义的除非了图片,还有其他的,自然不排除word中再嵌套其他文件啥的也是在这里面实现,当然我没有验证, 而本例的word中有两张图片,对应的资源ID分别为 rId4、rId5
这个ID是word在添加图片时自动生成的,而是递增的,会根据当前文件中已有资源数量增加,这个我验证过了。
总结一下就是,在你往word中插入一张图片时,首先它会将这张图片写入到media
目录下,并同时生成一个资源ID, 然后会在 document.xml.rels
中将这个ID与资源具体位置建立关联, 最后在document.xml
中用<w:drawing>
来定义这张图片,并与资源ID绑定上,这样就能找到具体对应的图片了。
获取word中的所有图片
上面我们了解了图片在word中的挂载方式,接下来该是我们盘它的时候了,获取就不用说了。
@Test
public void testGetWordImage() throws InvalidFormatException, IOException {
XWPFDocument document = new XWPFDocument(OPCPackage.open("/Users/tenney/Desktop/2222222222.docx"));
List<XWPFPictureData> allPictures = document.getAllPictures();
System.err.println( "word中图片数据:" + allPictures.size());
allPictures.forEach(p->{
//p.getData() 图片字节数组,可自行处理
System.err.println(p.getFileName() + " : " + p.getPictureType() );
});
}
往word中插入图片
不啰嗦了,直接上代码吧
@Test
public void testAppendWordImage() throws InvalidFormatException, IOException {
// 读入一个文档
XWPFDocument document = new XWPFDocument(OPCPackage.open("/Users/tenney/Desktop/2222222222.docx"));
FileInputStream fis = new FileInputStream("/Users/tenney/Desktop/test.jpg");
//创建一个段落,把图片放到这个段落里面
XWPFParagraph title = document.createParagraph();
XWPFRun run = title.createRun();
run.setText("图片名称");
run.setBold(true);
title.setAlignment(ParagraphAlignment.CENTER);
//添加图片
//该方法会向word中写入图片,并返回资源关系ID,即r:embed值,但打开文档不会显示,因为它未在document.xml文件中画出来
// document.addPictureData(IOUtils.toByteArray(fis), org.apache.poi.xwpf.usermodel.Document.PICTURE_TYPE_JPEG);
run.addBreak();
run.addPicture(fis, XWPFDocument.PICTURE_TYPE_JPEG, "test.jpg", Units.toEMU(300), Units.toEMU(200)); // 300x200 pixels
//将文档重新保存
FileOutputStream fos = new FileOutputStream("/Users/tenney/Desktop/image.docx");
document.write(fos);
fos.close();
}
执行效果如下:
替换word中的图片
这部分交给你们了,不接收反驳,哈哈