当前位置: 首页 > 知识库问答 >
问题:

POI字无法垂直合并新创建的单元格

薛鹏飞
2023-03-14

我知道如何用Apache POI Word垂直合并单元格。但似乎如果创建了新行,合并就不会生效。

下面是输入表:

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

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

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

共有1个答案

商畅
2023-03-14

问题不在于MergeCellVertily方法,而在于复制表行的方法。当复制基础ctrow并使用xwpftable.addrow将其插入ctbl.trarray时,必须完全完成。以后的更改不是用XML编写的。在我的回答中,java Apache POI Word现有的表,插入行的单元格样式和格式已经告诉了这一点。我在回答中提供了一个方法committableRows不能更改。docx文件中的行文本,一旦行添加到table。在写出文档之前需要调用此方法,因此后面的更改将写入XML

因此,因为您正在复制第二行,这是合并的开始,所以该设置也会被复制。后面称为MergeCellVertily的命令不生效。因此,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();

 }
}
 类似资料:
  • 谁能解释一下如何使用Apache POI为合并的单元格创建边框吗? 我使用的代码只影响一个单元格。

  • 我想数一下一行中有多少单元格被合并了?我在Excel中合并(缩放)它,在Java中我想显示例如:“合并的单元格:4”(A1,B1,C1,D1)?

  • 在旧版本中,您可以键入以下内容: 这会产生外部边框--即使是在合并的单元格周围,也会使用选定的颜色。不幸的是,在POI的新版本中,我似乎不能使用索引颜色,因为获得自定义RGB颜色索引的方法不起作用,并且被降级或标记为仅供测试。

  • 我正在使用Apache POI将数据导出到一个.xlsx文件,我想对文件中包含的一些行和单元格设置样式。 我使用XSSF,因为该文件将在Excel2007+中读取。

  • 我做了一个从Java到xls的导出,我使用POI库。 我的createCell方法: 我在一个For中调用这个方法,我有这样的消息错误: Echec de l'export:超出了单元格样式的最大数目。在.xls工作簿中最多可以定义4000个样式 如何重用我的单元格而不必重新创建每个迭代? THX

  • 无法建立垂直。云环境中垂直柱之间的x群集 我在配置vert时遇到问题。x eventbus群集在私有云环境中。 在实验室测试中,我尝试使用Hazelcast集群管理器制作两个垂直点来建立一个集群,每个垂直点都在您自己的容器中运行。 问题很可能是由于配置错误引起的,但我无法找到它。在这个云上,不可能进行多播调用,那么我使用的是TCP IP发现策略。 最初的计划是制作一个“labatf api”垂直链