Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert SVG Path to Absolute Commands

Tags:

javascript

svg

Given an SVG Path element, how can I convert all path commands into absolute coordinates? For example, convert this path:

<path d="M17,42 l100,0 v100 h-100 z"/>

into this equivalent path:

<path d="M17,42 L117,42 V142 H17 Z"/>

This question was motivated by this question.

like image 777
Phrogz Avatar asked Mar 13 '12 03:03

Phrogz


People also ask

How do you convert to absolute path?

The absolutePath function works by beginning at the starting folder and moving up one level for each "../" in the relative path. Then it concatenates the changed starting folder with the relative path to produce the equivalent absolute path.

Is it possible to draw any path in SVG?

It can draw anything! I've heard that under the hood all the other drawing elements ultimately use path anyway. The path element takes a single attribute to describe what it draws: the d attribute.

What is Z in SVG path?

We can shorten the above path declaration a little bit by using the "Close Path" command, called with Z . This command draws a straight line from the current position back to the first point of the path. It is often placed at the end of a path node, although not always.

What is the path of an SVG?

A path is defined in SVG using the 'path' element. The basic shapes are all described in terms of what their equivalent path is, which is what their shape is as a path. (The equivalent path of a 'path' element is simply the path itself.)


3 Answers

Here's the JavaScript code I came up with:

function convertToAbsolute(path){
  var x0,y0,x1,y1,x2,y2,segs = path.pathSegList;
  for (var x=0,y=0,i=0,len=segs.numberOfItems;i<len;++i){
    var seg = segs.getItem(i), c=seg.pathSegTypeAsLetter;
    if (/[MLHVCSQTA]/.test(c)){
      if ('x' in seg) x=seg.x;
      if ('y' in seg) y=seg.y;
    }else{
      if ('x1' in seg) x1=x+seg.x1;
      if ('x2' in seg) x2=x+seg.x2;
      if ('y1' in seg) y1=y+seg.y1;
      if ('y2' in seg) y2=y+seg.y2;
      if ('x'  in seg) x+=seg.x;
      if ('y'  in seg) y+=seg.y;
      switch(c){
        case 'm': segs.replaceItem(path.createSVGPathSegMovetoAbs(x,y),i);                   break;
        case 'l': segs.replaceItem(path.createSVGPathSegLinetoAbs(x,y),i);                   break;
        case 'h': segs.replaceItem(path.createSVGPathSegLinetoHorizontalAbs(x),i);           break;
        case 'v': segs.replaceItem(path.createSVGPathSegLinetoVerticalAbs(y),i);             break;
        case 'c': segs.replaceItem(path.createSVGPathSegCurvetoCubicAbs(x,y,x1,y1,x2,y2),i); break;
        case 's': segs.replaceItem(path.createSVGPathSegCurvetoCubicSmoothAbs(x,y,x2,y2),i); break;
        case 'q': segs.replaceItem(path.createSVGPathSegCurvetoQuadraticAbs(x,y,x1,y1),i);   break;
        case 't': segs.replaceItem(path.createSVGPathSegCurvetoQuadraticSmoothAbs(x,y),i);   break;
        case 'a': segs.replaceItem(path.createSVGPathSegArcAbs(x,y,seg.r1,seg.r2,seg.angle,seg.largeArcFlag,seg.sweepFlag),i);   break;
        case 'z': case 'Z': x=x0; y=y0; break;
      }
    }
    // Record the start of a subpath
    if (c=='M' || c=='m') x0=x, y0=y;
  }
}

Used like so with the path from the question:

var path = document.querySelector('path');
convertToAbsolute(path);
console.log(path.getAttribute('d'));
// M 17 42 L 117 42 V 142 H 17 Z

Edit: Here's a test page with a path that includes every command (absolute and relative) interleaved and shows that the conversion works in the now-current versions of IE, Chrome, FF, and Safari.
http://phrogz.net/svg/convert_path_to_absolute_commands.svg

like image 165
Phrogz Avatar answered Nov 15 '22 11:11

Phrogz


If you have Raphaël, you have both Raphael.pathToRelative and Raphael._pathToAbsolute.

Raphael._pathToAbsolute is not in documentation (like pathToRelative) and is used in many places in Raphael source internally. But it can be used also externally, if you add _ before function name this way: Raphael._pathToAbsolute.

Online conversion: http://jsbin.com/mudusiseta

The usage is the same as relative one:

<script src="raphael.js"></script>
<script>
  // ...

  var paper = Raphael(10, 50, 320, 200);
  var path_string = "M10 10 L 20 20 L 100 10"; // Original coordinates
  var path = paper.path(path_string);

  // To relative coordinates
  var path_string_rel = Raphael.pathToRelative(path_string);
  console.log(path_string_rel);

  // To absolute coordinates
  var path_string_abs = Raphael._pathToAbsolute(path_string_rel);
  console.log(path_string_abs);

  // ...    
</script>

I don't know why pathToAbsolute is not in documentation. It should.

If you want this to work in Web Workers (to speedup code), it is easy to grab the desired function from Raphael (which should be allowed by the licence) and use it as a DOM free method. Raphael is a DOM manipulating library (as well as jQuery) and it cannot be used in Workers, because DOM is not supported in Workers. If you have very complex paths, the browser may hang and to prevent this Web Workers provide a solution in modern browsers.

like image 24
Timo Kähkönen Avatar answered Nov 15 '22 10:11

Timo Kähkönen


Here is library for svg path (d attr) manipulations: https://github.com/fontello/svgpath.

like image 1
Vitaly Avatar answered Nov 15 '22 09:11

Vitaly