├── README.md ├── assets ├── actionbar_tag.svg ├── app_icon.svg └── tag.9.svg ├── gen9patch.py ├── process_assets └── res ├── drawable-hdpi ├── actionbar_tag.png ├── app_icon.png └── tag.9.png ├── drawable-ldpi ├── actionbar_tag.png ├── app_icon.png └── tag.9.png ├── drawable-mdpi ├── actionbar_tag.png ├── app_icon.png └── tag.9.png └── drawable-xhdpi ├── actionbar_tag.png ├── app_icon.png └── tag.9.png /README.md: -------------------------------------------------------------------------------- 1 | SVG -> drawable-xhdpi, drawable-hdpi, drawable-mdpi, drawable-ldpi 2 | 3 | Android SVG Asset Generator 4 | ---- 5 | Future proof your assets and save time! 6 | 7 | Create / find once and don't worry about DPI buckets. 8 | 9 | This tool allows you to use SVG files for your Android apps image resources. 10 | 11 | SVG images are scaled and put into appropriate folders for android and the 9 patch is applied. 12 | 13 | Iconograpy for apps can be quickly generated from content from sites like those below: 14 | http://www.fileformat.info/info/unicode/char/search.htm 15 | 16 | Generating Images 17 | ---- 18 | Example: 19 | 20 | ./process_assets ./assets/ ./ 21 | 22 | The asset generator code can be kept in a sub folder and called from there. This makes it really easy to include in your project as a git submodule. 23 | 24 | ./asset_generator/process_assets ./source_images/ ./AndroidProjectFolder/ 25 | 26 | Source Image Info 27 | ---- 28 | The document size on your SVG files should reflect the image size at 72dpi. 29 | 30 | Ex: an app icon should be 27x27 px at 72dpi so that when scaled up to 240dpi (HDPI) it is 72x72 px. 31 | 32 | To add a 9patch to generated images, add a hidden layer called 9patch see tag.svg for an example 33 | 34 | Requirements 35 | ---- 36 | * Linux, OS X, (Cygwin? requires inkscape to be on your path.) 37 | * Inkscape 38 | * Python 39 | * PIL -------------------------------------------------------------------------------- /assets/actionbar_tag.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 38 | 40 | 41 | 43 | image/svg+xml 44 | 46 | 47 | 48 | 49 | 50 | 55 | 59 | 64 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /assets/app_icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 27 | 32 | 37 | 43 | 45 | 48 | 51 | 52 | 53 | 54 | 72 | 74 | 75 | 77 | image/svg+xml 78 | 80 | 81 | 82 | 83 | 84 | 89 | 99 | AppIcon 116 | 117 | 118 | -------------------------------------------------------------------------------- /assets/tag.9.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 23 | 27 | 31 | 32 | 41 | 42 | 62 | 64 | 65 | 67 | image/svg+xml 68 | 70 | 71 | 72 | 73 | 74 | 79 | 84 | 85 | 105 | 106 | -------------------------------------------------------------------------------- /gen9patch.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | 3 | import os 4 | import sys 5 | from xml.etree.ElementTree import ElementTree 6 | import subprocess 7 | from PIL import Image 8 | import platform 9 | 10 | document = ElementTree() 11 | 12 | def toBlackOrTransparent(color): 13 | if color[3] == 0: 14 | return (0,0,0,0) 15 | else: 16 | return (0,0,0,255) 17 | 18 | def create9PatchSvg(file): 19 | document.parse(file) 20 | root = document.getroot() 21 | for elem in root.iter("{http://www.w3.org/2000/svg}g"): 22 | layerName = elem.get("{http://www.inkscape.org/namespaces/inkscape}label") 23 | layerId = elem.get("id") 24 | if layerName == "9patch" or layerId == "_x39_patch": 25 | elem.set('style', '') 26 | elem.set('display', '') 27 | else: 28 | elem.set('style', 'display:none') 29 | elem.set('display', 'none') 30 | 31 | document.write('./temp/9patch.svg') 32 | 33 | def create9PatchForDpi(file, dpi, name, resourceLocation): 34 | 35 | if platform.system() == "Darwin": 36 | inkscapePath = "/Applications/Inkscape.app/Contents/Resources/bin/inkscape" 37 | else: 38 | inkscapePath = "inkscape" 39 | 40 | subprocess.check_output([inkscapePath,"-d", str(dpi), "-e", "./temp/out.png", "./temp/9patch.svg"]) 41 | 42 | im = Image.open("./temp/out.png") 43 | pix = im.load() 44 | newSize = (im.size[0] +2 , im.size[1] +2) 45 | nim = Image.new("RGBA", newSize, (255, 255, 255, 0)) 46 | npix = nim.load() 47 | 48 | for x in range(0, im.size[0]): 49 | data = toBlackOrTransparent(pix[x,0]) 50 | npix[x+1, 0] = data 51 | data = toBlackOrTransparent(pix[x, im.size[1]-1]) 52 | npix[x+1, newSize[1]-1] = data 53 | 54 | for y in range(0, im.size[1]): 55 | data = toBlackOrTransparent(pix[0, y]) 56 | npix[0, y+1] = data 57 | data = toBlackOrTransparent(pix[im.size[0]-1, y]) 58 | npix[newSize[0]-1, y+1] = data 59 | 60 | subprocess.check_output([inkscapePath,"-d", str(dpi), "-e", "./temp/out.png", file]) 61 | 62 | im = Image.open("./temp/out.png") 63 | nim.paste(im, (1,1)) 64 | 65 | filename = os.path.split(file)[1] 66 | filename = filename.replace(".svg", ".png") 67 | 68 | nim.save(resourceLocation + "/res/drawable-" + name + "/" + filename) 69 | 70 | dir = "./temp" 71 | if not os.path.exists(dir): 72 | os.makedirs(dir) 73 | 74 | create9PatchSvg(sys.argv[1]); 75 | for (dpi, name) in [(320, "xhdpi"), (240, "hdpi"), (160, "mdpi"), (120, "ldpi")]: 76 | create9PatchForDpi(sys.argv[1], dpi, name, sys.argv[2]) 77 | 78 | -------------------------------------------------------------------------------- /process_assets: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #Requires Inkscape to be installed. 4 | 5 | #This script scales and creates images at the correct dpi level for Android. 6 | #When creating svg files set the image size to the size that you want your mdpi images to be. 7 | 8 | 9 | function processImage { 10 | file=$(basename $1) 11 | 12 | case $OSTYPE in 13 | darwin*) 14 | inkscape="/Applications/Inkscape.app/Contents/Resources/bin/inkscape" 15 | ;; 16 | *) 17 | inkscape="inkscape" 18 | ;; 19 | esac 20 | 21 | mkdir -p $2/res/drawable-xxxhdpi 22 | mkdir -p $2/res/drawable-xxhdpi 23 | mkdir -p $2/res/drawable-xhdpi 24 | mkdir -p $2/res/drawable-hdpi 25 | mkdir -p $2/res/drawable-mdpi 26 | mkdir -p $2/res/drawable-ldpi 27 | 28 | $inkscape -d 640 -e $2/res/drawable-xxxhdpi/${file/.svg}.png $1 >& /dev/null 29 | $inkscape -d 480 -e $2/res/drawable-xxhdpi/${file/.svg}.png $1 >& /dev/null 30 | $inkscape -d 320 -e $2/res/drawable-xhdpi/${file/.svg}.png $1 >& /dev/null 31 | $inkscape -d 240 -e $2/res/drawable-hdpi/${file/.svg}.png $1 >& /dev/null 32 | $inkscape -d 160 -e $2/res/drawable-mdpi/${file/.svg}.png $1 >& /dev/null 33 | $inkscape -d 120 -e $2/res/drawable-ldpi/${file/.svg}.png $1 >& /dev/null 34 | } 35 | 36 | function displayHelp { 37 | 38 | echo "-h Help" 39 | echo "-f Force processing of all files" 40 | echo "process_assets ./source_image_folder/ ./output_folder" 41 | exit 42 | } 43 | 44 | while getopts ":fh" opt; do 45 | case $opt in 46 | f) 47 | rm ./lastprocess 48 | ;; 49 | h) 50 | displayHelp 51 | ;; 52 | \?) 53 | echo "Invalid option: -$OPTARG" >&2 54 | displayHelp 55 | exit 56 | ;; 57 | esac 58 | done 59 | 60 | if [ $# -ne 2 ] 61 | then 62 | echo "Incorrect number of args." >&2 63 | displayHelp 64 | exit 65 | fi 66 | 67 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 68 | 69 | if [ ! -e ./lastprocess ] 70 | then 71 | touch -t 197001010100.00 ./lastprocess 72 | fi 73 | 74 | for f in $(find $1 -name *.svg -type f -newer ./lastprocess) ; 75 | do 76 | echo "Processing $f" 77 | if [[ "$f" == *.9.svg* ]]; then 78 | $DIR/gen9patch.py $f $2 79 | else 80 | processImage $f $2 81 | fi 82 | done 83 | 84 | touch -t $(date +%Y%m%d%H%M.%S) ./lastprocess 85 | 86 | -------------------------------------------------------------------------------- /res/drawable-hdpi/actionbar_tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDRBoxman/Android-SVG-Asset-Generator/30ffa7f62f7af1dc2d12ddcb3f082c26e2501d0a/res/drawable-hdpi/actionbar_tag.png -------------------------------------------------------------------------------- /res/drawable-hdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDRBoxman/Android-SVG-Asset-Generator/30ffa7f62f7af1dc2d12ddcb3f082c26e2501d0a/res/drawable-hdpi/app_icon.png -------------------------------------------------------------------------------- /res/drawable-hdpi/tag.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDRBoxman/Android-SVG-Asset-Generator/30ffa7f62f7af1dc2d12ddcb3f082c26e2501d0a/res/drawable-hdpi/tag.9.png -------------------------------------------------------------------------------- /res/drawable-ldpi/actionbar_tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDRBoxman/Android-SVG-Asset-Generator/30ffa7f62f7af1dc2d12ddcb3f082c26e2501d0a/res/drawable-ldpi/actionbar_tag.png -------------------------------------------------------------------------------- /res/drawable-ldpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDRBoxman/Android-SVG-Asset-Generator/30ffa7f62f7af1dc2d12ddcb3f082c26e2501d0a/res/drawable-ldpi/app_icon.png -------------------------------------------------------------------------------- /res/drawable-ldpi/tag.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDRBoxman/Android-SVG-Asset-Generator/30ffa7f62f7af1dc2d12ddcb3f082c26e2501d0a/res/drawable-ldpi/tag.9.png -------------------------------------------------------------------------------- /res/drawable-mdpi/actionbar_tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDRBoxman/Android-SVG-Asset-Generator/30ffa7f62f7af1dc2d12ddcb3f082c26e2501d0a/res/drawable-mdpi/actionbar_tag.png -------------------------------------------------------------------------------- /res/drawable-mdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDRBoxman/Android-SVG-Asset-Generator/30ffa7f62f7af1dc2d12ddcb3f082c26e2501d0a/res/drawable-mdpi/app_icon.png -------------------------------------------------------------------------------- /res/drawable-mdpi/tag.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDRBoxman/Android-SVG-Asset-Generator/30ffa7f62f7af1dc2d12ddcb3f082c26e2501d0a/res/drawable-mdpi/tag.9.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/actionbar_tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDRBoxman/Android-SVG-Asset-Generator/30ffa7f62f7af1dc2d12ddcb3f082c26e2501d0a/res/drawable-xhdpi/actionbar_tag.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDRBoxman/Android-SVG-Asset-Generator/30ffa7f62f7af1dc2d12ddcb3f082c26e2501d0a/res/drawable-xhdpi/app_icon.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/tag.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDRBoxman/Android-SVG-Asset-Generator/30ffa7f62f7af1dc2d12ddcb3f082c26e2501d0a/res/drawable-xhdpi/tag.9.png --------------------------------------------------------------------------------