iText - avoid last row not to cut tables on page split to next page
Asked Answered
E

3

9

I am working on itext 5 using java. I have pages with mutiple tables with dynamic rows. In some instances, the table last row is splitted into next page with the folowing header. I am using setHeaderRows() and setSkipFirstHeader() to manage continuation of next page. The last row has enough space to fit on earlier page. I would like to fit that last row in same page instead of next page.

For example, on page 1, the last row is splitted into first row of next page. Instead I would like to fit that row in page 1 so save one extra page with all blanks.

I tried using setExtendLastRow(), but its not working. Does anyone know how to fix this problem. I am attaching a working sample code.

public class ProposalItextSplitLastRow {
 public static void main(String[] args) {
    try {
        Document document = new Document();
        document.setPageSize(PageSize.LETTER);
        document.setMargins(16, 14, 14, 14);
        PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("C:/SplitLastRow.pdf"));
        document.open();
        document.setPageSize(PageSize.LETTER);
        document.setMargins(16, 14, 42, 38);

        for (int m = 1; m < 20; m++) {

            int row = 0;
            PdfPTable table = new PdfPTable(1);
            table.setSpacingAfter(0);
            table.setSpacingBefore(0);
            table.setWidthPercentage(100);

            table.setHeaderRows(1);
            table.setSkipFirstHeader(true);
            add(table, "Header Row continued " + m, BaseColor.LIGHT_GRAY, row++);
            add(table, "Header Row normal " + m, BaseColor.LIGHT_GRAY, row++);

            add(table, "Text Row 1 ", BaseColor.WHITE, row++);
            add(table, "Text Row 2 ", BaseColor.WHITE, row++);
            add(table, "Text Row 3 ", BaseColor.WHITE, row++);

            addPadding(table);

            document.add(table);
        }

        document.close();
    } catch (Exception de) {
        de.printStackTrace();
    }
}

private static void add(PdfPTable table, String text, BaseColor color, int row) {
    PdfPCell pdfCellHeader = new PdfPCell();
    pdfCellHeader.setBackgroundColor(color);
    pdfCellHeader.addElement(new Paragraph(new Phrase(text)));
    table.addCell(pdfCellHeader);
}

private static void addPadding(PdfPTable table) {
    PdfPCell cell = new PdfPCell();
    cell.setFixedHeight(2f);
    cell.setBorder(Rectangle.NO_BORDER);
    cell.setColspan(table.getNumberOfColumns());
    table.addCell(cell);
}
}
Ecru answered 18/5, 2013 at 1:37 Comment(0)
P
8

I had to execute the example to understand your question. You confused me by talking about a header that isn't a header (the rows with "Header Row normal" aren't header rows!) and your reference to setExtendLastRow() didn't help either (mentioning that method doesn't make sense to me; it's very confusing).

This being said, the solution to your problem is a no-brainer. I've rewritten the main class:

public static void main(String[] args) {
    try {
        Document document = new Document();
        document.setPageSize(PageSize.LETTER);
        document.setMargins(16, 14, 14, 14);
        PdfWriter writer = PdfWriter.getInstance(document,
                new FileOutputStream("SplitLastRow.pdf"));
        document.open();
        document.setPageSize(PageSize.LETTER);
        document.setMargins(16, 14, 42, 38);

        for (int m = 1; m < 20; m++) {

            int row = 0;
            PdfPTable table = new PdfPTable(1);
            table.setSpacingAfter(0);
            table.setSpacingBefore(0);
            table.setTotalWidth(document.right() - document.left());
            table.setLockedWidth(true);

            table.setHeaderRows(1);
            table.setSkipFirstHeader(true);
            add(table, "Header Row continued " + m, BaseColor.LIGHT_GRAY, row++);
            add(table, "Header Row normal " + m, BaseColor.LIGHT_GRAY, row++);

            add(table, "Text Row 1 ", BaseColor.WHITE, row++);
            add(table, "Text Row 2 ", BaseColor.WHITE, row++);
            add(table, "Text Row 3 ", BaseColor.WHITE, row++);

            addPadding(table);
            if (writer.getVerticalPosition(true) - table.getRowHeight(0) - table.getRowHeight(1) < document.bottom()) {
                document.newPage();
            }
            document.add(table);
        }

        document.close();
    } catch (Exception de) {
        de.printStackTrace();
    }
}

Make sure you define a total width instead of a width percentage, and lock the width. As documented (and as common sense tells you), a PdfPTable object doesn't know its actual width if you define a width percentage. It goes without saying that you can't calculate the height of a table that doesn't know it's actual width.

Then use getVerticalPosition() method to get the current position of the cursor, and check if the first two rows fit on the page. If they don't go to a new page before adding the table. If you want to check if the complete table fits, use the getTotalHeight() method instead of the getRowHeight() method.

Philbin answered 18/5, 2013 at 15:56 Comment(4)
It worked perfectly :) Thank you very much. Your help is greatly appreciated. Sorry for confusing with my question. I was struggling this from past couple of weeks. This has solved my problem with avoiding first row to cut tables on page split posted at #16548501. Just curios to know why your solution does not work with table.setWidthPercentage().Ecru
You might have an idea how to solve my other issue of not to split the last row into next page (not to widow orphan last row). This is what its happening with my code. Header Row normal 16 Text Row 1 Text Row 2 -----page break---- Header Row continued 16 Text Row 3 I am looking for a solution not to have a page break (split) and print row 3 in first page, like Header Row normal 16 Text Row 1 Text Row 2 Text Row 3Ecru
Why would I answer a follow-up question that is posted to an answer that isn't accepted? Also: additional questions require a new question.Philbin
About your curiosity: common sense is sufficient to understand why the solution doesn't work with width percentages, but I've adapted the answer in case it's not clear. If you don't want a final row to be orphaned, you need to extend the test so that it covers more rows instead of just row 0 and 1. For instance: you could check if the total height of the table fits on the page. If not, check if the total height minus the height of the last row fits. If it does, your last row will be orphaned and you'll have to decide where to split the table.Philbin
E
10

you can table.setKeepRowsTogather(true);

table.setHeaderRows(1) as well alongwith it

setKeepRowsTogather() checks if it can keep all the rows in page but splits the rows in case the table spans multiple pages. In that case setHeaderRows(1) will put the header rows again in the next page.

Encumbrancer answered 23/7, 2013 at 5:51 Comment(1)
Thanks for your help! It worked for me table.setKeepRowsTogather(true);, but in my iText version (5.5.5) this function doesn't exist: actually it's counterpart it's table.setKeepTogether(true);Precedent
P
8

I had to execute the example to understand your question. You confused me by talking about a header that isn't a header (the rows with "Header Row normal" aren't header rows!) and your reference to setExtendLastRow() didn't help either (mentioning that method doesn't make sense to me; it's very confusing).

This being said, the solution to your problem is a no-brainer. I've rewritten the main class:

public static void main(String[] args) {
    try {
        Document document = new Document();
        document.setPageSize(PageSize.LETTER);
        document.setMargins(16, 14, 14, 14);
        PdfWriter writer = PdfWriter.getInstance(document,
                new FileOutputStream("SplitLastRow.pdf"));
        document.open();
        document.setPageSize(PageSize.LETTER);
        document.setMargins(16, 14, 42, 38);

        for (int m = 1; m < 20; m++) {

            int row = 0;
            PdfPTable table = new PdfPTable(1);
            table.setSpacingAfter(0);
            table.setSpacingBefore(0);
            table.setTotalWidth(document.right() - document.left());
            table.setLockedWidth(true);

            table.setHeaderRows(1);
            table.setSkipFirstHeader(true);
            add(table, "Header Row continued " + m, BaseColor.LIGHT_GRAY, row++);
            add(table, "Header Row normal " + m, BaseColor.LIGHT_GRAY, row++);

            add(table, "Text Row 1 ", BaseColor.WHITE, row++);
            add(table, "Text Row 2 ", BaseColor.WHITE, row++);
            add(table, "Text Row 3 ", BaseColor.WHITE, row++);

            addPadding(table);
            if (writer.getVerticalPosition(true) - table.getRowHeight(0) - table.getRowHeight(1) < document.bottom()) {
                document.newPage();
            }
            document.add(table);
        }

        document.close();
    } catch (Exception de) {
        de.printStackTrace();
    }
}

Make sure you define a total width instead of a width percentage, and lock the width. As documented (and as common sense tells you), a PdfPTable object doesn't know its actual width if you define a width percentage. It goes without saying that you can't calculate the height of a table that doesn't know it's actual width.

Then use getVerticalPosition() method to get the current position of the cursor, and check if the first two rows fit on the page. If they don't go to a new page before adding the table. If you want to check if the complete table fits, use the getTotalHeight() method instead of the getRowHeight() method.

Philbin answered 18/5, 2013 at 15:56 Comment(4)
It worked perfectly :) Thank you very much. Your help is greatly appreciated. Sorry for confusing with my question. I was struggling this from past couple of weeks. This has solved my problem with avoiding first row to cut tables on page split posted at #16548501. Just curios to know why your solution does not work with table.setWidthPercentage().Ecru
You might have an idea how to solve my other issue of not to split the last row into next page (not to widow orphan last row). This is what its happening with my code. Header Row normal 16 Text Row 1 Text Row 2 -----page break---- Header Row continued 16 Text Row 3 I am looking for a solution not to have a page break (split) and print row 3 in first page, like Header Row normal 16 Text Row 1 Text Row 2 Text Row 3Ecru
Why would I answer a follow-up question that is posted to an answer that isn't accepted? Also: additional questions require a new question.Philbin
About your curiosity: common sense is sufficient to understand why the solution doesn't work with width percentages, but I've adapted the answer in case it's not clear. If you don't want a final row to be orphaned, you need to extend the test so that it covers more rows instead of just row 0 and 1. For instance: you could check if the total height of the table fits on the page. If not, check if the total height minus the height of the last row fits. If it does, your last row will be orphaned and you'll have to decide where to split the table.Philbin
G
-1

You can do

table.setSplitRows(false);

But I believe that when there is a row that wont fit it just wont be shown. It's worth a shot though

Gravure answered 18/5, 2013 at 4:39 Comment(1)
This thing wont work for keep table all together on one page.Aldwon

© 2022 - 2024 — McMap. All rights reserved.