From 1507edb6b7b99d844a3f4e0fe12e2007cbb2c1b6 Mon Sep 17 00:00:00 2001 From: Don Armstrong Date: Wed, 27 Feb 2013 11:45:43 -0800 Subject: [PATCH] add svgtune --- svgtune | 251 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 251 insertions(+) create mode 100755 svgtune diff --git a/svgtune b/svgtune new file mode 100755 index 0000000..ea5f635 --- /dev/null +++ b/svgtune @@ -0,0 +1,251 @@ +#!/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) + -- 2.39.5