John Brinkman on form design

Posts in Category "layout"

In order for customers to successfully distribute forms, they require predictability. Predictability is more important than bug-free software (if there were such a thing). Predictability means the form you originally developed continues to work the same when opened in a new version of Adobe Reader. "the same" means it has the same set of features and the same set of bugs as in the previous version.

Same features, same bugs. If you want the new features and bug fixes you need to bring your form into Designer and target it for a newer release of Reader. But even so, for complex forms you probably prefer to get the new features without necessarily getting the new bug fixes. Bug fixes can change form appearance and/or script behaviour. What users most often want is to use that cool new feature, but not to tweak their layout due to the fixes in form rendering.

To accommodate these needs, there are two dials that control the form: the target version and the original version.

Target version

field.setItems() was an enhancement to the XFA object model for Reader 9. Suppose Adobe Reader 9 opens a form Designed for Reader 8. What should it do if this Reader 8 form happens to have a script calling field.setItems() ? It should do the same thing that Reader 8 does: throw an error.

Each form is stamped with a target version. The stamp is expressed as the XFA version. Reader 8 uses XFA version 2.6. Reader 9 uses XFA version 2.8. Whenever Reader is asked to execute a script function, it first checks whether that script function is valid for that version of XFA.

This applies to more than just script functions. It applies to XFA markup as well. e.g. Hyphenation was added in XFA 2.8. If Reader encounters a 2.6 form that has the markup commands to turn on hyphenation, the hyphenation will not be enabled.

The XFA version is defined by the XML namespace of the template: e.g.

<template xmlns="http://www.xfa.org/schema/xfa-template/2.8/">

The target version is controlled in the Designer UI under form properties/defaults. Note that by default, Designer will set the target version one version back from the current shipping version of Adobe Reader.

Original Version

Form behaviour is determined by more than just new features, it is also determined by bug fixes. In some cases fixing a bug in Reader can actually break existing forms that inadvertently relied on the buggy behaviour. Therefore by default we preserve old behaviours when migrating a form forward.

We preserve the old behaviour by storing the original version of the form in a processing instruction in the template. When you modify your form in Designer to take it from Reader 8.1 to Reader 9.0, we embed the original version inside the form so that you end up with the version 9 features, but the version 8.1 behaviours. The processing instruction looks like this:

<?originalXFAVersion http://www.xfa.org/schema/xfa-template/2.6/?>

When Reader 9 opens a form with an original version of 2.6, it makes sure that the behaviours are consistent with Reader 8.1.

This is all well and good, but there are times where you really, really need the latest bug fixes. Unfortunately there’s no UI to move the original version forward. If you need the latest and greatest bug fixes, the go into the XML source tab, find the originalXFAVersion processing instruction and delete it. Now your form will get all the behaviours and features of XFA 2.8 and Reader 9.

Sample

I have attached a sample form that demonstrates changing the target and original versions. There are three interesting part to the form.

1. The form has both target version and original version set to 2.6 (Reader 8.1). There is a button on the form that extracts these values from the template and populates a couple of fields with the result.

2. When you open the form in Designer, you will see a field displaying current date and time in ISO8601 format. This field has a calculation that looks like this:

this.rawValue = "20090205T163000"; xfa.host.currentDateTime();

When you preview this form, the first line executes fine, but the second line encounters the currentDateTime() function that was added to XFA version 2.8. As a result, the script fails on the second line, and the value of the field remains "20090205T163000". If you open the script console in Acrobat (control+J) you’ll see the error.

3. The bottom part of the form illustrates a bug fix that was made in Reader 9. Hidden fields are not supposed to affect layout. The hidden property has always behaved properly in flowed subforms, but we discovered a bug in positioned containers. Prior to Reader 9 a hidden field would expand the extent of a growable, positioned subform. In this form, the subform is taller than it should be.

Update The Target Version

Change the target version (in the form properties/defaults tab) from "Acrobat and Adobe Reader 8.1 or later" to "Acrobat and Adobe Reader 9.0 and later". Now when you preview the form you will see:

1. The target version is now 2.8, but the original version remains 2.6.

2. The calculation now works without error. The field displays the current date and time.

3. The growable subform renders at the same height as it did in Reader 8.1

Update the Original Version

Go into XML source mode and remove the originalXFAVersion processing instruction. Now when you preview the form you will see:

1. Both target and original versions are 2.8

2. The calculation continues to work

3. The growable subform has collapsed to an extent that does not include the hidden field

The Deep End

Discovering Processing Instructions at Runtime

I was able to get at the processing instructions by loading an E4X XML object with the results of xfa.template.saveXML(). I don’t recommend doing this in a production form, since the result of calling saveXML() on the template can be a very, very large string — especially if there are images embedded in the template.

If you wanted just the target version (the xml namespace), there is an easy way to get it with the ns property: xfa.template.ns.

FormTargetVersion

I simplified a couple of details in order to make this easier to explain. The full story is a little more complicated.

When Designer opens a form that has markup that is newer than the target version, it will automatically update the namespace of the XFA template. So for example: Designer opens a 2.6 template with a target version of Reader 8.1. It discovers syntax for turning on letter spacing, so it will automatically update the template to 2.8. But the target version remains Reader 8.1. This is because the UI for target version is actually driven by another processing instruction:

<?templateDesigner FormTargetVersion 26?>

Under most circumstances, Designer will keep the FormTargetVersion and the XFA template version synchronized. If the template version is newer, then you will undoubtedly find a bunch of messages in your warnings tab. This is Designer’s way of telling you that you’re using features that do not work in your target version. Until you clean those up, your form will not work correctly in your target version.

Choosing Selected Changes

Changing the original version is pretty high level switch. You either get all the bug fixes for a new release, or you get none. In reality, there are qualifiers that allow you to preserve selected behaviours. Suppose that in my sample form I wanted the bug fixes that came with Reader 9/XFA 2.8 except for the fix for hidden fields in positioned subforms. In that case I can specify the processing instruction as:

Most of the time, I am happy with the default behaviour where original version behaviour is enforced by default. However there is one exception. My previous post described how the behaviour of strict scoping changed from 8.1 to 9.0. The difference is that with strict scoping on in 8.1 we release JavaScript variables declared in script objects. In 9.0 we preserve these variables. If you are targeting 9.0 and make extensive use of script objects, make sure that you set your original version to 9.0 as well.

Changing Default Behaviours

There have been a couple of times where we have changed behaviour and made the new behaviour the default without protecting it with the original version mechanism. The most infamous was when we added "direct rendering" for dynamic forms. In this case we had developed a way to dramatically improve the performance of dynamic forms. We had to choose between turning the new behaviour (and the performance boost) on for all forms or just for those forms designed for 8.1 and later. We chose to make the new behaviour the default. If this caused problems, the form author could "opt out" by inserting an originalXFAVersion processing instruction.

A common layout task I’ve heard requested is the ability to place a flowed subform at the bottom of a page. Picture a series of detail subforms followed by a summary. Instead of having the summary positioned immediately below the last detail subform, we want it anchored to the bottom of the page.

There is nothing in the XFA markup that allows this kind of subform placement. However, we can achieve this via script. The solution involves placing a "spacer" subform immediately before the summary subform. Initially the spacer subform has a height of zero. After the initial layout is complete, we calculate how much space is left at the bottom of the page. We then set the height of the spacer subform to the remainder amount and force a re-layout.

With the size taken up by the newly expanded spacer subform, the summary subform will now be positioned at page bottom. To see this work, have a look at the sample form. Notice that as you add/remove detail subforms the summary subform remains at a fixed position. One important detail to make this work is that we added script to the add/remove buttons to clear the current spacer. Before changing the layout we set the height of the spacer subform back to zero and set a form variable to indicate that the spacer needs to be re-evaluated.