❏ How to render an SVG with a feDropShadow filter to PNG raster

In one of my most recent geeky escapades, I was attempting to automate generation of some icons / buttons. Turns out all is fun and games, until someone uses feDropShadow.

There’s more than one ways to raster an SVG

There’s tons of reasons why you wouldn’t want to generate a bunch of icons with a clumsy UI, such as Adobe Illustrator or Inkscape. You can set up a script that dumps your icons out as SVG, then add something that auto-converts to PNG. Think of the possibilities:

  • Parametrize your pipeline: Flip one parameter, run again, voila! A new batch of icons, with a different color or stroke width or font family, all freshly baked!
  • Add a time dimension: Run multiple times with some variation, then join the results to create animated GIFs.
  • Expand the range of glyphs: With a small change in your code, you can render any range of unicode glyphs. Think currency symbols, tool buttons, numbers, etc.
  • Tile all the buttons into a sprite: Then, use the CSS background-position rule to display the right button at the right place. The browser will only do one HTTP request to download all the buttons.

Why would you do all that, you say?

Well, for one, you could try to sell icon collections on places like vectorstock.com. Once you have a pipeline ready, you can flood the market! Or so I theorize…

Also, code is beautiful!

Way 1: Native PHP

You could use the PHP ImageMagick library to generate your PNG file. In the following example, I will write out some SVG for the Bitcoin icon, using the Unicode character and some rotation.

<?php

ob_start();

$text = '&#x20bf;';

?><?xml version="1.0"?>
<svg
	xmlns="http://www.w3.org/2000/svg"
	xmlns:xlink="http://www.w3.org/1999/xlink"
	viewBox="0 0 64 64">

    <circle
		fill="orange"
		stroke="transparent"
		cx="32"
		cy="32"
		r="31"
		stroke-width="0">
	</circle>


    <text
		transform="rotate(10 32 32)"
		x="50%"
		y="50%"
		text-anchor="middle"
		fill="white"
		font-size="36pt"
		stroke="0"
		dy=".3em">
		
		<?php echo $text; ?>
	</text>
</svg>
<?php

$svg = ob_get_clean();

// write out the SVG to file
file_put_contents( 'bitcoin-icon.svg', $svg );

// now use Imagick to render the button to raster
$im = new Imagick();
$im->setBackgroundColor( new ImagickPixel( 'transparent' ) );
$im->readImageBlob( $svg );
$im->setImageFormat( "png24" );
$im->writeImage( 'bitcoin-icon.png' );
$im->clear();
$im->destroy();

Not too shabby! You can now use the full power of PHP templating to wrap this code into loops and generate all kinds of icons. Parametrise colors, sizes, stroke widths, etc.

Bitcoin icon generated using some custom PHP

bitcoin-icon.png

Way 2: Convert

ImageMagick can be invoked on Linux systems with the convert command.

convert bitcoin-icon.svg bitcoin-icon.png

Way 3: Inkscape CLI

Inkscape can be used from the command line to convert an SVG to PNG. Here’s how:

inkscape -z bitcoin-icon.svg -e bitcoin-icon.png

Depending on various details of your SVG file, and on the current phase of the moon, this way might give you a better result compared to the other two.

Keep your face to the PNG and you cannot see a shadow

If you are a novice user of Scalable Vector Graphics, you might think that you can add a drop shadow with this well-known incantation:

<svg ... <!-- header goes here as before -->

	<defs>
		<filter id="shadow">
			<feDropShadow dx="4" dy="8" stdDeviation="4"/>
		</filter>
	</defs>

	<g filter="url(#shadow)">
		<!-- rest of SVG shapes go here as before -->
	</g>
</svg>

You could, but only web browsers would render the output with a shadow. None of the three methods above render the shadow correctly. In fact, even the preview renderer in Nautilus screws up when it sees this filter.

When small scripts begin to cast big shadows

What to do now? Simple. A quick look at the MDN docs reveals that feDropShadow is a filter primitive, and it is equivalent to a bunch of simpler filters, merged together. Let’s try the whole thing again, this time spelling the shadow out a bit more verbosely:

It seems that the feDropShadow filter is not yet supported in ImageMagick. At least not in the version I’m using. But the primitives it depends on, are!

 

Bitcoin icon with shadow, generated using some custom PHP

bitcoin-icon.png

Leave a Reply

Your email address will not be published. Required fields are marked *