Posts in Category "XFA"

Fonts are Big

It strikes me that we don’t complain about big email attachments as much as we used to. We don’t mind as much when aunt Grace sends us a batch of 2MB jpg files straight from her camera.  The photo quality is the same as always (sigh), but at least it doesn’t bring our internet connection and email client to it’s knees anymore.  Heck, even Dad and Mom now have a broadband connection.

But just because we *can* send big attachments doesn’t mean we should.  Let’s have some professional pride in making sure that our files are as lean as possible.  Today’s topic is about managing your fonts so that your PDF forms are tidy and small. 

I’m sure you already knew this, but I’ll repeat the basics.  PDF files can embed font definitions.  The advantage of embedding a font is that it guarantees your PDF will look exactly the same no matter where it’s opened — you don’t have to worry whether the user opening your form has copies of the fonts you used or not. If you don’t embed fonts, and the user doesn’t have
"Charlemagne Std" on their system, Reader will display the PDF with a substitute font.  It won’t look the same.

Of course, the disadvantage of embedding fonts is that they’re big.  They bump up the size of your PDF in a big hurry. Often around 200K per font.

Here are some notes to remember about font usage:

Note #1: If you’re using common fonts, and especially if you can tolerate some variance in your page display, then don’t embed fonts.  Designer embeds all fonts by default:

Designer Form Properties dialog showing that fonts are embedded by default.

Note #2: If you’re embedding fonts, use as few fonts as possible in your form design.

There was a reason I just finished updated the form reporter.  It will tell you what fonts you have used and how many instances of each. I recently reviewed a form that showed this in the report:

All fonts were embeded. The form had one object using Times New Roman.  That one instance bloated the PDF by over 200K.  After I consolidated all font instances to Myriad Pro, the form was a total of 600K smaller.

Note #3: Not all fonts are equal in size

I haven’t done an extensive accounting, but it appears that Myriad Pro is smaller than most. A small form with Myriad Pro embedded is 77K.  While the not-embedded version is 13K.  Why does embedding Myriad Pro cost only 64K while Times New Roman was 200K?  I’m told that the embedded Myriad Pro excludes character sets that are not in use e.g. Cyrillic.

Note #4: Reader installs fonts

On my system, Reader X installed Minion Pro, Myriad Pro and Courier Std. There are asian font packs for Reader available for download. I’d like to think that for most users having Adobe Reader installed fonts would mean they don’t need to be embedded in your PDFs.

Note #5: Fonts can be subset

If you use a font in an interactive form field, you need to have the entire font embeded.  But if the font is used only in boilerplate text, then you only need to embed the definitions of the characters that are found in the PDF.  In this case we can reduce the size by embedding only a subset of the font.  Options to embed subset fonts are not exposed in Designer.  This is server-side processing.  And as long as you’re mucking in that area, you can also explicitly choose on a font-by-font basis which are embedded and which are never embedded.

Edit Fonts in Designer

Here’s the problem: The form report shows you have one instance of Times New Roman in your form design.  Now find that one instance among the 300 fields on your form and change it. If it were me, I’d probably switch over to source view.  But that’s not very user friendly.

This becomes yet another case where Designer macros can be very helpful. Here is a zip file that contains a Designer macro to perform global font substitutions. When you run it, you’ll get a dialog like this:

The macro will replace the font references it finds in <font> elements, as well as the font references it finds inside rich text values.  Just be sure to type the names of the fonts correctly.  If you mis-spell the replacement font, the macro will happily give you a form full of "Myirod Pro" references. When the macro completes, look in Designer’s log display for a summary of the changes.

 

Updated Form Report Tool

Has it been two years already?  Seems like just the other day I published a tool for summarizing form content.

I’m sure it’s been sitting on your desktop for regular use.  In my day job I’m, often asked to look at customer forms.  Or I sometimes need to go back and review one of my own form designs.  The first thing I always want to know is the "big picture" of what’s inside this form.  That’s what the summary tool is all about. You’d be surprised what you can learn from a report. I’ll hopefully point out a thing or two in follow-up blog posts.

Now that we have a macro capability in Designer (yes, yes, still in beta) it’s time to migrate this functionality from being an external tool to a Designer macro.

Here are the files you need: FormReport.pdf and FormReport.txt (rename to FormReport.js).  If you need a reminder about how to install them with Designer, then read this blog entry.

 

Debug merge and layout: Updated

Almost two years ago I published a blog post with a tool that I developed to help debug data merge and layout problems: http://blogs.adobe.com/formfeed/2009/05/debug_merge_and_layout.html

I continue to use this sample pdf on a regular basis.  It has a reserved location among my desktop icons for quick access.  But occasionally I’d run into PDF forms that cause it to hang.  So finally I took the time to find and fix the problem.  While I was at it, I took care of a couple other minor issues.  The updated form is available for download here.  I hope you find it useful.

Sample: Convert to Strict Scoping

In the past couple of months I’ve had the opportunity to help a couple of customers through migrating a form design so that it was not dependent on non-strict scoping.

First, to review what this means, you could read this blog post describing the problem.

There’s no single technique to convert a form script so that it conforms to strict scoping. But there are a couple of common patterns.  I’ll disclose these by way of an example.

I’ve designed a sample form that makes use of “expando properties” to add multi-step undo/redo functionality to individual form fields.  In this case, the form adds an expando script object to a text field.  For review purposes, the reason this is problematic is that allowing users to modify the JavaScript representations of XFA objects means that the script engine must preserve these JavaScript objects. On large forms, this means we cannot release JavaScript objects to garbage collection and memory usage climbs.

var This = this;

This.ctl = new function() {
    this.history = [This.rawValue],
    this.current = 0,
    this.startValue = null;

    this.undo = function() {
        if (this.current > 0) {
            This.rawValue = this.history[this.current-1];
            this.current--;
        }
    }
    this.redo = function() {
        if (this.current < (this.history.length - 1)) {
            this.current++;
            This.rawValue = this.history[this.current];
        }
    }
    this.enter = function() {
        this.startValue = This.rawValue;
    }
    this.exit = function() {
        if (this.startValue != This.rawValue) {
            this.current++;
            this.history.length = this.current;
            this.history[this.current] = This.rawValue;
        }
    }
};

With this code in place, the enter event can call:

this.ctl.enter();

Similarly, assuming the text field is named “Test”, the click event on an undo button can call:

Test.ctl.undo();

This is all pretty powerful, elegant stuff, and it’s tempting to adopt this form design pattern. But as I said — the side effects are a problem.

There are two possible ways to revise the form so that it works in strict scoping:

  1. Move the expando properties to script objects
  2. Re-code the logic so that it doesn’t use expando properties

Move the expando properties

The solution in this case consists of moving the logic from the field to a script object. A by-product of the move means we have to have a referencing mechanism from the script object back to the fields. (note that the strategy of moving expandos to script properties will work only in Reader version 8.1 9.0 and later)

The steps to modify this form to allow it to work under strict scoping.

1) Move the logic to a script object

In the original form, we added an instance of the control function to each field.  In our revised design, we’ll move the instances of the control function to a script object and maintain a mapping between each field and its corresponding control function.

I added a script object called “ctl” and moved the logic from the initization event there almost verbatim:

var This;

var control = function() {
    this.history = [This.rawValue],
    this.current = 0,
    this.startValue = null;

    this.undo = function() {
     . . . .
};

The difference is that in the initialization script I created an instance of the function (with the new operator) right away.  Whereas in the script object we simply declare the function and will create instances of it later.

2) Add some bookkeeping

I’ve added some bookkeeping functionality to the script object.  This “glue” code allows us to keep our original logic intact by providing a mapping from fields to the corresponding instance of the control object:

// The fieldList object is a place to store instances
// of the control function for each field
var fieldList = {};

// We need to assign unique id's to each field.
// The unique ids will index into fieldList.

// Use nUID as a global counter to assign id's
var nUID = 0;

// register a field and create an id, and an instance of control
function register(fld) {

    // We'll store the id in the field under "extras"
    var UID = fld.extras.nodes.namedItem("UID");
    if (UID === null) {
        UID = xfa.form.createNode("text", "UID");
        fld.extras.nodes.append(UID);
    }
    UID.value = "ID" + nUID.toString();
    This = fld;

    // create an instance of the control function
    fieldList[UID.value] = new control();
    nUID++;
}

// If a field is part of a subform that gets removed, remove it here also
function unregister(fld) {
	   delete fieldList[fld.extras.UID.value];
}

// Convenience methods for accessing the control functionality
// associated with each field
function getObj(field) {
   return fieldList[field.extras.UID.value];
}
function enter(field) {
    This = field;
    getObj(field).enter();
}
function exit(field) {
    This = field;
    getObj(field).exit();
}
function undo(field) {
    This = field;
    getObj(field).undo();
}
function redo(field) {
    This = field;
    getObj(field).redo();
}

3) Change the code references

The syntax of code that called methods directly on the Test field now needs to change. The initialization script of the Test field now makes a call to:

ctl.register(this);

The enter event changes to:

ctl.enter(this);

 

The click event is now:

ctl.undo(Test);

 

4) Cleanup

When the control function was hosted by the Test field, the instance was conveniently removed when the Test Field was removed.  But now this needs to be done explicitly. The code to remove the subform has a new call:

ctl.unregister(Test);
_item.removeInstance(this.parent.index)

After following the steps above, the revised form works under strict scoping.  Check it out.

Recode the Logic

But now the question needs to be asked: was this the best way to convert the form?  The process I followed was geared to keeping the control logic intact.  My assumption was that this is where the customer has invested the most and they’d prefer to keep that code intact.  But the implementation of the control function could have been implemented differently.  The history is represented as a JavaScript array, but could be re-implemented as an array of elements under <extras>.  The final solution is more elegant — but the effort to get there is riskier.

I’ve attached a 3rd variation of the form that implements undo/redo using extras. I won’t go into details on how it works here.  But one of the interesting side-effects of using extras rather than JavaScript variables is that due to formstate functionality, the undo history is preserved when the form is closed and saved. i.e. you can re-open the form later and the undo history is intact.

 

Multiple Top Level Subforms

I’m willing to bet that all the XFA form definitions you’ve looked at all have one top level subform.  Not surprising, because that’s what Designer
allows you to author.  But according to the XFA specification, there can be any number of top-level subforms below the <template> root.

Today I’ll briefly describe the processing rules for top-level subforms, provide a simple example where you might find them useful, and give you a couple of macros to make these easier to work with in Designer.

The Processing Rules

The rules are simple:

  • When there are multiple top-level subforms, only one is rendered.
  • We choose which subform to render based on which one best matches the data.  i.e. the template behaves like a choice subformSet: <subformSet relation="choice">. 
  • If no data is supplied, we render the first subform below the <template> element

A Sample

Suppose I want different variations of my form for printing and for interactive.  I construct my template so that it has top level subforms for both print and interactive.  Since the binding is all "by name", the form will render according to the name of the top level node in the data.  Try previewing the sample with these data files: interactive.xml, print.xml — or import these data files in Acrobat, and you’ll see the form change according to the data provided.  However, controlling print behavior with different data files is more applicable on the server than on the client. 

In order to force the form to use the print subform when printing from Acrobat/Reader, I added a pre-print event to the interactive root subform.  The script renames the top level node in the data, and forces a remerge.  This will cause the print operation to use the print subform.

interactive::prePrint – (JavaScript, client)
xfa.record.name = "print";
xfa.form.remerge();

Then to restore to the interactive view, I added a postPrint event to the print subform:

print::postPrint – (JavaScript, client)
xfa.record.name = "interactive";
xfa.form.remerge();

Design Experience

I’ve already hinted that Designer doesn’t really support multiple top-level subforms. But with a couple of handy macros, we can get by.

First problem is that there’s no easy way to create a new top level subform.  Here’s a macro to proide that functionality.  You’ll notice when you run the macro that there’s a problem.  The changes made by the macro don’t show up in the hierarchy.  This is a bug that will be fixed next release.  Meanwhile, closing and re-opening the form will workaround the issue.  Note that the macro to add the new top level subform also inserts a default master page (<pageSet>). Designer and the XFA runtime are generally very unhappy if they don’t have a set of master pages to work with.

Next problem is to figure out how to edit the new top level subform in Designer.  Here’s a macro to solve that problem. Since Designer always renders the first subform, this macro re-orders the top-level subforms so that you get a different top-level subform. Specifically, the macro re-orders the first subform to be last.  Just make sure that when you save the form that your preferred default subform is first in order.

Now, for the specific example of print and interactive variations, I could have implemented the solution in a number of different ways.  e.g. with a choice subformSet or with optional subforms at the second level in the hierarchy.  But the point was to bring to light some functionality that you might not otherwise been aware of.

Disable your form in older versions of Reader

When designing forms that require newer versions of Reader, you may want to be intentional about the experience users get when they open the form in older versions of Reader.  Today if you target a form for
Reader X and open it in Reader 9, you get this dialog:

After you’ve dismissed the message, Reader goes on to display the form as best it can.

The problem is that "as best it can" could lead to a very poor experience.  If you have functionality in your form that relies on a more recent Reader, then in all likelihood, your form is broken.  This could manifest itself in any number of ways.  e.g. Validations not firing correctly or the form not displaying correctly. 

As a form author, you’d prefer to prevent the user with an old version of Reader from having any interaction with the form.  No experience is better than a bad experience.  The topic for today is a design pattern to author a tailored experience for the user with an older Reader.

Design Pattern

There are probably several ways this can be done. I’ve chosen to add a version-checking subform below the root.  This version-checking subform has the following properties:

  • Contains content that warns the user that their version of Reader is not new enough
  • Points the user to the reader update page
  • Has a form field with the required Reader version number
  • Has an initialization script that compares the Reader version to the required version
  • If the Reader version is not new enough, the initialization script will remove instances of other subforms below the root and make sure the version checking subform is visible.
  • If the Reader version is new enough, the initialization script will add instances of other subforms below the root and hide the version checking subform

For this script to be effective, the other subforms below the root must be defined with occurence settings where the minimum is zero and the initial number is also zero:

I have attached a sample form that uses this technique. The form is targeted for Reader 10.  If you open it in version 9, you will get the version check warning experience.

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.