Inkscape.org
Creating New Extensions Adding defs to an svg document
  1. #1
    Gavin Johnson Gavin Johnson @snow6oy

    I would like to make diagrams with arrow heads. Using Inkscape, with the Fill and Stroke tool I can generate XML that looks like

      <defs id="defs11454">
        <marker style="overflow:visible;"  id="Arrow1Lend" ..

    Programatically I can also read defs in Python with

    def effect(self):

            doc = self.get_template(width=w, height=h)
            svg = doc.getroot()
            defs = svg.defs

    In order to set a new definition I think I should pass something into get_template(). If someone has done this before and can share an example .. well that would be marvellous :-)

    Thanks

     

  2. #2
    inklinea inklinea @inklinea⛰️

    I've not made <defs> before:

    You shouldn't need to create a <defs> element if the file is open in Inkscape, as it will be automatically added ( you can see that in the Edit>XML Editor panel ) 

    If you write a simple extension which deletes the <defs> element - Inkscape automatically regenerates it as soon as the extension exits.

    Maybe this will do what you want: 

     

    import inkex
    from lxml import etree
    
    def add_to_defs(self, my_object):
    
        my_object_id = my_object.get_id()
    
        svg_defs = self.svg.xpath('//svg:defs')
        svg_defs[0].append(my_object)
    
        parent = self.svg
        my_use_object = etree.SubElement(parent, inkex.addNS('use', 'svg'))
        my_use_object.set('xlink:href', '#' + str(my_object_id))
    
    class DefsMan(inkex.EffectExtension):
    
        def effect(self):
    
            my_selected = self.svg.selected
            # Make sure something is selected
            if len(my_selected) > 0:
                for my_object in my_selected:
                    add_to_defs(self, my_object)
    
    if __name__ == '__main__':
        DefsMan().run()
  3. #3
    Gavin Johnson Gavin Johnson @snow6oy
    *

    Sorry it took a while to reply .. I got seriously distracted with another issue :-)

    And thanks for sharing your code. It worked first time! But then I tried to extend it and Inkscape started crashing. Looking at the XML that is dumped prior to the crash, I can see that <defs /> contains the marker. Here is my amended version. Any idea what causes the crash?

    import inkex
    from lxml import etree
    from inkex import Marker
    
    def marker():
      m = Marker()
      m.set('style', "overflow:visible")
      m.set_id("Arrow1Lend")
      m.set('refX', "0.0")
      m.set('refY', "0.0")
      m.set('orient', "auto")
      m.set('inkscape:stockid', "Arrow1Lend")
      m.set('inkscape:isstock', "true")
      return m
    
    def add_to_defs(self, my_object):
    
        my_object_id = my_object.get_id()
    
        svg_defs = self.svg.xpath('//svg:defs')
        svg_defs[0].append(my_object)
    
        parent = self.svg
        my_use_object = etree.SubElement(parent, inkex.addNS('use', 'svg'))
        my_use_object.set('xlink:href', '#' + str(my_object_id))
    
    class DefsMan(inkex.EffectExtension):
    
        def effect(self):
    
            marker_to_add = marker()
            add_to_defs(self, marker_to_add)
    
    if __name__ == '__main__':
        DefsMan().run()

     

  4. #4
    inklinea inklinea @inklinea⛰️

    I think it might be because the <marker> you are adding is empty.

    from inkex import Circle

    I added this in the marker block

     

    my_circle = Circle()
    my_circle.set("cx", "20")
    my_circle.set("cy", "30")
    my_circle.set("r", "10")
    
    m.append(my_circle)
  5. #5
    Gavin Johnson Gavin Johnson @snow6oy

    It's working now 😊

  6. #6
    Gavin Johnson Gavin Johnson @snow6oy

    To finish the story here is a beautiful arrow and some ugly code. 

    import inkex
    from lxml import etree
    from inkex import Marker, TextElement, Ellipse, PathElement, Transform
    
    class DefsMan(inkex.EffectExtension):
    
      def add_to_defs(self, marker):
        svg_defs = self.svg.xpath('//svg:defs')
        svg_defs[0].append(marker)
    
      def effect(self):
    
        m = Marker()
        m.set_id('m')
        p = PathElement()
        p.set_path("M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z ")
        p.transform = Transform(rotate=(180), scale=(0.5))
        p.set_id('p')
        m.append(p)
        self.add_to_defs(m)
    
        s = self.svg
    
        e1 = Ellipse(cx='45', cy='112', rx="12", ry="10")
        e1.set_id('e1')
        e1.style = { "fill": "#ccc", "stroke": "#ccc" }
        s.add(e1)
    
        e2 = Ellipse(cx='145', cy='112', rx="12", ry="10")
        e2.set_id('e2')
        e2.style = { "fill": "#ccc", "stroke": "#ccc" }
        s.add(e2)
    
        p1 = PathElement()
        p1.set_path('m 57,112 h 70')
        p1.set_id('p1')
        p1.style = { "marker-end": "url(#m)", "stroke": "#000" }
        s.add(p1)
    
        t1 = TextElement(x='45', y='114')
        t1.style = { 'text-anchor': 'middle', 'text-align': 'center' }
        t1.text = 'A'
        t1.set_id('t1')
        s.add(t1)
    
        t2 = TextElement(x='145', y='114')
        t2.style = { 'text-anchor': 'middle', 'text-align': 'center' }
        t2.text = 'B'
        t2.set_id('t2')
        s.add(t2)
    
    if __name__ == '__main__':
      DefsMan().run()

     

    Abarrow
  7. #7
    Gavin Johnson Gavin Johnson @snow6oy

    Well the arrow was beautiful before the upload crushed it 😭

  8. #8
    inklinea inklinea @inklinea⛰️

    I'm glad it all worked out :)