一、前言
1.1 目标
在指定DOCX模板时,在模板指定位置插入文本或图片。
1.2 了解
拓展名为DOCX的Word07版文件加药后可以获得xml文件,也直接直接另存为xml。这里面就是特有的文件属性,一下就是图片属性与图片内容两个bean。POI3.17就是直接编写a:graphic,随后的版本就不需要这么麻烦了,直接操作接口就行。但是配置属性(例如浮动文字上方)还是需要编写wp:anchor才行。
二、开始
2.1 模板
本次模板必须是拓展名为DOCX的Word07版文件,03版的实现方法是完全不同的,这是POI团队的锅。
内容参数格式:${参数}
2.2 jar包
POI的一个问题就是jar包升级的问题,每次升级有的地方差异还是挺大的,所选好版本之后开始研究即可。我之前的帖子有用3.17,本次使用4.1.2,插入图片的是现实不一样的。
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.9</version>
</dependency>
2.3 代码
非表格样式处理直接读取段落即可
public void create(String filePath, JSONObject jsonObject) {
try {
InputStream template = new InputStream(new File(filePath));
//获取docx解析对象
XWPFDocument document = new XWPFDocument(template);
//解析替换文本段落对象.
List<XWPFParagraph> paragraphs = document.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
//判断此段落时候需要进行替换
String text = paragraph.getText().trim();
if (WordUtil.checkText(text)) {
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
if (!" ".equals(run.toString().trim())) {
if (run.toString().indexOf("${jpeg}") != -1 && textMap.containsKey("jpeg")){
addPic(run,textMap, 1188000, 1728000,0, 0);
break;
}
//替换模板原来位置
String value = changeValue(run.toString(), jsonObject);
setWrap(value, run);
}
}
}
}
// workBook写入输出流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
document.write(baos);
baos.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
表格内样式处理精确到表格内的单元格,然后再按照段落的方式处理
public void create(String filePath, JSONObject jsonObject) {
try {
InputStream template = new InputStream(new File(filePath));
List<String[]> tableList = Lists.newArrayList();
//解析替换表格对象
List<XWPFTable> tables = document.getTables();
for (int i = 0; i < tables.size(); i++) {
//只处理行数大于等于2的表格,且不循环表头
XWPFTable table = tables.get(i);
if (table.getRows().size() > 1) {
//判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
if (WordUtil.checkText(table.getText())) {
List<XWPFTableRow> rows = table.getRows();
//遍历表格,并替换模板
eachTable(rows, jsonObject);
}
}
}
// workBook写入输出流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
document.write(baos);
baos.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 遍历表格
*
* @param rows 表格行对象
* @param textMap 需要替换的信息集合
*/
public static void eachTable(List<XWPFTableRow> rows, JSONObject textMap) {
for (XWPFTableRow row : rows) {
List<XWPFTableCell> cells = row.getTableCells();
for (XWPFTableCell cell : cells) {
//判断单元格是否需要替换
if (checkText(cell.getText())) {
List<XWPFParagraph> paragraphs = cell.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
// run.setText(changeValue(run.toString(), textMap), 0);
//新增制证照片单独处理
if (run.toString().indexOf("${jpeg}") != -1 && textMap.containsKey("jpeg")){
addPic(run,textMap, 1188000, 1728000,0, 0);
break;
}
String value = changeValue(run.toString(), textMap);
setWrap(value, run);
}
}
}
}
}
}
一些对应的方法,可以放到工具类里面
/**
* 匹配传入信息集合与模板
*
* @param value 模板需要替换的区域
* @param textMap 传入信息集合
* @return 模板需要替换区域信息集合对应值
*/
public static String changeValue(String value, JSONObject textMap) {
boolean flag = false;
Set<Map.Entry<String, Object>> textSets = textMap.entrySet();
for (Map.Entry<String, Object> textSet : textSets) {
//匹配模板与替换值 格式${key}
String key = "${" + textSet.getKey() + "}";
if (value.indexOf(key) != -1) {
value = textSet.getValue() == null ? "" : ("" + textSet.getValue());
flag = true;
break;
}
}
//模板未匹配到区域替换为空
if (checkText(value)) {
value = "";
}
return value;
}
public static void setWrap(String value, XWPFRun run) {
if ( value.indexOf("\n") > 0) {
//设置换行
String[] text = value.split("\n");
for (int f = 0; f < text.length; f++) {
if (f == 0) {
run.setText(text[f].trim(),0);
} else {
// run.addCarriageReturn();//硬回车
// 换行
run.addBreak();
run.setText(text[f]);
}
}
} else {
run.setText((String) value,0);
}
}
/**
* @param ctGraphicalObject 图片数据
* @param deskFileName 图片描述
* @param width 宽
* @param height 高
* @param leftOffset 水平偏移 left
* @param topOffset 垂直偏移 top
* @param behind 文字上方,文字下方
* @return
* @throws Exception
*/
public static CTAnchor getAnchorWithGraphic(CTGraphicalObject ctGraphicalObject, String deskFileName, int width, int height, int leftOffset, int topOffset, boolean behind) {
String anchorXML =""
+"<wp:anchor xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\" simplePos=\"0\" relativeHeight=\"0\" behindDoc=\"" + ((behind) ? 1 : 0) + "\" locked=\"0\" layoutInCell=\"1\" allowOverlap=\"1\">"
+" <wp:simplePos x=\"0\" y=\"0\"/>"
+" <wp:positionH relativeFrom=\"column\">"
+" <wp:posOffset>"+ leftOffset + "</wp:posOffset>"
+" </wp:positionH>"
+" <wp:positionV relativeFrom=\"line\">"
+" <wp:posOffset>"+ topOffset +"</wp:posOffset>"
+" </wp:positionV>"
+" <wp:extent cx=\"" + width + "\" cy=\"" + height + "\"/>"
+" <wp:effectExtent l=\"0\" t=\"0\" r=\"0\" b=\"0\"/>"
+" <wp:wrapNone/>"
+" <wp:docPr id=\"1\" name=\"Drawing 0\" descr=\"" +deskFileName+ "\"/><wp:cNvGraphicFramePr/>"
+"</wp:anchor>";
try {
CTDrawing drawing = CTDrawing.Factory.parse(anchorXML);
CTAnchor anchor = drawing.getAnchorArray(0);
anchor.setGraphic(ctGraphicalObject);
return anchor;
} catch (XmlException e) {
e.printStackTrace();
return null;
}
}
/**
* 新增制证照片单独处理
* @param run
* @param textMap
*/
public static void addPic(XWPFRun run, JSONObject textMap, int width, int height, int leftOffset, int topOffset){
String runText = run.toString().trim();
byte[] zjzp = Base64.decode(textMap.getString("jpeg"));
try(ByteArrayInputStream byteInputStream = new ByteArrayInputStream(zjzp)) {
//1、添加图片
run.addPicture(byteInputStream,XWPFDocument.PICTURE_TYPE_JPEG,"照片", width, height);
//2、获取图片
CTDrawing cTDrawing = run.getCTR().getDrawingArray(0);
CTGraphicalObject cTGraphicalObject = cTDrawing.getInlineArray(0).getGraphic();
//3、设置属性
CTAnchor ctAnchor = getAnchorWithGraphic(cTGraphicalObject,"照片", width, height,leftOffset, topOffset,false);
cTDrawing.setAnchorArray(new CTAnchor[]{ctAnchor});
cTDrawing.removeInline(0);
run.setText(runText.replace("${jpeg}",""), 0);
}catch (Exception e){
e.printStackTrace();
}
}
总结
效果就不展示了,JSON使用的是fastJSON,需要修改的内容放到JSON中即可,也可以自己放置对象。这么操作,已经满足使用