Interactive Features

From
Revision as of 17:27, 23 May 2018 by Adminko (talk | contribs) (Forms code examples)
Jump to: navigation, search

This article describes the API related to the PDF features that allow a user to interact with a document when viewing it using the mouse and keyboard e.g. links, annotations, actions, events, various form controls etc.

Viewer preferences

The preferrred viewing parameters can be set using the document-level API described in details here.

Document-level navigation

The PDF standard describes several approaches for implementing a document-level navigation. For these purposes a few PDF objects were defined, namely Destinations and Document Outlines (sometimes referred as “Bookmarks”).

Destinations

A destination defines a particular view of a document, consisting of the following items:

  • The page of the document that shall be displayed
  • The location of the document window on that page
  • The magnification factor (zoom)

Destinations may be associated with outline items (see the section 12.3.3 “Document Outline” of the specification), annotations or actions. In each case, the destination specifies the view of the document that shall be presented when the outline item or annotation is being opened or the action is being performed. A destination may be specified either explicitly, by creating an instance of the Destination class and defining its properties or indirectly, using its name.

Explicit destinations

Explicit destinations in PDF documents, as the name suggests, are defined in place, explicitly, with all the parameters needed for the navigation provided at the moment of creation.

Named destinations

Named destinations are in fact references to the destination objects present in the document's internal registry and describing the actual navigation. These destination objects should be registered in the PDF document with names, and in case of Fixed layout API, it's the FixedDocument.Destinations collection they should be added into to become usable.


The code below demonstrates how to add two link annotations onto the page of the PDF document and set their targets using explicit and named Destination objects.

// create output PDF file
using (FileStream outputStream = new FileStream("navigation.pdf", FileMode.Create, FileAccess.Write))
{
    // create new PDF document
    FixedDocument document = new FixedDocument();
    document.Pages.Add(new Page());
    
    // second page we are going to add a link to
    Page page2 = new Page();
    document.Pages.Add(page2);
    
    // create and register the named destination object
    document.Destinations.Add("myDestination",new Destination(page2, new DestinationTypeFit()));
    
    // add and place text object containing the text for our links
    TextObject textObject = new TextObject(StandardFonts.Helvetica, 14);
    textObject.AppendText("Navigate to page 2 using explicit destination");
    textObject.MoveToNextLine(0,-30);
    textObject.AppendText("Navigate to page 2 using named destination");
    document.Pages[0].Content.Translate(10,815);
    document.Pages[0].Content.AppendText(textObject);
    
    // create link annotation using explicit destination
    LinkAnnotation linkAnnotation1 = new LinkAnnotation(new Boundary(10, 810, 300, 830));
    linkAnnotation1.BorderStyle = new AnnotationBorderStyle(1);
    linkAnnotation1.Color = new double[] { 0, 0, 0.9 };
    linkAnnotation1.Destination = new Destination(page2, new DestinationTypeFit());
    
    // create link annotation using named destination
    LinkAnnotation linkAnnotation2 = new LinkAnnotation(new Boundary(10, 780, 300, 800));
    linkAnnotation2.BorderStyle = new AnnotationBorderStyle(1);
    linkAnnotation2.Color = new double[] { 0, 0, 0.9 };
    linkAnnotation2.Destination = new Destination("myDestination");
    
    // add annotations to the PDF page
    document.Pages[0].Annotations.Add(linkAnnotation1);
    document.Pages[0].Annotations.Add(linkAnnotation2);
    document.Save(outputStream);
}
 

Usage of explicit destinations is pretty straightforward, while named destinations require registration in their parent document. Using named destination it becomes possible to separate references generation from actual content generation. You may generate a list of links using destinations names first and later register these destination objects pointing to the correct locations.


Resulting PDF file is shown on the image below:

Links added using explicit and named destinations


Document outline (bookmarks)

A PDF document may contain a document outline that the conforming reader may display on the screen, allowing the user to navigate interactively from one part of the document to another. The outline consists of a tree-structured hierarchy of outline items (sometimes called bookmarks), which serve as a visual table of contents to display the document's structure to the user. The user may interactively open and close individual items by clicking them with the mouse. When an item is open, its immediate children in the hierarchy shall become visible on the screen, each child may in turn be opened or closed, selectively showing or hiding further parts of the document hierarchy. When an item is closed, all of its descendants in the hierarchy should become hidden. Clicking the text of any visible item shall activate the item, causing the conforming reader to jump to a destination or trigger an action associated with the item.

Document bookmarks or outlines, as they are named in PDF specification, can be enumerated, added or edited using the Bookmark class provided by the Apitron PDF Kit. The Bookmarks property of the FixedDocument class is designed for this purpose. Each bookmark element in its turn has Bookmarks property holding its children and so on. A bookmark can have either a destination or action assigned.

Consider the code below demonstrating the creation of bookmarks:

// create output PDF file
using (FileStream outputStream = new FileStream("navigation.pdf", FileMode.Create, FileAccess.Write))
{
    // create new PDF document
    FixedDocument document = new FixedDocument();
    // add three pages
    document.Pages.Add(new Page());
    document.Pages.Add(new Page());
    document.Pages.Add(new Page());
    
    // add root bookmark and set it open by default
    Bookmark rootBookmark = new Bookmark(document.Pages[0], "Table of contents");
    rootBookmark.IsOpen = true;
    document.Bookmarks.AddFirst(rootBookmark);
    
    // add nested bookmarks
    rootBookmark.AddLast(new Bookmark(new Destination(document.Pages[1]), "Page 2"));
    rootBookmark.AddLast(new Bookmark(new GoToAction(document.Pages[2]), "Page 3"));
    rootBookmark.AddLast(new Bookmark(new JavaScriptAction("app.alert('Hello World!');"), "JavaScript Bookmark"));
    document.Save(outputStream);
}
 


The image below shows the resulting document produced by the sample code with the TOC containing three bookmarks of different nature - first uses explicit destination, second uses GoTo action, and the last uses JavaScript action invoking the alert window:

Creating bookmarks in PDF document

Forms

Interactive forms

An interactive form sometimes referred to as an AcroForm - is a collection of fields for gathering information interactively from the user. A PDF document may contain any number of fields diplayed on any combination of pages, all of which make up a single, global interactive form spanning the entire document. Arbitrary subsets of these fields can be imported or exported from the document.

A PDF form can be seen as two separate parts - data model that use document fields to store information entered by user and its view presenting the data to the user and providing interaction.

If a field has visual representation on a PDF page, then it’s provided by using a special type of annotation object called Widget Annotation. There are widgets defined for common control elements e.g. button, checkbox, textbox etc. See section 12.7 “Interactive Forms” of the PDF specification for the detailed description of PDF forms related objects.

One can programmatically create PDF forms or edit existing forms using Apitron PDF Kit, as shown by the samples provided in the form samples section.

Fields

An FixedDocument.AcroForm property if you use Fixed layout API or FlowDocument.Fields property if you use Flow layout API can be used to read or write document's fields - objects of specific type defined by the PDF specification and corresponding data. Available field types are as follows:

  • Button fields, represent interactive controls on the screen that the user can manipulate with the pointing device. These are push-buttons, check boxes, and radio buttons.
  • Text fields are boxes in which the user can enter some text from the keyboard.
  • Choice fields contain several text items, at most one of which can be selected as the field's value. They include scrollable list boxes and combo boxes.
  • Signature fields represent the digital signatures and optional data for authenticating the signer and the validity of document’s contents.

All corresponding classes implementing form fields can be found in Apitron.PDF.Kit.Interactive.Forms namespace.

Form actions

In addition to the standard actions described earlier, interactive forms support additional actions described below:

  • submit-form action - when invoked, an interactive PDF reader should transmit the names and values of the selected interactive form fields to a specified URL
  • reset-form actionon - when invoked, an interactive PDF reader whould reset selected interactive form fields to their default values
  • import-data action - when invoked, a PDF reader whould import data from Forms Data Format (FDF), XFDF (XML-based Forms Data Format according to ISO 19444-1:2016) or any other data format it supports into the document’s interactive form from a specified file

Named pages

The optional FixedDocument.Names.Pages entry in a document’s name registry contains a map that maps name strings to individual pages within the document. Naming a page allows it to be referenced in two different ways:

  • An import-data action can add the named page to the document into which FDF is being imported, either as a page or as a button appearance
  • A script executed by an ECMAScript action can add the named page to the current document as a regular page

Non interactive forms

It's possible to have fields without any visual representation, hence available for software-only reading and writing. It could be useful for saving some specific info related to the document, e.g. in order to implement custom workflows. Otherwise it might be an interactive form, converted to use only page content. This forms can be called non-interactive forms.

Forms code samples

Create, save and edit PDF form

The following example shows how to create a simple PDF form, fill it with some values, save to PDF file and then load and read the stored data.

public static void CreatePDFFormAndReadItsData()
{
    // create output PDF file
    using (FileStream outputStream = new FileStream("forms.pdf", FileMode.Create, FileAccess.Write))
    {
        FixedDocument document = new FixedDocument();
        document.Pages.Add(new Page());
        
        // create fields and add them into the document
        TextField txtName = new TextField("Name", "John Doe");
        TextField txtBirthDate = new TextField("birthDate","12.05.1975");
        PushbuttonField btnChange = new PushbuttonField("btnChange","Change");
        
        document.AcroForm.Fields.Add(txtName);
        document.AcroForm.Fields.Add(txtBirthDate);
        document.AcroForm.Fields.Add(btnChange);
        
        // create views for fields and set their properties
        TextFieldView nameView = new TextFieldView(txtName, new Boundary(10, 800, 100, 825));
        nameView.FontResourceID = "Arial";
        nameView.FontSize = 14;
        nameView.BorderStyle = new AnnotationBorderStyle(2, AnnotationBorderType.Inset);
        nameView.BorderColor = new double[]{1};
        
        TextFieldView birthDateView = new TextFieldView(txtBirthDate,new Boundary(10,775,100,795));
        birthDateView.FontResourceID = "Courier";
        birthDateView.FontSize = 12;
        birthDateView.BorderStyle = new AnnotationBorderStyle(1,AnnotationBorderType.Inset);
        birthDateView.BorderColor = new double[] { 1 };
        
        PushbuttonFieldView buttonView = new PushbuttonFieldView(btnChange, new Boundary(10, 745, 50, 770));
    
        // add views to the PDF page
        AnnotationCollection pageAnnottations = document.Pages[0].Annotations;
        pageAnnottations.Add(nameView);
        pageAnnottations.Add(birthDateView);
        pageAnnottations.Add(buttonView);
        
        document.Save(outputStream);
    }

    // open file for reading
    using (FileStream inputStream = new FileStream("forms.pdf", FileMode.Open, FileAccess.Read))
    {
        FixedDocument document = new FixedDocument(inputStream);
        // read text fields
        foreach (TextField textField in document.AcroForm.Fields.OfType<TextField>())    
        {
            Console.WriteLine("Field \"{0}\" has value: {1}",textField.FieldName, textField.Text);
        }
    }
    Console.ReadLine();
}
 


Produced PDF file looks as follows:

Create and fill the PDF form

Looking into the code we can see that fields storing form values are created first and then we create and assign views for them. After that we put these views on page using the annotations collection (because these views are essentially Widgets annotations).


The results of reading this file using Apitron PDF Kit are shown on the image below:

Read the PDF form's values

It’s possible to change the saved fields values and write the file back, thus making it possible to programmatically fill the desired PDF form.


Change form field value using JavaScript action

To recall what actions are and how to work with JavaScript actions in particular, navigate the corresponding links. What if we would like to use “Change” button from previous code sample for changing the default value of the “Name” field belonging to the generated PDF form? A JavaScript action looks like an obvious choice and we could simply assign such action to this button and change the field's value should the click occur.

In code it would look as follows:

…
// create button view
PushbuttonFieldView buttonView = new PushbuttonFieldView(btnChange, new Boundary(10, 745, 50, 770));
// set JS action that changes text field value
buttonView.Action = new JavaScriptAction("this.getField('Name').value='Jane Doe';");
…
 

When user clicks on this button the value of the field “Name” will be changed accordingly, resulting change is shown on the image below (John Doe -> Jane Doe):

Change form field value using JavaScript action


Flatten PDF form fields

PDF form is a collection of objects called “fields” and these fields get stored inside a PDF file when you create or modify them. Some fields can be displayed on PDF page, while others are fully internal; it depends on how the field was created. In order to display the field on a page you need to create a “view” for it, in PDF terms it’s called a Widget Annotation. See the section 12.5.6.19 Widget Annotations of the PDF specification for the details.

While the field’s view is a dynamic object allowing you to change the value of the field interactively (if the read-only flag is not set), sometimes you may need to make it static, turning it to a regular PDF content and unlinking from the parent field. This process is called flattening of the PDF fields and in this code sample we’ll show how to do it using Apitron PDF Kit and its Fixed layout API.

See the code:

class Program
{
    // creates test PDF document with text field
    private static void CreateTestDocument()
    {
        FixedDocument doc = new FixedDocument();
        // create text field and add to doc's field collection
        TextField textField = new TextField("textField", "Text field content");
        doc.AcroForm.Fields.Add(textField);

        // create new page and add text view to it
        Page page = new Page();
        TextFieldView fieldView = new TextFieldView(textField, new Boundary(10, 800, 150, 820));
        fieldView.BorderColor = RgbColors.Red.Components;
        page.Annotations.Add( fieldView);
        // add page to document
        doc.Pages.Add(page);
        // save document
        using (Stream stream = File.Create("documentWithField.pdf"))
        {
            doc.Save(stream);
        }
    }
    
    static void Main(string[] args)
    {
        CreateTestDocument();
        using (Stream inputStream = File.Open("documentWithField.pdf",FileMode.Open))
        {
            FixedDocument doc = new FixedDocument(inputStream);
            // we can either flatten all fields or enumerate the collection
            // and flatten specific field by calling its Flatten() method
            doc.AcroForm.FlattenFields();
            // save document
            using (Stream outputStream = File.Create("fieldsFlattening.pdf"))
            {
                doc.Save(outputStream);
            }
        }
    
        Process.Start("fieldsFlattening.pdf");
    }
}
 

This code creates a PDF form containing a text field and saves it. After that it loads and flattens the form, producing the flattened version of it. The complete sample can be found in our github repo.

Results look as follows:

PDF form before flattening
Flattened PDF form