1.0.8 • Published 2 months ago

svg-parse-path-normalized v1.0.8

Weekly downloads
-
License
MIT
Repository
github
Last release
2 months ago

npm version

svg-parse-path-normalized

Parses path data from string including fine-grained normalisation and conversion options.

This library aims to provide a robust and versatile yet quite compact (~6KB/3KB minified; gzipped) parser – respecting all minified/shorthand notations as a basis for all kinds of custom path data manipulations. Compatible with the w3C SVGPathData interface draft format recommendations.

Yet another SVG path parser?

While there is no shortage of excellent parsers – unfortunately, the same applies to rather incomplete ones often deployed in libraries due to their appealing lightweight codebase.

  1. Minified A arcto commands quite often crash lightweight parsers since they can't unravell concatenated largeArc, sweep and final on path x values.
  2. You may not need to load a full fledged SVG library but just a robust foundation for your specific SVG path manipulations.
  3. Finegrained normalization: Normalize as little as possible: Usually you need at least absolute coordinates as well as "unshortened" ones. You may in some case need to convert quadratic béziers to cubics or A arcs to cubics – your choice – by default the parser will return the least destructive normalization.
  4. Just parsing without normalization? Fair enough, works as well e.g if you need a to scale the path data proportinally/keeping the aspect ratio.
  5. Debugging Sometimes you may also need some hints to what's wrong with your current path data – e.g if paths were sliced or concatenated incorrectly. The debugging option will return info about the problematic commands.

Table of content

1. Basic functionality and helpers

1.1 Parse, normalize and stringify

Usually parsing alone is not enough to get computable path data values – due to relative or shorthand commands or a arcto commands that may rather complicate further manipulations such as length or area calculations – especially when dealing with elliptical and/or rotated arcs.

Normalization (admittedly a slightly ambigious term) via parsePathDataNormalized(d) applies by default these conversions:

  • (default) all commands to absolute
  • (default) decompose implicit or repeated commands
    e.g m 0 0 .5.5.5.5 to M 0 0 l 0.5 0.5 l 0.5 0.5
  • commands to shorthand/reflected commands to longhand equivalents like e.g h, v, s, t to L, C, T
  • (optional) convert/approximate arcs to cubics
  • (optional) convert quadratic béziers to cubics
  • (optional) debug: detect malformed path data inputs
  • (optional) round coordinates
  • stringify to d attribute – including minification options

1.2 Advanced conversions

Provided by pathDataConvert.js: Useful to convert your manipulated/processed path data to all kind of command types/structures (E.g to get a more compact or special formats like lineto-to-bezier conversions for morphing animations by converting)

  • all commands to relative (usually more concise in file size)
  • apply shorthands – if possible (also decreases filesize)
  • linetos to cubic or quadratic béziers
  • cubic béziers to quadratic
  • different path data formats e.g array based path data notations as used in snap.svg and other libraries or APIs
  • this scripts also includes all normalizations options such as relative-absolute, shorthand-to-longhands, rounding etc.
  • can be used as an addon complementing getPathData() or other parsers compliant with the w3C SVGPathData interface draft format recommendations.

2. Usage parser

2.1 Browser

<script src="https://www.unpkg.com/svg-parse-path-normalized@latest/js/pathDataParseNormalized.js"></script>

Optional: Load minified script via jsDelivr (~6KB/3KB minified; gzipped)

<!--basic parser --->
<script src="https://cdn.jsdelivr.net/npm/svg-parse-path-normalized@latest/js/pathDataParseNormalized.min.js"></script>
<script>

//parse
const d ="m 0 0 .5.5.5.5a 5 10 45 1040 20" ;
/* verbose notation */
//let pathData = parsePathDataNormalized(d)

/* shorthand notation */
let pathData = parseD(d)

//stringify to pathdata d string
let minify = false;
let dNew = pathDataToD(pathData, 1, minify);

console.log(pathData);
console.log(dNew);

</script>

2.2 Node

npm install svg-parse-path-normalized
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:

const d ="m 0 0 .5.5.5.5a 5 1045 1040 20" 
parsePathDataNormalized(d)
[
    {"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
}
parameterdefaulteffect
toAbsolutetrueconvert all to absolute
unshorttrueconvert all shorthands to longhands
arcToCubicfalseconvert arcs A commands to cubic béziers
quadraticToCubicfalseconvert quadratic to cubic béziers
lineToCubicfalseconvert all L linetos to cubic béziers (handy for morphing animations)
decimals-1round values to floating point decimals. -1=no rounding
debugfalsereports malformed path data structures via console.log
normalizenullshorthand to also convert arcs and quadratic béziers to cubic – similar to the W3C draft's suggested getPathData({normalize:true}) parameter

4.1 Original path data: normalization disabled

Set normalize to false to get the original (not normalized) pathdata – including relative or shorthand commands.

parsePathDataNormalized(d, {normalize:false})

4.2 Recommendations

  • 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.

parameterdefaulteffect
toRelativefalseconvert all to relative
toAbsolutetrueconvert all to absolute
toShorthandsfalseconvert all to to shorthands – if applicable
toLonghandstrueconvert all shorthands to longhands
arcToCubicfalseconvert arcs A commands to cubic béziers
lineToCubicfalseconvert all L linetos to cubic béziers (handy for morphing animations)
quadraticToCubicfalseconvert quadratic to cubic béziers
cubicToQuadraticfalseconvert all cubic to quadratic
cubicToQuadraticPrecision0.1cubic to quadratic accuracy
decimals-1round values to floating point decimals. -1=no rounding
normalizenull , true, falseshorthand to also convert arcs and quadratic béziers to cubic – similar to the W3C draft's suggested getPathData({normalize:true}) parameter
optimizefalseshorthand to convert to shorthands, relative and round to 3 decimals for a more compact output

6.1 Usage

<script src="https://www.unpkg.com/svg-parse-path-normalized@latest/js/pathDataConvert.js"></script>

Load minified via jsDelivr (13KB/6KB minified)

<!-- optional conversions -->
<script src="https://cdn.jsdelivr.net/npm/svg-parse-path-normalized@latest/js/pathDataConvert.min.js"></script>
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)

6.2 Usage as an addon/plugin for getPathData()

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)

6.3 Convert pathdata structure

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
  }
];

7. Demos

Credits

1.0.8

2 months ago

1.0.7

6 months ago

1.0.6

7 months ago

1.0.5

10 months ago

1.0.4

11 months ago

1.0.3

11 months ago

1.0.2

11 months ago

1.0.1

11 months ago

1.0.0

11 months ago