skip to Main Content

Input is below:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="800pt" height="600pt" viewBox="0 0 800 600">
    <g enable-background="new">
    <path style="fill:none;stroke-width:0.074;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(50.19989%,50.19989%,50.19989%);stroke-opacity:1;stroke-miterlimit:10;" d="M 110.949219 265.421875 L 109.730469 265.691406 L 108.699219 266.410156 L 108.011719 267.460938 L 107.78125 268.679688 " transform="matrix(1,0,0,-1,0,600)"/>
    <path style="fill:none;stroke-width:0.074;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(50.19989%,50.19989%,50.19989%);stroke-opacity:1;stroke-miterlimit:10;" d="M 111.050781 268.679688 L 111.050781 265.421875 " transform="matrix(1,0,0,-1,0,600)"/>
    <path style="fill:none;stroke-width:0.074;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(50.19989%,50.19989%,50.19989%);stroke-opacity:1;stroke-miterlimit:10;" d="M 110.949219 268.679688 L 110.949219 265.421875 " transform="matrix(1,0,0,-1,0,600)"/>
      </g>
    </svg>

Expected Output is below:

    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="800pt" height="600pt" viewBox="0 0 800 600">
        <g enable-background="new">
        <path transform="matrix(1,0,0,-1,0,600)" stroke-width="0.074" stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#929292" d="M 110.95 265.42 L 109.73 265.69 L 108.7 266.41 L 108.01 267.46 L 107.78 268.68 "/>
        <path transform="matrix(1,0,0,-1,0,600)" stroke-width="0.074" stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#929292" d="M 111.05 268.68 L 111.05 265.42 "/>
        <path transform="matrix(1,0,0,-1,0,600)" stroke-width="0.074" stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#929292" d="M 110.95 268.68 L 110.95 265.42 "/>
          </g>
        </svg>

This is about simplifying and beautifying SVG path. I would like the expected output to have the elements of path transform, stroke-width, stroke-linecap, stroke-linejoin, fill stroke and d (rounded to 2 decimals for simplicity).

I have tried let svg_object_alt=document.querySelector('svg').outerHTML;, to convert svg as a string to be read and I am not sure how to extract all the information from it, to be formatted to the expected output? I would really appreciate any help I can obtain 🙂

I think that the stroke-colour can just hardcode the code number (#929292) into it instead of using the rgb colour 🙂

2

Answers


  1. you can use that…

    const mySvg_Paths = document.querySelectorAll('#my-svg > g > path');
     
    
    for ( let attr of mySvg_Paths[2].attributes ) 
      {
      console.log(attr.name,'-->n', attr.value);
      }
      
    //  or
    // console.log( 'outerHTML -->n', mySvg_Paths[2].outerHTML );
    // console.log( 'path.d -->n', mySvg_Paths[2].getAttribute('d'));
    #my-svg {
      width  : 800pt;
      height : 600pt;
      background : lightgreen;
      }
    #my-svg path {
      fill              : none;
      stroke-width      : 0.074;
      stroke-linecap    : round;
      stroke-linejoin   : round;
      stroke            : rgb(50.19989%, 50.19989%, 50.19989%);
      stroke-opacity    : 1;
      stroke-miterlimit : 10;
      }
    <svg id="my-svg" viewBox="0 0 800 600">
      <g enable-background="new">
        <path
          d="M 110.949219 265.421875 L 109.730469 265.691406 L 108.699219 266.410156 L 108.011719 267.460938 L 107.78125 268.679688 "
          transform="matrix(1,0,0,-1,0,600)"/>
        <path
          d="M 111.050781 268.679688 L 111.050781 265.421875 "
          transform="matrix(1,0,0,-1,0,600)" />
        <path
          style="fill:none;stroke-width:0.074;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(50.19989%,50.19989%,50.19989%);stroke-opacity:1;stroke-miterlimit:10;" 
          d="M 110.949219 268.679688 L 110.949219 265.421875 " 
          transform="matrix(1,0,0,-1,0,600)"/>
      </g>
    </svg>

    then, you can have a look to -> Use RegEx to parse a string with complicated delimiting

    specialy this function:

    function parseData(pathData)
      {
      var pieces = pathData.match(/([a-z]+[-.,d ]*)/gi), i;
      /* now parse each piece into its own array */
      for (i=0; i<pieces.length; i++)
          pieces[i] = pieces[i].match(/([a-z]+|-?[.d]*d)/gi);
      return pieces;
      }
    
    Login or Signup to reply.
  2. You could actually do all these conversions with SVGOMG.
    If you need to do this in a custom script:

    Working example: style to attributes, round pathData, rgb to hex

    let paths = document.querySelectorAll('path');
    let decimals = 2;
    paths.forEach((path, c) => {
    
      /**
       * 1. convert inline css style to 
       * svg presentational attributes
       */
      let style = path.style;
      if (style.length) {
        for (let i = 0; i < style.length; i++) {
          let propertyName = style.item(i);
          let value = style.getPropertyValue(propertyName);
    
          // 1.2 convert rgb colors to hex
          if ((propertyName === 'stroke' || propertyName === 'fill') && value !== 'none') {
            //hexToRgb
            let [r, g, b] = value.replaceAll('rgb(', '').replaceAll(')', '').split(',');
            let colorHex = rgbToHex(r, g, b);
            value = colorHex;
          }
          //set attribute
          path.setAttribute(propertyName, value)
    
        }
        // remove style attribute
        path.removeAttribute('style')
      }
    
      /**
       * 1.3 optional: convert transform matrix 
       * to readable properties
       */
      let transform = window.getComputedStyle(path).transform;
      let matrix = path.transform.baseVal[0].matrix;
      let transforms = qrDecomposeMatrix(matrix);
      path.setAttribute('transform', transforms.svgTransform)
    
    
      /**
       * 2. round pathdata
       * requires getPathData() polyfill
       */
      roundPath(path, decimals)
      
      // output
      output.value = new XMLSerializer().serializeToString(svg)
    
    
    })
    
    
    /**
     * round pathData
     */
    function roundPath(path, decimals = 3) {
      let pathData = path.getPathData();
      pathData.forEach((com, c) => {
        if (decimals >= 0) {
          com.values.forEach((val, v) => {
            pathData[c].values[v] = +val.toFixed(decimals);
          });
        }
      });
      // apply rounded
      path.setPathData(pathData)
    }
    
    
    
    /**
     *  Decompose matrix to readable transform properties 
     *  translate() rotate() scale() etc.
     *  based on @AndreaBogazzi's answer
     *  https://stackoverflow.com/questions/5107134/find-the-rotation-and-skew-of-a-matrix-transformation#32125700
     *  return object with seperate transform properties 
     *  and ready to use css or svg attribute strings
     */
    function qrDecomposeMatrix(matrix, precision = 1) {
      let {
        a,
        b,
        c,
        d,
        e,
        f
      } = matrix;
      // matrix is array
      if (Array.isArray(matrix)) {
        [a, b, c, d, e, f] = matrix;
      }
    
      let angle = Math.atan2(b, a),
        denom = Math.pow(a, 2) + Math.pow(b, 2),
        scaleX = Math.sqrt(denom),
        scaleY = (a * d - c * b) / scaleX,
        skewX = Math.atan2(a * c + b * d, denom) / (Math.PI / 180),
        translateX = e ? e : 0,
        translateY = f ? f : 0,
        rotate = angle ? angle / (Math.PI / 180) : 0;
      let transObj = {
        translateX: translateX,
        translateY: translateY,
        rotate: rotate,
        scaleX: scaleX,
        scaleY: scaleY,
        skewX: skewX,
        skewY: 0
      };
    
      let cssTransforms = [];
      let svgTransforms = [];
      for (let prop in transObj) {
        transObj[prop] = +parseFloat(transObj[prop]).toFixed(precision);
        let val = transObj[prop];
        let unit = "";
        if (prop == "rotate" || prop == "skewX") {
          unit = "deg";
        }
        if (prop.indexOf("translate") != -1) {
          unit = "px";
        }
    
        // combine these properties
        let convert = ["scaleX", "scaleY", "translateX", "translateY"];
    
        if (val !== 0) {
          cssTransforms.push(`${prop}(${val}${unit})`);
        }
    
        if (convert.indexOf(prop) == -1 && val !== 0) {
          svgTransforms.push(`${prop}(${val})`);
        } else if (prop == "scaleX") {
          svgTransforms.push(
            `scale(${+scaleX.toFixed(precision)} ${+scaleY.toFixed(precision)})`
          );
        } else if (prop == "translateX") {
          svgTransforms.push(
            `translate(${transObj.translateX} ${transObj.translateY})`
          );
        }
    
      }
      // append css style string to object
      transObj.cssTransform = cssTransforms.join(" ");
      transObj.svgTransform = svgTransforms.join(" ");
      return transObj;
    }
    
    
    
    /**
     *  based on @Tim Down's answer
     *  https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb#5624139
     */
    function rgbToHex(r, g, b) {
      return "#" + (1 << 24 | r << 16 | g << 8 | b).toString(16).slice(1);
    }
    body {
      padding: 2em;
    }
    
    svg {
      border: 1px solid #ccc;
      overflow: visible;
      width: 50%;
    }
    
    textarea{
    min-height:20em;
    width:100%;
    }
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/path-data-polyfill.min.js"></script>
    
    
    <svg id="svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="107.7 331.25 3.5 3.5">
            <g>
                <path
                    style="fill:none;stroke-width:0.074;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(50.19989%,50.19989%,50.19989%);stroke-opacity:1;stroke-miterlimit:10;"
                    d="M 110.949219 265.421875 L 109.730469 265.691406 L 108.699219 266.410156 L 108.011719 267.460938 L 107.78125 268.679688 "
                    transform="matrix(1,0,0,-1,0,600)" />
                <path
                    style="fill:none;stroke-width:0.074;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(50.19989%,50.19989%,50.19989%);stroke-opacity:1;stroke-miterlimit:10;"
                    d="M 111.050781 268.679688 L 111.050781 265.421875 " transform="matrix(1,0,0,-1,0,600)" />
                <path
                    style="fill:none;stroke-width:0.074;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(50.19989%,50.19989%,50.19989%);stroke-opacity:1;stroke-miterlimit:10;"
                    d="M 110.949219 268.679688 L 110.949219 265.421875 " transform="matrix(1,0,0,-1,0,600)" />
            </g>
        </svg>
        
        
        <textarea id="output"></textarea>

    1. Replace inline css style with svg attribute

    Get each path’s style:

    let style = path.style;
    

    Loop through the style list and replace style with attribute

    for (let i = 0; i < style.length; i++) {
        let propertyName = style.item(i);
        let value = style.getPropertyValue(propertyName);
        //set attribute
        path.setAttribute(propertyName, value)
    }
    // remove style attribute
    path.removeAttribute('style')
    

    1.2 Color conversion rgb to hex

    See SO answer "RGB to hex and hex to RGB"

    1.3 Optional: decompose the transform matrix

    You might also prefer separate transformation properties like scale(), translate().
    Based on @AndreaBogazzi’s answer

    2. Round pathData

    Use a reliable parser like getPathData() polyfill and parse your pathData like so

    /**
     * round pathData
     */
     let pathData = path.getPathData();
     roundPath(path, 2)
    
    function roundPath(path, decimals = 3) {
        let pathData = path.getPathData();
        pathData.forEach((com, c) => {
            if (decimals >= 0) {
                com.values.forEach((val, v) => {
                    pathData[c].values[v] = +val.toFixed(decimals);
                });
            }
        });
        // apply rounded
        path.setPathData(pathData)
    }
    

    Since getPathData() returns an array of objects like this

    {type:'L', values: [10 20]}
    

    We can easily round each commands values.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search