Review, Markup, and Approval

In this chapter you will learn how to make use of Acrobat’s ability to facilitate online collaborative reviews for many types of content. At the heart of this process is the set of commenting, markup, and approval tools available to the reviewers, and the tools available to the initiator for managing the review.

You can use JavaScript to customize the review process and how comments are handled, to add additional annotations, and to configure a SOAP-based online repository.

Working with comments using JavaScript

The Commenting toolbar provides reviewers with the tools necessary to create comments, which may be placed in the document in the form of notes, highlighting, and other markup. In this section, you are introduced to the various annotation types from the JavaScript point of view. In Getting annotation data you will learn how to extract content from comments and later, in Setting comment properties, you will learn to set the properties of a comment.

Annotation types

Each annotation has a JavaScript type. In the paragraphs that follow, the relation between each UI name and its corresponding type is delineated.

UI tool name

Type

Sticky Note tool

Text

Text Box tool

FreeText

Highlight Text tool

HighLight

Cross-Out Text tool

StrikeOut

Underline Text tool

Underline

Stamp tool

Stamp

Cloud tool. Polygon tool

Polygon

Arrow tool

Line

Rectangle tool

Square

Pencil Tool

Ink

Oval Tool

Circle

Attach a File as a Comment tool

FileAttachment

Record Audio Comment tool

Sound

Polygon Line tool

PolyLine

Getting annotation data

There are two methods for getting annotation information present in a document, or a collection of documents. These methods are getAnnot and getAnnots of the Doc object.

The getAnnot method returns an Annotation object, an object that holds all data of the annotation. This method takes two parameters, nPage (page number) and cName (the name of the annot). For example,

var a = this.getAnnot({ nPage: 2, cName: "myAnnot" });
if ( a != null ) {
    console.println("This annot has type " + a.type);
    if ( (a.type != "FileAttachment")  (a.type != "Sound") )
        console.println("The comment of this annot is " + a.contents);
}

When the user makes a comment using the UI, the name of the comment is randomly assigned. As a consequence, unless the annotation is created with the addAnnot method, in which the name of the annot can be assigned at the time of creation, the name is not typically known to the developer.

In normal workflows, the problem is to gather all comments in a document and process them in some way. The tool for doing this is getAnnots. The method returns an array of Annotation objects based on the search parameters, all of which are optional:

  • nPage - The page number to search for annotations, if not provided, the whole document is searched.

  • nSortBy - The method used to sort the search results, these include page, author, and modification date.

  • bReverse - If true, the array is reverse-sorted.

  • nFilterBy - Get anntotations satisfying certain criteria, such as getting only those annotations that can be printed, that can be viewed, or that can be edited.

Additional discussion can be found in Sorting comments. See the JavaScript for Acrobat API Reference or full descriptions of these parameters.

The following code retrieves all annotations in the document, and sorts them by author name:

var annots = this.getAnnots({
    nSortBy: ANSB_Author
});
console.println("nAnnot Report for document: " + this.documentFileName);
if ( annots != null ) {
    console.show();
    console.println("Number of Annotations: " + annots.length);
    var msg = "    %s in a %s annot said: "%s"";
    for (var i = 0; i < annots.length; i++)
    console.println(util.printf(msg, annots[i].author, annots[i].type,
    annots[i].contents));
} else
    console.println("       No annotations in this document.");

Adding comments with JavaScript

You can include a text box comment in a document and control its border, background color, alignment, font, and size characteristic. To create a Square type annotation, such as one created by the Rectangle tool in the UI, use the Document method addAnnot as follows:

this.addAnnot({
    page: 0,
    type: "Square",
    rect: [0,0,100,100],
    name: "OnMarketShare",
    author: "A.C. Robat",
    contents: "This section needs revision"
});

Refer to the Acrobat JavaScript API Reference for full descriptions of the properties specified above.

All annotations can be constructed in this way, in the case of sound and file attachment annotations, there is no JavaScript method for associating a recording with a sound annotation or a file with a file attachment.

Setting comment properties

To set the properties of a comment, create an object literal containing the properties to be applied to your comment. Then apply the properties to your annotation:

// Create the common properties in an object literal:
var myProps = {
    strokeColor: color.red,
    popupOpen: true,
    arrowBegin: "Diamond",
    arrowEnd: "OpenArrow"
};

// Assign the common properties to a previously created annot:
myAnnot.setProps(myProps);

The object literal, myProps, can be used again to change the properties of a collection of annotations, perhaps ones returned by the getAnnots, as discussed in Getting annotation data.

Online collaboration essentials

You can initiate several types of review workflows for PDF documents:

  • Email the document to all reviewers, and import the returned comments into the original document.

  • Set up an automated email-based review.

  • Set up an automated browser-based review through the use of a shared server.

  • Initiate an email-based approval workflow.

  • Initiate an JavaScript-based review.

Reviewing documents with additional usage rights

For email-based reviews, the specification of additional usage rights within a document enables extra capabilities within Acrobat Reader. This enables the reviewer to add comments, import and export form-related content, save the document, or apply a digital signature.

For example, when using the Doc object encryptForRecipients method, you can specify the following permissions for reviewers:

  • allowAll: Permits full and unrestricted access to the entire document.

  • allowAccessibility: Permits content accessed for readers with visual or motor impairments.

  • allowContentExtraction: Permits content copying and extraction.

  • allowChanges: Permits either no changes, or changes to part or all of the document assembly, content, forms, signatures, and notes.

  • allowPrinting: Permits no printing, low-quality printing, or high-quality printing.

The following code allows full and unrestricted access to the entire document for one set of users (importantUsers), and allows high quality printing for another set of users (otherUsers):

   var sh = security.getHandler("Adobe.PPKMS");
   var dir = sh.directories[0];
   var dc = dir.connect();
   dc.setOutputFields({oFields:["certificates","email"]});
   var importantUsers = dc.search({oParams:{lastName:"Smith"}});
   var otherUsers = dc.search({oParams:{lastName:"Jones"}});
   this.encryptForRecipients({
       oGroups:[
           {
               userEntities: importantUsers,
               permissions: { allowAll: true }
           },
           {
               userEntities: otherUsers,
               permissions: { allowPrinting: "highQuality" }
           }
       ],
       bMetaData: true
   });
   eMailList = "";
   for ( var i=0; i < importantUsers.length; i++)
       eMailList +=  (importantUsers[i].email + ",");
   for ( var i=0; i<otherUsers.length; i++)
       eMailList +=  (otherUsers[i].email + ",");
   // Now email the secured document.
   this.mailDoc({
       cTo: eMailList,
       cSubject: "For your eyes only",
       cMsg: "Please review for the meeting on Friday."
})

Emailing PDF documents

In addition to the email options available in the Acrobat menu and toolbar, it is also possible to use JavaScript to set up an automated email review workflow. This may be done through the Doc object mailDoc method. In the code shown below, the document is automatically sent to recipient@example.com :

this.mailDoc({
    bUI: false,
    cTo: "recipient@example.com",

    cSubject: "Review",
    cMsg: "Please review this document and return. Thank you."
});

Note

For Windows systems, the default mail program must be MAPI-enabled.

JavaScript-based collaboration driver

JavaScript can be used to describe the workflow for a given document review, and can be used in review management. This is done by specifying a state model for the types of annotations a reviewer may use and creating an annotation store on the server for customized comment and review within browser-based workflows. The Collab object provides you with control over the possible states annotation objects may have, and may be used in conjunction with the SOAP object to create an annotation store.

There are several methods available within the Collab object that enable you to describe the state model for the review: these include addStateModel, getStateInModel, transitionToState, and removeStateModel.

The addStateModel method is used to add a new state model to Acrobat describing the possible states for an annot object using the model, and the removeStateModel method removes the model, though it does not affect previously created annot objects. Their usage is shown in the code below:

// Add a state model, this script can be placed at the folder level to
// install a custom state model for enterprise users, for example.
try{
    var myStates = new Object;
    myStates["initial"] = {cUIName: "Haven't reviewed it"};
    myStates["approved"] = {cUIName: "I approve"};
    myStates["rejected"] = {cUIName: "Forget it"};
    myStates["resubmit"] = {cUIName: "Make some changes"};
    Collab.addStateModel({
        cName: "ReviewStates",
        cUIName: "My Review",
        oStates: myStates,
        cDefault: "initial"
    });
}
catch(e){console.println(e);}

// Now transition all annots to the "rejected" state.
var myAnnots =              this.getAnnots( );
for ( var i=0; i<myAnnots.length; i++ )
    myAnnots[i].transitionToState("ReviewStates", "rejected");

// Now remove the state model.
try {Collab.removeStateModel("ReviewStates");}
catch(e){console.println(e);}

You can also use the SOAP object’s connect, request, and response methods to create customized commenting and review within browser-based workflows. You can do this by setting up a SOAP-based annotation store on the server using the Collab object’s addAnnotStore and setStoreSettings methods.

The Collab object’s addAnnotStore method requires three parameters:

  • cIntName: The internal name for the annotation store.

  • cDispName: The display name for the annotation store.

  • cStore: The definition for the new Collab store class.

The new Collab store class must contain the following definitions for the functions used to add, delete, update, and enumerate through the array of annotations:

  • enumerate: Communicates with the web service to request the array of annotations stored on the server. It is used when the PDF document is loaded for online review, or when the user clicks Upload or Send on the Commenting toolbar.

  • complete: Passes the annotation data to the collaboration server so that it can be updated.

  • update: Uploads new and modified annotations to the server.

The class SDKSampleSOAPAnnotStore, as shown in the sample code below, is defined in sdkSOAPCollabSample.js in the Acrobat SDK, and contains complete definitions of the three functions described above.

The sample code below provides a standard example of how to use the SOAP and Collab objects to customize your online collaborative review. Note that all of the reviewers must have a copy of the JavaScript collaboration store code. In Acrobat 7.0 and later, the Custom collaboration store type allows you to put the JavaScript on the server. The store type used is CUSTOM, and the setting is a URL to the JavaScript file:

// Here is the URL for a SOAP HTTP service:
var mySetting = "http://sampleSite/comments.asmx?WSDL";

// Here is the internal name for the collaborative store:
var myType = "mySOAPCollabSample";

// Set the connection settings for the SOAP collab store:
Collab.setStoreSettings(mySetting, myType);

// Set the default collab store:
Collab.defaultStore = myType;

// Add the collab store to the Acrobat Collab servers:
if (typeof SOAPFileSys == "undefined")
    Collab.addAnnotStore(
        myType,
        "SOAP Sample",
        {
            // Annot store instantiation function is required:
            create: function(doc, user, settings)
            {
                if (settings && settings != "")
                    return new SDKSampleSOAPAnnotStore(
                        doc, user, settings
                    );
                else
                    return null;
            }
        }
    );

Spell-checking in comments and forms

You can check the spelling of any word using the spell object’ checkWord method. This can be applied to any form field or annotation. First retrieve the contents, and submit each word to the method.

Setting spelling preferences

To set the dictionary order, first retrieve the array of dictionaries using the Doc object’s spellDictionaryOrder property. Then modify the order of the array entries, and assign the array to the same property. An array of currently available dictionaries can be obtained using the spell object’s dictionaryNames property.

To set the language order, perform a similar algorithm using the Doc object’s spellLanguageOrder property. An array of currently available dictionaries can be obtained using the spell object’s languages property.

Adding words to a dictionary

You can add words to a dictionary by invoking the spell object’s addWord method, as shown in the code sample below:

spell.addWord(myDictionary, "myNewWord");

Approval

Approval workflows may include an automated process in which a PDF document is automatically sent via email to a recipient for their approval. For example, this may be accomplished through the usage of the Doc object’s mailDoc method. The user may then use a standard approval stamp, use a custom stamp, or use a Hanko stamp to create a secure digital signature.

Managing comments

In this section, you will look in a more detailed way at the method of managing the comments in a document. The topics covered are:

Selecting, moving, and deleting comments

Just as you can access the Comments List in the Acrobat user interface, you can likewise do so through using the syncAnnotScan and getAnnots methods of the Doc object. The syncAnnotScan method guarantees that all annotations in the document are scanned, and the getAnnots method returns a list of annotations satisfying specified criteria.

For example, the following code scans all the annotations on page 2 of the document and captures them all in the variable myAnnotList :

this.syncAnnotScan();
var myAnnotList = this.getAnnots({nPage: 1}); // Zero-based page number

To move a comment, use the corresponding setProps method of the Annotation object to specify a new location or page. To delete the comment, invoke the corresponding destroy method of the Annotation object. In the code sample below, all the free text comments on page 2 of the document are deleted:

for (var i=0; i<myAnnotList.length; i++)
    if (myAnnotList[i].type == "FreeText")
        myAnnotList[i].destroy();

Using the comments list

Once you have acquired the comments list through the syncAnnotScan and getAnnots methods of the Doc object, you can change their status, appearance, order, and visibility. In addition, you will be able to search for comments having certain characteristics.

Changing the status of comments

To change the status of a comment, invoke the corresponding transitionToState method of the Annotation object, as shown in the code below:

// Transition myAnnot to the "approved" state:
myAnnot.transitionToState("ReviewStates", "approved");

The code above assumes myAnnot is an Annotation object of the document.

Changing the appearance of comments

You can change the appearance of a comment in a variety of ways. In general, the appearance of any comment may be changed by invoking the setProps method of the Annotation object, as shown in the code below:

myAnnot.setProps({
    page: 0,
    points: [[10,40], [200,200]],
    strokeColor: color.red,
    popupOpen: true,
    popupRect: [200,100,400,200],
    arrowBegin: "Diamond",
    arrowEnd: "OpenArrow"
});

Sorting comments

To sort comments, use getAnnots method of the Doc object and specify a value for the nSortBy parameter. Permissible values of nSortBy are

  • ANSB_None: Do not sort.

  • ANSB_Page: Sort by page number.

  • ANSB_Author: Sort by author.

  • ANSB_ModDate: Sort by modification date.

  • ANSB_Type: Sort by annotation type.

In addition, you can specify that the sorting be performed in reverse order by submitting the optional bReverse parameter to the method.

The code sample given below shows how to obtain a list of comments from page 2 of the document, sorted in reverse order by author:

this.syncAnnotScan();
var myAnnotList = this.getAnnots({
    nPage: 2,
    nSortBy: ANSB_Author,
    bReverse: true
});

Showing and hiding comments

To show or hide a comment, set its corresponding hidden property of the Annotation object. For example, the following code hides myAnnot :

myAnnot.hidden = true;

Exporting and importing comments

To export all the comments in a file, invoke the exportAsFDF or exportAsXFDF methods of the Doc object. In both cases, set the bAnnotations parameter to true, as shown in the code sample below, which exports only the comments and nothing else:

this.exportAsFDF({bAnnotations: true});

To import comments from an FDF or XFDF into a file, invoke the importAnFDF or importAnXFDF methods of the Doc object.

Aggregating comments for use in Excel

The createDataObject method of the Doc object may be used to create a tab-delimited text file, which can then be used in Excel. To aggregate comments for use in Excel, collect all the comments using the getAnnots method, iterate through them and store them into a tab-delimited string, create a text file attachment object using the createDataObject method of the Doc object, pass the string to the cValue parameter in the createDataObject method, and optionally, save the attachment to the local hard drive using exportDataObject. Below is a sample script which follows the above outline:

var annots = this.getAnnots();
var cMyC = "NametPagetComment";
for ( var i=0; i<annots.length; i++ )
    cMyC += ("n"+annots[i].author + "t" + annots[i].page + "t""
        + annots[i].contents+""");

this.createDataObject({cName: "myCommentList.xls", cValue: cMyC});
this.exportDataObject({cName: "myCommentList.xls", nLaunch: 1});

Comparing comments in two PDF documents

While the Acrobat user interface provides you with a menu choice for comparing two documents, it is possible to customize your comparisons using JavaScript. To gain access to multiple documents, invoke the app object’s openDoc method for each document you would like to analyze. Each Doc object exposes the contents of each document, such as an array of annotations. You can then compare and report any information using customized algorithms. For example, the code below reports how many annotations exist in the two documents:

var doc2 = app.openDoc("/C/secondDoc.pdf");
var annotsDoc1 = this.getAnnots();
var annotsDoc2 = doc2.getAnnots();
console.println("Doc 1: " + annotsDoc1.length + " annots.");
console.println("Doc 2: " + annotsDoc2.length + " annots.");

The above code will work if executed in the console. If executed from a non-privileged context, the secondDoc.pdf must be disclosed for app.openDoc to return its Doc object. Disclosed means that the code this.disclosed=true is executed when the document is opened, either as an open page action, or as part of a top level execution of document scripts. See the documentation of app.openDoc in the Acrobat JavaScript API Reference for details.

Extracting comments in a batch process

In a batch process, you can open any number of Doc objects using the app object openDoc method. For each open document, you can invoke its corresponding Doc object getAnnots method to collect the comments in that file. If you would like to put all the comments together in one file, you can do so by creating a new document and saving the various arrays of comments into that new file.

The batch sequence samples include a sequence called Comments to Tab-Delimited File. This sequence uses the techniques described in the previous paragraph.

Approving documents using stamps (Japanese workflows)

Approval workflows are similar to other email-based collaborative reviews, and provide you with the ability to set the order in which participants are contacted. This means that, based on the approval issued by a participant, the document can be mailed to the next participant, and an email can be sent to the initiator.

Setting up a Hanko approval workflow

A registered Hanko is a stamp used in Japanese document workflows, and can be used to sign official contracts. Every registered hanko is unique and is considered a legal form of identification.

A personal Hanko is not registered, and is used for more common types of signatures, such as those used in meeting notes or budget proposals. Everyone in an organization who is involved in a document review must add their Hanko to the document in order for it to gain final approval.

Acrobat provides an assistant to help you set up an approval workflow. You can customize your workflow as well, by adding form fields to the document containing recipient lists to be chosen by the participant. This way, in case there are multiple directions for a given branch in the workflow, the participant may invoke automated functions that send the document to the correct participants, as well as an email to the initiator containing a record of activity.

You can use JavaScript to automate various steps within the workflow by sending the document and other information by email using the Doc.mailDoc method.

Participating in a Hanko approval workflow

A participant receives an email with instructions for opening the document and completing their portion of the approval process. As noted above, this can be customized and automated through the use of form fields if the workflow is complex.

A Hanko stamp is a commenting tool used in approval workflows, and an Inkan stamp is a unique image that can represent an individual’s identity and can be used in place of a signature. Both are created, customized, and managed through the Acrobat user interface.

In order to use a Hanko or Inkan stamp, you will need to create a custom stamp and add digital signature information. Once the stamp has been created, you can apply it in your workflows.

Installing and customizing Hanko stamps

Creating custom Hanko stamp information involves the combination of user information and a digital signature. Once you have set this up, it can be saved in a PDF file which is stored in the Stamps folder.

Creating custom Inkan stamps

To create an Inkan stamp, add your name, title, department, and company, choose a layout, and provide a name to use for the stamp. You can also import a PDF form to add customized features and additional fields containing personal information. In addition, it is possible to add secure digital signature information to an Inkan stamp.

Deleting custom stamps

You can delete any Hanko and Inkan stamps that you created, though it is not possible to delete any of the predefined stamps in the Stamps palette.