1

I'm transforming my SVG icons to fonts by icomoon.io and I wanna use them in my React web app

the problem is when I import these three SVG icons to the Icomoon project, the appearance of my icons will change and I don't know why this is happening

other SVGs imported properly and without any change in appearance

SVG icons before import:

enter image description here

after importing to icomoom.io:

enter image description here

I also added SVG icons code here: codesandbox

Is it possible that the problem is with the SVGs icons?

Carl
  • 191
  • 1
  • 8

1 Answers1

0

Your icon <path> elements have a non ideal structure (at least for apps like icomoon):

  • apps like icomoon can't convert fill-rule like "evenodd":
    your paths need alternating path directions
  • the path' d attribute (pathdata string) should start with the outer most shapes/sub paths

<p>First subpath: should be second; needs reversed path direction</p>
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <path d="M4.43934 3.43934C4.72064 3.15804 5.10218 3 5.5 3H14.5C14.8978 3 15.2794 3.15804 15.5607 3.43934C15.842 3.72064 16 4.10218 16 4.5V6H9.5C8.11929 6 7 7.11929 7 8.5V17H5.5C5.10218 17 4.72064 16.842 4.43934 16.5607C4.15804 16.2794 4 15.8978 4 15.5V4.5C4 4.10218 4.15804 3.72064 4.43934 3.43934Z
" />
</svg>

<p>Second subpath: should be first; direction correct</p>

<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <path d="M7 18H5.5C4.83696 18 4.20107 17.7366 3.73223 17.2678C3.26339 16.7989 3 16.163 3 15.5V4.5C3 3.83696 3.26339 3.20107 3.73223 2.73223C4.20107 2.26339 4.83696 2 5.5 2H14.5C15.163 2 15.7989 2.26339 16.2678 2.73223C16.7366 3.20107 17 3.83696 17 4.5V6H18.5C19.8807 6 21 7.11929 21 8.5V19.5C21 20.8807 19.8807 22 18.5 22H9.5C8.11929 22 7 20.8807 7 19.5V18Z
" />
</svg>
<p>Third subpath: order correct; needs reversed path direction</p>
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <path d="M8 8.5C8 7.67157 8.67157 7 9.5 7H18.5C19.3284 7 20 7.67157 20 8.5V19.5C20 20.3284 19.3284 21 18.5 21H9.5C8.67157 21 8 20.3284 8 19.5V8.5Z
" />
</svg>

<p>Fixed path:</p>

<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <path d="
M7 18H5.5C4.83696 18 4.20107 17.7366 3.73223 17.2678C3.26339 16.7989 3 16.163 3 15.5V4.5C3 3.83696 3.26339 3.20107 3.73223 2.73223C4.20107 2.26339 4.83696 2 5.5 2H14.5C15.163 2 15.7989 2.26339 16.2678 2.73223C16.7366 3.20107 17 3.83696 17 4.5V6H18.5C19.8807 6 21 7.11929 21 8.5V19.5C21 20.8807 19.8807 22 18.5 22H9.5C8.11929 22 7 20.8807 7 19.5V18Z
           
M4.4393 3.4393C4.158 3.7206 4 4.1022 4 4.5V15.5C4 15.8978 4.158 16.2794 4.4393 16.5607C4.7206 16.842 5.1022 17 5.5 17H7V8.5C7 7.1193 8.1193 6 9.5 6H16V4.5C16 4.1022 15.842 3.7206 15.5607 3.4393C15.2794 3.158 14.8978 3 14.5 3H5.5C5.1022 3 4.7206 3.158 4.4393 3.4393Z
           
M8 8.5V19.5C8 20.3284 8.6716 21 9.5 21H18.5C19.3284 21 20 20.3284 20 19.5V8.5C20 7.6716 19.3284 7 18.5 7H9.5C8.6716 7 8 7.6716 8 8.5Z
" />
</svg>

Fix issues with java script helper methods

const svgs = document.querySelectorAll("svg");
const precision = 3;
let svgMarkup = "";

removeFillRules();
function removeFillRules(){
    svgs.forEach((svg) => {
    const path = svg.querySelector("path");
    svg.removeAttribute("fill");
    path.removeAttribute("fill");
    path.removeAttribute("fill-rule");
    path.removeAttribute("clip-rule");
    });
}

function fixAllPathDirections() {
  svgs.forEach((svg) => {
    const path = svg.querySelector("path");
    // change path directions - if necessary
    fixPath(path);
    // update markup
    svgMarkup += svg.outerHTML;
  });

  svgOpt.value = svgMarkup;
}

function fixPath(path) {
  // get pathData array
  let pathData = path.getPathData({
    normalize: true
  });
  // split sub paths
  let pathDataSubArr = splitSubpaths(pathData, true);
  // fix path directions
  let fixedPathData = fixInnerPathDirections(path, pathDataSubArr);
  // convert to relative commands and round coordinates
  let pathDataMin = pathDataToRelative(fixedPathData, precision);
  path.setPathData(pathDataMin);
}

/**
 * helpers
 */
function fixInnerPathDirections(path, pathDataSubArr) {
  let svg = path.closest("svg");
  let fixedPathData = [];
  let subPathEls = [];
  let bbO = path.getBBox();
  let [xO, yO, wO, hO, rO, bO] = [
    bbO.x,
    bbO.y,
    bbO.width,
    bbO.height,
    bbO.x + bbO.width,
    bbO.y + bbO.height
  ];
  let outerPathData = pathDataSubArr[0];
  let outerPathTmp = document.createElementNS(
    "http://www.w3.org/2000/svg",
    "path"
  );
  outerPathTmp.setPathData(outerPathData);
  outerPathTmp.classList.add("outer");
  let outerClockwise = isClockwise(outerPathTmp);

  pathDataSubArr.forEach(function (pathDataSub, i) {
    // create temporary subpath elements for checking positions
    let subPath = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "path"
    );
    subPath.setPathData(pathDataSub);
    subPath.setAttribute("stroke", "red");
    subPath.setAttribute("stroke-width", "1");
    subPath.setAttribute("fill", "none");
    svg.appendChild(subPath);
    subPathEls.push(subPath);
    let bb = subPath.getBBox();
    let [x, y, w, h, r, b] = [
      bb.x,
      bb.y,
      bb.width,
      bb.height,
      bb.x + bb.width,
      bb.y + bb.height
    ];
    // remove temporary subpaths
    subPath.remove();

    if (i > 0) {
      // is subpath
        let isClockwiseInner = isClockwise(subPath);
        // if subpath has same direction as outer path: reverse direction
        if (isClockwiseInner == outerClockwise) {
          pathDataSub = reversePathData(pathDataSub);
        }
    }
    // concatenate subpaths to compound path data
    fixedPathData = fixedPathData.concat(pathDataSub);
  });
  return fixedPathData;
}

function reversePathData(pathData) {
  let M = pathData[0];
  let newPathData = [M];
  // split subpaths
  let subPathDataArr = splitSubpaths(pathData, true);
  subPathDataArr.forEach(function (subPathData, s) {
    let subPathDataL = subPathData.length;
    let closed = subPathData[subPathDataL - 1]["type"] == "Z" ? true : false;
    let subM = subPathData[0]["values"];

    // insert Lineto if last path segment has created by z
    let lastCom = closed
      ? subPathData[subPathDataL - 2]
      : subPathData[subPathDataL - 1];
    let lastComL = lastCom["values"].length;
    let lastXY = [
      lastCom["values"][lastComL - 2],
      lastCom["values"][lastComL - 1]
    ];
    let diff = Math.abs(subM[0] - lastXY[0]);
    if (diff > 1 && closed) {
      subPathData.pop();
      subPathData.push({
        type: "L",
        values: [subM[0], subM[1]]
      });
      subPathData.push({
        type: "Z",
        values: []
      });
    }
    subPathData.forEach( (com, i)=> {
      // reverse index
      let subpathDataL = subPathData.length;
      let indexR = subpathDataL - 1 - i;
      let comR = subPathData[indexR];
      let comF = subPathData[i];
      let [typeR, valuesR] = [comR["type"], comR["values"]];
      let [typeF, valuesF] = [comF["type"], comF["values"]];
      if (typeF == "M" && s > 0) {
        newPathData.push(comF);
      } else if (typeR != "M" && typeR != "Z") {
        indexR--;
        let prevCom =
          i > 0 ? subPathData[indexR] : subPathData[subpathDataL - 1 - i];
        let prevVals = prevCom
          ? prevCom["values"]
            ? prevCom["values"]
            : [0, 0]
          : [];
        prevVals = prevCom["values"];
        let prevValsL = prevVals.length;
        let newCoords = [];

        if (typeR == "C") {
          newCoords = [
            valuesR[2],
            valuesR[3],
            valuesR[0],
            valuesR[1],
            prevVals[prevValsL - 2],
            prevVals[prevValsL - 1]
          ];
          if (!closed) {
            let nextVals =
              i < subpathDataL - 1 ? subPathData[i + 1]["values"] : lastXY;
            let lastCX =
              i < subpathDataL - 2 ? nextVals[prevValsL - 2] : subM[0];
            let lastCY =
              i < subpathDataL - 2 ? nextVals[prevValsL - 1] : subM[1];
            newCoords[4] = lastCX;
            newCoords[5] = lastCY;
          }
        } else {
          newCoords = [prevVals[prevValsL - 2], prevVals[prevValsL - 1]];
        }
        newPathData.push({
          type: typeR,
          values: newCoords
        });
      }
    });
    //use last coordinates as M values if path isn't closed
    if (!closed) {
      newPathData[0]["values"] = lastXY;
    }
    if (closed) {
      newPathData.push({
        type: "Z",
        values: []
      });
    }
  });
  return newPathData;
}

/**
 * check path direction of polygon
 * based on @mpen
 * https://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order/1180256#answer-46613578
 *
 */
function isClockwise(path) {
  let length = path.getTotalLength();
  let vertices = 6;
  let step = length / vertices;
  let poly = [];
  for (let l = 0; l < length; l += step) {
    let p = path.getPointAtLength(l);
    poly.push([p.x, p.y]);
  }

  if (!poly || poly.length < 3) return null;
  let end = poly.length - 1;
  let area = poly[end][0] * poly[0][1] - poly[0][0] * poly[end][1];
  for (let i = 0; i < end; ++i) {
    const n = i + 1;
    area += poly[i][0] * poly[n][1] - poly[n][0] * poly[i][1];
  }
  let cw = area > 0;
  return cw;
}

function splitSubpaths(pathData, sortByPosition = false) {
  let pathDataL = pathData.length;
  let subPathArr = [];
  let subPathMindex = [];
  pathData.forEach(function (com, i) {
    let [type, values] = [com["type"], com["values"]];
    if (type == "M") {
      subPathMindex.push(i);
    }
  });
  //split segments after M command
  subPathMindex.forEach(function (index, i) {
    let n = subPathMindex[i + 1];
    let thisSeg = pathData.slice(index, n);
    subPathArr.push(thisSeg);
  });

  subPathArr = sortByPosition ? sortSubpaths(subPathArr) : subPathArr;
  return subPathArr;
}

function sortSubpaths(pathDataArr) {
  let subPathArr = [];
  let ns = "http://www.w3.org/2000/svg";
  pathDataArr.forEach(function (pathData, i) {
    let svg = document.createElementNS(ns, "svg");
    let newSvg = document.createElementNS(ns, "path");
    document.body.appendChild(svg);
    svg.appendChild(newSvg);
    newSvg.setPathData(pathData);
    let bb = newSvg.getBBox();
    svg.remove();
    let [x, y, width, height] = [bb.x, bb.y, bb.width, bb.height];
    subPathArr.push({
      x: x,
      y: y,
      width: width,
      height: height,
      index: i
    });
  });

  //sort by position: left most is outer path and must be first in pathdata array
  subPathArr.sort(fieldSorter(["x", "y"]));
  let subPathsSorted = [];
  subPathArr.forEach(function (sub, i) {
    let index = sub.index;
    subPathsSorted.push(pathDataArr[index]);
  });
  return subPathsSorted;
}

// sort object by key values
function fieldSorter(fields) {
  return function (a, b) {
    return fields
      .map(function (o) {
        var dir = 1;
        if (o[0] === "-") {
          dir = -1;
          o = o.substring(1);
        }
        if (a[o] > b[o]) return dir;
        if (a[o] < b[o]) return -dir;
        return 0;
      })
      .reduce(function firstNonZeroValue(p, n) {
        return p ? p : n;
      }, 0);
  };
}

function pathDataToRelative(pathDataR, decimals = null) {
  let M = pathDataR[0].values;
  let x = M[0],
    y = M[1],
    mx = x,
    my = y;
  // loop through commands
  for (let i = 1; i < pathDataR.length; i++) {
    let cmd = pathDataR[i];
    let type = cmd.type;
    let typeRel = type.toLowerCase();
    let values = cmd.values;

    // is absolute
    if (type != typeRel) {
      type = typeRel;
      cmd.type = type;
      // check current command types
      switch (typeRel) {
        case "a":
          values[5] = +(values[5] - x);
          values[6] = +(values[6] - y);
          break;
        case "v":
          values[0] = +(values[0] - y);
          break;
        case "m":
          mx = values[0];
          my = values[1];
        default:
          // other commands
          if (values.length) {
            for (let v = 0; v < values.length; v++) {
              // even value indices are y coordinates
              values[v] = values[v] - (v % 2 ? y : x);
            }
          }
      }
    }
    // is already relative
    else {
      if (cmd.type == "m") {
        mx = values[0] + x;
        my = values[1] + y;
      }
    }
    let vLen = values.length;
    switch (type) {
      case "z":
        x = mx;
        y = my;
        break;
      case "h":
        x += values[vLen - 1];
        break;
      case "v":
        y += values[vLen - 1];
        break;
      default:
        x += values[vLen - 2];
        y += values[vLen - 1];
    }

    // round coordinates
    if (decimals !== null && decimals >= 0) {
      cmd.values = values.map((val) => {
        return +val.toFixed(decimals);
      });
    }
  }

  return pathDataR;
}
svg{
  width:10em;
  height:auto;
}

textarea{
  width:100%;
  display:block;
}
<script src="https://cdn.jsdelivr.net/npm/path-data-polyfill@1.0.4/path-data-polyfill.min.js"></script>


<p><button type="button" onclick="fixAllPathDirections()">Fix path directions</button></p>


<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.43934 3.43934C4.72064 3.15804 5.10218 3 5.5 3H14.5C14.8978 3 15.2794 3.15804 15.5607 3.43934C15.842 3.72064 16 4.10218 16 4.5V6H9.5C8.11929 6 7 7.11929 7 8.5V17H5.5C5.10218 17 4.72064 16.842 4.43934 16.5607C4.15804 16.2794 4 15.8978 4 15.5V4.5C4 4.10218 4.15804 3.72064 4.43934 3.43934ZM7 18H5.5C4.83696 18 4.20107 17.7366 3.73223 17.2678C3.26339 16.7989 3 16.163 3 15.5V4.5C3 3.83696 3.26339 3.20107 3.73223 2.73223C4.20107 2.26339 4.83696 2 5.5 2H14.5C15.163 2 15.7989 2.26339 16.2678 2.73223C16.7366 3.20107 17 3.83696 17 4.5V6H18.5C19.8807 6 21 7.11929 21 8.5V19.5C21 20.8807 19.8807 22 18.5 22H9.5C8.11929 22 7 20.8807 7 19.5V18ZM8 8.5C8 7.67157 8.67157 7 9.5 7H18.5C19.3284 7 20 7.67157 20 8.5V19.5C20 20.3284 19.3284 21 18.5 21H9.5C8.67157 21 8 20.3284 8 19.5V8.5Z" fill="#090B0C"/>
</svg>


<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.6425 20.776C14.5607 21.2229 13.406 21.4653 12.2371 21.4917C12.1587 21.5 12.0796 21.5041 12 21.5041C11.9191 21.5041 11.8389 21.4998 11.7592 21.4913C10.2869 21.4556 8.84203 21.077 7.53999 20.3841L4.53999 21.3841C4.27332 21.4699 3.98805 21.4797 3.71608 21.4126C3.44412 21.3455 3.19617 21.2041 2.99999 21.0041C2.80348 20.8058 2.6658 20.5569 2.60225 20.285C2.5387 20.0132 2.55177 19.729 2.64 19.4641L3.64 16.4641C2.93187 15.1331 2.55389 13.6597 2.52727 12.167C2.50938 12.1151 2.5 12.0601 2.5 12.0041C2.5 11.9484 2.5093 11.8937 2.52703 11.842C2.53379 11.4282 2.5676 11.0136 2.62889 10.6008C2.92768 8.58846 3.86387 6.72502 5.29997 5.28412C6.18137 4.40152 7.22814 3.70134 8.38036 3.22362C9.53258 2.74589 10.7677 2.5 12.015 2.5C13.2623 2.5 14.4974 2.74589 15.6496 3.22362C16.8019 3.70134 17.8486 4.40152 18.73 5.28412C19.6126 6.16551 20.3128 7.21226 20.7905 8.36448C21.2682 9.5167 21.5141 10.7518 21.5141 11.9991C21.5141 13.2465 21.2682 14.4815 20.7905 15.6338C20.3128 16.786 19.6126 17.8327 18.73 18.7141C17.8468 19.5982 16.7975 20.2989 15.6425 20.776ZM3.53132 12.5041C3.6058 13.7898 3.9716 15.0492 4.60997 16.1841C4.6372 16.249 4.65123 16.3187 4.65123 16.3891C4.65123 16.4595 4.6372 16.5292 4.60997 16.5941L3.52998 19.8241C3.49967 19.9118 3.49486 20.0062 3.5161 20.0965C3.53734 20.1868 3.58376 20.2692 3.64998 20.3341C3.71492 20.4003 3.79731 20.4468 3.88759 20.468C3.97787 20.4893 4.07234 20.4844 4.15999 20.4541L7.39998 19.3741L7.58998 19.3441C7.67693 19.3437 7.76269 19.3643 7.83998 19.4041C8.41912 19.7279 9.03045 19.9809 9.66093 20.1605C8.41941 18.5939 7.58307 15.7952 7.50585 12.5041H3.53132ZM7.5059 11.5041H3.53224C3.54508 11.2895 3.56608 11.0749 3.59532 10.8607C3.84624 9.02304 4.69062 7.31768 5.99999 6.00412C7.02869 4.98375 8.29044 4.25207 9.65941 3.8609C8.42336 5.42995 7.58366 8.22223 7.5059 11.5041ZM8.5059 11.5041C8.61312 6.98876 10.1627 3.89296 11.7055 3.53821C11.8034 3.53482 11.9016 3.53312 12 3.53312C12.0984 3.53312 12.1965 3.53482 12.2945 3.53821C13.8373 3.89297 15.3869 6.98877 15.4941 11.5041H8.5059ZM8.5059 12.5041H15.4941C15.3854 17.0805 13.7952 20.1986 12.232 20.4829C12.078 20.4872 11.9241 20.4874 11.7705 20.4834C10.2065 20.2019 8.61462 17.0829 8.5059 12.5041ZM16.4942 12.5041C16.417 15.7915 15.5825 18.5877 14.3432 20.1553C15.7179 19.7603 16.9776 19.023 18 18.0041C19.4651 16.5271 20.335 14.5696 20.4563 12.5041H16.4942ZM20.4563 11.5041C20.335 9.43866 19.4651 7.48116 18 6.00412C16.9713 4.98376 15.7096 4.25208 14.3406 3.86092C15.5766 5.42996 16.4163 8.22224 16.4941 11.5041H20.4563Z" fill="#707B84"/>
</svg>


<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 3C9.74566 3 7.58365 3.89554 5.98959 5.4896C5.08214 6.39705 4.40106 7.48855 3.98269 8.67636C4.29987 8.53507 4.64607 8.46002 5 8.46002C5.39782 8.46002 5.77936 8.61806 6.06066 8.89936C6.34197 9.18067 6.5 9.5622 6.5 9.96002V14.96C6.5 15.3578 6.34197 15.7394 6.06066 16.0207C5.77936 16.302 5.39782 16.46 5 16.46C4.33696 16.46 3.70107 16.1966 3.23223 15.7278C2.76339 15.2589 2.5 14.6231 2.5 13.96V13.5V11.5V10.96C2.5 10.769 2.52187 10.5802 2.56421 10.397C2.81121 8.28359 3.76284 6.30213 5.28248 4.78249C7.06407 3.0009 9.48044 2 12 2C14.5196 2 16.9359 3.0009 18.7175 4.78249C20.2372 6.30213 21.1888 8.28359 21.4358 10.397C21.4781 10.5802 21.5 10.769 21.5 10.96V11.5V13.5V13.96C21.5 14.6231 21.2366 15.2589 20.7678 15.7278C20.5284 15.9672 20.2454 16.153 19.9375 16.2776C19.7444 17.5625 19.1023 18.7392 18.1227 19.5973C17.1927 20.412 16.0179 20.8874 14.7897 20.9525C14.4779 21.5512 13.8517 21.96 13.13 21.96H10.87C9.83722 21.96 9 21.1228 9 20.09V19.83C9 18.7973 9.83722 17.96 10.87 17.96H13.13C14.1628 17.96 15 18.7973 15 19.83V19.9321C15.9087 19.8303 16.7696 19.4532 17.4638 18.8451C18.1814 18.2164 18.6777 17.3788 18.8871 16.4558C18.5306 16.4289 18.1939 16.2753 17.9393 16.0207C17.658 15.7394 17.5 15.3578 17.5 14.96V9.96002C17.5 9.5622 17.658 9.18067 17.9393 8.89936C18.2206 8.61806 18.6022 8.46002 19 8.46002C19.3539 8.46002 19.7001 8.53508 20.0173 8.67637C19.5989 7.48855 18.9179 6.39705 18.0104 5.4896C16.4164 3.89554 14.2543 3 12 3ZM20.4494 10.5737C20.382 10.3208 20.2489 10.0876 20.0607 9.89936C19.7794 9.61806 19.3978 9.46002 19 9.46002C18.8674 9.46002 18.7402 9.5127 18.6464 9.60647C18.5527 9.70023 18.5 9.82741 18.5 9.96002V14.96C18.5 15.0926 18.5527 15.2198 18.6464 15.3136C18.7402 15.4073 18.8674 15.46 19 15.46C19.3978 15.46 19.7794 15.302 20.0607 15.0207C20.342 14.7394 20.5 14.3578 20.5 13.96V13.5V11.5C20.5 11.1891 20.483 10.8799 20.4494 10.5737ZM3.55059 10.5737C3.51704 10.8799 3.5 11.1891 3.5 11.5V13.5V13.96C3.5 14.3578 3.65803 14.7394 3.93934 15.0207C4.22064 15.302 4.60218 15.46 5 15.46C5.13261 15.46 5.25979 15.4073 5.35355 15.3136C5.44732 15.2198 5.5 15.0926 5.5 14.96V9.96002C5.5 9.82741 5.44732 9.70024 5.35355 9.60647C5.25979 9.5127 5.13261 9.46002 5 9.46002C4.60217 9.46002 4.22064 9.61806 3.93934 9.89936C3.75106 10.0876 3.618 10.3208 3.55059 10.5737ZM10 19.83C10 19.3495 10.3895 18.96 10.87 18.96H13.13C13.6105 18.96 14 19.3495 14 19.83V20.09C14 20.5705 13.6105 20.96 13.13 20.96H10.87C10.3895 20.96 10 20.5705 10 20.09V19.83Z" fill="black"/>
</svg>


<textarea  id="svgOpt" cols="30" rows="10"></textarea>

How it works

  • path commands are parsed (using path-data-polyfill by Jarek Foksa)
    to an array of absolute commands via path.getPathData({normalize: true}):
    all commands will be converted to a reduced set of commands using only M, L, C, Z commands.
  • splitSubpaths(pathData) splits up path data into sub paths (each sub path starts with a new M command)
  • sortSubpaths(pathDataArr) sorts sub paths by their x and y position:
    The left most sub paths will become the first element
  • fixInnerPathDirections(svg, path, pathDataSubArr) compares outer and inner path directions (using the helper isClockwise(path):
    if the outer sub path is drawn clockwise – all inner sub paths need to be counter-clockwise (or vice versa)
  • concatenate all sub paths and overwrite the original d property

Codepen example:
You can also play around with this codepen helper: Fix svg compound path direction

Alternative: refactor paths in an graphic editor

  1. Decompose compound paths (inkscape: Path / Break apart
  2. Subtract inner paths from outer shape (inkscape: Path / Exclusion)
    See also: "Converting an svg with fill-rule="evenodd" to fill-rule="nonzero""
herrstrietzel
  • 11,541
  • 2
  • 12
  • 34