本文为笔记、仅作参考。 前文为:
用FreeMarker生成Word文档。
现在新需求来了,导出的Word文档中、需要包含图片。
大致的处理流程为:
- 解析导出信息、将
抽取出来。 替换为 [img[xxxIdInt]]
;
- 替换 HTML 标签
- 将
[img[xxxIdInt]]
格式的字符串、使用XML标签替换回去。 参考下面的代码段。
- 下载 src 为 byte[];
- 处理为对象;
- 处理对应的 XML 文档,生成 String 和 byte[]
- 替换 docx/zip 文件的某些 entry, 加上图片的 entry.
相关源代码
图片部分的XML大致如下:
public static String template_t_start = "w:t>w:r><w:r>";
public static String template_t_end = "w:r><w:r><w:t>";
public static String template_drawing
= "<w:drawing>" +
"<wp:inline>" +
"<wp:extent cx="{{imgWEMU}}" cy="{{imgHEMU}}"/>" +
"<wp:docPr id="{{imgId}}" name="图片 {{imgId}}"/>" +
"<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">" +
"<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">" +
"<pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">" +
"<pic:nvPicPr>" +
"<pic:cNvPr id="{{imgId}}" name="Picture {{imgId}}"/>" +
"<pic:cNvPicPr/>" +
"pic:nvPicPr>" +
"<pic:blipFill>" +
"<a:blip r:embed="rId{{imgId}}">a:blip>" +
"<a:stretch><a:fillRect/>a:stretch>" +
"pic:blipFill>" +
"<pic:spPr bwMode="auto">" +
"<a:xfrm><a:ext cx="{{imgWEMU}}" cy="{{imgHEMU}}"/>a:xfrm>" +
"<a:prstGeom prst="rect"><a:avLst/>a:prstGeom>" +
"<a:noFill/><a:ln><a:noFill/>a:ln>" +
"pic:spPr>" +
"pic:pic>" +
"a:graphicData>" +
"a:graphic>" +
"wp:inline>" +
"w:drawing>";
public static String imgTemplate = template_t_start + template_drawing + template_t_end;
如果文件版本不一致、请自己抽取 docx 文件中的相关文档来对比。
图片信息模型如下:
public class WordImagePropertyVO {
public static int emuWeight = 9525;
private String imgId;
private String relationshipId;
private Integer width=0;
private Integer height=0;
private Integer imgHEMU;
private Integer imgWEMU;
private String imgSrc;
private byte[] imgByte;
...
}
ZipUtils 类增加了一些方法:
/**
* 自定义的ZIP工具
*/
public class ZipUtils {
/**
* 替换ZIP中的部分条目
* @param zipInputStream 输入zip流
* @param zipOutputStream 写出zip流
* @param replaceItems 替换列表
*/
public static void replaceItems(ZipInputStream zipInputStream,
ZipOutputStream zipOutputStream,
Map replaceItems
) {
if (null == zipInputStream || null == zipOutputStream) {
return;
}
if (null == replaceItems) {
replaceItems = new HashMap<>();
}
ZipEntry entryIn;
try {
while ((entryIn = zipInputStream.getNextEntry()) != null) {
String entryName = entryIn.getName();
boolean shouldReplaceItem = replaceItems.containsKey(entryName);
InputStream targetItemInputStream = replaceItems.get(entryName);
if(shouldReplaceItem && null == targetItemInputStream){
continue;
}
ZipEntry entryOut = new ZipEntry(entryName);
zipOutputStream.putNextEntry(entryOut);
byte[] buf = new byte[8 * 1024];
int len;
if (shouldReplaceItem) {
while ((len = (targetItemInputStream.read(buf))) > 0) {
zipOutputStream.write(buf, 0, len);
}
} else {
while ((len = (zipInputStream.read(buf))) > 0) {
zipOutputStream.write(buf, 0, len);
}
}
zipOutputStream.closeEntry();
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public static void addItems(ZipInputStream zipInputStream,
ZipOutputStream zipOutputStream,
Map addItems
) {
if (null == zipInputStream || null == zipOutputStream || null == addItems || addItems.isEmpty()) {
return;
}
Set itemKeys = addItems.keySet();
try {
for(String entryName :itemKeys){
InputStream targetItemInputStream = addItems.get(entryName);
if(null == entryName || null == targetItemInputStream){
continue;
}
ZipEntry entryOut = new ZipEntry(entryName);
zipOutputStream.putNextEntry(entryOut);
byte[] buf = new byte[8 * 1024];
int len;
while ((len = (targetItemInputStream.read(buf))) > 0) {
zipOutputStream.write(buf, 0, len);
}
zipOutputStream.closeEntry();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
1. 引入文件格式:
根据需要增加, 如
png
或
jpeg
[Content_Types].xml
文件中需要增加以下内容:
<Default Extension="png" ContentType="image/png"/>
<Default Extension="jpeg" ContentType="image/jpeg"/>
2. word/document.xml
引入命名空间:
此部分、可以将一个无图的docx、一个有图的docx、抽取相关xml文件格式化后对比得出。
<w:document
......
xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas"
xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml"
xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup"
xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk"
xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="w14 wp14"
>
图片绘制元素w:drawing
<w:r>
<w:drawing>
...
w:drawing>
w:r>
Docx文件具体的API和参考文档请访问:
https://msdn.microsoft.com/en-us/library/documentformat.openxml.drawing.wordprocessing(v=office.14).aspx
w:drawing
子元素: 行内元素(inline)
<w:drawing>
<wp:inline distT="0" distB="0" distL="0" distR="0">
...
wp:inline>
w:drawing>
其中
distT
之类的属性是距离上方Text的间距。(Distance From Text on Top Edge)
wp:inline
子元素:
<wp:inline distT="0" distB="0" distL="0" distR="0">
<wp:extent cx="5274310" cy="3620117"/>
<wp:effectExtent l="0" t="0" r="2540" b="0"/>
<wp:docPr id="20" name="图片 20"/>
<wp:cNvGraphicFramePr>
<a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
noChangeAspect="1"/>
wp:cNvGraphicFramePr>
......
wp:inline>
其中:
wp:inline
子元素
:
<wp:inline distT="0" distB="0" distL="0" distR="0">
......
<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
...
a:graphic>
wp:inline>
a:graphic
单一子元素
<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
...
a:graphicData>
a:graphic>
子元素:
<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
...
pic:pic>
a:graphicData>
pic:pic
子元素列表:
<pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:nvPicPr>
<pic:cNvPr id="20" name="Picture 20"/>
<pic:cNvPicPr/>
pic:nvPicPr>
<pic:blipFill>
...
pic:blipFill>
<pic:spPr bwMode="auto">
...
pic:spPr>
pic:pic>
必需元素:
:
表示
Non-Visual Picture Properties:
。
cNvPicPr
表示 (Non-Visual Picture Drawing Properties)
- 非必需元素
a:picLocks
, 指定生成程序的行为等。
cNvPr
表示(Non-Visual Drawing Properties),是必需元素, 其中ID互相之间不重复即可
必需元素:
:
Picture Fill, 如下所示:
<pic:blipFill>
<a:blip r:embed="rId20">
a:blip>
<a:srcRect/>
<a:stretch>
<a:fillRect/>
a:stretch>
pic:blipFill>
其中, 重要元素是
:
- 重要属性:
embed
(Embedded Picture Reference),表示嵌入的图片引用。
- 引用关系处于文件:
word/_rels/document.xml.rels
文件中.
- 重要子元素:
控制缩放,
表示填满?
扩展属性列表, BlipExtensionList, 可以忽略
必需元素:
:
Shape Properties.
<pic:spPr bwMode="auto">
<a:xfrm>
<a:off x="0" y="0"/>
<a:ext cx="5274310" cy="3620117"/>
a:xfrm>
<a:prstGeom prst="rect">
<a:avLst/>
a:prstGeom>
<a:noFill/>
<a:ln>
<a:noFill/>
a:ln>
pic:spPr>
其中:
bwMode
属性(Black and White Mode)
- Transform2D, 属性。
表示 Offset; 有默认值可忽略。
- 重要属性:
表示大小, 参考上文。
3. word/_rels/document.xml.rels
文件中存在对应的 Rid
形如:
<Relationships
xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
....
<Relationship Id="rId20"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
Target="media/image20.png"/>
...
Relationships>
可以看到,
rId20
的关系。
如
word/media/image20.png
5. 注意事项: