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:
\input{latexfile.tex}
-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 <strong>export-png</strong> and <strong>export-area-drawing</strong>. 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:
```bash
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 = Image.open(temp_file)
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)
resized.save(output_file, '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/build_drawables.py --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: build_drawables.py
Happy drawables building!