Can you make a table in the compartment inside the table like case 2?
Can you make a table in the compartment inside the table like case 2?
enter image description here
public class PoiTest3 {
public static void main(String[] args) throws Exception {
try (XWPFDocument doc = new XWPFDocument()) {
XWPFTable table = doc.createTable();
//Creating first Row
XWPFTableRow row1 = table.getRow(0);
row1.getCell(0).setText("First Row, First Column");
row1.addNewTableCell().setText("First Row, Second Column");
row1.addNewTableCell().setText("First Row, Third Column");
//Creating second Row
XWPFTableRow row2 = table.createRow();
row2.getCell(0).setText("Second Row, First Column");
row2.getCell(1).setText("Second Row, Second Column");
row2.getCell(2).setText("Second Row, Third Column");
//create third row
XWPFTableRow row3 = table.createRow();
row3.getCell(0).setText("Third Row, First Column");
row3.getCell(1).setText("Third Row, Second Column");
row3.getCell(2).setText("Third Row, Third Column");
// save to .docx file
try (FileOutputStream out = new FileOutputStream("c:\\excel\\table.docx")) {
doc.write(out);
}
}
}
}
XWPFTableCell is a IBody. So it provides XWPFTable insertNewTbl(org.apache.xmlbeans.XmlCursor cursor). So yes, it is possible to add a table into a table cell.
But the usage of that org.apache.xmlbeans.XmlCursor is not well documented.
To create that cursor we need the table cell, the cursor shall be in, and an new empty paragrsph in that cell. The empty paragrsph is needed because all content we insert using that cursor will be before the element the cursor points to. So that element should be an empty paragraph to avoid inserting content into existing elements like paragraphs with content or other content containing elements.
The following shows the simplest possible complete example.
import java.io.FileOutputStream;
import org.apache.poi.xwpf.usermodel.*;
public class CreateWordTableInTable {
public static void main(String[] args) throws Exception {
try (XWPFDocument doc = new XWPFDocument()) {
//create main table
XWPFTable table = doc.createTable();
//create rows and cells
XWPFTableRow row = table.getRow(0);
row.getCell(0).setText("Main table A1");
row.addNewTableCell().setText("Main table B1");
row.addNewTableCell().setText("Main table C1");
row = table.createRow();
row.getCell(0).setText("Main table A2");
row.getCell(1).setText("Main table B2");
row.getCell(2).setText("Main table C2");
//create inner table
//we need the first table cell and an new empty paragrsph in that first cell
row = table.getRow(0);
XWPFTableCell cell = row.getTableCells().get(0);
XWPFParagraph paragraph = cell.addParagraph();
//now we can insert a table there
org.apache.xmlbeans.XmlCursor cursor = paragraph.getCTP().newCursor();
XWPFTable innerTable = cell.insertNewTbl(cursor);
//set table borders
innerTable.setTopBorder(XWPFTable.XWPFBorderType.SINGLE, 4, 0, "000000");
innerTable.setRightBorder(XWPFTable.XWPFBorderType.SINGLE, 4, 0, "000000");
innerTable.setBottomBorder(XWPFTable.XWPFBorderType.SINGLE, 4, 0, "000000");
innerTable.setLeftBorder(XWPFTable.XWPFBorderType.SINGLE, 4, 0, "000000");
innerTable.setInsideHBorder(XWPFTable.XWPFBorderType.SINGLE, 4, 0, "000000");
innerTable.setInsideVBorder(XWPFTable.XWPFBorderType.SINGLE, 4, 0, "000000");
//create rows and cells
XWPFTableRow rowInInnerTable = innerTable.createRow();
XWPFTableCell cellInInnerTable = rowInInnerTable.createCell();
cellInInnerTable.setText("Inner table A1");
cellInInnerTable = rowInInnerTable.createCell();
cellInInnerTable.setText("Inner table B1");
cellInInnerTable = rowInInnerTable.createCell();
cellInInnerTable.setText("Inner table C1");
rowInInnerTable = innerTable.createRow();
cellInInnerTable = rowInInnerTable.getCell(0);
cellInInnerTable.setText("Inner table A2");
cellInInnerTable = rowInInnerTable.getCell(1);
cellInInnerTable.setText("Inner table B2");
cellInInnerTable = rowInInnerTable.getCell(2);
cellInInnerTable.setText("Inner table C2");
//save to .docx file
try (FileOutputStream out = new FileOutputStream("./CreateWordTableInTable.docx")) {
doc.write(out);
}
}
}
}
It produces:
This code is tested an works using the current Apache POI version 5.2.3. Download: https://poi.apache.org/download.html#POI-5.2.3. Needed components see https://poi.apache.org/components/index.html#components.
Related
I'm working on an application that fetches database records and creates an excel doc from that data.
The excel doc is generated fine and all the data is readable; as of a previous answer from this forum the table is also appropriately generated (the header row remains visible even when I've scrolled past it, so the table is definitely present). However, I had expected that once I had a table I'd be able to sort and filter the columns as is the case when you 'Insert -> Table' in excel, but there are no such options when I open the doc.
I don't see a setFitlerable or setSortable or anything like that on the XSSFTable or XSSFTableColumn classes... How do I enable sorting/filtering on the table columns?
Table creation code follows, if it's useful:
//Create table
CellReference topLeft = new CellReference(sheet.getRow(3).getCell(0));
CellReference bottomRight = new CellReference(sheet.getRow(nextRow-1).getCell(3));
AreaReference tableArea = workbook.getCreationHelper().createAreaReference(topLeft, bottomRight);
XSSFTable dataTable = sheet.createTable(tableArea);
dataTable.setName("TableData" + EXCEL_OBJECT_NUMBER);
dataTable.setDisplayName("TableData" + EXCEL_OBJECT_NUMBER);
XSSFTableColumn column = dataTable.getColumns().get(0);
column.setId(1);
column.setName("COLUMN1");
column = dataTable.getColumns().get(1);
column.setId(2);
column.setName("COLUMN2");
column = dataTable.getColumns().get(2);
column.setId(3);
column.setName("COLUMN3");
column = dataTable.getColumns().get(3);
column.setId(4);
column.setName("COLUMN4");
If dataTable is a XSSFTable and tableArea is the AreaReference of that XSSFTable, then the following code sets the auto filters into the table headers as Excel also does it:
dataTable.getCTTable().addNewAutoFilter().setRef(tableArea.formatAsString());
Complete example:
import java.io.FileOutputStream;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellReference;
import java.util.GregorianCalendar;
class CreateExcelTable {
public static void main(String[] args) throws Exception {
Object[][] data = new Object[][] {
new Object[] {"Text", "Date", "Number", "Boolean"},
new Object[] {"Text 1", new GregorianCalendar(2020, 0, 1), 1234d, true},
new Object[] {"Text 2", new GregorianCalendar(2020, 1, 15), 5678d, true},
new Object[] {"Text 3", new GregorianCalendar(2020, 2, 1), 90.1234, false},
new Object[] {"Text 4", new GregorianCalendar(2020, 3, 15), 567.89, false}
};
try (XSSFWorkbook workbook = new XSSFWorkbook();
FileOutputStream fileout = new FileOutputStream("Excel.xlsx") ) {
XSSFCellStyle dateCellStyle = workbook.createCellStyle();
dateCellStyle.setDataFormat(14);
XSSFSheet sheet = workbook.createSheet();
XSSFRow row = sheet.createRow(0);
XSSFCell cell = row.createCell(0);
cell.setCellValue("Lorem ipsum");
row = sheet.createRow(1);
cell = row.createCell(0);
cell.setCellValue("semit dolor");
int nextRow = 3;
int nextCol = 0;
for (Object[] dataRow : data) {
row = sheet.createRow(nextRow++);
nextCol = 0;
for (Object value : dataRow) {
cell = row.createCell(nextCol++);
if (value instanceof String) cell.setCellValue((String)value);
else if (value instanceof GregorianCalendar) {
cell.setCellValue((GregorianCalendar)value);
cell.setCellStyle(dateCellStyle);
}
else if (value instanceof Double) cell.setCellValue((Double)value);
else if (value instanceof Boolean) cell.setCellValue((Boolean)value);
}
}
CellReference topLeft = new CellReference(sheet.getRow(3).getCell(0));
CellReference bottomRight = new CellReference(sheet.getRow(nextRow-1).getCell(3));
AreaReference tableArea = workbook.getCreationHelper().createAreaReference(topLeft, bottomRight);
XSSFTable dataTable = sheet.createTable(tableArea);
//dataTable.setName("Table1");
dataTable.setDisplayName("Table1");
//this styles the table as Excel would do per default
dataTable.getCTTable().addNewTableStyleInfo();
XSSFTableStyleInfo style = (XSSFTableStyleInfo)dataTable.getStyle();
style.setName("TableStyleMedium2");
style.setShowColumnStripes(false);
style.setShowRowStripes(true);
//this sets auto filters
dataTable.getCTTable().addNewAutoFilter().setRef(tableArea.formatAsString());
workbook.write(fileout);
}
}
}
I have a table in the docx template.
Depending on the number of objects, I have to duplicate the table as many times as I have objects. Duplicate tables must be after the table from the template.
I have several tables in the template that should behave like this.
XmlCursor take the place of the first table from the template and put the next one there. I want to insert the next table after the previous one, which I added myself, but xmlcursor does not return the table item I added, but returns "STARTDOC"
XmlCursor cursor = docx.getTables().get(pointer).getCTTbl().newCursor();
cursor.toEndToken();
while (cursor.toNextToken() != XmlCursor.TokenType.START) ;
XWPFParagraph newParagraph = docx.insertNewParagraph(cursor);
newParagraph.createRun().setText("", 0);
cursor.toParent();
cursor.toEndToken();
while (cursor.toNextToken() != XmlCursor.TokenType.START) ;
docx.insertNewTbl(cursor);
CTTbl ctTbl = CTTbl.Factory.newInstance();
ctTbl.set(docx.getTables().get(numberTableFromTemplate).getCTTbl());
XWPFTable tableCopy = new XWPFTable(ctTbl, docx);
docx.setTable(index + 1, tableCopy);
Not clear what you are aiming for with the cursor.toParent();. And I also cannot reproduce the issue having only your small code snippet. But having a complete working example may possible help you.
Assuming we have following template:
Then following code:
import java.io.FileOutputStream;
import java.io.FileInputStream;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlCursor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
public class WordCopyTableAfterTable {
static XmlCursor setCursorToNextStartToken(XmlObject object) {
XmlCursor cursor = object.newCursor();
cursor.toEndToken(); //Now we are at end of the XmlObject.
//There always must be a next start token.
while(cursor.hasNextToken() && cursor.toNextToken() != org.apache.xmlbeans.XmlCursor.TokenType.START);
//Now we are at the next start token and can insert new things here.
return cursor;
}
static void removeCellValues(XWPFTableCell cell) {
for (XWPFParagraph paragraph : cell.getParagraphs()) {
for (int i = paragraph.getRuns().size()-1; i >= 0; i--) {
paragraph.removeRun(i);
}
}
}
public static void main(String[] args) throws Exception {
//The data. Each row a new table.
String[][] data= new String[][] {
new String[] {"John Doe", "5/23/2019", "1234.56"},
new String[] {"Jane Doe", "12/2/2019", "34.56"},
new String[] {"Marie Template", "9/20/2019", "4.56"},
new String[] {"Hans Template", "10/2/2019", "4567.89"}
};
String value;
XWPFDocument document = new XWPFDocument(new FileInputStream("WordTemplate.docx"));
XWPFTable tableTemplate;
CTTbl cTTblTemplate;
XWPFTable tableCopy;
XWPFTable table;
XWPFTableRow row;
XWPFTableCell cell;
XmlCursor cursor;
XWPFParagraph paragraph;
XWPFRun run;
//get first table (the template)
tableTemplate = document.getTableArray(0);
cTTblTemplate = tableTemplate.getCTTbl();
cursor = setCursorToNextStartToken(cTTblTemplate);
//fill in first data in first table (the template)
for (int c = 0; c < data[0].length; c++) {
value = data[0][c];
row = tableTemplate.getRow(1);
cell = row.getCell(c);
removeCellValues(cell);
cell.setText(value);
}
paragraph = document.insertNewParagraph(cursor); //insert new empty paragraph
cursor = setCursorToNextStartToken(paragraph.getCTP());
//fill in next data, each data row in one table
for (int t = 1; t < data.length; t++) {
table = document.insertNewTbl(cursor); //insert new empty table at position t
cursor = setCursorToNextStartToken(table.getCTTbl());
tableCopy = new XWPFTable((CTTbl)cTTblTemplate.copy(), document); //copy the template table
//fill in data in tableCopy
for (int c = 0; c < data[t].length; c++) {
value = data[t][c];
row = tableCopy.getRow(1);
cell = row.getCell(c);
removeCellValues(cell);
cell.setText(value);
}
document.setTable(t, tableCopy); //set tableCopy at position t instead of table
paragraph = document.insertNewParagraph(cursor); //insert new empty paragraph
cursor = setCursorToNextStartToken(paragraph.getCTP());
}
paragraph = document.insertNewParagraph(cursor);
run = paragraph.createRun();
run.setText("Inserted new text below last table.");
cursor = setCursorToNextStartToken(paragraph.getCTP());
FileOutputStream out = new FileOutputStream("WordResult.docx");
document.write(out);
out.close();
document.close();
}
}
leads to following result:
Is that about what you wanted to achieve?
Please note how I insert the additional tables.
Using table = document.insertNewTbl(cursor); a new empty table is inserted at position t. This table is placed into the document body. So this table must be taken for adjusting the cursor.
Then tableCopy = new XWPFTable((CTTbl)cTTblTemplate.copy(), document); copys the template table. Then this copy is filled with data. And then it is set into the document at position t using document.setTable(t, tableCopy);.
Unfortunately apache poi is incomplete here. XWPFDocument.setTable only sets the internally ArrayLists but not the underlying XML. XWPFDocument.insertNewTbl sets the underlying XML but only using an empty table. So we must do it that ugly complicated way.
I create a Table using apache poi like this:
XWPFDocument document= new XWPFDocument();
//Write the Document in file system
FileOutputStream out = new FileOutputStream(new File("create_table.docx"));
//create table
XWPFTable table = document.createTable();
//create first row
XWPFTableRow tableRowOne = table.getRow(0);
tableRowOne.getCell(0).setText("col one, row one");
tableRowOne.addNewTableCell().setText("col two, row one");
tableRowOne.addNewTableCell().setText("col three, row one");
//create second row
XWPFTableRow tableRowTwo = table.createRow();
tableRowTwo.getCell(0).setText("col one, row two");
tableRowTwo.getCell(1).setText("col two, row two");
tableRowTwo.getCell(2).setText("col three, row two");
//create third row
XWPFTableRow tableRowThree = table.createRow();
tableRowThree.getCell(0).setText("col one, row three");
tableRowThree.getCell(1).setText("col two, row three");
tableRowThree.getCell(2).setText("col three, row three");
Now I would like get table format and data in a String variable like:
<w:style w:type="table"...>
<w:rPr><w:t> data </w:t> </w:rPr>
Can I do this?
If the Office OpenXML of the XWPFTable is meant, then:
XWPFTable.getCTTbl() returns a CTTbl and this extends XmlObject which provides a XmlObject.toString() which returns an XML string for this XML object.
So
String tableXML = table.getCTTbl().toString();
I have Java code to create a table and some texts to a Word document using Apache POI, but it adds table in last document. I want to write some text, then add table and write some text again.
Currently it adds table first and last document add 2 texts (Hi & Bye)
My code :
public static void main(String[] args)throws Exception {
//Blank Document
XWPFDocument document= new XWPFDocument();
//Write the Document in file system
FileOutputStream out = new FileOutputStream(
new File("create_table.docx"));
//create table
XWPFTable table = document.createTable();
XWPFParagraph para = document.createParagraph();
XWPFRun run = para.createRun();
run.setText("Hi");
//create first row
XWPFTableRow tableRowOne = table.getRow(0);
tableRowOne.getCell(0).setText("col one, row one");
tableRowOne.addNewTableCell().setText("col two, row one");
tableRowOne.addNewTableCell().setText("col three, row one");
//create second row
XWPFTableRow tableRowTwo = table.createRow();
tableRowTwo.getCell(0).setText("col one, row two");
tableRowTwo.getCell(1).setText("col two, row two");
tableRowTwo.getCell(2).setText("col three, row two");
//create third row
XWPFTableRow tableRowThree = table.createRow();
tableRowThree.getCell(0).setText("col one, row three");
tableRowThree.getCell(1).setText("col two, row three");
tableRowThree.getCell(2).setText("col three, row three");
run.setText("Bye");
document.write(out);
out.close();
System.out.println("create_table.docx written successully");
}
How can i print Hi first of page and add table and print Bye after table?
And how can i save document every time when i want add content to it and finally write it and open it ?
Thanks
You have to keep the right order and more importantly : You have to create a new paragraph.
This is the code, you'll need :
public static void main(String[] args) throws IOException {
//Blank Document
XWPFDocument document = new XWPFDocument();
//Write the Document in file system
FileOutputStream out = new FileOutputStream(new File("create_table.docx"));
//Write first Text in the beginning
XWPFParagraph para = document.createParagraph();
XWPFRun run = para.createRun();
run.setText("Hi");
//create table
XWPFTable table = document.createTable();
//create first row
XWPFTableRow tableRowOne = table.getRow(0);
tableRowOne.getCell(0).setText("col one, row one");
tableRowOne.addNewTableCell().setText("col two, row one");
tableRowOne.addNewTableCell().setText("col three, row one");
//create second row
XWPFTableRow tableRowTwo = table.createRow();
tableRowTwo.getCell(0).setText("col one, row two");
tableRowTwo.getCell(1).setText("col two, row two");
tableRowTwo.getCell(2).setText("col three, row two");
//create third row
XWPFTableRow tableRowThree = table.createRow();
tableRowThree.getCell(0).setText("col one, row three");
tableRowThree.getCell(1).setText("col two, row three");
tableRowThree.getCell(2).setText("col three, row three");
//Write second Text after the table (by creating a new paragraph)
XWPFParagraph para2 = document.createParagraph();
XWPFRun run2 = para2.createRun();
run2.setText("Bye");
document.write(out);
out.close();
System.out.println("create_table.docx written successully");
}
This is the output, you'll get:
I'm using itext-rtf 2.1.7 to generate an RTF document.
The parameter table below for the method writeSectionHeaderTableInACell() will have two columns.
For each column, I need to insert a new inner Table, with two columns as well. This inner table will have the first column as left-aligned, and the second as right-aligned.
However, the code below causes a corrupted Table in the generated RTF document. The two inner Tables which supposed to be both on a single row, they appear as one row for each inner Table.
Anyone, has any idea why it seems we cannot add an inner Table within a Cell for RTF document? Thanks.
private static void writeSectionHeaderTableInACell(PdfPTable table) {
PdfPTable sectionTable1 = new PdfPTable(2);
Phrase phrase1 = new Phrase("? 1 ?", FontFactory.getFont("MS Mincho", 10, Font.NORMAL));
Paragraph p1 = new Paragraph(phrase1);
p1.setAlignment(Element.ALIGN_LEFT);
PdfPCell cell1 = new PdfPCell(p1);
sectionTable1.addCell(cell1);
Phrase phrase2 = new Phrase("2 Chi", FontFactory.getFont("MS Mincho", 10, Font.NORMAL));
Paragraph p2 = new Paragraph(phrase2);
p2.setAlignment(Element.ALIGN_RIGHT);
PdfPCell cell2 = new PdfPCell(p2);
sectionTable1.addCell(cell2);
table.addCell(new PdfPCell(sectionTable1));
PdfPTable sectionTable2 = new PdfPTable(2);
Phrase phrase3 = new Phrase("Section 1", FontFactory.getFont("Times New Roman", 10, Font.NORMAL));
Paragraph p3 = new Paragraph(phrase3);
p3.setAlignment(Element.ALIGN_LEFT);
PdfPCell cell3 = new PdfPCell(p3);
sectionTable2.addCell(cell3);
Phrase phrase4 = new Phrase("2 Eng", FontFactory.getFont("Times New Roman", 10, Font.NORMAL));
Paragraph p4 = new Paragraph(phrase4);
p4.setAlignment(Element.ALIGN_RIGHT);
PdfPCell cell4 = new PdfPCell(p4);
sectionTable2.addCell(cell4);
table.addCell(new PdfPCell(sectionTable2));
}