From 97a29e6adaeee0cc2623405f9d7137337906a823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Lehni?= Date: Sat, 2 Nov 2013 09:30:40 +0100 Subject: [PATCH] Improve SVG path data parser. --- src/path/PathItem.js | 44 +++++++++++++------------------------------- 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/src/path/PathItem.js b/src/path/PathItem.js index a46876b7..5aefa674 100644 --- a/src/path/PathItem.js +++ b/src/path/PathItem.js @@ -85,25 +85,16 @@ var PathItem = Item.extend(/** @lends PathItem# */{ // This is a very compact SVG Path Data parser that works both for Path // and CompoundPath. - var parts = data.match(/[a-z][^a-z]*/ig), + // First split the path data into parts of command-coordinates pairs + // Commands are any of these characters: mzlhvcsqta + var parts = data.match(/[mzlhvcsqta][^mzlhvcsqta]*/ig), coords, - coordsLength, relative = false, control, current = new Point(); // the current position function getCoord(index, coord, update) { - var val = coords[index]; - // Before parsing the value, we might actually have to further split - // it, in case it contains two '.': - var match = val.match(/^([+-]?\d*\.\d+)(\.\d*)$/); - if (match) { - // Insert the 2nd half as the next value. - coords.splice(index + 1, 0, match[2]); - coordsLength++; - val = match[1]; - } - val = parseFloat(val); + var val = parseFloat(coords[index]); if (relative) val += current[coord]; if (update) @@ -125,36 +116,27 @@ var PathItem = Item.extend(/** @lends PathItem# */{ var part = parts[i], cmd = part[0], lower = cmd.toLowerCase(); - // Split at white-space, commas but also before signs. - // Use positive lookahead to include signs. - // Note that we should also check for coordinate values that contain - // two decimal periods, as these should be plit into two separate - // values. With positive lookbehind, this would look like this: - // |(?<=\d*\.\d+)(?=\.\d+) - // Unfortunately, JavaScript does not have lookbehind in RegExps, so - // instead we leave these values that way and parse them further in - // getCoord() above. Note that coordsLength may increase as we're - // reading values. - coords = part.slice(1).trim().split(/[\s,]+|(?=[+-])/); - coordsLength = coords.length; + // Match all coordinate values + coords = part.match(/([+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?)/g); + var length = coords && coords.length; relative = cmd === lower; switch (lower) { case 'm': case 'l': - for (var j = 0; j < coordsLength; j += 2) + for (var j = 0; j < length; j += 2) this[j === 0 && lower === 'm' ? 'moveTo' : 'lineTo']( getPoint(j, true)); break; case 'h': case 'v': var coord = lower == 'h' ? 'x' : 'y'; - for (var j = 0; j < coordsLength; j++) { + for (var j = 0; j < length; j++) { getCoord(j, coord, true); this.lineTo(current); } break; case 'c': - for (var j = 0; j < coordsLength; j += 6) { + for (var j = 0; j < length; j += 6) { this.cubicCurveTo( getPoint(j), control = getPoint(j + 2), @@ -163,7 +145,7 @@ var PathItem = Item.extend(/** @lends PathItem# */{ break; case 's': // Shorthand cubic bezierCurveTo, absolute - for (var j = 0; j < coordsLength; j += 4) { + for (var j = 0; j < length; j += 4) { this.cubicCurveTo( // Calculate reflection of previous control points current.multiply(2).subtract(control), @@ -172,14 +154,14 @@ var PathItem = Item.extend(/** @lends PathItem# */{ } break; case 'q': - for (var j = 0; j < coordsLength; j += 4) { + for (var j = 0; j < length; j += 4) { this.quadraticCurveTo( control = getPoint(j), getPoint(j + 2, true)); } break; case 't': - for (var j = 0; j < coordsLength; j += 2) { + for (var j = 0; j < length; j += 2) { this.quadraticCurveTo( // Calculate reflection of previous control points control = current.multiply(2).subtract(control),