InteractiveForms

Sample C# 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 Xamarin SDK and PDF Data Extraction SDK Capabilities.

1//---------------------------------------------------------------------------------------
2// Copyright (c) 2001-2021 by PDFTron Systems Inc. All Rights Reserved.
3// Consult legal.txt regarding legal and license information.
4//---------------------------------------------------------------------------------------
5
6using System;
7using System.Collections.Generic;
8using pdftron;
9using pdftron.Common;
10using pdftron.SDF;
11using pdftron.PDF;
12using pdftron.PDF.Annots;
13
14using NUnit.Framework;
15
16namespace MiscellaneousSamples
17{
18 /// <summary>
19 ///---------------------------------------------------------------------------------------
20 /// This sample illustrates basic PDFNet capabilities related to interactive
21 /// forms (also known as AcroForms).
22 ///---------------------------------------------------------------------------------------
23 /// </summary>
24 [TestFixture]
25 public class InteractiveFormsTest
26 {
27
28 [Test]
29 public static void Sample()
30 {
31
32 // Relative path to the folder containing test files.
33 const string input_path = "TestFiles/";
34
35 // The vector used to store the name and count of all fields.
36 // This is used later on to clone the fields
37 Dictionary<string, int> field_names = new Dictionary<string, int>();
38
39 //----------------------------------------------------------------------------------
40 // Example 1: Programatically create new Form Fields and Widget Annotations.
41 //----------------------------------------------------------------------------------
42 try
43 {
44 using (PDFDoc doc = new PDFDoc())
45 {
46 // Create a blank new page and add some form fields.
47 Page blank_page = doc.PageCreate();
48
49 // Text Widget Creation
50 // Create an empty text widget with black text.
51 TextWidget text1 = TextWidget.Create(doc, new Rect(110, 700, 380, 730));
52 text1.SetText("Basic Text Field");
53 text1.RefreshAppearance();
54 blank_page.AnnotPushBack(text1);
55 // Create a vertical text widget with blue text and a yellow background.
56 TextWidget text2 = TextWidget.Create(doc, new Rect(50, 400, 90, 730));
57 text2.SetRotation(90);
58 // Set the text content.
59 text2.SetText(" ****Lucky Stars!****");
60 // Set the font type, text color, font size, border color and background color.
61 text2.SetFont(Font.Create(doc, Font.StandardType1Font.e_helvetica_oblique));
62 text2.SetFontSize(28);
63 text2.SetTextColor(new ColorPt(0, 0, 1), 3);
64 text2.SetBorderColor(new ColorPt(0, 0, 0), 3);
65 text2.SetBackgroundColor(new ColorPt(1, 1, 0), 3);
66 text2.RefreshAppearance();
67 // Add the annotation to the page.
68 blank_page.AnnotPushBack(text2);
69 // Create two new text widget with Field names employee.name.first and employee.name.last
70 // This logic shows how these widgets can be created using either a field name string or
71 // a Field object
72 TextWidget text3 = TextWidget.Create(doc, new Rect(110, 660, 380, 690), "employee.name.first");
73 text3.SetText("Levi");
74 text3.SetFont(Font.Create(doc, Font.StandardType1Font.e_times_bold));
75 text3.RefreshAppearance();
76 blank_page.AnnotPushBack(text3);
77 Field emp_last_name = doc.FieldCreate("employee.name.last", Field.Type.e_text, "Ackerman");
78 TextWidget text4 = TextWidget.Create(doc, new Rect(110, 620, 380, 650), emp_last_name);
79 text4.SetFont(Font.Create(doc, Font.StandardType1Font.e_times_bold));
80 text4.RefreshAppearance();
81 blank_page.AnnotPushBack(text4);
82
83 // Signature Widget Creation (unsigned)
84 SignatureWidget signature1 = SignatureWidget.Create(doc, new Rect(110, 560, 260, 610));
85 signature1.RefreshAppearance();
86 blank_page.AnnotPushBack(signature1);
87
88 // CheckBox Widget Creation
89 // Create a check box widget that is not checked.
90 CheckBoxWidget check1 = CheckBoxWidget.Create(doc, new Rect(140, 490, 170, 520));
91 check1.RefreshAppearance();
92 blank_page.AnnotPushBack(check1);
93 // Create a check box widget that is checked.
94 CheckBoxWidget check2 = CheckBoxWidget.Create(doc, new Rect(190, 490, 250, 540), "employee.name.check1");
95 check2.SetBackgroundColor(new ColorPt(1, 1, 1), 3);
96 check2.SetBorderColor(new ColorPt(0, 0, 0), 3);
97 // Check the widget (by default it is unchecked).
98 check2.SetChecked(true);
99 check2.RefreshAppearance();
100 blank_page.AnnotPushBack(check2);
101
102 // PushButton Widget Creation
103 PushButtonWidget pushbutton1 = PushButtonWidget.Create(doc, new Rect(380, 490, 520, 540));
104 pushbutton1.SetTextColor(new ColorPt(1, 1, 1), 3);
105 pushbutton1.SetFontSize(36);
106 pushbutton1.SetBackgroundColor(new ColorPt(0, 0, 0), 3);
107 // Add a caption for the pushbutton.
108 pushbutton1.SetStaticCaptionText("PushButton");
109 pushbutton1.RefreshAppearance();
110 blank_page.AnnotPushBack(pushbutton1);
111
112 // ComboBox Widget Creation
113 ComboBoxWidget combo1 = ComboBoxWidget.Create(doc, new Rect(280, 560, 580, 610));
114 // Add options to the combobox widget.
115 combo1.AddOption("Combo Box No.1");
116 combo1.AddOption("Combo Box No.2");
117 combo1.AddOption("Combo Box No.3");
118 // Make one of the options in the combo box selected by default.
119 combo1.SetSelectedOption("Combo Box No.2");
120 combo1.SetTextColor(new ColorPt(1, 0, 0), 3);
121 combo1.SetFontSize(28);
122 combo1.RefreshAppearance();
123 blank_page.AnnotPushBack(combo1);
124
125 // ListBox Widget Creation
126 ListBoxWidget list1 = ListBoxWidget.Create(doc, new Rect(400, 620, 580, 730));
127 // Add one option to the listbox widget.
128 list1.AddOption("List Box No.1");
129 // Add multiple options to the listbox widget in a batch.
130 string[] list_options = new string[2] {"List Box No.2", "List Box No.3"};
131 list1.AddOptions(list_options);
132 // Select some of the options in list box as default options
133 list1.SetSelectedOptions(list_options);
134 // Enable list box to have multi-select when editing.
135 list1.GetField().SetFlag(Field.Flag.e_multiselect, true);
136 list1.SetFont(Font.Create(doc, Font.StandardType1Font.e_times_italic));
137 list1.SetTextColor(new ColorPt(1, 0, 0), 3);
138 list1.SetFontSize(28);
139 list1.SetBackgroundColor(new ColorPt(1, 1, 1), 3);
140 list1.RefreshAppearance();
141 blank_page.AnnotPushBack(list1);
142
143 // RadioButton Widget Creation
144 // Create a radio button group and add three radio buttons in it.
145 RadioButtonGroup radio_group = RadioButtonGroup.Create(doc, "RadioGroup");
146 RadioButtonWidget radiobutton1 = radio_group.Add(new Rect(140, 410, 190, 460));
147 radiobutton1.SetBackgroundColor(new ColorPt(1, 1, 0), 3);
148 radiobutton1.RefreshAppearance();
149 RadioButtonWidget radiobutton2 = radio_group.Add(new Rect(310, 410, 360, 460));
150 radiobutton2.SetBackgroundColor(new ColorPt(0, 1, 0), 3);
151 radiobutton2.RefreshAppearance();
152 RadioButtonWidget radiobutton3 = radio_group.Add(new Rect(480, 410, 530, 460));
153 // Enable the third radio button. By default the first one is selected
154 radiobutton3.EnableButton();
155 radiobutton3.SetBackgroundColor(new ColorPt(0, 1, 1), 3);
156 radiobutton3.RefreshAppearance();
157 radio_group.AddGroupButtonsToPage(blank_page);
158
159 // Custom push button annotation creation
160 PushButtonWidget custom_pushbutton1 = PushButtonWidget.Create(doc, new Rect(260, 320, 360, 360));
161 // Set the annotation appearance.
162 custom_pushbutton1.SetAppearance(CreateCustomButtonAppearance(doc, false), Annot.AnnotationState.e_normal);
163 // Create 'SubmitForm' action. The action will be linked to the button.
164 FileSpec url = FileSpec.CreateURL(doc, "http://www.pdftron.com");
165 pdftron.PDF.Action button_action = pdftron.PDF.Action.CreateSubmitForm(url);
166 // Associate the above action with 'Down' event in annotations action dictionary.
167 Obj annot_action = custom_pushbutton1.GetSDFObj().PutDict("AA");
168 annot_action.Put("D", button_action.GetSDFObj());
169 blank_page.AnnotPushBack(custom_pushbutton1);
170
171 // Add the page as the last page in the document.
172 doc.PagePushBack(blank_page);
173
174 // If you are not satisfied with the look of default auto-generated appearance
175 // streams you can delete "AP" entry from the Widget annotation and set
176 // "NeedAppearances" flag in AcroForm dictionary:
177 // doc.GetAcroForm().PutBool("NeedAppearances", true);
178 // This will force the viewer application to auto-generate new appearance streams
179 // every time the document is opened.
180 //
181 // Alternatively you can generate custom annotation appearance using ElementWriter
182 // and then set the "AP" entry in the widget dictionary to the new appearance
183 // stream.
184 //
185 // Yet another option is to pre-populate field entries with dummy text. When
186 // you edit the field values using PDFNet the new field appearances will match
187 // the old ones.
188 doc.RefreshFieldAppearances();
189
190 doc.Save(Utils.CreateExternalFile("forms_test1.pdf"), 0);
191
192 Console.WriteLine("Done.");
193 }
194 }
195 catch (PDFNetException e)
196 {
197 Console.WriteLine(e.Message);
198 Assert.True(false);
199 }
200
201 //----------------------------------------------------------------------------------
202 // Example 2:
203 // Fill-in forms / Modify values of existing fields.
204 // Traverse all form fields in the document (and print out their names).
205 // Search for specific fields in the document.
206 //----------------------------------------------------------------------------------
207 try
208 {
209 using (PDFDoc doc = new PDFDoc(Utils.CreateExternalFile("forms_test1.pdf")))
210 {
211 doc.InitSecurityHandler();
212
213 FieldIterator itr;
214 for(itr=doc.GetFieldIterator(); itr.HasNext(); itr.Next())
215 {
216 Field field = itr.Current();
217 string cur_field_name = field.GetName();
218 // Add one to the count for this field name for later processing
219 field_names[cur_field_name] = (field_names.ContainsKey(cur_field_name) ? field_names[cur_field_name] + 1 : 1);
220
221 Console.WriteLine("Field name: {0}", field.GetName());
222 Console.WriteLine("Field partial name: {0}", field.GetPartialName());
223 string str_val = field.GetValueAsString();
224
225 Console.Write("Field type: ");
226 Field.Type type = field.GetType();
227 switch(type)
228 {
229 case Field.Type.e_button:
230 Console.WriteLine("Button");
231 break;
232 case Field.Type.e_radio:
233 Console.WriteLine("Radio button: Value = " + str_val);
234 break;
235 case Field.Type.e_check:
236 field.SetValue(true);
237 Console.WriteLine("Check box: Value = " + str_val);
238 break;
239 case Field.Type.e_text:
240 {
241 Console.WriteLine("Text");
242
243 // Edit all variable text in the document
244 String old_value = "none";
245 if (field.GetValue() != null)
246 old_value = field.GetValue().GetAsPDFText();
247
248 field.SetValue("This is a new value. The old one was: " + old_value);
249 }
250 break;
251 case Field.Type.e_choice:
252 Console.WriteLine("Choice");
253 break;
254 case Field.Type.e_signature:
255 Console.WriteLine("Signature");
256 break;
257 }
258
259 Console.WriteLine("------------------------------");
260 }
261
262 // Search for a specific field
263 Field fld = doc.GetField("employee.name.first");
264 if (fld != null)
265 {
266 Console.WriteLine("Field search for {0} was successful", fld.GetName());
267 }
268 else
269 {
270 Console.WriteLine("Field search failed.");
271 }
272
273 // Regenerate field appearances.
274 doc.RefreshFieldAppearances();
275 doc.Save(Utils.CreateExternalFile("forms_test_edit.pdf"), 0);
276 Console.WriteLine("Done.");
277 }
278 }
279 catch (PDFNetException e)
280 {
281 Console.WriteLine(e.Message);
282 Assert.True(false);
283 }
284
285 //----------------------------------------------------------------------------------
286 // Sample: Form templating
287 // Replicate pages and form data within a document. Then rename field names to make
288 // them unique.
289 //----------------------------------------------------------------------------------
290 try
291 {
292 // Sample: Copying the page with forms within the same document
293 using (PDFDoc doc = new PDFDoc(Utils.CreateExternalFile("forms_test1.pdf")))
294 {
295 doc.InitSecurityHandler();
296
297 Page src_page = doc.GetPage(1);
298 doc.PagePushBack(src_page); // Append several copies of the second page
299 doc.PagePushBack(src_page); // Note that forms are successfully copied
300 doc.PagePushBack(src_page);
301 doc.PagePushBack(src_page);
302
303 // Now we rename fields in order to make every field unique.
304 // You can use this technique for dynamic template filling where you have a 'master'
305 // form page that should be replicated, but with unique field names on every page.
306 foreach (KeyValuePair<string, int> cur_field in field_names)
307 {
308 RenameAllFields(doc, cur_field.Key, cur_field.Value);
309 }
310
311 doc.Save(Utils.CreateExternalFile("forms_test1_cloned.pdf"), 0);
312 Console.WriteLine("Done.");
313 }
314 }
315 catch (PDFNetException e)
316 {
317 Console.WriteLine(e.Message);
318 Assert.True(false);
319 }
320
321 //----------------------------------------------------------------------------------
322 // Sample:
323 // Flatten all form fields in a document.
324 // Note that this sample is intended to show that it is possible to flatten
325 // individual fields. PDFNet provides a utility function PDFDoc.FlattenAnnotations()
326 // that will automatically flatten all fields.
327 //----------------------------------------------------------------------------------
328 try
329 {
330 using (PDFDoc doc = new PDFDoc(Utils.CreateExternalFile("forms_test1.pdf")))
331 {
332 doc.InitSecurityHandler();
333
334 bool auto = true;
335 if (auto)
336 {
337 doc.FlattenAnnotations();
338 }
339 else // Manual flattening
340 {
341 // Traverse all pages
342 PageIterator pitr = doc.GetPageIterator();
343 for (; pitr.HasNext(); pitr.Next())
344 {
345 Page page = pitr.Current();
346 for (int i = page.GetNumAnnots() - 1; i >= 0; --i)
347 {
348 Annot annot = page.GetAnnot(i);
349 if (annot.GetType() == Annot.Type.e_Widget)
350 {
351 annot.Flatten(page);
352 }
353 }
354 }
355 }
356
357 doc.Save(Utils.CreateExternalFile("forms_test1_flattened.pdf"), 0);
358 Console.WriteLine("Done.");
359 }
360 }
361 catch (PDFNetException e)
362 {
363 Console.WriteLine(e.Message);
364 Assert.True(false);
365 }
366 }
367
368 // field_nums has to be greater than 0.
369 static void RenameAllFields(PDFDoc doc, String name, int field_nums = 1)
370 {
371 Field fld = doc.GetField(name);
372 for (int counter = 1; fld != null; ++counter)
373 {
374 string field_new_name = name;
375 int update_count = System.Convert.ToInt32(Math.Ceiling(counter / (double)field_nums));
376 fld.Rename(name + "-" + update_count.ToString());
377 fld = doc.GetField(name);
378 }
379 }
380
381 static Obj CreateCustomButtonAppearance(PDFDoc doc, bool button_down)
382 {
383 // Create a button appearance stream ------------------------------------
384 using (ElementBuilder builder = new ElementBuilder())
385 using (ElementWriter writer = new ElementWriter())
386 {
387 writer.Begin(doc);
388
389 // Draw background
390 Element element = builder.CreateRect(0, 0, 101, 37);
391 element.SetPathFill(true);
392 element.SetPathStroke(false);
393 element.GetGState().SetFillColorSpace(ColorSpace.CreateDeviceGray());
394 element.GetGState().SetFillColor(new ColorPt(0.75, 0.0, 0.0));
395 writer.WriteElement(element);
396
397 // Draw 'Submit' text
398 writer.WriteElement(builder.CreateTextBegin());
399
400 element = builder.CreateTextRun("Submit", Font.Create(doc, Font.StandardType1Font.e_helvetica_bold), 12);
401 element.GetGState().SetFillColor(new ColorPt(0, 0, 0));
402
403 if (button_down)
404 element.SetTextMatrix(1, 0, 0, 1, 33, 10);
405 else
406 element.SetTextMatrix(1, 0, 0, 1, 30, 13);
407 writer.WriteElement(element);
408 writer.WriteElement(builder.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 }
419}

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales