依赖

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

核心工具类

package com.dist.web.util.poiword;

import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils; import java.io.*;
import java.util.*; /**
* Create by IntelliJ Idea 2018.2
*
* @author: qyp
* Date: 2019-10-25 14:48
*/
public class DynWordUtils { private final Logger logger = LoggerFactory.getLogger(DynWordUtils.class); /**
* 被list替换的段落 被替换的都是oldParagraph
*/
private XWPFParagraph oldParagraph; /**
* 参数
*/
private Map<String, Object> paramMap; /**
* 当前元素的位置
*/
int n = 0; /**
* 判断当前是否是遍历的表格
*/
boolean isTable = false; /**
* 模板对象
*/
XWPFDocument templateDoc; /**
* 默认字体的大小
*/
final int DEFAULT_FONT_SIZE = 10; /**
* 重复模式的占位符所在的行索引
*/
private int currentRowIndex; /**
* 入口
*
* @param paramMap 模板中使用的参数
* @param templatePaht 模板全路径
* @param outPath 生成的文件存放的本地全路径
*/
public static void process(Map<String, Object> paramMap, String templatePaht, String outPath) {
DynWordUtils dynWordUtils = new DynWordUtils();
dynWordUtils.setParamMap(paramMap);
dynWordUtils.createWord(templatePaht, outPath);
} /**
* 生成动态的word
* @param templatePath
* @param outPath
*/
public void createWord(String templatePath, String outPath) {
try (FileOutputStream outStream = new FileOutputStream(outPath)) {
InputStream inputStream = new FileInputStream(ResourceUtils.getFile(templatePath));
templateDoc = new XWPFDocument(OPCPackage.open(inputStream));
parseTemplateWord();
templateDoc.write(outStream);
} catch (Exception e) {
StackTraceElement[] stackTrace = e.getStackTrace(); String className = stackTrace[0].getClassName();
String methodName = stackTrace[0].getMethodName();
int lineNumber = stackTrace[0].getLineNumber(); logger.error("错误:第:{}行, 类名:{}, 方法名:{}", lineNumber, className, methodName);
throw new RuntimeException(e.getCause().getMessage());
}
} /**
* 解析word模板
*/
public void parseTemplateWord() throws Exception { List<IBodyElement> elements = templateDoc.getBodyElements(); for (; n < elements.size(); n++) {
IBodyElement element = elements.get(n);
// 普通段落
if (element instanceof XWPFParagraph) { XWPFParagraph paragraph = (XWPFParagraph) element;
oldParagraph = paragraph;
if (paragraph.getParagraphText().isEmpty()) {
continue;
} delParagraph(paragraph); } else if (element instanceof XWPFTable) {
// 表格
isTable = true;
XWPFTable table = (XWPFTable) element; delTable(table, paramMap);
isTable = false;
}
} } /**
* 处理段落
*/
private void delParagraph(XWPFParagraph paragraph) throws Exception {
List<XWPFRun> runs = oldParagraph.getRuns();
StringBuilder sb = new StringBuilder();
for (XWPFRun run : runs) {
String text = run.getText(0);
if (text == null) {
continue;
}
sb.append(text);
run.setText("", 0);
}
Placeholder(paragraph, runs, sb);
} /**
* 匹配传入信息集合与模板
*
* @param placeholder 模板需要替换的区域()
* @param paramMap 传入信息集合
* @return 模板需要替换区域信息集合对应值
*/
public void changeValue(XWPFRun currRun, String placeholder, Map<String, Object> paramMap) throws Exception { String placeholderValue = placeholder;
if (paramMap == null || paramMap.isEmpty()) {
return;
} Set<Map.Entry<String, Object>> textSets = paramMap.entrySet();
for (Map.Entry<String, Object> textSet : textSets) {
//匹配模板与替换值 格式${key}
String mapKey = textSet.getKey();
String docKey = PoiWordUtils.getDocKey(mapKey); if (placeholderValue.indexOf(docKey) != -1) {
Object obj = textSet.getValue();
// 需要添加一个list
if (obj instanceof List) {
placeholderValue = delDynList(placeholder, (List) obj);
} else {
placeholderValue = placeholderValue.replaceAll(
PoiWordUtils.getPlaceholderReg(mapKey)
, String.valueOf(obj));
}
}
} currRun.setText(placeholderValue, 0);
} /**
* 处理的动态的段落(参数为list)
*
* @param placeholder 段落占位符
* @param obj
* @return
*/
private String delDynList(String placeholder, List obj) {
String placeholderValue = placeholder;
List dataList = obj;
Collections.reverse(dataList);
for (int i = 0, size = dataList.size(); i < size; i++) {
Object text = dataList.get(i);
// 占位符的那行, 不用重新创建新的行
if (i == 0) {
placeholderValue = String.valueOf(text);
} else {
XWPFParagraph paragraph = createParagraph(String.valueOf(text));
if (paragraph != null) {
oldParagraph = paragraph;
}
// 增加段落后doc文档的element的size会随着增加(在当前行的上面添加
// 这里减操作是回退并解析新增的行(因为可能新增的带有占位符,这里为了支持图片和表格)
if (!isTable) {
n--;
}
}
}
return placeholderValue;
} /**
* 创建段落 <p></p>
*
* @param texts
*/
public XWPFParagraph createParagraph(String... texts) { // 使用游标创建一个新行
XmlCursor cursor = oldParagraph.getCTP().newCursor();
XWPFParagraph newPar = templateDoc.insertNewParagraph(cursor);
// 设置段落样式
newPar.getCTP().setPPr(oldParagraph.getCTP().getPPr()); copyParagraph(oldParagraph, newPar, texts); return newPar;
} /**
* 处理表格(遍历)
*
* @param table 表格
* @param paramMap 需要替换的信息集合
*/
public void delTable(XWPFTable table, Map<String, Object> paramMap) throws Exception {
List<XWPFTableRow> rows = table.getRows();
for (int i = 0, size = rows.size(); i < size; i++) {
XWPFTableRow row = rows.get(i);
currentRowIndex = i;
// 如果是动态添加行 直接处理后终止
if (delAndJudgeRow(table, paramMap, row)) {
return;
}
}
} /**
* 判断并且是否是动态行,并且处理表格占位符
* @param table 表格对象
* @param paramMap 参数map
* @param row 当前行
* @return
* @throws Exception
*/
private boolean delAndJudgeRow(XWPFTable table, Map<String, Object> paramMap, XWPFTableRow row) throws Exception {
// 当前行是动态行标志
if (PoiWordUtils.isAddRow(row)) {
List<XWPFTableRow> xwpfTableRows = addAndGetRows(table, row, paramMap);
// 回溯添加的行,这里是试图处理动态添加的图片
for (XWPFTableRow tbRow : xwpfTableRows) {
delAndJudgeRow(table, paramMap, tbRow);
}
return true;
} // 如果是重复添加的行
if (PoiWordUtils.isAddRowRepeat(row)) {
List<XWPFTableRow> xwpfTableRows = addAndGetRepeatRows(table, row, paramMap);
/*// 回溯添加的行,这里是试图处理动态添加的图片
for (XWPFTableRow tbRow : xwpfTableRows) {
delAndJudgeRow(table, paramMap, tbRow);
}*/
return true;
}
// 当前行非动态行标签
List<XWPFTableCell> cells = row.getTableCells();
for (XWPFTableCell cell : cells) {
//判断单元格是否需要替换
if (PoiWordUtils.checkText(cell.getText())) {
List<XWPFParagraph> paragraphs = cell.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
List<XWPFRun> runs = paragraph.getRuns();
StringBuilder sb = new StringBuilder();
for (XWPFRun run : runs) {
sb.append(run.toString());
run.setText("", 0);
}
Placeholder(paragraph, runs, sb);
}
}
}
return false;
} /**
* 处理占位符
* @param runs 当前段的runs
* @param sb 当前段的内容
* @throws Exception
*/
private void Placeholder(XWPFParagraph currentPar, List<XWPFRun> runs, StringBuilder sb) throws Exception {
if (runs.size() > 0) {
String text = sb.toString();
XWPFRun currRun = runs.get(0);
if (PoiWordUtils.isPicture(text)) {
// 该段落是图片占位符
ImageEntity imageEntity = (ImageEntity) PoiWordUtils.getValueByPlaceholder(paramMap, text);
int indentationFirstLine = currentPar.getIndentationFirstLine();
// 清除段落的格式,否则图片的缩进有问题
currentPar.getCTP().setPPr(null);
//设置缩进
currentPar.setIndentationFirstLine(indentationFirstLine);
addPicture(currRun, imageEntity);
} else {
changeValue(currRun, text, paramMap);
}
}
} /**
* 添加图片
* @param currRun 当前run
* @param imageEntity 图片对象
* @throws InvalidFormatException
* @throws FileNotFoundException
*/
private void addPicture(XWPFRun currRun, ImageEntity imageEntity) throws InvalidFormatException, FileNotFoundException {
Integer typeId = imageEntity.getTypeId().getTypeId();
String picId = currRun.getDocument().addPictureData(new FileInputStream(imageEntity.getUrl()), typeId);
ImageUtils.createPicture(currRun, picId, templateDoc.getNextPicNameNumber(typeId),
imageEntity.getWidth(), imageEntity.getHeight());
} /**
* 添加行 标签行不是新创建的
*
* @param table
* @param flagRow flagRow 表有标签的行
* @param paramMap 参数
*/
private List<XWPFTableRow> addAndGetRows(XWPFTable table, XWPFTableRow flagRow, Map<String, Object> paramMap) throws Exception {
List<XWPFTableCell> flagRowCells = flagRow.getTableCells();
XWPFTableCell flagCell = flagRowCells.get(0); String text = flagCell.getText();
List<List<String>> dataList = (List<List<String>>) PoiWordUtils.getValueByPlaceholder(paramMap, text); // 新添加的行
List<XWPFTableRow> newRows = new ArrayList<>(dataList.size());
if (dataList == null || dataList.size() <= 0) {
return newRows;
} XWPFTableRow currentRow = flagRow;
int cellSize = flagRow.getTableCells().size();
for (int i = 0, size = dataList.size(); i < size; i++) {
if (i != 0) {
currentRow = table.createRow();
// 复制样式
if (flagRow.getCtRow() != null) {
currentRow.getCtRow().setTrPr(flagRow.getCtRow().getTrPr());
}
}
addRow(flagCell, currentRow, cellSize, dataList.get(i));
newRows.add(currentRow);
}
return newRows;
} /**
* 添加重复多行 动态行 每一行都是新创建的
* @param table
* @param flagRow
* @param paramMap
* @return
* @throws Exception
*/
private List<XWPFTableRow> addAndGetRepeatRows(XWPFTable table, XWPFTableRow flagRow, Map<String, Object> paramMap) throws Exception {
List<XWPFTableCell> flagRowCells = flagRow.getTableCells();
XWPFTableCell flagCell = flagRowCells.get(0);
String text = flagCell.getText();
List<List<String>> dataList = (List<List<String>>) PoiWordUtils.getValueByPlaceholder(paramMap, text);
String tbRepeatMatrix = PoiWordUtils.getTbRepeatMatrix(text);
Assert.notNull(tbRepeatMatrix, "模板矩阵不能为空"); // 新添加的行
List<XWPFTableRow> newRows = new ArrayList<>(dataList.size());
if (dataList == null || dataList.size() <= 0) {
return newRows;
} String[] split = tbRepeatMatrix.split(PoiWordUtils.tbRepeatMatrixSeparator);
int startRow = Integer.parseInt(split[0]);
int endRow = Integer.parseInt(split[1]);
int startCell = Integer.parseInt(split[2]);
int endCell = Integer.parseInt(split[3]); XWPFTableRow currentRow;
for (int i = 0, size = dataList.size(); i < size; i++) {
int flagRowIndex = i % (endRow - startRow + 1);
XWPFTableRow repeatFlagRow = table.getRow(flagRowIndex);
// 清除占位符那行
if (i == 0) {
table.removeRow(currentRowIndex);
}
currentRow = table.createRow();
// 复制样式
if (repeatFlagRow.getCtRow() != null) {
currentRow.getCtRow().setTrPr(repeatFlagRow.getCtRow().getTrPr());
}
addRowRepeat(startCell, endCell, currentRow, repeatFlagRow, dataList.get(i)); // 处理新生成的那行是否有占位符需要处理
delAndJudgeRow(table, paramMap, currentRow); newRows.add(currentRow);
}
deleteTemplateRow(startRow, endRow, table);
return newRows;
} /**
* 删除模板行
*/
private void deleteTemplateRow(int startRowIdx, int endRowIdx, XWPFTable table) {
for (;startRowIdx <= endRowIdx; startRowIdx++) {
table.removeRow(0);
}
} /**
* 根据模板cell添加新行
*
* @param flagCell 模板列(标记占位符的那个cell)
* @param row 新增的行
* @param cellSize 每行的列数量(用来补列补足的情况)
* @param rowDataList 每行的数据
*/
private void addRow(XWPFTableCell flagCell, XWPFTableRow row, int cellSize, List<String> rowDataList) {
for (int i = 0; i < cellSize; i++) {
XWPFTableCell cell = row.getCell(i);
cell = cell == null ? row.createCell() : row.getCell(i);
if (i < rowDataList.size()) {
PoiWordUtils.copyCellAndSetValue(flagCell, cell, rowDataList.get(i));
} else {
// 数据不满整行时,添加空列
PoiWordUtils.copyCellAndSetValue(flagCell, cell, "");
}
}
} /**
* 根据模板cell 添加重复行
* @param startCell 模板列的开始位置
* @param endCell 模板列的结束位置
* @param currentRow 创建的新行
* @param repeatFlagRow 模板列所在的行
* @param rowDataList 每行的数据
*/
private void addRowRepeat(int startCell, int endCell, XWPFTableRow currentRow, XWPFTableRow repeatFlagRow, List<String> rowDataList) {
int cellSize = repeatFlagRow.getTableCells().size();
for (int i = 0; i < cellSize; i++) {
XWPFTableCell cell = currentRow.getCell(i);
cell = cell == null ? currentRow.createCell() : currentRow.getCell(i);
int flagCellIndex = i % (endCell - startCell + 1);
XWPFTableCell repeatFlagCell = repeatFlagRow.getCell(flagCellIndex);
if (i < rowDataList.size()) {
PoiWordUtils.copyCellAndSetValue(repeatFlagCell, cell, rowDataList.get(i));
} else {
// 数据不满整行时,添加空列
PoiWordUtils.copyCellAndSetValue(repeatFlagCell, cell, "");
}
}
} /**
* 复制段落
*
* @param sourcePar 原段落
* @param targetPar
* @param texts
*/
private void copyParagraph(XWPFParagraph sourcePar, XWPFParagraph targetPar, String... texts) { targetPar.setAlignment(sourcePar.getAlignment());
targetPar.setVerticalAlignment(sourcePar.getVerticalAlignment()); // 设置布局
targetPar.setAlignment(sourcePar.getAlignment());
targetPar.setVerticalAlignment(sourcePar.getVerticalAlignment()); if (texts != null && texts.length > 0) {
String[] arr = texts;
XWPFRun xwpfRun = sourcePar.getRuns().size() > 0 ? sourcePar.getRuns().get(0) : null; for (int i = 0, len = texts.length; i < len; i++) {
String text = arr[i];
XWPFRun run = targetPar.createRun(); run.setText(text); run.setFontFamily(xwpfRun.getFontFamily());
int fontSize = xwpfRun.getFontSize();
run.setFontSize((fontSize == -1) ? DEFAULT_FONT_SIZE : fontSize);
run.setBold(xwpfRun.isBold());
run.setItalic(xwpfRun.isItalic());
}
}
} public void setParamMap(Map<String, Object> paramMap) {
this.paramMap = paramMap;
}
}

poi工具类

PoiWordUtils

package com.dist.web.util.poiword;

import org.apache.poi.xwpf.usermodel.*;
import org.springframework.util.Assert; import java.util.List;
import java.util.Map;
import java.util.Optional; /**
* Create by IntelliJ Idea 2018.2
*
* @author: qyp
* Date: 2019-10-26 2:12
*/
public class PoiWordUtils { /**
* 占位符第一个字符
*/
public static final String PREFIX_FIRST = "$"; /**
* 占位符第二个字符
*/
public static final String PREFIX_SECOND = "{"; /**
* 占位符的前缀
*/
public static final String PLACEHOLDER_PREFIX = PREFIX_FIRST + PREFIX_SECOND; /**
* 占位符后缀
*/
public static final String PLACEHOLDER_END = "}"; /**
* 表格中需要动态添加行的独特标记
*/
public static final String addRowText = "tbAddRow:"; public static final String addRowRepeatText = "tbAddRowRepeat:"; /**
* 表格中占位符的开头 ${tbAddRow: 例如${tbAddRow:tb1}
*/
public static final String addRowFlag = PLACEHOLDER_PREFIX + addRowText; /**
* 表格中占位符的开头 ${tbAddRowRepeat: 例如 ${tbAddRowRepeat:0,2,0,1} 第0行到第2行,第0列到第1列 为模板样式
*/
public static final String addRowRepeatFlag = PLACEHOLDER_PREFIX + addRowRepeatText; /**
* 重复矩阵的分隔符 比如:${tbAddRowRepeat:0,2,0,1} 分隔符为 ,
*/
public static final String tbRepeatMatrixSeparator = ","; /**
* 占位符的后缀
*/
public static final String PLACEHOLDER_SUFFIX = "}"; /**
* 图片占位符的前缀
*/
public static final String PICTURE_PREFIX = PLACEHOLDER_PREFIX + "image:"; /**
* 判断当前行是不是标志表格中需要添加行
*
* @param row
* @return
*/
public static boolean isAddRow(XWPFTableRow row) {
return isDynRow(row, addRowFlag);
} /**
* 添加重复模板动态行(以多行为模板)
* @param row
* @return
*/
public static boolean isAddRowRepeat(XWPFTableRow row) {
return isDynRow(row, addRowRepeatFlag);
} private static boolean isDynRow(XWPFTableRow row, String dynFlag) {
if (row == null) {
return false;
}
List<XWPFTableCell> tableCells = row.getTableCells();
if (tableCells != null) {
XWPFTableCell cell = tableCells.get(0);
if (cell != null) {
String text = cell.getText();
return text != null && text.startsWith(dynFlag);
}
}
return false;
} /**
* 从参数map中获取占位符对应的值
*
* @param paramMap
* @param key
* @return
*/
public static Object getValueByPlaceholder(Map<String, Object> paramMap, String key) {
if (paramMap != null) {
if (key != null) {
return paramMap.get(getKeyFromPlaceholder(key));
}
}
return null;
} /**
* 后去占位符的重复行列矩阵
* @param key 占位符
* @return {0,2,0,1}
*/
public static String getTbRepeatMatrix(String key) {
Assert.notNull(key, "占位符为空");
String $1 = key.replaceAll("\\" + PREFIX_FIRST + "\\" + PREFIX_SECOND + addRowRepeatText + "(.*:)(.*)" + "\\" + PLACEHOLDER_SUFFIX, "$2");
return $1;
} /**
* 从占位符中获取key
*
* @return
*/
public static String getKeyFromPlaceholder(String placeholder) {
return Optional.ofNullable(placeholder).map(p -> p.replaceAll("[\\$\\{\\}]", "")).get();
} public static void main(String[] args) {
String s = "${aa}";
s = s.replaceAll(PLACEHOLDER_PREFIX + PLACEHOLDER_SUFFIX , "");
System.out.println(s);
// String keyFromPlaceholder = getKeyFromPlaceholder("${tbAddRow:tb1}");
// System.out.println(keyFromPlaceholder);
} /**
* 复制列的样式,并且设置值
* @param sourceCell
* @param targetCell
* @param text
*/
public static void copyCellAndSetValue(XWPFTableCell sourceCell, XWPFTableCell targetCell, String text) {
//段落属性
List<XWPFParagraph> sourceCellParagraphs = sourceCell.getParagraphs();
if (sourceCellParagraphs == null || sourceCellParagraphs.size() <= 0) {
return;
}
XWPFParagraph sourcePar = sourceCellParagraphs.get(0);
XWPFParagraph targetPar = targetCell.getParagraphs().get(0); // 设置段落的样式
targetPar.getCTP().setPPr(sourcePar.getCTP().getPPr()); List<XWPFRun> sourceParRuns = sourcePar.getRuns();
if (sourceParRuns != null && sourceParRuns.size() > 0) {
// 如果当前cell中有run
List<XWPFRun> runs = targetPar.getRuns();
Optional.ofNullable(runs).ifPresent(rs -> rs.stream().forEach(r -> r.setText("", 0)));
if (runs != null && runs.size() > 0) {
runs.get(0).setText(text, 0);
} else {
XWPFRun cellR = targetPar.createRun();
cellR.setText(text, 0);
// 设置列的样式位模板的样式
targetCell.getCTTc().setTcPr(sourceCell.getCTTc().getTcPr());
}
setTypeface(sourcePar, targetPar);
} else {
// targetCell.setText(text);
List<XWPFRun> runs = targetPar.getRuns();
if (runs != null && runs.size() > 0) {
runs.get(0).setText(text, 0);
} else {
XWPFRun newRun = targetPar.createRun();
newRun.setText(text, 0);
}
}
} /**
* 复制字体
*/
private static void setTypeface(XWPFParagraph sourcePar, XWPFParagraph targetPar) {
XWPFRun sourceRun = sourcePar.getRuns().get(0);
String fontFamily = sourceRun.getFontFamily();
//int fontSize = sourceRun.getFontSize();
String color = sourceRun.getColor();
// String fontName = sourceRun.getFontName();
boolean bold = sourceRun.isBold();
boolean italic = sourceRun.isItalic();
int kerning = sourceRun.getKerning();
// String style = sourcePar.getStyle();
UnderlinePatterns underline = sourceRun.getUnderline(); XWPFRun targetRun = targetPar.getRuns().get(0);
targetRun.setFontFamily(fontFamily);
// targetRun.setFontSize(fontSize == -1 ? 10 : fontSize);
targetRun.setBold(bold);
targetRun.setColor(color);
targetRun.setItalic(italic);
targetRun.setKerning(kerning);
targetRun.setUnderline(underline);
//targetRun.setFontSize(fontSize);
}
/**
* 判断文本中时候包含$
* @param text 文本
* @return 包含返回true,不包含返回false
*/
public static boolean checkText(String text){
boolean check = false;
if(text.indexOf(PLACEHOLDER_PREFIX)!= -1){
check = true;
}
return check;
} /**
* 获得占位符替换的正则表达式
* @return
*/
public static String getPlaceholderReg(String text) {
return "\\" + PREFIX_FIRST + "\\" + PREFIX_SECOND + text + "\\" + PLACEHOLDER_SUFFIX;
} public static String getDocKey(String mapKey) {
return PLACEHOLDER_PREFIX + mapKey + PLACEHOLDER_SUFFIX;
} /**
* 判断当前占位符是不是一个图片占位符
* @param text
* @return
*/
public static boolean isPicture(String text) {
return text.startsWith(PICTURE_PREFIX);
} /**
* 删除一行的列
* @param row
*/
public static void removeCells(XWPFTableRow row) {
int size = row.getTableCells().size();
try {
for (int i = 0; i < size; i++) {
row.removeCell(i);
}
} catch (Exception e) { }
}
}

对图片的支持

ImageEntity

package per.qiao.utils.hutool.poi;

import static per.qiao.utils.hutool.poi.ImageUtils.ImageType.PNG;

/**
* Create by IntelliJ Idea 2018.2
*
* 图片实体对象
*
* @author: qyp
* Date: 2019-10-26 21:52
*/
public class ImageEntity { /**
* 图片宽度
*/
private int width = 400; /**
* 图片高度
*/
private int height = 300; /**
* 图片地址
*/
private String url; /**
* 图片类型
* @see ImageUtils.ImageType
*/
private ImageUtils.ImageType typeId = PNG; public int getWidth() {
return width;
} public void setWidth(int width) {
this.width = width;
} public int getHeight() {
return height;
} public void setHeight(int height) {
this.height = height;
} public String getUrl() {
return url;
} public void setUrl(String url) {
this.url = url;
} public ImageUtils.ImageType getTypeId() {
return typeId;
} public void setTypeId(ImageUtils.ImageType typeId) {
this.typeId = typeId;
}
}

图片工具类

ImageUtils

package per.qiao.utils.hutool.poi;

import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlToken;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline; /**
* Create by IntelliJ Idea 2018.2
*
* @author: qyp
* Date: 2019-10-26 21:55
*/
public class ImageUtils { /**
* 图片类型枚举
*/
enum ImageType { /**
* 支持四种类型 JPG/JPEG, GIT, BMP, PNG
*/
JPG("JPG", XWPFDocument.PICTURE_TYPE_JPEG),
JPEG("JPEG", XWPFDocument.PICTURE_TYPE_JPEG),
GIF("GIF", XWPFDocument.PICTURE_TYPE_GIF),
BMP("BMP", XWPFDocument.PICTURE_TYPE_GIF),
PNG("PNG", XWPFDocument.PICTURE_TYPE_PNG)
;
private String name;
private Integer typeId;
ImageType(String name, Integer type) {
this.name = name;
this.typeId = type;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Integer getTypeId() {
return typeId;
} public void setTypeId(Integer typeId) {
this.typeId = typeId;
}
} public static void createPicture(XWPFRun run, String blipId, int id, int width, int height) {
final int EMU = 9525;
width *= EMU;
height *= EMU;
CTInline inline = run.getCTR().addNewDrawing().addNewInline(); String picXml = "" +
"<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=\"" + id + "\" name=\"Generated\"/>" +
" <pic:cNvPicPr/>" +
" </pic:nvPicPr>" +
" <pic:blipFill>" +
" <a:blip r:embed=\"" + blipId + "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"/>" +
" <a:stretch>" +
" <a:fillRect/>" +
" </a:stretch>" +
" </pic:blipFill>" +
" <pic:spPr>" +
" <a:xfrm>" +
" <a:off x=\"0\" y=\"0\"/>" +
" <a:ext cx=\"" + width + "\" cy=\"" + height + "\"/>" +
" </a:xfrm>" +
" <a:prstGeom prst=\"rect\">" +
" <a:avLst/>" +
" </a:prstGeom>" +
" </pic:spPr>" +
" </pic:pic>" +
" </a:graphicData>" +
"</a:graphic>"; XmlToken xmlToken = null;
try {
xmlToken = XmlToken.Factory.parse(picXml);
} catch(XmlException xe) {
xe.printStackTrace();
}
inline.set(xmlToken); inline.setDistT(0);
inline.setDistB(0);
inline.setDistL(0);
inline.setDistR(0); CTPositiveSize2D extent = inline.addNewExtent();
extent.setCx(width);
extent.setCy(height); CTNonVisualDrawingProps docPr = inline.addNewDocPr();
docPr.setId(id);
docPr.setName("Picture " + id);
docPr.setDescr("Generated");
} }

测试类

package com.dist.web.util.poiword;

import java.util.*;

/**
* Create by IntelliJ Idea 2018.2
*
* @author: qyp
* Date: 2019-10-26 17:34
*/
public class DynWordUtilsTest { /**
* 说明 普通占位符位${field}格式
* 表格中的占位符为${tbAddRow:tb1} tb1为唯一标识符
* @param args
* @throws Exception
*/
public static void main(String[] args) { // 模板全的路径
String templatePaht = "E:\\Java4IDEA\\comm_test\\commutil\\src\\main\\resources\\wordtemplate\\审查报告模板1023体检表.docx";
// 输出位置
String outPath = "e:\\22.docx"; Map<String, Object> paramMap = new HashMap<>(16);
// 普通的占位符示例 参数数据结构 {str,str}
paramMap.put("title", "德玛西亚");
paramMap.put("startYear", "2010");
paramMap.put("endYear", "2020");
paramMap.put("currentYear", "2019");
paramMap.put("currentMonth", "10");
paramMap.put("currentDate", "26");
paramMap.put("name", "黑色玫瑰"); // 段落中的动态段示例 [str], 支持动态行中添加图片
List<Object> list1 = new ArrayList<>(Arrays.asList("2、list1_11111", "3、list1_2222", "${image:image0}"));
ImageEntity imgEntity = new ImageEntity();
imgEntity.setHeight(200);
imgEntity.setWidth(300);
imgEntity.setUrl("E:\\Java4IDEA\\comm_test\\commutil\\src\\main\\resources\\wordtemplate\\image1.jpg");
imgEntity.setTypeId(ImageUtils.ImageType.JPG); paramMap.put("image:image0", imgEntity);
paramMap.put("list1", list1); List<String> list2 = new ArrayList<>(Arrays.asList("2、list2_11111", "3、list2_2222"));
paramMap.put("list2", list2); // 表格中的参数示例 参数数据结构 [[str]]
List<List<String>> tbRow1 = new ArrayList<>();
List<String> tbRow1_row1 = new ArrayList<>(Arrays.asList("1、模块一", "分类1"));
List<String> tbRow1_row2 = new ArrayList<>(Arrays.asList("2、模块二", "分类2"));
tbRow1.add(tbRow1_row1);
tbRow1.add(tbRow1_row2);
paramMap.put(PoiWordUtils.addRowText + "tb1", tbRow1); List<List<String>> tbRow2 = new ArrayList<>();
List<String> tbRow2_row1 = new ArrayList<>(Arrays.asList("指标c", "指标c的意见"));
List<String> tbRow2_row2 = new ArrayList<>(Arrays.asList("指标d", "指标d的意见"));
tbRow2.add(tbRow2_row1);
tbRow2.add(tbRow2_row2);
paramMap.put(PoiWordUtils.addRowText + "tb2", tbRow2); List<List<String>> tbRow3 = new ArrayList<>();
List<String> tbRow3_row1 = new ArrayList<>(Arrays.asList("3", "耕地估值"));
List<String> tbRow3_row2 = new ArrayList<>(Arrays.asList("4", "耕地归属", "平方公里"));
tbRow3.add(tbRow3_row1);
tbRow3.add(tbRow3_row2);
paramMap.put(PoiWordUtils.addRowText + "tb3", tbRow3); // 支持在表格中动态添加图片
List<List<String>> tbRow4 = new ArrayList<>();
List<String> tbRow4_row1 = new ArrayList<>(Arrays.asList("03", "旅游用地", "18.8m2"));
List<String> tbRow4_row2 = new ArrayList<>(Arrays.asList("04", "建筑用地"));
List<String> tbRow4_row3 = new ArrayList<>(Arrays.asList("04", "${image:image3}"));
tbRow4.add(tbRow4_row3);
tbRow4.add(tbRow4_row1);
tbRow4.add(tbRow4_row2); // 支持在表格中添加重复模板的行
List<List<String>> tbRow5 = new ArrayList<>();
List<String> tbRow5_row1 = new ArrayList<>(Arrays.asList("欢乐喜剧人"));
List<String> tbRow5_row2 = new ArrayList<>(Arrays.asList("常远", "艾伦"));
List<String> tbRow5_row3 = new ArrayList<>(Arrays.asList("${tbAddRow:tb5}", "")); List<List<String>> tb5List = new ArrayList<>();
List<String> tb5Row1 = new ArrayList<>(Arrays.asList("岳云鹏", "孙悦"));
List<String> tb5Row2 = new ArrayList<>(Arrays.asList("小沈阳", "宋小宝"));
List<String> tb5Row3 = new ArrayList<>(Arrays.asList("张云雷", "严鹤翔"));
tb5List.add(tb5Row1);
tb5List.add(tb5Row2);
tb5List.add(tb5Row3);
paramMap.put("tbAddRow:tb5", tb5List); List<String> tbRow5_row4 = new ArrayList<>(Arrays.asList("诺克萨斯"));
List<String> tbRow5_row5 = new ArrayList<>(Arrays.asList("德莱文", "诺手"));
List<String> tbRow5_row6 = new ArrayList<>(Arrays.asList("男枪", "卡特琳娜")); tbRow5.add(tbRow5_row1);
tbRow5.add(tbRow5_row2);
tbRow5.add(tbRow5_row3);
tbRow5.add(tbRow5_row4);
tbRow5.add(tbRow5_row5);
tbRow5.add(tbRow5_row6);
paramMap.put("tbAddRowRepeat:tb5:0,2,0,1", tbRow5); ImageEntity imgEntity3 = new ImageEntity();
imgEntity3.setHeight(100);
imgEntity3.setWidth(100);
imgEntity3.setUrl("E:\\Java4IDEA\\comm_test\\commutil\\src\\main\\resources\\wordtemplate\\image1.jpg");
imgEntity3.setTypeId(ImageUtils.ImageType.JPG); paramMap.put(PoiWordUtils.addRowText + "tb4", tbRow4);
paramMap.put("image:image3", imgEntity3); // 图片占位符示例 ${image:imageid} 比如 ${image:image1}, ImageEntity中的值就为image:image1
// 段落中的图片
ImageEntity imgEntity1 = new ImageEntity();
imgEntity1.setHeight(500);
imgEntity1.setWidth(400);
imgEntity1.setUrl("E:\\Java4IDEA\\comm_test\\commutil\\src\\main\\resources\\wordtemplate\\image1.jpg");
imgEntity1.setTypeId(ImageUtils.ImageType.JPG);
paramMap.put("image:image1", imgEntity1); // 表格中的图片
ImageEntity imgEntity2 = new ImageEntity();
imgEntity2.setHeight(200);
imgEntity2.setWidth(100);
imgEntity2.setUrl("E:\\Java4IDEA\\comm_test\\commutil\\src\\main\\resources\\wordtemplate\\image1.jpg");
imgEntity2.setTypeId(ImageUtils.ImageType.JPG);
paramMap.put("image:image2", imgEntity2); DynWordUtils.process(paramMap, templatePaht, outPath);
} }

模板图

效果图

部分参考了easypoi源码

代码以及模板下载

java使用poi操作word, 支持动态的行(一个占位符插入多条)和表格中动态行, 支持图片的更多相关文章

  1. Java利用poi生成word(包含插入图片,动态表格,行合并)

    转(小改): Java利用poi生成word(包含插入图片,动态表格,行合并) 2018年12月20日 09:06:51 wjw_11093010 阅读数:70 Java利用poi生成word(包含插 ...

  2. Java操作word文档使用JACOB和POI操作word,Excel,PPT需要的jar包

    可参考文档: http://wibiline.iteye.com/blog/1725492 下载jar包 http://download.csdn.net/download/javashixiaofe ...

  3. 使用java Apache poi 根据word模板生成word报表

    项目开发过程中,客户提出一堆导出报表的需求,需要导出word格式,页眉还需要加上客户公司的logo,试了几种方案,最后选择了用 Apache poi 加上自定义标签的方式实现. 目前功能还比较简单,一 ...

  4. java使用POI操作XWPFDocument中的XWPFRun(文本)对象的属性详解

    java使用POI操作XWPFDocument中的XWPFRun(文本)对象的属性详解 我用的是office word 2016版 XWPFRun是XWPFDocument中的一段文本对象(就是一段文 ...

  5. java使用poi读取word(简单,简约,直观)

    java使用poi读取word(简单,简约,直观) 说明 其实poi的官网上面都是有接口和样例的,只是都是英文 例如网址:http://poi.apache.org/spreadsheet/quick ...

  6. java里poi操作excel的工具类(兼容各版本)

    转: java里poi操作excel的工具类(兼容各版本) 下面是文件内具体内容,文件下载: import java.io.FileNotFoundException; import java.io. ...

  7. Java 读取Word表格中的文本和图片

    本文通过Java程序来展示如何读取Word表格,包括读取表格中的文本和图片.下面是具体实现的步骤和方法. 1. 程序环境准备 代码编译工具:IntelliJ IDEA Jdk版本:1.8.0 测试文档 ...

  8. 在页面中添加两个 <select> 标签,用来显示年份和月份;同时添加两个 <ul> 标签,一个用来显示星期,另一个用来显示日期 在 JavaScript 脚本中动态添加年份和月份,获取当前日期的年份

    查看本章节 查看作业目录 需求说明: 使用 JavaScript 中的 Date 对象,在页面上显示一个万年历.选择不同的年份和月份,在页面中显示当前月的日历 实现思路: 在页面中添加两个 <s ...

  9. java 使用 POI 操作 XWPFDocumen 创建和读取 Office Word 文档基础篇

    注:有不正确的地方还望大神能够指出,抱拳了 老铁! 参考 API:http://poi.apache.org/apidocs/org/apache/poi/xwpf/usermodel/XWPFDoc ...

随机推荐

  1. 启动项目报错:Unsupported major.minor version 52.0

    解决方案: 确保Build Path或者电脑配置的环境变量版本号,和pom中的一致 Exception in thread "main" java.lang.Unsupported ...

  2. Learning to rank基本算法

    搜索排序相关的方法,包括 Learning to rank 基本方法 Learning to rank 指标介绍 LambdaMART 模型原理 FTRL 模型原理 Learning to rank ...

  3. 对pdf中的图片进行自动识别

    对pdf中的图片进行自动识别 商务合作,科技咨询,版权转让:向日葵,135—4855__4328,xiexiaokui#qq.com 原理:增强扫描 效果:自动识别所有图片中的文字,可以选择.复制,进 ...

  4. eclipse自定义代码块折叠

    1.下载插件 com.cb.eclipse.folding_1.0.6.jar 下载地址:http://files.cnblogs.com/haiq/代码折叠插件_com.cb.eclipse.fol ...

  5. Vuex 的使用 State Mutation Getter Action

    import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex); /*1.state在vuex中用于存储数据*/ var state={ cou ...

  6. evo:评测slam参数设置

    zn@buaa:~$ evo_ape euroc m1/state_groundtruth_estimate0/data.csv pl-svo-master2/trajout.txt -angle_d ...

  7. Qt编写气体安全管理系统24-地图管理

    一.前言 地图管理的主要功能是将系统中的地图文件做添加和删除,支持常见的jpg.png.bmp等格式图片,图片分辨率建议小于1080P,最好是和目标客户端电脑分辨率一致,这样在拉伸缩放的时候会比较清晰 ...

  8. Python初级 3 基本数学运算

    一. 四大基本运算操作符 1 加+ print(3 + 2) 2 减- print(3 - 2) 3 乘:* print(3 * 2) 4 除/, // print(3 / 2) print(3 // ...

  9. 一种计算hash的思路

    /*** * 转换请求hash,根据转换模式计算hash,防止重复发送请求,浪费服务器资源(内存.cpu.文件系统等) * @param mode * @param data * @return */ ...

  10. Dockerfile构建nginx镜像

    Dockerfile构建nginx镜像 [root@bogon ~]# mkdir /opt/docker-file [root@bogon ~]# cd /opt/docker-file/ [roo ...