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:
How do I search for (all) element(s) with a given label?
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'
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.
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).
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'
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:
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.
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.
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).
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.
Thanks, that worked! Just wondering why I wouldn't just replace the item.text instead of deleting it first...
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