UNPKG

svg-pathdata

Version:

Manipulate SVG path data (path[d] attribute content) simply and efficiently.

309 lines (238 loc) 9.36 kB
[//]: # ( ) [//]: # (This file is automatically generated by a `metapak`) [//]: # (module. Do not change it except between the) [//]: # (`content:start/end` flags, your changes would) [//]: # (be overridden.) [//]: # ( ) # svg-pathdata > Manipulate SVG path data (path[d] attribute content) simply and efficiently. [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/nfroidure/svg-pathdata/blob/main/LICENSE) [![Coverage Status](https://coveralls.io/repos/github/nfroidure/svg-pathdata/badge.svg?branch=main)](https://coveralls.io/github/nfroidure/svg-pathdata?branch=main) [//]: # (::contents:start) ## Usage Install the module: ```sh npm install --save svg-pathdata ``` Then in your JavaScript files: ```js import { SVGPathData, SVGPathDataTransformer, SVGPathDataEncoder, SVGPathDataParser, } from 'svg-pathdata'; ``` ## Reading PathData ```js const pathData = new SVGPathData(` M 10 10 H 60 V 60 L 10 60 Z`); console.log(pathData.commands); // [ {type: SVGPathData.MOVE_TO, relative: false, x: 10, y: 10}, // {type: SVGPathData.HORIZ_LINE_TO, relative: false, x: 60}, // {type: SVGPathData.VERT_LINE_TO, relative: false, y: 60}, // {type: SVGPathData.LINE_TO, relative: false, x: 10, y: 60}, // {type: SVGPathData.CLOSE_PATH}] ``` ## Reading PathData in chunks ```js const parser = new SVGPathDataParser(); parser.parse(' '); // returns [] parser.parse('M 10'); // returns [] parser.parse(' 10'); // returns [{type: SVGPathData.MOVE_TO, relative: false, x: 10, y: 10 }] parser.write('H 60'); // returns [{type: SVGPathData.HORIZ_LINE_TO, relative: false, x: 60 }] parser.write('V'); // returns [] parser.write('60'); // returns [{type: SVGPathData.VERT_LINE_TO, relative: false, y: 60 }] parser.write('L 10 60 \n Z'); // returns [ // {type: SVGPathData.LINE_TO, relative: false, x: 10, y: 60 }, // {type: SVGPathData.CLOSE_PATH }] parser.finish(); // tell parser there is no more data: will throw if there are unfinished commands. ``` ## Outputting PathData ```js const pathData = new SVGPathData(` M 10 10 H 60 V 60 L 10 60 Z`); // returns "M10 10H60V60L10 60Z" encodeSVGPath({ type: SVGPathData.MOVE_TO, relative: false, x: 10, y: 10 }); // returns "M10 10" encodeSVGPath({ type: SVGPathData.HORIZ_LINE_TO, relative: false, x: 60 }); // returns "H60" encodeSVGPath([ { type: SVGPathData.VERT_LINE_TO, relative: false, y: 60 }, { type: SVGPathData.LINE_TO, relative: false, x: 10, y: 60 }, { type: SVGPathData.CLOSE_PATH }, ]); // returns "V60L10 60Z" ``` ## Transforming PathData This library can perform transformations on SVG paths. Here is [an example of that kind of use](https://github.com/nfroidure/svgicons2svgfont/blob/aa6df0211419e9d61c417c63bcc353f0cb2ea0c8/src/index.js#L192). ### Transforming entire paths ```js new SVGPathData(` m 10,10 h 60 v 60 l 10,60 z`) .toAbs() .encode(); // return s"M10,10 H70 V70 L80,130 Z" ``` ### Transforming partial data Here, we take SVGPathData from stdin and output it transformed to stdout. ```js const transformingParser = new SVGPathDataParser().toAbs().scale(2, 2); transformingParser.parse('m 0 0'); // returns [{ type: SVGPathData.MOVE_TO, relative: false, x: 0, y: 0 }] transformingParser.parse('l 2 3'); // returns [{ type: SVGPathData.LINE_TO, relative: false, x: 4, y: 6 }] ``` ## Supported transformations You can find all supported transformations in [src/SVGPathDataTransformer.ts](https://github.com/nfroidure/SVGPathData/blob/master/src/SVGPathDataTransformer.ts#L47). Additionally, you can create your own by writing a function with the following signature: ```js type TransformFunction = (command: SVGCommand) => SVGCommand | SVGCommand[]; function SET_X_TO(xValue = 10) { return function(command) { command.x = xValue; // transform command objects and return them return command; }; }; // Synchronous usage new SVGPathData('...') .transform(SET_X_TO(25)) .encode(); // Chunk usage new SVGPathDataParser().transform(SET_X_TO(25)); ``` ## Contributing Clone this project, run: ```sh npm install; npm test ``` [//]: # (::contents:end) # API ## Functions <dl> <dt><a href="#annotateArcCommand">annotateArcCommand()</a></dt> <dd><p><a href="https://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes">https://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes</a> Fixes rX and rY. Ensures lArcFlag and sweepFlag are 0 or 1 Adds center coordinates: command.cX, command.cY (relative or absolute, depending on command.relative) Adds start and end arc parameters (in degrees): command.phi1, command.phi2; phi1 &lt; phi2 iff. c.sweepFlag == true</p> </dd> <dt><a href="#intersectionUnitCircleLine">intersectionUnitCircleLine()</a></dt> <dd><p>Solves a quadratic system of equations of the form a * x + b * y = c x² + y² = 1 This can be understood as the intersection of the unit circle with a line. =&gt; y = (c - a x) / b =&gt; x² + (c - a x)² / b² = 1 =&gt; x² b² + c² - 2 c a x + a² x² = b² =&gt; (a² + b²) x² - 2 a c x + (c² - b²) = 0</p> </dd> <dt><a href="#arePointsCollinear">arePointsCollinear(p1, p2, p3)</a> ⇒</dt> <dd><p>Determines if three points are collinear (lie on the same straight line) and the middle point is on the line segment between the first and third points</p> </dd> <dt><a href="#createEllipse">createEllipse()</a></dt> <dd><p>Creates an ellipse path centered at (cx,cy) with radii rx and ry</p> </dd> <dt><a href="#createRect">createRect()</a></dt> <dd><p>Creates a rectangle path with optional rounded corners</p> </dd> <dt><a href="#createPolyline">createPolyline()</a></dt> <dd><p>Creates a polyline from an array of coordinates [x1,y1,x2,y2,...]</p> </dd> <dt><a href="#createPolygon">createPolygon()</a></dt> <dd><p>Creates a closed polygon from an array of coordinates</p> </dd> <dt><a href="#REMOVE_COLLINEAR">REMOVE_COLLINEAR(commands)</a> ⇒</dt> <dd><p>Process a path and remove collinear points</p> </dd> <dt><a href="#REVERSE_PATH">REVERSE_PATH(commands, preserveSubpathOrder)</a> ⇒</dt> <dd><p>Reverses the order of path commands to go from end to start IMPORTANT: This function expects absolute commands as input. It doesn&#39;t convert relative to absolute - use SVGPathDataTransformer.TO_ABS() first if needed.</p> </dd> </dl> <a name="annotateArcCommand"></a> ## annotateArcCommand() https://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes Fixes rX and rY. Ensures lArcFlag and sweepFlag are 0 or 1 Adds center coordinates: command.cX, command.cY (relative or absolute, depending on command.relative) Adds start and end arc parameters (in degrees): command.phi1, command.phi2; phi1 < phi2 iff. c.sweepFlag == true **Kind**: global function <a name="intersectionUnitCircleLine"></a> ## intersectionUnitCircleLine() Solves a quadratic system of equations of the form a * x + b * y = c x² + y² = 1 This can be understood as the intersection of the unit circle with a line. => y = (c - a x) / b => x² + (c - a x)² / b² = 1 => x² b² + c² - 2 c a x + a² x² = b² => (a² + b²) x² - 2 a c x + (c² - b²) = 0 **Kind**: global function <a name="arePointsCollinear"></a> ## arePointsCollinear(p1, p2, p3) ⇒ Determines if three points are collinear (lie on the same straight line) and the middle point is on the line segment between the first and third points **Kind**: global function **Returns**: true if the points are collinear and p2 is on the segment p1-p3 | Param | Description | | --- | --- | | p1 | First point [x, y] | | p2 | Middle point that might be removed | | p3 | Last point [x, y] | <a name="createEllipse"></a> ## createEllipse() Creates an ellipse path centered at (cx,cy) with radii rx and ry **Kind**: global function <a name="createRect"></a> ## createRect() Creates a rectangle path with optional rounded corners **Kind**: global function <a name="createPolyline"></a> ## createPolyline() Creates a polyline from an array of coordinates [x1,y1,x2,y2,...] **Kind**: global function <a name="createPolygon"></a> ## createPolygon() Creates a closed polygon from an array of coordinates **Kind**: global function <a name="REMOVE_COLLINEAR"></a> ## REMOVE\_COLLINEAR(commands) ⇒ Process a path and remove collinear points **Kind**: global function **Returns**: New array with collinear points removed | Param | Description | | --- | --- | | commands | Array of SVG path commands to process (must be absolute) | <a name="REVERSE_PATH"></a> ## REVERSE\_PATH(commands, preserveSubpathOrder) ⇒ Reverses the order of path commands to go from end to start IMPORTANT: This function expects absolute commands as input. It doesn't convert relative to absolute - use SVGPathDataTransformer.TO_ABS() first if needed. **Kind**: global function **Returns**: New SVG commands in reverse order with absolute coordinates | Param | Description | | --- | --- | | commands | SVG path commands in absolute form to reverse | | preserveSubpathOrder | If true, keeps subpaths in their original order | # Authors - [Nicolas Froidure](https://insertafter.com/en/index.html) - [Anders Kaseorg](mailto:andersk@mit.edu) # License [MIT](https://github.com/nfroidure/svg-pathdata/blob/main/LICENSE)