Archive for October, 2008

Exporting Charts to PNG

On a recent project, we needed to export PNG images for a series of charts. Part of the design was to create the charts in ActionScript, rather than MXML. Below are code highlights of how we went about the process.
The charts were listed in a type safe enumeration. Coming from a long Java background, it’s always comforting to see an old OOP standby get implemented in ActionScript. For each chart, a representative ChartSetup class was created, that contained the specifics of the chart’s configuration, series, axis’s, etc. For each entry in the enumeration, we included a reference to each representation class, as well as a constant that declared what type of chart the class was representing.
Construction of an enumeration entry, passing in SpendingByBusinessUnit, the ChartSetup class for that chart:

public static var SPENDING_BY_BUSINESS_UNIT:ChartType = 
new ChartType(0, SpendingByBusinessUnit);

This class reference is later used during the creation of the charts. A loop reads all of the constants in the enumeration, the class representing each chart is instantiated, and the chart is added to the user interface.

for each (var chartType:ChartType in ChartType.allCharts)
var ClassReference:Class = chartType.chartClass;
var chartDetails:* = (new ClassReference() as ChartSetup);
var chartDisplay:*;
if (chartType.type == ChartType.COLUMN) {
chartDisplay = new ColumnChart();
chartDisplay.height = chartDetails.chartHeight;
chartDisplay.width = chartDetails.chartWidth;
chartDisplay.showDataTips = true;
chartDisplay.type= chartDetails.columnType;
chartDisplay.series = chartDetails.series;
// etc

This created a set of charts, but what about the screenshots? We needed to collect a PNG of each chart, which was sent off to be embedded in a PowerPoint template. Capturing a bitmap image of a DisplayObject is not a big secret these days. For us, the trick was all in the timing. If you capture the bitmap directly after the chart creation, you might get a blank image. This is where the ‘Update Complete’ event comes in handy. We added a listener, calling the bitmap capture once ‘Update Complete’ fired. With the listener added, the charts were placed in a UI container, for the immediate enjoyment of the user.

// chart build, add listener  
var chartBox:VBox = Application.application.chartsPane.chartBox;
FlexEvent.UPDATE_COMPLETE, exportReady); // add to stage chartBox.addChild(chartDisplay);

Last, we needed to get the chart image captured. With a fully rendered DisplayObject, it’s not too complicated. Just create an instance of BitmapData using the DisplayObject‘s dimensions and encode with the PNGEncoder. Done!
Here’s the code:

public static function exportReady(event:FlexEvent):void
   // if all items are ready
   for each(var item:DisplayObject in images.getChildren())
       // etc
// code snip from screenshotChartForExport ...
var bitmapData:BitmapData =
new BitmapData(displayObject.width, displayObject.height, true, 0);
bitmapData.draw(displayObject); var pngEncoder:PNGEncoder = new PNGEncoder(); return pngEncoder.encode(bitmapData);

That’s it. If your charts are strictly MXML, you should still be able to capture the PNG, by referencing the chart by it’s id attribute.