Main

March 25, 2009

Tool for Summarizing Form Content

I am often asked to take a look at forms that arrive from a variety of sources -- customers, quality assurance, sales engineers etc.  Often one of the first things I do is have a look at some summary information about the template.   Having a unix background, I often save the form as an XDP and poke around with some grep commands e.g.: grep "<field" form.xdp | wc -l to find out how many fields are in the form.  But certain types of information are a little difficult to coax out with unix shell commands, so I set out to do something more user friendly.

Since the template definition, is completely accessible to JavaScript, I decided to design a form that would summarize the contents of another form. The result is today's sample.  The form uses the Acrobat APIs to load and launch a file.  Once launched, we simply iterate over the contents of the template and generate a report from what we find.  The report consists of:

  • meta data about the form: File Name, Creator, Template Version, Compatible Version, Strict Scoping setting, Static/Dynamic setting
  • Enumerate the referenced fonts (including references found inside rich text fragments)
  • List all linked and embedded images (for embedded images, indicate their base64-encoded size)
  • Count instances of plain text vs. rich text
  • Enumerate scripts, indicating which language (FormCalc or JavaScript) along with what context (event) and how many lines long
  • Binding properties -- summarize what kinds of data binding are in use
  • Picture Formats - enumerate all the picture formats found in the form
  • All other properties.  For example, if you want to see how many captions are on the form, note how many times the <caption> element appears.

To use the form, you need to be running Acrobat (not Reader).  Simply press the button, select a form and wait for the report.  Note that for large forms, this can take a few seconds (sometimes more than a few :-).  I've attached another sample that has enough of each kind of content to generate an interesting report.  Save this form to disk and select it from the reporter form.  You'll see each category of the report populated with data.

I have also attached a report generated from a customer form with which I have enjoyed some quality time.

The script in this form is pretty complex.  But if you're a good JavaScript programmer you could probably extend the script to capture other information that you find interesting.

February 9, 2009

Calling FormCalc Functions From JavaScript

For form calculations, validations and general scripting problems, our form designers can choose between using FormCalc and JavaScript. FormCalc was designed as an expression grammar targeting forms functionality -- and designed to be easy to use for a novice form author familiar with expressions in Excel.  JavaScript on the other hand, is a much more general purpose and powerful scripting language.  Over time, form authors have gravitated more toward JavaScript, mainly because of its familiarity.  However, there are functionality gaps -- specifically a set of built in functions that are available in FormCalc but not in JavaScript. 

Many of the built-in FormCalc functions can be easily mimicked in JavaScript.  It doesn't take a rocket scientist to write a JavaScript script object function to imitate the FormCalc sum() function. However, there are some functions that are not so easily mimicked.  The most notable of these are the Parse() and Format() functions available in FormCalc.  Parse() and Format() are the entry points into the very rich picture clause processing functionality.  When you consider the inherent locale handling, date/time functionality, different calendars etc. it's plain to see that you don't want to do this in JavaScript.

But now we have a problem.  Many users are committed to JavaScript because they built frameworks for controlling validations and other form behaviours.  I did the same in the series of blog posts on validations (the most recent version of the framework was in the sample from this post).  The problem is that you cannot directly call FormCalc from JavaScript.  So it would seem that you can't enjoy both the power and familiarity of JavaScript as well as the built-in functions of FormCalc.  Well, ... actually you can.

There are two design patterns I'd like to cover:

  1. Validating fields with picture clauses
  2. General mechanism for calling FormCalc functions

Validating Fields with Picture Formats

Picture clause validations are a simple, declarative mechanism for form authors to ensure that input data complies with a specific format.  If the field value can successfully be formatted with the validation picture clause, then the field is considered valid.  e.g. if your validation picture clause is "Date{YYYYMMDD}", then the field is considered valid only if its value can be formatted as a date.  If you were to express this validation as a script, you could write this FormCalc expression:

form1.#subform[0].DateTimeField1::validate - (FormCalc, client)
format("YYYYMMDD", $) <> ""

Now the question is how to tap into this functionality from JavaScript.  The short answer:

  1. Define a *display* picture format (do not a validation picture format)
  2. Write a JavaScript validation script that returns true when the field.rawValue is different from field.formattedValue

Most often when you use a validation picture clause you also use a display picture.  In fact, there's really no reason why these picture clauses need to be different.  Combine that knowledge with an understanding of how the field.formattedValue property works:  when a field value can be formatted using the display picture, field.formattedValue will return the result of the format operation.  If the format operation fails, field.formattedValue returns the same as field.rawValue.  So to find out if a field was formatted correctly, use this JavaScript validation:

form1.#subform[0].DateTimeField1::validate - (JavaScript, client)
this.rawValue != this.formattedValue;

Mechanism for Calling FormCalc Functions from JavaScript

The solution is to use the execCalculate() method to indirectly cross the bridge between JavaScript and FormCalc.  When you open the attached sample, you will find a subform called "fc" that holds a script object called "func".  "func" has a series of embedded JavaScript functions that mimic the FormCalc functions with the same name.  Each function populates form variables with the function name and input parameters.  It then calls execCalculate() on the fc subform and returns the resulting value:

FCfromJS.fc.#variables[0].func - (JavaScript, client)
function Format(vPicture, vValue)
{
    F.value = "Format";
    P1.value = vPicture;
    P2.value = vValue;
    this.execCalculate();
    return ResultString.value;
}

The subform calculation script looks like this:

 FCfromJS.fc::calculate - (FormCalc, client)
; execute the requested function based on the input
; request parameters
if (F == "WordNum") then
    ResultString = WordNum(P1)

elseif (F == "Parse") then
    ResultString = Parse(P1, P2)

elseif (F == "Format") then
    ResultString = Format(P1, P2)

elseif (F == "Uuid") then
    ResultString = Uuid(P1)

elseif (F == "UnitValue") then
    ResultString = UnitValue(P1, P2)

else
    ResultString = ""
endif

The field that wants to use the format() functionality has a simple calculate script:

FCfromJS.#subform[1].format.Result::calculate - (JavaScript, client)
fc.func.Format(PictureClause.rawValue, Value.rawValue);

This fc subform can easily be incorporated as a custom library object in Designer that can be dragged onto any form.  It should be pretty easy to follow the design pattern if you want to extend the sample and add other FormCalc functions.

One usage note -- if you want to call these functions from initialization scripts, then be sure to place the fc subform at the beginning of your template.  This is necessary because the fc subform has an initialization script that creates the necessary form variables.  By placing this subform at the top of the form hierarchy, we'll be certain that the fc initialization event fires before other initialization events.

August 12, 2009 Update

I have updated the sample form:

  • Added access to the formcalc Get() function. 
  • Added a version checking mechanism (see this post for details).
  • The subform housing this functionality is now available as a downloadable fragment: scFormCalc.xdp
  • The functionality is accessed using the subform "enter" event rather than the calculate.  The calculate event was introducing unwanted calculation dependencies.

November 5, 2008

Validating a Postal Code

There are several interesting scripting validation techniques that can be demonstrated in the context of a Canadian postal code field.  While the subject domain is fairly specific, the techniques used are applicable to other field validations.

The challenge is to get an accurate and user-friendly data capture experience for a postal code. Some of the considerations:

  • Make sure the field value conforms to the rules for Canadian postal codes (use regular expressions for validating)
  • Make sure that error messages are as specific as possible to assist better user input (customize validation error messages)
  • Make sure the user can optionally key in a space inside their postal code  (use an edit picture clause)
  • Make sure the postal code is entered in upper case (modify keystrokes on the change event)
  • Make sure the postal code value is consistent with other address fields: province and city (inter-field validation)

The sample form can be found here.

Canadian Postal Code Rules

The default way to validate a postal code is to use a validation picture clause: text{A9A 9A9}. However, this picture allows many illegal postal codes. For example, the meta character “A” in a picture clause allows any Unicode letter value – whereas a postal code letter is far more restricted:

  • The first character of a Canadian postal code corresponds to a geographic region and is limited to the set of characters: ABCEGHJKLMNPRSTVXY
  • The third and fifth characters may not include the letters D, F, I, O, Q or U (because they are hard for OCR software to deal with).

In order to implement these restrictions, we can use regular expressions. The JavaScript string class has a match() method that searches for a pattern.  For example, the code to validate the first character is:

if (vTestValue.charAt(0).match(/[ABCEGHJKLMNPRSTVXY]/) == null)
{
    ... error condition ...
}

Specific Error Messages

When the user enters an invalid postal code, be as specific as possible in telling them what to fix. There is a world of difference between:

“Invalid postal code” and “Second, fourth and sixth postal characters must be numeric”

For the person who typed in a letter “O” instead of a "0" (zero), this will be the clue they need to correct their error.

To make this work, validate the portions of the postal code individually and then set the field error message accordingly:

var vNums = vTestValue.charAt(1) +
            vTestValue.charAt(3) +
            vTestValue.charAt(5);
if (vNums.match(/[0-9][0-9][0-9]/) == null)
{
    vTestField.validationMessage =  
        "Second, fourth and sixth postal characters must be numeric";
    return false;
}

Note that you might choose to store error messages as form variables.  That way they will be examined by Designer's spell checker.

The Optional Space

The user ought to be able to enter their data with or without a space – but we want to store it consistently – without the space. To accomplish this, use an edit picture clause: text{OOO OOO}

Edit picture clauses are used by Reader to:

  1. format the raw value for editing when the user enters the field
  2. parse the edited value to set the raw value when the user exits the field

For example, a raw value: A2A2A2 will be displayed as A2A 2A2 when the user enters the field. When they exit the field, both A2A2A2 and A2A 2A2 will parse into A2A2A2 successfully. Note that I used the meta character “O” (letter or digit). This is so that if the user enters invalid characters, we still parse/format the space correctly.

Naturally, we also use a display picture clause to render with the space: text{A9A 9A9}

Upper Case Only

The easiest way to force the value to upper case is to trap the characters as the user enters them and convert them as they type. We do this with a very simple change event:

form1.address.PostalCode::change - (JavaScript, client)
// Change all user entered characters to upper case.
xfa.event.change = xfa.event.change.toUpperCase();

Because we know that the input data is in upper case, it makes our validation logic cleaner, as it needs only be concerned with the upper case variations.

Consistency with City and Province

The first character of the postal code determines the geographic region.  We can assign the province based on the postal code. In a couple of cases (M and H) we can also assign the value for the city (Toronto and Montréal).

More Considerations

While we now have a better-than-average postal code validation, it does not ensure100% correctness. Since only about 12% of the possible 7.2 million postal code variations are currently allocated, it is still pretty easy to enter a non-existent postal code.

Because this validation script is as specific as possible, it does mean that the script may need to be updated as the postal code rules evolve (The Canadian postal code rules were modified after Nunavut was added in 1999). 

My sources for postal code specifics were:
 http://en.wikipedia.org/wiki/Canadian_postal_code and http://www.columbia.edu/kermit/postal-ca.html.