Stefan Cameron on Forms
Building intelligent forms using Adobe LiveCycle Designer

Home

Complex Validations

A couple of days ago, Michael Ramirez asked me how to do complex validations on forms. He asked how one could have a validation as follows: Given 3 text fields A, B and C and a check box E, A is mandatory only if B and C are filled or if E is checked. I thought this would make a great little sample of both complex validation scripts and what I like to call the “Two Button Submit” technique.

Download Sample [pdf]

Minimum Requirements: Designer 7.x, Acrobat 7.x.

The Plan

First, let’s figure-out what the plan is before we start scripting. The idea is to place these fields on a new form along with a submit button such that the user cannot submit the form unless the validation rules succeed. Since Field A isn’t always mandatory, we can’t default it to be User Entered — Required by using the Value tab in the Object palette. We’ll need a way to make it required only when Fields B & C are filled or when Field E is checked. Furthermore, we don’t want the form’s data to be submitted unless these rules are met.

The easiest solution that comes to mind is one that’s passive as the user is entering data into the fields but aggressive at the point when they try to submit it by putting the validation script on the submit button’s Click event. The catch here is that there’s no way of stopping the submission process once the Click event has fired so regardless of whether your validation succeeds or fails, the form is going to be submitted. That’s where the “Two Button Submit” technique comes-in to save the day.

“Two Button Submit” Technique

Thankfully, there’s a scripting method called execEvent that lets you programmatically cause an event to be executed on a field. With this in mind, you place a regular (non-submit type) button on the form, make it visible and set its caption to read something like, “Submit”. Then you place the actual submit button (could be email, HTTP or even print) on the form and make it invisible. Please note: make it invisible, not hidden or else the execEvent call on it will fail because Acrobat won’t know it exists in the XFA Scripting Model. For this example, let’s call it “EmailSubmitButton”.

Now that both buttons are on the form, in the regular (fake submit) button’s Click event, put the following script (FormCalc or JavaScript works just the same):

EmailSubmitButton.execEvent("click");

With that script, when you click on the fake submit button, you’ll cause the Click event on the actual submit button to fire and the fake submit button will, to the user, behave just like a normal submit button.

The advantage of this is that you now control if and when the real submit button’s Click event is executed and therefore if and when the form is submitted.

If you were to add the following JavaScript statement before the execEvent line:

if (FieldA.rawValue != null && FieldA.rawValue.length > 0)

the form wouldn’t submit unless Field A was filled.

Validations Prior to Submitting

Using the “Two Button Submit” technique, we can now perform any kind of validations — really simple to very complex — we want and have total control over if and when the form may be submitted.

Given our example of fields A, B, C and E, we can do something like this:

var bCanSubmit = true;

if ( (IsSpecified(FieldB) && IsSpecified(FieldC)) ||
    IsChecked(FieldE) )
{
  if (!IsSpecified(FieldA))
  {
    xfa.host.messageBox("Please fill Field A.");
    bCanSubmit = false;
  }
}

if (bCanSubmit)
  EmailSubmitButton1.execEvent("click"); // submit the form

This will prevent the form from being submitted if Field A isn’t filled when Fields B & C are filled or Check Box E is checked.


Posted by Stefan Cameron on August 24th, 2006
Filed under Scripting

Invalid Flashing Fields 2.0

A colleague of mine here at Adobe pointed-out today that the use of the AcroForm Document object’s getField method wasn’t necessary in the script I used for my original Invalid Flashing Fields sample.

There’s an alternative which uses xfa.form.resolveNode in the app.setInterval script. xfa.form.resolveNode takes a SOM Expression and returns a reference to an XFA node. What’s more is that this API call can be made from within the context of the AcroForm Scripting Object Model.

The app.setInterval script therefore changes from this:

moFlashTimerID = app.setInterval(
  "var f =  this.getField('" +
    GetAcroFormFieldName(oField) + "');  " +
  "if (color.equal(f.fillColor, color.red))" +
    "{ f.fillColor = [" + moAcroFieldFillColor.toString() + "]; }" +
  "else" +
    "{ f.fillColor = color.red; }",
500);

to this:

moFlashTimerID = app.setInterval(
  "var f =  xfa.form.resolveNode('" +
    oField.somExpression + "');  " +
  "if (f.ui.oneOfChild.border.fill.color.value == '255,0,0')" +
    "{ f.ui.oneOfChild.border.fill.color.value = '232,232,232'; }" +
  "else" +
    "{ f.ui.oneOfChild.border.fill.color.value = '255,0,0'; }",
500);

Also note the changes in the way the color values are compared and assigned (whereby the newer version uses more familiar XFA script rather than the AcroForm script from the first version).

Since the use of the AcroForm Scripting Object Model should always be secondary to using the XFA Scripting Object Model (because AcroForm objects are, after all, in a separate Object Model which may change separately from the XFA Scripting Object Model), I wanted to highlight this alternative which makes more extensive use of the XFA Scripting Object Model than the first version did.

Download Sample [pdf]

Minimum Requirements: Designer 7.1, Acrobat 7.0.5.


Posted by Stefan Cameron on August 15th, 2006
Filed under AcroForm Objects,Scripting,Tutorials

Importing Data in Acrobat

Data is central to every form. Some forms simply collect data while others use pre-collected data to do various things to forms, such as pre-populate names, phone numbers, addresses, affect the layout of a form or various other things.

Using pre-collected data to affect a form as described above involves importing data into a form via the host application (assuming a server isn’t part of the picture). This time around, I want to talk specifically about importing data using Acrobat.

As you all know, Adobe distributes the Reader application for free. Because of this, you can save the forms you design in Designer as PDF and anyone with the free Reader application can fill your form and submit its data electronically.

The catch is when your PDF form requires data to be imported. Unless the host application is Acrobat Professional or Acrobat Standard, a regular PDF form cannot import data — no matter if it comes from an XML Data file or from a data connection to an ODBC or WSDL data source. Acrobat Pro/Std comes with all the tools you need to import data into a form and permits data to be automatically imported via an ODBC or WSDL connection. PDF forms opened in Reader (or Elements for that matter), on the other hand, aren’t privy to that functionality by default.

Side note: In Reader 7.0.5, there was a bug that resulted in Reader having the ability to import data into non-Reader-Extended (more on Reader Extensions below) forms. That bug was fixed in Reader 7.0.7 such that Reader can no longer, by default, import data into a PDF form.

The general rule is that PDF forms opened in Reader must be individually extended using Adobe LiveCycle Reader Extensions in order to enable the use of special hidden features such as Data Import, Digital Signatures, Local Save, Commenting and more. The only exception is if you own a licensed copy of Acrobat Pro 7.x which allows you to send a PDF out for review via email — which will enable Commenting capabilities in Reader for that PDF — or version 8.x in which you now have access to a new feature called “Enable Usage Rights in Adobe Reader“, available under the “Advanced“ menu. Using this new version 8.x command enables the Local Save and Digital Signature features in Reader for a particular PDF form. That said, before you start enabling all your forms, you should note that use of these Acrobat Pro features is subject to certain restrictions as detailed in your license agreement which you should be aware of.

Important: Local Save is not equivalent to Data Import. Local Save simply gives the user the ability to save a copy of a PDF form, along with any filled data, using the free Reader such that the form may be closed and re-opened at a later date in order to be completed. Therefore, you cannot use the “Enable Usage Rights in Adobe Reader“ feature in Acrobat Pro to enable Data Import features in the free Reader.

Another option is to use Adobe LiveCycle Forms to deploy your forms. Using this server product, you can pre-populate forms with data on the server prior to deploying them to the client application (on the user’s system). Using this option, you don’t need to reader-extend a PDF form which imports data because the data is imported and merged into the PDF form on the server and then deployed to the client application (any version of Acrobat/Reader on the user’s system), which, in turn, doesn’t need to import any data.

To summarize, here’s a little table that illustrates the conditions under which you can import data into a PDF form in Acrobat:

Version PDF Form With Reader Extensions With LiveCycle Forms
Reader no yes yes
Elements no yes yes
Standard yes yes yes
Professional yes yes yes

Sep 12, 2008 — Added information about “Enabling Usage Rights in Adobe Reader” feature


Posted by Stefan Cameron on August 12th, 2006
Filed under Acrobat

Tracking Mouse Clicks

I just recently received another comment from Zack. This time, he was wondering about how one would go about tracking mouse clicks on an image field.

I had never attempted to do that so I took it on as a challenge and thought I would share the results in this post.

I knew from the start that XFA alone wasn’t going to be able to handle this simply because (to my knowledge) it doesn’t provide any information as to the position of the mouse pointer when an event occurs. The most logical place I thought would’ve provided the information — the Event Pseudo Model (the xfa.event object available in all XFA events) — didn’t live up to my expectations. Thankfully, XFA at least provides a Click event so that I could know when the image got clicked.

The next logical place to look was in Acrobat’s Scripting Object Model (in the AcroForm Objects). In the Acrobat Document object, I found what I was looking for: the mouseX and mouseY properties which provided the location of the mouse with respect to the document window.

The last thing I needed was information about the dimensions and location (within the Acrobat Document Object’s coordinate space) of the image field and the Acrobat Field object’s rect property would give me just that.

The combination of the XFA Click event, the Acrobat Document object’s mouseX and mouseY properties and the Field object’s rect property was just what I needed to get this to work.

Of course, I soon discovered that I had another problem to figure-out: The behaviour of an image field in a PDF form running in Acrobat is that when clicked, it opens a browse dialog that lets you pick the content for the field. Unfortunately, there isn’t any way to suppress that dialog other than making the image field read-only or by using a static image object but then both alternatives prevent the Click event from firing. So I needed some clever way to capture a mouse click over an image (whether it was a field or a static object) and I decided to use a button with a transparent fill and no border (so it was essentially transparent). Since buttons are fields just like image fields, the mouseX, mouseY and rect properties would still be available for the button and if I sized the button to fit the image and placed it over-top, I would essentially end-up with an HTML <map>.

Download Sample [pdf]

Minimum Requirements: Designer 7.1, Acrobat 7.0.5.

The first challenge was getting an instance of the Acrobat Field object which represents the button using the

event.target.getField

method. That was easily accomplished by using the script I provided on my AcroForm Field Name Generator article.

The next problem to be solved was the fact that the default behaviour, in Acrobat, for a button when it’s clicked is to invert its content area. Since I was trying to hide the button, I needed a way to suppress the inversion so that clicking on the button would give no visual feedback to the user. That way, it would give the impression that the user was actually clicking on some sort of hyperlink on the image itself. That was easily solved by using the highlight property of the Acrobat Field object representing the XFA button in its Enter event (had to be Enter and not Initialize because Initialize is too early in the form’s initialization process for the association between the XFA button and it’s Acrobat Field counterpart to be established):

event.target.getField(ScriptObject.GetFQSOMExp(this)).highlight =
  highlight.n;

The last problem was with respect to calculating the coordinates of the hot spots on the button that would trigger a reaction (in this sample, I was just going to set the value of a text field somewhere else on the form to reflect the area that was clicked). The problem there was that while I had the mouse location and the button’s dimensions all in the same coordinate space (Acrobat Document), the coordinates were specified with (0,0) set to the document’s lower left corner.

While this may not seem like a big deal to some of you, it really messes me up when (0,0) isn’t at the top left corner (with the maximum (x,y) set to the bottom right corner). I guess that’s a result of years of writing code for Windows where an MFC CWnd’s coordinate space places (0,0) at the top left corner. Anyway, after lots of hair pulling, I finally figured-out how to properly calculate the hot spots in this strange — no, alien — coordinate system.

Zack, if you have any other questions, please post a comment.


Posted by Stefan Cameron on August 4th, 2006
Filed under AcroForm Objects,Scripting,Tutorials

Auto-Localizing Your Forms

A few days ago, I posted about Designer 7.1’s new Dynamic Properties feature. In that post, I explained how this feature could be used to automatically populate a list box or drop down list field with data from a data connection without having to write any script.

Today, I thought I would highlight one of the main advantages to using this feature: localization of your forms!

By using the Dynamic Properties feature to bind the caption of form fields to data nodes in a data connection, you can easily localize your forms without having to write any script!

To illustrate how this would work, I’ve designed a simple little form which has an address block on it (taken from the “Address Block” custom object that ships with Designer, found under the Custom tab in the Library palette). Each field in the address block (which excludes the “Locale” field at the top that’s just there for informational purposes) has its Caption property bound to a specific data node in the data connection I’ve defined, based on some different localized XML Data files. To localize the form at run-time (e.g. in Acrobat), just open the form and then load the XML Data file pertaining to the locale you want to use.

Download Sample [zip]

Minimum Requirements: Designer 7.1, Acrobat Pro 7.0.5.

Note: If you open the form in Acrobat, don’t forget to load a data file into it by using the options under the “File | Form Data” menu.

I should also mention that since it’s possible to have multiple data connections pointing to databases and/or WSDL connections, you could use one connection to localize your form and another to capture the data entered into your form (by binding the field values as usual, using the Binding tab in the Object palette, to data nodes in the second data connection). If you have any questions about this, I’ll be glad to answer them!


Posted by Stefan Cameron on August 2nd, 2006
Filed under Data Binding,Tutorials