If I understood correctly, you want to add an annotation among other layout elements to your document flow.
Currently in iText7
there is no quick way to achieve it (like setAnnotation
method in iText5
). However, iText7
is flexible enough to allow you to create custom elements and not dig very deeply into the code.
The initial part will be the same as in the current example. Here an annotation itself is being set up:
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(DEST));
Rectangle rect = new Rectangle(36, 700, 50, 50);
PdfFileSpec fs = PdfFileSpec.createEmbeddedFileSpec(pdfDoc, PATH, null, "test.docx", null, null, false);
PdfAnnotation attachment = new PdfFileAttachmentAnnotation(rect, fs)
.setContents("Click me");
PdfFormXObject xObject = new PdfFormXObject(rect);
ImageData imageData = ImageDataFactory.create(IMG);
PdfCanvas canvas = new PdfCanvas(xObject, pdfDoc);
canvas.addImage(imageData, rect, true);
attachment.setNormalAppearance(xObject.getPdfObject());
Then, what we want to achieve is being able to add custom annotation elements to the layout Document
flow.
In the best case, the code would look like this:
Document document = new Document(pdfDoc);
Paragraph p = new Paragraph("There are two").add(new AnnotationElement(attachment)).add(new Text("elements"));
document.add(p);
document.close();
Now we are left with defining the AnnotationElement
and corresponding renderer. Element itself does not have any specific logic in it apart from creating custom renderer and passing an annotation to it:
private static class AnnotationElement extends AbstractElement<AnnotationElement> implements ILeafElement {
private PdfAnnotation annotation;
public AnnotationElement(PdfAnnotation annotation) {
this.annotation = annotation;
}
@Override
protected IRenderer makeNewRenderer() {
return new AnnotationRenderer(annotation);
}
}
The renderer implementation has more code but what it that is simply defines the occupied area on layout
and adds the annotation to the page on draw
. Please note that it does not cover all the cases (e.g. there is not enough space left to fit an annotation), but this is more than enough for simple cases and demonstration purposes.
private static class AnnotationRenderer extends AbstractRenderer implements ILeafElementRenderer {
private PdfAnnotation annotation;
public AnnotationRenderer(PdfAnnotation annotat) {
this.annotation = annotat;
}
@Override
public float getAscent() {
return annotation.getRectangle().toRectangle().getHeight();
}
@Override
public float getDescent() {
return 0;
}
@Override
public LayoutResult layout(LayoutContext layoutContext) {
occupiedArea = layoutContext.getArea().clone();
float myHeight = annotation.getRectangle().toRectangle().getHeight();
float myWidth = annotation.getRectangle().toRectangle().getWidth();
occupiedArea.getBBox().moveUp(occupiedArea.getBBox().getHeight() - myHeight).setHeight(myHeight);
occupiedArea.getBBox().setWidth(myWidth);
return new LayoutResult(LayoutResult.FULL, occupiedArea, null, null);
}
@Override
public void draw(DrawContext drawContext) {
super.draw(drawContext);
annotation.setRectangle(new PdfArray(occupiedArea.getBBox()));
drawContext.getDocument().getPage(occupiedArea.getPageNumber()).addAnnotation(annotation);
}
@Override
public IRenderer getNextRenderer() {
return new AnnotationRenderer(annotation);
}
}
Please note that the example is for current 7.0.3-SNAPSHOT
version. Might not work for the release 7.0.2
version yet because ILeafElementRenderer
interface was added later, but still it would be possible to adapt the code to 7.0.2
if really needed.