Posts in Category "Samples"

Merging text to a caption

In your form design you have two choices when it comes to labelling your fields.  You can either place the descriptive label text in a field caption or you can create a separate text object with the label text. 

There are pros and cons to each, but today I am going to try and convince you to use captions — and give you a tool to make it easier to create captions.

Whether your descriptive text is stored as a separate object or whether it is specified as a caption, in most cases we can achieve the same visual effect with either. Admittedly there are some layout conditions where a caption cannot be used.  e.g. where the label extends both above and to the left of the field.  But this is the exception.

The advantages of using separate objects:

  1. Absolute control over label positioning
  2. Can be less effort to author in this mode

The advantages of using captions:

  1. Far better accessibility experience
  2. Results in smaller form templates with better performance
  3. Lower form maintenance

Accessibility

Field captions can be used to generate a good accessible experience.  The caption text can be used as the text that the screen reader uses to describe the field. Obviously when it is stored as a separate object we do not have this correlation. When the label is a separate object, we have extra “noise” as the screen reader traverses the read order of the document. The read order will encounter the label and the field as objects that will be read separately.  If they’re not consecutive in the read order, there is potential for even more confusion.

Form Size

As I’ve mentioned before, form size matters. If your form is delivered with rights enabled and/or with certification, the time to open the form is directly impacted by the size of the form template.  This is because both RE and certification use encryption and we need to validate that the form has not been tampered with at open time.  To make a long story short, the validation involves loading the form template more than once.  If there are fewer objects and less syntax, the form will load faster and open time will be reduced.

Lower form maintenance

When a caption is combined with a field, then all operations treat the field and caption atomically: 

  • A script to make a field invisible will also hide the caption. 
  • A page break will never separate a field from its caption, but could separate a field from label text. 
  • Moving or deleting a field in Designer automatically does the same for the caption. 

The Form Conversion Problem

My suspicion is that very few form authors consciously choose to separate the label from the field.  In most cases this is a byproduct of a conversion process from some other format to XDP.  The form author’s starting point is a form design where all fields are separated from their caption.  The question becomes: is it worth the time and effort for an author to combine these objects?  Is the net gain worth the investment?  The answer is dependent on your own scenario — and dependent on the amount of effort to fix the form.

Designer has a built-in function that will combine text and field into a combined field with caption.  You select the text and the field and choose “Layout/Merge as Caption”.  The problem you’ll find is that while designer does combine the objects, it does not preserve the layout, positioning, font and paragraph properties of the label when converted to a caption.  I think you will see this function improve in future versions of Designer.  But in the mean time, this is also a function that can be customized using a designer macro.  Here is a designer macro you can install that will combine labels with fields when the label is positioned above the field. 

You should find that the macro works fairly hard to give you a finished result with the same visual appearance as the original.  Here is a sample PDF with the separated label/text on the left and the combined result on the right. 

As I was writing the macro, I realized there are cases where the end-user might want a different behaviour than I’ve provided.  For example, when the caption is wider than the field, should we expand the field to match the width of the caption? That was the choice I made. If you prefer a different result, you can tailor the macro to your own needs.

Enjoy.  Hopefully there’s enough of a starting point here that someone could expand the macro to also handle captions placement to the left/right/bottom of fields.

Form Stitching Design Pattern

If the term "Stitching" is new to you, it refers to server applications that take the content of multiple XDP files and combine them into a whole.  While this sounds like fragment resolution, there is a twist in that the content to be combined isn’t known until the document is requested.  The most common scenario is where a company has a series of individual forms that can be delivered stand-alone or may be included as sub-forms in a larger package.  Package in this context is not a portfolio or PDF package, but rather, a sequence of templates combined serially into one big form.

Historically there have been at least two ways to implement stitching:

  • Stitching implemented by professional services
  • Solution Accelerator (ODA)

Then in LiveCycle ES2 we delivered fragment stitching in LiveCycle Assembler.

Going forward, the Assembler version is the one customers should be targeting, as it is fully supported and will be the solution that
receives enhancements.

Building a system where we can stitch templates together requires some careful planning.  Random forms cannot be arbitrarily stitched together. Any attempt to
do so will inevitably run into conflicts regarding scripting, master
pages, schemas, submit logic, validations etc. In order for individual
forms to be combined into a stitched package, the individual forms must
follow a design pattern that allows them to participate in the final
grouping.

Terminology

In order to explain the stitching process and the necessary design patterns, it helps to start by using some terminology consistently.

  • individual form – a template that can be used as a standalone form
    or can be combined with other individual forms into a stitched package
  • stitched package – a collection of individual forms combined into a
    single, large XDP/PDF. Not to be confused with portfolios or PDF
    attachments
  • host template – the XDP that forms the root of the stitched
    package. Individual forms are inserted into the host template to form
    the stitched package
  • root subform – the subform that appears under the template element in an XDP definition.
  • stitched subform – the subform that will be extracted from individual forms to be included in a stitched package

Stitching Requirements

Some of the common requirements that we see in customer implementations of stitching:

  1. Individual forms must retain their own master page associations
  2. Shared script objects must not be duplicated when individual forms are combined
  3. Individual forms must be able to retain their own form-specific logic
  4. Submit logic for individual forms must collapse into a global
    submit capability in the resulting stitched package. i.e. submit
    buttons from individual forms must be hidden when part of the larger
    package.
  5. We must be able to include multiple copies of any individual form into a stitched result
  6. The number of individual forms included in the final package needs to be arbitrary – no pre-defined limits

The Form Design Pattern

Host Template

The host template will form the root of the stitched package. It
will define all properties that are global to the form. e.g. default
locale, target Reader version, Server PDF render format, form state
setting, meta data etc.

Individual Form

Each individual form must have compatible settings to the host
template, especially: same target Reader version, same
originalXFAVersion.

Common Schema

All individual forms to be combined into a stitched package must be
based on the same XML schema (or must all be designed without a schema). The host template will include the
connectionSet for this shared schema. If an organization has multiple
schemas, they must either combine them into a single uber-schema or they
must maintain one host template for each schema that is in use.

Shared WSDL definitions

Since we do not stitch together connectionSets, the host
template must also include the aggregate set of WSDL definitions used by
individual forms.  It is necessary that individual forms use consistent names when
referencing XML schemas and WSDL definitions. This is especially true
for WSDL definitions where the names are used in script and in template
syntax.

Individual Form Hierarchy

Individual forms must be designed in such a way that so that all
content to be included in the package resides under a single subform
included under the root subform. This second-level subform is the
stiched subform – the content that will be extracted from the individual
form and included in the stitched package.
For reference, here is a sample hierarchy of an individual form:
Designer hierarchy view with a root of "purchaseOrderGroup" with children: script objects and PO_Portrait subform.  PO_Portrait subform has children that include master pages.
In this example, the subform named “PO_Portrait” is the stitched subform that will be included in the final stitched package.

Shared logic

Any logic that is common to multiple individual forms should be
included in the individual forms as a child of the root subform (as a
sibling to the stitched subform). The host template must include a copy
of this shared script. In the example above, the “countryScript”
script object would not be included with the content of the individual
form, but since it is present in the host document, the logic inside
PO_Portrait will continue to work in the package context.

Form-specific logic

If there is logic that is unique to an individual form, it must be
included as a descendant of the stitched subform. This way, it will be
extracted with the stitched subform into the stitched package.

Stitched subforms must define master page definitions as descendants

As long as the master pages used by a stitched subform are
descendants of that subform, they will be included in the final stitched
package. Details at nested master pages.

Note as well that for multiple master page collections to work in the
stitched package, all individual forms should use a consistent printing
option. One of: Print on Front Side Only/Print on Both Sides/Page
Occurrence.

The stitched subform must explicitly target its nested master page.
In the example, the pagination option for PO_Portrait is to place: ‘On
Page “PO_PortraitPage” ‘.

The stitched subform must be a fragment

picture of the Designer dialog used to set the properties of a subform so that it is an inline fragment.
From the context menu in Designer, Fragments/Create Fragment…
Choose “Create New Fragment in Current Document”. The Name you choose
will be the name that is referenced by Assembler. It can be either a
name that is unique to the individual form, or you can use the same name
for all individual forms. re-using the same name will make the
Assembler DDX syntax simpler.

The host document needs an insertion point

Create a nameless subform directly below the root subform. Use this subform to define a named insertion point:
picture of the dialog box used in Designer to define an insertion point at a subform.
Note that the insertion point subform must not specify any properties –
name, width, height etc, or else these will override the properties of
the fragment that will be inserted. In the sample, the XML for the
insertion point subform is very simple:

<subform>
   <extras>
      <text name="insertionPoint">POSchemaContent</text>
   </extras>
</subform>

Note that because of the way Assembler processes insertion points, a
single insertion point can be used to insert an arbitrary number of
individual forms.

Control the presence of submit buttons

In the form properties of the host template, create a variable to
indicate the document is a stitched package. The example uses
“IsPackageDoc” and gives it a value: “1″. In the individual forms, put
logic on the submit button initialize event that looks like:

if (IsPackageDoc && IsPackageDoc.value === "1") {
	this.presence = "hidden";
}

This logic will ensure that the button is visible when the form is an individual form, but hidden when part of a package.

Make sure your schema can repeat

If you want to include more than one copy of any individual form, you
need to make sure that the schema element the stitched subform binds to
is allowed to repeat. This might require wrapping the root element of
your schema in an aggregating subform.

A starter template

For customers who have the luxury of designing these solutions from
scratch, it is wise to create a starter skeleton individual form
template that includes the shared logic, second level subform, nested
master pages, corporate schema, WSDL definition(s) and submit button.
This reduces the need for form authors to remember all the necessary
requirements that are part of the design pattern.

The Assembler Step

In assembler we use the XDPContent command to insert individual forms
into the host template. e.g. our sample below uses this DDX:

<DDX xmlns="http://ns.adobe.com/DDX/1.0/">
 <PDF result="poPackage">
  <XDP>
   <XDP source="hostXDP" retainInsertionPoints="All" baseDocument="true"/>
   <XDPContent insertionPoint="POSchemaContent" source="sourceXDP1" fragment="PO_Portrait"/>
   <XDPContent insertionPoint="POSchemaContent" source="sourceXDP2" fragment="PO_Landscape"/>
   <XDPContent insertionPoint="POSchemaContent" source="sourceXDP3" fragment="PO_Comments"/>
  </XDP>
 </PDF>
 <?ddx-source-hint name="hostXDP"?>
 <?ddx-source-hint name="sourceXDP1"?>
 <?ddx-source-hint name="sourceXDP2"?>
 <?ddx-source-hint name="sourceXDP3"?>
</DDX>

A sample

Here is an example an that follows the design pattern described in this note. An explanation of the included files:

  • Purchase Order.xdp – an individual form
  • ExtraComments.xdp – An individual form
  • Purchase Order Landscape – An individual form
  • Purchase Order.tif – A referenced image
  • Purchase Order Dim.tif – referenced image
  • Purchase Order Group.xsd – the uber schema
  • Purchase Order Host.xdp – the host template
  • stitch.ddx.txt – the Assembler DDX definition used to stitch
  • result.pdf – what it looks like when assembled.

The Deep End

If you’re interested in understanding the details on how insertion points work, here’s some copy/paste from the specification:

The Algorithm

Requirements:

  • The stitching capability will allow us to “push” fragments into
    specific locations (insertion points) within a host document based on
    logic that exists outside the document.
  • In cases where the number of inserted fragments is not known in
    advance, we need to be able to push an arbitrary number of fragments
    into a single stitch point.
  • The host template may have placeholder content that needs to be removed

In preparation for stitching, these grammar rules will be defined:

  1. Add “insertion points” in the host template – places where external content may be inserted
  2. Add “placeholder content” in the host template so that it is visible in Designer, but excluded from the final stitched result
  3. The LiveCycle assembler component grammar (DDX) will have a command (XDPContent) for inserting a fragment to a insertion point

A insertion point will be a mnemonic defined using <extras> named: “insertionPoint“. E.g.:

<extras>
   <text name="insertionPoint">TermsAndConditions"<text/>
</extras>

Placeholder content will be marked with a mnemonic named: “insertionPointPlaceholder”

When Assembler executes a stitch, it will:

  1. Find all insertion points that match the target attribute of the XDPContent command
  2. For each insertion point, clone the XFA element containing insertion point
  3. Replace the insertion point syntax with the syntax of a fragment
    reference. If the element already has a fragment reference, over-write
    the existing reference.
  4. Remove any content marked as being a placeholder

When Assembler has completed it will execute a post-process:

  1. Remove any elements that still have a remnant insertionPoint extras element.
  2. Resolve all fragments

Example

Host template:

<subform name="TAC">
  <extras>
     <text name="insertionPoint">TermsAndConditions<text/>
  </extras>
  <setProperty target="font.typeface" ref="$record.Style.Font"/>

  <draw>
    <value><text>This is temporary placeholder content</text></value>
    <extras>
      <text name="insertionPointPlaceholder"/>
    </extras>
  </draw>
</subform>
                  

The assembler DDX definition would include an XDPContent command to inject a fragment reference at this subform:

<PDF result="final.pdf">
   <XDP source="docin.xdp">
      <XDPContent insertionPoint="TermsAndConditions" >
         <XDP source="tac.xdp" fragment="Alabama"/>
      </XDPContent>
   </XDP>
</PDF>

The result after processing the XDPContent command:

<subform name="TAC" usehref="TAC.xdp#som($template.Alabama)">
  <setProperty target="font.typeface" ref="$record.Style.Font"/>
</subform>

<subform name="TAC">
  <extras>
     <text name="insertionPoint">TermsAndConditions<text/>
  </extras>
  <setProperty target="font.typeface" ref="$record.Style.Font"/>
  <draw>
    <value><text>This is temporary placeholder content</text></value>
    <extras>
      <text name="insertionPointPlaceholder"/>
    </extras>
  </draw>
</subform>
      

After all XDPContent commands have executed, we will:

  1. remove any unresolved elements with insertion points
  2. expand fragment references

 

Nested Master Pages

Fragments and Master Pages

If you are designing your forms using fragments, you will likely have done some work to get consistent master page definitions in your final document. The strategy most form authors follow is to define a set of re-usable master pages and include them in their host template.  All included fragment subfoms will then make use of the master page definitions found in the host template.

But what many form authors do not realize is that it is possible for the fragment to bring its own set of master pages into the host document.  The strategy is to define the collection of master pages as a child of the fragment being included.  I have created a sample XDP to illustrate this.  Note in the hierarchy that there are three sets of master pages: one for the root subform, and one each for the group1 and group2 subforms:

In this example, the group1 subform will look up its master page definitions in its nested master page collection. Same for group2.  And if group1 or group2 were to be included as a fragment, their master page definitions would be automatically included, since they’re part of the subform.  If group1 and group2 used the master page definitions defined under form1 and were included as a fragment, they would then use the master page definitions found in their host document.

Referencing Master Pages

Assume we have a master page that is defined with this syntax:

<pageArea name=”Page1″ id=”Page1_ID”>

In Designer, when the pagination option is: ‘ Place: On Page: “Page1″ ‘, the syntax generated by Designer will reference this page by name:


<subform name=”usePage1″>
    <breakBefore target=”Page1″ targetType=”pageArea”/>   

However, we can also reference by id:


<subform name=”usePage1″>
    <breakBefore target=”#Page1_ID” targetType=”pageArea”/>   

Referencing pages by name is the best strategy when you are sharing a global set of master pages. As long as you are consistent in your naming scheme, the “Page1″ referenced by your fragment will be replaced by “Page1″ found in the host template.

However, if your master pages are embedded in your fragments, then you don’t want to share page definitions. In this case there is less ambiguity if we use ID references instead of name references.

In fact, there is a bug in the current shipping products where if the top-level “Page1″ is in use when group1 is added to the layout, then “Page1″ will continue to be used for “group1″ — but it is the wrong “Page1″.  Here is a sample XDP to illustrate the bug.  The workaround is to either use unique names for your nested master pages, or switch to use id references.

Using unique names is easier. Using id references means either hand-editing the XML source or writing a designer macro to change the syntax.

Flash in XFA — sample column chart

After posting about the new features arriving in Acrobat X / XFA 3.3 I received several requests for samples. So today we’ll have a look at a sample that uses a generic column chart to display form data.  If you have used flash inside Acrobat 9, the concepts here should be very familiar.  And before we go any further, I should remind you that you don’t (yet) have the tools to design this form yourselves.  You need to wait for the next version of LiveCycle Designer.  But in the meantime, you can look at the script and internals of the sample form using the current version of Designer.

First, here is the sample PDF, here is the flex project for the chart swf.

Some comments about how to use the form:

  1. In the "Type" field, select the column chart variation you want: clustered, overlaid or stacked.
  2. Edit the hex values in the color fields to choose the colors for the 5 data series that can be created
  3. Click "Start Chart" to create the chart with its first data series
  4. Click "Add Row" to add another row of dataa — and to include it in the chart. (the new row values are randomized)
  5. Edit some of the sales values in the table.  Notice how the chart reacts when you exit the field.

Now the nitty gritty:

A Generic Column Chart

If you use Flash Builder to create a column chart, it’s pretty easy to define it with declarative markup when the data source is well understood. In my case I wanted a skeletal chart definition and allow it to be modified by actionscript methods — which would in turn be called from the form JavaScript.

To achieve this, the sample column chart exposes three methods:

  • reset() — get rid of existing data and restart the chart
  • addSeries() — add a new data series to the chart
  • updateValue() — notify the chart that one of the values in its data series has changed

The actionscript in the sample chart calls ExternalInterface.addCallback() to register each of these methods.

These three methods provide all the parameters needed for the chart (title, data, colors, labels). There are plenty of other chart properties that could be exposed by an energetic developer who is a little more industrious than me.

A couple other pointers for developing flash for embedding in PDF:

  • Round-tripping the flash to test in Designer is cumbersome.  I’m continually exporting release builds from Flash Builder.  For expedient testing I added buttons to the chart sample to call the relevant methods so I can test from within flash builder.  Then when I’m happy with the result I comment out the buttons.
  • You need the right settings so that your embedded flash will respond to document zoom events.  The magic bit is this code in the application element:
    <mx:Application applicationComplete="stage.scaleMode=StageScaleMode.EXACT_FIT;" …>

Embedding the Flash

To embed the flash, I created a host field with the same aspect ratio as the flash application — in this case my flex specified: <mx:application width="675" height="450"/> so I made the dimensions of my host field: w=6.75in h=4.5in.

I specified a poster image to use when the flash is not active.  To get a suitable image, I took a screen shot of the empty chart, cropped it and saved as a jpg.

In order to pass in chart data and chart parameters, the form JavaScript calls methods on the flash object using the ‘invoke’ method on exObject:

chart.ui.exObject.invoke("reset", …);

Restoring the state of the flash object

When re-opening the form we need to restore the state of the embedded flash object.  If the flash were a simple widget with a data value, we’d likely use the multimedea calls to save/restore data:

ExternalInterface.call( “multimedia_saveSettingsString”, “…” );

state = ExternalInterface.call( “multimedia_loadSettingsString” );

If we called these methods, the widget data would be saved/restored from the form data associated with the host field.  However in the case of our chart, we want to restore the chart from the data in the table.

I failed in my first attempt to restore the flash data.  I called the chart reset() method from the docReady event.  The problem was that the flash object was not yet activated and the call was ignored.  The solution was to have the embedded chart call a script object method when it was activated. From actionscript this looked like:

ExternalInterface.call("chartHelper.initialize");

This calls the initialize() method on the chartHelper script object.  The initialize method then uses the reset() and addSeries() methods to re-populate the chart.

 

JavaScript Lint check in Designer

Some time ago I distributed a sample form that used Douglas Crockford’s excellent JSLint script to check form JavaScript for common coding errors.  You can read about it here. The logical next step is to use macros to integrate this capability into Designer (macros introduced here).

Here is everything you need to install the macro.

One improvement I’ve made over the previous version is to automatically populate the list of global objects with all the subform and field names in the form.  Now it will flag far fewer global objects — and if it does flag a global, you really want to pay attention, since it is very likely an un-declared variable.

I really strongly recommend this tool to you. When you run this against your script you will very likely be surprised at some of the coding errors you’ve made.  I know I was.

XDP Size Matters

I have recently been involved in a couple of contexts where customers have worked hard to reduce the size of their XDP and resulting PDF files.

We know that size matters.  The smaller the better.  We want small PDF files for downloading.  We know that encryption-based technologies such as certificates and rights enabling perform faster when the form definition is smaller.  Today’s discussion is a couple of tips for reducing the size of your XDP and PDF.

When concerned about size, the first two places to look are fonts and images.  I’ve covered images a couple times.  Check out: Linked vs Embedded Images and also the sample in Parameterize your SOAP address.

Fonts are a simple discussion.  Your PDFs are much smaller when the fonts are not embedded. If you need to embed fonts, use as few as possible.  To see which fonts are referenced by you form, you can check out the form summary tool here.

But the main topic for today is syntax cleanup.  The elements and attributes in the XFA grammar all have reasonable default values.  We save lots of space by not writing out the syntax when the value corresponds to the default.  For example, the default value for an ordinate is "0". If your field is at (0,0), we will not write out the x and y attributes. 

However, there are a couple of places where the syntax cleanup could use some help. Specifically: borders (edges and corners) and margins. There are scenarios where these elements can be safely deleted from your form.

Today’s sample is a Designer macro (with the sample form) for eliminating extra syntax. If you don’t want to understand the gory details, you can just download and install the Designer macro.  (If you haven’t already, please read this regarding Designer macros).  After running the macro, look in Designer’s log tab for a summary of the cleanup.  Then double check the rendering of your form and make sure it hasn’t changed.

If you want to understand a bit more, I’ll explain what is going on.  There are three instances of syntax bloat that the macro will clean up:

Extra corners

In the attached form there are two fields: border1 and border2. 

The border definition for border1 looks like:

<border>
  <edge thickness="0.882mm"/>
  <corner thickness="0.882mm"/>
  <edge thickness="0.3528mm"/>
  <edge thickness="0.3528mm"/>
  <edge thickness="0.3528mm"/>
  <corner thickness="0.353mm"/>
  <corner thickness="0.353mm"/>
  <corner thickness="0.353mm"/>
</border>

When you modify edge properties, designer will inject <corner> definitions. However, the only time a corner definition has any impact on the rendering is when the radius attribute is non-zero.  In this example, the <corner> elements may all be safely removed without changing the rendering of the form.

Hidden Borders

The border definition for border2 looks like:

<border>
  <edge thickness="0.882mm" presence="hidden"/>
  <corner thickness="0.882mm" presence="hidden"/>
  <edge thickness="0.3528mm" presence="hidden"/>
  <edge thickness="0.3528mm" presence="hidden"/>
  <edge thickness="0.3528mm" presence="hidden"/>
  <corner thickness="0.353mm" presence="hidden"/>
  <corner thickness="0.353mm" presence="hidden"/>
  <corner thickness="0.353mm" presence="hidden"/>
</border>

I’m not sure how I ended up with this configuration, but as you can see, all the edges and corners are hidden.This would be be represented more efficiently as <border presence="hidden"/> or better yet, no border element at all. But before cleaning up this syntax, bear in mind that there can be a useful purpose here.  If you have script that toggles the presence of border edges, it is useful to have the edge properties (e.g. thickness, color) defined in the markup.  If you remove the markup, you will need to set those properties via script.

Zero Margins

If you have edited object margins, you could end up in a situation where your margin element looks like:

<margin topInset="0mm" bottomInset="0mm" leftInset="0mm" rightInset="0mm"/>

Since the default margin insets are all zero, this element can be safely removed.

How frequently this syntax bloat occurs in your forms depends on your editing patterns. In my testing against several customer forms, I saw size reductions of 10 – 15% by removing these elements.

 

Editable Floating Fields V3

I had a user report a bug in the floating field sample  — the sample described in these blog entries: Version1   Version2

I’ve updated the code again.  The specific problems fixed were:

  1. The editing field can now be unbound (binding="none"). This is important in cases where you are using an xml schema and there isn’t data that you can bind to the editor field.
  2. There was a problem with preserving trailing spaces in edited values.  The easiest fix was to strip trailing spaces from the edited values.

Here is the updated form.  And the updated fragment.

xfa.context

Yesterday I discovered that we have an undocumented script object property that you might find useful.

The root xfa object has a property called “context”.  This property is a reference to the object that is hosting a calculation/validation or event.  i.e. it returns what is normally referenced by “this” in those scripts.

I recently came across a scenario where knowing the source context is very useful.  In my case, I wanted a function in a script object to know the calling context.  It worked well to use xfa.context.

I have attached a sample form where I have a set of expense report items.  I wanted to write a sum() function in JavaScript (rather than using formcalc).  Of course, I wanted the sum function to be re-usable so I put it in a script object on the root subform.  You call the function with a SOM expression that returns the fields to be summed:

utils.sum("expense[*].amount");

The challenge here is that the SOM expression is relative to the field calculation that uses it.  That means this code will not work:

function sum(sExpression) {
  // "this" is at the form root
  // and won’t find the result
  this.resolveNodes(sExpression);
  ...
}

One workaround is to provide a fully explicit SOM expression:

utils.sum("ExpenseReport.expenses.expense[*].amount");

But I don’t like this approach.  The calculation is now not encapsulated.  If the form author modifies the hierarchy in any way, the script will fail.  It also means that it’s very difficult to place this logic in a fragment where you don’t know the absolute context.

A second workaround is to resolve the nodes before calling the method:

utils.sum(this.resolveNodes("expense[*].amount"));

I don’t really like this either – it reduces the readability of the code. 
Instead, I used this:

function sum(sExpression) {
  // resolve sExpression in the context
  // of the calling script
  xfa.context.resolveNodes(sExpression);
  ...
}

Note that xfa.context is a read/write property, but at this point I have not found a useful reason to assign a value to xfa.context.

Shared Data in Packages Part 2

Last week I started to outline a design pattern for sharing data in a package.  Again, the general problem we’re trying to solve is to allow multiple forms in a PDF package exchange data — where fields common to multiple forms is propagated.  Part 1 of the problem is establishing a communication mechanism between documents.  Once the documents have been disclosed to each other, any document in the package can modify any other document.  Today’s Part 2 entry describes how to do the sharing.

Data mapping strategies

Implementing data sharing means solving a mapping problem: How do we know which fields in one PDF correlate to which fields in other PDFs? There are several techniques we could choose, including:

  1. Use the same name for common fields
  2. Generate a manifest that explicitly correlates fields
  3. Base all forms in the package on a common data schema

For my solution, I’ve chosen the 3rd option: common schema.  The underlying assumptions are:

  • Field values get propagated by assigning values in the data dom — a data value from one form will have the same data node address in each of the other forms
  • The data dom in the package document will hold the aggregate data of all forms in the package — this is the ‘master copy’ of the data.  Note that the package document does not have to have fields correlating to all the data elements.
  • Attached forms will have their data synchronized from the master copy of the data when they are launched in Reader

Data Sharing Algorithm

With these assumptions in place, the actual algorithm is fairly simple:

  1. When any attachment opens, it registers with the package document
    (the topic of Part 1)
  2. At registration, all data values of the attachment are synchronized from the master copy of the data in the package
  3. While the attachments are open, detect when field values change.  The technique used to detect field value changes is to use propagating enter/exit events. We save the field value at field enter, and compare it to the result at field exit. 
  4. When a field value changes, send a synchronize message to the package document.
  5. When the package document gets a synchronizing message, it updates the master copy of the data and then propagates the change to all other open attached PDFs.

One benefit of this approach is that the actual logic used to synchronize the data resides in the package document. This means you can customize your data sharing algorithm by modifying only the script in the package.

Design Experience

The really good news is that you can put this all together in a very simple design experience.  With today’s sample, there are two fragment subforms: packageSync and embeddedSync. You can probably figure out where they each go.  The fragment subforms have all the logic needed to register and synchronize the documents.  They contain propagating enter/exit events so that all fields in the form are automatically synchronized.  So the Design experience is as simple as:

  1. Drag the packageSync fragment on to a package subform
  2. Drag the embeddedSync fragment on each each attachment form
  3. Attach the embedded forms to the package form

Global Submit

Since the package document holds the aggregate of all the data, a global submit operation can be achieved by simply submitting the data from the package document.

Limits

There are some limits to the synchronization that you should be aware of:

  • No handling for complex field value: rich text, images, multi-select choice lists
  • Does not synchronize subform occurrences
  • Does not synchronize calculated values
  • Because we are relying on propagating events, the solution works only in Reader 9.1 or later

Each of these problems are solvable. I was just too lazy.

The Sample

The sample form and all fragments are in this zip file. Try opening PackageSample.pdf and one or both attachments.  Fill in fields and observe that they get propagated.  Note that any field that has focus will not get updated until you exit the field.

Accessibility Bug Fix

Recently one of our customers discovered an accessibility bug that caused read order and tab order to diverge. When we investigated it, we discovered that the bug existed in the reader runtime, but that we could work-around the bug by changing the syntax that Designer generates. Fixing the problem in Designer is much preferable, since that doesn’t require upgrading Reader. Of course, waiting for a fix in Designer is not easy either. Fortunately, the macro capability offers us an alternative way to disseminate a fix.  First the usual caveat: macros are an experimental, unsupported product feature.  

The Bug

The bug scenario is when a custom tab order exits a subform; the read order follows the next-in-geographic order, while the tab order follows the explicit custom order.  Described differently, imagine a form with fields named "One", "Two" and "Three" and a subform named "S’.  The geographic order is: S.One, Three, Two, but the custom tab order is S.One, Two, Three. In the Reader runtime, the tab order correctly follows the custom order, but the Reader order gets confused on exiting the subform.

If you’re only interested in correct tab order you will not notice the problem.  However if you are using a screen reader and following read order, you will follow an incorrect order.  (If you examine the resulting form with tools like inspect32 or AccExplorer32 you’ll be able to see the result without actually running a screen reader)

I have included a sample form that illustrates the problem, and a version of the form with the fix applied.

The Fix

Down below, I’ll discuss the technical details of the fix.  But first the macro (rename to FixReadOrder.js) When you install and run the macro, it will scan the entire form searching for all the places where a custom tab order exits a subform. Each of these places will be modified. 

You will need to re-run the macro every time you make edits to the form that modify tab order.

The macro includes some logic so that it will stop functioning in the next version of Designer – given that we expect the bug to be fixed.

The Deep End

Custom tab order is controlled by the <traversal> and <traverse> elements.  The (stripped down) syntax of the original form looks like this:

 1 <subform>
 2  <subform name="S">
 3    <field name="One">
 4      <traversal>
 5        <traverse ref="Two[0]"/>
 6      </traversal>
 7    </field>
 8    <traversal>
 9      <traverse operation="first" ref="One[0]"/>
10    </traversal>
11  </subform>
12  <field name="Three"/>
13  <field name="Two">
14    <traversal>
15      <traverse ref="Three[0]"/>
16    </traversal>
17  </field>
18  <traversal>
19    <traverse operation="first" ref="Subform1[0]"/>
20  </traversal>
21 </subform>

There are two variations of the traverse element used here:

  1. operation="first": specifies the first child of a container (subform)
  2. operation attribute is unspecified (the default) — which means operation="next". Specifies the next element.

The runtime problem occurs when the traverse from field "one" at line 5 does not correctly navigate to field "Two".

Here is the fixed version:

 1 <subform>
 2   <subform name="S">
 3    <field name="One">
 4       <traversal>
 5         <traverse ref="Two[0]"/>
 6       </traversal>
 7     </field>
 8     <traversal>
 9       <traverse operation="first" ref="One[0]"/>
10       <traverse ref="Two[0]"/>
11     </traversal>
12   </subform>
13   <field name="Three"/>
14   <field name="Two">
15     <traversal>
16       <traverse ref="Three[0]"/>
17     </traversal>
18   </field>
19   <traversal>
20     <traverse operation="first" ref="Subform1[0]"/>
21   </traversal>
22 </subform>

The macro added a traverse element at line 10. Now we’ve explicitly told the subform which child to navigate to first and also where to navigate to when the children are finished.  In fact, we end up with two traverse elements pointing to field "Two".  Not surprisingly, the runtime correctly moves to "Two" in both tab order and read order.