I want to know the coordinates of the text that is added to the page. And apply some canvas to this text. (for example custom underline or strikethrough or text inside the tria
In iText7 you implement such tasks using a custom Renderer
. The technique is shown in the DashedUnderline example:
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(DEST));
Document doc = new Document(pdfDoc);
doc.add(new Paragraph("This text is not underlined"));
Text text1 = new Text("This text is underlined with a solid line");
text1.setUnderline(1, -3);
doc.add(new Paragraph(text1));
Text text2 = new Text("This text is underlined with a dashed line");
text2.setNextRenderer(new DashedLineTextRenderer(text2));
doc.add(new Paragraph(text2));
doc.close();
i.e. you merely set a custom Renderer
to the Text
bit in question. In the example at hand the custom Renderer
class is
protected class DashedLineTextRenderer extends TextRenderer {
public DashedLineTextRenderer(Text textElement) {
super(textElement);
}
@Override
public void draw(DrawContext drawContext) {
super.draw(drawContext);
Rectangle rect = this.getOccupiedAreaBBox();
PdfCanvas canvas = drawContext.getCanvas();
canvas
.saveState()
.setLineDash(3, 3)
.moveTo(rect.getLeft(), rect.getBottom() - 3)
.lineTo(rect.getRight(), rect.getBottom() - 3)
.stroke()
.restoreState();
}
}
As you see you can override draw
to first call the super
implementation to have the text drawn normally. After that you can retrieve the area used for drawing the text by calling getOccupiedAreaBBox
and use it for your task, be it for decorating the text or merely storing the position somewhere.
If you wonder why this example is in the events
sub-package... the example corresponds to an iText5 example, and in iText5 you implement such a task by means of setting a generic tag to the Chunk
in question and listening for that generic tag in the onGenericTag
method of a page event listener, see this answer for an example.
In a comment you asked
Can i do it with table? (draw border with help canvas)
Yes, again you use the associated renderers for that, e.g. see the DottedLineCell example:
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, 2)));
table.addCell(new Cell().add(new Paragraph("A1")).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));
table.addCell(new Cell().add(new Paragraph("B1")).setBorder(Border.NO_BORDER));
table.addCell(new Cell().add(new Paragraph("B2")).setBorder(Border.NO_BORDER));
table.addCell(new Cell().add(new Paragraph("B3")).setBorder(Border.NO_BORDER));
table.addCell(new Cell().add(new Paragraph("C1")).setBorder(Border.NO_BORDER));
table.addCell(new Cell().add(new Paragraph("C2")).setBorder(Border.NO_BORDER));
table.addCell(new Cell().add(new Paragraph("C3")).setBorder(Border.NO_BORDER));
doc.add(table);
doc.add(new Paragraph("Cell event"));
table = new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth();
Cell cell = new Cell().add(new Paragraph("Test"));
cell.setNextRenderer(new DottedLineCellRenderer(cell));
cell.setBorder(Border.NO_BORDER);
table.addCell(cell.setBorder(Border.NO_BORDER));
doc.add(table);
doc.close();
Here the first table shows how to do this using the table renderer after turning off standard table cell borders while the second table shows how to do this using the cell renderers.
The customized renderer classes are
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();
canvas.setLineDash(3f, 3f);
// first horizontal line
CellRenderer[] cellRenderers = rows.get(0);
canvas.moveTo(cellRenderers[0].getOccupiedArea().getBBox().getLeft(),
cellRenderers[0].getOccupiedArea().getBBox().getTop());
canvas.lineTo(cellRenderers[cellRenderers.length - 1].getOccupiedArea().getBBox().getRight(),
cellRenderers[cellRenderers.length - 1].getOccupiedArea().getBBox().getTop());
for (int i = 0; i < rows.size(); i++) {
cellRenderers = rows.get(i);
// horizontal lines
canvas.moveTo(cellRenderers[0].getOccupiedArea().getBBox().getX(),
cellRenderers[0].getOccupiedArea().getBBox().getY());
canvas.lineTo(cellRenderers[cellRenderers.length - 1].getOccupiedArea().getBBox().getRight(),
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());
// vertical lines
for (int j = 0; j < cellRenderers.length; j++) {
cellRect = cellRenderers[j].getOccupiedArea().getBBox();
canvas.moveTo(cellRect.getRight(), cellRect.getBottom());
canvas.lineTo(cellRect.getRight(), cellRect.getTop());
}
}
canvas.stroke();
}
}
and
private class DottedLineCellRenderer extends CellRenderer {
public DottedLineCellRenderer(Cell modelElement) {
super(modelElement);
}
@Override
public void draw(DrawContext drawContext) {
super.draw(drawContext);
drawContext.getCanvas().setLineDash(3f, 3f);
drawContext.getCanvas().rectangle(this.getOccupiedArea().getBBox());
drawContext.getCanvas().stroke();
}
}
respectively.
As explained by Alexey Subach in this answer, a complete customization of a Renderer
should also override the getNextRenderer()
method. In particular if an area break might occur in the object in question, this is necessary, otherwise the customization only works in the first area as the OP has observed.