Archive for January, 2011

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