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

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>

模版

在这里插入图片描述

代码

@Test
    public 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;
    }

    @Override
    public 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 row
                    XmlCursor newCursor = templateRow.getCtRow().newCursor();
                    newCursor.toPrevSibling();
                    XmlObject object = newCursor.getObject();
                    nextRow = new XWPFTableRow((CTRow) object, table);
                    if (!firstFlag) {
                        // update VMerge cells for non-first row
                        List<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.kler.cn/a/430030.html

相关文章:

  • WPF中组件之间传递参数的方法研究
  • 从SS到CSS:探索网页样式设计的奥秘
  • 掌握 Node EventEmitter:原理剖析、手写实现与项目代码深度讲解
  • 【Linux】模拟Shell命令行解释器
  • hive数据迁移
  • Photon最新版本PUN 2.29 PREE,在无网的局域网下,无法连接自己搭建的本地服务器
  • C# 关于加密技术以及应用(一)
  • 《Vue进阶教程》第一课:什么是组合式API
  • 深度学习常见激活函数介绍
  • ansible学习笔记之02command模块与shell模块
  • apache的BeanUtils的Converter被相互污染覆盖问题
  • 【Linux】NUMA如何梆核
  • .NET正则表达式
  • Spark on Yarn安装配置,大数据技能竞赛(容器环境)
  • Sqoop导入数据(mysql---->>hive)
  • 工作中常用springboot启动后执行的方法
  • 计算机视觉在科学研究(数字化)中的实际应用
  • 机器人的动力学前馈控制
  • spring6:2入门
  • “大数据+中职”:VR虚拟仿真实训室的发展前景
  • 柯桥职场商务英语生活英语口语培训外贸纺织口语学习
  • 传输层5——TCP可靠传输的实现(重点!!)
  • vue3中使用mqtt
  • 【Flux.jl】 卷积神经网络
  • 【CDH国产化替代案例】全面简化架构,降低成本,大幅提升数据处理效率
  • ruoyi的excel批量导入