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.pswp.cn/web/62473.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/62473.shtml
英文地址,請注明出處:http://en.pswp.cn/web/62473.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

OpenCV相機標定與3D重建(11)機器人世界手眼標定函數calibrateRobotWorldHandEye()的使用

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 計算機器人世界/手眼標定&#xff1a; w T b _{}^{w}\textrm{T}_b w?Tb? 和 c T g _{}^{c}\textrm{T}_g c?Tg?。 cv::calibrateRobotWorldHa…

GPT系列模型簡要概述

GPT-1&#xff1a;&#xff08;0.117B參數量&#xff0c;0.8B words預訓練數據) 動機&#xff1a; 在RNN和Transformer之間&#xff0c;選擇了后者。 和《All your need is Attention》翻譯模型的Encoder-Decoder架構相比&#xff0c;只保留Decoder&#xff0c;因此去掉了Cross…

汽車升級到底應不應該設置“可取消“功能

最近&#xff0c;汽車OTA&#xff08;Over-the-Air&#xff09;升級頻頻成為車主討論的熱點。有些車主反映&#xff0c;一些升級增加了實用功能&#xff0c;而另一些卻讓體驗變得復雜甚至帶來不便。于是&#xff0c;大家不禁發問&#xff1a;汽車升級功能究竟應不應該允許“可取…

單片機 PCB 設計要點

一、引言 單片機作為現代科技的重要組成部分&#xff0c;其 PCB 設計至關重要。本文將詳細介紹單片機 PCB 設計的要點和流程&#xff0c;幫助讀者更好地掌握這一關鍵技術。 在電子世界的浩瀚星海中&#xff0c;單片機無疑是現代科技中一顆閃爍的明珠。作為掌握嵌入式系統的基…

Django+Apscheduler 開發定時任務模塊【六】

目錄 回顧 前五個文章講述了django-autojob的部分代碼和執行邏輯 【DjangoApscheduler 開發定時任務模塊】【一】 【DjangoApscheduler 開發定時任務模塊】【二】 【DjangoApscheduler 開發定時任務模塊】【三】 【DjangoApscheduler 開發定時任務模塊】【四】 【DjangoApsch…

Ubuntu中配置交叉編譯工具的三條命令的詳細研究

關于該把下面的三條交叉編譯配置語句加到哪里&#xff0c;詳情見 https://blog.csdn.net/wenhao_ir/article/details/144326545 的第2點。 現在試解釋下面三條交叉編譯配置語句&#xff1a; export ARCHarm export CROSS_COMPILEarm-buildroot-linux-gnueabihf- export PATH$…

wlanapi.dll丟失怎么辦?有沒有什么靠譜的修復wlanapi.dll方法

在遇到各種系統文件錯誤當中&#xff0c;其中之一就是“wlanapi.dll文件丟失”的問題。這種問題通常發生在Windows操作系統上&#xff0c;特別是當系統試圖執行與無線網絡相關的任務時。wlanapi.dll是一個重要的系統文件&#xff0c;它負責處理Windows無線網絡服務的許多功能。…

利用ipmi工具設置ip、用戶等設置

#打開交互模式 ipmitool -I open shell #切換管理端口為lom1&#xff0c;即共享em1/eth0 delloem lan set shared with lom1 #設置ip、mask、gateway lan set 1 ipaddr 10.0.0.250 lan set 1 netmask 10.0.0.250 lan set 1 defgw ipaddr 10.0.0.250 #查看用戶名 user list 1 …

Python之因子分析詳細步驟

1.數學原理 1.1數學模型 1.2正交因子模型假設 注意&#xff1a;下面的推導都是基于這一假設。因此&#xff0c;這里的模型都是屬于正交因子模型。 1.3正交因子模型的協方差結構 1.4各類方差貢獻的介紹 在1.3正交因子模型的協方差結構中&#xff0c;我們介紹了“方差貢獻”&…

unity3d—demo(2d人物左右移動發射子彈)

目錄 人物代碼示例&#xff1a; 子彈代碼示例&#xff1a; 總結上面代碼&#xff1a; 注意點&#xff1a; 人物代碼示例&#xff1a; using System.Collections; using System.Collections.Generic; using UnityEngine;public class PlayerTiao : MonoBehaviour {public f…

linux之vim

一、模式轉換命令 vim主要有三種模式&#xff1a;命令模式&#xff08;Normal Mode&#xff09;、輸入模式&#xff08;Insert Mode&#xff09;和底線命令模式&#xff08;Command-Line Mode&#xff09;。 從命令模式切換到輸入模式&#xff1a;i&#xff1a;在當前光標所在…

顯存和GPU之間的通信;GPUDirect P2P,NVLink,NCCL;聚合通信和點對點通信

目錄 顯存和GPU之間的分配 顯存和GPU之間的通信 原語是什么,簡單舉例說明 GPUDirect P2P,NVLink,NCCL的全稱及解釋 聚合通信和點對點通信 聚合通信(Collective Communication) 點對點通信(Point-to-Point Communication) 為什么使用GPUDirect P2P,NVLink,NCCL…

Mysql 的 B+ 樹是否包含行數據?

在 MySQL 中&#xff0c;是否在 B樹 的葉子節點上存儲完整的行數據&#xff0c;取決于使用的 存儲引擎 和 索引類型&#xff1a; 聚簇索引 (Clustered Index) 葉子節點包含完整的行數據。 適用場景&#xff1a;MySQL InnoDB 存儲引擎的主鍵索引&#xff08;或聚簇索引&#xf…

【記錄】用JUnit 4的@Test注解時報錯java.lang.NullPointerException的原因與解決方法

項目場景&#xff1a; 在練習黑馬點評的邏輯過期解決緩存擊穿時&#xff0c;編寫了一個預熱緩存數據的單元測試 SpringBootTest public class HmDianPingApplicationTests {Resourceprivate ShopServiceImpl shopService;Testpublic void testSaveShop() throws InterruptedE…

echarts使用整理

4、條形分區統計 <div ref"chartsVal1" class"chartsline-div"></div> const chartsVal1 ref(null); const chartsVal1Title ref(運行時間統計);drewCharts2(chartsVal1, chartsVal1Title.value);function drewCharts2(id, title) {const m…

【八股】HTTP

瀏覽器輸入URL之后發生的過程 瀏覽器解析URL中的協議&#xff0c;主機&#xff0c;端口&#xff0c;路徑參數等DNS域名解析得到對應的IP地址通過IP和PORT對服務器發送TCP三次握手建立連接瀏覽器發送請求服務器接受請求&#xff0c;處理并響應瀏覽器得到HTTP響應&#xff0c;對…

torch.optim.lr_scheduler.ReduceLROnPlateau

torch.optim.lr_scheduler.ReduceLROnPlateau 是 PyTorch 中的一種學習率調度器&#xff0c;主要用于在模型訓練過程中根據某些指標&#xff08;如驗證損失&#xff09;動態調整學習率。它是一種基于性能指標動態調整學習率的策略&#xff0c;而不是預定義的固定時間調整。 主要…

ubuntu下的chattts 學習6:音色固定的學習

魔搭社區 該區提供了隨機種子級音樂的試聽與下載。 spk torch.load(<PT-FILE-PATH>) params_infer_code {spk_emb: spk, } 略 測試過程&#xff1a; 1.先建一個文件夾&#xff1a;然后從上面的網站上下載了兩個。放在里面測試 2 2.測試代碼 import ChatTTS impo…

數據集的重要性:如何構建AIGC訓練集

文章目錄 一、為什么數據集對AIGC如此重要&#xff1f;1. 數據決定模型的知識邊界2. 數據質量直接影響生成效果3. 數據集多樣性提升模型魯棒性 二、構建AIGC訓練集的關鍵步驟1. 明確目標任務和生成需求2. 數據源的選擇3. 數據清洗與預處理4. 數據標注5. 數據增強 三、針對不同類…

47 基于單片機的書庫環境監測

目錄 一、主要功能 二、硬件資源 三、程序編程 四、實現現象 一、主要功能 基于51單片機&#xff0c;采用DHT11濕度傳感器檢測濕度&#xff0c;DS18B20溫度傳感器檢測溫度&#xff0c; 采用滑動變阻器連接數模轉換器模擬二氧化碳和氧氣濃度檢測&#xff0c;各項數值通過lc…