pax_global_header00006660000000000000000000000064125170414260014514gustar00rootroot0000000000000052 comment=4649c46c9b759559c6d967264d5c2cf4d981f4c8 svgtune-0.2.0/000077500000000000000000000000001251704142600132065ustar00rootroot00000000000000svgtune-0.2.0/.gitignore000066400000000000000000000000121251704142600151670ustar00rootroot00000000000000svgtune.1 svgtune-0.2.0/Makefile000066400000000000000000000005231251704142600146460ustar00rootroot00000000000000MANPAGE=svgtune.1 all: $(MANPAGE) %.1: % help2man -n "Tune and generate multiple SVGs out of source SVG" \ -s 1 -N -o $@ ./$< install: $(MANPAGE) mkdir -p $(PREFIX)/usr/bin mkdir -p $(PREFIX)/usr/share/man/man1 install -t $(PREFIX)/usr/bin/ svgtune install -t $(PREFIX)/usr/share/man/man1/ $(MANPAGE) clean: rm -f $(MANPAGE) svgtune-0.2.0/README.rst000066400000000000000000000041161251704142600146770ustar00rootroot00000000000000.. -*- mode: rst; fill-column: 78; indent-tabs-mode: nil -*- .. ex: set sts=4 ts=4 sw=4 et tw=79: =========== DESCRIPTION =========== svgtune is just a little helper to generate a set of .svg files out of a single .svg file, by tuning respective groups/layers visibility, transparency or anything else. It might come very handy for generation of incremental figures to be embedded into the presentation. For the input, this takes a single command line parameter -- file with instructions, which might look like:: # Load the file we should use %file somefigure.svg # Make all layers invisible layers style=display:none # Store current state into 0_blank file %save 0_blank layer label=elements style=display:inline %save 1 layer id=layer2 style=display:inline %save 1_3 # Lets make some group visible g id=g14546 style=display:inline %save 1_3 You can match elements either by 'id' or 'label', or use python regular expressions to match sets of those:: layer label:re=^base.* style=display:inline ============ COMMAND LINE ============ See ./svtune --help for more information on command line parameters. ====== OUTPUT ====== For the results, look under somefigure_tuned/ directory. ======= Helpers ======= You might make advantage of having following in your Makefile, so you get automatic 'tuning' and rendering of 'pdf' and 'eps' of your .svg's for easy embedding them into your publications:: all:: pics # For every .svg we must have a pdf PICS=$(shell find . -iname \*svg | sed -e 's/svg/pdf/g') SVGIS=$(shell /bin/ls *.svgtune | sed -e 's/.svgtune/_tuned/g') FMAKE := $(MAKE) -s -f $(lastword $(MAKEFILE_LIST)) pics: $(SVGIS) $(PICS) clean:: for p in *.svg; do rm -f $${p%*.svg}.{pdf,eps}; done ignore-%: @grep -q "^$*$$" .gitignore || { \ echo "$*" >> .gitignore; echo "Ignore $@"; } %_tuned: %.svgtune ignore-%_tuned @echo "Tuning SVG using $<" @svgtune $< %.pdf: %.svg ignore-%.pdf @echo "Rendering $@" @inkscape -z -f "$<" -A "$@" %.eps: %.svg ignore-%.eps @echo "Rendering $@" @inkscape -z -T -f "$<" -E "$@" svgtune-0.2.0/svgtune000077500000000000000000000240371251704142600146350ustar00rootroot00000000000000#!/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 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, 2, 0) __copyright__ = 'Copyright (c) 2009-2015 Yaroslav Halchenko' __license__ = 'GPL' import os, re from lxml import etree from optparse import OptionParser # Setup rudimentary logging import logging lgr = logging.getLogger("svgtune") lgr.addHandler(logging.StreamHandler()) 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: lgr.info(" "*(level-1) + 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] value = t[1] if len(t) > 1 else None if option in ["previews", "nosvgs"]: # if only said "previews" assume that we want them if value is None: value = True setattr(options, option, 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 def parse_cmdline(): 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") parser.add_option("--no-svgs", action="store_true", dest="nosvgs", help="Remove 'final' svgs") (options_, args) = parser.parse_args() if len(args) != 1: raise SystemExit( "Error: Please provide single input file with instructions.") lgr.setLevel(logging.DEBUG if options_.verbose >= 4 else logging.INFO) return args[0], options_ def svgtune(ifile, options): svg = None dname = None for line_ in open(ifile).readlines(): line = line_.strip() if line.startswith('#') or line == '': continue if line.rstrip() == '%exit': break # 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)) elif cmd == '%save': ofile = '%s/%s.svg' % (dname, params) verbose(1, "Storing into %s" % ofile) with file(ofile, 'w') as f: f.write(etree.tostring(svgdoc, pretty_print=True)) if options.previews: verbose(3, "Generating preview") os.system('inkscape -z -f %s -e %s -d 300 >/dev/null 2>&1' % (ofile, ofile.replace('.svg', '_preview.png'))) if options.nosvgs: verbose(4, "Removing generated svg") os.remove(ofile) 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', 'any']: # parse out first element identifier, changes = split2(params, "For each layer or g you must list id or label + changes") # determine the victims sid = '' id1, id2 = identifier.split('=') sid_re_attr = None # either we need to do re.search sid_re_str = id2 if cmd == 'layer': sid += '[@{%s}groupmode="layer"]' % svg.nsmap['inkscape'] elem = 'g' elif cmd == 'any': # will be custom anyways pass else: elem = cmd 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'] lgr.debug(svg.nsmap['xlink']) lgr.debug("sid=%r" % 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)) if cmd != 'any': victims = svg.findall('.//{%s}%s%s' % (svg.nsmap['svg'], elem, sid)) lgr.debug("Found %d victims searching for %r" % (len(victims), sid)) else: victims = svg.findall('.//') lgr.debug("Found %d overall victims" % len(victims)) if not id1.endswith(':re'): victims = filter(lambda v: v.attrib.get(id1, None) == id2, victims) lgr.debug("After selection based on %s was left with " "%d victims" % (id1, len(victims))) # optionally perform search using re if sid_re_attr is not None: regexp = re.compile(sid_re_str) victims_orig = victims # for debugging victims = [v for v in victims if regexp.search(v.attrib.get(sid_re_attr, ""))] lgr.debug("Left with %d after matching regexp %r" % (len(victims), sid_re_str)) nvictims = len(victims) if nvictims == 0: raise ValueError("Cannot find any victim for '%s'" % identifier) elif nvictims > 1 and not sid_re_attr: 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) if __name__ == "__main__": ifile, options = parse_cmdline() svgtune(ifile, options)