mysw_logo

Gimp SVG to Raster Script

Aside from being a fantastic application for interactive use, The Gimp may be used in a non-interactive mode from scripts, written in a scheme-based language called Script-Fu.

A recent question posted on the Open Clipart mailing list asked if anyone knew of a tool for batch conversion of SVG images to raster formats (other than ImageMagick which wasn't working very well for what the poster wanted). This got me thinking about Script-Fu because I've been meaning to look into it for a while.

Anyhow, after a short time being horrified at scheme's syntax (it reminds me very much of my experiences of elisp - yuk! I still have nightmares where I'm drowning in brackets...), I knocked up a little script-fu routine for reading SVG files and writing raster formats: svg-to-raster.scm.

Example 1 - Converting One File.

Step 1: Download svg-to-raster.scm and save it in the gimp user settings folder in the sub-directory "scripts" (for example, on Linux this is ~/.gimp-2.2/scripts with the current stable Gimp version).

Step 2: Open the terminal (windows users should install cygwin and open a bash shell). Change to the directory which contains your svg files.

Step 3: Invoke gimp like this:

$ gimp -i -b '( svg-to-raster "infile.svg" "outfile.png" 72 0 0)' '(gimp-quit 0)'

Now you should have a PNG version of your SVG file. If you want a different raster format, just change the outfile extension. Gimp understands quite a lot of them, so you'll probably be in luck unless it's some evil proprietary format.

Example 2 - Converting a Whole Bunch of Files.

The example above is all well and good, but it's a little pointless. If you have just one file to convert you might as well just load it into Gimp interactively and save it in your desired format. The joy of making a command work from the shell is that it can be batched and run many times with no interaction necessary.

So, lets have a go. Say we have a directory with 10 SVG files, and we want to convert them all to PNG format:

$ ls
az-lizard_benji_park_01.svg	 cormorant-md.svg	     turkey_on_platter_01.svg
baby-tux_alex_kuehne_01.svg	 gull_marcelo_staudt_01.svg  wing_mitchell_johnson_si_.svg
bird_of_peace_mauro_oliv_01.svg  puffin-md.svg
contour_bat.svg			 rooster_01.svg
$ for f in *.svg; do gimp -i -b "( svg-to-raster \"$f\" \"${f%.svg}.png\" 72 0 0)" '(gimp-quit 0)'; done
[Gimp output removed]
$ ls 
az-lizard_benji_park_01.png	 contour_bat.svg	     rooster_01.png
az-lizard_benji_park_01.svg	 cormorant-md.png	     rooster_01.svg
baby-tux_alex_kuehne_01.png	 cormorant-md.svg	     turkey_on_platter_01.png
baby-tux_alex_kuehne_01.svg	 gull_marcelo_staudt_01.png  turkey_on_platter_01.svg
bird_of_peace_mauro_oliv_01.png  gull_marcelo_staudt_01.svg  wing_mitchell_johnson_si_.png
bird_of_peace_mauro_oliv_01.svg  puffin-md.png		     wing_mitchell_johnson_si_.svg
contour_bat.png			 puffin-md.svg
  

The business end of this example is a little iteration using the shell. Notice that the quotes have been changed a little bit to allow the shell to substitute variable values in the command. Also note that the name of the output file is generated using a little shell trick ${f%.svg}.png. This expression says "take the value of f but remove any ".svg" suffix, and then add ".png". There are lots of these tricks that you can do from the shell. It's well worth learning them.

Example 3 - Converting All Files in a Directory Tree

OK, this one is really the answer to the post in the open clipart mailing list.

I think it's best to put all this into a script file - writing mini-scripts on the command line is nice, but it can get cumbersome. Formatting is always easier nicer in a script file.

Create a file, "ocal2png", containing this:

#!/bin/bash
# Name:         ocal2png
# Description:  descend down a directory hierarchy and convert any SVG 
#               files that are found into PNG images.
# Author:       Matthew Gates
# Date:         2006-01-28
# Notes:        You must have a copy of svg-to-raster.scm in your gimp
#               scripts directory.  Both this file and svg-to-raster.scm 
#               can be downloaded from porpoisehead.net:
#
#               http://porpoisehead.net/mysw/downloads/ocal2png
#               http://porpoisehead.net/mysw/downloads/svg-to-raster.scm
#
# License:      GPL v2

# Set the resolution of the PNGs you want to generate
DPI=72

# if clobber is 0, existing .png files will not be over-written
# else they will.
CLOBBER=0

# OK, descend the hierarchy and make a .png version of each file
( find ${1:-.} -type f -iname \*.svg | while read infile
      do
      outfile=${infile%.svg}
      outfile=${outfile%.SVG}.png
      if [ -e "$outfile" ] && [ $CLOBBER = "0" ]; then
	  echo "WARNING: will not over-write existing file: $outfile" 1>&2
      else
	  echo "( svg-to-raster \"$infile\" \"$outfile\" $DPI 0 0)"
      fi
    done
    echo '(gimp-quit 0)'
) | gimp --batch-interpreter plug_in_script_fu_eval -i -b -
  

All you need to do is make it executable and run it. It takes on optional parameter: the diectory name to descend into. If this is not supplied then the current working directory will be used instead.

A note for the curious: I do not invoke gimp for each file. This would work, but it's a whole lot less efficient. There are two rule for getting decent performance out of shell scripts:

  1. Invoke externals as seldom as possible.
  2. Don't use shell scripts - use Perl instead ;-)

The Script-Fu Code

(define (svg-to-raster infile
		       outfile
		       resolution
		       xmax
		       ymax)
  (let* ((image (car (file-svg-load 
		      RUN-NONINTERACTIVE 
		      infile 
		      "" 
		      resolution 
		      (- 0 xmax) 
		      (- 0 ymax) 
		      0
		      )
		     )
		)
	 (drawable (car (gimp-image-get-active-layer image))))
    (plug-in-autocrop RUN-NONINTERACTIVE image drawable)
    (gimp-file-save RUN-NONINTERACTIVE image drawable outfile outfile)
    (gimp-image-delete image)
    )
  )

So, lets have a little look at it. All the work is done by invoking the file-svg-load function - a built-in of Gimp. The function we define simply loads an SVG file as a raster, crops it and saves to an image file. The paramaters to the svg-to-raster function are:

infile The name of the SVG input file.
outfile The name of the output file. Note that the extension of this filename will determine the output file format.
resolution This is the resolution in DPI of the output image.
xmax This chooses the size of the image in pixels along the x axis. Note that this size is the pre-cropped size, so you may well get a smaller. Note also that using xmax may destort the final image. To ignore this setting, use 0.
ymax This chooses the size of the image in pixels along the y axis. Note that this size is the pre-cropped size, so you may well get a smaller. Note also that using ymax may destort the final image. To ignore this setting, use 0.