Hi Paulo and Bruno, � Thank you for a very useful tool! We use it to generate reports in one of our internal applications. � Recently it became an issue for us that iText drops rows from the output when using PdfPTable if a row is larger than a page. In order to fix this problem, I added some new methods to PdfWriter, PdfDocument, PdfPTable, and PdfPRow which split rows that are larger than a page into smaller rows. Currently all I did was break apart nested tables that are too large for a page. I can expand upon this work to split up cells of pure text into multiple cells if there is interest. To split apart large cells the user must call PdfWriter.fitCellsToPageSize(PdfPTable table) before adding their table to the document. Otherwise there is no change to the existing logic. � I hope that you will consider incorporating these changes into your source. I made the changes and tested them against Bruno's most recent release, 1.02b. I can apply and test them against a newer version of the source code if you like. I've attached a text file that contains all of my additions along with a sample java program that demonstrates the new functionality. � Thanks again, -Dan
TestPageSplitting.java
Description: TestPageSplitting.java
I added new methods to four files, all in the com.lowagie.txt.pdf package * PdfWriter.java * PdfDocument.java * PdfPTable.java * PdfPRow.java
--- Additions to PdfWriter.java ------------------------------------------
/**
* Split up the cells in a table to fit to the size of a page, so that no rows
* are silently dropped from the output. Currently this function only breaks
* up nested tables to accomplish this, it can't handle blocks of text
* @see com.lowagie.text.pdf.PdfPTable#fitCellsToPageSize(float,float)
*/
public void fitCellsToPageSize(PdfPTable table)
{
getPdfDocument().fitCellsToPageSize(table);
}
--- Additions to PdfDocument.java ----------------------------------------
/**
* Split up the cells in a table to fit to the size of a page, so that no rows
* are silently dropped from the output. Currently this function only breaks
* up nested tables to accomplish this, it can't handle blocks of text
* @see com.lowagie.text.pdf.PdfPTable#fitCellsToPageSize(float,float)
*/
public void fitCellsToPageSize(PdfPTable table)
{
float width = ((indentRight() - indentLeft()) * table.getWidthPercentage()) / 100;
float height = indentTop() - indentBottom();
table.fitCellsToPageSize(width, height);
}
--- Additions to PdfPTable.java ------------------------------------------
/** This function tries to split the cells in a PdfPTable to fit the given
* page size. It does so by dividing up any sub tables within the table
* into seperate rows within the table. This function adds rows to the
* table. The top row after a breakup contains all of the cells on the same
* row as the broken up cell plus whatever could fit on a page for the
* broken up cell. The subsequent rows contain blank cells plus the
* remaining content of the cells.
*/
public void fitCellsToPageSize(float maxWidth, float maxHeight)
{
setTotalWidth(maxWidth);
maxHeight -= getHeaderHeight();
for(int i = getHeaderRows(); i < size(); i++) {
float rowHeight = getRowHeight(i);
if(rowHeight > maxHeight) {
PdfPRow oversizedRow = getRow(i);
ArrayList dividedRows = oversizedRow.splitToPageSize(maxWidth, maxHeight);
rows.remove(i);
rows.addAll(i, dividedRows);
}
}
calculateWidths();
calculateHeights();
}
/** Split the table up into smaller tables that each fit on a page.
* this function is primarily used for nested tables. If
* we call fitCellsToPageSize on a table with a nested table inside
* we need to split that table into pieces that each fit on a page
*/
protected ArrayList splitToPageSize(float maxWidth, float maxHeight)
{
ArrayList splitTables = new ArrayList();
//first split up the cells to the size of a page
fitCellsToPageSize(maxWidth, maxHeight);
if(getTotalHeight() < maxHeight) {
splitTables.add(this);
return splitTables;
}
else {
float height = getHeaderHeight();
int startIndex = 0;
for(int i = getHeaderRows(); i < rows.size(); i++) {
height += ((PdfPRow)rows.get(i)).getMaxHeights();
//if the height up to this point is larger than the size of a page
//break up the table at the last cell
if(height > maxHeight - 1) {
PdfPTable table = subTable(startIndex, i);
splitTables.add(table);
height = ((PdfPRow)rows.get(i)).getMaxHeights() + getHeaderHeight();
startIndex = i;
}
}
if(startIndex != rows.size()) {
PdfPTable table = subTable(startIndex, rows.size());
splitTables.add(table);
}
}
return splitTables;
}
/** Returns a new table containing the rows between fromIndex inclusive and
* toIndex exclusive. Header rows are copied into the new table. Unlike
* the java.util.List subList function, changes to this table are not
* reflected in the original table.
*/
protected PdfPTable subTable(int fromIndex, int toIndex)
{
if(toIndex < fromIndex) {
throw new IllegalArgumentException("From Index must be less than toIndex.");
}
if(toIndex < 0 || fromIndex > rows.size() + 1) {
throw new IndexOutOfBoundsException("endpoints out of range fromIndex < 0 ||
toIndex > size");
}
PdfPTable newTable = new PdfPTable(relativeWidths);
newTable.setTotalWidth(totalWidth);
newTable.setHeaderRows(headerRows);
newTable.tableEvent = tableEvent;
newTable.runDirection = runDirection;
newTable.defaultCell = new PdfPCell(defaultCell);
//if we are including the last row, lets include the current row
if(fromIndex == rows.size() + 1) {
newTable.isColspan = isColspan;
newTable.currentRowIdx = currentRowIdx;
for (int k = 0; k < currentRow.length; ++k) {
if (currentRow[k] == null)
break;
newTable.currentRow[k] = new PdfPCell(currentRow[k]);
}
}
//copy the header rows into the new table
if(fromIndex < getHeaderRows()) {
fromIndex = getHeaderRows();
}
else {
for (int k = 0; k < getHeaderRows(); ++k) {
newTable.rows.add(new PdfPRow((PdfPRow)(rows.get(k))));
}
}
//copy the selected rows into the new table
for (int k = fromIndex; k < toIndex; ++k) {
newTable.rows.add(new PdfPRow((PdfPRow)(rows.get(k))));
}
newTable.calculateWidths();
newTable.calculateHeights();
return newTable;
}
--- Additions to PdfPRow.java ----------------------------------------
/** split the row into multiple rows that each fit within a given page
* height. Do so by breaking tables into multiple tables
*/
public ArrayList splitToPageSize(float maxWidth, float maxHeight)
{
ArrayList splitRows = new ArrayList();
splitRows.add(this);
for(int i = 0; i < cells.length; i++)
{
float height = cells[i].getFixedHeight();
if (height <= 0)
height = cells[i].height();
if (height < cells[i].getFixedHeight())
height = cells[i].getFixedHeight();
else if (height < cells[i].getMinimumHeight())
height = cells[i].getMinimumHeight();
if(height > maxHeight) {
PdfPTable table = cells[i].getTable();
Image img = cells[i].getImage();
if(img != null) {
//the image will automatically be scaled to fit
continue;
}
else if(table != null) {
//split the table into pieces
ArrayList splitTables = table.splitToPageSize(widths[i] -
cells[i].getPaddingLeft() - cells[i].getPaddingRight(), maxHeight -
cells[i].getPaddingBottom() - cells[i].getPaddingTop());
if(splitTables.size() == 1) {
cells[i].setTable((PdfPTable)splitTables.get(0));
continue;
}
else {
//create new rows to hold the table pieces if we don't have enough
while(splitTables.size() > splitRows.size()) {
PdfPCell[] newRowCells = new PdfPCell[cells.length];
for(int index = 0; index < newRowCells.length; index++) {
PdfPCell newCell = new PdfPCell(cells[index]);
newCell.setTable(null);
newCell.setImage(null);
newCell.setPhrase(new Phrase(""));
newRowCells[index] = newCell;
}
splitRows.add(new PdfPRow(newRowCells));
}
//add each piece of the table to our new rows
for(int newRowIndex = 0; newRowIndex < splitRows.size(); newRowIndex++) {
((PdfPRow)splitRows.get(newRowIndex)).cells[i].setTable((PdfPTable)splitTables.get(newRowIndex));
}
}
}
else {
//break up a block of text that is larger than a page. This functionality
is not yet implimented.
}
}
}
return splitRows;
}
