├── README.md ├── android_export.inx └── android_export.py /README.md: -------------------------------------------------------------------------------- 1 | inkscape-android-export 2 | ======== 3 | 4 | This Inkscape extension exports all selected items in different densities. The exported PNGs will be named by their ID in the SVG. 5 | 6 | Install 7 | -------- 8 | 9 | Download `android_export.inx` and `android_export.py` and copy both files to your `$HOME/.config/inkscape/extensions` folder. A restart of Inkscape is required. 10 | 11 | Usage 12 | -------- 13 | 14 | * Select at least one item to export 15 | * Select `Extensions -> Export -> Android Export…` 16 | * Customize the settings according to your needs 17 | 18 | License 19 | ======== 20 | 21 | Copyright 2013 Christian Becker 22 | 23 | Licensed under the Apache License, Version 2.0 (the "License"); 24 | you may not use this file except in compliance with the License. 25 | You may obtain a copy of the License at 26 | 27 | http://www.apache.org/licenses/LICENSE-2.0 28 | 29 | Unless required by applicable law or agreed to in writing, software 30 | distributed under the License is distributed on an "AS IS" BASIS, 31 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 32 | See the License for the specific language governing permissions and 33 | limitations under the License. 34 | -------------------------------------------------------------------------------- /android_export.inx: -------------------------------------------------------------------------------- 1 | 2 | 3 | <_name>Android Export 4 | de.cbecker.android_export 5 | 6 | android_export.py 7 | 8 | 9 | 10 | <_param name="title" type="description">Exports all currently selected items in different densities. If there are multiple selections, the exported PNGs will be named by their ID in the SVG, so make sure that all items have reasonable IDs. 11 | true 12 | false 13 | 1 14 | 15 | 16 | <_param name="title" type="description">Exports the entire page in different densities. 17 | 18 | 19 | 20 | 21 | 22 | false 23 | false 24 | true 25 | true 26 | true 27 | true 28 | true 29 | false 30 | true 31 | 32 | 33 | all 34 | 35 | 36 | 37 | 38 | 39 | 42 | 43 | -------------------------------------------------------------------------------- /android_export.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # ***** BEGIN APACHE LICENSE BLOCK ***** 4 | # 5 | # Copyright 2013 Christian Becker 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | # ***** END APACHE LICENCE BLOCK ***** 20 | 21 | import optparse 22 | import os 23 | import subprocess 24 | import sys 25 | from copy import copy 26 | 27 | try: 28 | from subprocess import DEVNULL 29 | except ImportError: 30 | DEVNULL = open(os.devnull, 'w') 31 | 32 | def checkForPath(command): 33 | return 0 == subprocess.call([ 34 | command, "--version" 35 | ], stdout=DEVNULL, stderr=subprocess.STDOUT) 36 | 37 | def error(msg): 38 | sys.stderr.write((unicode(msg) + "\n").encode("UTF-8")) 39 | sys.exit(1) 40 | 41 | def export(svg, options): 42 | for qualifier, dpi in options.densities: 43 | export_density(svg, options, qualifier, dpi) 44 | 45 | def export_density(svg, options, qualifier, dpi): 46 | dir_type = "drawable" 47 | if options.launcher_icon: 48 | dir_type = "mipmap" 49 | 50 | dir = "%s/%s-%s" % (options.resdir, dir_type, qualifier) 51 | 52 | if not os.path.exists(dir): 53 | os.makedirs(dir) 54 | 55 | def export_resource(params, name): 56 | png = "%s/%s.png" % (dir, name) 57 | 58 | call_params = ["inkscape", 59 | "--without-gui", 60 | "--export-dpi=%s" % dpi, 61 | "--export-png=%s" % png] 62 | 63 | if isinstance(params, list): 64 | call_params.extend(params) 65 | else: 66 | call_params.append(params) 67 | 68 | call_params.append(svg) 69 | 70 | subprocess.check_call(call_params, stdout=DEVNULL, stderr=subprocess.STDOUT) 71 | 72 | if options.strip: 73 | subprocess.check_call([ 74 | "convert", "-antialias", "-strip", png, png 75 | ], stdout=DEVNULL, stderr=subprocess.STDOUT) 76 | if options.optimize: 77 | subprocess.check_call([ 78 | "optipng", "-quiet", "-o7", png 79 | ], stdout=DEVNULL, stderr=subprocess.STDOUT) 80 | 81 | if options.source == '"selected_ids"': 82 | if options.scale and (options.scale > 0): 83 | dpi *= options.scale 84 | 85 | params = create_selection_params(options) 86 | 87 | for id in options.ids: 88 | current_params = ["--export-id=%s" % id] 89 | current_params.extend(params) 90 | 91 | filename = get_selection_filename(id, options) 92 | 93 | export_resource(current_params, filename) 94 | 95 | else: 96 | export_resource("--export-area-page", options.resname) 97 | 98 | 99 | def create_selection_params(options): 100 | params = [] 101 | if options.only_selected: 102 | params.append("--export-id-only") 103 | if options.transparent_background: 104 | params.append("-y 0") 105 | return params 106 | 107 | 108 | def get_selection_filename(id, options): 109 | if len(options.ids) == 1 and options.resname: 110 | return options.resname 111 | 112 | return id 113 | 114 | 115 | def check_boolstr(option, opt, value): 116 | value = value.capitalize() 117 | if value == "True": 118 | return True 119 | if value == "False": 120 | return False 121 | raise optparse.OptionValueError("option %s: invalid boolean value: %s" % (opt, value)) 122 | 123 | class Option(optparse.Option): 124 | TYPES = optparse.Option.TYPES + ("boolstr",) 125 | TYPE_CHECKER = copy(optparse.Option.TYPE_CHECKER) 126 | TYPE_CHECKER["boolstr"] = check_boolstr 127 | 128 | def append_density(option, opt_str, value, parser, *density): 129 | if not value: 130 | return 131 | if getattr(parser.values, option.dest) is None: 132 | setattr(parser.values, option.dest, []) 133 | getattr(parser.values, option.dest).append(density) 134 | 135 | class DensityGroup(optparse.OptionGroup): 136 | def add_density_option(self, name, dpi): 137 | self.add_option("--%s" % name, action="callback", type="boolstr", dest="densities", metavar="BOOL", 138 | callback=append_density, callback_args=(name, dpi), help="Export %s variants" % name.upper()) 139 | 140 | parser = optparse.OptionParser(usage="usage: %prog [options] SVGfile", option_class=Option) 141 | parser.add_option("--source", action="store", type="choice", choices=('"selected_ids"', '"page"'), help="Source of the drawable") 142 | parser.add_option("--id", action="append", dest="ids", metavar="ID", help="ID attribute of objects to export, can be specified multiple times") 143 | parser.add_option("--resdir", action="store", help="Resources directory") 144 | parser.add_option("--resname", action="store", help="Resource name (when --source=page)") 145 | parser.add_option("--launcher-icon", action="store", type="boolstr", help="Whether the icon is a launcher icon") 146 | parser.add_option("--only-selected", action="store", type="boolstr", help="Export only selected (without any background or other elements)") 147 | parser.add_option("--scale", action="store", type="float", help="Output image scale") 148 | parser.add_option("--transparent-background", action="store", type="boolstr", help="Transparent background") 149 | 150 | group = DensityGroup(parser, "Select which densities to export") 151 | group.add_density_option("ldpi", 67.5) 152 | group.add_density_option("mdpi", 90) 153 | group.add_density_option("hdpi", 135) 154 | group.add_density_option("xhdpi", 180) 155 | group.add_density_option("xxhdpi", 270) 156 | group.add_density_option("xxxhdpi", 360) 157 | parser.add_option_group(group) 158 | 159 | parser.add_option("--strip", action="store", type="boolstr", help="Use ImageMagick to reduce the image size") 160 | parser.add_option("--optimize", action="store", type="boolstr", help="Use OptiPNG to reduce the image size") 161 | 162 | (options, args) = parser.parse_args() 163 | if len(args) != 1: 164 | parser.error("Expected exactly one argument, got %d" % len(args)) 165 | svg = args[0] 166 | 167 | if options.resdir is None: 168 | error("No Android Resource directory specified") 169 | if not os.path.isdir(options.resdir): 170 | error("Wrong Android Resource directory specified:\n'%s' is no dir" % options.resdir) 171 | if not os.access(options.resdir, os.W_OK): 172 | error("Wrong Android Resource directory specified:\nCould not write to '%s'" % options.resdir) 173 | if options.source not in ('"selected_ids"', '"page"'): 174 | error("Select what to export (selected items or whole page)") 175 | if options.source == '"selected_ids"' and options.ids is None: 176 | error("Select at least one item to export") 177 | if options.source == '"page"' and not options.resname: 178 | error("Please enter a resource name") 179 | if not options.densities: 180 | error("Select at least one DPI variant to export") 181 | if not checkForPath("inkscape"): 182 | error("Make sure you have 'inkscape' on your PATH") 183 | if options.strip and not checkForPath("convert"): 184 | error("Make sure you have 'convert' on your PATH if you want to reduce the image size using ImageMagick") 185 | if options.optimize and not checkForPath("optipng"): 186 | error("Make sure you have 'optipng' on your PATH if you want to reduce the image size using OptiPNG") 187 | 188 | export(svg, options) 189 | --------------------------------------------------------------------------------