【问题标题】:POI Word Unable to merge newly created cell verticallyPOI Word 无法垂直合并新创建的单元格
【发布时间】:2021-06-04 01:24:22
【问题描述】:

我知道如何使用 Apache POI 字垂直合并单元格。但是好像新建了一行,合并就不会生效了。

这是输入表:

我希望在old row 2old row 3 之间添加一个新行,并将第一列的新行单元格合并到 C2 中,如下所示:

所以我创建了一个新行并将其添加到old row 2下面的表格中,并尝试合并单元格

github源码link is here,可以重现问题。

public class POIWordAddSubRowQuestionDemo{
    public static void main(String[] args) throws IOException, XmlException{
        ClassLoader classLoader = POIWordAddSubRowQuestionDemo.class.getClassLoader();
        InputStream inputStream = classLoader.getResourceAsStream("input.docx");
        String outputDocxPath = "F:/TEMP/output.docx";
        assert inputStream != null;
        XWPFDocument doc = new XWPFDocument(inputStream);
        XWPFTable table = doc.getTables().get(0);
        //this is 'old row 2'
        XWPFTableRow secondRow = table.getRows().get(1);

        //create a new row that is based on 'old row 2'
        CTRow ctrow = CTRow.Factory.parse(secondRow.getCtRow().newInputStream());
        XWPFTableRow newRow = new XWPFTableRow(ctrow, table);
        XWPFRun xwpfRun = newRow.getCell(1).getParagraphs().get(0).getRuns().get(0);
        //set row text
        xwpfRun.setText("new row", 0);
        // add new row below 'old row 2'
        table.addRow(newRow, 2);

        //merge cells at first column of 'old row 2', 'new row', and 'old row 3'
        mergeCellVertically(doc.getTables().get(0), 0, 1, 3);

        FileOutputStream fos = new FileOutputStream(outputDocxPath);
        doc.write(fos);
        fos.close();
    }

    static void mergeCellVertically(XWPFTable table, int col, int fromRow, int toRow) {
        for(int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
            XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
            CTVMerge vmerge = CTVMerge.Factory.newInstance();
            if(rowIndex == fromRow){
                // The first merged cell is set with RESTART merge value
                vmerge.setVal(STMerge.RESTART);
            } else {
                // Cells which join (merge) the first one, are set with CONTINUE
                vmerge.setVal(STMerge.CONTINUE);
                // and the content should be removed
                for (int i = cell.getParagraphs().size(); i > 0; i--) {
                    cell.removeParagraph(0);
                }
                cell.addParagraph();
            }
            // Try getting the TcPr. Not simply setting an new one every time.
            CTTcPr tcPr = cell.getCTTc().getTcPr();
            if (tcPr == null) tcPr = cell.getCTTc().addNewTcPr();
            tcPr.setVMerge(vmerge);
        }
    }
}

但是合并不起作用,我得到了:

另外一次尝试,我尝试根据图3中的表格进行合并,得到图2中的表格,并且成功了。两次尝试之间的唯一区别是 new row 不是新创建的,而是从 docx 文档中读取的,所以我认为创建新行是合并失败的原因。

那么有没有合并新创建的行的解决方案?我真的不想像这样拆分这个操作:添加行>将docx保存到磁盘>从磁盘读取docx>合并行。

【问题讨论】:

标签: java ms-word apache-poi row cell


【解决方案1】:

您遇到的问题不是mergeCellVertically 方法,而是您复制表格行的方法。当复制底层CTRow 并使用XWPFTable.addRow 将其插入CTTbl.TrArray 时,它必须完全完成。后面的改动没有写在XML。我已经在回答java Apache POI Word existing table insert row with cell style and formatting 中说过了。我在回答Can't change row text in .docx file once row is added to table 中提供了一个方法commitTableRows。这个方法需要在写出文档之前调用,所以后面的修改写在XML中。

因此,因为您正在复制第二行,即合并的开始,该设置也会被复制。而后面调用的mergeCellVertically 不生效。所以你的newRow 仍然是合并的新开始。这就是你得到的。

因此,在完成所有更改后,在写出之前,请致电commitTableRows

完整示例:

import java.io.*;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;


public class WordInsertTableRowAndMerge {
    
 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;
 }

 static void commitTableRows(XWPFTable table) {
  int rowNr = 0;
  for (XWPFTableRow tableRow : table.getRows()) {
   table.getCTTbl().setTrArray(rowNr++, tableRow.getCtRow());
  }
 }
    
 static void mergeCellVertically(XWPFTable table, int col, int fromRow, int toRow) {
  for(int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
   System.out.println("rowIndex: " + rowIndex);
   XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
   CTVMerge vmerge = CTVMerge.Factory.newInstance();
   if(rowIndex == fromRow){
    // The first merged cell is set with RESTART merge value
    vmerge.setVal(STMerge.RESTART);
   } else {
    // Cells which join (merge) the first one, are set with CONTINUE
    vmerge.setVal(STMerge.CONTINUE);
    // and the content should be removed
    for (int i = cell.getParagraphs().size(); i > 0; i--) {
     cell.removeParagraph(0);
    }
    cell.addParagraph();
   }
   // Try getting the TcPr. Not simply setting an new one every time.
   CTTcPr tcPr = cell.getCTTc().getTcPr();
   if (tcPr == null) tcPr = cell.getCTTc().addNewTcPr();
   tcPr.setVMerge(vmerge);
  }
 }
 
 public static void main(String[] args) throws Exception {

  XWPFDocument doc = new XWPFDocument(new FileInputStream("./source.docx"));
  
  XWPFTable table = doc.getTables().get(0);

  XWPFTableRow row = table.getRow(1);
  XWPFTableRow newRow = insertNewTableRow(row, 2);
  
  XWPFTableCell cell = newRow.getCell(0); if (cell == null) cell = newRow.addNewTableCell();
  // not needed because merged to cell above
  cell = newRow.getCell(1); if (cell == null) cell = newRow.addNewTableCell();
  for (XWPFParagraph paragraph : cell.getParagraphs()) { // only use first text runs in paragraphs
   for (int r = paragraph.getRuns().size()-1; r >= 0; r--) {
    XWPFRun run = paragraph.getRuns().get(r);
    if (r == 0) {
     run.setText("new row 1", 0);
    } else {
     paragraph.removeRun(r); 
    }
   }
  }

  mergeCellVertically(table, 0, 1, 3);

  commitTableRows(table);

  FileOutputStream out = new FileOutputStream("./result.docx");
  doc.write(out);
  out.close();
  doc.close();

 }
}

【讨论】:

  • 我试过commitTableRows,效果很好。
【解决方案2】:

这是您可能希望采用的 VBA 方法。它在第 3 行和第 4 行之间插入一个新行:

Sub Demo()
Application.ScreenUpdating = False
With ActiveDocument.Tables(1)
  .Cell(4, 2).Range.InsertBreak (wdColumnBreak)
  .Rows.Add
  .Cell(2, 1).Merge MergeTo:=.Cell(4, 1)
  .Range.Characters.Last.Next.Delete
  .Cell(2, 1).Merge MergeTo:=.Cell(5, 1)
End With
Application.ScreenUpdating = True
End Sub

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-23
    • 1970-01-01
    相关资源
    最近更新 更多