Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to normalize SVG path data (cross browser)?

I have tried to find a way to implement cross browser path normalizer. There IS a native way which is described here and functional example is here, but it works only in newest Opera (but not in IE, FF, Safari, Chrome).

The native way uses pathElm.normalizedPathSegList and it converts all relative coordinates to absolute ones and represents all path segment types as a following subset of types: M,L,C,z.

I have found only one javascript code and jsfiddled functional example of it, but it works only in IE and FF. Chrome gives "Uncaught Error: INDEX_SIZE_ERR: DOM Exception 1". How this could be fixed to work also in Opera, Safari and Chrome or is there any other way for normalizing SVG paths?

like image 824
Timo Kähkönen Avatar asked Oct 19 '12 15:10

Timo Kähkönen


People also ask

How do Paths work in SVG?

The <path> element is the most powerful element in the SVG library of basic shapes. It can be used to create lines, curves, arcs, and more. Paths create complex shapes by combining multiple straight lines or curved lines. Complex shapes composed only of straight lines can be created as <polyline> s.

What is path normalize?

What is path normalization? Normalizing a path involves modifying the string that identifies a path or file so that it conforms to a valid path on the target operating system. Normalization typically involves: Canonicalizing component and directory separators. Applying the current directory to a relative path.

What is d attribute in SVG?

The d attribute defines a path to be drawn. A path definition is a list of path commands where each command is composed of a command letter and numbers that represent the command parameters.


1 Answers

EDIT: Do not use this! I tested it more and realized that the A->C conversion is not reliable in all cases and also some other path command combinations fail. Please use this instead!

Finally got it to work in Safari, Opera, IE9, Firefox and Chrome: http://jsfiddle.net/timo2012/M6Bhh/41/

The function normalizes SVG path's data, so that all path segments are converted to M, C, L and z ( = absolute coordinates, which means that all relative coordinates are converted to absolute ones). All other segments are trivial and 100% accurate, but arc (A) is a special case and you can select whether arcs are converted to lines (L), quadratic curves (Q) or cubic curves (C). The most accurate are lines, but then we lose resolution independence. Quadratics for some reason fails in certain arcs, but cubics are more accurate.

If we have the following path:

<svg width="400" height="400">
    <path stroke="red" stroke-width="3" d="M30 30 S40 23 23 42 L23,42 C113.333,113.333 136.667,113.333 150,80 t40,50 T230,240 q20 20 54 20 s40 23 23 42 t20,30 a20,30 0,0,1 -50,-50"/>
</svg>

and normalize it using:

var path = document.querySelector('path');
path.normalizePath(3, 0.1); // 3 = C = cubic curves. Best alternative, rather good accuracy and path data remains reasonable sized

the normalized version is this:

<svg width="400" height="400">
    <path stroke="red" stroke-width="3" d="M 30 30 C 30 30 40 23 23 42 L 23 42 C 113.333 113.333 136.667 113.333 150 80 C 150 80 163.333 96.6667 190 130 C 216.667 163.333 230 200 230 240 C 243.333 253.333 261.333 260 284 260 C 284 260 324 283 307 302 C 307 302 313.667 312 327 332 C 324.811 336.924 321.997 341.154 318.719 344.448 C 315.441 347.741 311.762 350.033 307.893 351.194 C 304.024 352.355 300.04 352.361 296.169 351.213 C 292.298 350.064 288.616 347.783 285.333 344.5 C 282.05 341.217 279.23 336.996 277.035 332.078 C 274.839 327.161 273.311 321.642 272.537 315.839 C 271.763 310.035 271.759 304.06 272.525 298.254 C 273.291 292.448 274.811 286.924 277 282"/>
</svg>

If we layout both on top of each other the result is this (red is normalized and black is original):

Normalized path and original

Other possibilities are these:

path.normalizePath(1,0.5); // A->L, Many lines, high accuracy. Very good accuracy, but not so resolution independent, because when scaled, the corners become visible

path.normalizePath(1,40); // A->L, Few lines, less accuracy

path.normalizePath(2,0.5); // A->Q, quadratic curves. I tested this, but not good. Fails in some cases.

And what are the benefits of this?

The native way for normalizing path data is not implemented yet in all browsers, so we are on our own so far. And when the native way is implemented, we are not sure that all browsers makes it same way. The SVG documentation speaks of converting arcs to lines, but this is not a good way, because SVG:s main advantage - resolution independence - will be lost. We should have a full control how normalization of arcs is done, and this script provides a way to it.

When data is normalized, it can be altered exactly the same way as coordinates in bitmap images. If we want to warp (Arc, Arch, Bulge, Shell, Flag, Wave, Fish, Rise, Fisheye, Inflate, Squeeze, Twist) paths in a Illustrator way or distort paths to achieve perspective illusion, the normalized path data can be modified reliably.

The code is based on YannickBochatay's script and I made it more cross browser.

like image 99
Timo Kähkönen Avatar answered Sep 23 '22 13:09

Timo Kähkönen