Flatten, Create, Modify PDF Forms - PHP 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<?php
2//---------------------------------------------------------------------------------------
3// Copyright (c) 2001-2023 by Apryse Software Inc. All Rights Reserved.
4// Consult LICENSE.txt regarding license information.
5//---------------------------------------------------------------------------------------
6if(file_exists("../../../PDFNetC/Lib/PDFNetPHP.php"))
7include("../../../PDFNetC/Lib/PDFNetPHP.php");
8include("../../LicenseKey/PHP/LicenseKey.php");
9
10// Relative path to the folder containing the test files.
11$input_path = getcwd()."/../../TestFiles/";
12$output_path = $input_path."Output/";
13
14//---------------------------------------------------------------------------------------
15// This sample illustrates basic PDFNet capabilities related to interactive
16// forms (also known as AcroForms).
17//---------------------------------------------------------------------------------------
18
19function RenameAllFields($doc, $name, $field_nums = 1)
20{
21 $itr = $doc->GetFieldIterator($name);
22 for ($counter = 1; $itr->HasNext(); $itr = $doc->GetFieldIterator($name), ++$counter) {
23 $f = $itr->Current();
24 $tmp = (int)ceil($counter*1.0/$field_nums);
25 $f->Rename($name."-".$tmp);
26
27 }
28}
29
30// Note: The visual appearance of check-marks and radio-buttons in PDF documents is
31// not limited to CheckStyle-s. It is possible to create a visual appearance using
32// arbitrary glyph, text, raster image, or path object. Although most PDF producers
33// limit the options to the above 'standard' styles, using PDFNet you can generate
34// arbitrary appearances.
35
36function CreateCheckmarkAppearance($doc)
37{
38 // Create a checkmark appearance stream ------------------------------------
39 $build = new ElementBuilder();
40 $writer = new ElementWriter();
41 $writer->Begin($doc->GetSDFDoc());
42 $writer->WriteElement($build->CreateTextBegin());
43
44 $symbol = "4";
45 # other options are circle ("l"), diamond ("H"), cross ("\x35")
46 # See section D.4 "ZapfDingbats Set and Encoding" in PDF Reference Manual for
47 # the complete graphical map for ZapfDingbats font.
48
49 $checkmark = $build->CreateTextRun($symbol, Font::Create($doc->GetSDFDoc(), Font::e_zapf_dingbats), 1.0);
50 $writer->WriteElement($checkmark);
51 $writer->WriteElement($build->CreateTextEnd());
52
53 $stm = $writer->End();
54 $stm->PutRect("BBox", -0.2, -0.2, 1.0, 1.0); // Clip
55 $stm->PutName("Subtype", "Form");
56 return $stm;
57}
58
59function CreateCustomButtonAppearance($doc, $button_down)
60{
61 // Create a button appearance stream ------------------------------------
62 $build = new ElementBuilder();
63 $writer = new ElementWriter();
64 $writer->Begin($doc->GetSDFDoc());
65
66 // Draw background
67 $element = $build->CreateRect(0, 0, 101, 37);
68 $element->SetPathFill(true);
69 $element->SetPathStroke(false);
70 $element->GetGState()->SetFillColorSpace(ColorSpace::CreateDeviceGray());
71 $element->GetGState()->SetFillColor(new ColorPt(0.75, 0.0, 0.0));
72 $writer->WriteElement($element);
73
74 // Draw 'Submit' text
75 $writer->WriteElement($build->CreateTextBegin());
76
77 $text = "Submit";
78 $element = $build->CreateTextRun($text, Font::Create($doc->GetSDFDoc(), Font::e_helvetica_bold), 12.0);
79 $element->GetGState()->SetFillColor(new ColorPt(0.0, 0.0, 0.0));
80
81 if ($button_down)
82 $element->SetTextMatrix(1.0, 0.0, 0.0, 1.0, 33.0, 10.0);
83 else
84 $element->SetTextMatrix(1.0, 0.0, 0.0, 1.0, 30.0, 13.0);
85 $writer->WriteElement($element);
86
87 $writer->WriteElement($build->CreateTextEnd());
88
89 $stm = $writer->End();
90
91 // Set the bounding box
92 $stm->PutRect("BBox", 0, 0, 101, 37);
93 $stm->PutName("Subtype","Form");
94 return $stm;
95}
96
97 PDFNet::Initialize($LicenseKey);
98 PDFNet::GetSystemFontList(); // Wait for fonts to be loaded if they haven't already. This is done because PHP can run into errors when shutting down if font loading is still in progress.
99
100 //----------------------------------------------------------------------------------
101 // Example 1: Programatically create new Form Fields and Widget Annotations.
102 //----------------------------------------------------------------------------------
103
104 $doc = new PDFDoc();
105
106 // Create a blank new page and add some form fields.
107 $blank_page = $doc->PageCreate();
108
109 // Text Widget Creation
110 // Create an empty text widget with black text.
111 $text1 = TextWidget::Create($doc, new Rect(110.0, 700.0, 380.0, 730.0));
112 $text1->SetText("Basic Text Field");
113 $text1->RefreshAppearance();
114 $blank_page->AnnotPushBack($text1);
115 // Create a vertical text widget with blue text and a yellow background.
116 $text2 = TextWidget::Create($doc, new Rect(50.0, 400.0, 90.0, 730.0));
117 $text2->SetRotation(90);
118 // Set the text content.
119 $text2->SetText(" ****Lucky Stars!****");
120 // Set the font type, text color, font size, border color and background color.
121 $text2->SetFont(Font::Create($doc->GetSDFDoc(), Font::e_helvetica_oblique));
122 $text2->SetFontSize(28);
123 $text2->SetTextColor(new ColorPt(0.0, 0.0, 1.0), 3);
124 $text2->SetBorderColor(new ColorPt(0.0, 0.0, 0.0), 3);
125 $text2->SetBackgroundColor(new ColorPt(1.0, 1.0, 0.0), 3);
126 $text2->RefreshAppearance();
127 // Add the annotation to the page.
128 $blank_page->AnnotPushBack($text2);
129 // Create two new text widget with Field names employee.name.first and employee.name.last
130 // This logic shows how these widgets can be created using either a field name string or
131 // a Field object
132 $text3 = TextWidget::Create($doc, new Rect(110.0, 660.0, 380.0, 690.0), "employee.name.first");
133 $text3->SetText("Levi");
134 $text3->SetFont(Font::Create($doc->GetSDFDoc(), Font::e_times_bold));
135 $text3->RefreshAppearance();
136 $blank_page->AnnotPushBack($text3);
137 $emp_last_name = $doc->FieldCreate("employee.name.last", Field::e_text, "Ackerman");
138 $text4 = TextWidget::Create($doc, new Rect(110.0, 620.0, 380.0, 650.0), $emp_last_name);
139 $text4->SetFont(Font::Create($doc->GetSDFDoc(), Font::e_times_bold));
140 $text4->RefreshAppearance();
141 $blank_page->AnnotPushBack($text4);
142
143 // Signature Widget Creation (unsigned)
144 $signature1 = SignatureWidget::Create($doc, new Rect(110.0, 560.0, 260.0, 610.0));
145 $signature1->RefreshAppearance();
146 $blank_page->AnnotPushBack($signature1);
147
148 // CheckBox Widget Creation
149 // Create a check box widget that is not checked.
150 $check1 = CheckBoxWidget::Create($doc, new Rect(140.0, 490.0, 170.0, 520.0));
151 $check1->RefreshAppearance();
152 $blank_page->AnnotPushBack($check1);
153 // Create a check box widget that is checked.
154 $check2 = CheckBoxWidget::Create($doc, new Rect(190.0, 490.0, 250.0, 540.0), "employee.name.check1");
155 $check2->SetBackgroundColor(new ColorPt(1.0, 1.0, 1.0), 3);
156 $check2->SetBorderColor(new ColorPt(0.0, 0.0, 0.0), 3);
157 // Check the widget (by default it is unchecked).
158 $check2->SetChecked(true);
159 $check2->RefreshAppearance();
160 $blank_page->AnnotPushBack($check2);
161
162 // PushButton Widget Creation
163 $pushbutton1 = PushButtonWidget::Create($doc, new Rect(380.0, 490.0, 520.0, 540.0));
164 $pushbutton1->SetTextColor(new ColorPt(1.0, 1.0, 1.0), 3);
165 $pushbutton1->SetFontSize(36);
166 $pushbutton1->SetBackgroundColor(new ColorPt(0.0, 0.0, 0.0), 3);
167 // Add a caption for the pushbutton.
168 $pushbutton1->SetStaticCaptionText("PushButton");
169 $pushbutton1->RefreshAppearance();
170 $blank_page->AnnotPushBack($pushbutton1);
171
172 // ComboBox Widget Creation
173 $combo1 = ComboBoxWidget::Create($doc, new Rect(280.0, 560.0, 580.0, 610.0));
174 // Add options to the combobox widget.
175 $combo1->AddOption("Combo Box No.1");
176 $combo1->AddOption("Combo Box No.2");
177 $combo1->AddOption("Combo Box No.3");
178 // Make one of the options in the combo box selected by default.
179 $combo1->SetSelectedOption("Combo Box No.2");
180 $combo1->SetTextColor(new ColorPt(1.0, 0.0, 0.0), 3);
181 $combo1->SetFontSize(28);
182 $combo1->RefreshAppearance();
183 $blank_page->AnnotPushBack($combo1);
184
185 // ListBox Widget Creation
186 $list1 = ListBoxWidget::Create($doc, new Rect(400.0, 620.0, 580.0, 730.0));
187 // Add one option to the listbox widget.
188 $list1->AddOption("List Box No.1");
189 // Add multiple options to the listbox widget in a batch.
190 $list_options = array("List Box No.2", "List Box No.3");
191 $list1->AddOptions($list_options);
192 // Select some of the options in list box as default options
193 $list1->SetSelectedOptions($list_options);
194 // Enable list box to have multi-select when editing.
195 $list1->GetField()->SetFlag(Field::e_multiselect, true);
196 $list1->SetFont(Font::Create($doc->GetSDFDoc(), Font::e_times_italic));
197 $list1->SetTextColor(new ColorPt(1.0, 0.0, 0.0), 3);
198 $list1->SetFontSize(28);
199 $list1->SetBackgroundColor(new ColorPt(1.0, 1.0, 1.0), 3);
200 $list1->RefreshAppearance();
201 $blank_page->AnnotPushBack($list1);
202
203 // RadioButton Widget Creation
204 // Create a radio button group and add three radio buttons in it.
205 $radio_group = RadioButtonGroup::Create($doc, "RadioGroup");
206 $radiobutton1 = $radio_group->Add(new Rect(140.0, 410.0, 190.0, 460.0));
207 $radiobutton1->SetBackgroundColor(new ColorPt(1.0, 1.0, 0.0), 3);
208 $radiobutton1->RefreshAppearance();
209 $radiobutton2 = $radio_group->Add(new Rect(310.0, 410.0, 360.0, 460.0));
210 $radiobutton2->SetBackgroundColor(new ColorPt(0.0, 1.0, 0.0), 3);
211 $radiobutton2->RefreshAppearance();
212 $radiobutton3 = $radio_group->Add(new Rect(480.0, 410.0, 530.0, 460.0));
213 // Enable the third radio button. By default the first one is selected
214 $radiobutton3->EnableButton();
215 $radiobutton3->SetBackgroundColor(new ColorPt(0.0, 1.0, 1.0), 3);
216 $radiobutton3->RefreshAppearance();
217 $radio_group->AddGroupButtonsToPage($blank_page);
218
219 // Custom push button annotation creation
220 $custom_pushbutton1 = PushButtonWidget::Create($doc, new Rect(260.0, 320.0, 360.0, 360.0));
221 // Set the annotation appearance.
222 $custom_pushbutton1->SetAppearance(CreateCustomButtonAppearance($doc, false), Annot::e_normal);
223 // Create 'SubmitForm' action. The action will be linked to the button.
224 $url = FileSpec::CreateURL($doc->GetSDFDoc(), "http://www.pdftron.com");
225 $button_action = Action::CreateSubmitForm($url);
226 // Associate the above action with 'Down' event in annotations action dictionary.
227 $annot_action = $custom_pushbutton1->GetSDFObj()->PutDict("AA");
228 $annot_action->Put("D", $button_action->GetSDFObj());
229 $blank_page->AnnotPushBack($custom_pushbutton1);
230
231 $doc->PagePushBack($blank_page); // Add the page as the last page in the document.
232
233 // If you are not satisfied with the look of default auto-generated appearance
234 // streams you can delete "AP" entry from the Widget annotation and set
235 // "NeedAppearances" flag in AcroForm dictionary:
236 // $doc->GetAcroForm()->PutBool("NeedAppearances", true);
237 // This will force the viewer application to auto-generate new appearance streams
238 // every time the document is opened.
239 //
240 // Alternatively you can generate custom annotation appearance using ElementWriter
241 // and then set the "AP" entry in the widget dictionary to the new appearance
242 // stream.
243 //
244 // Yet another option is to pre-populate field entries with dummy text. When
245 // you edit the field values using PDFNet the new field appearances will match
246 // the old ones.
247
248 $doc->RefreshFieldAppearances();
249
250 $doc->Save($output_path."forms_test1.pdf", 0);
251 echo "Done.\n";
252
253 //----------------------------------------------------------------------------------
254 // Example 2:
255 // Fill-in forms / Modify values of existing fields.
256 // Traverse all form fields in the document (and print out their names).
257 // Search for specific fields in the document.
258 //----------------------------------------------------------------------------------
259
260 $doc = new PDFDoc($output_path."forms_test1.pdf");
261 $doc->InitSecurityHandler();
262
263 $itr = $doc->GetFieldIterator();
264 $field_names = array();
265 for(; $itr->HasNext(); $itr->Next())
266 {
267 $cur_field_name = $itr->Current()->GetName();
268 // Add one to the count for this field name for later processing
269 if(isset($field_names [$cur_field_name])){
270 $field_names [$cur_field_name] += 1;
271 }
272 else{
273 $field_names [$cur_field_name] = 1;
274 }
275 echo nl2br("Field name: ".$itr->Current()->GetName()."\n");
276 echo nl2br("Field partial name: ".$itr->Current()->GetPartialName()."\n");
277
278 echo "Field type: ";
279 $type = $itr->Current()->GetType();
280 $str_val = $itr->Current()->GetValueAsString();
281
282 switch($type)
283 {
284 case Field::e_button:
285 echo nl2br("Button\n");
286 break;
287 case Field::e_radio:
288 echo nl2br("Radio button: Value = ".$str_val."\n");
289 break;
290 case Field::e_check:
291 $itr->Current()->SetValue(true);
292 echo nl2br("Check box: Value = ".$str_val."\n");
293 break;
294 case Field::e_text:
295 {
296 echo nl2br("Text\n");
297 // Edit all variable text in the document
298 $itr->Current()->SetValue("This is a new value. The old one was: ".$str_val);
299 }
300 break;
301 case Field::e_choice: echo nl2br("Choice\n"); break;
302 case Field::e_signature: echo nl2br("Signature\n"); break;
303 }
304
305 echo "------------------------------\n";
306 }
307
308 // Search for a specific field
309 $f = $doc->GetField("employee.name.first");
310 if ($f)
311 {
312 echo nl2br("Field search for ".$f->GetName()." was successful\n");
313 }
314 else
315 {
316 echo nl2br("Field search failed\n");
317 }
318
319 // Regenerate field appearances.
320 $doc->RefreshFieldAppearances();
321 $doc->Save(($output_path."forms_test_edit.pdf"), 0);
322 echo nl2br("Done.\n");
323
324 //----------------------------------------------------------------------------------
325 // Sample: Form templating
326 // Replicate pages and form data within a document. Then rename field names to make
327 // them unique.
328 //----------------------------------------------------------------------------------
329
330 // Sample: Copying the page with forms within the same document
331 $doc = new PDFDoc($output_path."forms_test1.pdf");
332 $doc->InitSecurityHandler();
333
334 $src_page = $doc->GetPage(1);
335 $doc->PagePushBack($src_page); // Append several copies of the first page
336 $doc->PagePushBack($src_page); // Note that forms are successfully copied
337 $doc->PagePushBack($src_page);
338 $doc->PagePushBack($src_page);
339
340 // Now we rename fields in order to make every field unique.
341 // You can use this technique for dynamic template filling where you have a 'master'
342 // form page that should be replicated, but with unique field names on every page.
343
344 foreach($field_names as $key => $val){
345 RenameAllFields($doc, $key, $val);
346 }
347
348 $doc->Save($output_path."forms_test1_cloned.pdf", 0);
349 echo nl2br("Done.\n");
350
351 //----------------------------------------------------------------------------------
352 // Sample:
353 // Flatten all form fields in a document.
354 // Note that this sample is intended to show that it is possible to flatten
355 // individual fields. PDFNet provides a utility function PDFDoc.FlattenAnnotations()
356 // that will automatically flatten all fields.
357 //----------------------------------------------------------------------------------
358 $doc = new PDFDoc($output_path."forms_test1.pdf");
359 $doc->InitSecurityHandler();
360
361 // Traverse all pages
362 if (false) {
363 $doc->FlattenAnnotations();
364 }
365 else // Manual flattening
366 {
367
368 for ($pitr = $doc->GetPageIterator(); $pitr->HasNext(); $pitr->Next())
369 {
370 $page = $pitr->Current();
371 for ($i = (int)($page->GetNumAnnots())-1; $i>=0; --$i)
372 {
373 $annot = $page->GetAnnot($i);
374 if ($annot->GetType() == Annot::e_Widget)
375 {
376 $annot->Flatten($page);
377 }
378 }
379 }
380 }
381
382
383 $doc->Save(($output_path."forms_test1_flattened.pdf"), 0);
384 PDFNet::Terminate();
385 echo nl2br("Done.\n");
386?>

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales