Flatten, Create, Modify PDF Forms - Go 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-2021 by PDFTron Systems Inc. All Rights Reserved.
3// Consult LICENSE.txt regarding license information.
4//---------------------------------------------------------------------------------------
5
6package main
7import (
8 "fmt"
9 "strconv"
10 "os"
11 "math"
12 . "pdftron"
13)
14
15import "pdftron/Samples/LicenseKey/GO"
16
17// Relative path to the folder containing the test files.
18var inputPath = "../../TestFiles/"
19var outputPath = "../../TestFiles/Output/"
20
21//---------------------------------------------------------------------------------------
22// This sample illustrates basic PDFNet capabilities related to interactive
23// forms (also known as AcroForms).
24//---------------------------------------------------------------------------------------
25
26// fieldNums has to be greater than 0.
27func RenameAllFields(doc PDFDoc, name string, fieldNums int){
28 itr := doc.GetFieldIterator(name)
29 counter := 1
30 for itr.HasNext(){
31 f := itr.Current()
32 radioCounter := (int)(math.Ceil(float64(counter/fieldNums)))
33 f.Rename(name + "-" + strconv.Itoa(radioCounter))
34 itr = doc.GetFieldIterator(name)
35 counter = counter + 1
36 }
37}
38func CreateCustomButtonAppearance(doc PDFDoc, buttonDown bool) Obj {
39 // Create a button appearance stream ------------------------------------
40 build := NewElementBuilder()
41 writer := NewElementWriter()
42 writer.Begin(doc.GetSDFDoc())
43
44 // Draw background
45 element := build.CreateRect(0.0, 0.0, 101.0, 37.0)
46 element.SetPathFill(true)
47 element.SetPathStroke(false)
48 element.GetGState().SetFillColorSpace(ColorSpaceCreateDeviceGray())
49 element.GetGState().SetFillColor(NewColorPt(0.75, 0.0, 0.0))
50 writer.WriteElement(element)
51
52 // Draw 'Submit' text
53 writer.WriteElement(build.CreateTextBegin())
54 text := "Submit"
55 element = build.CreateTextRun(text, FontCreate(doc.GetSDFDoc(), FontE_helvetica_bold), 12.0)
56 element.GetGState().SetFillColor(NewColorPt(0.0, 0.0, 0.0))
57
58 if buttonDown{
59 element.SetTextMatrix(1.0, 0.0, 0.0, 1.0, 33.0, 10.0)
60 }else{
61 element.SetTextMatrix(1.0, 0.0, 0.0, 1.0, 30.0, 13.0)
62 }
63 writer.WriteElement(element)
64
65 writer.WritePlacedElement(build.CreateTextEnd())
66
67 stm := writer.End()
68
69 // Set the bounding box
70 stm.PutRect("BBox", 0.0, 0.0, 101.0, 37.0)
71 stm.PutName("Subtype","Form")
72 return stm
73}
74func main(){
75 PDFNetInitialize(PDFTronLicense.Key)
76
77 // The map (vector) used to store the name and count of all fields.
78 // This is used later on to clone the fields
79 fieldNames:= make(map[string]int)
80 //----------------------------------------------------------------------------------
81 // Example 1: Programatically create new Form Fields and Widget Annotations.
82 //----------------------------------------------------------------------------------
83
84 doc := NewPDFDoc()
85
86 // Create a blank new page and Add some form fields.
87 blankPage := doc.PageCreate()
88
89 // Text Widget Creation
90 // Create an empty text widget with black text.
91 text1 := TextWidgetCreate(doc, NewRect(110.0, 700.0, 380.0, 730.0))
92 text1.SetText("Basic Text Field")
93 text1.RefreshAppearance()
94 blankPage.AnnotPushBack(text1)
95 // Create a vertical text widget with blue text and a yellow background.
96 text2 := TextWidgetCreate(doc, NewRect(50.0, 400.0, 90.0, 730.0))
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(FontCreate(doc.GetSDFDoc(), FontE_helvetica_oblique))
102 text2.SetFontSize(28)
103 text2.SetTextColor(NewColorPt(0.0, 0.0, 1.0), 3)
104 text2.SetBorderColor(NewColorPt(0.0, 0.0, 0.0), 3)
105 text2.SetBackgroundColor(NewColorPt(1.0, 1.0, 0.0), 3)
106 text2.RefreshAppearance()
107 // Add the annotation to the page.
108 blankPage.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 := TextWidgetCreate(doc, NewRect(110.0, 660.0, 380.0, 690.0), "employee.name.first")
113 text3.SetText("Levi")
114 text3.SetFont(FontCreate(doc.GetSDFDoc(), FontE_times_bold))
115 text3.RefreshAppearance()
116 blankPage.AnnotPushBack(text3)
117 empLastName := doc.FieldCreate("employee.name.last", FieldE_text, "Ackerman")
118 text4 := TextWidgetCreate(doc, NewRect(110.0, 620.0, 380.0, 650.0), empLastName)
119 text4.SetFont(FontCreate(doc.GetSDFDoc(), FontE_times_bold))
120 text4.RefreshAppearance()
121 blankPage.AnnotPushBack(text4)
122
123 // Signature Widget Creation (unsigned)
124 signature1 := SignatureWidgetCreate(doc, NewRect(110.0, 560.0, 260.0, 610.0))
125 signature1.RefreshAppearance()
126 blankPage.AnnotPushBack(signature1)
127
128 // CheckBox Widget Creation
129 // Create a check box widget that is not checked.
130 check1 := CheckBoxWidgetCreate(doc, NewRect(140.0, 490.0, 170.0, 520.0))
131 check1.RefreshAppearance()
132 blankPage.AnnotPushBack(check1)
133 // Create a check box widget that is checked.
134 check2 := CheckBoxWidgetCreate(doc, NewRect(190.0, 490.0, 250.0, 540.0), "employee.name.check1")
135 check2.SetBackgroundColor(NewColorPt(1.0, 1.0, 1.0), 3)
136 check2.SetBorderColor(NewColorPt(0.0, 0.0, 0.0), 3)
137 // Check the widget (by default it is unchecked).
138 check2.SetChecked(true)
139 check2.RefreshAppearance()
140 blankPage.AnnotPushBack(check2)
141
142 // PushButton Widget Creation
143 pushbutton1 := PushButtonWidgetCreate(doc, NewRect(380.0, 490.0, 520.0, 540.0))
144 pushbutton1.SetTextColor(NewColorPt(1.0, 1.0, 1.0), 3)
145 pushbutton1.SetFontSize(36)
146 pushbutton1.SetBackgroundColor(NewColorPt(0.0, 0.0, 0.0), 3)
147 // Add a caption for the pushbutton.
148 pushbutton1.SetStaticCaptionText("PushButton")
149 pushbutton1.RefreshAppearance()
150 blankPage.AnnotPushBack(pushbutton1)
151
152 // ComboBox Widget Creation
153 combo1 := ComboBoxWidgetCreate(doc, NewRect(280.0, 560.0, 580.0, 610.0));
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(NewColorPt(1.0, 0.0, 0.0), 3)
161 combo1.SetFontSize(28)
162 combo1.RefreshAppearance()
163 blankPage.AnnotPushBack(combo1)
164
165 // ListBox Widget Creation
166 list1 := ListBoxWidgetCreate(doc, NewRect(400.0, 620.0, 580.0, 730.0))
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 listOptions := NewVectorString()
171 listOptions.Add("List Box No.2")
172 listOptions.Add("List Box No.3")
173 list1.AddOptions(listOptions)
174 // Select some of the options in list box as default options
175 list1.SetSelectedOptions(listOptions)
176 // Enable list box to have multi-select when editing.
177 list1.GetField().SetFlag(FieldE_multiselect, true)
178 list1.SetFont(FontCreate(doc.GetSDFDoc(),FontE_times_italic))
179 list1.SetTextColor(NewColorPt(1.0, 0.0, 0.0), 3)
180 list1.SetFontSize(28)
181 list1.SetBackgroundColor(NewColorPt(1.0, 1.0, 1.0), 3)
182 list1.RefreshAppearance()
183 blankPage.AnnotPushBack(list1)
184
185 // RadioButton Widget Creation
186 // Create a radio button group and Add three radio buttons in it.
187 radioGroup := RadioButtonGroupCreate(doc, "RadioGroup")
188 radiobutton1 := radioGroup.Add(NewRect(140.0, 410.0, 190.0, 460.0))
189 radiobutton1.SetBackgroundColor(NewColorPt(1.0, 1.0, 0.0), 3)
190 radiobutton1.RefreshAppearance()
191 radiobutton2 := radioGroup.Add(NewRect(310.0, 410.0, 360.0, 460.0))
192 radiobutton2.SetBackgroundColor(NewColorPt(0.0, 1.0, 0.0), 3)
193 radiobutton2.RefreshAppearance()
194 radiobutton3 := radioGroup.Add(NewRect(480.0, 410.0, 530.0, 460.0))
195 // Enable the third radio button. By default the first one is selected
196 radiobutton3.EnableButton()
197 radiobutton3.SetBackgroundColor(NewColorPt(0.0, 1.0, 1.0), 3)
198 radiobutton3.RefreshAppearance()
199 radioGroup.AddGroupButtonsToPage(blankPage)
200
201 // Custom push button annotation creation
202 customPushbutton1 := PushButtonWidgetCreate(doc, NewRect(260.0, 320.0, 360.0, 360.0))
203 // Set the annotation appearance.
204 customPushbutton1.SetAppearance(CreateCustomButtonAppearance(doc, false), AnnotE_normal)
205 // Create 'SubmitForm' action. The action will be linked to the button.
206 url := FileSpecCreateURL(doc.GetSDFDoc(), "http://www.pdftron.com")
207 buttonAction := ActionCreateSubmitForm(url)
208 // Associate the above action with 'Down' event in annotations action dictionary.
209 annotAction := customPushbutton1.GetSDFObj().PutDict("AA")
210 annotAction.Put("D", buttonAction.GetSDFObj())
211 blankPage.AnnotPushBack(customPushbutton1)
212
213 // Add the page as the last page in the document.
214 doc.PagePushBack(blankPage)
215
216 // If you are not satisfied with the look of default auto-generated appearance
217 // streams you can delete "AP" entry from the Widget annotation and set
218 // "NeedAppearances" flag in AcroForm dictionary:
219 // doc.GetAcroForm().PutBool("NeedAppearances", true);
220 // This will force the viewer application to auto-generate new appearance streams
221 // every time the document is opened.
222 //
223 // Alternatively you can generate custom annotation appearance using ElementWriter
224 // and then set the "AP" entry in the widget dictionary to the new appearance
225 // stream.
226 //
227 // Yet another option is to pre-populate field entries with dummy text. When
228 // you edit the field values using PDFNet the new field appearances will match
229 // the old ones.
230
231 //doc.GetAcroForm().PutBool("NeedAppearances", true)
232 doc.RefreshFieldAppearances()
233
234 doc.Save(outputPath + "forms_test1.pdf", uint(0))
235 doc.Close()
236 fmt.Println("Done.")
237
238 //----------------------------------------------------------------------------------
239 // Example 2:
240 // Fill-in forms / Modify values of existing fields.
241 // Traverse all form fields in the document (and sys.stdout.write(out their names).
242 // Search for specific fields in the document.
243 //----------------------------------------------------------------------------------
244
245 doc = NewPDFDoc(outputPath + "forms_test1.pdf")
246 doc.InitSecurityHandler()
247
248 itr := doc.GetFieldIterator()
249 for itr.HasNext(){
250 curFieldName := itr.Current().GetName()
251 // Add one to the count for this field name for later processing
252 if val, found := fieldNames[curFieldName]; found{
253 fieldNames[curFieldName] = val + 1
254 }else{
255 fieldNames[curFieldName] = 1
256 }
257
258 fmt.Println("Field name: " + itr.Current().GetName())
259 fmt.Println("Field partial name: " + itr.Current().GetPartialName())
260 os.Stdout.Write([]byte("Field type: "))
261 fieldType := itr.Current().GetType()
262 strVal := itr.Current().GetValueAsString()
263 if (fieldType == FieldE_button){
264 os.Stdout.Write([]byte("Button\n"))
265 }else if (fieldType == FieldE_radio){
266 os.Stdout.Write([]byte("Radio button: Value = " + strVal + "\n"))
267 }else if (fieldType == FieldE_check){
268 itr.Current().SetValue(true)
269 os.Stdout.Write([]byte("Check box: Value = " + strVal + "\n"))
270 }else if (fieldType == FieldE_text){
271 os.Stdout.Write([]byte("Text" + "\n"))
272 // Edit all variable text in the document
273 if itr.Current().GetValue().GetMp_obj().Swigcptr() != 0 {
274 old_value := itr.Current().GetValueAsString();
275 itr.Current().SetValue("This is a new value. The old one was: " + old_value)
276 }
277 }else if (fieldType == FieldE_choice){
278 os.Stdout.Write([]byte("Choice" + "\n"))
279 }else if (fieldType == FieldE_signature){
280 os.Stdout.Write([]byte("Signature" + "\n"))
281 }
282 fmt.Println("------------------------------")
283 itr.Next()
284 }
285 // Search for a specific field
286 f := doc.GetField("employee.name.first")
287 if f.GetMp_field().Swigcptr() != 0{
288 fmt.Println("Field search for " + f.GetName() + " was successful")
289 }else{
290 fmt.Println("Field search failed")
291 }
292
293 // Regenerate field appearances.
294 doc.RefreshFieldAppearances()
295 doc.Save(outputPath + "forms_test_edit.pdf", uint(0))
296 doc.Close()
297 fmt.Println("Done.")
298
299 //----------------------------------------------------------------------------------
300 // Sample: Form templating
301 // Replicate pages and form data within a document. Then rename field names to make
302 // them unique.
303 //----------------------------------------------------------------------------------
304
305 // Sample: Copying the page with forms within the same document
306 doc = NewPDFDoc(outputPath + "forms_test1.pdf")
307 doc.InitSecurityHandler()
308
309 srcPage := doc.GetPage(1)
310 doc.PagePushBack(srcPage) // Append several copies of the first page
311 doc.PagePushBack(srcPage) // Note that forms are successfully copied
312 doc.PagePushBack(srcPage)
313 doc.PagePushBack(srcPage)
314
315 // Now we rename fields in order to make every field unique.
316 // You can use this technique for dynamic template filling where you have a 'master'
317 // form page that should be replicated, but with unique field names on every page.
318 for key, curField := range fieldNames{
319 RenameAllFields(doc, key, curField)
320 }
321
322 doc.Save(outputPath + "forms_test1_cloned.pdf", uint(0))
323 doc.Close()
324 fmt.Println("Done.")
325
326 //----------------------------------------------------------------------------------
327 // Sample:
328 // Flatten all form fields in a document.
329 // Note that this sample is intended to show that it is possible to flatten
330 // individual fields. PDFNet provides a utility function PDFDoc.FlattenAnnotations()
331 // that will automatically flatten all fields.
332 //----------------------------------------------------------------------------------
333 doc = NewPDFDoc(outputPath + "forms_test1.pdf")
334 doc.InitSecurityHandler()
335
336 // Traverse all pages
337 if false{
338 doc.FlattenAnnotations()
339 }else{ // Manual flattening
340 for pitr := doc.GetPageIterator(); pitr.HasNext(); pitr.Next(){
341 page := pitr.Current()
342 for i := int(page.GetNumAnnots()) - 1; i >= 0; i-- {
343 annot := page.GetAnnot(uint(i))
344 if (annot.GetType() == AnnotE_Widget){
345 annot.Flatten(page)
346 }
347 }
348 }
349 }
350 doc.Save(outputPath + "forms_test1_flattened.pdf", uint(0))
351 doc.Close()
352 PDFNetTerminate()
353 fmt.Println("Done.")
354}

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales