Bake all path transform attributes into path data instead
by Chris Ball
HTML
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 256 256">
<defs>
<linearGradient id="a">
<stop stop-color="#fff" offset="0"/>
<stop stop-color="#fff" offset=".09512668"/>
<stop stop-color="#3674b9" offset=".60349512"/>
<stop stop-color="#204670" offset=".79231769"/>
<stop stop-color="#204670" offset="1"/>
</linearGradient>
<radialGradient id="b" gradientUnits="userSpaceOnUse" cy="525.06256" cx="-639.67908" r="60.861691" gradientTransform="matrix(1 0 0 .33195 0 350.7679)">
<stop stop-color="#254462" offset="0"/>
<stop stop-color="#264766" stop-opacity="0" offset="1"/>
</radialGradient>
<linearGradient id="c" y2="411.57959" xlink:href="#a" gradientUnits="userSpaceOnUse" x1="-552.76746" y1="526.99841" x2="-667.4364" gradientTransform="matrix(.99884 0 0 .998699 752.84249 -359.1503)"/>
<linearGradient id="d" x1="-663.59308" y1="408.98203" y2="526.19476" xlink:href="#a" gradientUnits="userSpaceOnUse" x2="-553.11768" gradientTransform="matrix(.99884 0 0 .998699 752.84249 -359.1503)"/>
<filter id="e" x="-.19337018" y="-.58252768" width="1.3867404" height="2.1650554">
<feGaussianBlur stdDeviation="9.8073636"/>
</filter>
</defs>
<path opacity=".827" d="m-578.81739 525.06256a60.861691 20.203051 0 1 1 -121.72338 0 60.861691 20.203051 0 1 1 121.72338 0z" transform="matrix(1.5595491 0 0 .65670714 1125.6109 -117.85785)" filter="url(#e)" fill="url(#b)"/>
<g transform="translate(-38.378418,-1098.68629)scale(1.3440586)"
stroke="#235a8e" stroke-miterlimit="4" stroke-width="9.2">
<path d="m125.94863 941.08846c23.21169 9.13191 53.06862-13.63189 53.06862-50.58675 0-43.88609-27.62085-68.41369-53.09762-68.41369-40.892422 0-79.835602 33.79608-79.835602 80.03589 0 51.396 44.64038 79.86497 79.835602 79.86497-7.96447-1.14675-51.649062-6.83395-52.005782-67.96677-.23987-41.3466 30.630432-57.86551 51.948132-50.59905.47743.17707 23.51392 9.26451 23.51392 38.95053 0 29.55992-23.42727...
JavaScript
// Helper tool to piece together Raphael's paths into strings again
Array.prototype.flatten || (Array.prototype.flatten = function() {
return this.reduce(function(a, b) {
return a.concat('function' === typeof b.flatten ? b.flatten() : b);
}, []);
});
// Bake the transforms into all path nodes and remove the transform attributes
var paths = [].slice.call(document.querySelectorAll('path'), 0);
paths.forEach(function bake(p) {
var before = p.getAttribute('d'), after = applyTransforms(p);
console.log('before/after path #'+arguments[1]+':\n', before, '\n', after);
p.setAttribute('d', after);
});
paths.forEach(function removeTransforms(p) {
do {
p.removeAttribute('transform');
p = p.parentNode;
} while (!/^svg$/i.test(p.nodeName)); // walk to the SVG root, to catch <g>s
});
// done!
// Uses Raphael.path2curve and Raphael.pathToRelative
//
// Calculates a new <path d> attribute relative to a given root (<svg>) element,
// folding in all the transforms into the path data itself so it can move there,
// and get rid of its transform attribute.
function applyTransforms(path, root) {
function point(x, y) { var p = svg.createSVGPoint(); p.x=x; p.y=y; return p; }
// add a copy of the path at the same level of the hierarchy to see transforms
path = path.parentElement.appendChild(path.cloneNode(false));
// turn all arc commands into splines so we can transform them even with skews
path.setAttribute('d', path2curve(path));
var svg = path.ownerSVGElement,
normal = (root||svg).getScreenCTM().inverse(), // compensation for root
matrix = normal.multiply(path.getCTM()), // transform, relative to svg root
_ = '',
coords = [_, 1, 2], // main and optional handle 1 and 2 coordinates
segs = path.pathSegList,
len = segs.numberOfItems,
x = { 0: 0, '': 0, 1: 0, 2: 0 },
y = { 0: 0, '': 0, 1: 0, 2: 0 },
i = -1,
seg, cmd;
// simplify the logic by normalizing to absolute coordinates...