How to bake the transform matrix into path coordinates with Raphael JS 2

Right click the T and hit "Inspect Element" to see the actual path data.

by Chris Ball

HTML

<svg xmlns="http://www.w3.org/2000/svg" viewBox="291 197 45 39">
<path id="black" transform="matrix(0.1389, 0, 0, 0.1389, 291.91, 235.465)" d="M150,-265C182,-275,171,-220,180,-194L178,-194C181,-192,180,-185,181,-180C211,-169,282,-173,306,-166C308,-169,316,-171,315,-165C268,-157,225,-148,184,-137C188,-118,186,-96,190,-79L282,-131C289,-131,296,-135,297,-126C293,-118,271,-105,236,-80C190,-48,155,-20,125,-6C112,-15,115,-34,111,-51C121,-70,108,-107,107,-133C70,-132,-5,-126,0,-157C18,-170,39,-181,64,-191C75,-190,92,-181,100,-185C99,-198,95,-211,89,-225Z">
</svg>

CSS

body { background: #445; }
#pink {
  stroke-width: 0.1;
  stroke: pink;
  fill:   rgba(255,0,0,0.1);
}

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);
  }, []);
});

// The only reason to create a new element here is to show that it's perfectly
// overlaid with the original – you can just as well be destructive on `path`:
var path = document.querySelector('path'),
  pink = path.parentNode.appendChild(path.cloneNode(false));
pink.id = 'pink';
pink.setAttribute('d', applyTransforms(pink));
pink.removeAttribute('transform');
// 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).getCTM().inverse() // compensation for root's scaling
    , 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 first
  absolutizePath(path);

  // walk the path, applying all transforms between us and the root as we go
  while (++i < len) {
    seg = segs.getItem(i);
    cmd =...