You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Java中使用POI实现XWPFTable单元格水平合并?

Hey there! Let's tackle that horizontal cell merging issue in XWPFTable together. I know it can be frustrating when vertical merging works but horizontal doesn't—POI's handling of table merges has some gotchas, especially around how Word renders the final result. Here's a reliable, tested approach that should get you the exact outcome you're aiming for:

Core Fix: Combine VMerge with Width Adjustments

The most common reason horizontal merges fail is that many older solutions only set the vMerge property but skip adjusting cell widths and cleaning up redundant cells. Word relies on both the merge flags and proper width allocation to render merged cells correctly.

Step-by-Step Code Implementation

Here's a reusable utility method to handle horizontal merges, plus a full example:

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

public class XWPFHorizontalMergeFix {
    public static void main(String[] args) throws IOException {
        // Create a sample document and table
        XWPFDocument doc = new XWPFDocument();
        XWPFTable table = doc.createTable(3, 4); // 3 rows, 4 columns

        // Populate initial content
        table.getRow(0).getCell(0).setText("Unmerged Cell");
        table.getRow(0).getCell(1).setText("Merge");
        table.getRow(0).getCell(2).setText("These");
        table.getRow(0).getCell(3).setText("Cells");

        // Merge columns 1-3 in the first row (0-indexed)
        mergeHorizontalCells(table, 0, 1, 3);

        // Save the document
        try (FileOutputStream out = new FileOutputStream("horizontal_merge_example.docx")) {
            doc.write(out);
        }
        doc.close();
    }

    /**
     * Merges cells horizontally in a specific row of an XWPFTable
     * @param table Target table
     * @param rowIndex Index of the row to modify (0-indexed)
     * @param startCol Starting column index (0-indexed)
     * @param endCol Ending column index (0-indexed, must be > startCol)
     */
    public static void mergeHorizontalCells(XWPFTable table, int rowIndex, int startCol, int endCol) {
        // Validate input parameters
        if (rowIndex < 0 || rowIndex >= table.getNumberOfRows()) return;
        XWPFRow targetRow = table.getRow(rowIndex);
        if (startCol < 0 || endCol >= targetRow.getTableCells().size() || startCol >= endCol) return;

        CTTblGrid tableGrid = table.getCTTbl().getTblGrid();
        int totalMergedWidth = 0;

        // Calculate total width of columns to merge
        for (int col = startCol; col <= endCol; col++) {
            totalMergedWidth += tableGrid.getGridColArray(col).getW().intValue();
        }

        // Configure the first cell in the merged range
        XWPFCell startCell = targetRow.getCell(startCol);
        CTCell ctStartCell = startCell.getCTCell();
        CTTcPr cellProps = ctStartCell.getTcPr() != null ? ctStartCell.getTcPr() : ctStartCell.addNewTcPr();

        // Mark as the start of the merge
        CTVMerge mergeStart = cellProps.addNewVMerge();
        mergeStart.setVal(STMerge.RESTART);

        // Set width to total merged column width
        CTTcW cellWidth = cellProps.getTcW() != null ? cellProps.getTcW() : cellProps.addNewTcW();
        cellWidth.setW(BigInteger.valueOf(totalMergedWidth));

        // Configure remaining cells in the merged range
        for (int col = startCol + 1; col <= endCol; col++) {
            XWPFCell currentCell = targetRow.getCell(col);
            if (currentCell == null) continue;

            CTCell ctCurrentCell = currentCell.getCTCell();
            CTTcPr currentProps = ctCurrentCell.getTcPr() != null ? ctCurrentCell.getTcPr() : ctCurrentCell.addNewTcPr();

            // Mark as continuation of the merge
            CTVMerge mergeContinue = currentProps.addNewVMerge();
            mergeContinue.setVal(STMerge.CONTINUE);

            // Set width to 0 and clear content to avoid rendering issues
            CTTcW zeroWidth = currentProps.getTcW() != null ? currentProps.getTcW() : currentProps.addNewTcW();
            zeroWidth.setW(BigInteger.valueOf(0));
            currentCell.removeParagraph(0); // Clear any existing text
        }
    }
}

Key Notes to Avoid Failure

  1. VMerge Flags: Always set the first cell to STMerge.RESTART and subsequent cells to STMerge.CONTINUE—this tells POI and Word where the merge starts and ends.
  2. Width Allocation: The starting cell must take up the total width of all merged columns, while merged cells get a width of 0. Without this, Word will still display separate narrow cells even if the merge flags are set.
  3. Clear Redundant Content: Empty out the cells being merged into the start cell to prevent leftover text from breaking the visual merge.
  4. Validate Cell Existence: Ensure all cells in the merge range exist (no null cells) before attempting to merge—create missing cells if needed.

Troubleshooting Checklist

If it's still not working:

  • Open the generated .docx file, go to File > Info > Inspect Document to check for table corruption.
  • Unzip the .docx file and look at word/document.xml—verify that the merged cells have the correct <w:vMerge w:val="restart"/> and <w:vMerge w:val="continue"/> tags, plus proper <w:tcW> width settings.
  • Double-check that you're using a recent version of Apache POI (5.2.0+ recommended)—older versions have bugs in table merge handling.

内容的提问来源于stack exchange,提问作者Joel George

火山引擎 最新活动