Generating objects from extensions

From Inkscape Wiki
Jump to: navigation, search

If you need an extension to generate an object in Inkscape, there are many tools that already exist to help you. inkex.py is the most notable, as this provides the routines to insert the SVG element into the XML tree of the SVG document.

There is currently no universal set of tools to allow a single function to be called, because different extension have different attibutes for thir objects. For example, the barcode extension merely needs a black rectangle with no stroke, but another may need a way to set the stroke width, dashes and opacity. Thus, it is often best to write your own subroutine for generating your objects.

A simple example

Let's look at a simple Python function for drawing a black rectangle:

#SVG element generation routine
def draw_SVG_square((w,h), (x,y), parent):

    style = {   'stroke'        : 'none',
                'stroke-width'  : '1',
                'fill'          : '#000000'
            }
                
    attribs = {
        'style'     : simplestyle.formatStyle(style),
        'height'    : str(h),
        'width'     : str(w),
        'x'         : str(x),
        'y'         : str(y)
            }
    circ = inkex.etree.SubElement(parent, inkex.addNS('rect','svg'), attribs )

The first thing to notice is that all the attributes of the object are stored in a dictionary format, with everything being a string. This means you have to convert all your parameters to strings if appropriate (like the height and width attributes).

Next, all the style attributes (colours, widths, fonts, etc) are put together in SVG under style. To generate this string, there exists a helper function simplestyle.formatStyle(). You simply feed this function a dictionary of the styles you want, just like the attributes. You can then use this directly as the style attribute.

The next thing to see is how to add the element to the XML tree. inkex.py has the function to do this using the LXML parser. This needs to be given the "parent" of the object (we'll come back to this), the "type" of the object, and the attributes of the object.

The type of the object is in the svg namespace, which means it begins svg: (for a rectangle, it is svg:rect). LXML cannot parse colons, so we use the inkex.addNS to prepend the namespace.

The attributes of the object just needs to be passed the dictionary attribs we made earlier.

The "parent" of the object is the containing element. This is usually a group or a layer. This will be passed in by the calling function, and we will see it in action later.

When this function is run with the right parameters, the rectangle will be added to the SVG document.

Getting the parent

Finding the parent is easy: you can just pass in the current layer from the self object if you like:

parent = self.current_layer
draw_SVG_square((1,1), (0,0), parent)

This will just plonk the rectangle into the document, centred on the origin.

Alternatively, you can create a group much like any other object:

centre = self.view_center   #Put in in the centre of the current view
grp_transform = 'translate' + str( centre )

grp_name = 'Group Name'
grp_attribs = {inkex.addNS('label','inkscape'):grp_name,
                           'transform':grp_transform }
grp = inkex.etree.SubElement(self.current_layer, 'g', grp_attribs)#the group to put everything in

By the way, any object can be assigned a name as we did there, which is often helpful when generating many objects:

inkex.addNS('label','inkscape') : name

By setting the transform as self.view_center, we made sure the origin of the group is in the centre of the current view of the document. We will come back to transforms later.

The grp object can now be used as a parent for the rectangle:

draw_SVG_square((1,1), (0,0), grp)

This will draw a 1×1 black square in the center of the view.

Transforms

It is easy to transform an object: just supply a string like the folllowing as the transform attribute of the element:

  • trans = 'translate(10,10)'
  • trans = 'translate(10,10) rotate(10)'
  • trans = 'skewX(-1)'

Available commands: translate, scale, rotate, skewX, skewY, matrix. The transforms are composed in left-right order (i.e. the translate comes first in the second example).

More Examples

Ellipses

Ellipses are actually path elements, but Inkscape generates the nodes automatically if you feed it the correct attributes in the Sodipodi namespace. The vital ones are rx, ry, cx, cy.

Ellipses require some attributes in the sodipodi: namespace, so we also use the addNS() function in the attribute dictionary:

def draw_SVG_ellipse((rx, ry), (cx, cy), parent, start_end=0,2*pi),transform='' ):

    style = {   'stroke'        : '#000000',
                'stroke-width'  : '1',
                'fill'          : 'none'            }
    ell_attribs = {'style':simplestyle.formatStyle(style),
        inkex.addNS('cx','sodipodi')        :str(cx),
        inkex.addNS('cy','sodipodi')        :str(cy),
        inkex.addNS('rx','sodipodi')        :str(rx),
        inkex.addNS('ry','sodipodi')        :str(ry),
        inkex.addNS('start','sodipodi')     :str(start_end[0]),
        inkex.addNS('end','sodipodi')       :str(start_end[1]),
        inkex.addNS('open','sodipodi')      :'true',    #all ellipse sectors we will draw are open
        inkex.addNS('type','sodipodi')      :'arc',
        'transform'                         :transform
        
            }
    ell = inkex.etree.SubElement(parent, inkex.addNS('path','svg'), ell_attribs )

This will draw an open arc, with a black stroke of width 1 and no fill. The transform here can be passed in from outside.

If in doubt about the right attribute name and format, just check a similar object in the XML viewer in Inkscape.

Line Segment

Paths can be quite tricky to get the hang of if you don't know the meaning of the letters. Read the SVG specification for a full list.

Here, the style information has been passed in from outside the function.

#draw an SVG line segment between the given (raw) points
def draw_SVG_line( (x1, y1), (x2, y2), style, name, parent):
    line_style   = { 'stroke': style.l_col,
                     'stroke-width':str(style.l_th),
                     'fill': style.l_fill
                   }

    line_attribs = {'style' : simplestyle.formatStyle(line_style),
                    inkex.addNS('label','inkscape') : name,
                    'd' : 'M '+str(x1)+','+str(y1)+' L '+str(x2)+','+str(y2)}

    line = inkex.etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs )