InteractiveForms

Sample Java code for using Apryse SDK with interactive forms (also known as AcroForms). Capabilities include programatically creating new fields and widget annotations, form filling, modifying existing field values, form templating, and flattening form fields. Learn more about our Android SDK and PDF Data Extraction SDK Capabilities.

1//---------------------------------------------------------------------------------------
2// Copyright (c) 2001-2019 by PDFTron Systems Inc. All Rights Reserved.
3// Consult legal.txt regarding legal and license information.
4//---------------------------------------------------------------------------------------
5
6package com.pdftron.android.pdfnetsdksamples.samples;
7
8import com.pdftron.android.pdfnetsdksamples.OutputListener;
9import com.pdftron.android.pdfnetsdksamples.PDFNetSample;
10import com.pdftron.android.pdfnetsdksamples.R;
11import com.pdftron.android.pdfnetsdksamples.util.Utils;
12import com.pdftron.common.PDFNetException;
13import com.pdftron.pdf.Action;
14import com.pdftron.pdf.Annot;
15import com.pdftron.pdf.ColorPt;
16import com.pdftron.pdf.ColorSpace;
17import com.pdftron.pdf.Element;
18import com.pdftron.pdf.ElementBuilder;
19import com.pdftron.pdf.ElementWriter;
20import com.pdftron.pdf.Field;
21import com.pdftron.pdf.FieldIterator;
22import com.pdftron.pdf.FileSpec;
23import com.pdftron.pdf.Font;
24import com.pdftron.pdf.PDFDoc;
25import com.pdftron.pdf.Page;
26import com.pdftron.pdf.PageIterator;
27import com.pdftron.pdf.Rect;
28import com.pdftron.pdf.annots.CheckBoxWidget;
29import com.pdftron.pdf.annots.ComboBoxWidget;
30import com.pdftron.pdf.annots.ListBoxWidget;
31import com.pdftron.pdf.annots.PushButtonWidget;
32import com.pdftron.pdf.annots.RadioButtonGroup;
33import com.pdftron.pdf.annots.RadioButtonWidget;
34import com.pdftron.pdf.annots.SignatureWidget;
35import com.pdftron.pdf.annots.TextWidget;
36import com.pdftron.sdf.Obj;
37import com.pdftron.sdf.SDFDoc;
38
39import java.util.ArrayList;
40import java.util.HashMap;
41import java.util.Map;
42
43//---------------------------------------------------------------------------------------
44//This sample illustrates basic PDFNet capabilities related to interactive
45//forms (also known as AcroForms).
46//---------------------------------------------------------------------------------------
47
48public class InteractiveFormsTest extends PDFNetSample {
49
50 private static OutputListener mOutputListener;
51
52 private static ArrayList<String> mFileList = new ArrayList<>();
53
54 public InteractiveFormsTest() {
55 setTitle(R.string.sample_interactiveforms_title);
56 setDescription(R.string.sample_interactiveforms_description);
57 }
58
59 @Override
60 public void run(OutputListener outputListener) {
61 super.run(outputListener);
62 mOutputListener = outputListener;
63 mFileList.clear();
64 printHeader(outputListener);
65
66 // string input_path = "../../TestFiles/";
67
68 // The vector used to store the name and count of all fields.
69 // This is used later on to clone the fields
70 Map<String, Integer> field_names = new HashMap<String, Integer>();
71
72 //----------------------------------------------------------------------------------
73 // Example 1: Programatically create new Form Fields and Widget Annotations.
74 //----------------------------------------------------------------------------------
75 try (PDFDoc doc = new PDFDoc()) {
76 // Create a blank new page and add some form fields.
77 Page blank_page = doc.pageCreate();
78
79 // Text Widget Creation
80 // Create an empty text widget with black text.
81 TextWidget text1 = TextWidget.create(doc, new Rect(110, 700, 380, 730));
82 text1.setText("Basic Text Field");
83 text1.refreshAppearance();
84 blank_page.annotPushBack(text1);
85 // Create a vertical text widget with blue text and a yellow background.
86 TextWidget text2 = TextWidget.create(doc, new Rect(50, 400, 90, 730));
87 text2.setRotation(90);
88 // Set the text content.
89 text2.setText(" ****Lucky Stars!****");
90 // Set the font type, text color, font size, border color and background color.
91 text2.setFont(Font.create(doc, Font.e_helvetica_oblique));
92 text2.setFontSize(28);
93 text2.setTextColor(new ColorPt(0, 0, 1), 3);
94 text2.setBorderColor(new ColorPt(0, 0, 0), 3);
95 text2.setBackgroundColor(new ColorPt(1, 1, 0), 3);
96 text2.refreshAppearance();
97 // Add the annotation to the page.
98 blank_page.annotPushBack(text2);
99 // Create two new text widget with Field names employee.name.first and employee.name.last
100 // This logic shows how these widgets can be created using either a field name string or
101 // a Field object
102 TextWidget text3 = TextWidget.create(doc, new Rect(110, 660, 380, 690), "employee.name.first");
103 text3.setText("Levi");
104 text3.setFont(Font.create(doc, Font.e_times_bold));
105 text3.refreshAppearance();
106 blank_page.annotPushBack(text3);
107 Field emp_last_name = doc.fieldCreate("employee.name.last", Field.e_text, "Ackerman");
108 TextWidget text4 = TextWidget.create(doc, new Rect(110, 620, 380, 650), emp_last_name);
109 text4.setFont(Font.create(doc, Font.e_times_bold));
110 text4.refreshAppearance();
111 blank_page.annotPushBack(text4);
112
113 // Signature Widget Creation (unsigned)
114 SignatureWidget signature1 = SignatureWidget.create(doc, new Rect(110, 560, 260, 610));
115 signature1.refreshAppearance();
116 blank_page.annotPushBack(signature1);
117
118 // CheckBox Widget Creation
119 // Create a check box widget that is not checked.
120 CheckBoxWidget check1 = CheckBoxWidget.create(doc, new Rect(140, 490, 170, 520));
121 check1.refreshAppearance();
122 blank_page.annotPushBack(check1);
123 // Create a check box widget that is checked.
124 CheckBoxWidget check2 = CheckBoxWidget.create(doc, new Rect(190, 490, 250, 540), "employee.name.check1");
125 check2.setBackgroundColor(new ColorPt(1, 1, 1), 3);
126 check2.setBorderColor(new ColorPt(0, 0, 0), 3);
127 // Check the widget (by default it is unchecked).
128 check2.setChecked(true);
129 check2.refreshAppearance();
130 blank_page.annotPushBack(check2);
131
132 // PushButton Widget Creation
133 PushButtonWidget pushbutton1 = PushButtonWidget.create(doc, new Rect(380, 490, 520, 540));
134 pushbutton1.setTextColor(new ColorPt(1, 1, 1), 3);
135 pushbutton1.setFontSize(36);
136 pushbutton1.setBackgroundColor(new ColorPt(0, 0, 0), 3);
137 // Add a caption for the pushbutton.
138 pushbutton1.setStaticCaptionText("PushButton");
139 pushbutton1.refreshAppearance();
140 blank_page.annotPushBack(pushbutton1);
141
142 // ComboBox Widget Creation
143 ComboBoxWidget combo1 = ComboBoxWidget.create(doc, new Rect(280, 560, 580, 610));
144 // Add options to the combobox widget.
145 combo1.addOption("Combo Box No.1");
146 combo1.addOption("Combo Box No.2");
147 combo1.addOption("Combo Box No.3");
148 // Make one of the options in the combo box selected by default.
149 combo1.setSelectedOption("Combo Box No.2");
150 combo1.setTextColor(new ColorPt(1, 0, 0), 3);
151 combo1.setFontSize(28);
152 combo1.refreshAppearance();
153 blank_page.annotPushBack(combo1);
154
155 // ListBox Widget Creation
156 ListBoxWidget list1 = ListBoxWidget.create(doc, new Rect(400, 620, 580, 730));
157 // Add one option to the listbox widget.
158 list1.addOption("List Box No.1");
159 // Add multiple options to the listbox widget in a batch.
160 String[] list_options = new String[] { "List Box No.2", "List Box No.3" };
161 list1.addOptions(list_options);
162 // Select some of the options in list box as default options
163 list1.setSelectedOptions(list_options);
164 // Enable list box to have multi-select when editing.
165 list1.getField().setFlag(Field.e_multiselect, true);
166 list1.setFont(Font.create(doc,Font.e_times_italic));
167 list1.setTextColor(new ColorPt(1, 0, 0), 3);
168 list1.setFontSize(28);
169 list1.setBackgroundColor(new ColorPt(1, 1, 1), 3);
170 list1.refreshAppearance();
171 blank_page.annotPushBack(list1);
172
173 // RadioButton Widget Creation
174 // Create a radio button group and add three radio buttons in it.
175 RadioButtonGroup radio_group = RadioButtonGroup.create(doc, "RadioGroup");
176 RadioButtonWidget radiobutton1 = radio_group.add(new Rect(140, 410, 190, 460));
177 radiobutton1.setBackgroundColor(new ColorPt(1, 1, 0), 3);
178 radiobutton1.refreshAppearance();
179 RadioButtonWidget radiobutton2 = radio_group.add(new Rect(310, 410, 360, 460));
180 radiobutton2.setBackgroundColor(new ColorPt(0, 1, 0), 3);
181 radiobutton2.refreshAppearance();
182 RadioButtonWidget radiobutton3 = radio_group.add(new Rect(480, 410, 530, 460));
183 // Enable the third radio button. By default the first one is selected
184 radiobutton3.enableButton();
185 radiobutton3.setBackgroundColor(new ColorPt(0, 1, 1), 3);
186 radiobutton3.refreshAppearance();
187 radio_group.addGroupButtonsToPage(blank_page);
188
189 // Custom push button annotation creation
190 PushButtonWidget custom_pushbutton1 = PushButtonWidget.create(doc, new Rect(260, 320, 360, 360));
191 // Set the annotation appearance.
192 custom_pushbutton1.setAppearance(createCustomButtonAppearance(doc, false), Annot.e_normal);
193 // Create 'SubmitForm' action. The action will be linked to the button.
194 FileSpec url = FileSpec.createURL(doc, "http://www.pdftron.com");
195 Action button_action = Action.createSubmitForm(url);
196 // Associate the above action with 'Down' event in annotations action dictionary.
197 Obj annot_action = custom_pushbutton1.getSDFObj().putDict("AA");
198 annot_action.put("D", button_action.getSDFObj());
199 blank_page.annotPushBack(custom_pushbutton1);
200
201 // Add the page as the last page in the document.
202 doc.pagePushBack(blank_page);
203
204 // If you are not satisfied with the look of default auto-generated appearance
205 // streams you can delete "AP" entry from the Widget annotation and set
206 // "NeedAppearances" flag in AcroForm dictionary:
207 // doc.GetAcroForm().PutBool("NeedAppearances", true);
208 // This will force the viewer application to auto-generate new appearance streams
209 // every time the document is opened.
210 //
211 // Alternatively you can generate custom annotation appearance using ElementWriter
212 // and then set the "AP" entry in the widget dictionary to the new appearance
213 // stream.
214 //
215 // Yet another option is to pre-populate field entries with dummy text. When
216 // you edit the field values using PDFNet the new field appearances will match
217 // the old ones.
218
219 //doc.GetAcroForm().Put("NeedAppearances", new Bool(true));
220 doc.refreshFieldAppearances();
221
222 doc.save(Utils.createExternalFile("forms_test1.pdf", mFileList).getAbsolutePath(), SDFDoc.SaveMode.NO_FLAGS, null);
223 mOutputListener.println("Done.");
224 } catch (Exception e) {
225 mOutputListener.printError(e.getStackTrace());
226 }
227
228 //----------------------------------------------------------------------------------
229 // Example 2:
230 // Fill-in forms / Modify values of existing fields.
231 // Traverse all form fields in the document (and print out their names).
232 // Search for specific fields in the document.
233 //----------------------------------------------------------------------------------
234 try (PDFDoc doc = new PDFDoc((Utils.createExternalFile("forms_test1.pdf", mFileList).getAbsolutePath()))) {
235 doc.initSecurityHandler();
236
237 FieldIterator itr = doc.getFieldIterator();
238 while (itr.hasNext()) {
239 Field current = itr.next();
240 String cur_field_name = current.getName();
241 // Add one to the count for this field name for later processing
242 if (field_names.containsKey(cur_field_name)) {
243 field_names.put(cur_field_name, field_names.get(cur_field_name) + 1);
244 }
245 else {
246 field_names.put(cur_field_name, 1);
247 }
248
249 mOutputListener.println("Field name: " + current.getName());
250 mOutputListener.println("Field partial name: " + current.getPartialName());
251
252 mOutputListener.print("Field type: ");
253 int type = current.getType();
254 String str_val = current.getValueAsString();
255 switch (type) {
256 case Field.e_button:
257 mOutputListener.println("Button");
258 break;
259 case Field.e_radio:
260 mOutputListener.println("Radio button: Value = " + str_val);
261 break;
262 case Field.e_check:
263 current.setValue(true);
264 mOutputListener.println("Check box: Value = " + str_val);
265 break;
266 case Field.e_text: {
267 mOutputListener.println("Text");
268 // Edit all variable text in the document
269 String old_value;
270 if (current.getValue() != null) {
271 old_value = current.getValueAsString();
272 current.setValue("This is a new value. The old one was: " + old_value);
273 }
274 }
275 break;
276 case Field.e_choice:
277 mOutputListener.println("Choice");
278 break;
279 case Field.e_signature:
280 mOutputListener.println("Signature");
281 break;
282 }
283
284 mOutputListener.println("------------------------------");
285 }
286
287 // Search for a specific field
288 Field f = doc.getField("employee.name.first");
289 if (f != null) {
290 mOutputListener.println("Field search for " + f.getName() + " was successful");
291 } else {
292 mOutputListener.println("Field search failed");
293 }
294
295 // Regenerate field appearances.
296 doc.refreshFieldAppearances();
297 doc.save((Utils.createExternalFile("forms_test_edit.pdf", mFileList).getAbsolutePath()), SDFDoc.SaveMode.NO_FLAGS, null);
298 mOutputListener.println("Done.");
299 } catch (Exception e) {
300 mOutputListener.printError(e.getStackTrace());
301 }
302
303 //----------------------------------------------------------------------------------
304 // Sample: Form templating
305 // Replicate pages and form data within a document. Then rename field names to make
306 // them unique.
307 //----------------------------------------------------------------------------------
308 try (PDFDoc doc = new PDFDoc((Utils.createExternalFile("forms_test1.pdf", mFileList).getAbsolutePath()))) {
309 // Sample: Copying the page with forms within the same document
310 doc.initSecurityHandler();
311
312 Page src_page = (Page) (doc.getPage(1));
313 doc.pagePushBack(src_page); // Append several copies of the first page
314 doc.pagePushBack(src_page); // Note that forms are successfully copied
315 doc.pagePushBack(src_page);
316 doc.pagePushBack(src_page);
317
318 // Now we rename fields in order to make every field unique.
319 // You can use this technique for dynamic template filling where you have a 'master'
320 // form page that should be replicated, but with unique field names on every page.
321 for (String cur_field : field_names.keySet()) {
322 renameAllFields(doc, cur_field, field_names.get(cur_field));
323 }
324
325 doc.save(Utils.createExternalFile("forms_test1_cloned.pdf", mFileList).getAbsolutePath(), SDFDoc.SaveMode.NO_FLAGS, null);
326 mOutputListener.println("Done.");
327 } catch (Exception e) {
328 mOutputListener.printError(e.getStackTrace());
329 }
330
331 //----------------------------------------------------------------------------------
332 // Sample:
333 // Flatten all form fields in a document.
334 // Note that this sample is intended to show that it is possible to flatten
335 // individual fields. PDFNet provides a utility function PDFDoc.flattenAnnotations()
336 // that will automatically flatten all fields.
337 //----------------------------------------------------------------------------------
338 try (PDFDoc doc = new PDFDoc((Utils.createExternalFile("forms_test1.pdf", mFileList).getAbsolutePath()))) {
339 doc.initSecurityHandler();
340
341 // Traverse all pages
342 if (true) {
343 doc.flattenAnnotations();
344 } else // Manual flattening
345 {
346
347 for (PageIterator pitr = doc.getPageIterator(); pitr.hasNext(); ) {
348 Page page = pitr.next();
349 for (int i = page.getNumAnnots() - 1; i >= 0; --i) {
350 Annot annot = page.getAnnot(i);
351 if (annot.getType() == Annot.e_Widget)
352 {
353 annot.flatten(page);
354 }
355 }
356 }
357 }
358
359 doc.save(Utils.createExternalFile("forms_test1_flattened.pdf", mFileList).getAbsolutePath(), SDFDoc.SaveMode.NO_FLAGS, null);
360 mOutputListener.println("Done.");
361 } catch (Exception e) {
362 mOutputListener.printError(e.getStackTrace());
363 }
364
365 for (String file : mFileList) {
366 addToFileList(file);
367 }
368 printFooter(outputListener);
369 }
370
371 // field_nums has to be greater than 0.
372 static void renameAllFields(PDFDoc doc, String name, int field_nums) throws PDFNetException {
373 FieldIterator itr = doc.getFieldIterator(name);
374 for (int counter = 1; itr.hasNext(); itr = doc.getFieldIterator(name), ++counter) {
375 Field f = itr.next();
376 int update_count = (int)java.lang.Math.ceil(counter/(double)field_nums);
377 f.rename(name + "-" + update_count);
378 }
379 }
380
381 static Obj createCustomButtonAppearance(PDFDoc doc, boolean button_down) throws PDFNetException {
382 // Create a button appearance stream ------------------------------------
383 ElementBuilder build = new ElementBuilder();
384 ElementWriter writer = new ElementWriter();
385 writer.begin(doc);
386
387 // Draw background
388 Element element = build.createRect(0, 0, 101, 37);
389 element.setPathFill(true);
390 element.setPathStroke(false);
391 element.getGState().setFillColorSpace(ColorSpace.createDeviceGray());
392 element.getGState().setFillColor(new ColorPt(0.75, 0, 0));
393 writer.writeElement(element);
394
395 // Draw 'Submit' text
396 writer.writeElement(build.createTextBegin());
397 {
398 String text = "Submit";
399 element = build.createTextRun(text, Font.create(doc, Font.e_helvetica_bold), 12);
400 element.getGState().setFillColor(new ColorPt(0, 0, 0));
401
402 if (button_down)
403 element.setTextMatrix(1, 0, 0, 1, 33, 10);
404 else
405 element.setTextMatrix(1, 0, 0, 1, 30, 13);
406 writer.writeElement(element);
407 }
408 writer.writeElement(build.createTextEnd());
409
410 Obj stm = writer.end();
411
412 // Set the bounding box
413 stm.putRect("BBox", 0, 0, 101, 37);
414 stm.putName("Subtype", "Form");
415 return stm;
416 }
417
418}

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales