--- /dev/null
+#!/usr/bin/python
+# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
+# vi: set ft=python sts=4 ts=4 sw=4 et:
+"""
+ Yaroslav Halchenko
+ web: http://www.onerussian.com
+ e-mail: yoh@onerussian.com
+
+ DESCRIPTION (NOTES):
+
+ See README.rst shipped with this tool.
+ "Homepage" for the tool is http://github.com/yarikoptic/svgtune
+
+ Copyright (C) 2009, Yaroslav Halchenko
+
+ LICENSE:
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301, USA.
+
+ On Debian system see /usr/share/common-licenses/GPL for the full license.
+"""
+
+__author__ = 'Yaroslav Halchenko'
+__version__ = (0, 1, 0)
+__copyright__ = 'Copyright (c) 2009-2010 Yaroslav Halchenko'
+__license__ = 'GPL'
+
+import os, re
+from optparse import OptionParser
+
+def split2(l, msg, sep=' '):
+ """Split l into 2 pieces using ' ' as a separator
+ """
+ p = [x.strip() for x in (l.split(sep, 1)) if x != '']
+
+ if len(p) != 2:
+ raise ValueError, msg + '. Got %s for "%s"' % (str(p), l)
+
+ return p
+
+
+def replace_key(attr_value, key, value):
+ """Replace value for a given key
+ If key is not found, new one is added
+ """
+ v = attr_value.split(';')
+ changed = False
+ newval = '%s:%s' % (key, value)
+ for i in xrange(len(v)):
+ if v[i].startswith(key + ':'):
+ v[i] = newval
+ changed = True
+ if not changed:
+ # Wasn't found -- add one
+ v += [newval]
+ return ';'.join([x for x in v if x.strip() != ''])
+
+
+def change_attr(el, attr, values):
+ """Change values listed within attribute attr
+ """
+ v = el.attrib.get(attr, '')
+ changed = False
+ for value in values.split(';'):
+ k, newv = split2(value, "Each value must be in the form x:y", ":")
+ v = replace_key(v, k, newv)
+ if v == '': # there were no such yet
+ v = "%s:%s" % (k, newv)
+ #print "Changing %s : %s, got %s" % (attr, values, str(v))
+ el.attrib[attr] = v
+
+def verbose(level, str_):
+ if level <= options.verbose:
+ print " "*level, str_
+
+def version():
+ print "svgtune v" + ".".join(__version__)
+ raise SystemExit
+
+def process_options(params, options):
+ # For now simply split by space
+ option_values = params.split(' ')
+ for option_value in option_values:
+ t = option_value.split('=', 1)
+ option = t[0]
+ if len(t) > 1: value = t[1]
+ else: value = None
+ if option == "previews":
+ # if only said "previews" assume that we want them
+ if value is None: value = True
+ options.previews = bool(value)
+ else:
+ raise ValueError, "Unknown option %s", option
+ pass
+
+
+def load_svg(svgfile):
+ verbose(1, "Processing file %s for tuning" % svgfile)
+ svgdoc = etree.parse(svgfile)
+ svg = svgdoc.getroot()
+
+ dname = '%s_tuned' % os.path.splitext(svgfile)[0]
+ try:
+ os.mkdir(dname)
+ except:
+ pass # if directory exists or hell with everything
+ return svg, svgdoc, dname
+
+parser = OptionParser(usage="%prog [options] inputfile.svgtune",
+ version="%prog version "
+ + ".".join([str(i) for i in __version__]))
+parser.add_option("-v", action="count", dest="verbose",
+ help="Increase verbosity with multiple -v")
+parser.add_option("-p", "--previews", action="store_true",
+ dest="previews", help="Store preview png's")
+
+(options, args) = parser.parse_args()
+
+if len(args) != 1:
+ raise SystemExit, \
+ "Error: Please provide single input file with instructions."
+
+# Now we can load lxml
+from lxml import etree
+
+ifile = args[0]
+# ifile = '/tmp/1.svgi'
+svgfile = None
+svg = None
+dname = None
+
+for line_ in open(ifile).readlines():
+ line = line_.strip()
+ if line.startswith('#') or line == '':
+ continue
+
+ # parse out first element
+ cmd, params = split2(line, "Each line must be 'command parameters'")
+
+ if cmd == '%file':
+ svg, svgdoc, dname = load_svg(params)
+ continue
+ elif cmd == "%options":
+ process_options(params, options)
+ continue
+
+ # We must have file loaded by now
+ if svg is None:
+ svgfile = os.path.splitext(ifile)[0] + '.svg'
+ try:
+ svg, svgdoc, dname = load_svg(svgfile)
+ except Exception, e:
+ raise RuntimeError, \
+ "Tried to load from %s but failed due to %s.\n" \
+ "Please provide %%file directive to load the file prior %s " \
+ "or have .svg file with the same name as .svgtune." \
+ % (svgfile, e, cmd)
+
+ if cmd == '%save':
+ ofile = '%s/%s.svg' % (dname, params)
+ verbose(1, " Storing into %s" % ofile)
+ file(ofile, 'w').write(
+ etree.tostring(svgdoc, pretty_print=True))
+ if options.previews:
+ verbose(3, "Generating preview")
+ os.system('inkscape -z -f %s -e %s -d 90 >/dev/null 2>&1' %
+ (ofile, ofile.replace('.svg', '_preview.png')))
+ continue
+
+ #
+ # Figure out the victims for changes -- victims
+ victims = None
+ if cmd == 'layers':
+ changes = params
+ victims = svg.findall('.//{%s}g[@{%s}groupmode="layer"]'
+ % (svg.nsmap['svg'], svg.nsmap['inkscape']))
+ elif cmd in ['layer', 'g', 'text']:
+ # parse out first element
+ identifier, changes = split2(params,
+ "For each layer or g you must list id or label + changes")
+
+ # determine the victims
+ sid = ''
+ if cmd == 'layer':
+ sid += '[@{%s}groupmode="layer"]' % svg.nsmap['inkscape']
+
+ id1, id2 = identifier.split('=')
+ sid_re_attr = None # either we need to do re.search
+ sid_re_str = id2
+ if id1 == 'label':
+ sid += '[@{%s}label="%s"]' % (svg.nsmap['inkscape'], id2)
+ elif id1 == 'id':
+ sid += '[@id="%s"]' % (id2)
+ elif id1 == 'href':
+ sid += '[@{%s}href="%s"]' % (svg.nsmap['xlink'], id2)
+ elif id1 == 'href:re':
+ sid += '{%s}href' % svg.nsmap['xlink']
+ print svg.nsmap['xlink']
+ print sid
+ elif id1 == 'label:re':
+ sid_re_attr = '{%s}label' % svg.nsmap['inkscape']
+ elif id1 == 'id:re':
+ sid_re_attr = 'id'
+ else:
+ raise ValueError, "Unknown identifier %s in %s" % (id1, line)
+
+ victims = svg.findall('.//{%s}g%s' % (svg.nsmap['svg'], sid))
+
+ # optionally perform search using re
+ if sid_re_attr is not None:
+ regexp = re.compile(sid_re_str)
+ victims = [v for v in victims
+ if regexp.search(v.attrib[sid_re_attr])]
+
+ nvictims = len(victims)
+ if nvictims == 0:
+ raise ValueError, "Cannot find any victim for '%s'" % identifier
+ elif nvictims > 1 and sid_re_attr is None:
+ raise ValueError, "We should get a single victim for %s " \
+ "but got %d of them" % (identifier, nvictims)
+ else:
+ raise ValueError, "Unknown command '%s'." % cmd
+
+ #
+ # Figure out the actual changes to take and do them
+ for change in changes.split(' '):
+ if change == '':
+ continue
+ attr, values = split2(change,
+ 'Each change should be listed as attribute=value', '=')
+ if attr == 'style':
+ func = lambda vic: change_attr(vic, attr, values)
+ else:
+ raise ValueError, "Don't yet handle attribute %s" % attr
+
+ for victim in victims:
+ func(victim)
+