Posts in Category "Samples"

Tables with Variable Numbers of Columns

Support for tables in Designer assumes you know in advance how many columns there will be in your table. Occasionally this will not be the case.  You might want a table that adapts to the number of columns that occur in your data.  In other words, you want a table with rows where a cell is represented by a repeating subform.  And while Designer doesn’t support creating such a table via the table UI, it is possible to create one using a bit of script

The technique involves leverages the fact that tables and rows are really just subforms with special attributes.  So when designing the form, we create:

  • A subform to represent the table.  Make it a subform where content flows top-to-bottom.
  • Inside the table subform create one or more row subforms.  Make the row subforms flow left-to-right.
  • Inside each row subform, create combinations of fields, text and subforms that will be treated as columns.
  • It’s best to make the cells growable in width and allow the layout engine to determine best fit.  If you want control over width, leave the columns growable and set the columnWidths attribute on the table subform (see script below)

And then to turn this into a table, add this initialization script to the table subform:

// mark this subform as a table
this.layout = "table";

/** 
* if needed, we could explicitly set the column widths * If left alone, the columns will be auto-sized */ //this.columnWidths = "1in 2in 2in 2in 1in";

And then for each row in the table, add this initialization script:

// mark this subform as a row
this.layout = "row";
if (this.instanceIndex === 0) {
    // mark the first row as a header
    this.assist.role="TH";
} else {
    // mark each subsequent row as a regular table row
    this.assist.role="TR";
}

Note that setting the role attribute is very important for accessibility — it allows the screen reader to use the table header cells to describe the contents of the table body cells. Here is a sample form that implements this technique — along with sample data for previewing.

Fix Layout Problems

This is the fourth of four exercises that were part of the LiveCycle best practices lab at MAX.

Fix Layout Problems using XFADebugger

The purpose of this exercise is to fix layout problems by analyzing their PDF with XFADebugger.pdf.

In Designer, open: EX4 LayoutProblem.pdf (with preview data file: afiList.xml)
In preview, notice that :

  • Content was pushed off the first page
  • The second page is truncated

Open XFADebugger.pdf in Acrobat. Use the “Load XFA/PDF file” to open EX4 LayoutProblem.pdf.

  • Explore the content in the debugger view. Expand the trees. Click on nodes.
  • Click the “Find Warnings” button several times to see various warnings reported.
  • Fix the warnings in Designer

Here is the file with the layout problems fixed: EX4 LayoutProblem (Solution).pdf

Moving code into a shared script object

This is the thrid of four exercises that were part of the LiveCycle best practices lab at MAX.

Create a shared script object fragment

Open the file: EX3 addressCopy.pdf in Designer

Preview the file with sample data and note the behavior when you check "same as billing".  The shipping address values are greyed out and the values are synchronized with the billing address.

The Exercise:

  • The logic to copy values and to change the appearance of the destination subform is implemented under the
    “Same as Billing” checkbox.
  • Take the script methods to copy subforms and “grey-out” data, centralize them in a script object
  • Create a re-usable fragment from the script object

Bonus 1:

  • Change the form properties so that script changes are not preserved:
    Form Properties/Run-time/Preserve scripting change… : Manually
  • Open the form in Acrobat, select “Same as Billing”, save, close and re-open the form
  • Notice the Shipping address is no longer read-only
  • Fix by applying the read-only setting in an initialization script

Bonus 2:

  • Both instances of the country field populate a list of provinces or states. Centralize/share this code.

Solution: EX3 addressCopy (Solution).pdf

Customize a Validation Experience

This is the second of four exercises that were part of the LiveCycle best practices lab at MAX

Implement Field Validation

Open EX2 Validation.pdf in Designer

  • Preview and click the “Submit by email” button to validate the form
  • Notice:
    • Errors are reported with message boxes. 
    • Selecting the error message in the listbox highlights the error field
    • Tabbing out of the listbox will set focus to the selected error field.
  • Change the form properties to customize error handling to: “Don’t show any message boxes”
  • Modify the (propagated) validationState event under the root subform:
    • On error: color fields and modify the toolTip value
    • Else: revert field color and toolTip
  • Bonus:
    • Add a change event to the phone number to restrict entry to digits

The solution file is: EX2 Validation (Solution).pdf

Reverting changes to fields:

To write re-usable code for changing values back to their original state, we should never hard-code the changes. E.g. we should never revert a color by writing: Field.fillColor = "255,255,255". The original color may not have been white.

Rather, after modifying an element, we can revert by removing the changed element. Removing an element forces the XFA processor to use the original value found in the form template.

E.g. Change the font color by modifying the font.fill.color element:
Field.font.fill.color.value = "255,0,0";

Revert the change by removing the color element:
Field.font.fill.nodes.remove(Field.font.fill.color);

Or by removing one of the ancestor properties:
Field.nodes.remove(Field.font);

Changing/filtering keystrokes

  • In the change event, xfa.event.change holds the contents of your key stroke (or paste buffer)
  • E.g. force all input to upper case:

    xfa.event.change = xfa.event.change.toUpperCase();

Validate image size

Let’s talk about image size (again).  But this time in the context of data capture. We have this wonderful image field that allows your users to attach image files to their form.  This is great. but it’s also scary.  You likely don’t want to use this for the 10MB images that came straight from their digital camera.  Consider that these images will be base64-encoded and inserted with the rest of the form data — and stored in the PDF.  You want to limit them to images that are reasonably sized. 

Today’s sample has an image field with a validation script that checks the size of the loaded image.  For an image field, field.rawValue will return the base64 value of the embedded image. The length of that string will tell you how big the image is. My validation script does a rough conversion — image size is roughly 3/4 the size of the base64 string. If that size is greater than your threshold you can choose to either reject the image (set the field to null) or simply mark the field as invalid.

More Macro Goodies

It’s been quiet here.  I have been mostly heads-down preparing for MAX.  I hope I’ll see many of you there.  If not, the content I’m working on will eventually find its way to this blog.

But today the focus is on some things to get you further ahead with macros.

Updated Samples

I’ve included a zip file with the following:

  • updated lint check macro
  • updated accessibility checker macro
  • updated form report macro
  • sample macro to use as a ‘starter’ for new macros

You can simply unzip this file under your Designer install directory and they should immediately work.

If you’re writing new macros, you should appreciate sample.js.  It includes:

  • A convenience method for emiting messages to Designer’s log panel
  • A convenience method for recursively iterating over the contents of your form
  • A ‘catch’ block that does a good job of reporting script errors in your macro.

Have a peek at the comments in the macro for more details.

In full disclosure, I admit that one of the reasons for updating these macros is that I found a couple issues where old macros didn’t work in the new version of Designer.  But not to worry, there are workarounds available and the new versions of these macros work even better than the old ones.

Dreamweaver

Not surprisingly, I use Dreamweaver to edit my macro javascript files. It is possible to extend the object assist/hinting mechanism of Dreamweaver.  Take this designer.xml and place it under the Dreamweaver/configuration/CodeHints directory.  Then, next time you start DW, you will have code hinting for the designer object.

I’d like to extend this to more of the XFA object model, but that’s a project for another day.

Macros in ADEP Designer 10.0

Well, here’s a poorly kept secret now made public.  The macro feature that we previewed in the ES2 Designer is now an officially supported feature.

You can find the online documentation here.

The good news is that the macros you wrote for ES2 should continue to work in the new Designer.  The only changes is that we’ve changed the directory name where they’re stored (now under "macros").  As well, each macro is described by a macro.xml file.

There are a couple items I should hightlight:

There is a method: designer.callExternalFunction() that allows you to call an arbitrary method in a .DLL. Here is your escape key for every integration problem you might conceive.

designer.filterNodeTree() is added to enhance performance.  Traversing the entire DOM searching for specific elements can ge slow on large forms.  For example,
designer.filterNodeTree(xfa.template, "className", "font")
will return all the <font> elements in the form. 
designer.filterNodeTree(xfa.template, "name", "price")
will return all the objects named "price".

If you produce .chm help files to document your macro, you can invoke them with designer.showHelp()

And, just to give you an excuse to try this out, I’ve written a new macro.  This one will append a floating field reference for an existing field to a text object.  Here are the macro.xml and javascript files. Usage: select the text and field objects, then invoke the macro.

 

A Runtime Accessibility Checker

I continue to be interested in the challenges of developing forms that give a good experience for visually impaired users using screen readers.  Previously we have focussed on checking accessibility properties at design time. However, analysing the form design template doesn’t always fit the user workflow.  There are cases where accessibility properties are populated at runtime by script or by data binding. In this case, these properties are un-populated in the design version and consequently, the design-time accessibility checker will give warnings — yet at runtime the accessibility experience may be just fine.
In addition, we want to make sure that the accessibility experience adapts to changes in dynamic forms. We need to be able to test accessibility in various states — e.g. after subforms are added or removed.

The obvious answer is to perform accessibility checking at runtime — while the form is open in Reader or Acrobat.  Today we’ll look at a solution to that problem.  A mechanism for instrumenting your forms to allow you to analyse the state of accessibility information at runtime.

The Solution

For those of you who want to use the solution without necessarily understanding how it works, follow these steps:

  • Drag this fragment anywhere onto your form.  It will add a hidden subform with no fields (just script)
  • With the form open in Reader/Acrobat, paste the value “AccCheck” into any field on the form
  • From the resulting dialog, select which accessibility checks to run
  • Copy the results from the report dialog for future reference
  • All elements with accessibility issues will be re-drawn with a 2pt red border.  If the element is a field or draw, it will also be shaded light red and the accessibility error text will be assigned to the tooltip.
  • Close your form and go back to Designer to fix any identified issues

Try it out with this sample form. Note that this form dynamically populates some properties.  The Designer-based accessibility checker will report twice the number of errors.

Hopefully the embedded checker is innocuous enough that you could leave it in a production form.   But if not, you should be able to inject it during your testing phase and remove it when you’re ready to deploy your form.

The Deep End

For those who want to know how this works…

The challenge is to add something to your form without introducing an observer effect – i.e. analytics that can run without changes to your form UI and without impacting normal form processing.

The runtime accessibility checker is designed as a form fragment that you can drag onto any form.  The fragment is a single, hidden subform with no fields, no data binding — just script.
We want to trigger the analysis script without requiring the user add a “check accessibility” button to their form.  In this case, I added a propagating change event to the fragment subform.  This script checks the xfa.event.change event property.  If the change text is equal to “AccCheck”, we launch the accessibility checker.
The checker uses JavaScript dialogs to prompt for checks to perform and has a second dialog to display the results.
The JavaScript performing the checks is adapted from the Designer version of the accessibility checker.  The main difference is that it analyses the Form DOM (the runtime) instead of the Template DOM (the design). 

Propagating Events

Back to another of the 9.1 enhancements intended to help you write less code: propagating events. See here for an overview.  Again, now that we have a Design tool capable of authoring these and now that the 9.1 Reader is more common, we should have a closer look. 

For starters, here is the sample form I’ll be referencing.  As you try the form you will notice a couple of behaviors:

  • as you move your mouse over fields, they are highlighted
  • invalid fields get a thick red border

The punchline here is that there are 14 text fields on the form, but only one each of a mouseEnter, mouseExit and validationState event scripts.  These events are defined on the root subform and apply to all descendent objects.  This is a great technique for minimizing code in your form.  A highly recommended form design technique. 

But now the bad news… So far, this functionality has not been easy to get at through Designer.  In the ES2 designer there was a check box to make an event propagate.  But the script dialog didn’t allow field events to be defined in the context of a subform.  Then because of the confusion this functionality caused, the checkbox was removed from the Desginer that shipped with Acrobat 10.  This functionality should re-emerge in a future version of Designer. 

In order to make it easier to get at this functionality, I have written a macro that will add propagating events to subforms.  The UI appears like this:

After you run the macro, the event and script will be added to your subform. Now, the tricky part — to edit the script you need to do two things: 1) close/re-open the form. Yeah, macros are still beta, and Designer doesn’t pick up on the changes made by the macro. 2) on the script dialog for the subform, choose to show: "events with scripts".

One interesting thing to note is that it is valid to have two or more of the same event defined on a subform. e.g. you could have both a propagating enter event as well as a non-propagating enter event.

The Deep End

When changing the display of an object to show an error state, it’s a bit of a challenge to revert the object back to its original state.  e.g. If you change the border color or border thickness you need to know the original color and thickness in order to restore the original state of the field.

Well, not really.  You’ll see the validationState script in the sample uses a different technique to restore the original display.  The script removes the border and assist elements from the field.  To understand why this works you need to understand the relationship between the form and the template.  The template is the definition of a form you see in Designer.  At runtime, we merge the template objects with data and create form objects.  A form field is then a sparsely defined object that has a pointer back to a template field. 

When we execute script that changes the color of a border, we modify the form definition of the border and override the default border properties found in the template. Removing the border from the form field means that we have removed the override and the field then reverts to the definition found in the template.

 

Border Control

In these times. many countries are interested in border control.  Today I’m interested in controlling field borders. Did you know that fields have two borders? There is a border around the entire field (including the caption) and there’s a border around just the content part — excluding the caption. These are edited in two different places in the Designer UI.  Field border is specified under the border tab:

The border around the content portion is specified in the "Appearance" selection of object/field:

Both of these borders can be manipulated in script.  Have a look at this sample form to see what I mean. 

You might notice that the scripts in the sample form do not use the field.borderColor or field.fillColor properties. These are shortcuts — convenience syntax that simplifies the underlying property structure. And while they’re convenient, they don’t give you full control. Most notably, they control only the outer border, do not give access to the widget border. 

As you look at the script in the sample form, you will notice some interesting things:

1) script reflects syntax. 

The (simplified) XML definition of a border looks like this:

<border presence="visible | hidden">

    <edge presence="visible | hidden" thickness="measurement">
        <color value="r,g,b"/>
    </edge>  <!-- [0..4] -->

    <fill presence="visible | hidden">
      <color value="r,g,b"/>
    </fill>
</border>
            

A script that changes a border fill color (border.fill.color.value = "255,0,0";) is simply traversing the hierarchy of elements in the grammar.

2) Edge access is tricky

The syntax to get at the four edges uses the "getElement()" method, whose second parameter is the occurence number of the edge.  Note that the sample always sets all 4 edges.  This is because edge definitions are inherited.  e.g. if only one edge is specified, then modifying the single edge property impacts all four edges.  The problem is that you don’t always know how many edges have been specified, so it’s safest to set all four explicitly.

3) Show/hide with the presence property. 

You’re likely accustomed to using the presence property of fields. That same attribute applies on the components of a border:

border.presence = "hidden";      // hides entire border — all edges and fill
border.fill.presence = "hidden"; // makes the border fill transparentborder.getElement("edge", 0).presence = "hidden"; // hides the first edge

4) There’s more.

There is more to the border definitions than I’ve shown you here. Using script you an control the four corners, fill patterns, edge thickness, border margins and more.

5) There are other borders

Subforms, rectangles, draw elements and exclusion groups all have border definitions.