Export SVG from Raphael JS to create a PNG bitmap

Update

I’m very pleased to be able to say that Jonathan Spies has taken the roots of this blog post and produced a fully fledged plugin. I’ve used it on a couple of projects already. Oh how I love the web sometimes.

http://github.com/jspies/raphael.serialize

Introduction

I’ve been playing around with RaphaelJS recently (if you haven’t seen it, check it out) for an ‘avatar builder’ type project to avoid the use of Flash. It’s a really fun bit of kit to use, but I needed a way to actually save the results of the avatar to a bitmap to avoid having heavy javascript libraries every time the avatar is displayed on a page. After a bit of poking around on forums/blogs I thought I’d tie up all the key pieces in one blog post in case anyone else was in the same boat.

The output is essentially a PNG-24 alpha channel bitmap image, which can then be treated as normal with the likes of ImageMagick (I’ve been adding drop shadows to the avatars for example).

Extracting SVG data from Raphael

Unfortunately this isn’t as easy as could be. In browsers that support SVG, we can simply extract all the data from the DOM itself to reproduce it, but as Raphael uses VML on non-SVG browsers (namely, IE) that idea won’t work.

However, the Raphael Javascript object does store the data we need in order to re-produce the SVG. This requires a little bit of Javascript, and for demonstration purposes I’m using PHP.

The Javascript

So the first thing I do build up a JSON object describing the data needed to produce the SVG file, with a little help from jQuery. I’ve put the code for this on this gist. It essentially loops through the ‘paper’ variable that RaphaelJS is using to build up our own array. In mine I was using just paths and images, so that was the only data I needed, however this could easily be expanded to include all the other properties as well.

The Server Side Code (PHP)

All this file does is decode the JSON data into an associative array for use in PHP, loop through the properties and outputs an SVN file. I’ve put the PHP I’m using below as there’s not much to it:

    $json = json_decode($_GET['json'], true);

    $output = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="'.$json[0]['width'].'" height="'.$json[0]['height'].'" xml:space="preserve"><desc>Created with Raphael</desc><defs></defs>';

    for ($i=1; $i &lt; count($json); $i++) {
        if ($json[$i]['type'] == "image") {
            $base64 = base64_encode(file_get_contents($json[$i]['src']));

            $output .= '<image overflow="visible" x="'.$json[$i]["x"].'" y="'.$json[$i]["y"].'" width="'.$json[$i]["width"].'" height="'.$json[$i]["height"].'" transform="'.$json[$i]["transform"].'" preserveAspectRatio="none" xlink:href="data:image/png;base64,'.$base64.'"></image>';
        }

        if ($json[$i]['type'] == "path") {
            $output .= '<path fill="'.$json[$i]['fill'].'" stroke="'.$json[$i]['stroke'].'" d="'.$json[$i]['path'].'" style="opacity: '.$json[$i]['opacity'].';" opacity="'.$json[$i]['opacity'].'" transform="'.$json[$i]['transform'].'"></path>';
        }
    }

    $output .= '</svg>';

Save the SVG and send to Apache Batik for Rasterization

So this part is fairly simple. I recommend using Apache Batik for the SVG rasterization as it seemed to have better results compared to ImageMagick. The first part is simply to write the ‘SVG string’ to a file.

file_put_contents('generated/avatar.svg', $output);

Now, just send it across to Apache Batik with the exec() command, and we have a PNG!

exec("java -jar batik-rasterizer.jar /path/to/avatar.svg");

Just the beginning

So this was just a basic introduction to exporting an SVG from Raphael, and will need more work for more complex shapes and transformations. I’m planning on writing a RaphaelJS extension to make all this a bit easier, and perhaps offer out the server side code on a public domain so there’s no need to write it out…

So stay tuned. Or, if you’re already doing this, I’d be more than happy to help out.

8 thoughts on “Export SVG from Raphael JS to create a PNG bitmap”

  1. This is excellent work, I’m going to have to think of a project to try it out. The only thing I’m thinking is, do you really need to use PHP on the server side to generate the SVG from the object on IE when you already have a load of code in Raphael to do that for you? If you could force R.svg to be true somehow would it then be possible to ‘trick’ Raphael to output the SVG for you directly?
    I’ve looked at the code and it probably isn’t a simple thing to do, but it’s probably going to be simpler than recreating all that Raphael functionality in PHP?

  2. Rob I think you might be on to something there. I agree, any way to avoid having to repeat all of Raphael’s hard work in PHP would be a bonus as it’s obviously faster and also makes the technique future proof with future releases.
    The only server side code we will still need though is the hook into Apache Batik or one of the other SVG rasterizers. One to actually execute it and probably a bit more logic to actually do something with the resulting PNG, even if it’s just outputting a URL to it. The rest could be handled client side as you say.
    Definitely something I’ll be looking into.

  3. Great post. Thanks a lot.

    I needed to serialize the SVG object for a different reason, but your gist really put me in the right direction. I noticed looping through the paper object didn’t give me all the SVG objects. Did you run into this? I found they were stored in a linked list starting at the top and bottom methods for the paper object.

    I put a modified gist up that also has support for some more basic shapes (rect, ellipse, and text).

  4. Thanks for emailing to let me know that there was an issue with posting comments. Having some troubles with spam so hopefully it’ll all be sorted now.

    I ran into a few issues re-building the SVG ‘path’ attribute as Raphael seems to store it’s arrays slightly differently (hence the lengthy loop for that case). However having run a few tests on some more involved drawings your JS has really helped. Thanks for that – also like the way you’ve cleaned up the return path as well, neatness is good.

    Did you need the server side element to this? Just wondering if you managed to adapt that side of things too.

  5. I don’t really need the server side. What I’m doing is allowing a user to draw, save that drawing, and pull it up again for editing. At the top of that gist I added a loadJSON function that builds a Raphael set out of the saved JSON.

    They remain objects and I return the set so I can throw some event handlers onto them to allow them to be selectable and editable.
    It’s all client site except for an Ajax call to save/load the json to/from the database.

Comments are closed.