Some test text!

menu
search

PDF interactive forms (AcroForms) in Swift

Sample Swift code for using PDFTron 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. Learn more about our PDF Form Filler SDK.

Get StartedSamplesDownload

To run this sample, get started with a free trial of PDFTron SDK.

//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2019 by PDFTron Systems Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.
//---------------------------------------------------------------------------------------

import PDFNet
import Foundation

//---------------------------------------------------------------------------------------
// This sample illustrates basic PDFNet capabilities related to interactive
// forms (also known as AcroForms).
//---------------------------------------------------------------------------------------

func RenameAllFields(doc: PTPDFDoc, name: String) {
    var itr: PTFieldIterator = doc.getFieldIterator(withName: name)
    var counter: Int = 0
    while itr.hasNext() {
        let f: PTField = itr.current()
        f.rename(name + ("\(counter)"))
        itr = doc.getFieldIterator(withName: name)
        counter += 1
    }
}

enum CheckStyle : Int {
    case e_tick
    case e_circle
    case e_cross
    case e_diamond
    case e_square
    case e_star
}

// Note: The visual appearance of check-marks and radio-buttons in PDF documents is
// not limited to CheckStyle-s. It is possible to create a visual appearance using
// arbitrary glyph, text, raster image, or path object. Although most PDF producers
// limit the options to the above 'standard' styles, using PDFNet you can generate
// arbitrary appearances.
func CreateCheckmarkAppearance(doc: PTPDFDoc, style: CheckStyle) -> PTObj {
    // Create a checkmark appearance stream ------------------------------------
    let build: PTElementBuilder = PTElementBuilder()
    let writer: PTElementWriter = PTElementWriter()
    writer.writerBegin(with: doc.getSDFDoc(), compress: true)
    writer.write(build.createTextBegin())
    
    var symbol: String
    switch style {
    case .e_circle:
        symbol = "\u{154}"
    case .e_diamond:
        symbol = "\u{165}"
    case .e_cross:
        symbol = "\u{65}"
    case .e_square:
        symbol = "\u{156}"
    case .e_star:
        symbol = "\u{110}"
    default:
        // e_tick
        symbol = "\u{64}"
    }
    
    let checkmark: PTElement = build.createTextRun(withFont: symbol, font: PTFont.create(doc.getSDFDoc(), type: e_ptzapf_dingbats, embed: false), font_sz: 1)
    writer.write(checkmark)
    writer.write(build.createTextEnd())
    
    let stm: PTObj = writer.end()
    stm.putRect("BBox", x1: -0.2, y1: -0.2, x2: 1, y2: 1)   // Clip
    stm.putName("Subtype", name: "Form")
    return stm
}

func CreateButtonAppearance(doc: PTPDFDoc, button_down: Bool) -> PTObj {
    // Create a button appearance stream ------------------------------------
    let build: PTElementBuilder = PTElementBuilder()
    let writer: PTElementWriter = PTElementWriter()
    writer.writerBegin(with: doc.getSDFDoc(), compress: true)
    
    // Draw background
    var element: PTElement = build.createRect(0, y: 0, width: 101, height: 137)
    element.setPathFill(true)
    element.setPathStroke(false)
    element.getGState().setFill(PTColorSpace.createDeviceGray())
    element.getGState().setFillColor(with: PTColorPt(x: 0.75, y: 0, z: 0, w: 0))
    writer.write(element)
    
    // Draw 'Submit' text
    writer.write(build.createTextBegin())
    do {
        let text = "Submit"
        element = build.createTextRun(withFont: text, font: PTFont.create(doc.getSDFDoc(), type: e_pthelvetica_bold, embed: false), font_sz: 12)
        element.getGState().setFillColor(with: PTColorPt(x: 0, y: 0, z: 0, w: 0))
        
        if button_down {
            element.setTextMatrix(with: PTMatrix2D(a: 1, b: 0, c: 0, d: 1, h: 33, v: 10))
        }
        else {
            element.setTextMatrix(with: PTMatrix2D(a: 1, b: 0, c: 0, d: 1, h: 30, v: 13))
        }
        writer.write(element)
    }
    writer.write(build.createTextEnd())
    
    let stm: PTObj = writer.end()
    
    // Set the bounding box
    stm.putRect("BBox", x1: 0, y1: 0, x2: 101, y2: 37)
    stm.putName("Subtype", name: "Form")
    return stm
}

func runInteractiveFormsTest() -> Int {
    return autoreleasepool {
        var ret: Int = 0
        
        
        //----------------------------------------------------------------------------------
        // Example 1: Programatically create new Form Fields and Widget Annotations.
        //----------------------------------------------------------------------------------
        do {
            try PTPDFNet.catchException {
                let doc: PTPDFDoc = PTPDFDoc()
                let blank_page: PTPage = doc.pageCreate(PTPDFRect(x1: 0, y1: 0, x2: 612, y2: 792)) // Create a blank new page and add some form fields.

                // Create new fields.
                let emp_first_name: PTField = doc.fieldCreate(with: "employee.name.first", type: e_pttext, field_value: "John", def_field_value: "")
                let emp_last_name: PTField = doc.fieldCreate(with: "employee.name.last", type: e_pttext, field_value: "Doe", def_field_value: "")
                let emp_last_check1: PTField = doc.fieldCreate(with: "employee.name.check1", type: e_ptcheck, field_value: "Yes", def_field_value: "")
                
                let submit: PTField = doc.fieldCreate("submit", type: e_ptbutton, field_value: PTObj())
                
                // Create page annotations for the above fields.
                
                // Create text annotations
                let annot1: PTWidget = PTWidget.create(doc.getSDFDoc(), pos: PTPDFRect(x1: 50, y1: 550, x2: 350, y2: 600), field: emp_first_name)
                let annot2: PTWidget = PTWidget.create(doc.getSDFDoc(), pos: PTPDFRect(x1: 50, y1: 450, x2: 350, y2: 500), field: emp_last_name)
                
                // Create a check-box annotation
                let annot3: PTWidget = PTWidget.create(doc.getSDFDoc(), pos: PTPDFRect(x1: 64, y1: 356, x2: 120, y2: 410), field: emp_last_check1)
                // Set the annotation appearance for the "Yes" state...
                annot3.setAppearance(CreateCheckmarkAppearance(doc: doc, style: .e_tick), annot_state: e_ptnormal, app_state: "Yes")
                
                // Create button annotation
                let annot4: PTWidget = PTWidget.create(doc.getSDFDoc(), pos: PTPDFRect(x1: 64, y1: 284, x2: 163, y2: 320), field: submit)
                // Set the annotation appearances for the down and up state...
                annot4.setAppearance(CreateButtonAppearance(doc: doc, button_down: false), annot_state: e_ptnormal, app_state: "")
                annot4.setAppearance(CreateButtonAppearance(doc: doc, button_down: true), annot_state: e_ptdown, app_state: "")
                
                // Create 'SubmitForm' action. The action will be linked to the button.
                let url = PTFileSpec.createURL(doc.getSDFDoc(), url: "http://www.pdftron.com")
                let button_action: PTAction = PTAction.createSubmitForm(url)
                
                // Associate the above action with 'Down' event in annotations action dictionary.
                let annot_action: PTObj = annot4.getSDFObj().putDict("AA")
                annot_action.put("D", obj: button_action.getSDFObj())
                
                blank_page.annotPushBack(annot1)   // Add annotations to the page
                blank_page.annotPushBack(annot2)
                blank_page.annotPushBack(annot3)
                blank_page.annotPushBack(annot4)
                
                doc.pagePushBack(blank_page)
                
                // Add the page as the last page in the document.
                // If you are not satisfied with the look of default auto-generated appearance
                // streams you can delete "AP" entry from the Widget annotation and set
                // "NeedAppearances" flag in AcroForm dictionary:
                //    doc.GetAcroForm().PutBool("NeedAppearances", true);
                // This will force the viewer application to auto-generate new appearance streams
                // every time the document is opened.
                //
                // Alternatively you can generate custom annotation appearance using ElementWriter
                // and then set the "AP" entry in the widget dictionary to the new appearance
                // stream.
                //
                // Yet another option is to pre-populate field entries with dummy text. When
                // you edit the field values using PDFNet the new field appearances will match
                // the old ones.
                
                //doc.getAcroForm().putBool("NeedAppearances", value: true)
                doc.refreshFieldAppearances()
                
                doc.save(toFile: URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]).appendingPathComponent("forms_test1.pdf").path, flags: 0)
                print("Done.")
            }
        } catch let e as NSError {
            print("\(e)")
            ret = 1
        }
        
        //----------------------------------------------------------------------------------
        // Example 2:
        // Fill-in forms / Modify values of existing fields.
        // Traverse all form fields in the document (and print out their names).
        // Search for specific fields in the document.
        //----------------------------------------------------------------------------------

        do {
            try PTPDFNet.catchException {
                let doc: PTPDFDoc = PTPDFDoc(filepath: URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]).appendingPathComponent("forms_test1.pdf").path)
                doc.initSecurityHandler()
                
                let itr: PTFieldIterator = doc.getFieldIterator()
                while itr.hasNext() {
                    print("Field name: \(itr.current().getName()!)")
                    print("Field partial name: \(itr.current().getPartialName()!)")
                    
                    print("Field type: ")
                    let type: PTFieldType = itr.current().getType()
                    let str_val = itr.current().getValueAsString()
                    
                    switch type {
                    case e_ptcheck:
                        itr.current().setValueWith(true)
                        print("Check box: Value = \(str_val!)")
                    case e_ptbutton:
                        print("Button")
                    case e_ptradio:
                        print("Radio button: Value = \(str_val!)")
                    case e_pttext:
                        print("Text")
                        // Edit all variable text in the document
                        itr.current().setValue("This is a new value. The old one was: \(str_val!)")
                    case e_ptchoice:
                        print("Choice")
                    case e_ptsignature:
                        print("Signature")
                    default:
                        break
                    }
                    
                    print("------------------------------")
                    itr.next()
                }
                
                // Search for a specific field
                if let f: PTField = doc.getField("employee.name.first") {
                    print("Field search for \(f.getName()!) was successful")
                }
                else {
                    print("Field search failed")
                }
                
                // Regenerate field appearances.
                doc.refreshFieldAppearances()
                doc.save(toFile: URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]).appendingPathComponent("forms_test_edit.pdf").path, flags: 0)
                print("Done.")

            }
        } catch let e as NSError {
            print("\(e)")
            ret = 1
        }
        
        //----------------------------------------------------------------------------------
        // Sample: Form templating
        // Replicate pages and form data within a document. Then rename field names to make
        // them unique.
        //----------------------------------------------------------------------------------
        do {
            try PTPDFNet.catchException {
                // Sample: Copying the page with forms within the same document
                let doc: PTPDFDoc = PTPDFDoc(filepath: URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]).appendingPathComponent("forms_test1.pdf").path)
                doc.initSecurityHandler()
                
                let src_page: PTPage = doc.getPage(1)
                doc.pagePushBack(src_page)
                // Append several copies of the first page
                doc.pagePushBack(src_page)
                // Note that forms are successfully copied
                doc.pagePushBack(src_page)
                doc.pagePushBack(src_page)
                
                // Now we rename fields in order to make every field unique.
                // You can use this technique for dynamic template filling where you have a 'master'
                // form page that should be replicated, but with unique field names on every page.
                RenameAllFields(doc: doc, name: "employee.name.first")
                RenameAllFields(doc: doc, name: "employee.name.last")
                RenameAllFields(doc: doc, name: "employee.name.check1")
                RenameAllFields(doc: doc, name: "submit")
                
                doc.save(toFile: URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]).appendingPathComponent("forms_test1_cloned.pdf").path, flags: 0)
                print("Done.")
            }
        } catch let e as NSError {
            print("\(e)")
            ret = 1
        }
        
        //----------------------------------------------------------------------------------
        // Sample:
        // Flatten all form fields in a document.
        // Note that this sample is intended to show that it is possible to flatten
        // individual fields. PDFNet provides a utility function PDFDoc.FlattenAnnotations()
        // that will automatically flatten all fields.
        //----------------------------------------------------------------------------------
        do {
            try PTPDFNet.catchException {
                let doc: PTPDFDoc = PTPDFDoc(filepath: URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]).appendingPathComponent("forms_test1.pdf").path)
                doc.initSecurityHandler()
                
                // Traverse all pages
                if true {
                    doc.flattenAnnotations(false)
                } else { // Manual flattening
//                    let pitr: PTPageIterator = doc.getPageIterator(1)
//                    while pitr.hasNext() {
//                        let page: PTPage = pitr.current()
//                        if let annots: PTObj = page.getAnnots() {
//                            // Look for all widget annotations (in reverse order)
//                            var i: Int = Int(annots.size()) - 1
//                            while i >= 0 {
//                                if (annots.getAt(UInt(i)).get("Subtype").value().getName() == "Widget") {
//                                    let field: PTField = PTField(field_dict: annots.getAt(UInt(i)))
//                                    field.flatten(page)
//
//                                    // Another way of making a read only field is by modifying
//                                    // field's e_read_only flag:
//                                    //    field.SetFlag(Field::e_read_only, true);
//                                }
//                                i -= 1
//                            }
//                        }
//                        pitr.next()
//                    }
                }
                doc.save(toFile: URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]).appendingPathComponent("forms_test1_flattened.pdf").path, flags: 0)
                print("Done.")
            }
        } catch let e as NSError {
            print("\(e)")
            ret = 1
        }
        
        return ret
    }
}
close

Free Trial

Get unlimited trial usage of PDFTron SDK to bring accurate, reliable, and fast document processing capabilities to any application or workflow.

Select a platform to get started with your free trial.

Unlimited usage. No email address required.

PDFTron Receives USD$71 Million Growth Investment Led By Silversmith Capital Partners

Learn More
close