Archive for June, 2010

Add Trace to Form Script

I continue to spend time playing with the prototype macro capability found in Designer 9 (ES2).  I have a new sample that uses it, but of course before showing you, I need to reiterate that the macro stuff is still an unsupported trial feature.

I wanted to do something to further help people debug their form script.  The usual way this happens today is that you sit down and add console.println() commands to the various scripts that run.  There are a few drawbacks to this mode of debugging:

  • It is tedious to add the debug/trace statements
  • It is not always clear which scripts need to have trace added
  • If you generate too much output, the console window fills up and stops showing content
  • FormCalc scripts cannot access the console object
  • Dumping large amounts of content to the console slows form execution considerably

To help matters along, I have written a macro that will modify scripts in your form and automatically add trace statements.  The injected code does the following:

  • Adds a top-level script object (scTrace) for collecting trace statements
  • scTrace stores trace statements in a JavaScript array — so that the size is not limited by the console
  • Adds a pre-print event that allows the array of trace statements to be dumped to a file
  • Adds a "full" event script to the root subform that can be used with execEvent() to emit strings from FormCalc scripts (this technique first introduced in this blog entry)

As an example, the before and after for a specific JavaScript looks like:

Before:

var vTarget = xfa.event.target;
if (vTarget.className === "field") {
     vTarget.fillColor = "220,220,255";
}

After:

scTrace.traceContext("enter:", this);
var vTarget = xfa.event.target;
if (vTarget.className === "field") {
     vTarget.fillColor = "220,220,255";
}

For FormCalc the result is a bit more verbose:

Before:

price * units

After:

;scTrace.trace
xfa.event.fullText = "calculate"
xfa.event.newText = $.somExpression
$form.#subform.execEvent("full")
price * units

Note that when you use this debugging technique you must not leave this script in your production forms.  It adds too much overhead.  When you want to trace/debug your form, follow these steps:

  1. Save a copy of your form
  2. Add trace to the copy
  3. Run the copy in Acrobat (not Reader)
  4. When you want a trace dump, print the form.  The pre-print event fires and prompts you to save the trace (when trace has been saved, cancel the print)
  5. debug and migrate changes/fixes back to the original document

The actual macro is here.  You need to rename it to have a .js suffix and put it in a subdirectory under the Scripts directory in your Designer install (see detailed instructions in the original blog entry).

Here is a sample form where trace has been injected.

I’ve found this tracing technique to work well in cases where form performance may be impacted by lots of events and scripts.  Especially in cases where there are dependency loops (see the discussion under "Dependency Loops" in this blog entry).

Note that the macro does not add trace to the indexChange event.  I ran into a scenario where this combination produced some bad behavior, so I elected to exclude that trace.  Hopefully this problem gets sorted out in the next version of Designer.

Display an XML Spreadsheet

Today’s blog is on a topic that I’ve intended to tackle for a long time. I finally got the opportunity to work on this after a customer contact.  The end goal is to add spreadsheet data to a PDF form.  Copy and paste aren’t enough — we need to preserve the formatting of the cell data.

The solution involves converting the spreadsheet to an xml format and then populating the form with the cell data from a nice friendly xml grammar.

To get consumable XML from within Excel, choose save-as “XML Spreadsheet 2003″.
You can read about this format at:

http://en.wikipedia.org/wiki/Microsoft_Office_XML_formats

and

http://msdn.microsoft.com/en-us/library/aa140066%28office.10%29.aspx

The spreadsheet grammar has all the cell and row properties needed for rendering: formatting, styling, widths, heights etc.  The only problem is that the format does not lend itself to populating a form via data binding. There are too many special cases to construct sensible binding expressions.  Instead, I’ve taken the approach of using JavaScript to parse the spreadsheet format and to turn it into corresponding formatted XFA fields.

Before I go further, I should mention that my implementation took a few shortcuts.  I didn’t implement the full XML Spreadsheet definition.  Some parts of it I implemented fairly poorly.  My intention wasn’t to provide a finished product, but rather to give you a sample you could build on.

Here is the sample form: spreadsheet.pdf.  I trolled around looking for interesting spreadsheets to test with and found the financial statement for Colgate.

To use the form, follow these steps (from Acrobat — not Reader):

  1. Take an excel spreadsheet, save-as “XML Spreadsheet 2003″.
  2. Click “Load XML Spreadsheet” to embed it as an attachment
  3. Select the region to display
  4. Click “Display”
  5. Repeat steps 4,5 with other regions.  Or go back to step 2 with a different spreadsheet.

Note that Acrobat won’t allow you to do step 2 while the spreadsheet is open in Excel. And if you’ve picked a big spreadsheet it will be slow.  Go ahead and try it with a couple of the spreadsheets you have on your hard drive.

The structure of the form is simple:

  • A Table subform that gets repeated for every worksheet. It has a conditional break so that every worksheet after the first one occurs on a new page.
  • A repeating Row Subform that corresponds to a spreadsheet row
  • A repeating Cell Subform with a Data field that repeats for each column

The only really tricky part of the layout is that the cells are explicitly positioned — not flowed left-to-right.  The reason for that is so that I could immitate the Z order of Excel.  When cells overflow they overwrite cells to their right. To make this work I needed the cells to be added in order from right-to-left so that the left-most cells are highest in Z-order.  I needed them to be positioned so that when a cell overflows it doesn’t push its neighbors over.

The script is another matter. There’s over 700 lines of script that are fairly complex.

It starts by prompting the user to add the spreadsheet as an attachment (with the doc.importDataObject() method).  Once the spreadsheet is stored as an attachment it extracts the contents into an E4X XML object.  Then most of the work is simply parsing the style and size information from the spreadsheet and applying those to the cells.

Note that the row and cell columns are unbound.  That means if you looked in the form data for the spreadsheet information, you wouldn’t find it.  But not to worry, the data is close by — available in the attached spreadsheet.