Flatten, Create, Modify PDF Forms - Java Sample Code

Sample 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. Sample code provided in Python, C++, C#, Java, Node.js (JavaScript), PHP, Ruby and VB.

Learn more about our full PDF Data Extraction SDK Capabilities.

To start your free trial, get stated with Server SDK.

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

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales