I'm using iTextPdf 5.4.1.
I have a table with many rows in it, and when the table auto-splits to the next page, I want to insert an Image and then another page break to continue the table's rows.
For example: say a table will take up 2 pages based on the number of rows it has.
The final output should be:
page 1: table rows that fit on first page
page 2: image
page 3: remaining table rows.
So, each time the table splits, i want to insert an image and then a page break.
I'm trying to use the PdfPTableEventSplit interface, as follows:
public class TableSplitEvent implements PdfPTableEventSplit
{
private Image pageImage;
private Document pdfDoc;
public TableSplitEvent( Image pageImage, Document pdfDoc )
{
super();
this.pageImage = pageImage;
this.pdfDoc = pdfDoc;
}
#Override
public void splitTable( PdfPTable table )
{
try
{
pdfDoc.add( pageImage );
pdfDoc.newPage();
}
catch ( DocumentException e )
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void tableLayout( PdfPTable table, float[][] widths, float[] heights, int headerRows, int rowStart,
PdfContentByte[] canvases )
{
}
}
However, that doesn't seem to work as the event seems to get called after the table is rendered and before it is written to the pdf.
I'm getting:
page 1: image
page 2: table rows
page 3: table rows
Does anyone know how to do this?
If I'm not mistaken, we've received a code contribution for an event called PdfPTableEventAfterSplit. It will be integrated into iText in a couple of weeks and released by the end of July. Before that date, iText can't meet your needs. Thank you for your patience.
Related
I am trying to populate a table within a docx file with data from java objects. More precisely each row represents an Object and my pattern starts with one row. I want to find out how can I introduce a new row in case I have more than one objects in my list. See example below:
Docx table looks like this:
And I successfully realized the mapping with the fields but for ONLY one object. How can i introduce another row (from Java) to make room for another object ? For this implementation I am using org.apache.poi.xwpf.usermodel.XWPFDocument;
public class DocMagic {
public static XWPFDocument replaceTextFor(XWPFDocument doc, String findText, String replaceText) {
replaceTextFor(doc.getParagraphs(),findText,replaceText);
doc.getTables().forEach(p -> {
p.getRows().forEach(row -> {
row.getTableCells().forEach(cell -> {
replaceTextFor(cell.getParagraphs(), findText, replaceText);
});
});
});
return doc;
}
private static void replaceTextFor(List<XWPFParagraph> paragraphs, String findText, String replaceText) {
paragraphs.forEach(p -> {
p.getRuns().forEach(run -> {
String text = run.text();
if (text.contains(findText)) {
run.setText(text.replace(findText, replaceText), 0);
}
});
});
}
public static void saveWord(String filePath, XWPFDocument doc) throws FileNotFoundException, IOException {
FileOutputStream out = null;
try {
out = new FileOutputStream(filePath);
doc.write(out);
} catch (Exception e) {
e.printStackTrace();
} finally {
out.close();
}
}
}
EDIT: using addNewTableCell().setText() places the values on the right side of the table
Normally you use below steps to add row in a table,
XWPFTableRow row =tbl.createRow();
row.addNewTableCell().setText("whatever you want");
tbl.addRow(row, y);
But in your case seems you want to add rows on the fly while you are iterating the docx table together with your Java list of object,
In Java your are not safe or able to change the collection while looping it,
so you might need to do it in 2 steps,
you need to expand/add rows first to the docx table before you populate it,
by firstly calculate how many objects you have in your java list.
when the table rows are already added accordingly, you could iterate and populate them
I have an activity where I create Tablerow programatically, as I don't know how many files I need.
Each TableRow has an ID starting by 100 and continue 200, 300 and so on.
When I create the row 3 the TableRow's ID is 300.
Then when someone edit other row I need to know what row is just to work with the elements of the row.
My problem: previous value:
trActiva.getId()=300
Then this is my code I put:
lineaActiva = (tv.getId()) / 100 * 100;
trActiva = findViewById(lineaActiva);
just on the point before these two lines trActiva.getId()=300
after the first row of the code lineaActiva = 200
That means after the second line trActiva.getId() should be 200 but it is still 300.
More weird things:
after these two lines:
trActiva.getId()=300
findViewById(lineaActiva).getId()=200
What is happening here? Why trActiva is not changing?
Thank you!!
We are unsure how you are increasing your Id and how you are storing your data as well how do you view 300 rows on a computer screen
So a lot of code is missing to give a very pointed answer to your question
Here is a table we populate from a Derby Database
The table is named ptable and we might also mention we are using a Model View Controller pattern to manage the data to GET and SET variables
First we read from the Database and populate the ptable with this code
private void ReadFromParent() throws SQLException{
ObservableList<ParentModel> TableData = FXCollections.observableArrayList();
stmnt = conn.createStatement();
ResultSet rs = stmnt.executeQuery("SELECT * FROM Parent");
while (rs.next()){// Add data to observableArrayList TableData to select a table ROW
TableData.add(new ParentModel(rs.getString("PID"),rs.getString("pMonth"),rs.getString("pYear")));
}
PropertyValueFactory<ParentModel, String> IDCellValueFactory = new PropertyValueFactory<>("PID");
colID.setCellValueFactory(IDCellValueFactory);
PropertyValueFactory<ParentModel, String> DateCellValueFactory = new PropertyValueFactory<>("pMonth");
colMonth.setCellValueFactory(DateCellValueFactory);
PropertyValueFactory<ParentModel, String> TypeCellValueFactory = new PropertyValueFactory<>("pYear");
colYear.setCellValueFactory(TypeCellValueFactory);
// ================================================================
// colTxDate are the ID's for the tableview columns
// sortDate are the Column Names for the Database TABLE CDBalance
// ================================================================
colMonth.setStyle("-fx-alignment: CENTER;");
colYear.setStyle("-fx-alignment:CENTER;");
Collections.sort(TableData, (p2, p1)-> p1.getPID().compareToIgnoreCase(p2.getPID()));
// Line of Code above Sorts Database Columns based on VARIABLE in ParentModel
// Change p2,p1 to sort Ascending or Descending
if(TableData.size() < 15) {// Format TableView to display Vertical ScrollBar
ptable.setPrefWidth(336);// 19 is the off set
}else {
ptable.setPrefWidth(355);
} ptable.setItems(TableData);
stmnt.close();
}
Then we write listener code to permit us to view the details of the row selected
Notice we get the ID from the row selected
This is where you can use that value to find the new entry that is greater than 300
OR you can write a Search query on the table that finds all records that have an ID greater than 300
Here is the code that permits you to click on a row in the table
private void showTableDataDetails(ParentModel info) throws IOException{
if (info != null) {
info = (ParentModel) ptable.getSelectionModel().getSelectedItem();
strID = info.getPID();
strMONTH = info.getPMonth();
strYEAR = info.getPYear();
toChildView();
System.out.println("########### PID "+strID);
}
}
#Override
public void initialize(URL url, ResourceBundle rb) {
ptable.getSelectionModel().selectedItemProperty().addListener((ObservableValue<? extends ParentModel>
observable,ParentModel oldValue, ParentModel newValue) -> {
try {
showTableDataDetails((ParentModel) newValue); // When a row of the table is Selected call
// Proper Construction // showTableDataDetails method
} catch (IOException ex) {
Logger.getLogger(ParentTableViewController.class.getName()).log(Level.SEVERE, null, ex);
}
});
}
Hope this helps and Welcome to Stack Overflow
PLEASE post a little more code in the future so your question can have a more definitive answer
This is official iText solution for creating a table on the last page at the bottom in pdf document. This solution puts my table at the bottom of the last pdf page. Great.
Unfortunately, it causes getting my table more narrow too. And this is what I don't want. I have tried several hours to get that table wider again, but without success. I cannot resolve it. How to put table at the bottom in original size before moving? What is the best solution of this problem?
Picture of the problem
Before moving, width of my table was created only based on table.setWidthPercentage(100); Then it started to report exception that width of the table must be greater than zero.
table.setWidths(number of columns in my table);
I tried table.setTotalWidth() set on different value than zero and then overwrite it with that official code from iText. But without success. I am looking for some elegant solution of this.
The code:
public static void main(String[] args)
{
Document document = new Document(PageSize.A4);
PdfWriter writer = null;
try {
writer = PdfWriter.getInstance(document,
new FileOutputStream("C:/radek-folder/calendar.pdf"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
document.open();
PdfPTable datatable = createHeaderTable();
document.add(datatable);
datatable = createFooterTable();
drawTableAtTheEndOfPage(document, writer, datatable);
document.close();
System.out.println("done");
}
private static void drawTableAtTheEndOfPage(Document document, PdfWriter writer, PdfPTable datatable) {
datatable.setTotalWidth(document.right(document.rightMargin()) - document.left(document.leftMargin()));
datatable.writeSelectedRows(0, -1, document.left(document.leftMargin()),
datatable.getTotalHeight() + document.bottom(document.bottomMargin()),
writer.getDirectContent());
}
private static PdfPTable createFooterTable() throws DocumentException {
int[] columnWidths = new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 1 };
PdfPTable datatable = new PdfPTable(columnWidths.length);
datatable.setKeepTogether(true);
datatable.setWidthPercentage(100);
datatable.setWidths(columnWidths);
datatable.getDefaultCell().setPadding(5);
datatable.getDefaultCell().setHorizontalAlignment(horizontalAlignment);
datatable.getDefaultCell().setVerticalAlignment(verticalAlignment);
for (int i = 0; i < 100; i++) {
addCellToTable(datatable, horizontalAlignmentLeft,
verticalAlignmentMiddle, "Přehledová tabulka", columnWidths.length, 1,
fontTypeBold, fontSizeRegular, cellLayout_Bottom);
}
return datatable;
}
I inserted(document.leftMargin() - 50) for example, and it moved with table to the side. I tried various values, but i havent found the suitable ones. In three days at work i will try zero :-)
You should try zero now!
Indeed, that official sample and the answer here on stack overflow that sample is derived from are slightly wrong when they apply the margins here:
datatable.setTotalWidth(document.right(document.rightMargin()) -
document.left(document.leftMargin()));
datatable.writeSelectedRows(0, -1, document.left(document.leftMargin()),
datatable.getTotalHeight() + document.bottom(document.bottomMargin()),
writer.getDirectContent());
because the Document methods left, bottom, right, and top themselves already apply the matching margin, e.g.
public float left(float margin) {
return pageSize.getLeft(marginLeft + margin);
}
Thus, the recommended code effectively applies the margins twice!
So your method drawTableAtTheEndOfPage should simply look like this:
private static void drawTableAtTheEndOfPage(Document document, PdfWriter writer, PdfPTable datatable)
{
datatable.setTotalWidth(document.right() - document.left());
datatable.writeSelectedRows(0, -1, document.left(),
datatable.getTotalHeight() + document.bottom(), writer.getDirectContent());
}
I have recently tried to code a small java file which will insert a row into an already existing table in a .odt document. The table itself has 4 rows and 3 column, but I would like to implement a check which will expand that table if the content to be inserted is larger than 4. However, every time I try to get the table's rows, it returns a null pointer. I am not that familiar with UNO api, but as far as i read through the documentation, the class XColumnsAndRowRange should be used in this situation. My code is as follows:
XTextTablesSupplier xTablesSupplier = (XTextTablesSupplier) UnoRuntime.queryInterface(XTextTablesSupplier.class, xTextDocument);
XNameAccess xNamedTables = xTablesSupplier.getTextTables();
try {
Object table = xNamedTables.getByName(tblName);
XTextTable xTable = (XTextTable) UnoRuntime.queryInterface(XTextTable.class, table);
XCellRange xCellRange = (XCellRange) UnoRuntime.queryInterface(XCellRange.class, table);
if(flag){
XColumnRowRange xCollumnAndRowRange =(XColumnRowRange)
UnoRuntime.queryInterface(XColumnRowRange.class, xCellRange);
XTableRows rows = xCollumnAndRowRange.getRows();
System.out.println("Testing if this works");
rows.insertByIndex(4, size-4);
}
I am not sure if I am missing something here or if I should be using a different function.
As Lyrl suggested, this works:
XTableRows rows = xTable.getRows();
Apparently XColumnRowRange is only used for spreadsheets.
Note: With Basic or Python you would not have this problem, because those languages do not need queryInterface. The code would simply be:
table = tables.getByName(tblName)
rows = table.getRows()
I am working with apose words java recently.
In my first page I have a table need to merge, which can grow any size, no fixed number of rows and at the end of my first page, I want to keep some content (for example contact details) to be fixed. (Note: I can't keep contact details in Footer or in foot note section because of some formatting I need to ensure which can't maintain in footer or foot note section)
On growing of table as many rows, My content is going down, But I want to fix it at the end of my first page. if table grows bigger in size, wanted to skip the content and render table in next page.
is there any solution/work around for this?
My expected results are like below....
Page 1 Start
dynamic Table row1
dynamic Table row2
dynamic Table row3
Contact Details ,wanted to fix at the end of my first page
Page 1 end
Page 2 Start
dynamic table row 4
dynamic table row 5
........
For your scenario, ideally the contact details should be set in a footer. It is possible, but very risky.
First create a new document, either in Aspose.Words or MS Word, it will be used as a template.
Add a blank table on top
Add contact details, after the blank table
Add a bookmark, after the contact details
Now, using Aspose.Words, you can check the location of the bookmark, every time you are adding a new row in the table. If bookmark is at page 1, add new row to the first table. If bookmark is at page 2, add new row to the second table. Below is the sample code that adds rows to the table, keeping the contact details fixed on page 1.
Template document: Google drive link
Java source code is given below.
public static void main(String[] args)
{
try
{
String template = Common.DATA_DIR + "Contact Template.docx";
String saveDocument = Common.DATA_DIR + "Contact with tables.docx";
String bookmarkNameContact = "ContactEnd";
// Load the template
com.aspose.words.Document wordDoc = new com.aspose.words.Document(template);
DocumentBuilder builder = new DocumentBuilder(wordDoc);
// Find the contacts bookmark
com.aspose.words.Bookmark bookmarkContact = wordDoc.getRange().getBookmarks().get(bookmarkNameContact);
// Set the table with null
com.aspose.words.Table table = null;
// Add some rows
for (int i = 0; i < 50; i++)
{
// If contacts bookmark is on 1st page, add new rows to first table
if (getBookmarkPage(wordDoc, bookmarkContact) == 1)
{
table = (com.aspose.words.Table) wordDoc.getChild(NodeType.TABLE, 0, true);
} else
{
// If the contacts bookmark is on second page, add rows to second table
table = (com.aspose.words.Table) wordDoc.getChild(NodeType.TABLE, 1, true);
// If there is no second table, create it
if (table == null)
{
table = createNewTable(wordDoc, bookmarkContact);
}
}
// Add rows dynamically to either first or second table
addRow(wordDoc, table, "some text " + i);
}
// Save the document
wordDoc.save(saveDocument);
} catch (Exception ex)
{
System.err.println(ex.getMessage());
}
}
private static com.aspose.words.Table createNewTable(com.aspose.words.Document wordDoc, com.aspose.words.Bookmark bookmarkContact) throws Exception
{
// Get the first table and clone it to create the second one
com.aspose.words.Table firstTable = (com.aspose.words.Table) wordDoc.getChild(NodeType.TABLE, 0, true);
com.aspose.words.Table table = (com.aspose.words.Table) firstTable.deepClone(true);
// Add the second table after the bookmark
bookmarkContact.getBookmarkEnd().getParentNode().getParentNode().appendChild(table);
// Delete all its rows
table.getRows().clear();
return table;
}
// Add a new row to the table
private static void addRow(com.aspose.words.Document wordDoc, com.aspose.words.Table table, String text)
{
// Create a new row
com.aspose.words.Row row = new com.aspose.words.Row(wordDoc);
row.getRowFormat().setAllowBreakAcrossPages(true);
// Add it to the table
table.appendChild(row);
// Add cells to the row
for (int iCell = 0; iCell < 4; iCell++)
{
// Create a new cell and set text inside it
com.aspose.words.Cell cell = new com.aspose.words.Cell(wordDoc);
cell.appendChild(new com.aspose.words.Paragraph(wordDoc));
cell.getFirstParagraph().appendChild(new Run(wordDoc, text));
cell.getFirstParagraph().getParagraphFormat().setSpaceAfter(0);
row.appendChild(cell);
}
}
private static int getBookmarkPage(com.aspose.words.Document wordDoc, com.aspose.words.Bookmark bookmarkContact) throws Exception
{
// Find the page number, where our contacts bookmark is
LayoutCollector collector = new LayoutCollector(wordDoc);
return collector.getStartPageIndex(bookmarkContact.getBookmarkEnd());
}
I work with Aspose as Developer Evangelist.