java使用poi操作word, 支持动态的行(一个占位符插入多条)和表格中动态行, 支持图片
依赖
<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, 支持动态的行(一个占位符插入多条)和表格中动态行, 支持图片的更多相关文章
- Java利用poi生成word(包含插入图片,动态表格,行合并)
转(小改): Java利用poi生成word(包含插入图片,动态表格,行合并) 2018年12月20日 09:06:51 wjw_11093010 阅读数:70 Java利用poi生成word(包含插 ...
- Java操作word文档使用JACOB和POI操作word,Excel,PPT需要的jar包
可参考文档: http://wibiline.iteye.com/blog/1725492 下载jar包 http://download.csdn.net/download/javashixiaofe ...
- 使用java Apache poi 根据word模板生成word报表
项目开发过程中,客户提出一堆导出报表的需求,需要导出word格式,页眉还需要加上客户公司的logo,试了几种方案,最后选择了用 Apache poi 加上自定义标签的方式实现. 目前功能还比较简单,一 ...
- java使用POI操作XWPFDocument中的XWPFRun(文本)对象的属性详解
java使用POI操作XWPFDocument中的XWPFRun(文本)对象的属性详解 我用的是office word 2016版 XWPFRun是XWPFDocument中的一段文本对象(就是一段文 ...
- java使用poi读取word(简单,简约,直观)
java使用poi读取word(简单,简约,直观) 说明 其实poi的官网上面都是有接口和样例的,只是都是英文 例如网址:http://poi.apache.org/spreadsheet/quick ...
- java里poi操作excel的工具类(兼容各版本)
转: java里poi操作excel的工具类(兼容各版本) 下面是文件内具体内容,文件下载: import java.io.FileNotFoundException; import java.io. ...
- Java 读取Word表格中的文本和图片
本文通过Java程序来展示如何读取Word表格,包括读取表格中的文本和图片.下面是具体实现的步骤和方法. 1. 程序环境准备 代码编译工具:IntelliJ IDEA Jdk版本:1.8.0 测试文档 ...
- 在页面中添加两个 <select> 标签,用来显示年份和月份;同时添加两个 <ul> 标签,一个用来显示星期,另一个用来显示日期 在 JavaScript 脚本中动态添加年份和月份,获取当前日期的年份
查看本章节 查看作业目录 需求说明: 使用 JavaScript 中的 Date 对象,在页面上显示一个万年历.选择不同的年份和月份,在页面中显示当前月的日历 实现思路: 在页面中添加两个 <s ...
- java 使用 POI 操作 XWPFDocumen 创建和读取 Office Word 文档基础篇
注:有不正确的地方还望大神能够指出,抱拳了 老铁! 参考 API:http://poi.apache.org/apidocs/org/apache/poi/xwpf/usermodel/XWPFDoc ...
随机推荐
- argmin ,argmax函数
在数学中,ARG MAX(或ARGMAX)代表最大值,即给定参数的点集,给定表达式的值达到其最大值: 换一种说法, 是f(x)具有最大值M的x的值的集合.例如,如果f(x)是1- | x |,那么它在 ...
- MySql数据库转设计文档(mysql-font工具和sql语句导出)
一.工具导出 1.使用的是MySQL-Front工具,这个工具使用非常方便,尤其是导出数据的时候,几百万的数据一两分钟就导完了,推荐使用. MySQL-Front下载(只有3.93M):http:// ...
- GPS 经纬度
经纬度地图: http://www.gpsspg.com/maps.htm http://www.gzhatu.com/dingwei.html 经纬度格式转化 http://www.gzhatu ...
- Cannot find module 'laravel-elixir'问题解决方法
在用gulp 安装elixir的时候报了这样的错误: Laravel elixir npm error Cannot find module 'laravel-elixir/ingredients/c ...
- npm配置淘宝镜像
npm直接安装包太慢,采用淘宝npm镜像安装 在linux和Mac上可以添加环境变量的形式修改bashrc文件,但是在windows上可以直接采取如下方式,以绝后患. 永久采用 npm config ...
- spring boot集成Websocket
websocket实现后台像前端主动推送消息的模式,可以减去前端的请求获取数据的模式.而后台主动推送消息一般都是要求消息回馈比较及时,同时减少前端ajax轮询请求,减少资源开销. spring boo ...
- Linux记录-shell自动化批量部署sql脚本并记录日志信息(转载)
#!/bin/bash #script_version=v110 db_host=127.0.0.1 db_port=3306 db_username=db_test_inst db_passwd=` ...
- mysql quick query row count using sql
1. command show table status like '{table-name}'; 2. sample mysql> use inventory; Database change ...
- SAS如何看待大数据
SAS如何看待大数据 "大数据"现在是一个炙手可热的词语,数据分析师这个词虽然比较新,但收集与存储大量信息的历史却不短了. 早在本世纪初,行业分析师Doug Laney就提出了&q ...
- 改进初学者的PID-微分冲击
最近看到了Brett Beauregard发表的有关PID的系列文章,感觉对于理解PID算法很有帮助,于是将系列文章翻译过来!在自我提高的过程中,也希望对同道中人有所帮助.作者Brett Beaure ...