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.