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

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales