Posts in Category "Scripting"

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

 

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.

 

E4X in Form Design

Over the time that I’ve been writing blog entries I’ve made many references to E4X and have included lots of samples that make use of this technology. However I’ve recently come to understand that I probably should have done a better job of introducing E4X usage, since it isn’t necessarily well understood by the average form developer.

For those of you who are unaware, E4X is an extension to JavaScript (ECMAScipt) for manipulating XML.  The reason it is not particularly well known among JavaScript developers is that few of the browsers have implemented E4X.  The notable exception is Mozilla / Firefox. And since the JavaScript engine inside Acrobat/Reader is based on Mozilla, E4X can be used reliably in Acrobat/Reader.

To learn more about E4X, here are some links you may find useful:

http://www.xml.com/pub/a/2007/11/28/introducing-e4x.html

http://www.ecma-international.org/publications/standards/Ecma-357.htm

http://rephrase.net/days/07/06/e4x

http://wso2.org/project/mashup/0.2/docs/e4xquickstart.html

The XMLData Object

Before E4X was invented, we had recognized the need to have generic XML processing in Acrobat / Reader. Consequently, we added the XMLData object to the Acrobat object model.

However, E4X was introduced shortly afterwards, and we ended up with two XML processing engines in Acrobat/Reader. In case there is any doubt, the right answer is to use E4X. You should no longer use the XMLData object. The XMLData object remains in the product for backward compatibility, but it does not get enhanced, it does not get bug fixes.  E4X is faster and uses less memory.

Some Usage Notes

There are several common learning curve issues you might encounter when using E4X:

Oddly, E4X refuses to process the xml processing instruction at the front of an XML fragment.  If it’s there, you will get a syntax error: “xml is a reserved identifier”.

The workaround is to remove leading processing instructions with a regular expression such as:

sXML = sXML.replace(/^[\s\S]*?(<[^\?!])/, “$1″);

Processing Instructions and Comments

By default E4X ignores processing instructions and comments. You can change the default with:

XML.ignoreProcessingInstructions = false;
XML.ignoreComments = false;

If you stop ignoring comments and processing instructions and if you are processing an XML fragment that includes an outer comment or processing instruction, you need to treat it as an XMLList object instead of an XML object:

XML.ignoreProcessingInstructions = false;
var xyz = new XMLList(‘data’);
xyz.toXMLString();

JSLint

Since I just finished recommending JSLint to you, I should point out that JSLint doesn’t handle the E4X syntax extensions to JavaScript.  e.g. XML literals. It will stop processing once it encounters syntax specific to E4X. In most cases you can keep JSLint happy by using function calls instead of the syntax extensions.  e.g. instead of:

xXML..a.length();

use:

xXML.descendants(“a”).length();

Mixed namespaces

Consider this example:

var xXML = <root><one><two>abc</two></one></root>;

The expression: xXML.one.two.toString(); returns “abc”. However, if I introduce another namespace, this expression no longer works:

var xXML = <root><one><two xmlns=”foo”>abc</two></one></root>;

Instead, use: xXML.one.*::two.toString();

And for completeness, the JSLint friendly version:

var xXML = new XML(‘<root><one><two xmlns=”foo”>abc</two></one></root>‘);
xXML.one.child(QName(null, “two”)).toString();

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.