Last active
December 16, 2015 19:59
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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