Question

I've been searching for an answer for this but cant find a good tutorial. I'm using Java itext to generate a pdf. I have a table which prints over 50 rows which eats around 2 pages. I need to print the last 5 rows of the previous page to the next page before continuing printing the list.

Example: row 31 row 32 row 33 row 34 row 35 row 36 --end of page 1-- row 32 row 33 row 34 row 35 row 36 row 37

Any idea on how to do this?

Currently it prints like this: row 35 row 36 --end of page 1-- row 37 row 38

Code: (Focus on start loop and never mind those headers)

PdfPTable table = new PdfPTable(4);
table.setWidthPercentage(97);
table.setWidths(new float[] {10, 45, 25, 25});

PdfPCell c1 = new PdfPCell(new Phrase("Logo", FontFactory.getFont(FontFactory.TIMES_ROMAN, 14)));
c1.setPadding(5);
c1.setHorizontalAlignment(Element.ALIGN_LEFT);
c1.setVerticalAlignment(Element.ALIGN_CENTER);
c1.setBorder(0);
c1.setColspan(4);
table.addCell(c1);

c1 = new PdfPCell(new Phrase("09/01/2013 - 09/15/2013", FontFactory.getFont(FontFactory.TIMES_ROMAN, 12)));
c1.setPadding(5);
c1.setHorizontalAlignment(Element.ALIGN_LEFT);
c1.setVerticalAlignment(Element.ALIGN_CENTER);
c1.setBorder(0);
c1.setColspan(4);
c1.setPaddingBottom(20);
table.addCell(c1);

//start header
c1 = new PdfPCell(new Phrase(" ", FontFactory.getFont(FontFactory.TIMES_ROMAN, 12)));
c1.setPaddingBottom(5);
c1.setHorizontalAlignment(Element.ALIGN_LEFT);
c1.setBackgroundColor(WebColors.getRGBColor("#99CCFF"));
c1.setBorder(1);
table.addCell(c1);

c1 = new PdfPCell(new Phrase("Name", FontFactory.getFont(FontFactory.TIMES_ROMAN, 12)));
c1.setPaddingBottom(5);
c1.setHorizontalAlignment(Element.ALIGN_LEFT);
c1.setBackgroundColor(WebColors.getRGBColor("#99CCFF"));
c1.setBorder(1);
table.addCell(c1);

c1 = new PdfPCell(new Phrase("Number", FontFactory.getFont(FontFactory.TIMES_ROMAN, 12)));
c1.setPaddingBottom(5);
c1.setHorizontalAlignment(Element.ALIGN_LEFT);
c1.setBackgroundColor(WebColors.getRGBColor("#99CCFF"));
c1.setBorder(1);
table.addCell(c1);

c1 = new PdfPCell(new Phrase("Amount", FontFactory.getFont(FontFactory.TIMES_ROMAN, 12)));
c1.setPaddingBottom(5);
c1.setHorizontalAlignment(Element.ALIGN_RIGHT);
c1.setBackgroundColor(WebColors.getRGBColor("#99CCFF"));
c1.setBorder(1);
table.addCell(c1);
//end header

//start for loop
for(int a=0; a<50; a++)
{
    int border=0;
    if(a%5==0)
    {border=1;}

    c1 = new PdfPCell(new Phrase((a+1)+"", FontFactory.getFont(FontFactory.TIMES_ROMAN, 12)));
    c1.setHorizontalAlignment(Element.ALIGN_LEFT);
    c1.setBorder(border);
    c1.setPaddingBottom(5);
    table.addCell(c1);

    c1 = new PdfPCell(new Phrase("Last Name, First Name", FontFactory.getFont(FontFactory.TIMES_ROMAN, 12)));
    c1.setHorizontalAlignment(Element.ALIGN_LEFT);
    c1.setBorder(border);
    c1.setPaddingBottom(5);
    table.addCell(c1);

    c1 = new PdfPCell(new Phrase("1", FontFactory.getFont(FontFactory.TIMES_ROMAN, 12)));
    c1.setHorizontalAlignment(Element.ALIGN_CENTER);
    c1.setBorder(border);
    c1.setPaddingBottom(5);
    table.addCell(c1);

    c1 = new PdfPCell(new Phrase("1", FontFactory.getFont(FontFactory.TIMES_ROMAN, 12)));
    c1.setHorizontalAlignment(Element.ALIGN_RIGHT);
    c1.setBorder(border);
    c1.setPaddingBottom(5);
    table.addCell(c1);


}
//end for loop

subCatPart.add(table);
Was it helpful?

Solution

You can't achieve this by using document.add(); instead you need to write specific rows using the writeSelectedRows() method. See for instance the RepeatLastRows method.

In this example, I have a table with 99 rows. If I would use document.add() to add this table to a document, it would take 3 pages, and the third page would only show 3 rows. You want this page to show 5 rows, repeating 2 rows from the previous page.

This is how it's done, assuming that we have an A4 page with a margin of 36pt. This means that the available surface measures 523 by 770 pt:

// we create a table that spans the width of the page and that has 99 rows
PdfPTable table = new PdfPTable(1);
table.setTotalWidth(523);
for (int i = 1; i < 100; i++)
    table.addCell("row " + i);

Note that I set the total width. You'll get an exception if you use writeSelectedRows() without specifying a width.

We initialize some parameters:

PdfContentByte canvas = writer.getDirectContent();
int currentRowStart = 0;
int currentRow = 0;
int totalRows = table.getRows().size();

We'll create a loop that continues as long as currentRow is lower than totalRows:

while (true) {
    // available height of the page
    float available_height = 770;
    // how many rows fit the height?
    while (available_height > 0 && currentRow < totalRows) {
        available_height -= table.getRowHeight(currentRow++);
    }
    // we stop as soon as all the rows are counted
    if (currentRow == totalRows)
        break;
    // we draw part the rows that fit the page and start a new page
    table.writeSelectedRows(currentRowStart, --currentRow, 36, 806, canvas);
    document.newPage();
    currentRowStart = currentRow;
}

In this loop, we have already filled two pages: one showing row 1 to 48, another one showing row 49 to 96. When we break out of the loop, the currentRow is 99 and the currentRowStart is 96 (this is the row index for row 97). This means that only row 97, 98 and 99 will be shown. We want to show 5 rows, so we change the value of currentRow accordingly.

if (currentRow - currentRowStart < 5)
    currentRowStart = currentRow - 5;
table.writeSelectedRows(currentRowStart, currentRow, 36, 806, canvas);

Please take a look at the resulting PDF, you'll see that the third page starts with row 95 (a row that was already shown on page 2).

Update: I misinterpreted the question in the sense that I thought that rows needed to be repeated only if there's an orphaned handful of rows on the final page. The actual requirement was to always repeat 5 rows every time the table is split.

I've slightly updated my original example. See RepeatLastRows2. You changed the value of currentRowStart so that the start goes back 5 rows, but you forgot to update the currentRow value. As a result, you don't subtract sufficient space from the available height.

Also make sure you don't introduce endless loops (or ArrayIndexOutOfBoundsExceptions) by repeating the same 5 rows over and over again if only 5 rows (or less) fit a single page.

OTHER TIPS

You can use the recently introduced PdfPTableEventAfterSplit which is triggered when a table needs to be split. It is triggered after the current page is rendered, but before the next page is started. You can use it to duplicate the rows you want to repeat on the next page.

class RepeatSplitEvent extends PdfPTableEventForwarder {
    @Override
    public void afterSplitTable(PdfPTable table, PdfPRow startRow, int startIdx) {
        for (int i = startIdx - 5; i < startIdx; i++) {
            table.getRows().add(0, table.getRow(i));
        }
    }
}

And

// ...
PdfPTable table = new PdfPTable(4);
table.setTableEvent(new RepeatSplitEvent());
// ...
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top