I am drawing the table with round corners. The table is dynamic and can grow to multiple pages. Now I want to apply some background color to cells. Each cell can have its own different color (not hard coded). But the issue is when I apply background color to corner cells, the color spills out from corners. The corner cells have their outer corners round. The implementation of round corner is done using this method https://stackoverflow.com/a/62764267/13446374 .
For implementing this I created a cell renderer which overrides the drawBackground() method to draw the expected results.
CellBackgroundColorRenderer.java
public class CellBackgroundColorRenderer extends CellRenderer {
protected Color color;
protected boolean isColoredBackground;
public CellBackgroundColorRenderer(Cell modelElement, Color color, boolean isColoredBackground) {
super(modelElement);
this.color = color;
this.isColoredBackground = isColoredBackground;
}
@Override
public void drawBackground(DrawContext drawContext) {
Rectangle rect = getOccupiedAreaBBox();
PdfCanvas canvas = drawContext.getCanvas();
// drawing white rectangle on top any pre existing background color.
canvas.saveState().rectangle(rect).setFillColor(new DeviceRgb(255, 255, 255)).fillStroke().restoreState();
// drawing round corner rectangle for colored bg.
canvas.saveState().roundRectangle(rect.getLeft(),rect.getBottom(), rect.getWidth(), rect.getHeight(), 5).setFillColor(color).fillStroke();
// TODO fill color in those cell corners which are in conjuntion with other cells.
canvas.restoreState();
}
}
TableBorderRenderer.java
public class TableBorderRenderer extends TableRenderer {
private int numberOfColumns;
public TableBorderRenderer(Table modelElement) {
super(modelElement);
numberOfColumns = modelElement.getNumberOfColumns();
}
@Override
public IRenderer getNextRenderer() {
return new TableBorderRenderer((Table) modelElement);
}
@Override
public void draw(DrawContext drawContext) {
CellRenderer[] cellRenderers = rows.get(0);
Cell cell = (Cell) cellRenderers[0].getModelElement();
((Cell) cellRenderers[0].getModelElement()).setNextRenderer(new CellBackgroundColorRenderer((Cell) cellRenderers[0].getModelElement(), new DeviceRgb(255, 150, 255), true));
((Cell) cellRenderers[numberOfColumns-1].getModelElement()).setNextRenderer(new CellBackgroundColorRenderer((Cell) cellRenderers[numberOfColumns-1].getModelElement(), new DeviceRgb(255, 150, 255), true));
super.draw(drawContext);
}
@Override
protected void drawBorders(DrawContext drawContext) {
Rectangle rect = getOccupiedAreaBBox();
PdfPage currentPage = drawContext.getDocument().getPage(getOccupiedArea().getPageNumber());
PdfCanvas aboveCanvas = new PdfCanvas(currentPage.newContentStreamAfter(), currentPage.getResources(), drawContext.getDocument());
float lineWidth = 0.5f;
rect.applyMargins(lineWidth / 2, lineWidth / 2, lineWidth / 2, lineWidth / 2, false);
aboveCanvas.saveState().setStrokeColor(new DeviceRgb(255,255,255)).rectangle(rect).stroke().restoreState();
aboveCanvas.saveState().setLineWidth(0.5f).setStrokeColor(new DeviceRgb(255,0,0)).roundRectangle(rect.getLeft(), rect.getBottom(), rect.getWidth(), rect.getHeight(), 5).stroke().restoreState();
super.drawBorders(drawContext);
}
}
TableTest.java
public void createPdf(String dest) throws FileNotFoundException {
PdfWriter writer = new PdfWriter(DEST);
PdfDocument pdfDoc = new PdfDocument(writer);
Document document = new Document(pdfDoc, PageSize.A4, false);
Table table = new Table(3);
for (int i=0; i < 100; i++) {
for (int j=0; j < 3; j++) {
table.addCell(new Cell().add(new Paragraph("Cell content")));
}
}
table.setNextRenderer(new TableBorderRenderer(table));
document.add(table);
document.close();
}
In draw() of TableBorderRenderer I am setting the CellBackgroundColorRenderer to the modal element, i.e. cell. For now, I am setting the CellBackgroudnColorRenderer for first row only. But when I run the program, it doesn't even go in that drawBackground() of cellRenderer.
I am not sure what I am missing here. Is my approach correct? If not, can you guide me with the correct approach?
Thanks.
No need to manually draw the background color for cells - the functionality is already there:
Table table = new Table(3);
for (int i=0; i < 100; i++) {
for (int j=0; j < 3; j++) {
table.addCell(new Cell()
.setBackgroundColor(ColorConstants.GREEN)
.add(new Paragraph("Cell content")));
}
}
table.setNextRenderer(new TableBorderRenderer(table));
document.add(table);
The only thing you need to do is to clip those backgrounds that are drawn outside of the rounded corners. You can overload drawChildren
in TableRenderer
for that:
public class TableBorderRenderer extends TableRenderer {
public TableBorderRenderer(Table modelElement) {
super(modelElement);
}
@Override
public IRenderer getNextRenderer() {
return new TableBorderRenderer((Table) modelElement);
}
@Override
protected void drawBorders(DrawContext drawContext) {
Rectangle rect = getOccupiedAreaBBox();
PdfPage currentPage = drawContext.getDocument().getPage(getOccupiedArea().getPageNumber());
PdfCanvas aboveCanvas = new PdfCanvas(currentPage.newContentStreamAfter(), currentPage.getResources(), drawContext.getDocument());
float lineWidth = 0.5f;
rect.applyMargins(lineWidth, lineWidth, lineWidth, lineWidth, false);
aboveCanvas.saveState().setLineWidth(lineWidth).setStrokeColor(new DeviceRgb(255,255,255)).rectangle(rect).stroke().restoreState();
aboveCanvas.saveState().setLineWidth(lineWidth).setStrokeColor(new DeviceRgb(255,0,0)).roundRectangle(rect.getLeft(), rect.getBottom(), rect.getWidth(), rect.getHeight(), 5).stroke().restoreState();
super.drawBorders(drawContext);
}
@Override
public void drawChildren(DrawContext drawContext) {
Rectangle rect = getOccupiedAreaBBox();
float lineWidth = 0.5f;
rect.applyMargins(lineWidth, lineWidth, lineWidth, lineWidth, false);
PdfCanvas canvas = drawContext.getCanvas();
canvas.saveState();
canvas.roundRectangle(rect.getLeft(), rect.getBottom(), rect.getWidth(), rect.getHeight(), 5);
canvas.clip().endPath();
super.drawChildren(drawContext);
canvas.restoreState();
}
}
Visual result: