bartbutenaers / node-red-dashboard-2-ui-svg

A Node-RED UI node to show SVG drawings in Node-RED dashboard v2
Apache License 2.0
2 stars 0 forks source link

Using Inkscape SVGs #7

Open l-althueser opened 3 weeks ago

l-althueser commented 3 weeks ago

Hi there and many thanks for making this project!

I was injecting an Inkscape SVG to the SVG node and noticed that I had to trim the first few lines of the SVG file as it starts with:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

instead of just <svg. This is an easy fix on the client side but then I noticed that Inkscape has the nasty feature of putting any text in spans which would be removed in the current version. Can I propose to change the setText function to something like the following? I am pretty sure there are some better ways to do it but this seems to work:

function setTextt(svgElement, payload) {
    _checkRequiredFields(payload, ['selector', 'text'])

// TODO in de oude node stond code om de unicode van een fontawesome icoon op te halen
// Misschien best met de nieuwe iconen werken!!!!!!!!!!!!!!!!!
// Wel niet zeker hoe we dat op de server side moeten doen, want daar bestaan die icons niet
    let elements = svgElement.querySelectorAll(payload.selector)
    if (!elements || !elements.length) {
        throw new Error(`No svg elements found for the specified 'selector' (${payload.selector})`)
    }

    elements.forEach(function(element){
        if (!element.innerHTML.startsWith('<tspan')) {
            element.textContent = payload.text
        } else {
            let text_start = element.innerHTML.indexOf(">")
            let text_end = element.innerHTML.indexOf("</tspan>")
            element.innerHTML = element.innerHTML.substring(0, text_start+1) + payload.text + element.innerHTML.substring(text_end, element.innerHTML.length+1)
        }
    })
}

What do you think? Thanks!

bartbutenaers commented 2 weeks ago

Hi @l-althueser

I had to trim the first few lines of the SVG file as it starts with

Ah ok. Good that you mention this. In my set_svg I should remove the xml and comment tags automatically, because it is indeed ok that the injected svg contains it.

Inkscape has the nasty feature of putting any text in spans

Could you share with me a piece of your svg that shows how something like that looks like?
Just to make sure I understand your problem correctly...

Thanks!

l-althueser commented 2 weeks ago

Hi!

I attached the full test file that I use at the moment. This is just plain Inkscape and adding some text components with no extra styling or conversion. A typical text component would be:

    <text
       xml:space="preserve"
       id="LS1_InB"
       style="white-space:pre;shape-inside:url(#rect2-6-7);display:inline;fill:#000000"
       transform="translate(-169.91543,-33.586677)"><tspan
         x="255"
         y="214.94796"
         id="tspan4">000.00</tspan></text>

Inkscape uses some transform and span logic to place the text field in the correct position - removing either screws up the format. Therefore, I want to avoid deleting the html components inside the text content of the text field :)

This is the full svg file: LS

bartbutenaers commented 2 weeks ago

So you want to set the content of the text element with id "LS1_InB", but that contains a tspan element with id "tspan4". I would think that you change the "tpsan4" to e.g. "LS1_InB_tspan"? Or perhaps the tspan is nowhere visible in Inkscape, and generated automatically. In the latter case I understand your fix in my code and I will adopt it...

l-althueser commented 2 weeks ago

Right! The naming and placement are automatically performed by Inkscape itself and not visible to the user. There is also no option in Inkscape to avoid the span elements. Unfortunately, I have to rely on Inkscape for these special cases as the future maintainers of the SVG files have no other paid software that handles the files better. Now that you saying it, we might be able to query for any tspan object inside the innerHTML and go recursively to the text element and just change that - no need for the html parsing. But you are the expert in that field and I would love to see a solution implemented in your node. Thanks!

bartbutenaers commented 2 weeks ago

@l-althueser,

I started implementing a solution for your problem, but I wanted to have something reusable for other use cases. Because in the past I added some workarounds for very specific use cases in the old svg node, which I regretted a lot later on.

There are various SVG editors around, each with their own dialects. And these dialects can change over time, in new versions of these tools. I can not support all these weird combinations unfortunately...

Then I suddenly realized that I don't need to change anything to solve this, because you can easily use a dedicated css selector!

This way we can keep the code simple, and still support numerous use cases.

And if you don't know how to write a custom css selector for some use case, just ask ChatGpt ;-)

Here is an example flow for Inkscape:

[{"id":"51b14b784794954b","type":"inject","z":"f624e6564f974a57","name":"Add text element with tspan child","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"command\":\"add_element\",\"type\":\"text\",\"id\":\"my_text\",\"attributes\":{\"x\":\"250\",\"y\":\"50\"}},{\"command\":\"add_element\",\"type\":\"tspan\",\"parentSelector\":\"#my_text\",\"style\":{\"fill\":\"red\",\"text-anchor\":\"middle\",\"dominant-baseline\":\"middle\",\"font-size\":\"30px\",\"font-style\":\"italic\",\"font-family\":\"serif\"},\"text\":\"A tspan child for a text element\"}]","payloadType":"json","x":950,"y":1420,"wires":[["9a338535ec8313b6"]]},{"id":"9a338535ec8313b6","type":"ui-svg","z":"f624e6564f974a57","name":"","group":"65805ec358e05ea4","width":0,"height":0,"x":1170,"y":1420,"wires":[["1a204e0ea61e1371"]]},{"id":"86f29f3f65dc2ff2","type":"inject","z":"f624e6564f974a57","name":"Get text","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"command\":\"get_text\",\"selector\":\"#my_text > tspan\"}]","payloadType":"json","x":1030,"y":1460,"wires":[["9a338535ec8313b6"]]},{"id":"918b284798103b7d","type":"inject","z":"f624e6564f974a57","name":"Set text","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"command\":\"set_text\",\"selector\":\"#my_text > tspan\",\"text\":\"Tspan content updated\"}]","payloadType":"json","x":1030,"y":1500,"wires":[["9a338535ec8313b6"]]},{"id":"1a204e0ea61e1371","type":"debug","z":"f624e6564f974a57","name":"Text content","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1330,"y":1420,"wires":[]},{"id":"5409476d82196839","type":"comment","z":"f624e6564f974a57","name":"Inkscape text with tspan demo","info":"","x":960,"y":1380,"wires":[]},{"id":"65805ec358e05ea4","type":"ui-group","name":"SVG demos","page":"0a90f52e8742b298","width":"10","height":"10","order":-1,"showTitle":true,"className":"","visible":"true","disabled":"false"},{"id":"0a90f52e8742b298","type":"ui-page","name":"SVG","ui":"be29745a6e568f30","path":"/page2","icon":"home","layout":"grid","theme":"092547d34959327c","order":-1,"className":"","visible":"true","disabled":"false"},{"id":"be29745a6e568f30","type":"ui-base","name":"UI Name","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false},{"id":"092547d34959327c","type":"ui-theme","name":"Theme Name","colors":{"surface":"#ffffff","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"}}]

tspan_demo

By inspecting the svg - using your browser's developer tools - you can easily see that this way the content of the nested tspan element has been updated:

image

I will create later on a wiki page about this, to assist others with a similar issue.