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

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales