Change AcroFields order in existing PDF with iText?

人盡茶涼 提交于 2019-12-13 15:13:28

问题


I have a pdf with text form fields at are layered one on top of the other. When I fill the fields via iText and flatten the form, the form field that I had created on top of the other form field is now on the bottom.

For instance, I have a text field named "number_field" and that is underneath a second text field that is titled "name_field". When I set the value for those fields via iText (so 10 for number_field and 'John' for name_field), the number_field is now on top of the name_field.

How do I change the order on the page of these fields with iText? Is it possible?

Link to example PDF: https://freecompany.sharefile.com/d-s84f6d63e7d04fe79


回答1:


I have made the following ticket in the issue tracker at iText Group:

A problem is caused by the fact that iText reads the field items into a HashMap, hence there is no way to predict in which order they will be flattened. This usually isn't a problem. I don't think this problem occurs in case you don't flatten the PDF, because in that case, the appearance is stored in the widget annotations and it's up to the PDF viewer to decide which field covers another one in case of overlapping fields.

However, if form fields overlap, then you can't predict which field will cover which when flattening.

Suppose that we'd use a TreeMap instead of a HashMap, would this solve the problem? Not really, because which Comparator would we use? Sometimes a Tab-order is defined, but not always. If it's not defined, should we order the fields in the order in which they appear in the /Fields array? Or does it make more sense to order them based on the order of the widget annotations in the /Annots array? Another option is to order them based on their position on the page. In short: this is not a decision iText should make.

However, if somebody would like to solve this problem, we could create a Comparator member variable for PdfStamperImp. If such a Comparator is provided (we could even provide some implementations), then the flattening process would be executed in the order defined by the Comparator.

This ticket has received a very low priority (I assume that you're not a customer of one of the iText Software companies), but while writing this ticket, I had another idea.

I already referred to underline portion of text using iTextSharp in the comments. In this case, you'd get all the field positions (using the getFieldPositions() method) and draw all the contents in the right order using ColumnText. This approach has several disadvantages: in order for the font, font size, font color to be correct, you'd have to examine the fields. That requires some programming.

I am now posting this as an answer, because I have a much better alternative: fill out the form in two passes! This is shown in the FillFormFieldOrder example. We fill out the form src resulting in the flattened form dest like this:

public void manipulatePdf(String src, String dest) throws DocumentException, IOException {
    go2(go1(src), dest);
}

As you can see, we execute the go1() method first:

public byte[] go1(String src) throws IOException, DocumentException {
    PdfReader reader = new PdfReader(src);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    PdfStamper stamper = new PdfStamper(reader, baos);
    AcroFields form = stamper.getAcroFields();
    form.setField("sunday_1", "1");
    form.setField("sunday_2", "2");
    form.setField("sunday_3", "3");
    form.setField("sunday_4", "4");
    form.setField("sunday_5", "5");
    form.setField("sunday_6", "6");
    stamper.setFormFlattening(true);
    stamper.partialFormFlattening("sunday_1");
    stamper.partialFormFlattening("sunday_2");
    stamper.partialFormFlattening("sunday_3");
    stamper.partialFormFlattening("sunday_4");
    stamper.partialFormFlattening("sunday_5");
    stamper.partialFormFlattening("sunday_6");
    stamper.close();
    reader.close();
    return baos.toByteArray();
}

This fills out all the sunday_x fields and uses partial form flattening to flatten only those fields. The go1() method takes src as parameter and returns a byte[] will the partially flattened form.

The byte[] will be used as a parameter for the go2() method, that takes dest as its second parameter. Now we are going to fill out the sunday_x_notes fields:

public void go2(byte[] src, String dest) throws IOException, DocumentException {
    PdfReader reader = new PdfReader(src);
    PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
    AcroFields form = stamper.getAcroFields();
    form.setField("sunday_1_notes", "It's Sunday today, let's go to the sea");
    form.setField("sunday_2_notes", "It's Sunday today, let's go to the park");
    form.setField("sunday_3_notes", "It's Sunday today, let's go to the beach");
    form.setField("sunday_4_notes", "It's Sunday today, let's go to the woods");
    form.setField("sunday_5_notes", "It's Sunday today, let's go to the lake");
    form.setField("sunday_6_notes", "It's Sunday today, let's go to the river");
    stamper.setFormFlattening(true);
    stamper.close();
    reader.close();
}

As you can see, we now flatten all the fields. The result looks like this:

Now, you no longer have to worry about the order of the fields, not in the /Fields array, not in the /Annots array. The fields are filled out in the exact order you want to. The notes cover the dates now, instead of the other way round.



来源:https://stackoverflow.com/questions/29633035/change-acrofields-order-in-existing-pdf-with-itext

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!