< Android samples

InteractiveFormsTest - Java (Android)

The sample illustrates some basic PDFNet capabilities related to interactive forms (also known as AcroForms).

//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2018 by PDFTron Systems Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.
//---------------------------------------------------------------------------------------

package com.pdftron.android.pdfnetsdksamples.samples;

import com.pdftron.android.pdfnetsdksamples.OutputListener;
import com.pdftron.android.pdfnetsdksamples.PDFNetSample;
import com.pdftron.android.pdfnetsdksamples.R;
import com.pdftron.android.pdfnetsdksamples.util.Utils;
import com.pdftron.common.PDFNetException;
import com.pdftron.pdf.Action;
import com.pdftron.pdf.Annot;
import com.pdftron.pdf.ColorPt;
import com.pdftron.pdf.ColorSpace;
import com.pdftron.pdf.Element;
import com.pdftron.pdf.ElementBuilder;
import com.pdftron.pdf.ElementWriter;
import com.pdftron.pdf.Field;
import com.pdftron.pdf.FieldIterator;
import com.pdftron.pdf.FileSpec;
import com.pdftron.pdf.Font;
import com.pdftron.pdf.PDFDoc;
import com.pdftron.pdf.Page;
import com.pdftron.pdf.PageIterator;
import com.pdftron.pdf.Rect;
import com.pdftron.sdf.Obj;
import com.pdftron.sdf.SDFDoc;

import java.io.File;
import java.util.ArrayList;

//---------------------------------------------------------------------------------------
//This sample illustrates basic PDFNet capabilities related to interactive 
//forms (also known as AcroForms). 
//---------------------------------------------------------------------------------------

public class InteractiveFormsTest extends PDFNetSample {

	private static OutputListener mOutputListener;

	private static ArrayList<String> mFileList = new ArrayList<>();

    public InteractiveFormsTest() {
        setTitle(R.string.sample_interactiveforms_title);
        setDescription(R.string.sample_interactiveforms_description);
    }

	@Override
	public void run(OutputListener outputListener) {
		super.run(outputListener);
		mOutputListener = outputListener;
		mFileList.clear();
		printHeader(outputListener);

        // string input_path =  "../../TestFiles/";

        //----------------------------------------------------------------------------------
        // Example 1: Programatically create new Form Fields and Widget Annotations.
        //----------------------------------------------------------------------------------
        try {
            PDFDoc doc = new PDFDoc();
            Page blank_page = doc.pageCreate(); // Create a blank new page and add some form fields.

            // Create new fields.
            Field emp_first_name = doc.fieldCreate("employee.name.first",
                    Field.e_text, "John");
            Field emp_last_name = doc.fieldCreate("employee.name.last",
                    Field.e_text, "Doe");
            Field emp_last_check1 = doc.fieldCreate("employee.name.check1", Field.e_check, "Yes");

            Field submit = doc.fieldCreate("submit", Field.e_button);

            // Create page annotations for the above fields.

            // Create text annotations
            com.pdftron.pdf.annots.Widget annot1 = com.pdftron.pdf.annots.Widget.create(doc, new Rect(50, 550, 350, 600), emp_first_name);
            com.pdftron.pdf.annots.Widget annot2 = com.pdftron.pdf.annots.Widget.create(doc, new Rect(50, 450, 350, 500), emp_last_name);

            // Create a check-box annotation
            com.pdftron.pdf.annots.Widget annot3 = com.pdftron.pdf.annots.Widget.create(doc, new Rect(64, 356, 120, 410), emp_last_check1);
            // Set the annotation appearance for the "Yes" state...
            annot3.setAppearance(createCheckmarkAppearance(doc), Annot.e_normal, "Yes");

            // Create button annotation
            com.pdftron.pdf.annots.Widget annot4 = com.pdftron.pdf.annots.Widget.create(doc, new Rect(64, 284, 163, 320), submit);
            // Set the annotation appearances for the down and up state...
            annot4.setAppearance(createButtonAppearance(doc, false), Annot.e_normal);
            annot4.setAppearance(createButtonAppearance(doc, true), Annot.e_down);

            // Create 'SubmitForm' action. The action will be linked to the button.
            FileSpec url = FileSpec.createURL(doc, "http://www.pdftron.com");
            Action button_action = Action.createSubmitForm(url);

            // Associate the above action with 'Down' event in annotations action dictionary.
            Obj annot_action = annot4.getSDFObj().putDict("AA");
            annot_action.put("D", button_action.getSDFObj());

            blank_page.annotPushBack(annot1);  // Add annotations to the page
            blank_page.annotPushBack(annot2);
            blank_page.annotPushBack(annot3);
            blank_page.annotPushBack(annot4);

            doc.pagePushBack(blank_page);    // Add the page as the last page in the document.

            // If you are not satisfied with the look of default auto-generated appearance
            // streams you can delete "AP" entry from the Widget annotation and set
            // "NeedAppearances" flag in AcroForm dictionary:
            //    doc.GetAcroForm().PutBool("NeedAppearances", true);
            // This will force the viewer application to auto-generate new appearance streams
            // every time the document is opened.
            //
            // Alternatively you can generate custom annotation appearance using ElementWriter
            // and then set the "AP" entry in the widget dictionary to the new appearance
            // stream.
            //
            // Yet another option is to pre-populate field entries with dummy text. When
            // you edit the field values using PDFNet the new field appearances will match
            // the old ones.

            //doc.GetAcroForm().Put("NeedAppearances", new Bool(true));
            doc.refreshFieldAppearances();

            doc.save(Utils.createExternalFile("forms_test1.pdf").getAbsolutePath(), SDFDoc.SaveMode.NO_FLAGS, null);
            mFileList.add(new File(doc.getFileName()).getName());
            doc.close();
            mOutputListener.println("Done.");
        } catch (Exception e) {
            e.printStackTrace();
        }

        //----------------------------------------------------------------------------------
        // Example 2:
        // Fill-in forms / Modify values of existing fields.
        // Traverse all form fields in the document (and print out their names).
        // Search for specific fields in the document.
        //----------------------------------------------------------------------------------
        try {
            PDFDoc doc = new PDFDoc((Utils.createExternalFile("forms_test1.pdf").getAbsolutePath()));
            doc.initSecurityHandler();

            FieldIterator itr = doc.getFieldIterator();
            while (itr.hasNext()) {
                Field current = itr.next();
                mOutputListener.println("Field name: " + current.getName());
                mOutputListener.println("Field partial name: " + current.getPartialName());

                mOutputListener.print("Field type: ");
                int type = current.getType();
                switch (type) {
                    case Field.e_button:
                        mOutputListener.println("Button");
                        break;
                    case Field.e_radio:
                        mOutputListener.println("Radio button");
                        break;
                    case Field.e_check:
                        current.setValue(true);
                        mOutputListener.println("Check box");
                        break;
                    case Field.e_text: {
                        mOutputListener.println("Text");
                        // Edit all variable text in the document
                        String old_value;
                        if (current.getValue() != null) {
                            old_value = current.getValueAsString();
                            current.setValue("This is a new value. The old one was: " + old_value);
                        }
                    }
                    break;
                    case Field.e_choice:
                        mOutputListener.println("Choice");
                        break;
                    case Field.e_signature:
                        mOutputListener.println("Signature");
                        break;
                }

                mOutputListener.println("------------------------------");
            }

            // Search for a specific field
            Field f = doc.getField("employee.name.first");
            if (f != null) {
                mOutputListener.println("Field search for " + f.getName() + " was successful");
            } else {
                mOutputListener.println("Field search failed");
            }

            // Regenerate field appearances.
            doc.refreshFieldAppearances();
            doc.save((Utils.createExternalFile("forms_test_edit.pdf").getAbsolutePath()), SDFDoc.SaveMode.NO_FLAGS, null);
            mFileList.add(new File(doc.getFileName()).getName());
            doc.close();
            mOutputListener.println("Done.");
        } catch (Exception e) {
            e.printStackTrace();
        }

        //----------------------------------------------------------------------------------
        // Sample: Form templating
        // Replicate pages and form data within a document. Then rename field names to make
        // them unique.
        //----------------------------------------------------------------------------------
        try {
            // Sample: Copying the page with forms within the same document
            PDFDoc doc = new PDFDoc((Utils.createExternalFile("forms_test1.pdf").getAbsolutePath()));
            doc.initSecurityHandler();

            Page src_page = (Page) (doc.getPage(1));
            doc.pagePushBack(src_page);  // Append several copies of the first page
            doc.pagePushBack(src_page);     // Note that forms are successfully copied
            doc.pagePushBack(src_page);
            doc.pagePushBack(src_page);

            // Now we rename fields in order to make every field unique.
            // You can use this technique for dynamic template filling where you have a 'master'
            // form page that should be replicated, but with unique field names on every page.
            renameAllFields(doc, "employee.name.first");
            renameAllFields(doc, "employee.name.last");
            renameAllFields(doc, "employee.name.check1");
            renameAllFields(doc, "submit");

            doc.save(Utils.createExternalFile("forms_test1_cloned.pdf").getAbsolutePath(), SDFDoc.SaveMode.NO_FLAGS, null);
            mFileList.add(new File(doc.getFileName()).getName());
            doc.close();
            mOutputListener.println("Done.");
        } catch (Exception e) {
            e.printStackTrace();
        }

        //----------------------------------------------------------------------------------
        // Sample:
        // Flatten all form fields in a document.
        // Note that this sample is intended to show that it is possible to flatten
        // individual fields. PDFNet provides a utility function PDFDoc.flattenAnnotations()
        // that will automatically flatten all fields.
        //----------------------------------------------------------------------------------
        try {
            PDFDoc doc = new PDFDoc((Utils.createExternalFile("forms_test1.pdf").getAbsolutePath()));
            doc.initSecurityHandler();

            // Traverse all pages
            if (true) {
                doc.flattenAnnotations();
            } else // Manual flattening
            {

                for (PageIterator pitr = doc.getPageIterator(); pitr.hasNext(); ) {
                    Page page = pitr.next();
                    Obj annots = page.getAnnots();
                    if (annots != null) {    // Look for all widget annotations (in reverse order)
                        for (int i = ((int) annots.size()) - 1; i >= 0; --i) {
                            if (annots.getAt(i).get("Subtype").value().getName().equals("Widget")) {
                                Field field = new Field(annots.getAt(i));
                                field.flatten(page);

                                // Another way of making a read only field is by modifying
                                // field's e_read_only flag:
                                //    field.SetFlag(Field::e_read_only, true);
                            }
                        }
                    }
                }
            }

            doc.save(Utils.createExternalFile("forms_test1_flattened.pdf").getAbsolutePath(), SDFDoc.SaveMode.NO_FLAGS, null);
            mFileList.add(new File(doc.getFileName()).getName());
            doc.close();
            mOutputListener.println("Done.");
        } catch (Exception e) {
            e.printStackTrace();
        }

		for (String file : mFileList) {
			addToFileList(file);
		}
		printFooter(outputListener);
	}

    static void renameAllFields(PDFDoc doc, String name) throws PDFNetException {
        FieldIterator itr = doc.getFieldIterator(name);
        for (int counter = 0; itr.hasNext(); itr = doc.getFieldIterator(name), ++counter) {
            Field f = itr.next();
            f.rename(name + counter);
        }
    }

    static Obj createCheckmarkAppearance(PDFDoc doc) throws PDFNetException {
        // Create a checkmark appearance stream ------------------------------------
        ElementBuilder build = new ElementBuilder();
        ElementWriter writer = new ElementWriter();
        writer.begin(doc);
        writer.writeElement(build.createTextBegin());
        {
            String symbol = "4";   // other options are circle ("l"), diamond ("H"), cross ("\x35")
            // See section D.4 "ZapfDingbats Set and Encoding" in PDF Reference Manual for
            // the complete graphical map for ZapfDingbats font.
            Element checkmark = build.createTextRun(symbol, Font.create(doc, Font.e_zapf_dingbats), 1);
            writer.writeElement(checkmark);
        }
        writer.writeElement(build.createTextEnd());

        Obj stm = writer.end();

        // Set the bounding box
        stm.putRect("BBox", -0.2, -0.2, 1, 1);
        stm.putName("Subtype", "Form");
        return stm;
    }

    static Obj createButtonAppearance(PDFDoc doc, boolean button_down) throws PDFNetException {
        // Create a button appearance stream ------------------------------------
        ElementBuilder build = new ElementBuilder();
        ElementWriter writer = new ElementWriter();
        writer.begin(doc);

        // Draw background
        Element element = build.createRect(0, 0, 101, 37);
        element.setPathFill(true);
        element.setPathStroke(false);
        element.getGState().setFillColorSpace(ColorSpace.createDeviceGray());
        element.getGState().setFillColor(new ColorPt(0.75, 0, 0));
        writer.writeElement(element);

        // Draw 'Submit' text
        writer.writeElement(build.createTextBegin());
        {
            String text = "Submit";
            element = build.createTextRun(text, Font.create(doc, Font.e_helvetica_bold), 12);
            element.getGState().setFillColor(new ColorPt(0, 0, 0));

            if (button_down)
                element.setTextMatrix(1, 0, 0, 1, 33, 10);
            else
                element.setTextMatrix(1, 0, 0, 1, 30, 13);
            writer.writeElement(element);
        }
        writer.writeElement(build.createTextEnd());

        Obj stm = writer.end();

        // Set the bounding box
        stm.putRect("BBox", 0, 0, 101, 37);
        stm.putName("Subtype", "Form");
        return stm;
    }

}