├── 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 |
--------------------------------------------------------------------------------