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 x="0.02" width="575.04" height="576"/>
</clipPath>
</defs>
<g >
<path 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 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:
- there is no grouping (this is easy)
- change the
path
,rect
, etc. elements such that they are clipped according to the what's in theclipPath
element. TheclipPath
should no longer be present in the SVG (because it isn't needed anymore)
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.
CodePudding user response:
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 x="10" width="36" height="18"/>
</clipPath>
</defs>
<g >
<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 x="10" width="36" height="18"/>
</clipPath>
</defs>
<g >
<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 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/[email protected]/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>