< iOS samples

InteractiveFormsTest - Swift

The sample illustrates some basic PDFNet capabilities related to interactive forms (also known as AcroForms).

//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2017 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
    }
}