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 = '₿'; ?><?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.
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!