2020-08-13 01:28:19 +02:00
#!/usr/bin/env python3
"""
Extension for InkScape 1.0
2020-08-23 14:08:11 +02:00
This extension parses the selection and will put all elements into one single group . If you have a cluster with lots of groups and elements you will clean up this way ( one top level group , all elements below it ) . If you select a single element or a set of elements you just wrap it like using CTRL + G ( like making a usual group ) . You can also use this extension to filter out unwanted SVG elements at all .
2020-08-13 01:28:19 +02:00
Author : Mario Voigt / FabLab Chemnitz
Mail : mario . voigt @stadtfabrikanten.org
Date : 13.08 .2020
2020-08-30 12:30:35 +02:00
Last Patch : 29.08 .2020
2020-08-13 01:28:19 +02:00
License : GNU GPL v3
"""
import inkex
2020-08-23 14:08:11 +02:00
from lxml import etree
2020-08-13 01:28:19 +02:00
class MigrateGroups ( inkex . Effect ) :
2020-08-30 12:30:35 +02:00
allElements = [ ] #list of all (sub)elements to process within selection
allGroups = [ ] #list of all groups (svg:g and svg:svg items) to delete
allNonMigrates = [ ] #list of all other elements except svg:g and svg:svg to drop while migrating
2020-08-20 02:41:10 +02:00
2020-08-13 01:28:19 +02:00
def __init__ ( self ) :
inkex . Effect . __init__ ( self )
2020-08-23 14:08:11 +02:00
self . arg_parser . add_argument ( " --allitems " , type = inkex . Boolean , default = True )
self . arg_parser . add_argument ( " --droponly " , type = inkex . Boolean , default = False )
self . arg_parser . add_argument ( " --showdroplist " , type = inkex . Boolean , default = False )
self . arg_parser . add_argument ( " --circle " , type = inkex . Boolean , default = True )
self . arg_parser . add_argument ( " --clipPath " , type = inkex . Boolean , default = True )
self . arg_parser . add_argument ( " --defs " , type = inkex . Boolean , default = True )
self . arg_parser . add_argument ( " --ellipse " , type = inkex . Boolean , default = True )
self . arg_parser . add_argument ( " --image " , type = inkex . Boolean , default = True )
self . arg_parser . add_argument ( " --line " , type = inkex . Boolean , default = True )
self . arg_parser . add_argument ( " --path " , type = inkex . Boolean , default = True )
self . arg_parser . add_argument ( " --polyline " , type = inkex . Boolean , default = True )
self . arg_parser . add_argument ( " --polygon " , type = inkex . Boolean , default = True )
self . arg_parser . add_argument ( " --rect " , type = inkex . Boolean , default = True )
#self.arg_parser.add_argument("--sodipodi", type=inkex.Boolean, default=True)
self . arg_parser . add_argument ( " --svg " , type = inkex . Boolean , default = True )
self . arg_parser . add_argument ( " --text " , type = inkex . Boolean , default = True )
self . arg_parser . add_argument ( " --tspan " , type = inkex . Boolean , default = True )
2020-08-30 12:30:35 +02:00
self . arg_parser . add_argument ( " --linearGradient " , type = inkex . Boolean , default = True )
self . arg_parser . add_argument ( " --radialGradient " , type = inkex . Boolean , default = True )
self . arg_parser . add_argument ( " --meshGradient " , type = inkex . Boolean , default = True )
self . arg_parser . add_argument ( " --meshRow " , type = inkex . Boolean , default = True )
self . arg_parser . add_argument ( " --meshPatch " , type = inkex . Boolean , default = True )
2020-08-23 21:09:57 +02:00
self . arg_parser . add_argument ( " --metadata " , type = inkex . Boolean , default = True )
2020-08-23 14:08:11 +02:00
self . arg_parser . add_argument ( " --script " , type = inkex . Boolean , default = True )
self . arg_parser . add_argument ( " --stop " , type = inkex . Boolean , default = True )
self . arg_parser . add_argument ( " --use " , type = inkex . Boolean , default = True )
2020-08-23 16:22:46 +02:00
self . arg_parser . add_argument ( " --flowRoot " , type = inkex . Boolean , default = True )
self . arg_parser . add_argument ( " --flowRegion " , type = inkex . Boolean , default = True )
self . arg_parser . add_argument ( " --flowPara " , type = inkex . Boolean , default = True )
2020-08-13 01:28:19 +02:00
def effect ( self ) :
2020-08-20 02:41:10 +02:00
namespace = [ ]
2020-08-23 14:08:11 +02:00
namespace . append ( " { http://www.w3.org/2000/svg}circle " ) if self . options . circle else " "
namespace . append ( " { http://www.w3.org/2000/svg}clipPath " ) if self . options . clipPath else " "
namespace . append ( " { http://www.w3.org/2000/svg}defs " ) if self . options . defs else " "
namespace . append ( " { http://www.w3.org/2000/svg}ellipse " ) if self . options . ellipse else " "
namespace . append ( " { http://www.w3.org/2000/svg}image " ) if self . options . image else " "
namespace . append ( " { http://www.w3.org/2000/svg}line " ) if self . options . line else " "
namespace . append ( " { http://www.w3.org/2000/svg}polygon " ) if self . options . polygon else " "
namespace . append ( " { http://www.w3.org/2000/svg}path " ) if self . options . path else " "
namespace . append ( " { http://www.w3.org/2000/svg}polyline " ) if self . options . polyline else " "
namespace . append ( " { http://www.w3.org/2000/svg}rect " ) if self . options . rect else " "
#namespace.append("{http://www.w3.org/2000/svg}sodipodi") if self.options.sodipodi else "" #do not do this. it will crash InkScape
#namespace.append("{http://www.w3.org/2000/svg}svg") if self.options.svg else ""
namespace . append ( " { http://www.w3.org/2000/svg}text " ) if self . options . text else " "
namespace . append ( " { http://www.w3.org/2000/svg}tspan " ) if self . options . tspan else " "
2020-08-30 12:30:35 +02:00
namespace . append ( " { http://www.w3.org/2000/svg}linearGradient " ) if self . options . linearGradient else " "
namespace . append ( " { http://www.w3.org/2000/svg}radialGradient " ) if self . options . radialGradient else " "
namespace . append ( " { http://www.w3.org/2000/svg}meshGradient " ) if self . options . meshGradient else " "
namespace . append ( " { http://www.w3.org/2000/svg}meshRow " ) if self . options . meshRow else " "
namespace . append ( " { http://www.w3.org/2000/svg}meshPatch " ) if self . options . meshPatch else " "
2020-08-23 14:08:11 +02:00
namespace . append ( " { http://www.w3.org/2000/svg}script " ) if self . options . script else " "
2020-08-23 21:09:57 +02:00
namespace . append ( " { http://www.w3.org/2000/svg}metadata " ) if self . options . metadata else " "
2020-08-23 14:08:11 +02:00
namespace . append ( " { http://www.w3.org/2000/svg}stop " ) if self . options . stop else " "
namespace . append ( " { http://www.w3.org/2000/svg}use " ) if self . options . use else " "
2020-08-23 16:22:46 +02:00
namespace . append ( " { http://www.w3.org/2000/svg}flowRoot " ) if self . options . flowRoot else " "
namespace . append ( " { http://www.w3.org/2000/svg}flowRegion " ) if self . options . flowRegion else " "
namespace . append ( " { http://www.w3.org/2000/svg}flowPara " ) if self . options . flowPara else " "
2020-08-23 14:08:11 +02:00
2020-08-30 12:30:35 +02:00
#inkex.utils.debug(namespace)
2020-08-23 14:08:11 +02:00
#check if we have selected elements or if we should parse the whole document instead
selected = [ ] #list of elements to parse
if len ( self . svg . selected ) == 0 :
for element in self . document . getroot ( ) . iter ( tag = etree . Element ) :
if element != self . document . getroot ( ) :
selected . append ( element )
else :
2020-08-30 12:30:35 +02:00
selected = self . svg . get_selected ( )
2020-08-23 14:08:11 +02:00
2020-08-30 12:30:35 +02:00
#check the element for it's type and put it into the the according list (either re-group or delete)
def parseElement ( self , element ) :
2020-08-23 14:08:11 +02:00
if self . options . allitems :
if element not in self . allElements :
if element . tag != inkex . addNS ( ' g ' , ' svg ' ) and element . tag != inkex . addNS ( ' svg ' , ' svg ' ) and element . tag != inkex . addNS ( ' namedview ' , ' sodipodi ' ) :
self . allElements . append ( element )
else :
2020-08-30 12:30:35 +02:00
#inkex.utils.debug(element.tag)
2020-08-23 14:08:11 +02:00
if element . tag in namespace :
if element not in self . allElements :
self . allElements . append ( element )
else :
2020-08-30 12:30:35 +02:00
#inkex.utils.debug(element.tag)
2020-08-23 14:08:11 +02:00
if element . tag != inkex . addNS ( ' g ' , ' svg ' ) and element . tag != inkex . addNS ( ' svg ' , ' svg ' ) and element . tag != inkex . addNS ( ' namedview ' , ' sodipodi ' ) :
if element not in self . allNonMigrates :
self . allNonMigrates . append ( element )
if element . tag == inkex . addNS ( ' g ' , ' svg ' ) or element . tag == inkex . addNS ( ' svg ' , ' svg ' ) :
if element not in self . allGroups :
self . allGroups . append ( element )
groups = element . getchildren ( )
2020-08-13 01:28:19 +02:00
if groups is not None :
for group in groups :
2020-08-30 12:30:35 +02:00
parseElement ( self , group )
2020-08-13 01:28:19 +02:00
if group not in self . allGroups :
self . allGroups . append ( group )
2020-08-23 14:08:11 +02:00
#get all elements from the selection. Remove all groups from the selection and form a new single group of it. We also handle svg:svg because it behaves like a group container too
for element in selected :
2020-08-30 12:30:35 +02:00
parseElement ( self , element )
2020-08-23 14:08:11 +02:00
2020-08-30 12:30:35 +02:00
#copy all element into the new group
2020-08-23 14:08:11 +02:00
if len ( self . allElements ) > 0 :
2020-08-23 21:09:57 +02:00
newGroup = self . document . getroot ( ) . add ( inkex . Group ( ) ) #make a new group at root level
2020-08-23 14:08:11 +02:00
for oldElement in self . allElements :
#oldElementId = oldElement.get('id')
2020-08-23 21:09:57 +02:00
newElement = oldElement . copy ( )
#newElement.set('id', oldElementId)
newGroup . add ( newElement )
if self . options . droponly :
2020-08-23 14:08:11 +02:00
if oldElement . getparent ( ) is not None :
oldElement . getparent ( ) . remove ( oldElement )
2020-08-20 02:41:10 +02:00
2020-08-30 12:30:35 +02:00
#show a list with items to delete
if self . options . showdroplist :
self . msg ( str ( len ( self . allNonMigrates ) ) + " elements were removed during nodes while migration: " )
for i in self . allNonMigrates :
if i . get ( ' id ' ) is not None :
self . msg ( i . tag . replace ( " { http://www.w3.org/2000/svg} " , " svg: " ) + " id: " + i . get ( ' id ' ) )
# #now remove the stuff with nonMigrates list. this has to be done before we drop the groups where they are located
# if len(self.allNonMigrates) > 0:
# for nonMigrate in self.allNonMigrates:
# if nonMigrate.getparent() is not None:
# nonMigrate.getparent().remove(nonMigrate)
#now remove all the obsolete groups
2020-08-23 14:08:11 +02:00
if self . options . droponly == False :
if len ( self . allGroups ) > 0 :
for group in self . allGroups :
2020-08-30 12:30:35 +02:00
if group . getparent ( ) is not None :
group . getparent ( ) . remove ( group )
2020-08-23 21:09:57 +02:00
#remove the selected, now empty group (if it's the case) - this applies not if there is no user selection at all so some dangling group(s) might be left over
if len ( self . svg . selected ) > 0 and len ( self . allElements ) > 0 :
2020-08-30 12:30:35 +02:00
if self . svg . get_first_selected ( ) . tag == inkex . addNS ( ' g ' , ' svg ' ) or self . svg . get_first_selected ( ) . tag == inkex . addNS ( ' svg ' , ' svg ' ) :
if self . svg . get_first_selected ( ) . getparent ( ) is not None :
self . svg . get_first_selected ( ) . getparent ( ) . remove ( self . svg . get_first_selected ( ) )
MigrateGroups ( ) . run ( )