Search code examples
cssarrayssvgimagemagick-convertjointjs

Replacing class styles with inline styles


I have svg image, that has some classes(jointJS paper). I'm saving this svg, and then converting it to png file on backend. Problem is - classes on my backend can't be recognized, so i would like to replace class names with styles that assigned to these classes, for ex.

<g id=\"j_42\" model-id=\"7c170ce6-09a5-49e9-b470-c1bb1980bd02\" class=\"link joint-theme-default\"><path class=\"connection\" stroke=\"#222\" d=\"M 803 387 C 881 387 881 196 959 196\" id=\"v-522\" stroke-width=\"3\"/>

<g id=\"j_42\" model-id=\"7c170ce6-09a5-49e9-b470-c1bb1980bd02\" style="some_styles_that_stands_for joint-theme-default class"><path class=\"connection\" stroke=\"#222\" d=\"M 803 387 C 881 387 881 196 959 196\" id=\"v-522\" stroke-width=\"3\"/

Solution

  • you can use document.styleSheets to read all stylesheets, find all the class rules you want and replace the class attribute of the Elemets with the style definition from the stylesheet.

    what i do here is basicly get:

    1. get the first sytlesheet (you can basicly loop over all stylesheets if you like)
    2. loop over all rules (assuming all selectors are class selectors)
    3. save those rules in an Object to more easily access them later on
    4. loop over all rect elements
    5. get a list of all class names of that element
    6. get the style definition of each class name form the previously generated mapper object
    7. concat those definitions
    8. remove the class attribute
    9. set the style attributes value to the concatenated string from 7.

    with this you end with rect elements which have their class names replaced by inline styles...

    P.S:: i know i make a lot of assumptions here. To make this work for you, or work in a general case, there still is a lot of work to do...

    classMap = {}
    var rules = document.styleSheets[0].cssRules // 1. get first stylesheet
    for (var j = 0; j < rules.length; j++) { // 2. loop over all rules
      var selector = rules[j].selectorText
      var csstext = rules[j].cssText
      classMap[selector.replace(".", "")] = csstext.split("{")[1].replace("}", "") // 3. save class name and rule text to mapper object
    }
    
    document.querySelectorAll("rect").forEach(function(item, index) { // 4. loop over all rects
      var style = ""
      item.classList.forEach(function(className) { // 5. loop over all class names
        style += classMap[className] // 6. + 7. get style rules form mapper and concat
      })
      item.removeAttribute("class") // 8. remove class
      item.setAttribute("style", style) // 9. add inline style
      document.write("<br/>" + item.id + ": " + style)
    })
    .cl1 {
      fill: green
    }
    .cl2 {
      fill: red
    }
    .st1 {
      stroke: blue;
      stroke-width: 10
    }
    .st2 {
      stroke: orange;
      stroke-width: 5
    }
    <svg>
      <rect id="rect1" x="10" y="10" width="100" height="100" class="cl1 st1" />
      <rect id="rect2" y="10" x="150" width="100" height="100" class="cl2 st2" />
    </svg>

    another approach would be to use getComputedStyle to get the style definition. this is more easy, and works for the general case, but will bloat your document quite fast...

    to prevent that (and prevent you from running into some chrome bug...) you can maintain a list of all style properties you are interested in, and get the current values only for these properties from getComputedStyle

    var props = ["fill", "stroke", "stroke-width"]
    
    document.querySelectorAll("rect").forEach(function(item, index) {
      var cstyle = getComputedStyle(item)
      props.forEach(function(prop) {
        item.setAttribute(prop, cstyle[prop])
      })
      item.removeAttribute("class")
      var X = new XMLSerializer()
      var txt = document.createTextNode(X.serializeToString(item))
      document.write("<br/><br/>")
      document.body.appendChild(txt)
    })
    .cl1 {
      fill: green
    }
    .cl2 {
      fill: red
    }
    .st1 {
      stroke: blue;
      stroke-width: 10
    }
    .st2 {
      stroke: orange;
      stroke-width: 5
    }
    <svg>
      <rect id="rect1" x="10" y="10" width="100" height="100" class="cl1 st1" />
      <rect id="rect2" y="10" x="150" width="100" height="100" class="cl2 st2" />
    </svg>