Appearance issues with pdf interactive forms using

2019-08-15 03:29发布

问题:

I have been following the 'Chapter 8: Filling out interactive forms' iText example (code and pdfs at http://itextpdf.com/examples/iia.php?id=154) and have noticed a number of appearance-related issues which also manifest themselves in the example pdfs.

The example features a list of radio buttons (classic dot within a circle) and a list of checkboxes (crossed box with a red background). So far so good. However the pdf generated after programmatically filling in these fields is visually inconsistent. The radio buttons have all reverted to squares and the selected ('Spanish') has a wingdings (?) cross within it rather than a dot. The fields are all still editable. When I select a different radio button, the previously selected radio button reverts to an empty circle. The newly selected field has changed from an empty square to a dotted circle. This can also be observed with the checkboxes.

Is there any way to fill the fields whilst preserving their original appearances?

Thanks!

EDIT - Here's a simplification of the code I use :

public void buildPdf() throws Exception {

    Document document = new Document(PageSize.A4);

    File file = new File("/test.pdf");
    final PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(file));

    document.open();

    PdfPCell cell = new PdfPCell();

    PdfRadioGroupTable group = new PdfRadioGroupTable("ndna", Arrays.asList("Not Available", "Not Applicable"), writer, Locale.ENGLISH, DEFAULT);

    cell.addElement(group);

    PdfPTable table = new PdfPTable(1);

    table.addCell(cell);

    cell = new PdfPCell();      

    cell.setCellEvent(new ActionEvent(writer, "http://localhost:8080/entrypointpdf"));

    table.addCell(cell);

    document.add(table);

    group.addAnnotations();

    document.close();
}

Here's the ActionEvent

class ActionEvent implements PdfPCellEvent {

        private PdfWriter writer;
        private String submissionUrl;

        public ActionEvent(PdfWriter writer, String submissionUrl) {
            this.writer = writer;
            this.submissionUrl = submissionUrl;
        }

        @Override
        public void cellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases) {

            PushbuttonField submit = new PushbuttonField(writer, position, "submit") {

                @Override
                public PdfFormField getField() throws IOException, DocumentException {

                    PdfFormField field = super.getField();

                    field.setAction(PdfAction.createSubmitForm(submissionUrl, null, 0));

                    return field;
                }
            };
            submit.setText("CLICK ME");
            submit.setTextColor(BaseColor.BLUE);
            submit.setFont(DEFAULT.getCalculatedBaseFont(false));
            submit.setVisibility(PushbuttonField.VISIBLE);

            try {
                writer.addAnnotation(submit.getField());
            } catch (Exception ignore) {
            }
        }
    }

Here's the PdfRadioGroupTable

public class PdfRadioGroupTable extends PdfPTable {

    private PdfWriter writer;
    public PdfFormField radioGroup;

    public PdfRadioGroupTable(String name, List<String> choices, PdfWriter writer, Locale locale, Font font) {
        super(2);

        this.writer = writer;

        setWidthPercentage(100);
        try {
            setTotalWidth(new float[] { 14, 400 });
        } catch (DocumentException ignore) {
        }

        radioGroup = PdfFormField.createRadioButton(writer, true);
        radioGroup.setFieldName(name);

        PdfPCell cell;

        for (String choice : choices) {
            cell = new PdfPCell();

            cell.setBorder(0);
            cell.setPaddingBottom(5);

            cell.setUseAscender(true);
            cell.setUseDescender(true);
            cell.setVerticalAlignment(PdfPCell.ALIGN_CENTER);

            cell.setCellEvent(new RadioCellEvent(radioGroup, choice));

            addCell(cell);

            cell = new PdfPCell();

            cell.setBorder(0);

            cell.setPaddingBottom(5);
            cell.setPaddingLeft(6);

            cell.setUseAscender(true);
            cell.setUseDescender(true);
            cell.setVerticalAlignment(PdfPCell.ALIGN_CENTER);

            cell.addElement(new Phrase(choice), font));

            addCell(cell);
        }
    }

    class RadioCellEvent implements PdfPCellEvent {

        protected PdfFormField radioGroup;
        protected String value;

        public RadioCellEvent(PdfFormField radioGroup, String value) {
            this.radioGroup = radioGroup;
            this.value = value;
        }

        @Override
        public void cellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases) {

            RadioCheckField radio = new RadioCheckField(writer, position, null, value);

            radio.setChecked(false);

            radio.setBorderColor(GrayColor.GRAYBLACK);
            radio.setBackgroundColor(GrayColor.GRAYWHITE);
            radio.setCheckType(RadioCheckField.TYPE_CIRCLE);

            try {
                PdfFormField field = radio.getRadioField();

                writer.addAnnotation(field);
                radioGroup.addKid(field);

            } catch (Exception exception) {
                throw new ExceptionConverter(exception);
            }
        }
    }
}   

And here's what happens on the server side when one clicks the button in the pdf (in acrobat) :

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        response.setContentType("application/pdf");
        response.setHeader("Content-Disposition", "inline; filename=\"your.pdf\"");

        try {
            FdfReader fdf = new FdfReader(request.getInputStream());

            File pdfFile = new File("/test.pdf");

            InputStream is = new FileInputStream(pdfFile);

            PdfReader reader = new PdfReader(is);

            File resultFile = new File(System.currentTimeMillis() + "_" + pdfFile.getName());
            FileOutputStream out = new FileOutputStream(resultFile);

            PdfStamper stamper = new PdfStamper(reader, out);

            AcroFields fields = stamper.getAcroFields();
            fields.setFields(fdf);

            // EXTRACT VALUES HERE                                  

            stamper.setFormFlattening(true);

            stamper.close();
            reader.close();

            ByteArrayOutputStream bos = new ByteArrayOutputStream();

            bos.write(FilesUtilities.read(resultFile));

            OutputStream os = response.getOutputStream();
            bos.writeTo(os);
            os.flush();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

So basically I generate a pdf with one radiogroup and a button. The button results in the pdf fields being imported and the idea is to send a 'receipt' back to the user in the form of the previously generated pdf containing the same fields with the user selection/values and import status fields (ok, missing, bad data, etc.) (which I have omitted in the above code as that part works fine). My problem is that the radio fields in the returned pdf don't look remotely like the editable ones in the original pdf (they don't even look like radio buttons anymore).

I tried using

fields.setFieldProperty("ndna", "setfflags", PdfFormField.FF_READ_ONLY, null);

and disabling form flattening by this results in the same alternate appearance in the returned pdf.

Your help would be most appreciated as I've been struggling with this for a while now...

Anthony

回答1:

This seems to be a bug on some versions of iText. I had the same problem with iTextSharp version 5.5.5 and it was solved after I upgraded to version 5.5.9. I used "nuget" to do this. Here's a link: https://www.nuget.org/packages/iTextSharp/5.5.9