What's an easy to print "first right, then down"?

Category: 
Tags: tablessplit tablePdfTemplateclippingiText 5

I would like to print a PdfPTable containing many more rows and columns than fit in one page of the PDF.

I would like to print "first right, then down", which is similar to row-major order. Print the first page-full of rows, but all columns for those rows. Then print the next page-full of rows, and again all columns for those rows.

As I understand it, iText can handle a PdfPTable containing more rows than fit on a page, but can't handle more columns than fit on a page. So I split the columns myself. But that means that I end up printing "first down, then right", which is not what I want.

Is there an easy way to do what I want? Note that I do not know the row heights ahead of time, because I call setMinimumHeight() on the PdfPCell objects.

The only way I can think of, which is clumsy, is to first split columns, as before. Then, for each sequence of columns that fit on one page, add rows one by one to a PdfPTable as long as they fit. When they don't, that's where we need to split the rows. At the end of this, we end up with PdfPTable objects for each set of rows and columns. We need to keep all of them in memory and add them to the PDF Document in the right (row-major) order.

This seems rather inelegant, so I wanted to check if there's a cleaner solution.

Posted on StackOverflow on Feb 28, 2014 by Kartick Vaddadi

If you have really large tables, the most elegant way to maintain the overview and to distribute the table over different pages, is by adding the table to one really large PdfTemplate object and then afterwards add clipped parts of that PdfTemplate object to different pages.

That's what I've done in the TableTemplate example.

I create a table with 15 columns a total width of 1500pt.

PdfPTable table = new PdfPTable(15);
table.setTotalWidth(1500);
PdfPCell cell;
for (int r = 'A'; r <= 'Z'; r++) {
    for (int c = 1; c <= 15; c++) {
        cell = new PdfPCell();
        cell.setFixedHeight(50);
        cell.addElement(new Paragraph(
            String.valueOf((char) r) + String.valueOf(c)));
        table.addCell(cell);
    }
}

Now I write this table to a Form XObject that measures 1500 by 1300. Note that 1300 is the total height of the table. You can get it like this: table.getTotalHeight() or you can multiply the 26 rows by the fixed height of 50 pt.

PdfContentByte canvas = writer.getDirectContent();
PdfTemplate tableTemplate = canvas.createTemplate(1500, 1300);
table.writeSelectedRows(0, -1, 0, 1300, tableTemplate);

Now that you have the Form XObject, you can reuse it on every page like this:

PdfTemplate clip;
for (int j = 0; j < 1500; j += 500) {
    for (int i = 1300; i > 0; i -= 650) {
        clip = canvas.createTemplate(500, 650);
        clip.addTemplate(tableTemplate, -j, 650 - i);
        canvas.addTemplate(clip, 36, 156);
        document.newPage();
    }
}

The result is table_template.pdf where we have cells A1 to M5 on page 1, cells N1 to Z5 on page 2, cells A6 to M10 on page 3, cells N6 to Z10 on page 4, cells A11 to M15 on page 5 and cells N11 to Z15 on page 6.