前提了解poi-tl
链接: springboot整合poi-tl
创建word模板
实现效果
代码实现
ServerTableData
import com.deepoove.poi.data.RowRenderData;
import java.util.List;
public class ServerTableData {
/**
* 携带表格中真实数据
*/
private List<RowRenderData> serverDataList;
public List<RowRenderData> getServerDataList() {
return serverDataList;
}
public void setServerDataList(List<RowRenderData> serverDataList) {
this.serverDataList = serverDataList;
}
}
MyPoiUtil
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBorder;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblBorders;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STBorder;
public class MyPoiUtil {
/**
* 设置表格边框样式为黑色实线
*/
public static void setTableBorder(XWPFTable table) {
CTTblPr tblPr = table.getCTTbl().getTblPr();
CTTblBorders borders = tblPr.isSetTblBorders() ? tblPr.getTblBorders() : tblPr.addNewTblBorders();
CTBorder hBorder = borders.addNewInsideH();
hBorder.setVal(STBorder.SINGLE); // 线条类型
hBorder.setColor("000000"); // 设置颜色
CTBorder vBorder = borders.addNewInsideV();
vBorder.setVal(STBorder.SINGLE);
vBorder.setColor("000000");
CTBorder lBorder = borders.addNewLeft();
lBorder.setVal(STBorder.SINGLE);
lBorder.setColor("000000");
CTBorder rBorder = borders.addNewRight();
rBorder.setVal(STBorder.SINGLE);
rBorder.setColor("000000");
CTBorder tBorder = borders.addNewTop();
tBorder.setVal(STBorder.SINGLE);
tBorder.setColor("000000");
CTBorder bBorder = borders.addNewBottom();
bBorder.setVal(STBorder.SINGLE);
bBorder.setColor("000000");
}
}
ConsecutiveDoublesUtil
import java.util.*;
/**
* 重复且连续的角标集合
*
* @author zhou
*/
public class ConsecutiveDoublesUtil {
public static Map<String, List<List<Integer>>> getConsecutiveDoubles(List<String> strings) {
Map<String, List<List<Integer>>> consecutiveDoublesMap = new HashMap<>();
// Start index of a potential consecutive sequence
int startIndex = -1;
// Last seen string
String lastStr = null;
for (int i = 0; i < strings.size(); i++) {
String str = strings.get(i);
if (str.equals(lastStr)) {
// If the current string is the same as the last seen one and we're in a sequence,
// continue the sequence.
if (startIndex >= 0) {
// Ensure we have a start index for the sequence.
consecutiveDoublesMap.computeIfAbsent(lastStr, k -> new ArrayList<>()).get(consecutiveDoublesMap.get(lastStr).size() - 1).add(i);
} else {
// Start a new sequence.
startIndex = i - 1;
consecutiveDoublesMap.computeIfAbsent(lastStr, k -> new ArrayList<>()).add(new ArrayList<>(Arrays.asList(startIndex, i)));
}
} else {
// If we reach the end of a sequence or a new string is found, check if we had a valid sequence.
if (startIndex >= 0 && i - startIndex > 1) {
// We had a sequence of length at least 2, so it's valid.
} else if (startIndex >= 0) {
// Sequence was too short, remove it.
consecutiveDoublesMap.get(lastStr).remove(consecutiveDoublesMap.get(lastStr).size() - 1);
}
// Reset for the next potential sequence.
startIndex = -1;
lastStr = str;
}
}
// Remove entries with no valid sequences
consecutiveDoublesMap.values().removeIf(List::isEmpty);
return consecutiveDoublesMap;
}
}
ServerTablePolicy
import com.chinadci.framework.bean.ServerTableData;
import com.chinadci.fzjc.utils.ConsecutiveDoublesUtil;
import com.deepoove.poi.data.RowRenderData;
import com.deepoove.poi.policy.DynamicTableRenderPolicy;
import com.deepoove.poi.policy.TableRenderPolicy;
import com.deepoove.poi.util.TableTools;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import java.util.*;
/**
* 动态合并单元格
*/
public class ServerTablePolicy extends DynamicTableRenderPolicy {
/**
* 填充数据所在行数
*/
private final int laborsStartRow;
/**
* 表格的列数
*/
private final int headerColumnIndex;
/**
* 合并的列
*/
private List<Integer> colArr;
/**
* 表格行高
*/
private int height = 250;
public ServerTablePolicy(int laborsStartRow, int headerColumnIndex) {
this.laborsStartRow = laborsStartRow;
this.headerColumnIndex = headerColumnIndex;
}
public void setColArr(List<Integer> colArr) {
this.colArr = colArr;
}
public void setHeight(int height) {
this.height = height;
}
@Override
public void render(XWPFTable xwpfTable, Object tableData) throws Exception {
if (null == tableData) {
return;
}
ServerTableData serverTableData = (ServerTableData) tableData;
List<RowRenderData> serverDataList = serverTableData.getServerDataList();
if (CollectionUtils.isNotEmpty(serverDataList)) {
// 先删除一行, demo中第一行是为了调整 三线表 样式
xwpfTable.removeRow(laborsStartRow);
// 行从中间插入, 因此采用倒序渲染数据
for (int i = serverDataList.size() - 1; i >= 0; i--) {
// 从表单的哪行开始插入数据,一般表单有一个标题 循环新增行 这样就会把数据往下层压
XWPFTableRow newRow = xwpfTable.insertNewTableRow(laborsStartRow);
newRow.setHeight(height);
for (int j = 0; j < headerColumnIndex; j++) {
newRow.createCell();
}
int fromCol = 0;
int toCol = 1;
String typeNameData = serverDataList.get(i).getCells().get(fromCol).getParagraphs().get(0).getContents().get(0).toString();
String typeNameData1 = serverDataList.get(i).getCells().get(toCol).getParagraphs().get(0).getContents().get(0).toString();
// 渲染一行数据
TableRenderPolicy.Helper.renderRow(newRow, serverDataList.get(i));
boolean equals = typeNameData.equals(typeNameData1);
// 合并水平单元格
if (equals) {
// TableTools.mergeCellsHorizonal(table, 0, 0, 3) 合并第0行的第0列到第3列单元格
TableTools.mergeCellsHorizonal(xwpfTable, laborsStartRow, fromCol, toCol);
// 加粗
// 遍历行中的所有单元格
for (int k = 0; k < newRow.getTableCells().size(); k++) {
XWPFTableCell tableCell = newRow.getTableCells().get(k);
List<XWPFRun> runs = tableCell.getParagraphArray(0).getRuns();
for (XWPFRun run1 : runs) {
run1.setBold(true);
}
}
}
}
for (Integer integer : colArr) {
List<String> strings = new ArrayList<>();
// 处理行合并
for (int i = 0; i < serverDataList.size(); i++) {
String typeNameData = serverDataList.get(i).getCells().get(integer).getParagraphs().get(0).getContents().get(0).toString();
strings.add(typeNameData);
}
Map<String, List<List<Integer>>> result = ConsecutiveDoublesUtil.getConsecutiveDoubles(strings);
for (Map.Entry<String, List<List<Integer>>> entry : result.entrySet()) {
List<List<Integer>> value = entry.getValue();
for (List<Integer> integers : value) {
// TableTools.mergeCellsVertically(table, 0, 0, 3) 合并第0列的第0行到第3行的单元格
TableTools.mergeCellsVertically(xwpfTable, integer, integers.get(0) + laborsStartRow, integers.get(integers.size() - 1) + laborsStartRow);
}
}
}
}
}
}
main
public class Test{
private static ServerTableData getServerTableData() {
ServerTableData serverTableData = new ServerTableData();
List<RowRenderData> serverDataList = Arrays.asList(
Rows.of("天河", "天河", "0.3").textFontSize(14).center().create(),
Rows.of("广州", "白云", "0.2").textFontSize(14).center().create(),
Rows.of("天河", "天河", "0.3").textFontSize(14).center().create(),
Rows.of("广州", "东山1", "0.1").textFontSize(14).center().create(),
Rows.of("广州", "东山2", "0.2").textFontSize(14).center().create(),
Rows.of("广州", "东山2", "0.2").textFontSize(14).center().create(),
Rows.of("佛山", "禅城", "0.3").textFontSize(14).center().create(),
Rows.of("深圳", "南山", "0.3").textFontSize(14).center().create(),
Rows.of("深圳", "福田", "0.3").textFontSize(14).center().create()
);
serverTableData.setServerDataList(serverDataList);
return serverTableData;
}
public static void main(String[] args) {
Map<String,Object> map = new HashMap<>();
// 伪造一个表格数据
ServerTableData oneTable = getServerTableData();
map.put("oneTable", oneTable);
map.put("title", "2023");
ServerTablePolicy serverTablePolicy = new ServerTablePolicy(2, 3);
// 需要合并的列
serverTablePolicy.setColArr(Arrays.asList(0,1));
serverTablePolicy.setHeight(230);
// 配置策略
ConfigureBuilder builder = Configure.builder()
.useSpringEL(false)
.bind("oneTable", serverTablePolicy);
// 获取模板文件流
String inputFilePath = "C:/Users/zhou/Desktop/1.docx";
String outFilePath = "C:/Users/zhou/Desktop/2.docx";
try(InputStream resourceAsStream = new FileInputStream(new File(inputFilePath));
// HttpServletResponse response
OutputStream out = new FileOutputStream(new File(outFilePath));
BufferedOutputStream bos = new BufferedOutputStream(out);
XWPFTemplate template = XWPFTemplate.compile(Objects.requireNonNull(resourceAsStream), builder.build()).render(map);) {
// 获取第一个表格
XWPFTable table = template.getXWPFDocument().getTableArray(0);
// 设置表格边框样式为黑色实线
MyPoiUtil.setTableBorder(table);
// 这里删除的是原先空白的第一行
table.removeRow(1);
template.write(bos);
bos.flush();
out.flush();
PoitlIOUtils.closeQuietlyMulti(template, bos, out);
} catch (IOException e) {
e.printStackTrace();
}
}
}