mkproject
mkproject

Reputation: 21

Apache POI 5.0 create XSLFTable

I try to create a simple table for a slide.

I'm using the following code :

        XSLFTable tbl = slide.createTable();
        tbl.setAnchor(anchor);
        
        XSLFTableRow row1 = tbl.addRow();
        row1.addCell().setText("Cell 1.1");
        XSLFTableCell cell = row1.addCell();
        cell.setText("Cell 1.2");
            
        XSLFTableRow row2 = tbl.addRow();
        row2.addCell().setText("Cell 2.1");
        cell = row2.addCell();
        cell.setText("Cell 2.2");

But the result is very strange - see image.

enter image description here

What I'm doing wrong or is it a bug?

Thanks for your help!

Upvotes: 1

Views: 1252

Answers (1)

Axel Richter
Axel Richter

Reputation: 61945

Apache poi had and has multiple problems when creating Word like tables. And PowerPoint tables also are Word like tables. The problems result from some weird decisions in methods to create table, rows and cells. And those decisions changes form version to version. So you always needs to check new after updating from version to version.

In this case, XSLFTable.addRow takes over all existent cells from row above. So if you have row one already having two cells and now are creating row two, then this row two also has two cells already. If you then add new cells to row two, then row two has more than two cells. And this results in that weird rendering result.

So, currently (apache poi 5.0.0), you need adding new cells only to first row. After adding second row, that row has the cells already and you only needs setting values to them:

 XSLFTable tbl;
 XSLFTableRow row;
 XSLFTableCell cell;
  
 tbl = slide.createTable();
 tbl.setAnchor(anchor);
  
 row = tbl.addRow();
 cell = row.addCell();
 cell.setText("Cell 1.1");
 cell = row.addCell();
 cell.setText("Cell 1.2");
  
 row = tbl.addRow();
 cell = tbl.getCell(1, 0);
 cell.setText("Cell 2.1");
 cell = tbl.getCell(1, 1);
 cell.setText("Cell 2.2");

Of course this is not very convenient to set values to tables in multiple rows and cells. There is XSLFSheet.createTable(int numRows, int numCols) which can create a table having all needed cells already, so we only need set cell values then. But this makes the same error as you had done and so also leads to a incorrect table. It also adds cells to new created rows although those rows had taken the cells from row above already while creating. Seems one developer had not known decisions of another.

So we need a bug fix for XSLFSheet.createTable(int numRows, int numCols).

Following complete example shows all the explained above:

import java.io.FileOutputStream;

import org.apache.poi.xslf.usermodel.*;
import org.apache.poi.sl.usermodel.TableCell.BorderEdge;

import java.awt.Rectangle;
import java.awt.Point;
import java.awt.Color;

public class CreatePPTXTable {
    
 //bug fix to https://svn.apache.org/viewvc/poi/tags/REL_5_0_0/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java?view=markup#l289
 //creates new cells in all new rows and so damages the table
 static XSLFTable createTable(XSLFSheet sheet, int numRows, int numCols) throws Exception {
  if (numRows < 1 || numCols < 1) {
   throw new IllegalArgumentException("numRows and numCols must be greater than 0");
  }
  XSLFTable sh = sheet.createTable();
  for (int r = 0; r < numRows; r++) {
   XSLFTableRow row = sh.addRow(); // this takes over all cells from present rows
   if (r == 0) { // so add new cells only in first row
    for (int c = 0; c < numCols; c++) {
     row.addCell();
    }
   }
  }
  return sh;
 }
    
 static void setAllCellBorders(XSLFTableCell cell, Color color) {
  cell.setBorderColor(BorderEdge.top, color);
  cell.setBorderColor(BorderEdge.right, color);
  cell.setBorderColor(BorderEdge.bottom, color);
  cell.setBorderColor(BorderEdge.left, color);
 }

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

  XMLSlideShow ppt = new XMLSlideShow();

  XSLFSlide slide = ppt.createSlide();
  
  XSLFTable tbl;
  XSLFTableRow row;
  XSLFTableCell cell;
  
  tbl = slide.createTable();
  tbl.setAnchor(new Rectangle(new Point(100, 100)));
  
  row = tbl.addRow();
  cell = row.addCell(); setAllCellBorders(cell, Color.BLACK);
  cell.setText("Cell 1.1");
  cell = row.addCell(); setAllCellBorders(cell, Color.BLACK);
  cell.setText("Cell 1.2");
  
  row = tbl.addRow();
  cell = tbl.getCell(1, 0); setAllCellBorders(cell, Color.BLACK);
  cell.setText("Cell 2.1");
  cell = tbl.getCell(1, 1); setAllCellBorders(cell, Color.BLACK);
  cell.setText("Cell 2.2");
  
  
  String[][] data = {
   {"R1C1", "R1C2", "R1C3"},
   {"R2C1", "R2C2", "R2C3"},
   {"R3C1", "R3C2", "R3C3"}
  };
  
  tbl = createTable(slide, data.length, data[0].length);
  tbl.setAnchor(new Rectangle(new Point(100, 300)));

  for (int r = 0; r < data.length; r++) {
   String[] dataRow = data[r];
   for (int c = 0; c < dataRow.length; c++) {
    String value = dataRow[c];
    cell = tbl.getCell(r, c);
    setAllCellBorders(cell, Color.BLACK);
    cell.setText(value);
   }
  }  

  FileOutputStream out = new FileOutputStream("fileName.pptx");
  ppt.write(out);
  out.close();
 }
}

Apache POI has fixed the bug described above. But not by fixing the XSLFSheet.createTable(int numRows, int numCols) nut by changing the XSLFTable.addRow. This now does not more take over all cells from present rows. So using current apache poi 5.2.2 one must new create all cells in added rows.

So the same code using apache poi 5.2.2 would must look like so:

import java.io.FileOutputStream;

import org.apache.poi.xslf.usermodel.*;
import org.apache.poi.sl.usermodel.TableCell.BorderEdge;

import java.awt.Rectangle;
import java.awt.Point;
import java.awt.Color;

public class CreatePPTXTable {
    
 static void setAllCellBorders(XSLFTableCell cell, Color color) {
  cell.setBorderColor(BorderEdge.top, color);
  cell.setBorderColor(BorderEdge.right, color);
  cell.setBorderColor(BorderEdge.bottom, color);
  cell.setBorderColor(BorderEdge.left, color);
 }

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

  XMLSlideShow ppt = new XMLSlideShow();

  XSLFSlide slide = ppt.createSlide();
  
  XSLFTable tbl;
  XSLFTableRow row;
  XSLFTableCell cell;
  
  tbl = slide.createTable();
  tbl.setAnchor(new Rectangle(new Point(100, 100)));
  
  row = tbl.addRow();
  cell = row.addCell(); setAllCellBorders(cell, Color.BLACK);
  cell.setText("Cell 1.1");
  cell = row.addCell(); setAllCellBorders(cell, Color.BLACK);
  cell.setText("Cell 1.2");
  
  row = tbl.addRow();
  cell = row.addCell(); setAllCellBorders(cell, Color.BLACK);
  cell.setText("Cell 2.1");
  cell = row.addCell(); setAllCellBorders(cell, Color.BLACK);
  cell.setText("Cell 2.2");
  
  
  String[][] data = {
   {"R1C1", "R1C2", "R1C3"},
   {"R2C1", "R2C2", "R2C3"},
   {"R3C1", "R3C2", "R3C3"}
  };
  
  tbl = slide.createTable(data.length, data[0].length);
  tbl.setAnchor(new Rectangle(new Point(100, 300)));

  for (int r = 0; r < data.length; r++) {
   String[] dataRow = data[r];
   for (int c = 0; c < dataRow.length; c++) {
    String value = dataRow[c];
    cell = tbl.getCell(r, c);
    setAllCellBorders(cell, Color.BLACK);
    cell.setText(value);
   }
  }  

  FileOutputStream out = new FileOutputStream("fileName.pptx");
  ppt.write(out);
  out.close();
 }
}

Upvotes: 2

Related Questions