Inkscape.org
Creating New Extensions Selecting an (text)element with a given label and replace text content
  1. #1
    Jurgen Gaeremyn Jurgen Gaeremyn @JurgenG

    Hi,

    I'd like to learn the basics of Inkscape extension development. I have a  few questions. I'll try not to put too much in one topic (for reference purpose)

    I'd like my extension to search for all (text) elements with the label "COUNTER" and replace the text value with a given variable.

    I chose label over ID because that's what changes when you rename an element in the Layers & Objects view.

    So I have 2 questions here:

    1. How do I search for (all) element(s) with a given label?
    2. How to I replace the text of an element?

     

     

  2. #2
    inklinea inklinea @inklinea⛰️

    When text objects are created in Inkscape they are 

    <text></text> with <tspan></tspan> children. 

    This may not be the best way to do it, but I would simply find each text tag, destroy it's entire contents including all child tags.

    Then replace its contents.

    # Use xpath to find all <text> tags in document
    my_text_objects = self.svg.xpath('//svg:text')
    for text_object in my_text_objects:
        # Convert label to lowercase to allow for Counter, counter variations etc
        if str(text_object.label).lower() == 'counter':
            # Find all child tags in the <text> element.
            text_object_contents = text_object.xpath('./*')
            for item in text_object_contents:
                # delete all child tags
                item.delete()
            # Set <text> text content
            text_object.text = 'Inkscape'
  3. #3
    inklinea inklinea @inklinea⛰️
    *

    I should mention this will have the effect of removing any styles in the tspans so will take you back to a default font / colour everything - but it's a just a very simple example.

    Probably a better idea would be to remove every element after the first <tspan> then insert your text into that, in order to keep some styling.

    However, with svg it also depends how it was created. ( ie: outside Inkscape for example )

    It's possible to have combinations or <text> Hello <tspan> Again </tspan> </text> 

    I would advise opening the svgs in a xml aware text editor, and have a poke around.

     

  4. #4
    Jurgen Gaeremyn Jurgen Gaeremyn @JurgenG

    Thank you so much for your answer already! I'll look into that.

    Yes, I have the SVG files open in gedit to look into the XML code natively. This has already learned me a lot (e.g. about how multipage is implemented in 1.2)

    There is the .tostring() function that converts an element into an xml string. I didn't find any documentation of the inverse: converting a text string into an element. It would make sense for that function to also exist (though admittedly much tougher to implement, so maybe that's why it maybe doesn't exist).

     

  5. #5
    inklinea inklinea @inklinea⛰️

    No, it was a bit of a naughty cheat, nothing to do with XML at all. 

    the .label property ( https://inkscape.gitlab.io/extensions/documentation/source/inkex.elements._base.html?highlight=label#inkex.elements._base.BaseElement.label ) 

    retrieves the inkscape label for the object. Inkscape labels are not part of the svg specification.

    Unless an object has been given a label, Inkscape just displays the object id in the object panel.

    In python my_object.label is returned as none for those objects without labels.

    I wanted to make sure that the comparison would accept variations in case for the word 'counter' so 'Counter' , 'counteR' etc.

    The usual  way to do this ( in any language ) is to convert the string to upper or lower ( your choice ) then do a comparison using ==

    Since the objects which return none for label return a 'NoneType' object - that object does now have .lower() available to it, so throws an error.

    Converting to string using str makes sure the returned value is a string and has .lower() available by default.

    Maybe it would have been better to examine the type of the returned value and only carry out the comparison for string types.

     

  6. #6
    Jurgen Gaeremyn Jurgen Gaeremyn @JurgenG

    Thanks, that worked! Just wondering why I wouldn't just replace the item.text instead of deleting it first...

    # Use xpath to find all <text> tags in document
    my_text_objects = self.svg.xpath('//svg:text')
    for text_object in my_text_objects:
        # Convert label to lowercase to allow for Counter, counter variations etc
        if str(text_object.label).lower() == 'counter':
            # Find all child tags in the <text> element.
            text_object_contents = text_object.xpath('./*')
            # Set <text> text content
            text_object.text = 'Inkscape'

     

  7. #7
    inklinea inklinea @inklinea⛰️

    If you think about the combinations possible:

    <text> Hello </text> or

    <text><tspan>Hello</tspan></text> or 

    <text> Hello <tspan> Again </tspan> 

    1. should return 'Hello' for item.text

    2. should return 'None' for item.text

    3. should return 'Hello' for item.text

     

     

     

Inkscape Inkscape.org Inkscape Forum Creating New Extensions Selecting an (text)element with a given label and replace text content