Parse svg path data from string and normalize it to calculable value arrays
npm install svg-parse-path-normalized
A arcto commands quite often lightweight parsers crash since they can't unravel concatenated largeArc, sweep and final on path x values.
A arcs to cubics – your choice – by default the parser will return the least destructive normalization.
a arcto commands that may rather complicate further manipulations such as length or area calculations – especially when dealing with elliptical and/or rotated arcs.
parsePathDataNormalized(d) applies by default these conversions:
m 0 0 .5.5.5.5 to M 0 0 l 0.5 0.5 l 0.5 0.5
h, v, s, t to L, C, T
d attribute – including minification options
pathDataConvert.js: Useful to convert your manipulated/processed path data to all kind of command types/structures
getPathData() or other parsers compliant with the w3C SVGPathData interface draft format recommendations.
/ shorthand notation /
let pathData = parseD(d, options)
`
`
/ original notation /
let pathData = parsePathDataNormalized(d, options)
`
Feel free to post an issue or write a post in the discussion if the recent update messed up anything.
$3
` lang-html
`
Optional: Load minified script via jsDelivr (~6KB/3KB minified; gzipped)
`
`
`
`
$3
`
npm install svg-parse-path-normalized
`
` lang-js
const parsepathData = require('svg-parse-path-normalized');
const {parsePathDataNormalized, pathDataToD} = parsepathData;
//parse
const d ="m 0 0 .5.5.5.5a 5 10 45 1040 20" ;
let pathData = parsePathDataNormalized(d)
//stringify to pathdata d string
let minify = false;
let dNew = pathDataToD(pathData, 1, minify);
console.log(pathData);
console.log(dNew);
`
3. Pathdata format
This library uses the pathdata format as suggested in the w3C SVGPathData interface draft.
The returned path data parsed from a stringified pathdata d attribute string is an array representing each command as an object like so:
` lang-js
const d ="m 0 0 .5.5.5.5a 5 1045 1040 20"
parsePathDataNormalized(d)
`
` lang-js
[
{"type":"M","values":[0,0]},
{"type":"L","values":[0.5, 0.5]},
{"type":"L","values":[1, 1]},
{"type":"A","values":[5, 10, 45, 1, 0, 41, 21]}
]
`
The above example illustrates a problem with overly "lightweight" path parsers:
We need an extra check to "unravel" the A arcto's largeArc and sweep flags, which can be concatenated with the subsequent on-path x coordinate value. (See basic example)
4. All normalization options
parsePathDataNormalized(d, options) accepts these parameters
`
let options= {
normalize: null, //shorthand for aggressive normalisation
toAbsolute: true, //necessary for most calculations
unshort: true, //dito
arcToCubic: false, //sometimes necessary
quadraticToCubic: false, //frankly, not necessary most of the time
lineToCubic: false, //handy for morphing animations
debug: false, //handy to detect malformed pathdata retrieved from user inputs
decimals: -1 //-1=no rounding
}
`
| parameter | default | effect |
| -- | -- | -- |
| toAbsolute | true | convert all to absolute |
| unshort | true | convert all shorthands to longhands |
| arcToCubic | false | convert arcs A commands to cubic béziers |
| quadraticToCubic | false | convert quadratic to cubic béziers |
| lineToCubic | false | convert all L linetos to cubic béziers (handy for morphing animations) |
| decimals | -1 | round values to floating point decimals. -1=no rounding |
| debug | false | reports malformed path data structures via console.log|
| normalize | null | shorthand to also convert arcs and quadratic béziers to cubic – similar to the W3C draft's suggested getPathData({normalize:true}) parameter |
$3
Set normalize to false to get the original (not normalized) pathdata – including relative or shorthand commands.
parsePathDataNormalized(d, {normalize:false})
$3
* Quadratic béziers usually provide much faster calculations/algorithms – think twice before converting to cubic.
* debug:true can be handy if you need to find errors in malformed pathdata – maybe caused by manual path splitting
* Arc to cubic conversion/approximation is quite complex and thus quite expensive – you may not need this conversion
5. Stringify to back to
d attribute string
Options:
* decimals: rounds pathdata
* minify: omits command letters for implicit or repeated commands and leading zeros
You can stringify the path data to a d attribute (or CSS property) by a chained prototype method or the basic function like so:
`
let d = pathData.toD(decimals, minify)
`
which is just a wrapper for the actual stringifying function.
`
let d = pathDataToD(pathData, decimals, minify)
`
and eventually apply it like so:
`
path.setAttribute('d', d);
`
----
6. More conversions via pathDataConvert.js
Load pathDataConvert.js to get more conversion methods. This script is intended to provide various conversions to optimize the path data after processing e.g for a minified path output.
| parameter | default | effect |
| -- | -- | -- |
| toRelative | false | convert all to relative |
| toAbsolute | true | convert all to absolute |
| toShorthands | false | convert all to to shorthands – if applicable |
| toLonghands | true | convert all shorthands to longhands |
| arcToCubic | false | convert arcs A commands to cubic béziers |
| lineToCubic | false | convert all L linetos to cubic béziers (handy for morphing animations) |
| quadraticToCubic | false | convert quadratic to cubic béziers |
| cubicToQuadratic | false | convert all cubic to quadratic |
| cubicToQuadraticPrecision | 0.1 | cubic to quadratic accuracy |
| decimals | -1 | round values to floating point decimals. -1=no rounding |
| normalize | null , true, false | shorthand to also convert arcs and quadratic béziers to cubic – similar to the W3C draft's suggested getPathData({normalize:true}) parameter |
| optimize | false | shorthand to convert to shorthands, relative and round to 3 decimals for a more compact output |
$3
`
`
Load minified via jsDelivr (13KB/6KB minified)
`
`
`
let options = {arcToCubic:true, toRelative:true, decimals:0}
let pathDataCon = pathData.convert(options)
`
Conversion can be applied via
* chainable prototype method convert(options) to apply all conversions at once
* separate chainable methods like pathData.toAbsolute(), pathData.toRelative(), pathData.toLonghands(), pathData.toShorthands(), pathData.round(), pathData.toQuadratic(), pathData.toVerbose()
* individual functions like pathDataToAbsolute(pathData), pathDataToRelative(pathData), pathDataToShorthands(pathData), pathDataToShorthands(pathData), pathDataToQuadratic(pathData), roundPathData(pathData)
$3
Currently, the W3C draft for the SVGPathData interface is not supported by any major browser. Fortunately Jarek Foksa wrote a this great polyfill library and also contributed to the potential spec outcome – most importantly that it should include geometry elements like circle, rect, polygon, line to retrieve path data.
This polyfill is a "battle-proof" parser! Since the W3C draft doesn't include fine-grained control over the normalisation/conversion process you can use the pathDataConvert.js script as an addon/plugin alongside with the aforementioned polyfill script. (See Demo/getPathDataAddon.html)
$3
You may already have a set of parsed/abstracted path data retrieved from other libraries or APIs or need a more verbose notation.
In this case you may use these conversion methods.
#### 6.3.1 Array notation to pathdata
A lot of libraries – such as snap.svg use a nested array structure for each command like so
`
[
["M", 0, 0] ,
["L", 0.5, 0.5],
["L", 1, 1],
["A", 5, 10, 45, 1, 0, 41, 21]
]
`
In case you need to convert these you can use the helper methods (included in pathDataConvert.js) to convert format in both directions
* convertArrayPathData(pathDataArray)
* revertPathDataToArray(pathData)
#### 6.3.2 pathDataToVerbose(pathData)
Besides you can use pathDataToVerbose(pathData) to get a more detailed data array including original and absolute point coordinates as well as parametrized arc data rx and ry, startAngle, endAngle, deltaAngle (in radians)
`
let data = [
{
type: "M",
values: [0, 0],
valuesAbsolute: [0, 0],
pFinal: { x: 0, y: 0 },
isRelative: false
},
{
type: "l",
values: [0.5, 0.5],
valuesAbsolute: [0.5, 0.5],
pFinal: { x: 0.5, y: 0.5 },
isRelative: true,
pPrev: { x: 0, y: 0 }
},
{
type: "l",
values: [0.5, 0.5],
valuesAbsolute: [1, 1],
pFinal: { x: 1, y: 1 },
isRelative: true,
pPrev: { x: 0.5, y: 0.5 }
},
{
type: "a",
values: [5, 10, 45, 1, 0, 40, 20],
valuesAbsolute: [5, 10, 45, 1, 0, 41, 21],
pFinal: { x: 41, y: 21 },
isRelative: true,
pPrev: { x: 1, y: 1 },
rx: 21.505813167606572,
ry: 43.011626335213144,
xAxisRotation: 45,
largeArcFlag: 1,
sweepFlag: 0,
startAngle: 2.976443999504017,
endAngle: 6.118036608390327,
deltaAngle: -3.1415926982932767
}
];
`
Changelog
* 1.1.0 fixes a rare parsing issue where 'M' commands were omitted (e.g z followed by another drawing command than M – unfortunately valid). See updated demo with "path-from-hell3".
7. Demos
* parse pathdata with different normalization options (demos/index.html)
* pathDataConvert.js as a addon/plugin for path.getPathData()` (demos/getPathDataAddon.html)