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<XWPFTable> 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<XWPFTable> 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<XWPFTable> 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());
        }
    }