itextjava-11

Dinamic Page Height in iText 8


I have this situation where I need to have everything in a single PDF page in iText8.

I have tried several approaches so far no success.

One of my ideas was have a list of elements that would be add to the document and calculate the height of each element and then sum all of them and sent the height on the PageSize in the Document object.

That didn't work.

The other idea was add rows to a Table and add the table to a temporary document, then get the Table height and create another document passing that table height to the PageSize, that didn't work either.

Any other ideas?

Create an example of a Table with 50 rows:

private List<IElement> createContent() {
    List<IElement> elements = new ArrayList<>();
    int totalRows = 50;
    Table table = new Table(UnitValue.createPercentArray(new float[] { 70f, 30f })).useAllAvailableWidth().setMarginTop(0f);
    for (int i = 0; i < totalRows; i++) {
        Cell coluna1 = new Cell().setPadding(0f).add(new Paragraph("" + (i + 1) + ", 1").setPadding(1f));
        Cell coluna2 = new Cell().setPadding(0f).add(new Paragraph("" + (i + 1) + ", 2").setPadding(1f));
        table.addCell(coluna1);
        table.addCell(coluna2);
    }
    elements.add(table);
    return elements;
}  

Trying to get the total height:

   private static float calculateTotalHeight(List<IElement> elements, float availableWidth) {
    float totalHeight = 0f;
    PdfDocument pdfDoc = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));
    Document doc = new Document(pdfDoc, new PageSize(availableWidth, 1000));
    doc.setMargins(0, 0, 0, 0);

    for (IElement element : elements) {
        IRenderer renderer = element.createRendererSubTree().setParent(doc.getRenderer());
        LayoutResult result = renderer.layout(new LayoutContext(new LayoutArea(0, new Rectangle(availableWidth, 1000))));
        totalHeight += result.getOccupiedArea().getBBox().getHeight();
    }

    doc.close();
    return totalHeight;
}

Trying to set the Document PageSize:

List<IElement> elements = createContent();
float totalHeight = calculateTotalHeight(elements, paperWidth);
PageSize pageSizeTemp = new PageSize(paperWidth, totalHeight);

Solution

  • Since you didn't add some output it generated for you I'm not sure what went wrong. But this code snippet seems to provide the desired behaviour.

        int pageWidth = 200;
        float INF = 1e6f;
    
        //layout in infinite space
        Document doc = new Document(new PdfDocument(new PdfWriter(new ByteArrayOutputStream())), new PageSize(pageWidth, INF));
    
        List<IElement> f = createContent();
        Div container = new Div();
        for (IElement iElement : f) {
            container.add((BlockElement<?>) iElement);
        }
        LayoutContext context = new LayoutContext(new LayoutArea(1, new Rectangle(pageWidth, INF)));
        DivRenderer renderer = (DivRenderer) container.createRendererSubTree();
        renderer.setParent(doc.getRenderer());
        LayoutResult result = renderer.layout(context);
    
        float size = result.getOccupiedArea().getBBox().getHeight();
        doc.close();
    
    
        // Use the calculated size to set the just fitting page
        PdfDocument doc2 = new PdfDocument(new PdfWriter("hello.pdf"));
        Document doc3 = new Document(doc2, new PageSize(200, size));
        //It's probably this line you are missing
        doc3.setMargins(0,0,0,0);
        for (IElement iElement : createContent()) {
            doc3.add((BlockElement<?>) iElement);
        }
        doc3.close();
    

    This resulted in a pdf looking like this:

    enter image description here