Search code examples
itext7

How can I add an PdfFormField using IText 7 at the current page position


We have been able to add a PdfFormField at a specific location on the page using the following Scala code snippet.

val form = PdfAcroForm.getAcroForm(document.getPdfDocument(), true)
val nameField = PdfFormField.createText(document.getPdfDocument(), new Rectangle(data.x, data.y, data.width, data.height), data.formName, data.formText)
form.addField(nameField)

However, what we would like to be able to do is add it after the last Paragraph on the page that we inserted. (i.e. This field just comes directly after). Is there a way that we can derive the proper rectangle, or is there an easier way? Thanks


Solution

  • There is currently no out-of-the-box way to add fields to layout, but iText team is considering implementing this functionality.

    Meanwhile, there are easy ways to achieve your goal, and there are a few of them.

    My examples will be in Java, but I assume you will easily be able to use them in Scala.

    The first approach is just to get the bottom position of the paragraph you have added and add your field with respect to that position. The bottom position of the last paragraph happens to be the top position of the rest of available box of content on the page (area), which converts to the following code:

    Document doc = new Document(pdfDoc);
    doc.add(new Paragraph("This is a paragraph.\nForm field will be inserted after it"));
    
    Rectangle freeBBox = doc.getRenderer().getCurrentArea().getBBox();
    float top = freeBBox.getTop();
    float fieldHeight = 20;
    PdfTextFormField field = PdfFormField.createText(pdfDoc,
                new Rectangle(freeBBox.getLeft(), top - fieldHeight, 100, fieldHeight), "myField", "Value");
    form.addField(field);
    

    The part you are interested in is

    Rectangle freeBBox = doc.getRenderer().getCurrentArea().getBBox();
    

    which gives you the rectangle where the content is not placed yet.

    Note, however, that this approach will not affect the following paragraphs after you the one after which you want to add the form field, which means this form field and the content might overlap.

    Do deal with this situation, you might want to take advantage of possibility to create custom layout elements in iText7.

    Which, in turn, is converted to the following code:

    private static class TextFieldRenderer extends DivRenderer {
        public TextFieldRenderer(TextFieldLayoutElement modelElement) {
            super(modelElement);
        }
    
        @Override
        public void draw(DrawContext drawContext) {
            super.draw(drawContext);
            PdfAcroForm form = PdfAcroForm.getAcroForm(drawContext.getDocument(), true);
            PdfTextFormField field = PdfFormField.createText(drawContext.getDocument(),
                    occupiedArea.getBBox(), "myField2", "Another Value");
            form.addField(field);
        }
    }
    
    private static class TextFieldLayoutElement extends Div {
        @Override
        public IRenderer getRenderer() {
            return new TextFieldRenderer(this);
        }
    }
    

    Then you will just need to add the elements in a fancy way:

    doc.add(new Paragraph("This is another paragraph.\nForm field will be inserted right after it."));
    doc.add(new TextFieldLayoutElement().setWidth(100).setHeight(20));
    doc.add(new Paragraph("This paragraph follows the form field"));
    

    In short, what we have done here is that we created a custom dummy Div element (which is an analogue of HTML's div), which will occupy area during laying out, but we defined custom #draw() operator for this element so that form field is inserted right when we know the exact position where we want to do it.

    You can find the complete code of the sample here. Note, however, that the link may change as samples repository is under reorganization now.