npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

svg-parse-path-normalized

v1.0.5

Published

Parse svg path data from string and normalize it to calculable value arrays

Downloads

749

Readme

svg-parse-path-normalized

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

This library aims to provide a performant yet compact (~6KB/3KB minified; gzipped) parser – respecting all minified/shorthand notations as a basis for all kinds of custom path data manipulations.

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" ;
let pathData = parsePathDataNormalized(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
}

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

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
pathDataToD(pathData, decimals, minify) 

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 |

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