#!/usr/bin/env python3
#! coding: utf-8
from __future__ import print_function
import argparse
import xml.dom.minidom
import re
from generate_utils import OutFileGreen as OutFile
p = argparse.ArgumentParser(description='''
In a svg file x.svg, look for every layer and creates x.state-{i}.svg
for all detected states.
The layers names must have set-pattern like
{1} (include me in state 1)
or {1,4,5} (in states 1 4 and 5)
or {7,8,a} (in states 7 8 and a)
or {1-5} (in states 1,2,3,4,5)
or {hello-x, world} (in states "hello-w" and "world")
''')
'''
Idea:
or {*} include in all slides
or {a*} include in all slides beginning with a
or {*a} include in all slides ending with a
or {a, b, c, 1-5, d*} in slides a, b, c from 1 to 5 and beginning with d
elif '*' in I:
L = I.split('*')
M = re.compile('.*'.join(map(re.escape, L)))
that regex will be used as an alternative way for the layer to be inserted in the slide, based on the slide name
but when done that way, each slide must be declared in some layer because the regex represent an infinite number of slide
Idea: {a1-5} to have {a1,a2,a3,a4,a5}
'''
p.add_argument('svg_file', nargs='+')
g = p.add_mutually_exclusive_group()
g.add_argument('--state-in-filename', action='store_true', help='Filenames will be x.state-{i}.svg and not x.{i}.svg (@deprecated)')
g.add_argument('--remove-state-in-filename', action='store_true', help='Forces --state-in-filename to be False (@deprecated)')
g.add_argument('-v', '--verbose', action='store_true')
g = p.add_mutually_exclusive_group()
g.add_argument('--numeric-layers', action='store_true', help='''
Overrides the default behaviour and create one svg per layer, each svg is numeric 1 2 3 ...''')
g.add_argument('--layer-name', action='store_true', help='''
Overrides the default behaviour and create one svg per layer, each svg has the name of the layer''')
a = args = p.parse_args()
if args.remove_state_in_filename:
args.state_in_filename = False
# for x in range(150): print(x, '\033[' + str(x) + 'm', 'Hel', '\033[0m')
def print_warning(*args): # orange
print('\033[33m' + 'Warning:', *(args + ('\033[0m',)))
def print_info(*args): # green
print('\033[32m' + 'Info:', *(args + ('\033[0m',)))
def print_error(*args): # error
print('\033[31m' + 'Error:', *(args + ('\033[0m',)))
for svg_file in args.svg_file:
if not svg_file.endswith('.svg'):
print_error('filename {!r} is not .svg'.format(svg_file))
continue
svg_filename = svg_file[:-4]
with open(svg_file) as f:
doc = xml.dom.minidom.parse(f)
root = doc.documentElement
def set_union(iterable):
it = iter(iterable)
S1 = next(it, set())
return set.union(S1, *it)
INK = 'http://www.inkscape.org/namespaces/inkscape'
layers = [x for x in root.childNodes
if getattr(x, 'tagName', None) == 'g'
if x.getAttributeNS(INK, 'groupmode') == 'layer']
layer_names = {layer: layer.getAttributeNS(INK, 'label').strip() for layer in layers}
R = re.compile('''
\{(
\d+ (- \d+)?
((\s+|\s*,\s*) \d+ (- \d+)?)*
(\s+|\s*,\s*)?
)\}''', re.X)
all_infos = []
nexts = {}
for i,layer in enumerate(layers):
nexts[layer] = layer.nextSibling
if args.numeric_layers:
all_infos.append((layer, {str(i+1)}))
continue
l = layer_names[layer]
if args.layer_name:
all_infos.append((layer, {l}))
continue
m = re.search('\{([^{}]*)\}', l)
if not m:
print_info('Layer {!r} on every slide'.format(l))
continue
S = set_where_layer_is_in = set()
for I in (s.strip() for s in m.group(1).split(',')):
if re.match('\d+\s*-\s*\d+', I):
a,b = tuple(map(int, I.split('-')))
S |= set(map(str, range(a,b+1)))
else:
S.add(I)
all_infos.append((layer, S))
# all_infos : {
# DOM Element g[groupmode=layer]: {'1', '5', '6', '7', 'a'},
# DOM Element g[groupmode=layer]: {'a'},
# }
# first g will be include in slides 1 5 6 7 a
#
# other slides non having S(...) infos will always be included
if not all_infos:
print_warning('In', svg_file, 'Nothing to do, add {tags} on your layers')
continue
slides = set_union(info for layer, info in all_infos)
Format = ("{}.state-{}.svg" if args.state_in_filename else
"{}.{}.svg" )
for layer in layers: # should only be with the layers with {} tag
layer.setAttribute('style', layer.getAttribute('style').replace('display:none', 'display:inline').replace('display: none', 'display: inline'))
for slide in slides:
if args.verbose:
bits = ['Slide {!r}'.format(slide)]
new = OutFile(Format.format(svg_filename, slide))
with new as f:
for layer, info in all_infos:
if args.verbose:
bits.append(str((slide, layer_names[layer], slide in info)))
if slide not in info:
root.removeChild(layer)
f.write(root.toxml())
for layer, info in reversed(all_infos):
if slide not in info:
root.insertBefore(layer, nexts[layer])
if args.verbose:
print_info(', '.join(bits))