Aspose.Words Java 表格动态删列、合并列、表头重建、全局字体统一解决方案
一、业务场景
Word 模板中存在多组引用相关双列,根据前端配置动态控制:
- 不需要展示时:直接删除整列
- 需要只展示其中一列(总引/他引二选一):删除两列、保留一列回填
- 重建表头并复刻原单元格样式,解决表头拥挤、文字换行问题
- 全局统一表格字体,指定第二行表头单独加粗,其余行常规字体
- 控制单元格内边距、禁止自动换行、文字居中排版
二、核心依赖
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是隐藏
六、常见问题解决
- 删列后下标错乱:倒序遍历配置列表
- 表头文字拥挤换行 :设置
setLeftPadding/setRightPadding+setWrapText(false) - 新建表头样式丢失:复刻旧单元格 CellFormat 宽、边距、对齐
- 表格字体不统一:提取基准行字体,遍历全表批量赋值
- 表头需要单独加粗:按行下标判断,只给指定行开启 Bold