Previously I asked a question about drawing custom table borders: Draw custom borders for table spanning across more than one page in itext7
And there an answer was provided with a way to draw custom borders, but it does not allow to affect the complete row or column line (for example to tilt it slightly) because cell borders are drawn separately for each cell.
I want to add some randomness to table borders like in the following screenshot:
Here is the code I have that works for tables that fit into a single page, but if table is spanned across several pages the code does not work anymore:
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
Document doc = new Document(pdfDoc);
doc.add(new Paragraph("Table event"));
Table table = new Table(UnitValue.createPercentArray(3)).useAllAvailableWidth();
table.setNextRenderer(new DottedLineTableRenderer(table, new Table.RowRange(0, 0)));
String s ="";
for(int i=0;i<35;i++){
s+="\nTest";
}
table.addCell(new Cell().add(new Paragraph(s)).setBorder(Border.NO_BORDER));
table.addCell(new Cell().add(new Paragraph("A2")).setBorder(Border.NO_BORDER));
table.addCell(new Cell().add(new Paragraph("A3")).setBorder(Border.NO_BORDER));
doc.add(table);
doc.close();
private class DottedLineTableRenderer extends TableRenderer {
public DottedLineTableRenderer(Table modelElement, Table.RowRange rowRange) {
super(modelElement, rowRange);
}
@Override
public void drawChildren(DrawContext drawContext) {
super.drawChildren(drawContext);
PdfCanvas canvas = drawContext.getCanvas();
int maxLineTo = 5;
int minLineTo = 2;
int lineToHorizontalLine = (int)(Math.random() * maxLineTo) + minLineTo;
int maxSkewHor = 5;
int minSkewHor = 2;
int skewHorizontalLine = (int)(Math.random() * maxSkewHor) + minSkewHor;
int maxVerticalLine = 5;
int minVerticalLine = 2;
int lineToVerticalLine = (int)(Math.random() * maxVerticalLine) + minVerticalLine;
canvas.setLineWidth(2).setStrokeColor(new DeviceRgb(222, 27, 27));
// first horizontal line
CellRenderer[] cellRenderers = rows.get(0);
canvas.moveTo(cellRenderers[0].getOccupiedArea().getBBox().getLeft()-lineToHorizontalLine,
cellRenderers[0].getOccupiedArea().getBBox().getTop());
canvas.lineTo(cellRenderers[cellRenderers.length - 1].getOccupiedArea().getBBox().getRight()+lineToHorizontalLine,
cellRenderers[cellRenderers.length - 1].getOccupiedArea().getBBox().getTop());
for (int i = 0; i < rows.size(); i++) {
skewHorizontalLine = (int)(Math.random() * maxSkewHor) + minSkewHor;
lineToHorizontalLine = (int)(Math.random() * maxLineTo) + minLineTo;
cellRenderers = rows.get(i);
// horizontal lines
canvas.moveTo(cellRenderers[0].getOccupiedArea().getBBox().getX()-lineToHorizontalLine,
cellRenderers[0].getOccupiedArea().getBBox().getY()+skewHorizontalLine);
canvas.lineTo(cellRenderers[cellRenderers.length - 1].getOccupiedArea().getBBox().getRight()+lineToHorizontalLine,
cellRenderers[cellRenderers.length - 1].getOccupiedArea().getBBox().getBottom());
// first vertical line
Rectangle cellRect = cellRenderers[0].getOccupiedArea().getBBox();
canvas.moveTo(cellRect.getLeft(), cellRect.getBottom());
canvas.lineTo(cellRect.getLeft(), cellRect.getTop()+lineToVerticalLine );
// vertical lines
for (int j = 0; j < cellRenderers.length; j++) {
lineToVerticalLine = (int)(Math.random() * maxVerticalLine) + minVerticalLine;
cellRect = cellRenderers[j].getOccupiedArea().getBBox();
canvas.moveTo(cellRect.getRight(), cellRect.getBottom()-lineToVerticalLine);
canvas.lineTo(cellRect.getRight(), cellRect.getTop()+lineToVerticalLine); //ячейки
}
}
canvas.stroke();
}
}
I want to draw custom borders by myself :)
First of all, we need to override the renderer correctly, i.e. override
getNextRenderer()
method. CurrentlyTableRenderer
is quite problematic to override because the parameterless constructor ofTableRenderer
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:Disclaimer: since the answer uses private implementation details of
TableRenderer
it may not work in the future. It works with7.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 containsheights
(line in code) andcountedColumnWidth
(line in code) fields which sound interesting, but they areprivate
. It means that we should create our custom fork of iText (source code available at https://github.com/itext/itext7), make those fieldsprotected
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.To activate the custom renderer, set it to the table just before adding to the document:
This is how the resultant table looks like: