summaryrefslogtreecommitdiffstats
path: root/share/extensions/frame.py
diff options
context:
space:
mode:
Diffstat (limited to 'share/extensions/frame.py')
-rw-r--r--share/extensions/frame.py175
1 files changed, 175 insertions, 0 deletions
diff --git a/share/extensions/frame.py b/share/extensions/frame.py
new file mode 100644
index 000000000..f6d54e180
--- /dev/null
+++ b/share/extensions/frame.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python
+"""
+An Inkscape extension that creates a frame around a selected object.
+
+Copyright (C) 2016 Richard White, rwhite8282@gmail.com
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+# These two lines are only needed if you don't put the script directly into
+# the installation directory
+import sys
+sys.path.append('/usr/share/inkscape/extensions')
+
+import inkex
+import simplestyle
+from simpletransform import *
+from simplestyle import *
+
+
+def get_picker_data(value):
+ """ Returns color data in style string format.
+ value -- The value returned from the color picker.
+ Returns an object with color and opacity properties.
+ """
+ v = '%08X' % (value & 0xFFFFFFFF)
+ color = '#' + v[0:-2].rjust(6, '0')
+ opacity = '%1.2f' % (float(int(v[6:].rjust(2, '0'), 16))/255)
+ return type('', (object,), {'color':color, 'opacity':opacity})()
+
+
+def size_box(box, delta):
+ """ Returns a box with an altered size.
+ delta -- The amount the box should grow.
+ Returns a box with an altered size.
+ """
+ return ((box[0]-delta), (box[1]+delta), (box[2]-delta), (box[3]+delta))
+
+
+# Frame maker Inkscape effect extension
+class Frame(inkex.Effect):
+ """ An Inkscape extension that creates a frame around a selected object.
+ """
+ def __init__(self):
+ inkex.Effect.__init__(self)
+ self.defs = None
+
+ # Parse the options.
+ self.OptionParser.add_option('--clip',
+ action='store', type='inkbool',
+ dest='clip', default=False)
+ self.OptionParser.add_option('--corner_radius',
+ action='store', type='int',
+ dest='corner_radius', default=0)
+ self.OptionParser.add_option('--fill_color',
+ action='store', type='int',
+ dest='fill_color', default='00000000')
+ self.OptionParser.add_option('--group',
+ action='store', type='inkbool',
+ dest='group', default=False)
+ self.OptionParser.add_option('--position',
+ action='store', type='string',
+ dest='position', default='outside')
+ self.OptionParser.add_option('--stroke_color',
+ action='store', type='int',
+ dest='stroke_color', default='00000000')
+ self.OptionParser.add_option('--tab',
+ action='store', type='string',
+ dest='tab', default='object')
+ self.OptionParser.add_option('--width',
+ action='store', type='float',
+ dest='width', default=2)
+
+
+ def add_clip(self, node, clip_path):
+ """ Adds a new clip path node to the defs and sets
+ the clip-path on the node.
+ node -- The node that will be clipped.
+ clip_path -- The clip path object.
+ """
+ if self.defs is None:
+ defs_nodes = self.document.getroot().xpath('//svg:defs', namespaces=inkex.NSS)
+ if defs_nodes:
+ self.defs = defs_nodes[0]
+ else:
+ inkex.errormsg('Could not locate defs node for clip.')
+ return
+ clip = inkex.etree.SubElement(self.defs, inkex.addNS('clipPath','svg'))
+ clip.append(copy.deepcopy(clip_path))
+ clip_id = self.uniqueId('clipPath')
+ clip.set('id', clip_id)
+ node.set('clip-path', 'url(#%s)' % str(clip_id))
+
+
+ def add_frame(self, parent, name, box, style, radius=0):
+ """ Adds a new frame to the parent object.
+ parent -- The parent that the frame will be added to.
+ name -- The name of the new frame object.
+ box -- The boundary box of the node.
+ style -- The style used to draw the path.
+ radius -- The corner radius of the frame.
+ returns a new frame node.
+ """
+ r = min([radius, (abs(box[1]-box[0])/2), (abs(box[3]-box[2])/2)])
+ if (radius > 0):
+ d = ' '.join(str(x) for x in
+ ['M', box[0], (box[2]+r)
+ ,'A', r, r, '0 0 1', (box[0]+r), box[2]
+ ,'L', (box[1]-r), box[2]
+ ,'A', r, r, '0 0 1', box[1], (box[2]+r)
+ ,'L', box[1], (box[3]-r)
+ ,'A', r, r, '0 0 1', (box[1]-r), box[3]
+ ,'L', (box[0]+r), box[3]
+ ,'A', r, r, '0 0 1', box[0], (box[3]-r), 'Z'])
+ else:
+ d = ' '.join(str(x) for x in
+ ['M', box[0], box[2]
+ ,'L', box[1], box[2]
+ ,'L', box[1], box[3]
+ ,'L', box[0], box[3], 'Z'])
+
+ attributes = {'style':style, inkex.addNS('label','inkscape'):name, 'd':d}
+ return inkex.etree.SubElement(parent, inkex.addNS('path','svg'), attributes )
+
+
+ def effect(self):
+ """ Performs the effect.
+ """
+ # Get the style values.
+ corner_radius = self.options.corner_radius
+ stroke_data = get_picker_data(self.options.stroke_color)
+ fill_data = get_picker_data(self.options.fill_color)
+
+ # Determine common properties.
+ parent = self.current_layer
+ position = self.options.position
+ width = self.options.width
+ style = simplestyle.formatStyle({'stroke':stroke_data.color
+ , 'stroke-opacity':stroke_data.opacity
+ , 'stroke-width':str(width)
+ , 'fill':(fill_data.color if (fill_data.opacity > 0) else 'none')
+ , 'fill-opacity':fill_data.opacity})
+
+ for id, node in self.selected.iteritems():
+ box = computeBBox([node])
+ if 'outside' == position:
+ box = size_box(box, (width/2))
+ else:
+ box = size_box(box, -(width/2))
+ name = 'Frame'
+ frame = self.add_frame(parent, name, box, style, corner_radius)
+ if self.options.clip:
+ self.add_clip(node, frame)
+ if self.options.group:
+ group = inkex.etree.SubElement(node.getparent(),inkex.addNS('g','svg'))
+ group.append(node)
+ group.append(frame)
+
+
+if __name__ == '__main__': #pragma: no cover
+ # Create effect instance and apply it.
+ effect = Frame()
+ effect.affect() \ No newline at end of file