I'm using Itext7 to create a pdf with a table in it.
The table occupies multiple pages.
I'm also overriding the cell renderer to add a formfield to some cell.
I noticed that when the table goes to a new page the first line doesn't trigger the draw method of the CellRender class.
I put some code below for better understanding.
...
//adding the cells to table
for (int i = 0; i < 10; i++) {
addRow(table,i);
}
...
//addRow implementation
private void addRow(Table table, int row) throws IOException {
table.startNewRow();
PdfFont zapfdingbats = PdfFontFactory.createFont(FontConstants.ZAPFDINGBATS);
Cell checkBoxCell = new Cell().add(new Paragraph("o").setFont(zapfdingbats).setMargins(9, 0, 0, 11)).setKeepTogether(true);
checkBoxCell.setNextRenderer(new CheckboxCellRenderer(checkBoxCell, "cb"+row));
table.addCell(checkBoxCell);
...
//other cells here
}
...
//The implementation of the custom CellRender
protected class CheckboxCellRenderer extends CellRenderer {
protected String name;
public CheckboxCellRenderer(Cell modelElement, String name) {
super(modelElement);
this.name = name;
}
//this method is not triggered for the first row when a new page is created
@Override
public void draw(DrawContext drawContext) {
System.out.println(name);
super.draw(drawContext);
float x = getOccupiedAreaBBox().getLeft();
float y = getOccupiedAreaBBox().getBottom();
float width = getOccupiedAreaBBox().getRight()-getOccupiedAreaBBox().getLeft();
float height = getOccupiedAreaBBox().getTop()-getOccupiedAreaBBox().getBottom();
Rectangle rect = new Rectangle(x+5, y+5, width-10, height-10);
PdfTextFormField pdfTextFormField = PdfFormField.createText(drawContext.getDocument(), rect, name);
PdfAcroForm.getAcroForm(drawContext.getDocument(), true).addField(pdfTextFormField);
}
}
What am I doing wrong?
You do not override one very important method: public IRenderer getNextRenderer()
.
If a cell cannot be placed on a page, iText creates an overflow cell (see protected AbstractRenderer createOverflowRenderer(int layoutResult)
method. If you do not override getNextRenderer
iText by default will create simple CellRenderer using its getModelElement().
So I suggest you should add summat:
@Override
public IRenderer getNextRenderer() {
return new CheckboxCellRenderer(getModelElement(), name);
}
I've created the next sample using your code and it works as you expect. `
public class ArrayToTable extends GenericTest {
public static final String DEST = "./target/test/resources/sandbox/tables/array_to_table.pdf";
public static void main(String[] args) throws Exception {
File file = new File(DEST);
file.getParentFile().mkdirs();
new CustomRendererTest().manipulatePdf(DEST);
}
@Override
protected void manipulatePdf(String dest) throws Exception {
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
Document doc = new Document(pdfDoc);
Table table = new Table(1);
//adding the cells to table
for (int i = 0; i < 25; i++) {
addRow(table, i);
}
doc.add(table);
doc.close();
}
//addRow implementation
private void addRow(Table table, int row) throws IOException {
table.startNewRow();
PdfFont zapfdingbats = PdfFontFactory.createFont(FontConstants.ZAPFDINGBATS);
Cell checkBoxCell = new Cell().add(new Paragraph("o").setFont(zapfdingbats).setMargins(9, 0, 0, 11)).setKeepTogether(true);
checkBoxCell.setNextRenderer(new CheckboxCellRenderer(checkBoxCell, "cb"+row));
table.addCell(checkBoxCell);
//other cells here
}
//The implementation of the custom CellRender
protected class CheckboxCellRenderer extends CellRenderer {
protected String name;
public CheckboxCellRenderer(Cell modelElement, String name) {
super(modelElement);
this.name = name;
}
//this method is not triggered for the first row when a new page is created
@Override
public void draw(DrawContext drawContext) {
System.out.println(name);
super.draw(drawContext);
float x = getOccupiedAreaBBox().getLeft();
float y = getOccupiedAreaBBox().getBottom();
float width = getOccupiedAreaBBox().getRight()-getOccupiedAreaBBox().getLeft();
float height = getOccupiedAreaBBox().getTop()-getOccupiedAreaBBox().getBottom();
Rectangle rect = new Rectangle(x+5, y+5, width-10, height-10);
PdfTextFormField pdfTextFormField = PdfFormField.createText(drawContext.getDocument(), rect, name);
PdfAcroForm.getAcroForm(drawContext.getDocument(), true).addField(pdfTextFormField);
}
@Override
public IRenderer getNextRenderer() {
return new CheckboxCellRenderer(getModelElement(), name);
}
}
} `
If you go to a new page, then your Renderer
is discarded and a new Renderer
object is created. This is done in order to avoid muddying up the state in case something changes in the model object. In this case, the method getNextRenderer
is called:
/**
* Gets a new instance of this class to be used as a next renderer, after this renderer is used, if
* {@link #layout(LayoutContext)} is called more than once.
* @return new renderer instance
*/
IRenderer getNextRenderer();
So you should be OK if you just override this method in your custom CheckboxCellRenderer
class:
@Override
public IRenderer getNextRenderer() {
return new CheckboxCellRenderer(getModelElement(), name);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With