Programmatically building drawables

There's something that really bothers me and it's doing things that a computer should do. One of these is saving different versions of drawables, in low, medium and high resolutions, for the drawables-ldpi, drawables-mdpi and drawables-hdpi folders respectively.

Coincidentally, I found a couple of weeks ago that the wonderful Inkscape accepts command line parameters and thus can be used for batch processing files.

If you execute Inkscape from a terminal with the --help parameter you get a list of things it can do for you:

$ inkscape --help

Usage: inkscape [OPTIONS...] [FILE...]

Available options:
  -V, --version                             Print the Inkscape version number
  -z, --without-gui                         Do not use X server (only process files from console)
  -g, --with-gui                            Try to use X server (even if $DISPLAY is not set)
  -f, --file=FILENAME                       Open specified document(s) (option string may be excluded)
  -p, --print=FILENAME                      Print document(s) to specified output file (use '| program' for pipe)
  -e, --export-png=FILENAME                 Export document to a PNG file
  -d, --export-dpi=DPI                      Resolution for exporting to bitmap and for rasterisation of filters in PS/EPS/PDF
                                            (default 90)
  -a, --export-area=x0:y0:x1:y1             Exported area in SVG user units (default is the page; 0,0 is lower-left corner)
  -D, --export-area-drawing                 Exported area is the entire drawing (not page)
  -C, --export-area-page                    Exported area is the entire page
      --export-area-snap                    Snap the bitmap export area outwards to the nearest integer values (in SVG user units)
  -w, --export-width=WIDTH                  The width of exported bitmap in pixels (overrides export-dpi)
  -h, --export-height=HEIGHT                The height of exported bitmap in pixels (overrides export-dpi)
  -i, --export-id=ID                        The ID of the object to export
  -j, --export-id-only                      Export just the object with export-id, hide all others (only with export-id)
  -t, --export-use-hints                    Use stored filename and DPI hints when exporting (only with export-id)
  -b, --export-background=COLOUR            Background colour of exported bitmap (any SVG-supported colour string)
  -y, --export-background-opacity=VALUE     Background opacity of exported bitmap (either 0.0 to 1.0, or 1 to 255)
  -l, --export-plain-svg=FILENAME           Export document to plain SVG file (no sodipodi or inkscape namespaces)
  -P, --export-ps=FILENAME                  Export document to a PS file
  -E, --export-eps=FILENAME                 Export document to an EPS file
  -A, --export-pdf=FILENAME                 Export document to a PDF file
      --export-latex                        Export PDF/PS/EPS without text. Besides the PDF/PS/EPS, a LaTeX file is exported,
                                            putting the text on top of the PDF/PS/EPS file. Include the result in LaTeX like:
  -T, --export-text-to-path                 Convert text object to paths on export (PS, EPS, PDF)
      --export-ignore-filters               Render filtered objects without filters, instead of rasterising (PS, EPS, PDF)
  -X, --query-x                             Query the X coordinate of the drawing or, if specified, of the object with --query-id
  -Y, --query-y                             Query the Y coordinate of the drawing or, if specified, of the object with --query-id
  -W, --query-width                         Query the width of the drawing or, if specified, of the object with --query-id
  -H, --query-height                        Query the height of the drawing or, if specified, of the object with --query-id
  -S, --query-all                           List id,x,y,w,h for all objects
  -I, --query-id=ID                         The ID of the object whose dimensions are queried
  -x, --extension-directory                 Print out the extension directory and exit
      --vacuum-defs                         Remove unused definitions from the defs section(s) of the document
      --verb-list                           List the IDs of all the verbs in Inkscape
      --verb=VERB-ID                        Verb to call when Inkscape opens.
      --select=OBJECT-ID                    Object ID to select when Inkscape opens.
      --shell                               Start Inkscape in interactive shell mode.

Help options:
  -?, --help                                Show this help message
      --usage                               Display brief usage message

As you can see there are many things that can be done! But the most useful ones for my purposes are export-png and export-area-drawing. The first one is obvious, and the second one allows us to make sure that only the drawn area is exported --so you don't need to worry about extra whitespace in the file or centering/cropping the drawing on the page.

Therefore, to export a file to PNG we would invoke inkscape like this:

inkscape input.svg --export-png=output.png --export-area-drawing

Once that's done, the rest is just a matter of resizing files and saving each file to its corresponding directory. I did this part using the Python Imaging Library (PIL).

bitmap =

for version in output_versions:
    output_file = os.path.abspath(os.path.join(output_dir, version['name'], image + '.png'))

    resized = bitmap.resize((version['width'], version['height']), Image.ANTIALIAS), 'PNG')

Of course I could have used ImageMagick or something like that too, but PIL was just easier and since the rest of the script was Python too, it made sense to me to avoid introducing another dependency.

I'm really happy with the result: the generated images are crisp but still smooth, thanks to using Image.ANTIALIAS as resize filter, and they don't lose their transparent background at any moment, as I was kind of fearing when developing this.

I also added support for command line arguments in the script. That way, input and output directories and the list of drawables to transform can be entered in the command line, and you can place this script somewhere accessible; it doesn't need to sit in your project folder. For example, you could save the script in your ~/scripts folder and call it like this:

python ~/scripts/ --input-dir=~/projects/whatever/gfx/ --output-dir=~/projects/whatever/res --images=icon,menu,start,stop,whatever

It could even be added as an step in the build process, but that would require digging into whichever Ant rules are there to build Android projects, and adding a rule to execute the script, another for refreshing the resources folder and another one for regenerating R (if that's possible!).

I think I'm going to avoid that for the time being, but if you know how to do that don't forget to leave a comment! :-)

Here's the script:

Happy drawables building!