Search code examples
c#itext7

How to repeat column value only on first row in each page (eventual) in itext 7 tables?


I need to achieve a table that looks like the one in the picture, with 1 or 2 columns values repeated only in first row of the table for each page, eventually rendered.

Table Example

I tried with a Footer HandleEvent where I add a canvas a specific place if page > 1.

This solution presents a problem: if the table has rendered only in the first page (after the table there are 3 rows with text), in the second page I found my text for column 1 e 2 (ID and Name).

Any suggestions to approach ?

I'm trying to figure out if the table event handling still exists in itext7 (setTableEvent` setter is no more available for Table with itext7)?

// ...
Footer footerHandler = new Footer();
pdf.AddEventHandler(PdfDocumentEvent.END_PAGE, footerHandler);
// ...
public virtual void HandleEvent(Event @event) {
    PdfDocumentEvent docEvent = (PdfDocumentEvent)@event;
    PdfDocument pdf = docEvent.GetDocument();
    PdfPage page = docEvent.GetPage();
    int pageNumber = pdf.GetPageNumber(page);
    // ...

    if (pageNumber !=1) {
          // get size of rectangle from file...

          Rectangle rect = new Rectangle(float.Parse(m.X_LABELNOME), float.Parse(m.Y_LABELNOME), float.Parse(m.WIDTH_LABELNOME), float.Parse(m.HEIGHT_LABELNOME));
          pdfCanvas = new PdfCanvas(page);
          Canvas mycanvas = new Canvas(pdfCanvas, rect);
          mycanvas.SetFontSize(9f);
          Paragraph mytext = new Paragraph(ID_Variable or Name Variable).SetTextAlignment(TextAlignment.CENTER);
          mycanvas.Add(mytext);
          mycanvas.Close();

          // ...

Solution

  • You can customize a TableRenderer and override its layout logic to copy cells from the previous part of the table to the next one.

    Here is an example on how to do it (please note that this code relies on some TableRenderer native implementation details, so there is no guarantees this code will work for all the future updates of iText 7):

    private static class CustomTableRenderer extends TableRenderer {
        public CustomTableRenderer(Table modelElement) {
            super(modelElement);
        }
    
        @Override
        public LayoutResult layout(LayoutContext layoutContext) {
            LayoutResult result = super.layout(layoutContext);
            if (result.getStatus() == LayoutResult.PARTIAL) {
                CustomTableRenderer overflowRenderer = (CustomTableRenderer) result.getOverflowRenderer();
                // Modify first two columns
                for (int i = 0; i < 2; i++) {
                    CellRenderer previousTablePartCell = overflowRenderer.rows.get(0)[i];
                    overflowRenderer.rows.get(0)[i] = (CellRenderer) ((Cell) previousTablePartCell.getModelElement()).createRendererSubTree().setParent(this);
                }
            }
            return result;
        }
    
        @Override
        public IRenderer getNextRenderer() {
            return new CustomTableRenderer((Table) getModelElement());
        }
    }
    

    Now all we need to do it set this custom renderer to our table. Full code:

    Document document = new Document(pdfDocument);
    
    Table table = new Table(3).useAllAvailableWidth();
    for (int i = 0; i < 10; i++) {
        table.addCell(new Paragraph("Hello " + i));
    
        table.addCell(String.valueOf(i));
    
        StringBuilder sb = new StringBuilder();
        for (int j = 0; j < 20; j++) {
            sb.append(UUID.randomUUID()).append("\n");
        }
        table.addCell(sb.toString());
    }
    table.setNextRenderer(new CustomTableRenderer(table));
    document.add(table);
    

    Visual result:

    result