Reputation: 33
I would like to use itext7 to generate something like this pdf document:
But i can't find any method to achieve that. What I see in the tutorial iText - clickable image should open ms word attachment can not place attachments and text together. You can only place attachments on a separate page. If it is itext5, i do like this:
PdfAnnotation anno = PdfAnnotation.createFileAttachment(writer, null, fileDescribe, pdfFileSpecification);
chunk.setAnnotation(anno);
paragrah.add(chunk);
So I can add text、annotation in one paragraph, But itext7 tutorial is the case:
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(DEST));
Rectangle rect = new Rectangle(36, 700, 100, 100);
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());
pdfDoc.addNewPage().addAnnotation(attachment);
pdfDoc.close();
Can someone help me?
Upvotes: 2
Views: 1304
Reputation: 33
Thank you for your reply @Alexey Subach, by using your method, I solved my own problems. Because I use the 7.0.2-SNAPSHOT version. So in the AnnotationRenderer class made a little change:
public class AnnotationRenderer extends AbstractRenderer implements IRenderer {
private PdfAnnotation annotation;
public AnnotationRenderer(PdfAnnotation annotation) {
this.annotation = annotation;
}
public float getAscent(){
return annotation.getRectangle().toRectangle().getHeight();
}
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 IRenderer getNextRenderer() {
return new AnnotationRenderer(annotation);
}
@Override
public void draw(DrawContext drawContext) {
super.draw(drawContext);
annotation.setRectangle(new PdfArray(occupiedArea.getBBox()));
drawContext.getDocument().getPage(occupiedArea.getPageNumber()).addAnnotation(annotation);
}
}
I just touched itext soon, just encountered this problem when I feel that they may not be able to solve, but fortunately got your help. In fact, LayoutContext, occupiedArea, LayoutResult these classes do not understand for me, I think that I need look at the API to learn more.
Upvotes: 1
Reputation: 12312
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.
Upvotes: 2