iText - Java Android - Adding fields to existing p

2019-08-11 01:06发布

问题:

I'm trying to add interactive fields to an existing PDF, i use a PdfReader and a PdfStamper to do this, and when i open it on my tablet, everything is ok. However, when I want to open it on my computer, there are no fields.

This is a sample of my code, I have more than one field for each page but i only printed two fields here:

public class SelfNoteFragment extends Fragment {
    private PdfStamper pdfStamper;

    class MyCellField implements PdfPCellEvent {
        protected String fieldname;
        protected int page;
        public MyCellField(String fieldname, int page) {
            this.fieldname = fieldname;
            this.page = page;
        }
        public void cellLayout(PdfPCell cell, Rectangle rectangle, PdfContentByte[] canvases) {
            final PdfWriter writer = canvases[0].getPdfWriter();
            final TextField textField = new TextField(writer, rectangle, fieldname);
            try {
                final PdfFormField field = textField.getTextField();
                pdfStamper.addAnnotation(field, page);
            } catch (final IOException ioe) {
                throw new ExceptionConverter(ioe);
            } catch (final DocumentException de) {
                throw new ExceptionConverter(de);
            }
        }
    }

    class CheckboxCellEvent implements PdfPCellEvent {
        protected String name;
        protected boolean check;
        protected int page;

        public CheckboxCellEvent(String name, boolean check, int page) {
            this.check = check;
            this.name = name;
            this.page = page;
        }

        public void cellLayout(PdfPCell cell, Rectangle position,
                               PdfContentByte[] canvases) {
            PdfWriter writer = canvases[0].getPdfWriter();
            float x = position.getLeft();
            float y = position.getBottom();
            Rectangle rect = new Rectangle(x-5, y-5, x+5, y+5);
            RadioCheckField checkbox = new RadioCheckField(
                    writer, rect, name, "Yes");
            checkbox.setCheckType(RadioCheckField.TYPE_CROSS);
            checkbox.setChecked(check);
            try {
                pdfStamper.addAnnotation(checkbox.getCheckField(), page);
            } catch (Exception e) {
                throw new ExceptionConverter(e);
            }
        }
    }

    private void createPdf(int idPrevision) throws FileNotFoundException, DocumentException {

        try {
            PdfReader pdfReader = new PdfReader("/storage/emulated/0/Documents/fiche_chantier2.pdf");

            //Create time stamp
            Date date = new Date() ;
            String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(date);

            File pdfFolder = new File(Environment.getExternalStoragePublicDirectory(
                    Environment.DIRECTORY_DOCUMENTS), "Prevision_");
            if (!pdfFolder.exists()) {
                pdfFolder.mkdir();
            }

            File myFile = new File(pdfFolder + timeStamp + ".pdf");

            OutputStream output = new FileOutputStream(myFile);

            this.pdfStamper = new PdfStamper(pdfReader, output);

            PdfContentByte canvas1;
            PdfContentByte canvas2;

            canvas1 = pdfStamper.getOverContent(1);
            canvas2 = pdfStamper.getOverContent(2);

            PdfPCell cellFillFieldPage1 = new PdfPCell();
            cellFillFieldPage1.setCellEvent(new MyCellField("", 1));
            cellFillFieldPage1.setFixedHeight(15);
            cellFillFieldPage1.setBorder(Rectangle.NO_BORDER);
            cellFillFieldPage1.setVerticalAlignment(Element.ALIGN_MIDDLE);

            PdfPCell cellCheckBoxPage2 = new PdfPCell();
            cellCheckBoxPage2.setCellEvent(new CheckboxCellEvent("", false, 2));
            cellCheckBoxPage2.setBorder(Rectangle.NO_BORDER);

            // ************** PAGE 1 ************** //

            // SET TABLE
            PdfPTable tableSection1Page1 = new PdfPTable(1);
            tableSection1Page1.setTotalWidth(136);
            tableSection1Page1.setWidthPercentage(100.0f);
            tableSection1Page1.setLockedWidth(true);

            // ADD CELLS TO TABLE
            tableSection1Page1.addCell(cellFillFieldPage1);


            // PRINT TABLES
            tableSection1Page1.writeSelectedRows(0, -1, 165, 730, canvas1);


            // ************ PAGE 2 ************ //

            // SET TABLES
            PdfPTable tableSection1Page2 = new PdfPTable(1);
            tableSection1Page2.setTotalWidth(10);
            tableSection1Page2.setWidthPercentage(100.0f);
            tableSection1Page2.setLockedWidth(true);

            // ADD CELLS TO TABLE
            tableSection1Page2.addCell(cellCheckBoxPage2);

            // PRINT TABLES
            tableSection1Page2.writeSelectedRows(0, -1, 182, 736, canvas2);

            // I tried this, but it didn't change anything
            pdfStamper.setFormFlattening(false);

            pdfStamper.close();
            pdfReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        }
     }
}

Do you have an idea why my created PDF is good on my Tablet and not on my Computer (same when i want to send it by mail)? If so, do you know how to manage this problem?

Thank you, Corentin

回答1:

There actually are two issues in your code,

  1. you create the fields with empty names:

    cellFillFieldPage1.setCellEvent(new MyCellField("", 1));
    ...
    cellCheckBoxPage2.setCellEvent(new CheckboxCellEvent("", false, 2));
    

    First of all fields need to have a name. And furthermore, even if the empty name was an allowed choice for a name, two fields with the same name are considered to be two representations of the same abstract field which hardly works out if one is a text field and the other a checkbox field.

    As soon as you name those fields with non-empty, different names, you'll see that you have fields on Adobe Reader DC/Win: You can tab through them with the TAB key and change their values. Unfortunately, though, they are borderless and, therefore, hard to spot if empty. This is because

  2. you don't set border colors for your fields. iText, consequently, creates the form field appearance streams without borders.

    You can set the border color like this:

    ...
    final TextField textField = new TextField(writer, rectangle, fieldname);
    textField.setBorderColor(BaseColor.BLACK);
    ...
    RadioCheckField checkbox = new RadioCheckField(writer, rect, name, "Yes");
    checkbox.setBorderColor(BaseColor.BLACK);
    ...
    

    Having done so, you get the fields with black borders...


If you wonder why different viewers (even by the same company with the same name) behave different here...

  1. Some PDF viewers recognize early that they couldn't properly handle fields with empty names and, therefore, don't even show them while other viewers recognize that later when trying to save or post the form, or don't recognize it at all, producing broken outputs.

  2. PDF viewers are allowed to ignore appearance streams of form fields (actually of annotations in general) and create their own appearances; thus, in the case at hand they may ignore the borderless appearance generated by iText and draw one with a visible border.