0

I want to horizontally merge columns of the row in a XWPFTable. I tried the answer in this link. How to merge cells (or apply colspan) using XWPFTable in POI in Java? and also of this link How to merge cells horizontally using apache-poi

It helped me to get cells merged vertically. But horizontal merge is not happening. I am attaching the sample screenshot of what I really wanted.

excel image

Thanks.

Johny
  • 2,128
  • 3
  • 20
  • 33
Joel George
  • 62
  • 2
  • 12
  • See https://stackoverflow.com/questions/34647624/how-to-colspan-a-table-in-word-with-apache-poi/34663420#34663420 – Axel Richter Mar 11 '18 at 10:53
  • @AxelRichter. I also tried this method. I got the output as row 0, col 0 as verticaly merged. I need columns to be vertically merged. I am using WPS writer for the docx file. Will that be the problem? – Joel George Mar 11 '18 at 11:05
  • Sure because the code does exactly this using `mergeCellVertically(table, 0, 0, 1);`. But it also does merging horizontally row 1, col 2 using `mergeCellHorizontally(table, 1, 2, 3);` and row 2, col 1 using `mergeCellHorizontally(table, 2, 1, 4);`. – Axel Richter Mar 11 '18 at 11:14
  • @AxelRichter. Yes. Vertical merging is happening. But horizontal merging is not happening. This is the output i got after commenting the vertical merging code. [Pic](https://prnt.sc/ipn0ic) – Joel George Mar 11 '18 at 11:25
  • The code in my linked answer works (tested) for Microsoft Office Word from 2007 on upwards. Please show the exact code you are using and the exact result in your `WPS writer` as a screen shot. – Axel Richter Mar 11 '18 at 11:30
  • @AxelRichter i used the same code provided in the link. except commented (mergeCellVertically(table, 0, 0, 1); ).This is the ouptui I got in WPS writer:[PIC] https://prnt.sc/ipn0ic – Joel George Mar 11 '18 at 11:34
  • @AxelRichter the same output without any data – Joel George Mar 11 '18 at 11:52
  • I have updated the code in my linked answer by creating table grid and deleting the content of the merged cells to support `Libreoffice Writer` too. Maybe this also will already support `WPS Writer`? Can you please try again? – Axel Richter Mar 11 '18 at 12:56
  • @AxelRichter Still horizontal merge is not happening. This was the output I got. [Picture](https://prnt.sc/ipoaai) – Joel George Mar 11 '18 at 13:46
  • Well then `WPS Writer` must be the problem. Later I will try installing this software to detect where the problem comes from. Now it is not possible since currenty my Windows 10 virtual machine runs Windows 10 Update Assistant and this seems taking for ever ;-). – Axel Richter Mar 11 '18 at 13:55
  • @AxelRichter Thanks Axel. Please let me know if you find a solution. – Joel George Mar 11 '18 at 14:26

1 Answers1

6

There are two methods setting horizontally merging. The first is using CTHMerge which is similar to the vertically merging using CTVMerge and it does not explicitly need a table grid. The second is using grid span properties. This method needs a table grid and the cells which are merged with the first one must be removed.

Microsoft Word supports all methods.

Libreoffice Writer supports CTHMerge too but a table grid must be set because of the correct rendering the table.

WPS Writer supports only setting grid span.

So this should be the most compatible solution:

import java.io.File;
import java.io.FileOutputStream;

import java.math.BigInteger;

import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;

import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;

public class CreateWordTableMerge {

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

 //merging horizontally by setting grid span instead of using CTHMerge
 static void mergeCellHorizontally(XWPFTable table, int row, int fromCol, int toCol) {
  XWPFTableCell cell = table.getRow(row).getCell(fromCol);
  // Try getting the TcPr. Not simply setting an new one every time.
  CTTcPr tcPr = cell.getCTTc().getTcPr();
  if (tcPr == null) tcPr = cell.getCTTc().addNewTcPr();
  // The first merged cell has grid span property set
  if (tcPr.isSetGridSpan()) {
   tcPr.getGridSpan().setVal(BigInteger.valueOf(toCol-fromCol+1));
  } else {
   tcPr.addNewGridSpan().setVal(BigInteger.valueOf(toCol-fromCol+1));
  }
  // Cells which join (merge) the first one, must be removed
  for(int colIndex = toCol; colIndex > fromCol; colIndex--) {
   table.getRow(row).removeCell(colIndex); // use only this for apache poi versions greater than 3
   //table.getRow(row).getCtRow().removeTc(colIndex); // use this for apache poi versions up to 3
   //table.getRow(row).removeCell(colIndex);
  }
 }

 public static void main(String[] args) throws Exception {

  XWPFDocument document= new XWPFDocument();

  XWPFParagraph paragraph = document.createParagraph();
  XWPFRun run=paragraph.createRun();  
  run.setText("The table:");

  //create table
  XWPFTable table = document.createTable(3,5);

  for (int row = 0; row < 3; row++) {
   for (int col = 0; col < 5; col++) {
    table.getRow(row).getCell(col).setText("row " + row + ", col " + col);
   }
  }

  //create CTTblGrid for this table with widths of the 5 columns. 
  //necessary for Libreoffice/Openoffice to accept the column widths.
  //values are in unit twentieths of a point (1/1440 of an inch)
  //first column = 1 inches width
  table.getCTTbl().addNewTblGrid().addNewGridCol().setW(BigInteger.valueOf(1*1440));
  //other columns (2 in this case) also each 1 inches width
  for (int col = 1 ; col < 5; col++) {
   table.getCTTbl().getTblGrid().addNewGridCol().setW(BigInteger.valueOf(1*1440));
  }

  //create and set column widths for all columns in all rows
  //most examples don't set the type of the CTTblWidth but this
  //is necessary for working in all office versions
  for (int col = 0; col < 5; col++) {
   CTTblWidth tblWidth = CTTblWidth.Factory.newInstance();
   tblWidth.setW(BigInteger.valueOf(1*1440));
   tblWidth.setType(STTblWidth.DXA);
   for (int row = 0; row < 3; row++) {
    CTTcPr tcPr = table.getRow(row).getCell(col).getCTTc().getTcPr();
    if (tcPr != null) {
     tcPr.setTcW(tblWidth);
    } else {
     tcPr = CTTcPr.Factory.newInstance();
     tcPr.setTcW(tblWidth);
     table.getRow(row).getCell(col).getCTTc().setTcPr(tcPr);
    }
   }
  }

  //using the merge methods
  mergeCellVertically(table, 0, 0, 1); 
  mergeCellHorizontally(table, 1, 2, 3); 
  mergeCellHorizontally(table, 2, 1, 4); 

  paragraph = document.createParagraph();

  FileOutputStream out = new FileOutputStream("create_table.docx"); 
  document.write(out);
  out.close();

  System.out.println("create_table.docx written successully");
 }
}
Axel Richter
  • 56,077
  • 6
  • 60
  • 87
  • awesome - Thank you Richter – mindSet Sep 18 '18 at 13:36
  • At the place where the merged cells are removed, I had to add ```table.getRow(row).removeCell(currentColumnIndex);```. Otherwise it looked like some data structures got out of sync, leading to strange exceptions when accessing CTTcPr later again. – Matthias Feb 18 '19 at 21:00
  • I have the same issue here, when I am getting rid of the "merged" cells I am facing with XmlValueDisconnectedException. Could you please share your solution for this? – csufsag Mar 07 '20 at 18:29