svginkscape

Change SVG paths to be clipped according to clipPath element


I have a SVG that looks like this:

<svg id="Layer_1" data-name="Layer 1"
  xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 576 576">
  <defs>
    <style>...</style>
    <clipPath id="clip-path">
      <rect class="cls-1" x="0.02" width="575.04" height="576"/>
    </clipPath>
  </defs>
  <g class="cls-2">
    <path class="cls-3" d="M137.91-147.28c-4-1.67-8.25-3.46-12.37-3.86-5.43-.53-9.26,1.73-12.55,5a18.75,18.75,0,0,0-4.69-9.42,19.23,19.23,0,0,0-6.45-...
    <path class="cls-4" d="M.08,502.59c-.79-5.67-6.22-4.3-5.81-.22a17.15,17.15,0,0,1,0,2.95c-.22,2.82-1.46,7.6-5,7.61-1.35,0-2.61-1-3.12...
    ...

I want to change it such that:

I've tried this:

inkscape --actions \
 "select-all:groups; SelectionUnGroup; ObjectUnSetClipPath; export-filename: output.svg; export-plain-svg; export-do;" \
 Decorations.svg

This removes the grouping, but the path elements are not clipped. The clipPath is still present.


Solution

  • ObjectUnSetClipPath will remove the clipping attribute clip-path="url(#clip-path)" – not the <def> clipping path itself.

    But you can't clip any element, if your clipPath definition is stripped.

    function stripClip(el){
      let clipPaths = document.querySelector(el).querySelectorAll('clipPath');
      if(clipPaths.length){
        clipPaths.forEach(function(item, i){
          item.remove();
        }
        )
      }
    }
    svg{
    width: 20vw;
    }
    
    .clipped{
    clip-path: url(#clip-path2);
    }
      <p><button onclick="stripClip('#svg1')" >remove ClipPath</button></p>
      
    <svg id="svg1" data-name="Layer 1"
      xmlns="http://www.w3.org/2000/svg"
      xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 36 36">
      <defs>
        <style>...</style>
        <clipPath id="clip-path">
          <rect class="cls-1" x="10" width="36" height="18"/>
        </clipPath>
      </defs>
      <g class="cls-2">
       <path clip-path="url(#clip-path)" d="M18 2.0845
      a 15.9155 15.916 0 0 1 0 31.83
      a 15.916 15.916 0 0 1 0 -31.83z" fill="red" />
      <rect  clip-path="url(#clip-path)" x="0" y="0" width="10" height="25" />
      </g>
      </svg>
      
      
      <svg id="svg2" data-name="Layer 1"
      xmlns="http://www.w3.org/2000/svg"
      xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 36 36">
      <defs>
        <style>...</style>
        <clipPath id="clip-path2">
          <rect class="cls-1" x="10" width="36" height="18"/>
        </clipPath>
      </defs>
      <g class="cls-2">
       <path class="clipped" d="M18 2.0845
      a 15.9155 15.916 0 0 1 0 31.83
      a 15.916 15.916 0 0 1 0 -31.83z" fill="red" />
      <rect class="clipped" x="0" y="0" width="10" height="25" />
      </g>
      </svg>
      

    Edit: Get svg intersection paths using paper.js

    Admittedly, not the most convenient approach but it is possible:
    In addition to paper.js, we also need some script/helper to convert svg shapes (circles, rects etc.) to <path> elements (I'm using 'pathThatSvg' , since paper.js can only calculate new intersection paths based on 2 path elements like so:

    var intersectionPath = path1.intersect(clipPath);

    Based on this answer

    var svg = document.querySelector("#svgIntersect");
    
    // set auto ids for processing
    function setAutoIDs(svg) {
      var svgtEls = svg.querySelectorAll(
        "path, polygon, rect, circle, line, text, g"
      );
      svgtEls.forEach(function (el, i) {
        if (!el.getAttribute("id")) {
          el.id = el.nodeName + "-" + i;
        }
      });
    }
    setAutoIDs(svg);
    
    // convert shapes to paths
    function shapesToPath(svg) {  
            pathThatSvg(svg.outerHTML).then((converted) => {
              var tmp = document.createElement("div");
              tmp.innerHTML = converted;
              svg.innerHTML = tmp.querySelector("svg").innerHTML;
            });
    }
    shapesToPath(svg);
    
    
    function intersectPath(svg, decimals=2) {
      // init paper.js and add canvas
      canvas = document.createElement('canvas');
      canvas.id = "canvasPaper";
      canvas.setAttribute('style','display:none')
      document.body.appendChild(canvas);
      paper.setup("canvasPaper");
    
      // process clipped elements
      var all = paper.project.importSVG(svg, function (item, i) {
        item.position = new paper.Point(
          item.bounds.width / 2,
          item.bounds.height / 2
        );
        //item.scale(0.5, new Point(0, 0) )
        var items = item.getItems();
        var ids = item._namedChildren;
    
        var groups = item.children;
        groups.forEach(function (gr, i) {
          var group = gr["_namedChildren"];
          if (group) {
            for (key in group) {
              //get clip path
              var clip = group["clipPath"][0];
              if (key !== "clipPath") {
                var el = group[key][0];
                 //get intersection path and generate d commands
                var elClipped = el.intersect(clip);
                var elClippedD = elClipped
                  .exportSVG({ precision: decimals })
                  .getAttribute("d");
                // select path by id and overwrite d attribute
                var newEl = svg.querySelector("#" + key);
                newEl.setAttribute("d", elClippedD);
              }
            }
          }
        });
    
        // remove clip defs and attributes
        var clippedEls = svg.querySelectorAll("[clip-path]");
        clippedEls.forEach(function (clippedEl, e) {
          clippedEl.removeAttribute("clip-path");
        });
        svg.querySelector("defs").remove();
        svg.classList.add("svg-intersect");
        console.log(svg.outerHTML)
      });
    }
    svg{
      border: 1px solid #ccc;
      display:inline-block;
      width:200px;
    }
    
    .svg-intersect path{
      stroke:red;
      stroke-width: 0.25;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.0/paper-full.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/path-that-svg@1.2.4/dist/pathThatSvg.umd.min.js"></script>
    <p>
      <button type="button" onclick="intersectPath(svg)">get Path Intersect</button>
    </p>
    
    <svg id="svgIntersect" viewBox="0 0 100 100">
      <defs>
        <clipPath id="clipPath">
          <circle cx="25" cy="25" r="25"/>
        </clipPath>
      </defs>
      <g id="clipGroup" clip-path="url(#clipPath)">
        <circle fill="#999" data-id="circle" cx="25" cy="25" r="25" />
        <rect fill="#555" id="rect" x="25" y="25" width="50" height="50" />
      </g>
      
      <g  clip-path="url(#clipPath)">
        <circle fill="#444" id="circle2" cx="66" cy="25" r="25" />
        <rect fill="#22" id="rect2" x="15" y="12.5" width="20" height="75" />
      </g>
    </svg>