Archive for March, 2009

XFA 3.0: List of Invalid Fields

This post describes the last of the enhancements targeting form authoring usability in XFA 3.0/Acrobat 9.1.

The subform object has a new script method:

<container list> = subform.getInvalidObjects();

This function returns a list of all the descendant containers (field, exclGroup, subform) of a subform which are in an invalid state.  If the subform that this script method is called on is itself invalid, that subform will be included in the returned list. The list is only generated on demand by recursively traversing the subform. The list returned is in document order.

This function will allow form authors to selectively highlight errors on sections of their form.  For example, in the "exit" event of a subform, you could highlight the missing or invalid fields contained within that subform, and direct the form filler to complete that section before continuing with the rest of the form.

As mentioned in yesterday’s post, missing mandatory fields are not considered invalid until they have been edited or there has been a call to execValidate().  To be consistent, the call to getInvalidObjects() respects the same rule.  The returned list will not include missing mandatory fields until the XFA processor considers them invalid. 

One of the places where the list of invalid fields is useful is during the submit process.  Form authors can use the preSubmit event to detect invalid fields, notify the user and cancel the submit action.  Note that submit performs a call to execValidate() — but only after preSubmit has fired.  This means that a call to getInvalidObjects() in the preSubmit event might not include missing mandatory fields.  If you want to be sure those fields included, simply call execValidate() before the call to getInvalidObjects().

It will be some time before form authors can really take advantage of the XFA 3.0 enhancements — mainly because it will be a while before Acrobat/Reader 9.1 is on enough desktops.  In the meantime, I expect authors will continue to make use of JavaScript frameworks to gain enough control over the user experience.  If you are new to the idea of JavaScript frameworks, have a look at the one that I’ve included in various samples.  The framework is described in a series of posts: Validation Patterns: Part 1, Part 2, Part 3 and here. The most recent version of the framework is embedded in the samples for this post

The framework was designed to:

  • Imitate the "form authoring usability" enhancements described in the last few XFA 3.0 blog postings
  • Leverage as much of the native XFA validation mechanism as possible for controlling validations
  • Over time, be replaced by the new built-in XFA 3.0 capabilities

I anticipate one one more post on XFA 3.0 enhancements and then back to regular programming.

XFA 3.0: Validation State Change Event

The field, exclGroup and subform elements have a new event: validationState.  The primary use of this event is to change the appearance of fields as they become valid or invalid.

Just for review — there are three ways in which objects can be considered invalid:

  1. A mandatory field/exclusion group that does not have a value
  2. A field with a value that cannot be formatted with its validation picture format
  3. A field/subform/exclusion group whose validation script returns "false"

Rules controlling the validationState Event

The validationState event fires under the following conditions:

  • Immediately following the initialize event — allowing the form author to control the initial appearance of the object
  • When the field/subform/exclusion group transitions between a valid and invalid state (or vice versa).
  • When the reason for the invalid state changes e.g. a field is in an invalid state because it is not filled in (missing mandatory).  The user then fills in a value that violates the validation picture clause.  In this case the field goes from one invalid state to a different invalid state and the event will fire.
  • The form session begins tracking mandatory fields

The last point needs a bit more explanation.  When a field is marked as mandatory, it starts the form session in a valid state — even though it does not have a value.  The reason is that we don’t want users notified about a whole bunch of invalid empty fields as soon as they open a blank form.  There are 3 things that will put a missing mandatory field into an invalid state:

  1. A populated mandatory field gets emptied
  2. A call to execValidate() on the field or one of its ancestors
  3. A submit action (which is really the same as an execValidate())

When any one of these three operations happen, the validationState event will fire for any unpopulated mandatory fields.

To be on the safe side, script associated with the validationState event should be robust enough to allow the event to fire multiple times — even if the validation state hasn’t actually changed.

The new errorText property

So the validationState event has fired… how does the script author know whether the field is now in a valid or invalid state?  Authors can check the new property on field/subform/exclusion group: errorText.  When a field is in a valid state, this property will return an empty string.  When in an invalid state, it will return the user-defined message for that error. 

Objects may have 3 different user-defined messages — one for each kind of validation failure.  These messages are represented in the scripting model as:

field.validate.message.scriptTest.value
field.validate.message.formatTest.value
field.validate.message.nullTest.value

or via their shortcut properties:

field.validationMessage
field.formatMessage
field.mandatoryMessage

The errorText property will be populated with the message from the associated validation failure.  If the user has not defined a message, a default message is provided ("Validation failed.").

Turning off Acrobat/Reader Field Highlighting

If you are using the validationState event to change the appearance of fields, the automatic field highlighting of Acrobat/Reader can get in the way.  Fortunately, it can be turned off.  This script (placed on a form:ready event) will do the trick:

    if (xfa.host.name == "Acrobat")
       app.runtimeHighlight = false;

Example

This example will hide the border on valid fields and for invalid fields will colour the border red.  Note that the example places the script on a subform and uses event propagation to cause it to apply to all nested objects.

<subform>
  <event activity="validationState" listen="refAndDescendents">
    <script>
      if (HasValue($event.target.errorText)) then
        $event.target.edge.presence = "hidden";
      else
        $event.target.edge.presence = "visible";
        $event.target.borderColor = "255,0,0"
      endif
    </script>
  </event>
</subform>

 

XFA 3.0: Message Handling Options

Of the XFA 3.0 enhancements, it’s safe to say this one was overdue.  Today XFA processor in Acrobat/Reader notifies users about invalid fields by popping up dialog boxes.  However, form authors feel so strongly about wanting control over the notification mechanism that they develop elaborate JavaScript frameworks to avoid using the message boxes associated with the built-in validation/notification mechanism.

To put the flexibility back in the hands of the form author, XFA 3.0 has a new configuration setting to control how errors are handled:

<acrobat>
  <common>
    <validationMessaging>
        noMessages              |
       
firstMessageOnly        |
        allMessagesIndividually |
        allMessagesTogether

    </validationMessaging>
  <common>
</acrobat>

Don’t worry about remembering the syntax or where it goes — Designer will have a UI for setting this option.
The impact of this setting is pretty self-explanatory — but I’ll explain anyway:

  • noMessages – Acrobat/Reader will not emit any messages for invalid fields.  It will be up to the form author to notify the user in some way — likely by changing the appearance of the invalid fields.

  • firstMessageOnly – When there are multiple errors on the form, Acrobat/Reader will display a message box for the first error and will suppress messages for all subsequent errors.  This prevents the user from being inundated with a long series of messages — one from each invalid field.

  • allMessagesIndividually — (default) This corresponds to today’s behaviour.  Well, not quite.  There are instances today where we combine messages — for example messages for missing mandatory fields.  But combined messages don’t happen as much as you might like.

  • allMessagesTogether — Aggregate all the messages from all invalid fields and emit a single dialog with all messages combined.

The sequence of validation script execution remains the same no matter which messaging option is chosen.  If the user attempts to submit when there are still missing or invalid fields, the submit operation will cancel with a message box.

XFA 3.0: Event Propagation

One of the themes for XFA 3.0 development was to make form development easier.  More specifically, to make the common things easy — and to reduce the amount of JavaScript code needed to develop a good user experience.  There were several enhancements geared toward achieving this goal.  Today’s blog entry describes one of them.

I have seen many forms where the author wants to implement the same behaviour on many fields.  For example: an author wants the field with focus to have a different background colour. They add "enter" and "exit" events on every field to set/reset the colour.  After a while, this gets… tedious and error-prone.  Especially when you need to tweak the script that has already been added to all your fields.

To improve this situation we have enhanced the <event> element in XFA 3.0 by adding the listen attribute.

<event activity="<enum>"
       name="<name>"
       ref="SOM" 
       listen="refOnly | refAndDescendents">

To understand the new listen attribute, you need to fully understand the functionality of the existing ref attribute.  The ref attribute is a SOM expression that indicates which object is listening to the event.  By default, ref has a value of "$" : indicating the current object.  The only time Designer sets this to anything other than "$" is:

  • for pre/postSave, pre/postPrint, docReady and docClose the ref target is "$host". 
  • for pre/postSubmit the ref target is "$form"
  • for ready the ref target is "$form" or "$layout" (form:ready, layout:ready)

What you probably already realize after reading this far is that the event definition can be hosted anywhere.  e.g. the enter event of field1 can be physically located in field2 as long as the ref attribute points back to field1.

The new listen attribute modifies the behaviour of the ref attribute.  It defaults to "refOnly" (the legacy behaviour) — where the event applies to only the object pointed at by ref.  When listen is set to "refAndDescendents", the event now applies to the referenced object and all nested objects. 

Example

If we want a single enter event script to apply to all fields in the form, we construct it like this:

<event activity="enter" name="event__enter" ref="$form" listen="refAndDescendents">
  <script contentType="application/x-javascript">
     if (xfa.event.target.className == "field")
        xfa.event.target.fillColor = "100,100,255";
  </script>
</event>

Now the event and script are specified in one place and apply to all containers in the form. 

Notes about the script:

  • The context for the script is the object that hosts the event.  If this event were hosted by the root subform, the JavaScript "this" object would be the root subform.
  • The target of the script — the object pointed to by the ref attribute is represented by the xfa.event.target property.
  • The script in my example guards its logic to work only on field objects.  The event propagates to all nested subforms, fields and exclusion groups, but we want it to affect only fields.  Without this guard in place, the entire form would turn blue the first time we set focus — since we would end up setting the fillColor of the root subform to blue.

Multiple Events

What you might have inferred from this discussion is that a single field may have multiple enter (or other) events. This has always been true — it’s just that Designer’s UI supports only one of each kind of event per object.  (That’s not a criticism of Designer — it’s by far the most common case to have at most one of each kind of event).

Now that we have propagating events, it is more likely that a field might end up with multiple enter events– a global enter event defined on the root subform and a local enter event defined on a field.  That raises the question:  what order will the events fire in?  The official answer is that the order is undefined.  The XFA spec does not prescribe in what order events will fire.  The unofficial answer is that the events will fire in document order.  That implies that the globally defined event will fire before the local event, since the global event is defined higher up in document order.

The Deep End

This change brings XFA event processing in closer alignment with XML event processing: http://www.w3.org/TR/xml-events. XFA events can now "bubble" in manner similar to XML events. Note however that XML events bubble from the target node upward through the target node’s ancestors; in contrast, XFA events are dispatched in document order.

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.

XFA 3.0: presence="inactive"

This is the first in a short series of posts to describe the new functionality found in XFA 3.0.

If you have developed a form with even modest complexity, you have almost certainly manipulated the presence property of fields, subforms and exclusion groups.  With XFA 3.0 we have extended the possible values of this property to include "inactive".

The existing presence attribute can be one of these properties:

  • visible – (default) Object is visible
  • invisible – Object is invisible, but still participates in layout and event processing.  i.e. space is allocated in the layout to display this object, its calculations and validations still fire.
  • hidden – Object is invisible and excluded from layout.  No space is reserved for this object. Its calculations and validations still fire.

In XFA 3.0 we now add:

  • inactive – Object is hidden and excluded from event processing.  Calculations, validations and other events do not fire.

Scenario

A mortgage application form is sent to a client. The form has an optional section where the user may apply for mortgage life insurance.  The life insurance application section is pre-populated with client information from the server.  If the user chooses not to apply for life insurance:

  • the optional section will remain hidden
  • The calculation and validation scripts in the optional section will not fire
  • Other event scripts such as layout:ready, form:ready, prePrint, preSubmit etc. do not fire
  • We do not check for mandatory fields

All these behaviours will be true as long as the subform that defines the life insurance application is set to presence="inactive".

Once the user checks the box to apply for insurance, we toggle the setting to presence="visible" and then that section of the form becomes fully functional.

Containers, Properties

The new behaviour for presence applies only to these container objects: subform, field, exclGroup.  The presence attribute is also found on draw, fill, items, edge, border, caption and corner — however in these contexts it will not introduce any new behaviour.  It will behave the same as "hidden".

The Deep End

In a previous post, I described the various stages of processing we go through when we open a form:

  1. Loading content
  2. Merging data with template (create Form DOM)
  3. Executing calculations and validations
  4. Layout (pagination)
  5. Render

The enumeration of the presence attribute determines which of these stages a form object will participate in:

  1. Loading content
  2. Merging data with template (create Form DOM) (visible, invisible, hidden, inactive)
  3. Executing calculations and validations (visible, invisible, hidden)
  4. Layout (pagination) (visible, invisible)
  5. Render (visible)

A Couple of Updates

As I continue to develop new samples, I make use of the tools/script libraries I’ve been developing along the way.  In the past week I have made improvements to a couple of the script libraries that I included in previous blog entries.  Rather than re-issue these samples in new entries, I chose to update the original entries.  For both Form DOM Debugging Tool and Handling JavaScript Exceptions I have updated the entries by adding a section at the end of the post and by updating the sample collateral.

Reader 9.1 and XFA 3.0

As most of you are aware, Adobe has released Reader 9.1.  What you might not be aware of is that there are some XFA enhancements bundled into this release.  This version of Reader now supports XFA 3.0 templates.  Here’s my take on a set of "frequently asked questions":

Q. XFA 3.0?  Is this a major release?
A. In spite of the fact that XFA 3.0 now has a new major version number, it is not a major revision.  We had to choose between incrementing XFA 2.9 to either XFA 2.10 or XFA 3.0.  We chose 3.0 primarily because we were worried about code (ours and partners) that converts the XFA version number to a floating point number for comparison purposes.  Any code that treats the version as a floating point number would not compare "2.10" properly against "2.9".

Q. What happened to XFA 2.9? 
A. We reserved XFA 2.9 for a server-side customer-specific dot release.  To the best of my knowledge, we did not end up modifying the specification for XFA 2.9.

Q. When will the XFA 3.0 spec be released to the public?
A. Currently scheduled for release on March 23

Q. When will we have a version of Designer that targets XFA 3.0 and Acrobat/Reader 9.1?
A. I can’t pass along a specific date.  There will be a new version of Designer with the next major releases of LiveCycle ES.

Q. What version of PDF will contain XFA 3.0?
A. Acrobat/Reader 9.0/XFA 2.8 were hosted by PDF 1.7 with Adobe Extensions Level 3.  Acrobat/Reader 9.1/XFA 3.0 is hosted by PDF 1.7 with Adobe Extensions Level 5.

Q. Does this mean that the XFA template namespace will change again? 
A. Yes.  The new namespace is: http://www.xfa.org/schema/xfa-template/3.0/ We are looking at an enhancement where we will store the XFA version number separately from the namespace.  If you have code in your applications that assumes it can find the version number in the namespace, you might want to think about making the code generic enough so that it could eventually retrieve the version number from somewhere else — perhaps an attribute on the <template> element.

Q. What’s new in XFA 3.0?
A. There are a relatively small number of enhancements, but they are improvements targeting ease of authoring.  I plan to blog the specifics in the coming weeks.

Handling JavaScript Exceptions

I have previously apologized for not being the most sophisticated JavaScript programmer on the block.  But one of the advantages of learning while I blog is that occasionally I discover things that a smart JavaScript programmer would already know, but is interesting to people who are climbing the same learning curve as me.

Today I learned more about JavaScript exceptions.  Or more specifically, the JavaScript exceptions thrown by script errors in XFA.

I’ve written lots of code that handles exceptions this way:

try
{
    xfa.template.foo = "bar";
} catch (err)
{
    app.alert(err.toString());
}

As it turns out, this is not a very satisfying way to report exceptions.  The error object that gets thrown by the script engine does not provide helpful information from the toString() method.  Typically it returns something like: "GeneralError: Operation failed.".  Thanks a lot.

Then it dawned on me that just maybe there was useful information locked up inside the exception object that the toString() method does not report.  I wrote some code (ok, I shamelessly copied some code from O’Reilly’s: JavaScript the Definitive Guide) to examine the contents of the object in my catch block.

I dumped the results out to a field on the form:

try
{
    doInit();
} catch(err)
{
    var vDebug = "";
    for (var prop in err)
    { 
       vDebug += "property: "+ prop+ " value: ["+ err[prop]+ "]\n";
    }
    vDebug += "toString(): " + " value: [" + err.toString() + "]";
    status.rawValue = vDebug;
}

function doInit()
{
    foo.ForceError();
}

The results of this dump show up as:

property: name value: [GeneralError]
property: message value: [Operation failed.]
property: fileName value: [XFA:form1[0]:page1[0]:Subform1[0]:initialize]
property: lineNumber value: [7]
property: extMessage value: [GeneralError: Operation failed.
XFAObject.bar:7:XFA:form1[0]:page1[0]:Subform1[0]:initialize
Invalid property set operation; template doesn’t have property ‘bar’

]
property: number value: [14]
property: stack value: [Error()@:0
nestedFunction()@2:7
ForceError()@2:3
ForceError()@:0
doInit()@XFA:form1[0]:page1[0]:Subform1[0]:initialize:17
@XFA:form1[0]:page1[0]:Subform1[0]:initialize:3
]
toString():  value: [GeneralError: Operation failed.]

Turns out there’s lots of great information available.  The exception we catch comes populated with properties: name, message, filename, lineNumber, extMessage, number and stack.  The toString() method on this object ignores the most useful information and simply concatenates the name and message properties.  The extMessage property is what normally gets dumped to the JavaScript console.

With this knowledge, I can write an exception object formatter that turns the exception message into something both useful and human readable:

Invalid property set operation; template doesn’t have property ‘bar’
Stack:
line: 7 of nestedFunction()
line: 3 of ForceError()
line: 10 of doInit() in the initialize event of: page1.Subform2
line: 3 in the initialize event of: page1.Subform2

The code that produces that output can be found in the attached sample.

Server Side

You might not be aware that our JavaScript processor on the server is different from the one embedded in Acrobat/Reader.  Fortunately, in spite of being different processors, the compatibility between the two is very good.  However the exception object that gets thrown on the server has a different set of properties from the exception object thrown on the client.  The function I wrote to format an exception has conditional code that produces a different result for the server, based on the properties available there:

Invalid property set operation; template doesn’t have property ‘bar’
at line: 8 of script:
1:
2: function ForceError()
3: {
4:     nestedFunction();
5: }
6: function nestedFunction()
7: {
8:     xfa.template.bar = "bar";
9: }
10:
11: this.ForceError = ForceError; this.nestedFunction = nestedFunction;

Why Bother?

If you’ve been debugging JavaScript for some time, then you might wonder why I went to all this trouble, since most of the useful information from a JavaScript error is dumped to the console anyway.  Three reasons:

  1. Some form authors make a practise of wrapping all their JavaScript code in try/catch blocks.
  2. The stack trace information is not part of the information dumped in the JavaScript console
  3. I found a bug where under certain conditions, JavaScript exceptions are *not* reported in the console.

The bug happens when an initialization script creates an instance of a subform.  If that new subform also has an initialization script, its errors go unreported in the console.  This condition (a nested initialization) is probably a bit unlikely for most forms, but has happened to me at least a couple of times.  My workaround is to wrap the initialization script in a try block and dump exception information to the console in the catch block.  And now that I have better tools for dumping exceptions I’m a happier form developer.

The Deep End

Server Side Debugging

When writing code for forms destined for the server, it is important to be able to dump information from your scripts so you can debug them.  In Reader/Acrobat using console.println() is invaluable.  But the console object is specific to Acrobat.  We need something for the server as well.  I use xfa.log.message().

A script to dump an error message then looks like:


catch (err)
{
    var vMsg = scMessage.prettyMessage(err) ;

    // client code
    if (xfa.host.name == "Acrobat")
        console.println(vMsg);

    // LiveCycle server
    if (xfa.host.name == "XFAPresentationAgent")
 &#16
0;      xfa.log.message(0, vMsg);

}

Modified Script

You might have noticed up above that the dump of the exception for the server had a line of code that did not appear in the form:

this.ForceError = ForceError; this.nestedFunction = nestedFunction;

This line gets added to the script by the XFA processor so that for a named script object we can support the syntax: foo.ForceError() and foo.nestedFunction().

Other Kinds of Exceptions

If you write JavaScript code that throws an exception, then the exception object will be whatever you throw.  For example you could do this:

throw "Bad date format";

In this case, the object caught in a catch block will be a string — not the exception object that XFA scripts throw.

March 19 Update

After using the sample code for a few days, I added a couple of improvements to the sample:

It turns out that the contents of the exception in Reader varies depending on the nature of the exception.  The code I wrote handled exceptions thrown by the XFA engine.  Regular JavaScript language exceptions did not populate an "extMessage" member of the exception class.  I updated the pretty printing of the exception accordingly, and added an example of this kind of exception to the form.

Since most of the time we want to display exceptions in the console or server log file, I added a handleError() method that does just that.  In addition, to logging to the console, it calls console.show() in order to make sure the form author sees the exception when they’re debugging.

August 14 Update

Improved the handling of message emitted in Designer.  Added support for thrown string objects.  These changes are explained here.  Download this fragment for the latest version of this script.

A Form to Design a Form

There are a class of form designs where the form fill-in experience follows a basic pattern with a small number of variations.  A survey form is the prime example.  The variable pieces are the question text and a set of response types.  A user who wants to create one of these forms should not have to learn a complicated form design tool.  Given the relatively small number of properties that need to be specified to make a survey work, it should be possible to design a single dynamic form that can be shaped by any survey definition — i.e. one form that can render all variations of a survey.

We can design that survey form, but then we need to figure out an easy way for the author to define their survey.  This is really just another data capture problem — one that can be handled by a form.  So we use a form to design our survey.  A form to design a form.  Kind of like a play within a play.

To accomplish the form-within-a-form, there are two sets of data.  The survey-designer-form captures information for question text and response types and saves this information as its form data.  The fill-in-form has built-in variability (dynamic subforms) whose appearance and behaviour are customized from the data produced by the designer form.  The design data moves from being form data for the designer form to become metadata for the fill-in form.  When the fill-in version of the form runs, it produces its own data — the survey results.

Two Forms in One

Ideally, design and fill would be two separate forms.  But two separate forms means moving data between forms.  And even a relatively simple operation such as moving data is probably more than we can expect from our target users’ skill set.  As well, any multi-step process gets in the way of quickly previewing a survey.  To keep the experience as simple as possible, I’ve taken the approach of combining the design and fill-in experience into the same PDF.   The advantage is that the user deals with only one form and doesn’t have to manage their data.  There are likely better ways to do this.  If the experience were tethered to a server (LiveCycle for example :), it would be easier to manage the data on behalf of the user and keep the forms separate.  That would also make it easier to use a cool flash UI for the survey-design piece. 

But for now, to make the sample easy to distribute, I’ve combined them into one PDF.

Here’s the sample in several stages of processing:

An XFA form can fairly easily house two separate design experiences.  In my example, I had two optional top-level subforms: DesignSurvey and FillSurvey.  During survey design, the DesignSurvey subform exists.  During preview, DesignSurvey and FillSurvey both exist.  During fill-in, only the FillSurvey subform exists.  Which subform(s) appear is controlled by the form data and by script logic.

The Design mode allows you to create sections and question within sections.  The data to define a simple one-question survey looks like this:

<DefineSurvey> 
  <SurveyTitle>A survey about surveys</SurveyTitle>

  <ChoiceList>
    <ChoiceName>YesNo</ChoiceName>
    <Choice>
      <ChoiceText>Yes</ChoiceText>
      <Value>1</Value>
    </Choice>
    <Choice>
      <ChoiceText>No</ChoiceText>
      <Value>0</Value>
    </Choice>
  </ChoiceList>

  <Section>
    <SectionName>Personal Information</SectionName>

    <Question>
      <QuestionNumber>3</QuestionNumber>
      <QuestionText>Are you married?</QuestionText>
      <QuestionType>multipleChoice</QuestionType>
      <Required>0</Required>
      <ChoiceListName>YesNo</ChoiceListName>
      <MinSelections>0</MinSelections>
      <MaxSelections>1</MaxSelections>
    </Question>

  </Section>
</DefineSurvey

When designing the form, this data resides in the normal place for form data under:

<xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">
   <xfa:data>
      <DefineSurvey>…</DefineSurvey>
   </xfa:data>
</xfa:datasets>

When we switch to "fill-mode", we move the form definition (<DefineSurvey>) to a separate dataset and the fill-in data then lives under <xfa:data>:

<xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">
  <DefineSurvey>…</DefineSurvey>
  <xfa:data>
    <Survey>
      <Section>
        <Question>
          <QuestionNumber>3</QuestionNumber>
          <QuestionText>Are you married?</QuestionText>
          <Answer>1</Answer>
        </Question>
      </Section>
    </Survey>
  </xfa:data>
</xfa:datasets>

Once the form is in "fill mode", the PDF can be distributed to users.  Enable usage rights so they can save the results.  Or better yet, host your survey on acrobat.com.

Next Steps

The form design could expose more options. e.g. conditional logic, more response types, more constraints on responses, styling options.  It’s all just SMOP (small matter of programming). 

Submit

I added a submit button to the form in order to ret
urn the survey results.  There are a couple of things that are interesting about the handling of the submission. The survey definition includes a target email address.  The submit button gets updated with target and subject with this bit of code:

var vEmailTarget = "mailto:" + xfa.datasets.DefineSurvey.Email.value
                             +
"?subject=" + xfa.datasets.DefineSurvey.SurveyTitle.value;
EmailSubmit.event__click.submit.target = vEmailTarget;

The other thing I did with submit was make use of the new event cancel capability.  When the user clicks on the "submit" button, the pre-submit event fires.  I put this script there:

Survey.FillSurvey.#subform[2].EmailSubmit::preSubmit:form – (JavaScript, client)
if (scValidate.formHasErrors())
{
    scValidate.showErrors();
    xfa.host.messageBox("The survey is incomplete.  Please fill in the highlighted questions.");
    xfa.event.cancelAction = true;
    xfa.host.setFocus(scValidate.getFirstError());
}

The xfa.event.cancelAction property is new in Acrobat/Reader 9.  It allows you to cancel the upcoming action in prePrint, preSubmit, preExecute, preOpen, preSign events.

Validation Framework

The form makes extensive use of the validation framework I defined in previous blog entries — most notably, the exclusion group objects.  The framework is contained in the three script objects at the top of the form: scValidation, scGeneral and scGroup. These are re-usable objects that can be used in forms where you want fine-tuned control over the form validation experience.

For those who have used previous versions of this framework, I added some enhancements to suit my needs for this sample:

New function: scValidate.hideErrors()

After this is called, any fields with errors are not highlighted until…

New function: scValidate.showErrors(subform)

This function causes error fields that are descendents of the input subform to be highlighted.  If subform is omitted, errors are displayed on the entire form.

New function: getFirstError(subform)

Returns the first descendent field under the given subform that has an error.  It subform is not specified, returns the first error field on the whole form.

scGeneral.assignRichText(targetField, source)

Where targetField is a rich text field and the source is either a dataValue with rich text or another rich text field.

I also changed the code that highlights invalid fields.  Instead of mucking with borders, I simply set the fill colour.