Skip to content

Instantly share code, notes, and snippets.

@larscwallin
Last active December 16, 2015 19:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save larscwallin/5489140 to your computer and use it in GitHub Desktop.
Save larscwallin/5489140 to your computer and use it in GitHub Desktop.
larscwallin.inx.extractelements version 0.1 extracts all selected SVG Elements to file and/or displayed as a string in the Inkscape GUI.
| A README
| larscwallin.inx.extractelements version 0.1 extracts all selected SVG Elements to file and/or displayed as a string in the
| Inkscape GUI.
|| UPDATE 130510
|||| Added option to keep the canvas size of the original drawing when exporting.
(Previously each exported drawing was sized according to its elements bounding box)
|||| Bug fixes
|| UPDATE 130506
|||| Added movePath method which moves all paths to 0,0 coordinates regardless of their original placement.
|||| If no elements are selected, the extension now defaults to exporting any visible layers.
Precondition for this is that layers have been hidden and then shown again at some point as Inkscape currently does not
add meta data by default.
|| UPDATE 130503
|||| Added calculation of bounding box and width and height attributes for the resulting SVG doc.
<inkscape-extension>
<_name>Extract Elements</_name>
<id>com.larscwallin.inx.extractelements</id>
<dependency type="executable" location="extensions">larscwallin.inx.extractelements.py</dependency>
<dependency type="executable" location="extensions">inkex.py</dependency>
<param name="where" type="string" _gui-text="Output path"></param>
<param name="encode" type="boolean" _gui-text="Do you wish to Base64 encode the SVG?">false</param>
<param name="viewresult" type="boolean" _gui-text="Do you wish to view the result?">true</param>
<param name="resize" type="boolean" _gui-text="Resize the drawing canvas for each element?">false</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu _name="Export"/>
</effects-menu>
</effect>
<script>
<command reldir="extensions" interpreter="python">larscwallin.inx.extractelements.py</command>
</script>
</inkscape-extension>
#!/usr/bin/env python
# These two lines are only needed if you don't put the script directly into
# the installation directory
import sys
import base64
import string
import webbrowser
import threading
import os.path
sys.path.append('/usr/share/inkscape/extensions')
import SvgDocument
import inkex
import simpletransform
import simplepath
import simplestyle
class ElementsToSVG(inkex.Effect):
exportTemplate = """<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="{{element.width}}" height="{{element.height}}">
{{element.source}}
</svg>"""
def __init__(self):
"""
Constructor.
Defines the "--what" option of a script.
"""
# Call the base class constructor.
inkex.Effect.__init__(self)
self.OptionParser.add_option('-w', '--where', action = 'store',
type = 'string', dest = 'where', default = '',
help = '')
self.OptionParser.add_option('--encode', action = 'store',
type = 'inkbool', dest = 'encode', default = False,
help = 'Base64 encode the result?')
self.OptionParser.add_option('--viewresult', action = 'store',
type = 'inkbool', dest = 'viewresult', default = False,
help = 'View resulting?')
self.OptionParser.add_option('--resize', action = 'store',
type = 'inkbool', dest = 'resize', default = False,
help = 'Resize the drawing canvas to the elements?')
def effect(self):
"""
Effect behaviour.
"""
self.where = self.options.where
self.base64Encode = self.options.encode
self.viewResult = self.options.viewresult
self.resizeDrawing = self.options.resize
self.getselected()
self.svgDoc = self.document.xpath('//svg:svg',namespaces=inkex.NSS)[0]
self.svgWidth = inkex.unittouu(self.svgDoc.get('width'))
self.svgHeight = inkex.unittouu(self.svgDoc.get('height'))
overideElementDim = False
layers = self.document.xpath('//svg:svg/svg:g[@style!="display:none"]',namespaces=inkex.NSS)
# If no elements where selected we default to exporting every visible layer in the drawing
if(self.selected.__len__() <= 0):
self.selected = layers
# As we are exporting whole layers we assume that the resulting SVG drawings has the
# same dimensions as the source document and thus overide the elements bounding box.
overideElementDim = True
else:
self.selected = self.selected.values()
if(self.selected.__len__() > 0):
selected = []
# Iterate through all elements
for element in self.selected:
elementLabel = str(element.get(inkex.addNS('label', 'inkscape'),''))
elementId = element.get('id')
tagName= self.getTagName(element)
if(tagName== 'path'):
pathData = self.movePath(element,0,0,'tl')
if(pathData):
element.set('d',pathData)
elementBox = list(simpletransform.computeBBox([element]))
elementBox[1] = (elementBox[1]-elementBox[0])
elementBox[3] = (elementBox[3]-elementBox[2])
if(overideElementDim == False):
elementWidth = elementBox[1]
elementHeight = elementBox[3]
else:
elementWidth = self.svgWidth
elementHeight = self.svgHeight
elementSource = inkex.etree.tostring(element)
if(elementSource!=''):
# Wrap the node in an SVG doc
if(self.resizeDrawing):
tplResult = string.replace(self.exportTemplate,'{{element.width}}',str(elementWidth))
tplResult = string.replace(tplResult,'{{element.height}}',str(elementHeight))
else:
tplResult = string.replace(self.exportTemplate,'{{element.width}}',str(self.svgWidth))
tplResult = string.replace(tplResult,'{{element.height}}',str(self.svgHeight))
tplResult = string.replace(tplResult,'{{element.source}}',elementSource)
# If the result of the operation is valid, add the SVG source to the selected array
if(tplResult):
selected.append({
'id':elementId,
'label':elementLabel,
'source':tplResult,
'box':elementBox
})
for node in selected:
#'data:image/svg+xml;base64,'+
# Cache this in a local var
content = node['source']
if(content!=''):
if(self.base64Encode):
content = ('data:image/svg+xml;base64,'+(base64.b64encode(content)))
if(self.where!=''):
# The easiest way to name rendered elements is by using their id since we can trust that this is always unique.
filename = os.path.join(self.where, (node['id']+node['label']+'.svg'))
success = self.saveToFile(content,filename)
if(success):
if(self.viewResult):
self.viewOutput(filename)
else:
inkex.debug('Unable to write to file "' + filename + '"')
else:
if(self.viewResult):
if(self.base64Encode):
inkex.debug(content)
#self.viewOutput('data:image/svg+xml;base64,'+content)
else:
inkex.debug(content)
#self.viewOutput('data:image/svg+xml,'+content)
else:
inkex.debug(content)
else:
inkex.debug('No SVG source available for element ' + node['id'])
else:
inkex.debug('No SVG elements or layers to extract.')
def getTagName(self,node):
type = node.get(inkex.addNS('type', 'sodipodi'))
if(type == None):
#remove namespace data {....}
tagName= node.tag
tagName= tagName.split('}')[1]
else:
tagName= str(type)
return tagName
def movePath(self,node,x,y,origin):
tagName= self.getTagName(node)
if(tagName!= 'path'):
inkex.debug('movePath only works on SVG Path elements. Argument was of type "' + tagName+ '"')
return False
path = simplepath.parsePath(node.get('d'))
id = node.get('id')
box = list(simpletransform.computeBBox([node]))
#inkex.debug(path)
offset_x = (box[0] - x)
offset_y = (box[2] - (y))
#inkex.debug('Will move path "'+id+'" from x, y ' + str(box[0]) + ', ' + str(box[2]))
#inkex.debug('to x, y ' + str(x) + ', ' + str(y))
#inkex.debug('The x offset is ' + str(offset_x))
#inkex.debug('The y offset is = ' + str(offset_y))
for cmd in path:
params = cmd[1]
i = 0
while(i < len(params)):
if(i % 2 == 0):
#inkex.debug('x point at ' + str( round( params[i] )))
params[i] = (params[i] - offset_x)
#inkex.debug('moved to ' + str( round( params[i] )))
else:
#inkex.debug('y point at ' + str( round( params[i]) ))
params[i] = (params[i] - offset_y)
#inkex.debug('moved to ' + str( round( params[i] )))
i = i + 1
#inkex.debug(simplepath.formatPath(path))
return simplepath.formatPath(path)
def saveToFile(self,content,filename):
FILE = open(filename,'w')
if(FILE):
FILE.write(content)
FILE.close()
return True
else:
return False
def viewOutput(self,url):
runner = BrowserRunner()
runner.url = url
runner.start()
class BrowserRunner(threading.Thread):
url = ''
def __init__(self):
threading.Thread.__init__ (self)
def run(self):
webbrowser.open('file://' + self.url)
# Create effect instance and apply it.
effect = ElementsToSVG()
effect.affect(output=False)
#inkex.errormsg(_("This will be written to Python stderr"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment