First of all, we need to override the renderer correctly, i.e. override getNextRenderer()
method. Currently TableRenderer
is quite problematic to override because the parameterless constructor of TableRenderer
is not accessible, and the other constructors do some implicit work that changes the state. But we can still work around this problem with the following code:
@Override
public IRenderer getNextRenderer() {
CustomTableRenderer nextTable = new CustomTableRenderer((Table) modelElement);
nextTable.rows.clear();
nextTable.rowRange = null;
return nextTable;
}
Disclaimer: since the answer uses private implementation details of TableRenderer
it may not work in the future. It works with 7.1.6
which is the latest released version at the moment of writing this post. You should create a custom fork of the code for such purposes. Pull requests are welcome as well.
If we look at the implementation of TableRenderer
we see that the class contains heights
(line in code) and countedColumnWidth
(line in code) fields which sound interesting, but they are private
. It means that we should create our custom fork of iText (source code available at https://github.com/itext/itext7), make those fields protected
and use them in our subclass.
You may use reflection in your code instead at your own risk, but it should not be used because it may not work with your JVM (changing accessibility modifiers is strongly discouraged) or may not work in the next version of iText, or may not work for another reason. I will not add reflection code in my answer to further discourage its usage.
All we need to do it override drawBorders()
method. Here is the code that already adds some randomness to the lines.
private static class CustomTableRenderer extends TableRenderer {
public CustomTableRenderer(Table modelElement) {
super(modelElement);
}
@Override
public IRenderer getNextRenderer() {
CustomTableRenderer nextTable = new CustomTableRenderer((Table) modelElement);
nextTable.rows.clear();
nextTable.rowRange = null;
return nextTable;
}
@Override
protected void drawBorders(DrawContext drawContext) {
PdfCanvas canvas = drawContext.getCanvas();
canvas.saveState();
canvas.setStrokeColor(ColorConstants.RED);
Random r = new Random();
// Draw vertical lines
float curX = getOccupiedAreaBBox().getLeft();
for (int i = 0; i <= countedColumnWidth.length; i++) {
canvas.moveTo(curX, getOccupiedAreaBBox().getTop() + 3);
canvas.lineTo(curX + r.nextInt(4), getOccupiedAreaBBox().getBottom() - 3);
if (i < countedColumnWidth.length) {
float curWidth = countedColumnWidth[i];
curX += curWidth;
}
}
// Draw horizontal lines
float curY = getOccupiedAreaBBox().getBottom();
for (int i = 0; i <= heights.size(); i++) {
canvas.moveTo(getOccupiedAreaBBox().getLeft() - 3, curY);
canvas.lineTo(getOccupiedAreaBBox().getRight() + 3, curY + r.nextInt(4));
if (i < heights.size()) {
float curHeight = heights.get(i);
curY += curHeight;
}
}
canvas.stroke();
canvas.restoreState();
}
}
To activate the custom renderer, set it to the table just before adding to the document:
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outFileName));
Document doc = new Document(pdfDoc);
Table table = new Table(UnitValue.createPercentArray(new float[]{30, 30}));
for (int i = 0; i < 40; i++) {
table.addCell(new Cell().add(new Paragraph("Hello")));
table.addCell(new Cell().add(new Paragraph("World")));
table.startNewRow();
}
table.setNextRenderer(new CustomTableRenderer(table));
doc.add(table);
This is how the resultant table looks like:
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…