模板图
希望可以帮到大家,希望给个start
项目git源码地址
效果图
1,引入maven依赖
org.apache.poi
poi
3.17
org.apache.poi
poi-ooxml
3.17
org.apache.poi
poi-ooxml-schemas
3.17
2,核心工具类
package per.qiao.utils.hutool.poi;
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.junit.Assert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
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 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 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) {
File inFile = new File(templatePath);
try (FileOutputStream outStream = new FileOutputStream(outPath)) {
templateDoc = new XWPFDocument(OPCPackage.open(inFile));
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 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 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 paramMap) throws Exception {
String placeholderValue = placeholder;
if (paramMap == null || paramMap.isEmpty()) {
return;
}
Set> textSets = paramMap.entrySet();
for (Map.Entry 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;
}
/**
* 创建段落
*
* @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 paramMap) throws Exception