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 Server SDK and PDF Data Extraction SDK Capabilities.

1//---------------------------------------------------------------------------------------
2// Copyright (c) 2001-2024 by Apryse Software 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
14namespace FormsTestCS
15{
16 /// <summary>
17 ///---------------------------------------------------------------------------------------
18 /// This sample illustrates basic PDFNet capabilities related to interactive
19 /// forms (also known as AcroForms).
20 ///---------------------------------------------------------------------------------------
21 /// </summary>
22 class Class1
23 {
24 private static pdftron.PDFNetLoader pdfNetLoader = pdftron.PDFNetLoader.Instance();
25 static Class1() {}
26
27 static void Main(string[] args)
28 {
29 PDFNet.Initialize(PDFTronLicense.Key);
30
31 // Relative path to the folder containing test files.
32 // string input_path = "../../../../TestFiles/";
33 string output_path = "../../../../TestFiles/Output/";
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(output_path + "forms_test1.pdf", 0);
191
192 Console.WriteLine("Done.");
193 }
194 }
195 catch (PDFNetException e)
196 {
197 Console.WriteLine(e.Message);
198 }
199
200 //----------------------------------------------------------------------------------
201 // Example 2:
202 // Fill-in forms / Modify values of existing fields.
203 // Traverse all form fields in the document (and print out their names).
204 // Search for specific fields in the document.
205 //----------------------------------------------------------------------------------
206 try
207 {
208 using (PDFDoc doc = new PDFDoc(output_path + "forms_test1.pdf"))
209 {
210 doc.InitSecurityHandler();
211
212 FieldIterator itr;
213 for(itr=doc.GetFieldIterator(); itr.HasNext(); itr.Next())
214 {
215 Field field = itr.Current();
216 string cur_field_name = field.GetName();
217 // Add one to the count for this field name for later processing
218 field_names[cur_field_name] = (field_names.ContainsKey(cur_field_name) ? field_names[cur_field_name] + 1 : 1);
219
220 Console.WriteLine("Field name: {0}", field.GetName());
221 Console.WriteLine("Field partial name: {0}", field.GetPartialName());
222 string str_val = field.GetValueAsString();
223
224 Console.Write("Field type: ");
225 Field.Type type = field.GetType();
226 switch(type)
227 {
228 case Field.Type.e_button:
229 Console.WriteLine("Button");
230 break;
231 case Field.Type.e_radio:
232 Console.WriteLine("Radio button: Value = " + str_val);
233 break;
234 case Field.Type.e_check:
235 field.SetValue(true);
236 Console.WriteLine("Check box: Value = " + str_val);
237 break;
238 case Field.Type.e_text:
239 {
240 Console.WriteLine("Text");
241
242 // Edit all variable text in the document
243 String old_value = "none";
244 if (field.GetValue() != null)
245 old_value = field.GetValue().GetAsPDFText();
246
247 field.SetValue("This is a new value. The old one was: " + old_value);
248 }
249 break;
250 case Field.Type.e_choice:
251 Console.WriteLine("Choice");
252 break;
253 case Field.Type.e_signature:
254 Console.WriteLine("Signature");
255 break;
256 }
257
258 Console.WriteLine("------------------------------");
259 }
260
261 // Search for a specific field
262 Field fld = doc.GetField("employee.name.first");
263 if (fld != null)
264 {
265 Console.WriteLine("Field search for {0} was successful", fld.GetName());
266 }
267 else
268 {
269 Console.WriteLine("Field search failed.");
270 }
271
272 // Regenerate field appearances.
273 doc.RefreshFieldAppearances();
274 doc.Save(output_path + "forms_test_edit.pdf", 0);
275 Console.WriteLine("Done.");
276 }
277 }
278 catch (PDFNetException e)
279 {
280 Console.WriteLine(e.Message);
281 }
282
283 //----------------------------------------------------------------------------------
284 // Sample: Form templating
285 // Replicate pages and form data within a document. Then rename field names to make
286 // them unique.
287 //----------------------------------------------------------------------------------
288 try
289 {
290 // Sample: Copying the page with forms within the same document
291 using (PDFDoc doc = new PDFDoc(output_path + "forms_test1.pdf"))
292 {
293 doc.InitSecurityHandler();
294
295 Page src_page = doc.GetPage(1);
296 doc.PagePushBack(src_page); // Append several copies of the second page
297 doc.PagePushBack(src_page); // Note that forms are successfully copied
298 doc.PagePushBack(src_page);
299 doc.PagePushBack(src_page);
300
301 // Now we rename fields in order to make every field unique.
302 // You can use this technique for dynamic template filling where you have a 'master'
303 // form page that should be replicated, but with unique field names on every page.
304 foreach (KeyValuePair<string, int> cur_field in field_names)
305 {
306 RenameAllFields(doc, cur_field.Key, cur_field.Value);
307 }
308
309 doc.Save(output_path + "forms_test1_cloned.pdf", 0);
310 Console.WriteLine("Done.");
311 }
312 }
313 catch (PDFNetException e)
314 {
315 Console.WriteLine(e.Message);
316 }
317
318 //----------------------------------------------------------------------------------
319 // Sample:
320 // Flatten all form fields in a document.
321 // Note that this sample is intended to show that it is possible to flatten
322 // individual fields. PDFNet provides a utility function PDFDoc.FlattenAnnotations()
323 // that will automatically flatten all fields.
324 //----------------------------------------------------------------------------------
325 try
326 {
327 using (PDFDoc doc = new PDFDoc(output_path + "forms_test1.pdf"))
328 {
329 doc.InitSecurityHandler();
330
331 bool auto = true;
332 if (auto)
333 {
334 doc.FlattenAnnotations();
335 }
336 else // Manual flattening
337 {
338 // Traverse all pages
339 PageIterator pitr = doc.GetPageIterator();
340 for (; pitr.HasNext(); pitr.Next())
341 {
342 Page page = pitr.Current();
343 for (int i = page.GetNumAnnots() - 1; i >= 0; --i)
344 {
345 Annot annot = page.GetAnnot(i);
346 if (annot.GetType() == Annot.Type.e_Widget)
347 {
348 annot.Flatten(page);
349 }
350 }
351 }
352 }
353
354 doc.Save(output_path + "forms_test1_flattened.pdf", 0);
355 Console.WriteLine("Done.");
356 }
357 }
358 catch (PDFNetException e)
359 {
360 Console.WriteLine(e.Message);
361 }
362 PDFNet.Terminate();
363 }
364
365 // field_nums has to be greater than 0.
366 static void RenameAllFields(PDFDoc doc, String name, int field_nums = 1)
367 {
368 Field fld = doc.GetField(name);
369 for (int counter = 1; fld != null; ++counter)
370 {
371 string field_new_name = name;
372 int update_count = System.Convert.ToInt32(Math.Ceiling(counter / (double)field_nums));
373 fld.Rename(name + "-" + update_count.ToString());
374 fld = doc.GetField(name);
375 }
376 }
377
378 static Obj CreateCustomButtonAppearance(PDFDoc doc, bool button_down)
379 {
380 // Create a button appearance stream ------------------------------------
381 using (ElementBuilder builder = new ElementBuilder())
382 using (ElementWriter writer = new ElementWriter())
383 {
384 writer.Begin(doc);
385
386 // Draw background
387 Element element = builder.CreateRect(0, 0, 101, 37);
388 element.SetPathFill(true);
389 element.SetPathStroke(false);
390 element.GetGState().SetFillColorSpace(ColorSpace.CreateDeviceGray());
391 element.GetGState().SetFillColor(new ColorPt(0.75, 0.0, 0.0));
392 writer.WriteElement(element);
393
394 // Draw 'Submit' text
395 writer.WriteElement(builder.CreateTextBegin());
396
397 element = builder.CreateTextRun("Submit", Font.Create(doc, Font.StandardType1Font.e_helvetica_bold), 12);
398 element.GetGState().SetFillColor(new ColorPt(0, 0, 0));
399
400 if (button_down)
401 element.SetTextMatrix(1, 0, 0, 1, 33, 10);
402 else
403 element.SetTextMatrix(1, 0, 0, 1, 30, 13);
404 writer.WriteElement(element);
405 writer.WriteElement(builder.CreateTextEnd());
406
407 Obj stm = writer.End();
408
409 // Set the bounding box
410 stm.PutRect("BBox", 0, 0, 101, 37);
411 stm.PutName("Subtype", "Form");
412 return stm;
413 }
414 }
415 }
416}

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales