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

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales