Wojciech Zabołotny
2022-10-06 11:57:54 UTC
Drawio (diagrams.net) is a wonderful package enbling creating professional
diagrams.
What was lacking for a long time: https://github.com/jgraph/drawio-desktop/issues/405
, https://github.com/jgraph/drawio-desktop/issues/737 ,
was a possibility to select which layers should be exported to PDF using the
command line tool.
That's essential when one needs to prepare the slides in the beamer, where
in consecutive slides certain objects are appearing or disappering, or
change somehow.
As a workaround, I have prepared a Python script that is able to switch on
and off the layer visibility in a XML file prepared in drawio.
#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.15.2).
# To extract the files from this archive, save it to some FILE, remove
# everything before the '#!/bin/sh' line above, then type 'sh FILE'.
#
lock_dir=_sh16182
# Made on 2022-10-06 13:55 CEST by <***@WZabHP>.
# Source directory was '/tmp/y/wzab-code-lib/drawio-support'.
#
# Existing files will *not* be overwritten, unless '-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 3431 -rwxr-xr-x drawio_select_layers.py
#
MD5SUM=${MD5SUM-md5sum}
f=`${MD5SUM} --version | egrep '^md5sum .*(core|text)utils'`
test -n "${f}" && md5check=true || md5check=false
${md5check} || \
echo 'Note: not verifying md5sums. Consider installing GNU coreutils.'
if test "X$1" = "X-c"
then keep_file=''
else keep_file=true
fi
echo=echo
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=
locale_dir=
set_echo=false
for dir in $PATH
do
if test -f $dir/gettext \
&& ($dir/gettext --version >/dev/null 2>&1)
then
case `$dir/gettext --version 2>&1 | sed 1q` in
*GNU*) gettext_dir=$dir
set_echo=true
break ;;
esac
fi
done
if ${set_echo}
then
set_echo=false
for dir in $PATH
do
if test -f $dir/shar \
&& ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
then
locale_dir=`$dir/shar --print-text-domain-dir`
set_echo=true
break
fi
done
if ${set_echo}
then
TEXTDOMAINDIR=$locale_dir
export TEXTDOMAINDIR
TEXTDOMAIN=sharutils
export TEXTDOMAIN
echo="$gettext_dir/gettext -s"
fi
fi
IFS="$save_IFS"
if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null
then if (echo -n test; echo 1,2,3) | grep n >/dev/null
then shar_n= shar_c='
'
else shar_n=-n shar_c= ; fi
else shar_n= shar_c='\c' ; fi
f=shar-touch.$$
st1=200112312359.59
st2=123123592001.59
st2tr=123123592001.5 # old SysV 14-char limit
st3=1231235901
if touch -am -t ${st1} ${f} >/dev/null 2>&1 && \
test ! -f ${st1} && test -f ${f}; then
shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'
elif touch -am ${st2} ${f} >/dev/null 2>&1 && \
test ! -f ${st2} && test ! -f ${st2tr} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'
elif touch -am ${st3} ${f} >/dev/null 2>&1 && \
test ! -f ${st3} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$2 "$8"'
else
shar_touch=:
echo
${echo} 'WARNING: not restoring timestamps. Consider getting and
installing GNU '\''touch'\'', distributed in GNU coreutils...'
echo
fi
rm -f ${st1} ${st2} ${st2tr} ${st3} ${f}
#
if test ! -d ${lock_dir} ; then :
else ${echo} "lock directory ${lock_dir} exists"
exit 1
fi
if mkdir ${lock_dir}
then ${echo} "x - created lock directory ${lock_dir}."
else ${echo} "x - failed to create lock directory ${lock_dir}."
exit 1
fi
# ============= drawio_select_layers.py ==============
if test -n "${keep_file}" && test -f 'drawio_select_layers.py'
then
${echo} "x - SKIPPING drawio_select_layers.py (file already exists)"
else
${echo} "x - extracting drawio_select_layers.py (text)"
sed 's/^X//' << 'SHAR_EOF' > 'drawio_select_layers.py' &&
#!/usr/bin/python3
"""
This script modifies the visibility of layers in the XML
file with diagram generated by drawio.
X
It works around the problem of lack of a possibility to export
only the selected layers from the CLI version of drawio.
X
Written by Wojciech M. Zabolotny 6.10.2022
(wzab01<at>gmail.com or wojciech.zabolotny<at>pw.edu.pl)
X
The code is published under LGPL V2 license
"""
from lxml import etree as let
import xml.etree.ElementTree as et
import xml.parsers.expat as pe
from io import StringIO
import os
import sys
import shutil
import zlib
import argparse
X
PARSER = argparse.ArgumentParser()
PARSER.add_argument("--layers", help="Selected layers, \"all\", comma separated list of integers or integer ranges like \"0-3,6,7\"", default="all")
PARSER.add_argument("--layer_prefix", help="Layer name prefix", default="Layer_")
PARSER.add_argument("--outfile", help="Output file", default="output.drawio")
PARSER.add_argument("--infile", help="Input file", default="input.drawio")
ARGS = PARSER.parse_args()
X
INFILENAME = ARGS.infile
OUTFILENAME = ARGS.outfile
X
# Find all elements with 'value' starting with the layer prefix.
# Return tuples with the element and the rest of 'value' after the prefix.
def find_layers(el_start):
X res = []
X for el in el_start:
X val = el.get('value')
X if val is not None:
X if val.find(ARGS.layer_prefix) == 0:
X # This is a layer element. Add it, and its name
X # after the prefix to the list.
X res.append((el,val[len(ARGS.layer_prefix):]))
X continue
X # If it is not a layer element, scan its children
X res.extend(find_layers(el))
X return res
X
# Analyse the list of visible layers, and create the list
# of layers that should be visible. Customize this part
# if you want a more sophisticate method for selection
# of layers.
# Now only "all", comma separated list of integers
# or ranges of integers are supported.
X
def build_visible_list(layers):
X if layers == "all":
X return layers
X res = []
X for lay in layers.split(','):
X # Is it a range?
X s = lay.find("-")
X if s > 0:
X # This is a range
X first = int(lay[:s])
X last = int(lay[(s+1):])
X res.extend(range(first,last+1))
X else:
X res.append(int(lay))
X return res
X
def is_visible(layer_tuple,visible_list):
X if visible_list == "all":
X return True
X if int(layer_tuple[1]) in visible_list:
X return True
X
try:
X EL_ROOT = et.fromstring(open(INFILENAME,"r").read())
except et.ParseError as perr:
X # Handle the parsing error
X ROW, COL = perr.position
X print(
X "Parsing error "
X + str(perr.code)
X + "("
X + pe.ErrorString(perr.code)
X + ") in column "
X + str(COL)
X + " of the line "
X + str(ROW)
X + " of the file "
X + INFILENAME
X )
X sys.exit(1)
X
visible_list = build_visible_list(ARGS.layers)
X
layers = find_layers(EL_ROOT)
for layer_tuple in layers:
X if is_visible(layer_tuple,visible_list):
X print("set "+layer_tuple[1]+" to visible")
X layer_tuple[0].attrib['visible']="1"
X else:
X print("set "+layer_tuple[1]+" to invisible")
X layer_tuple[0].attrib['visible']="0"
X
# Now write the modified file
t=et.ElementTree(EL_ROOT)
with open(OUTFILENAME, 'w') as f:
X t.write(f, encoding='unicode')
X
SHAR_EOF
(set 20 22 10 06 12 57 18 'drawio_select_layers.py'
eval "${shar_touch}") && \
chmod 0755 'drawio_select_layers.py'
if test $? -ne 0
then ${echo} "restore of drawio_select_layers.py failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'drawio_select_layers.py': 'MD5 check failed'
) << \SHAR_EOF
2f48cb5d33e1f46dc4b624f12796e916 drawio_select_layers.py
SHAR_EOF
else
test `LC_ALL=C wc -c < 'drawio_select_layers.py'` -ne 3431 && \
${echo} "restoration warning: size of 'drawio_select_layers.py' is not 3431"
fi
fi
if rm -fr ${lock_dir}
then ${echo} "x - removed lock directory ${lock_dir}."
else ${echo} "x - failed to remove lock directory ${lock_dir}."
exit 1
fi
exit 0
diagrams.
What was lacking for a long time: https://github.com/jgraph/drawio-desktop/issues/405
, https://github.com/jgraph/drawio-desktop/issues/737 ,
was a possibility to select which layers should be exported to PDF using the
command line tool.
That's essential when one needs to prepare the slides in the beamer, where
in consecutive slides certain objects are appearing or disappering, or
change somehow.
As a workaround, I have prepared a Python script that is able to switch on
and off the layer visibility in a XML file prepared in drawio.
#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.15.2).
# To extract the files from this archive, save it to some FILE, remove
# everything before the '#!/bin/sh' line above, then type 'sh FILE'.
#
lock_dir=_sh16182
# Made on 2022-10-06 13:55 CEST by <***@WZabHP>.
# Source directory was '/tmp/y/wzab-code-lib/drawio-support'.
#
# Existing files will *not* be overwritten, unless '-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 3431 -rwxr-xr-x drawio_select_layers.py
#
MD5SUM=${MD5SUM-md5sum}
f=`${MD5SUM} --version | egrep '^md5sum .*(core|text)utils'`
test -n "${f}" && md5check=true || md5check=false
${md5check} || \
echo 'Note: not verifying md5sums. Consider installing GNU coreutils.'
if test "X$1" = "X-c"
then keep_file=''
else keep_file=true
fi
echo=echo
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=
locale_dir=
set_echo=false
for dir in $PATH
do
if test -f $dir/gettext \
&& ($dir/gettext --version >/dev/null 2>&1)
then
case `$dir/gettext --version 2>&1 | sed 1q` in
*GNU*) gettext_dir=$dir
set_echo=true
break ;;
esac
fi
done
if ${set_echo}
then
set_echo=false
for dir in $PATH
do
if test -f $dir/shar \
&& ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
then
locale_dir=`$dir/shar --print-text-domain-dir`
set_echo=true
break
fi
done
if ${set_echo}
then
TEXTDOMAINDIR=$locale_dir
export TEXTDOMAINDIR
TEXTDOMAIN=sharutils
export TEXTDOMAIN
echo="$gettext_dir/gettext -s"
fi
fi
IFS="$save_IFS"
if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null
then if (echo -n test; echo 1,2,3) | grep n >/dev/null
then shar_n= shar_c='
'
else shar_n=-n shar_c= ; fi
else shar_n= shar_c='\c' ; fi
f=shar-touch.$$
st1=200112312359.59
st2=123123592001.59
st2tr=123123592001.5 # old SysV 14-char limit
st3=1231235901
if touch -am -t ${st1} ${f} >/dev/null 2>&1 && \
test ! -f ${st1} && test -f ${f}; then
shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'
elif touch -am ${st2} ${f} >/dev/null 2>&1 && \
test ! -f ${st2} && test ! -f ${st2tr} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'
elif touch -am ${st3} ${f} >/dev/null 2>&1 && \
test ! -f ${st3} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$2 "$8"'
else
shar_touch=:
echo
${echo} 'WARNING: not restoring timestamps. Consider getting and
installing GNU '\''touch'\'', distributed in GNU coreutils...'
echo
fi
rm -f ${st1} ${st2} ${st2tr} ${st3} ${f}
#
if test ! -d ${lock_dir} ; then :
else ${echo} "lock directory ${lock_dir} exists"
exit 1
fi
if mkdir ${lock_dir}
then ${echo} "x - created lock directory ${lock_dir}."
else ${echo} "x - failed to create lock directory ${lock_dir}."
exit 1
fi
# ============= drawio_select_layers.py ==============
if test -n "${keep_file}" && test -f 'drawio_select_layers.py'
then
${echo} "x - SKIPPING drawio_select_layers.py (file already exists)"
else
${echo} "x - extracting drawio_select_layers.py (text)"
sed 's/^X//' << 'SHAR_EOF' > 'drawio_select_layers.py' &&
#!/usr/bin/python3
"""
This script modifies the visibility of layers in the XML
file with diagram generated by drawio.
X
It works around the problem of lack of a possibility to export
only the selected layers from the CLI version of drawio.
X
Written by Wojciech M. Zabolotny 6.10.2022
(wzab01<at>gmail.com or wojciech.zabolotny<at>pw.edu.pl)
X
The code is published under LGPL V2 license
"""
from lxml import etree as let
import xml.etree.ElementTree as et
import xml.parsers.expat as pe
from io import StringIO
import os
import sys
import shutil
import zlib
import argparse
X
PARSER = argparse.ArgumentParser()
PARSER.add_argument("--layers", help="Selected layers, \"all\", comma separated list of integers or integer ranges like \"0-3,6,7\"", default="all")
PARSER.add_argument("--layer_prefix", help="Layer name prefix", default="Layer_")
PARSER.add_argument("--outfile", help="Output file", default="output.drawio")
PARSER.add_argument("--infile", help="Input file", default="input.drawio")
ARGS = PARSER.parse_args()
X
INFILENAME = ARGS.infile
OUTFILENAME = ARGS.outfile
X
# Find all elements with 'value' starting with the layer prefix.
# Return tuples with the element and the rest of 'value' after the prefix.
def find_layers(el_start):
X res = []
X for el in el_start:
X val = el.get('value')
X if val is not None:
X if val.find(ARGS.layer_prefix) == 0:
X # This is a layer element. Add it, and its name
X # after the prefix to the list.
X res.append((el,val[len(ARGS.layer_prefix):]))
X continue
X # If it is not a layer element, scan its children
X res.extend(find_layers(el))
X return res
X
# Analyse the list of visible layers, and create the list
# of layers that should be visible. Customize this part
# if you want a more sophisticate method for selection
# of layers.
# Now only "all", comma separated list of integers
# or ranges of integers are supported.
X
def build_visible_list(layers):
X if layers == "all":
X return layers
X res = []
X for lay in layers.split(','):
X # Is it a range?
X s = lay.find("-")
X if s > 0:
X # This is a range
X first = int(lay[:s])
X last = int(lay[(s+1):])
X res.extend(range(first,last+1))
X else:
X res.append(int(lay))
X return res
X
def is_visible(layer_tuple,visible_list):
X if visible_list == "all":
X return True
X if int(layer_tuple[1]) in visible_list:
X return True
X
try:
X EL_ROOT = et.fromstring(open(INFILENAME,"r").read())
except et.ParseError as perr:
X # Handle the parsing error
X ROW, COL = perr.position
X print(
X "Parsing error "
X + str(perr.code)
X + "("
X + pe.ErrorString(perr.code)
X + ") in column "
X + str(COL)
X + " of the line "
X + str(ROW)
X + " of the file "
X + INFILENAME
X )
X sys.exit(1)
X
visible_list = build_visible_list(ARGS.layers)
X
layers = find_layers(EL_ROOT)
for layer_tuple in layers:
X if is_visible(layer_tuple,visible_list):
X print("set "+layer_tuple[1]+" to visible")
X layer_tuple[0].attrib['visible']="1"
X else:
X print("set "+layer_tuple[1]+" to invisible")
X layer_tuple[0].attrib['visible']="0"
X
# Now write the modified file
t=et.ElementTree(EL_ROOT)
with open(OUTFILENAME, 'w') as f:
X t.write(f, encoding='unicode')
X
SHAR_EOF
(set 20 22 10 06 12 57 18 'drawio_select_layers.py'
eval "${shar_touch}") && \
chmod 0755 'drawio_select_layers.py'
if test $? -ne 0
then ${echo} "restore of drawio_select_layers.py failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'drawio_select_layers.py': 'MD5 check failed'
) << \SHAR_EOF
2f48cb5d33e1f46dc4b624f12796e916 drawio_select_layers.py
SHAR_EOF
else
test `LC_ALL=C wc -c < 'drawio_select_layers.py'` -ne 3431 && \
${echo} "restoration warning: size of 'drawio_select_layers.py' is not 3431"
fi
fi
if rm -fr ${lock_dir}
then ${echo} "x - removed lock directory ${lock_dir}."
else ${echo} "x - failed to remove lock directory ${lock_dir}."
exit 1
fi
exit 0