Aspose.Words Java 表格动态删列、合并列、表头重建、全局字体统一解决方案

Aspose.Words Java 表格动态删列、合并列、表头重建、全局字体统一解决方案

一、业务场景

Word 模板中存在多组引用相关双列,根据前端配置动态控制:

  1. 不需要展示时:直接删除整列
  2. 需要只展示其中一列(总引/他引二选一):删除两列、保留一列回填
  3. 重建表头并复刻原单元格样式,解决表头拥挤、文字换行问题
  4. 全局统一表格字体,指定第二行表头单独加粗,其余行常规字体
  5. 控制单元格内边距、禁止自动换行、文字居中排版

二、核心依赖

xml 复制代码
<!-- Aspose.Words -->
<dependency>
    <groupId>com.aspose</groupId>
    <artifactId>aspose-words</artifactId>
    <version>22.6</version>
</dependency>

三、完整核心代码

java 复制代码
// 加载Word文档
doc = new Document(exportPath + "new.docx");
XBPaperDel xbPaperDel = ExportService.Totals(vo);

// 获取指定表格(第二个表格 下标1)
Table table = getTable(doc, 1);
if (vo.getTableListShowsList() != null) {
    List<TableListShow> items = new ArrayList<>(vo.getTableListShowsList());
    // 倒序遍历,防止删列导致下标错乱
    Collections.reverse(items);

    // 提前缓存数据行默认单元格样式
    Row firstDataRow = table.getRows().get(2);
    CellFormat defaultDataFormat = null;
    if (firstDataRow.getCells().getCount() > 0) {
        defaultDataFormat = firstDataRow.getCells().get(0).getCellFormat();
    }

    for (TableListShow item : items) {
        boolean isQuote = item.getName().contains("引用");
        boolean showTotal = item.isShowTotal();
        boolean showOther = item.isShowOther();

        // 1. 配置不展示:直接删除对应表头+整表数据列
        if (!item.isShow()) {
            String name = item.getName();
            table = getTable(doc, 1);
            Row headerRow = table.getRows().get(1);

            // 匹配表头列下标
            int headerColIndex = -1;
            for (int i = 0; i < headerRow.getCells().getCount(); i++) {
                if (headerRow.getCells().get(i).getText().trim().equals(name.trim())) {
                    headerColIndex = i;
                    break;
                }
            }
            if (headerColIndex == -1) continue;

            // 计算数据列起始下标
            int dataStartCol = -1;
            if (isQuote) {
                int currentQuoteIndex = 0;
                for (int i = 0; i < headerColIndex; i++) {
                    if (headerRow.getCells().get(i).getText().trim().contains("引用")) {
                        currentQuoteIndex++;
                    }
                }
                int currentNormalCount = 0;
                for (int i = 0; i < headerRow.getCells().getCount(); i++) {
                    if (!headerRow.getCells().get(i).getText().trim().contains("引用")) {
                        currentNormalCount++;
                    } else break;
                }
                dataStartCol = currentNormalCount + (currentQuoteIndex * 2);
            } else {
                dataStartCol = headerColIndex;
            }

            // 删除表头单元格
            if (headerColIndex < headerRow.getCells().getCount()) {
                headerRow.getCells().get(headerColIndex).remove();
            }

            // 遍历所有数据行删除对应列
            for (Row row : table.getRows()) {
                if (row == headerRow) continue;
                if (isQuote) {
                    if (dataStartCol + 1 < row.getCells().getCount()) {
                        row.getCells().get(dataStartCol + 1).remove();
                    }
                    if (dataStartCol < row.getCells().getCount()) {
                        row.getCells().get(dataStartCol).remove();
                    }
                } else {
                    if (dataStartCol < row.getCells().getCount()) {
                        row.getCells().get(dataStartCol).remove();
                    }
                }
            }
        }

        // 2. 引用列二选一展示:删除双列、保留一列回填
        else if (isQuote && showTotal != showOther) {
            String quoteName = item.getName();
            table = getTable(doc, 1);
            Row headerRow = table.getRows().get(1);

            // 匹配表头下标
            int headerColIndex = -1;
            for (int i = 0; i < headerRow.getCells().getCount(); i++) {
                if (headerRow.getCells().get(i).getText().trim().equals(quoteName)) {
                    headerColIndex = i;
                    break;
                }
            }
            if (headerColIndex == -1) continue;

            // 计算引用列偏移下标
            int currentQuoteIndex = 0;
            for (int i = 0; i < headerColIndex; i++) {
                if (headerRow.getCells().get(i).getText().trim().contains("引用")) {
                    currentQuoteIndex++;
                }
            }
            int currentNormalCount = 0;
            for (int i = 0; i < headerRow.getCells().getCount(); i++) {
                if (!headerRow.getCells().get(i).getText().trim().contains("引用")) {
                    currentNormalCount++;
                } else break;
            }
            int dataStartCol = currentNormalCount + (currentQuoteIndex * 2);
            // 选中要保留的列:总引/他引
            int keepDataCol = showTotal ? dataStartCol : dataStartCol + 1;

            // 数据行:删除两列,把原单元格回填(保留原有样式)
            for (Row row : table.getRows()) {
                if (row == headerRow) continue;
                if (keepDataCol >= row.getCells().getCount()) continue;

                Cell keepCell = row.getCells().get(keepDataCol);
                if (dataStartCol + 1 < row.getCells().getCount())
                    row.getCells().get(dataStartCol + 1).remove();
                if (dataStartCol < row.getCells().getCount())
                    row.getCells().get(dataStartCol).remove();

                row.getCells().insert(dataStartCol, keepCell);
            }

            // 表头重建:删除旧表头、新建复刻样式
            Cell oldHeader = headerRow.getCells().get(headerColIndex);
            headerRow.getCells().get(headerColIndex).remove();

            Cell newHeader = new Cell(table.getDocument());
            newHeader.ensureMinimum();
            setCellText(newHeader, quoteName);
            headerRow.getCells().insert(headerColIndex, newHeader);

            // 复刻原表头样式 + 解决拥挤、自动换行
            CellFormat oldFmt = oldHeader.getCellFormat();
            CellFormat newFmt = newHeader.getCellFormat();
            newFmt.setWidth(oldFmt.getWidth());
            newFmt.setVerticalAlignment(CellVerticalAlignment.CENTER);
            // 设置左右内边距,文字不贴边不拥挤
            newFmt.setLeftPadding(8);
            newFmt.setRightPadding(8);
            // 禁止单元格文字自动换行
            newFmt.setWrapText(false);

            // 段落水平居中
            Paragraph para = newHeader.getFirstParagraph();
            para.getParagraphFormat().setAlignment(ParagraphAlignment.CENTER);
        }
    }
    // 刷新页面布局
    doc.updatePageLayout();

    // 3. 全局统一表格字体:第1行表头单独加粗,其余行常规
    if (table.getRows().getCount() > 0) {
        // 以第0行作为字体基准样式
        Row firstDataRows = table.getRows().get(0);
        if (firstDataRows.getCells().getCount() > 0) {
            Cell firstCell = firstDataRows.getCells().get(0);
            if (firstCell.getFirstParagraph() != null && firstCell.getFirstParagraph().getRuns().getCount() > 0) {
                Font templateFont = firstCell.getFirstParagraph().getRuns().get(0).getFont();

                // 遍历所有行统一字体
                for (int i = 0; i < table.getRows().getCount(); i++) {
                    Row row = table.getRows().get(i);
                    // 下标1为表头行,单独加粗
                    boolean isHeader = (i == 1);

                    for (Cell cell : row.getCells()) {
                        if (cell.getFirstParagraph() == null) continue;
                        // 表头行禁止换行、居中排版
                        if(isHeader){
                            cell.getCellFormat().setWrapText(false);
                            Paragraph para = cell.getFirstParagraph();
                            para.getParagraphFormat().setAlignment(ParagraphAlignment.CENTER);
                            para.getParagraphFormat().setFirstLineIndent(0);
                        }

                        // 统一字体名称、大小、颜色,控制表头加粗
                        for (Run run : cell.getFirstParagraph().getRuns()) {
                            run.getFont().setName(templateFont.getName());
                            run.getFont().setSize(templateFont.getSize());
                            run.getFont().setColor(templateFont.getColor());
                            run.getFont().setBold(isHeader);
                        }
                    }
                }
            }
        }
    }
}

//可设置 aspose表格自适应

table.autoFit(AutoFitBehavior.AUTO_FIT_TO_CONTENTS);
table.autoFit(AutoFitBehavior.AUTO_FIT_TO_WINDOW);

四、关键核心知识点解析

1. 倒序遍历删列

直接正序删除单元格会导致数组下标前移错乱 ,采用 Collections.reverse(items) 倒序遍历规避下标漂移问题。

2. 引用双列动态计算下标

引用类字段固定占两列,通过统计前面普通列、引用列数量,自动计算数据列起始下标,适配任意表头顺序。

3. 单元格复用保留原生样式

不新建单元格,直接取出原有单元格、删除多余列、回填插入,自带原字体、边框、对齐样式,无需逐个复制格式。

4. 表头重建样式适配

java 复制代码
// 左右内边距,解决文字拥挤
newFmt.setLeftPadding(8);
newFmt.setRightPadding(8);
// 禁止自动换行,单行展示
newFmt.setWrapText(false);
// 文字水平居中
para.getParagraphFormat().setAlignment(ParagraphAlignment.CENTER);

5. 全局字体统一+差异化加粗

  • 以表格第0行为字体基准(字体、字号、颜色)
  • 指定下标1行为表头,单独设置加粗
  • 其余行继承基准字体、不加粗,全局样式整齐统一

五、工具方法依赖

java 复制代码
/**
 * 获取文档中指定下标表格
 */
private Table getTable(Document doc, int index) {
    List<Table> tableList = doc.getTables().stream().collect(Collectors.toList());
    return tableList.get(index);
}

/**
 * 给单元格设置文本
 */
private void setCellText(Cell cell, String text) {
    cell.getFirstParagraph().getRuns().clear();
    Run run = new Run(cell.getDocument(), text);
    cell.getFirstParagraph().appendChild(run);
}
java 复制代码
//前段请求入参如下 只要包含引用就是两列     
// 
    "tableListShowsList": [
        {
            "name": "WR引用", //标识要隐藏哪个表头
            "show": true, // 是否隐藏 false是隐藏
            "showTotal": false, // 是否隐藏总引 false是隐藏
            "showOther": true // 是否隐藏他引 false是隐藏
        }, 
        {
            "name": "GZ引用",
            "show": true,
            "showTotal": false,
            "showOther": true
        },
        {
            "name": "G被引",
            "show": false
        }
    ]
// 实体类为 
// TableListShow
    private String name;
    private boolean isShow;
    private boolean showTotal;  // 是否显示总引(默认true)true是显示 false是隐藏
    private boolean showOther;  // 是否显示他引(默认true)true是显示 false是隐藏

六、常见问题解决

  1. 删列后下标错乱:倒序遍历配置列表
  2. 表头文字拥挤换行 :设置 setLeftPadding/setRightPadding + setWrapText(false)
  3. 新建表头样式丢失:复刻旧单元格 CellFormat 宽、边距、对齐
  4. 表格字体不统一:提取基准行字体,遍历全表批量赋值
  5. 表头需要单独加粗:按行下标判断,只给指定行开启 Bold
相关推荐
lynnlovemin7 小时前
C++高精度加减乘除算法详解
开发语言·c++·算法·高精度
计算机学姐7 小时前
基于微信小程序的宠物服务系统【uniapp+springboot+vue】
java·vue.js·spring boot·mysql·微信小程序·uni-app·宠物
lst04267 小时前
Maven 构建命令
java·maven
Dxy12393102167 小时前
js如何根据开始位置结束位置在类表中取对应范围的数据
开发语言·javascript·ecmascript
空中海7 小时前
第一章:入门篇 — Maven 核心概念与基础使用
java·maven
eastyuxiao7 小时前
OpenClaw 文档处理Skill
开发语言·人工智能
Trival_dream7 小时前
应用与实例的关系
java·docker·kubernetes
rrr27 小时前
【PyQt5】| 多线程设计模式
开发语言·qt·设计模式
凉、介7 小时前
C 语言类型强转引发的隐蔽内存破坏问题分析
c语言·开发语言·笔记·学习·嵌入式