.前言
前面的文章介绍了在word中创建表格的行,这一篇中简单介绍一下合并单元格以及修改样式的一些方法。

1.合并单元格
分为合并行和合并列两种方式

void testXWPFTable1(){
    System.out.println("开始执行");
    try {
        //定义word
        XWPFDocument doc = new XWPFDocument();
        //在word中创建一个表格(2行,3列)
        XWPFTable table = doc.createTable(3,3);
        //第一行数据
        table.getRow(0).getCell(0).setText("1");
        table.getRow(0).getCell(1).setText("2");
        table.getRow(0).getCell(2).setText("3");
        table.getRow(1).getCell(0).setText("4");
        table.getRow(1).getCell(1).setText("5");
        table.getRow(1).getCell(2).setText("6");
        table.getRow(2).getCell(0).setText("7");
        table.getRow(2).getCell(1).setText("8");
        table.getRow(2).getCell(2).setText("9");
        //横向合并,第一行的0和1
        table.getRow(0).getCell(0).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
        table.getRow(0).getCell(1).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
        //竖向合并,
        table.getRow(1).getCell(0).getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
        table.getRow(2).getCell(0).getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
        //导出
        String path = "C:\\";  //文件路径
        String name = "test1";  //文件名
        path = path + "/" + name + ".docx";
        File file = new File(path);
        if (!file.exists()) {
            file.createNewFile();
        }
        FileOutputStream out = new FileOutputStream(file);
        doc.write(out);
    }catch (IOException  e){
        e.printStackTrace();
    }
}

效果:

提示:如果要合并第一列到第三列,单独写话每一个都要写一次,要不就参考用下面封装好的方法

我们也可以用封装好的方法:

/**
• @Description: 跨列合并
• table
• row:行
• fromCell:开始列
• toCell:结束列
/
 public void mergeCellsHorizontal(XWPFTable table, int row, int fromCell, int toCell) {
 for (int cellIndex = fromCell; cellIndex <= toCell; cellIndex++) {
 XWPFTableCell cell = table.getRow(row).getCell(cellIndex);
 if ( cellIndex == fromCell ) {
 // The first merged cell is set with RESTART merge value
 cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
 } else {
 // Cells which join (merge) the first one, are set with CONTINUE
 cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
 }
 }
 }
 /*• @Description: 跨行合并
• table
• col:列
• formRow:开始行
• toRow:结束行
 */
 public void mergeCellsVertically(XWPFTable table, int col, int fromRow, int toRow) {
 for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
 XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
 if ( rowIndex == fromRow ) {
 // The first merged cell is set with RESTART merge value
 cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
 } else {
 // Cells which join (merge) the first one, are set with CONTINUE
 cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
 }
 }
 }

  • 再测试一下导入word中合并单元格的方法

要导入的表格如下:

测试代码:

void testXWPFTable2(){
 System.out.println(“开始执行”);
 try {
 InputStream is = new FileInputStream(“C:\test.docx”);
 //定义word
 XWPFDocument doc = new XWPFDocument(is);
 //获取word中所有的表格
 List tableList = doc.getTables();
 XWPFTable table1 = tableList.get(0); //表格中第一个表
 table1.createRow(); //创建一行
 //合并单元格
 mergeCellsHorizontal(table1,0,0,2);
 mergeCellsVertically(table1,1,1,2);
//导出
     String path = "C:\\";  //文件路径
     String name = "test2";  //文件名
     path = path + "/" + name + ".docx";
     File file = new File(path);
     if (!file.exists()) {
         file.createNewFile();
     }
     FileOutputStream out = new FileOutputStream(file);
     doc.write(out);
 }catch (IOException  e){
     e.printStackTrace();
 }

}
效果:

2.addRow合并的问题:
上面验证了合并单元格的一些方式不论是手动创建表格,还是读取表格后,都能正常合并。包括使用createRow这个方法后,也能正常合并。但是使用addRow方法后,发现并不能正常使用。例子如下:

我们先读取一个本地文件

我们在红色箭头处通过addRow方法插入一个新行,然后执行合并操作,看看效果。

参考代码:

void testXWPFTable2(){
 System.out.println(“开始执行”);
 try {
 InputStream is = new FileInputStream(“C:\test.docx”); //读取了一个4*4的表格
 //定义word
 XWPFDocument doc = new XWPFDocument(is);
 //获取word中所有的表格
 List tableList = doc.getTables();
 XWPFTable table1 = tableList.get(0); //表格中第一个表
CTRow ctrow = null;
       try {
           ctrow = CTRow.Factory.parse(table1.getRow(0).getCtRow().newInputStream());  //复制
       } catch (XmlException e) {
           e.printStackTrace();
       } catch (IOException e) {
           e.printStackTrace();
       }
       XWPFTableRow newRow = new XWPFTableRow(ctrow, table1);
       newRow.getCell(0).setText("A");
       newRow.getCell(1).setText("B");
       newRow.getCell(2).setText("C");
       table1.addRow(newRow,1);

       mergeCellsHorizontal(table1,0,1,3);  //合并列(未涉及新增行)
       mergeCellsHorizontal(table1,1,0,1);  //合并列(涉及新增行)
       mergeCellsVertically(table1,2,1,4);  //合并行(涉及新增行)
       mergeCellsVertically(table1,0,0,2);  //合并行(涉及新增行)
        
       //导出
       String path = "C:\\";  //文件路径
       String name = "test2";  //文件名
       path = path + "/" + name + ".docx";
       File file = new File(path);
       if (!file.exists()) {
           file.createNewFile();
       }
       FileOutputStream out = new FileOutputStream(file);
       doc.write(out);
   }catch (IOException  e){
       e.printStackTrace();
   }

}
效果如下:

可以看到,我们使用addRow新增的行是无法进行合并的,即使合并的单元格中包含也会被忽略掉。

官方文档中并没有详细说明过这个问题,但是应该是跟我们复制有关。

最后终于找到了解决办法,加入一个函数commitTabkeRows,在合并完调用这个函数即可。

static void commitTableRows(XWPFTable table) {
 int rowNr = 0;
 for (XWPFTableRow tableRow : table.getRows()) {
 table.getCTTbl().setTrArray(rowNr++, tableRow.getCtRow());
 }
 }
 那么,合并以后调用即可:void testXWPFTable2(){
 System.out.println(“开始执行”);
 try {
 InputStream is = new FileInputStream(“C:\test.docx”); //读取了一个4*4的表格
 //定义word
 XWPFDocument doc = new XWPFDocument(is);
 //获取word中所有的表格
 List tableList = doc.getTables();
 XWPFTable table1 = tableList.get(0); //表格中第一个表
CTRow ctrow = null;
       try {
           ctrow = CTRow.Factory.parse(table1.getRow(0).getCtRow().newInputStream());  //复制
       } catch (XmlException e) {
           e.printStackTrace();
       } catch (IOException e) {
           e.printStackTrace();
       }
       XWPFTableRow newRow = new XWPFTableRow(ctrow, table1);
       newRow.getCell(0).setText("A");
       newRow.getCell(1).setText("B");
       newRow.getCell(2).setText("C");
       table1.addRow(newRow,1);

       mergeCellsHorizontal(table1,0,1,3);  //合并列(未涉及新增行)
       mergeCellsHorizontal(table1,1,0,1);  //合并列(涉及新增行)
       mergeCellsVertically(table1,2,1,4);  //合并行(涉及新增行)
       mergeCellsVertically(table1,0,0,2);  //合并行(涉及新增行)
       commitTableRows(table1);
       //导出
       String path = "C:\\";  //文件路径
       String name = "test2";  //文件名
       path = path + "/" + name + ".docx";
       File file = new File(path);
       if (!file.exists()) {
           file.createNewFile();
       }
       FileOutputStream out = new FileOutputStream(file);
       doc.write(out);
   }catch (IOException  e){
       e.printStackTrace();
   }

}
效果:

3.总结
上面用到了很多封装好的函数,在此总结一下

1.跨列合并

/**
 * @Description: 跨列合并
 * table
 * row:行
 * fromCell:开始列
 * toCell:结束列
 */
 public void mergeCellsHorizontal(XWPFTable table, int row, int fromCell, int toCell) {
 for (int cellIndex = fromCell; cellIndex <= toCell; cellIndex++) {
 XWPFTableCell cell = table.getRow(row).getCell(cellIndex);
 if ( cellIndex == fromCell ) {
 // The first merged cell is set with RESTART merge value
 cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
 } else {
 // Cells which join (merge) the first one, are set with CONTINUE
 cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
 }
 }
 }
 2.跨行合并/**
 * @Description: 跨行合并
 * table
 * col:列
 * formRow:开始行
 * toRow:结束行
 */
 public void mergeCellsVertically(XWPFTable table, int col, int fromRow, int toRow) {
 for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
 XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
 if ( rowIndex == fromRow ) {
 // The first merged cell is set with RESTART merge value
 cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
 } else {
 // Cells which join (merge) the first one, are set with CONTINUE
 cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
 }
 }
 }
 3.addRow方法封装/**
 * 复制一行到一个新位置插入
 * @param sourceTableRow 要复制的行
 * @param pos 要插入的位置
 * @return
 * @throws Exception
 */
 static XWPFTableRow insertNewTableRow(XWPFTableRow sourceTableRow, int pos) throws Exception {
 XWPFTable table = sourceTableRow.getTable();
 CTRow newCTRrow = CTRow.Factory.parse(sourceTableRow.getCtRow().newInputStream());
 XWPFTableRow tableRow = new XWPFTableRow(newCTRrow, table);
 table.addRow(tableRow, pos);
 return tableRow;
 }


4.重新合并单元格

/**
 * 合并单元格,在使用过addRow方法并合并单元格后再调用
 * @param table
 */
 static void commitTableRows(XWPFTable table) {
 int rowNr = 0;
 for (XWPFTableRow tableRow : table.getRows()) {
 table.getCTTbl().setTrArray(rowNr++, tableRow.getCtRow());
 }
 }


0.前言
前面的文章介绍了在word中创建表格的行,这一篇中简单介绍一下合并单元格以及修改样式的一些方法。

1.合并单元格
分为合并行和合并列两种方式

void testXWPFTable1(){
    System.out.println("开始执行");
    try {
        //定义word
        XWPFDocument doc = new XWPFDocument();
        //在word中创建一个表格(2行,3列)
        XWPFTable table = doc.createTable(3,3);
        //第一行数据
        table.getRow(0).getCell(0).setText("1");
        table.getRow(0).getCell(1).setText("2");
        table.getRow(0).getCell(2).setText("3");
        table.getRow(1).getCell(0).setText("4");
        table.getRow(1).getCell(1).setText("5");
        table.getRow(1).getCell(2).setText("6");
        table.getRow(2).getCell(0).setText("7");
        table.getRow(2).getCell(1).setText("8");
        table.getRow(2).getCell(2).setText("9");
        //横向合并,第一行的0和1
        table.getRow(0).getCell(0).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
        table.getRow(0).getCell(1).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
        //竖向合并,
        table.getRow(1).getCell(0).getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
        table.getRow(2).getCell(0).getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
        //导出
        String path = "C:\\";  //文件路径
        String name = "test1";  //文件名
        path = path + "/" + name + ".docx";
        File file = new File(path);
        if (!file.exists()) {
            file.createNewFile();
        }
        FileOutputStream out = new FileOutputStream(file);
        doc.write(out);
    }catch (IOException  e){
        e.printStackTrace();
    }
}

效果:

提示:如果要合并第一列到第三列,单独写话每一个都要写一次,要不就参考用下面封装好的方法

我们也可以用封装好的方法:

/**
• @Description: 跨列合并
• table
• row:行
• fromCell:开始列
• toCell:结束列
/
 public void mergeCellsHorizontal(XWPFTable table, int row, int fromCell, int toCell) {
 for (int cellIndex = fromCell; cellIndex <= toCell; cellIndex++) {
 XWPFTableCell cell = table.getRow(row).getCell(cellIndex);
 if ( cellIndex == fromCell ) {
 // The first merged cell is set with RESTART merge value
 cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
 } else {
 // Cells which join (merge) the first one, are set with CONTINUE
 cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
 }
 }
 }
 /*• @Description: 跨行合并
• table
• col:列
• formRow:开始行
• toRow:结束行
 */
 public void mergeCellsVertically(XWPFTable table, int col, int fromRow, int toRow) {
 for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
 XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
 if ( rowIndex == fromRow ) {
 // The first merged cell is set with RESTART merge value
 cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
 } else {
 // Cells which join (merge) the first one, are set with CONTINUE
 cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
 }
 }
 }

  • 再测试一下导入word中合并单元格的方法

要导入的表格如下:

测试代码:

void testXWPFTable2(){
 System.out.println(“开始执行”);
 try {
 InputStream is = new FileInputStream(“C:\test.docx”);
 //定义word
 XWPFDocument doc = new XWPFDocument(is);
 //获取word中所有的表格
 List tableList = doc.getTables();
 XWPFTable table1 = tableList.get(0); //表格中第一个表
 table1.createRow(); //创建一行
 //合并单元格
 mergeCellsHorizontal(table1,0,0,2);
 mergeCellsVertically(table1,1,1,2);
//导出
     String path = "C:\\";  //文件路径
     String name = "test2";  //文件名
     path = path + "/" + name + ".docx";
     File file = new File(path);
     if (!file.exists()) {
         file.createNewFile();
     }
     FileOutputStream out = new FileOutputStream(file);
     doc.write(out);
 }catch (IOException  e){
     e.printStackTrace();
 }

}
效果:

2.addRow合并的问题:
上面验证了合并单元格的一些方式不论是手动创建表格,还是读取表格后,都能正常合并。包括使用createRow这个方法后,也能正常合并。但是使用addRow方法后,发现并不能正常使用。例子如下:

我们先读取一个本地文件

我们在红色箭头处通过addRow方法插入一个新行,然后执行合并操作,看看效果。

参考代码:

void testXWPFTable2(){
System.out.println(“开始执行”);
try {
InputStream is = new FileInputStream(“C:\test.docx”); //读取了一个4*4的表格
//定义word
XWPFDocument doc = new XWPFDocument(is);
//获取word中所有的表格
List tableList = doc.getTables();
XWPFTable table1 = tableList.get(0); //表格中第一个表

CTRow ctrow = null;
       try {
           ctrow = CTRow.Factory.parse(table1.getRow(0).getCtRow().newInputStream());  //复制
       } catch (XmlException e) {
           e.printStackTrace();
       } catch (IOException e) {
           e.printStackTrace();
       }
       XWPFTableRow newRow = new XWPFTableRow(ctrow, table1);
       newRow.getCell(0).setText("A");
       newRow.getCell(1).setText("B");
       newRow.getCell(2).setText("C");
       table1.addRow(newRow,1);

       mergeCellsHorizontal(table1,0,1,3);  //合并列(未涉及新增行)
       mergeCellsHorizontal(table1,1,0,1);  //合并列(涉及新增行)
       mergeCellsVertically(table1,2,1,4);  //合并行(涉及新增行)
       mergeCellsVertically(table1,0,0,2);  //合并行(涉及新增行)
        
       //导出
       String path = "C:\\";  //文件路径
       String name = "test2";  //文件名
       path = path + "/" + name + ".docx";
       File file = new File(path);
       if (!file.exists()) {
           file.createNewFile();
       }
       FileOutputStream out = new FileOutputStream(file);
       doc.write(out);
   }catch (IOException  e){
       e.printStackTrace();
   }

}
效果如下:

可以看到,我们使用addRow新增的行是无法进行合并的,即使合并的单元格中包含也会被忽略掉。

官方文档中并没有详细说明过这个问题,但是应该是跟我们复制有关。

最后终于找到了解决办法,加入一个函数commitTabkeRows,在合并完调用这个函数即可。

static void commitTableRows(XWPFTable table) {
 int rowNr = 0;
 for (XWPFTableRow tableRow : table.getRows()) {
 table.getCTTbl().setTrArray(rowNr++, tableRow.getCtRow());
 }
 }


那么,合并以后调用即可:

void testXWPFTable2(){
 System.out.println(“开始执行”);
 try {
 InputStream is = new FileInputStream(“C:\test.docx”); //读取了一个4*4的表格
 //定义word
 XWPFDocument doc = new XWPFDocument(is);
 //获取word中所有的表格
 List tableList = doc.getTables();
 XWPFTable table1 = tableList.get(0); //表格中第一个表
CTRow ctrow = null;
       try {
           ctrow = CTRow.Factory.parse(table1.getRow(0).getCtRow().newInputStream());  //复制
       } catch (XmlException e) {
           e.printStackTrace();
       } catch (IOException e) {
           e.printStackTrace();
       }
       XWPFTableRow newRow = new XWPFTableRow(ctrow, table1);
       newRow.getCell(0).setText("A");
       newRow.getCell(1).setText("B");
       newRow.getCell(2).setText("C");
       table1.addRow(newRow,1);

       mergeCellsHorizontal(table1,0,1,3);  //合并列(未涉及新增行)
       mergeCellsHorizontal(table1,1,0,1);  //合并列(涉及新增行)
       mergeCellsVertically(table1,2,1,4);  //合并行(涉及新增行)
       mergeCellsVertically(table1,0,0,2);  //合并行(涉及新增行)
       commitTableRows(table1);
       //导出
       String path = "C:\\";  //文件路径
       String name = "test2";  //文件名
       path = path + "/" + name + ".docx";
       File file = new File(path);
       if (!file.exists()) {
           file.createNewFile();
       }
       FileOutputStream out = new FileOutputStream(file);
       doc.write(out);
   }catch (IOException  e){
       e.printStackTrace();
   }

}
效果:

3.总结
上面用到了很多封装好的函数,在此总结一下

1.跨列合并

/**
 * @Description: 跨列合并
 * table
 * row:行
 * fromCell:开始列
 * toCell:结束列
 */
 public void mergeCellsHorizontal(XWPFTable table, int row, int fromCell, int toCell) {
 for (int cellIndex = fromCell; cellIndex <= toCell; cellIndex++) {
 XWPFTableCell cell = table.getRow(row).getCell(cellIndex);
 if ( cellIndex == fromCell ) {
 // The first merged cell is set with RESTART merge value
 cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
 } else {
 // Cells which join (merge) the first one, are set with CONTINUE
 cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
 }
 }
 }
 2.跨行合并/**
 * @Description: 跨行合并
 * table
 * col:列
 * formRow:开始行
 * toRow:结束行
 */
 public void mergeCellsVertically(XWPFTable table, int col, int fromRow, int toRow) {
 for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
 XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
 if ( rowIndex == fromRow ) {
 // The first merged cell is set with RESTART merge value
 cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
 } else {
 // Cells which join (merge) the first one, are set with CONTINUE
 cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
 }
 }
 }
 3.addRow方法封装/**
 * 复制一行到一个新位置插入
 * @param sourceTableRow 要复制的行
 * @param pos 要插入的位置
 * @return
 * @throws Exception
 */
 static XWPFTableRow insertNewTableRow(XWPFTableRow sourceTableRow, int pos) throws Exception {
 XWPFTable table = sourceTableRow.getTable();
 CTRow newCTRrow = CTRow.Factory.parse(sourceTableRow.getCtRow().newInputStream());
 XWPFTableRow tableRow = new XWPFTableRow(newCTRrow, table);
 table.addRow(tableRow, pos);
 return tableRow;
 }
 4.重新合并单元格/**
 * 合并单元格,在使用过addRow方法并合并单元格后再调用
 * @param table
 */
 static void commitTableRows(XWPFTable table) {
 int rowNr = 0;
 for (XWPFTableRow tableRow : table.getRows()) {
 table.getCTTbl().setTrArray(rowNr++, tableRow.getCtRow());
 }
 }