2 # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
3 # vi: set ft=python sts=4 ts=4 sw=4 et:
6 web: http://www.onerussian.com
7 e-mail: yoh@onerussian.com
11 See README.rst shipped with this tool.
12 "Homepage" for the tool is http://github.com/yarikoptic/svgtune
14 Copyright (C) 2009, Yaroslav Halchenko
18 This program is free software; you can redistribute it and/or modify
19 it under the terms of the GNU General Public License as published by
20 the Free Software Foundation; either version 2 of the License, or
21 (at your option) any later version.
23 This program is distributed in the hope that it will be useful,
24 but WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 GNU General Public License for more details.
28 You should have received a copy of the GNU General Public License
29 along with this program; if not, write to the
30 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
33 On Debian system see /usr/share/common-licenses/GPL for the full license.
36 __author__ = 'Yaroslav Halchenko'
37 __version__ = (0, 1, 0)
38 __copyright__ = 'Copyright (c) 2009-2010 Yaroslav Halchenko'
42 from optparse import OptionParser
44 def split2(l, msg, sep=' '):
45 """Split l into 2 pieces using ' ' as a separator
47 p = [x.strip() for x in (l.split(sep, 1)) if x != '']
50 raise ValueError, msg + '. Got %s for "%s"' % (str(p), l)
55 def replace_key(attr_value, key, value):
56 """Replace value for a given key
57 If key is not found, new one is added
59 v = attr_value.split(';')
61 newval = '%s:%s' % (key, value)
62 for i in xrange(len(v)):
63 if v[i].startswith(key + ':'):
67 # Wasn't found -- add one
69 return ';'.join([x for x in v if x.strip() != ''])
72 def change_attr(el, attr, values):
73 """Change values listed within attribute attr
75 v = el.attrib.get(attr, '')
77 for value in values.split(';'):
78 k, newv = split2(value, "Each value must be in the form x:y", ":")
79 v = replace_key(v, k, newv)
80 if v == '': # there were no such yet
81 v = "%s:%s" % (k, newv)
82 #print "Changing %s : %s, got %s" % (attr, values, str(v))
85 def verbose(level, str_):
86 if level <= options.verbose:
90 print "svgtune v" + ".".join(__version__)
93 def process_options(params, options):
94 # For now simply split by space
95 option_values = params.split(' ')
96 for option_value in option_values:
97 t = option_value.split('=', 1)
99 if len(t) > 1: value = t[1]
101 if option == "previews":
102 # if only said "previews" assume that we want them
103 if value is None: value = True
104 options.previews = bool(value)
106 raise ValueError, "Unknown option %s", option
110 def load_svg(svgfile):
111 verbose(1, "Processing file %s for tuning" % svgfile)
112 svgdoc = etree.parse(svgfile)
113 svg = svgdoc.getroot()
115 dname = '%s_tuned' % os.path.splitext(svgfile)[0]
119 pass # if directory exists or hell with everything
120 return svg, svgdoc, dname
122 parser = OptionParser(usage="%prog [options] inputfile.svgtune",
123 version="%prog version "
124 + ".".join([str(i) for i in __version__]))
125 parser.add_option("-v", action="count", dest="verbose",
126 help="Increase verbosity with multiple -v")
127 parser.add_option("-p", "--previews", action="store_true",
128 dest="previews", help="Store preview png's")
130 (options, args) = parser.parse_args()
134 "Error: Please provide single input file with instructions."
136 # Now we can load lxml
137 from lxml import etree
140 # ifile = '/tmp/1.svgi'
145 for line_ in open(ifile).readlines():
147 if line.startswith('#') or line == '':
150 # parse out first element
151 cmd, params = split2(line, "Each line must be 'command parameters'")
154 svg, svgdoc, dname = load_svg(params)
156 elif cmd == "%options":
157 process_options(params, options)
160 # We must have file loaded by now
162 svgfile = os.path.splitext(ifile)[0] + '.svg'
164 svg, svgdoc, dname = load_svg(svgfile)
166 raise RuntimeError, \
167 "Tried to load from %s but failed due to %s.\n" \
168 "Please provide %%file directive to load the file prior %s " \
169 "or have .svg file with the same name as .svgtune." \
173 ofile = '%s/%s.svg' % (dname, params)
174 verbose(1, " Storing into %s" % ofile)
175 file(ofile, 'w').write(
176 etree.tostring(svgdoc, pretty_print=True))
178 verbose(3, "Generating preview")
179 os.system('inkscape -z -f %s -e %s -d 90 >/dev/null 2>&1' %
180 (ofile, ofile.replace('.svg', '_preview.png')))
184 # Figure out the victims for changes -- victims
188 victims = svg.findall('.//{%s}g[@{%s}groupmode="layer"]'
189 % (svg.nsmap['svg'], svg.nsmap['inkscape']))
190 elif cmd in ['layer', 'g', 'text']:
191 # parse out first element
192 identifier, changes = split2(params,
193 "For each layer or g you must list id or label + changes")
195 # determine the victims
198 sid += '[@{%s}groupmode="layer"]' % svg.nsmap['inkscape']
200 id1, id2 = identifier.split('=')
201 sid_re_attr = None # either we need to do re.search
204 sid += '[@{%s}label="%s"]' % (svg.nsmap['inkscape'], id2)
206 sid += '[@id="%s"]' % (id2)
208 sid += '[@{%s}href="%s"]' % (svg.nsmap['xlink'], id2)
209 elif id1 == 'href:re':
210 sid += '{%s}href' % svg.nsmap['xlink']
211 print svg.nsmap['xlink']
213 elif id1 == 'label:re':
214 sid_re_attr = '{%s}label' % svg.nsmap['inkscape']
218 raise ValueError, "Unknown identifier %s in %s" % (id1, line)
220 victims = svg.findall('.//{%s}g%s' % (svg.nsmap['svg'], sid))
222 # optionally perform search using re
223 if sid_re_attr is not None:
224 regexp = re.compile(sid_re_str)
225 victims = [v for v in victims
226 if regexp.search(v.attrib[sid_re_attr])]
228 nvictims = len(victims)
230 raise ValueError, "Cannot find any victim for '%s'" % identifier
231 elif nvictims > 1 and sid_re_attr is None:
232 raise ValueError, "We should get a single victim for %s " \
233 "but got %d of them" % (identifier, nvictims)
235 raise ValueError, "Unknown command '%s'." % cmd
238 # Figure out the actual changes to take and do them
239 for change in changes.split(' '):
242 attr, values = split2(change,
243 'Each change should be listed as attribute=value', '=')
245 func = lambda vic: change_attr(vic, attr, values)
247 raise ValueError, "Don't yet handle attribute %s" % attr
249 for victim in victims: