当前位置: 首页 > news >正文

word poi-tl 表格功能增强,实现表格功能垂直合并

目录

    • 问题
    • 解决问题
      • poi-tl介绍
    • 功能实现
      • 引入依赖
      • 模版
      • 代码
      • 效果图
    • 附加(插件实现)
      • MergeColumnData 对象
      • MergeGroupData 类
      • ServerMergeTableData 数据信息
      • ServerMergeTablePolicy 合并插件

问题

由于在开发功能需求中,word文档需要垂直合并表格,而word模版引擎原有的插件功能只能做行循环,不满足需求;

解决问题

  • 目前选择的poi-tl的模版引擎,在原有的基础上新增自定义插件来实现功能

poi-tl介绍

poi-tl 是一个基于Apache POI的Word模板引擎,也是一个免费开源的Java类库,你可以非常方便的加入到你的项目中;

Word模板引擎功能描述
文本将标签渲染为文本
图片将标签渲染为图片
表格将标签渲染为表格
图表条形图(3D条形图)、柱形图(3D柱形图)、面积图(3D面积图)、折线图(3D折线图)、雷达图、饼图(3D饼图)、散点图等图表渲染
If Condition判断根据条件隐藏或者显示某些文档内容(包括文本、段落、图片、表格、列表、图表等)
Foreach Loop循环根据集合循环某些文档内容(包括文本、段落、图片、表格、列表、图表等)
Loop表格行循环复制渲染表格的某一行
Loop表格列循环复制渲染表格的某一列
Loop有序列表支持有序列表的循环,同时支持多级列表
Highlight代码高亮word中代码块高亮展示,支持26种语言和上百种着色样式
Markdown将Markdown渲染为word文档
Word批注完整的批注功能,创建批注、修改批注等
Word附件Word中插入附件
SDT内容控件内容控件内标签支持
Textbox文本框文本框内标签支持
图片替换将原有图片替换成另一张图片
书签、锚点、超链接支持设置书签,文档内锚点和超链接功能
Expression Language完全支持SpringEL表达式,可以扩展更多的表达式:OGNL, MVEL
样式支持有序列表的循环,同时支持多级列表
模板嵌套模板包含子模板,子模板再包含子模板
模板嵌套模板包含子模板,子模板再包含子模板
合并Word合并Merge,也可以在指定位置进行合并
用户自定义函数(插件)插件化设计,在文档任何位置执行函数

功能实现

引入依赖

		<dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.12.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-full</artifactId><version>5.2.5</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.5</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.5</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.25</version></dependency><!-- spring el表达式 --><dependency><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId><version>5.3.18</version></dependency>

模版

在这里插入图片描述

代码

@Testpublic void test() throws Exception {Configure config = Configure.builder().bind("precipitationInfoList", new ServerMergeTablePolicy()).useSpringEL(false).build();Map<String, Object> dataMap = new HashMap<String, Object>();dataMap.put("precipitationInfoList", getServerMergeTableData());ClassPathResource classPathResource = new ClassPathResource("static/word/template.docx");try (InputStream resourceInputStream = classPathResource.getInputStream();XWPFTemplate template = XWPFTemplate.compile(resourceInputStream, config);) {template.render(dataMap);template.writeAndClose(new FileOutputStream("output.docx"));} catch (Exception e) {e.printStackTrace();}}private ServerMergeTableData getServerMergeTableData() {List<Map<String,Object>> serverDataList = new ArrayList<>();Map<String,Object> serverData1 = new HashMap<>();serverData1.put("province","广东省");serverData1.put("city","深圳市");serverData1.put("precipitation","0.3");serverDataList.add(serverData1);Map<String,Object> serverData2 = new HashMap<>();serverData2.put("province","广东省");serverData2.put("city","广州市");serverData2.put("precipitation","5.1");serverDataList.add(serverData2);Map<String,Object> serverData3 = new HashMap<>();serverData3.put("province","广东省");serverData3.put("city","东莞市");serverData3.put("precipitation","10");serverDataList.add(serverData3);Map<String,Object> serverData4 = new HashMap<>();serverData4.put("province","湖南");serverData4.put("city","长沙");serverData4.put("precipitation","10");serverDataList.add(serverData4);Map<String,Object> serverData6 = new HashMap<>();serverData6.put("province","湖南");serverData6.put("city","湘潭");serverData6.put("precipitation","4.5");serverDataList.add(serverData6);List<MergeGroupData> groupDataList = new ArrayList<>();groupDataList.add(MergeGroupData.builder().indexList(Arrays.asList("0","1","2")).build());groupDataList.add(MergeGroupData.builder().indexList(Arrays.asList("3","4")).build());List<MergeColumnData> mergeColumns = new ArrayList<>();mergeColumns.add(MergeColumnData.builder().groupDataList(groupDataList).mergeColumn(0).build());ServerMergeTableData serverMergeTableData = new ServerMergeTableData();serverMergeTableData.setMergeColumns(mergeColumns);serverMergeTableData.setServerDataList(serverDataList);return serverMergeTableData;}

效果图

在这里插入图片描述

附加(插件实现)

MergeColumnData 对象


import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.List;@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class MergeColumnData {/*** 携带要分组的信息*/private List<MergeGroupData> groupDataList;/*** 需要合并的列,从0开始*/private Integer mergeColumn;
}

MergeGroupData 类


/*** 合并对象信息*/
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class MergeGroupData {/*** 名称*/private List<String> indexList;public Integer getListSize() {if (Objects.isNull(indexList)) {return 0;}return indexList.size();}
}

ServerMergeTableData 数据信息


import lombok.Data;import java.util.List;@Data
public class ServerMergeTableData {private List<?> serverDataList;/*** 列合并信息*/private List<MergeColumnData> mergeColumns;
}

ServerMergeTablePolicy 合并插件


import cn.hutool.core.collection.CollUtil;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.exception.RenderException;
import com.deepoove.poi.policy.AbstractRenderPolicy;
import com.deepoove.poi.render.RenderContext;
import com.deepoove.poi.render.compute.EnvModel;
import com.deepoove.poi.render.compute.RenderDataCompute;
import com.deepoove.poi.render.processor.DocumentProcessor;
import com.deepoove.poi.render.processor.EnvIterator;
import com.deepoove.poi.resolver.TemplateResolver;
import com.deepoove.poi.template.ElementTemplate;
import com.deepoove.poi.template.MetaTemplate;
import com.deepoove.poi.template.run.RunTemplate;
import com.deepoove.poi.util.ReflectionUtils;
import com.deepoove.poi.util.TableTools;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
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 org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;import java.util.Iterator;
import java.util.List;public class ServerMergeTablePolicy extends AbstractRenderPolicy<ServerMergeTableData> {private String prefix;private String suffix;private boolean onSameLine;public ServerMergeTablePolicy() {this(false);}public ServerMergeTablePolicy(boolean onSameLine) {this("[", "]", onSameLine);}public ServerMergeTablePolicy(String prefix, String suffix) {this(prefix, suffix, false);}public ServerMergeTablePolicy(String prefix, String suffix, boolean onSameLine) {this.prefix = prefix;this.suffix = suffix;this.onSameLine = onSameLine;}@Overridepublic void doRender(RenderContext<ServerMergeTableData> context) throws Exception {XWPFTemplate template = context.getTemplate();ServerMergeTableData mergeTableData = context.getData();ElementTemplate eleTemplate = context.getEleTemplate();this.renderTo(eleTemplate, mergeTableData, template);}public void renderTo(ElementTemplate eleTemplate, Object tableData, XWPFTemplate template) {RunTemplate runTemplate = (RunTemplate) eleTemplate;XWPFRun run = runTemplate.getRun();try {XWPFTableCell tagCell = (XWPFTableCell) ((XWPFParagraph) run.getParent()).getBody();XWPFTable table = tagCell.getTableRow().getTable();run.setText("", 0);if (null == tableData) {return;}ServerMergeTableData serverTableData = (ServerMergeTableData) tableData;List data = serverTableData.getServerDataList();if (!TableTools.isInsideTable(run)) {throw new IllegalStateException("The template tag " + runTemplate.getSource() + " must be inside a table");}int templateRowIndex = getTemplateRowIndex(tagCell);int tempStartRow = templateRowIndex;if (null != data && data instanceof Iterable) {Iterator<?> iterator = ((Iterable<?>) data).iterator();XWPFTableRow templateRow = table.getRow(templateRowIndex);int insertPosition = templateRowIndex;TemplateResolver resolver = new TemplateResolver(template.getConfig().copy(prefix, suffix));boolean firstFlag = true;int index = 0;boolean hasNext = iterator.hasNext();while (hasNext) {Object root = iterator.next();hasNext = iterator.hasNext();insertPosition = templateRowIndex++;XWPFTableRow nextRow = table.insertNewTableRow(insertPosition);setTableRow(table, templateRow, insertPosition);// double set rowXmlCursor newCursor = templateRow.getCtRow().newCursor();newCursor.toPrevSibling();XmlObject object = newCursor.getObject();nextRow = new XWPFTableRow((CTRow) object, table);if (!firstFlag) {// update VMerge cells for non-first rowList<XWPFTableCell> tableCells = nextRow.getTableCells();for (XWPFTableCell cell : tableCells) {CTTcPr tcPr = TableTools.getTcPr(cell);CTVMerge vMerge = tcPr.getVMerge();if (null == vMerge) continue;if (STMerge.RESTART == vMerge.getVal()) {vMerge.setVal(STMerge.CONTINUE);}}} else {firstFlag = false;}setTableRow(table, nextRow, insertPosition);RenderDataCompute dataCompute = template.getConfig().getRenderDataComputeFactory().newCompute(EnvModel.of(root, EnvIterator.makeEnv(index++, hasNext)));List<XWPFTableCell> cells = nextRow.getTableCells();cells.forEach(cell -> {List<MetaTemplate> templates = resolver.resolveBodyElements(cell.getBodyElements());new DocumentProcessor(template, resolver, dataCompute).process(templates);});}}table.removeRow(templateRowIndex);afterloop(table, data);//合并表格信息mergeTable(serverTableData, tempStartRow, table);} catch (Exception e) {throw new RenderException("HackLoopTable for " + eleTemplate + " error: " + e.getMessage(), e);}}private void mergeTable(ServerMergeTableData serverMergeTableData, int startRow, XWPFTable xwpfTable) {List serverDataList = serverMergeTableData.getServerDataList();List<MergeColumnData> mergeColumns = serverMergeTableData.getMergeColumns();if (CollUtil.isNotEmpty(mergeColumns)) {for (int i = 0; i < serverDataList.size(); i++) {for (MergeColumnData mergeColumnData : mergeColumns) {Integer mergeColumn = mergeColumnData.getMergeColumn();List<MergeGroupData> groupDataList = mergeColumnData.getGroupDataList();for (int j = 0; j < groupDataList.size(); j++) {MergeGroupData mergeGroupData = groupDataList.get(j);List<String> indexList = mergeGroupData.getIndexList();int listSize = mergeGroupData.getListSize();if (listSize == 1) {continue;}// 若匹配上 就直接合并if (indexList.contains(i + "")) {int col = i + startRow;int fromRow = i + (startRow - 1) + listSize;TableTools.mergeCellsVertically(xwpfTable, mergeColumn, col, fromRow);groupDataList.remove(j);break;}}}}}}private int getTemplateRowIndex(XWPFTableCell tagCell) {XWPFTableRow tagRow = tagCell.getTableRow();return onSameLine ? getRowIndex(tagRow) : (getRowIndex(tagRow) + 1);}protected void afterloop(XWPFTable table, Object data) {}@SuppressWarnings("unchecked")private void setTableRow(XWPFTable table, XWPFTableRow templateRow, int pos) {List<XWPFTableRow> rows = (List<XWPFTableRow>) ReflectionUtils.getValue("tableRows", table);rows.set(pos, templateRow);table.getCTTbl().setTrArray(pos, templateRow.getCtRow());}private int getRowIndex(XWPFTableRow row) {List<XWPFTableRow> rows = row.getTable().getRows();return rows.indexOf(row);}
}
http://www.lryc.cn/news/499695.html

相关文章:

  • LSTM-CNN-BP-RF-SVM五模型咖喱融合策略混合预测模型
  • 《鸿蒙开发-答案之书》 怎么设置Json字段的别名
  • ftp服务器搭建-安装、配置及验证
  • 鸿蒙应用获取wifi连接的ip地址(官方文档获取的格式转换成192.168.1.xxx格式)
  • c++数据结构算法复习基础--11--高级排序算法-快速排序-归并排序-堆排序
  • 人工智能学习路线详细规划
  • 深度学习之视觉处理
  • 遇到问题:hive中的数据库和sparksql 操作的数据库不是同一个。
  • Spring Boot与Spring Security集成:前后分离认证流程的优化实践
  • 设计模式——Chain(责任链)设计模式
  • HarmonyOS(63) ArkUI 自定义占位组件NodeContainer
  • Python深度强化学习对冲策略:衍生品投资组合套期保值Black-Scholes、Heston模型分析...
  • 【opencv入门教程】2. Point()类用法
  • 前端导出excel实战(xlsx库和exceljs库)
  • 【附源码】基于环信鸿蒙IM SDK实现一个聊天Demo
  • Python库常用函数-数据分析
  • 汽车EEA架构:架构的简介
  • 渗透测试--数据库攻击
  • 反向路径转发(RPF)
  • Python 正则表达式常用特殊字符及其含义
  • Uniapp Android SpringBoot3 对接支付宝支付(最新教程附源码)
  • SQL DML 语句
  • 饲料颗粒机全套设备有哪些机器组成
  • MySQL事务与锁
  • 汽车免拆案例 | 2007款宝马650i车发动机偶尔无法起动
  • PostgreSQL和Oracle的sql差异
  • SpringMvc完整知识点二(完结)
  • 29 - Java Serializable 序列化
  • 59 基于STM32的烟雾、红外、温湿度检测
  • 使用Excel 对S型曲线加减速算法进行仿真