poi-tl的详细教程(动态表格、单元格合并)

前提了解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();
        }

    }

}

   
poi-tl是一个基于Apache POIJava模板引擎,用于生成Word、Excel和PowerPoint文档。它提供了一种简单而强大的方式来操作这些文档。 在poi-tl中,要合并Word文档中的列单元格,可以按照以下步骤进行操作: 1. 创建一个Word模板文件,其中包含需要合并单元格表格。 2. 使用poi-tl的API加载模板文件,并获取到需要操作的表格对象。 3. 使用表格对象的合并单元格方法,指定需要合并的起始行、起始列、结束行和结束列。 4. 根据需要重复步骤3,合并多个列单元格。 5. 保存修改后的Word文档。 下面是一个示例代码,演示了如何使用poi-tl合并Word文档中的列单元格: ```java import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFTable; import org.apache.poi.xwpf.usermodel.XWPFTableCell; import org.apache.poi.xwpf.usermodel.XWPFTableRow; import org.apache.poi.xwpf.usermodel.XWPFRun; import org.apache.poi.xwpf.usermodel.VerticalAlign; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class MergeTableCellsExample { public static void main(String[] args) { try { // 加载Word模板文件 FileInputStream fileInputStream = new FileInputStream("template.docx"); XWPFDocument document = new XWPFDocument(fileInputStream); // 获取第一个表格对象 XWPFTable table = document.getTables().get(0); // 合并第一行的前两个单元格 mergeTableCells(table, 0, 0, 0, 1); // 保存修改后的Word文档 FileOutputStream fileOutputStream = new FileOutputStream("output.docx"); document.write(fileOutputStream); fileOutputStream.close(); System.out.println("合并单元格成功!"); } catch (IOException e) { e.printStackTrace(); } } private static void mergeTableCells(XWPFTable table, int startRow, int startCol, int endRow, int endCol) { for (int rowIndex = startRow; rowIndex <= endRow; rowIndex++) { XWPFTableRow row = table.getRow(rowIndex); for (int colIndex = startCol; colIndex <= endCol; colIndex++) { XWPFTableCell cell = row.getCell(colIndex); if (colIndex == startCol) { // 设置合并单元格的垂直对齐方式为居中 cell.setVerticalAlignment(VerticalAlign.CENTER); } else { // 移除非起始列的单元格 row.removeCell(colIndex); } } } } } ``` 请注意,上述示例代码中的"template.docx"是模板文件的路径,"output.docx"是保存合并后的Word文档的路径。你需要根据实际情况修改这些路径。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值