Tuesday, June 16, 2009

Open flash chart save image (ruby+IE6)

Presentation: one of my tasks was to implement a feature to print a flash chart. To generate the chart, I'm using Open Flash chart 2, great open source, free, library to draw chart dynamically. I downloaded it from PullMonkey website (because I'm working on Ruby).

Now let's try to print the chart. After the chart is generated (can't print if no render), we can get the image as binary:

document.getElementById('divfMyChart').get_img_binary()
//return a base 64 image, something like this: R0lGODlhFGHGGHRGH..... (very long string)

More info about base64. And here is an example of a base64 image:



If your web site support only FireFox and IE8 (and higher) that's perfect, because there is no problem! Here is the code, it will create a new window with the image:

img_src = document.getElementById('divOfMyChart').get_img_binary();
img_tag = "";
var img_win = window.open("", null);
with(img_win.document) {
write("my image" + img_tag + ""); } //change "t-itle" for "title"

You can see a more complete examples here.
Now if you open your web page with IE6 or IE7 it won't work! Why? Now begin the most interesting part of this post.
Well, it's not working because IE6 and IE7 don't support base64 format. It's not even a problem of your server, so you can not do anything except to change your source code.
I could see there is a library in PHP who can create an image from base64 image (base64_encode($string);).
So is there a similar library in Ruby? Yes there is! It's RMagick. An interface to the ImageMagick and GraphicsMagick image processing libraries. Supports over 100 image formats, including GIF, JPEG, PNG.

Download RMagick (current version 2.9.2): http://rubyforge.org/projects/rmagick/
If you're using Windows, download "rmagick-win32"!
Extract the files from the archive, and read README.html. Please follow the "Installation instructions". It's easy to install. RMagick requires ImageMagick or GraphicsMagick, so installed one (only one!). You can find the installation files in RMagick website.

And we need something else, the PNG library: http://www.libpng.org/pub/png/pngcode.html
PNG library is divided into 2 libraries :
zlib:Zlib is designed to be a free, general-purpose, legally unencumbered -- that is, not covered by any patents -- lossless data-compression library for use on virtually any computer hardware and operating system.
Download "zlib compiled DLL, version 1.2.3": http://www.zlib.net/ Extract the files and read USAGE.txt. It's easy to install: copy ZLIB1.DLL to the SYSTEM or the SYSTEM32 directory.

libpng:This is an open source project to develop and maintain the reference library for use in applications that read, create, and manipulate PNG (Portable Network Graphics) raster image files.
download "libpng-1.2.37-setup.exe": http://sourceforge.net/project/showfiles.php?group_id=23617&package_id=16183 and install it.


So now you have everything!
We create a JS function who will send an ajax request (with the binary image) to the controller. The controller will create an image (jpg) on the server and return the path to this image. Then the JS function will create a popup to show the image.
In my view:

function printChart(event){
new Ajax.Request(urlForPrintImage, {
method: 'put',
postBody: document.getElementById('divOfMyChart').get_img_binary(),
onSuccess: function(transport){
image_path = transport.responseText;

img = "";
var img_win = window.open("", null);
with(img_win.document) {
write("My image" + img + ""); } //change "t-itle" for "title"
},
onFailure: function(transport){}
});
}

In my controller:

require 'base64'
require 'rubygems'
require 'RMagick'

def print_image
#get the parameter sent by ajax query
png_data = request.raw_post

#file name and path for the image file
#(you have to change this depending of your routes)
file_name = "myPath/chart.jpg"

#decode the binary image with Base64
blob = Base64.decode64(png_data)

#use RMagick to create an image from the blob
image = Magick::Image.from_blob(blob).first
#save the file on the server
image.write(file_name){ self.quality = 100 }

#return the file path
render :text => file_name
end

2 comments:

Marco said...

hi, i'm trying to use your example with a basic ofc2 chart but firebug reports "get_img_binary is not a function". I can see that function is declared on an actionscript source (main.as) but i don't know how can i link it to my page.
thanks

Admiral Adney said...

I was searching for ror development and landed up on your post and i must say thanks for sharing such useful information.