
Security News
/Research
Wallet-Draining npm Package Impersonates Nodemailer to Hijack Crypto Transactions
Malicious npm package impersonates Nodemailer and drains wallets by hijacking crypto transactions across multiple blockchains.
svg-path-commander
Advanced tools
A modern set of Typescript tools for manipulating the d
(description) attribute for SVGPathElement items. The library is implementing modern JavaScript API to produce reusable path strings with lossless quality. In addition, you also have a powerful tool to convert other SVG shapes like <circle>
or <rect>
to <path>
.
While you may find familiar tools inside, this library brings new additions:
getBBox
, getPointAtLength
and getTotalLength
are more reliable and much more accurate than the native methods, not to mention their high performance ratings;getPropertiesAtLength
, getSegmentOfPoint
or isPointInStroke
;The key differences with other libraries:
SVGPathCommander can use the DOMMatrix API for SVGPathElement path command transformation and implements a very fast and modernized DOMMatrix shim. There are a couple of good reasons for this implementation:
This library is available on CDN and npm.
npm install svg-path-commander
<script src="https://cdn.jsdelivr.net/npm/svg-path-commander/dist/svg-path-commander.js">
Flip a path on the X axis:
import SVGPathCommander from 'svg-path-commander';
const path = 'M0 0L100 0L50 100';
const flippedPathString = new SVGPathCommander(path).flipX().toString();
// result => 'M0 100h100L50 0'
Optimize a path string by using the round
option, to round numbers to 2 decimals and finding shorthand where possible:
const optimizedPathString = new SVGPathCommander(path, {round: 2}).optimize().toString();
Or why not apply a 2D transformation and even a 3D transformation:
// a transform object
const transform = {
translate: 15, // X axis translation
rotate: 15, // Z axis rotation
scale: 0.75, // uniform scale on X, Y, Z axis
skew: 15, // skew 15deg on the X axis
origin: [15, 0] // if not specified, it will use the default origin value [0, 0]
}
const transformed2DPathString = new SVGPathCommander(path).transform(transform).toString();
// apply a 3D transformation
const transform = {
translate: [15, 15, 15], // `[15, 15]` would apply a 2D translation, and only `15` for X axis translation
rotate: [15, 15, 15], // or only "15" for 2D rotation on Z axis
scale: [0.7, 0.75, 0.8], // or only "0.7" for 2D scale on all X, Y, Z axis
skew: [15, 15], // or only "15" for the X axis
origin: [15, 15, 15] // full `transform-origin` for a typical 3D transformation
}
const transformed3DPathString = new SVGPathCommander(path).transform(transform).toString();
Access the bbox
instance property to apply a consistent transform-origin
:
// apply a 3D transformation with a consistent origin
const transformed3DPath = new SVGPathCommander(path);
const { cx, cy, cz } = transformed3DPath.bbox;
const transform = {
translate: [15, 15, 15], // `[15, 15]` would apply a 2D translation, and only `15` for X axis translation
rotate: [15, 15, 15], // or only "15" for 2D rotation on Z axis
scale: [0.7, 0.75, 0.8], // or only "0.7" for 2D scale on all X, Y, Z axis
skew: [15, 15], // or only "15" for the X axis
origin: [cx, cy, cz] // the origin
}
const transformed3DPathString = transformed3DPath.transform(transform).toString();
SVGPathCommander comes with a full range of additional static methods, here's how to normalize a path:
const path = 'M0 0 H50';
const normalizedPath = SVGPathCommander.normalizePath(path);
// result => [['M', 0, 0], ['L', 50, 0]]
Reverse a path:
const path = SVGPathCommander.parsePathString('M0 0 H50');
const reversedPath = SVGPathCommander.reversePath(path);
// result => [['M', 50, 0], ['H', 0]]
Export to string:
const myPathString = SVGPathCommander.pathToString([['M', 0, 0], ['L', 50, 0]]);
// result => 'M0 0 L50 0'
Check a path string validity:
SVGPathCommander.isValidPath(path);
// result => boolean
Check if path is a certain kind of PathArray
:
SVGPathCommander.isAbsoluteArray([['M', 0, 0], ['L', 50, 0]]);
// result => true
Use treeshake and create a custom function to apply a 3D transformation using static methods:
import { parsePathString, getPathBBox, transformPath, pathToString } from 'svg-path-commander';
function myTransformFn(pathInput: string | PathArray, transformObject: TransformObject) {
const path = parsePathString(pathInput);
const { cx, cy, cz } = getPathBBox(path);
return pathToString(
transformPath(path, {
...transformObject, origin: [cx, cy, cz]
})
)
}
In extreme cases where performance is paramount, you can consider the parent SVG viewBox
attribute to extract a bounding box required for a consistent transform origin.
// const svgViewBox = document.getElementById('my-svg').getAttribute('viewBox');
const viewBox = '0 0 24 24';
const [x, y, width, height] = viewBox.split(/\s/).map(Number);
const origin = [
x + width / 2, // CX
y + height / 2, // CY
Math.max(width, height) + Math.min(width, height) / 2, // CZ
];
// use this origin for your shape transformation
const myNewString = new SVGPathCommander('M0 0 H50')
.transform({ rotate: [35, 0, 0], origin })
.toString();
Convert a shape to <path>
and transfer all non-specific attributes
const myCircle = document.getElementById('myCircle');
SVGPathCommander.shapeToPath(myCircle, true);
Alternatively you can create <path>
from specific attributes:
const myRectAttr = {
type: 'rect',
x: 25,
y: 25,
width: 50,
height: 50,
rx: 5
};
const myRectPath = SVGPathCommander.shapeToPath(myRectAttr);
document.getElementById('mySVG').append(myRectPath);
Server-side using jsdom
:
const { document } = new JSDOM(
`<html>
<head></head>
<body>
<svg id="mySVG" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect id="myRect" x="0" width="100" height="100" rx="15" />
</svg>
</body>
</html>`,
{
pretendToBeVisual: true,
}
).window;
const myRect = document.getElementById('myRect');
SVGPathCommander.shapeToPath(myRect, true, document);
Get the path length:
const myPathLength = SVGPathCommander.getTotalLength('M0 0L50 0L25 50z');
// result => 161.80339887498948
Get a point along the path:
const myPoint = SVGPathCommander.getPointAtLength('M0 0L50 0L25 50z', 85);
// result => {x: 34.34752415750147, y: 31.304951684997057}
Get the path bounding box:
const myPathBBox = SVGPathCommander.getPathBBox('M0 0L50 0L25 50z');
// result => {width: 50, height: 50, x: 0, y: 0, x2: 50, y2: 50, cx: 25, cy: 25, cz: 75}
For developer guidelines, and a complete list of static methods, head over to the wiki pages.
optimize()
instance method will not merge path segments (for instance two or more cubic-bezier segments into one or more arc segments); however, the script will try to provide shorthand notations where possible, pick the shortest string for each segment, and generally try to deliver the best possible outcome;pathToString
, optimizePath
and especially roundPath
will always round values to the default of 4 decimals; EG: 0.56676 => 0.567, 0.50 => 0.5; you can change the default option with SVGPathCommander.options.round = 2
or remove the value rounding all together with SVGPathCommander.options.round = false
; you can also control this feature via instance options;getSVGMatrix
utility we developed will always compute the matrix by applying the transform functions in the following order: translate
, rotate
, skew
and scale
, which is the default composition/recomposition order specified in the W3C draft;A
(arc) path commands to C
(cubic bezier) due to the lack of resources;R
(catmulRomBezier), O
, U
(ellipse and shorthand ellipse) are not present in the current draft and are not supported;SVGPathElement.getTotalLength()
or SVGPathElement.getPointAtLength()
, the output of our static methods is within a [0.002 - 0.05] margin delta, but from our experience it's proven to be a more consistent outcome.SVGPathCommander is released under MIT Licence.
FAQs
🛹 Modern TypeScript tools for SVG
The npm package svg-path-commander receives a total of 7,574 weekly downloads. As such, svg-path-commander popularity was classified as popular.
We found that svg-path-commander demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
/Research
Malicious npm package impersonates Nodemailer and drains wallets by hijacking crypto transactions across multiple blockchains.
Security News
This episode explores the hard problem of reachability analysis, from static analysis limits to handling dynamic languages and massive dependency trees.
Security News
/Research
Malicious Nx npm versions stole secrets and wallet info using AI CLI tools; Socket’s AI scanner detected the supply chain attack and flagged the malware.