Socket
Socket
Sign inDemoInstall

svg-pathdata

Package Overview
Dependencies
Maintainers
3
Versions
37
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

svg-pathdata - npm Package Compare versions

Comparing version 5.0.0 to 5.0.1

.vscode/settings.json

5

CHANGELOG.md

@@ -0,1 +1,6 @@

<a name="5.0.1"></a>
## [5.0.1](https://github.com/nfroidure/svg-pathdata/compare/v5.0.0...v5.0.1) (2018-06-03)
<a name="5.0.0"></a>

@@ -2,0 +7,0 @@ # [5.0.0](https://github.com/nfroidure/svg-pathdata/compare/v4.0.0...v5.0.0) (2018-06-02)

35

lib/SVGPathDataParser.d.ts
import { SVGCommand, TransformFunction } from "./SVGPathData";
import { TransformableSVG } from "./TransformableSVG";
export declare class SVGPathDataParser extends TransformableSVG {
curCommand: any;
state: number;
curNumber: string;
private curNumber;
private curCommandType;
private curCommandRelative;
private canParseCommandOrComma;
private curNumberHasExp;
private curNumberHasExpDigits;
private curNumberHasDecimal;
private curArgs;
constructor();

@@ -14,26 +19,2 @@ finish(commands?: SVGCommand[]): SVGCommand[];

transform(transform: TransformFunction): this;
static readonly STATE_WSP: number;
static readonly STATE_WSPS: number;
static readonly STATE_COMMA: number;
static readonly STATE_COMMAS: number;
static readonly STATE_COMMAS_WSPS: number;
static readonly STATE_NUMBER: number;
static readonly STATE_NUMBER_DIGITS: number;
static readonly STATE_NUMBER_INT: number;
static readonly STATE_NUMBER_FLOAT: number;
static readonly STATE_NUMBER_EXP: number;
static readonly STATE_NUMBER_EXPSIGN: number;
static readonly STATE_NUMBER_MASK: number;
static readonly STATE_RELATIVE: number;
static readonly STATE_CLOSE_PATH: number;
static readonly STATE_MOVE_TO: number;
static readonly STATE_LINE_TO: number;
static readonly STATE_HORIZ_LINE_TO: number;
static readonly STATE_VERT_LINE_TO: number;
static readonly STATE_CURVE_TO: number;
static readonly STATE_SMOOTH_CURVE_TO: number;
static readonly STATE_QUAD_TO: number;
static readonly STATE_SMOOTH_QUAD_TO: number;
static readonly STATE_ARC: number;
static readonly STATE_COMMANDS_MASK: number;
}
"use strict";
// Parse SVG PathData
// http://www.w3.org/TR/SVG/paths.html#PathDataBNF
var __extends = (this && this.__extends) || (function () {

@@ -15,34 +13,26 @@ var extendStatics = Object.setPrototypeOf ||

Object.defineProperty(exports, "__esModule", { value: true });
var _a;
// Parse SVG PathData
// http://www.w3.org/TR/SVG/paths.html#PathDataBNF
var SVGPathData_1 = require("./SVGPathData");
var TransformableSVG_1 = require("./TransformableSVG");
// Private consts : Char groups
var WSP = [" ", "\t", "\r", "\n"];
var DIGITS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
var SIGNS = ["-", "+"];
var EXPONENTS = ["e", "E"];
var DECPOINT = ["."];
var FLAGS = ["0", "1"];
var COMMA = [","];
var COMMANDS = [
"m",
"M",
"z",
"Z",
"l",
"L",
"h",
"H",
"v",
"V",
"c",
"C",
"s",
"S",
"q",
"Q",
"t",
"T",
"a",
"A",
];
var isWhiteSpace = function (c) { return " " === c || "\t" === c || "\r" === c || "\n" === c; };
var isDigit = function (c) {
return "0".charCodeAt(0) <= c.charCodeAt(0) && c.charCodeAt(0) <= "9".charCodeAt(0);
};
var COMMANDS = "mMzZlLhHvVcCsSqQtTaA";
// @ts-ignore
var COMMAND_ARG_COUNTS = (_a = {},
_a[SVGPathData_1.SVGPathData.MOVE_TO] = 2,
_a[SVGPathData_1.SVGPathData.LINE_TO] = 2,
_a[SVGPathData_1.SVGPathData.HORIZ_LINE_TO] = 1,
_a[SVGPathData_1.SVGPathData.VERT_LINE_TO] = 1,
_a[SVGPathData_1.SVGPathData.CLOSE_PATH] = 0,
_a[SVGPathData_1.SVGPathData.QUAD_TO] = 4,
_a[SVGPathData_1.SVGPathData.SMOOTH_QUAD_TO] = 2,
_a[SVGPathData_1.SVGPathData.CURVE_TO] = 6,
_a[SVGPathData_1.SVGPathData.SMOOTH_CURVE_TO] = 4,
_a[SVGPathData_1.SVGPathData.ARC] = 7,
_a);
var SVGPathDataParser = /** @class */ (function (_super) {

@@ -52,5 +42,10 @@ __extends(SVGPathDataParser, _super);

var _this = _super.call(this) || this;
_this.curCommand = undefined;
_this.state = SVGPathDataParser.STATE_COMMAS_WSPS;
_this.curNumber = "";
_this.curCommandType = -1;
_this.curCommandRelative = false;
_this.canParseCommandOrComma = true;
_this.curNumberHasExp = false;
_this.curNumberHasExpDigits = false;
_this.curNumberHasDecimal = false;
_this.curArgs = [];
return _this;

@@ -60,458 +55,221 @@ }

if (commands === void 0) { commands = []; }
var result = this.parse(" ", commands);
this.parse(" ", commands);
// Adding residual command
if (undefined !== this.curCommand) {
if (this.curCommand.invalid) {
throw new SyntaxError("Unterminated command at the path end.");
}
commands.push(this.curCommand);
this.curCommand = undefined;
this.state ^= this.state & SVGPathDataParser.STATE_COMMANDS_MASK;
if (0 !== this.curArgs.length || !this.canParseCommandOrComma) {
throw new SyntaxError("Unterminated command at the path end.");
}
return result;
return commands;
};
SVGPathDataParser.prototype.parse = function (str, commands) {
var _this = this;
if (commands === void 0) { commands = []; }
var finishCommand = function (command) {
commands.push(command);
_this.curArgs.length = 0;
_this.canParseCommandOrComma = true;
};
for (var i = 0; i < str.length; i++) {
var c = str[i];
// White spaces parsing
if (this.state & SVGPathDataParser.STATE_WSP ||
this.state & SVGPathDataParser.STATE_WSPS) {
if (-1 !== WSP.indexOf(str[i])) {
this.state ^= this.state & SVGPathDataParser.STATE_WSP;
// any space stops current number parsing
if ("" !== this.curNumber) {
this.state ^= this.state & SVGPathDataParser.STATE_NUMBER_MASK;
}
else {
continue;
}
}
if (isDigit(c)) {
this.curNumber += c;
this.curNumberHasExpDigits = this.curNumberHasExp;
continue;
}
// Commas parsing
if (this.state & SVGPathDataParser.STATE_COMMA ||
this.state & SVGPathDataParser.STATE_COMMAS) {
if (-1 !== COMMA.indexOf(str[i])) {
this.state ^= this.state & SVGPathDataParser.STATE_COMMA;
// any comma stops current number parsing
if ("" !== this.curNumber) {
this.state ^= this.state & SVGPathDataParser.STATE_NUMBER_MASK;
}
else {
continue;
}
}
if ("e" === c || "E" === c) {
this.curNumber += c;
this.curNumberHasExp = true;
continue;
}
// Numbers parsing : -125.25e-125
if (this.state & SVGPathDataParser.STATE_NUMBER) {
// Reading the sign
if ((this.state & SVGPathDataParser.STATE_NUMBER_MASK) ===
SVGPathDataParser.STATE_NUMBER) {
this.state |=
SVGPathDataParser.STATE_NUMBER_INT |
SVGPathDataParser.STATE_NUMBER_DIGITS;
if (-1 !== SIGNS.indexOf(str[i])) {
this.curNumber += str[i];
continue;
}
if (("-" === c || "+" === c) && this.curNumberHasExp && !this.curNumberHasExpDigits) {
this.curNumber += c;
continue;
}
// if we already have a ".", it means we are starting a new number
if ("." === c && !this.curNumberHasExp && !this.curNumberHasDecimal) {
this.curNumber += c;
this.curNumberHasDecimal = true;
continue;
}
// New number
if (this.curNumber && -1 !== this.curCommandType) {
var val = Number(this.curNumber);
if (isNaN(val)) {
throw new SyntaxError("Invalid number ending at " + i);
}
// Reading the exponent sign
if (this.state & SVGPathDataParser.STATE_NUMBER_EXPSIGN) {
this.state ^= SVGPathDataParser.STATE_NUMBER_EXPSIGN;
this.state |= SVGPathDataParser.STATE_NUMBER_DIGITS;
if (-1 !== SIGNS.indexOf(str[i])) {
this.curNumber += str[i];
continue;
if (this.curCommandType === SVGPathData_1.SVGPathData.ARC) {
if (0 === this.curArgs.length || 1 === this.curArgs.length) {
if (0 > val) {
throw new SyntaxError("Expected positive number, got \"" + val + "\" at index \"" + i + "\"");
}
}
}
// Reading digits
if (this.state & SVGPathDataParser.STATE_NUMBER_DIGITS) {
if (-1 !== DIGITS.indexOf(str[i])) {
this.curNumber += str[i];
continue;
else if (3 === this.curArgs.length || 4 === this.curArgs.length) {
if ("0" !== this.curNumber && "1" !== this.curNumber) {
throw new SyntaxError("Expected a flag, got \"" + this.curNumber + "\" at index \"" + i + "\"");
}
}
this.state ^= SVGPathDataParser.STATE_NUMBER_DIGITS;
}
// Ended reading left side digits
if (this.state & SVGPathDataParser.STATE_NUMBER_INT) {
this.state ^= SVGPathDataParser.STATE_NUMBER_INT;
// if got a point, reading right side digits
if (-1 !== DECPOINT.indexOf(str[i])) {
this.curNumber += str[i];
this.state |=
SVGPathDataParser.STATE_NUMBER_FLOAT |
SVGPathDataParser.STATE_NUMBER_DIGITS;
continue;
// if got e/E, reading the exponent
}
else if (-1 !== EXPONENTS.indexOf(str[i])) {
this.curNumber += str[i];
this.state |=
SVGPathDataParser.STATE_NUMBER_EXP |
SVGPathDataParser.STATE_NUMBER_EXPSIGN;
continue;
}
// else we"re done with that number
this.state ^= this.state & SVGPathDataParser.STATE_NUMBER_MASK;
}
// Ended reading decimal digits
if (this.state & SVGPathDataParser.STATE_NUMBER_FLOAT) {
this.state ^= SVGPathDataParser.STATE_NUMBER_FLOAT;
// if got e/E, reading the exponent
if (-1 !== EXPONENTS.indexOf(str[i])) {
this.curNumber += str[i];
this.state |=
SVGPathDataParser.STATE_NUMBER_EXP |
SVGPathDataParser.STATE_NUMBER_EXPSIGN;
continue;
}
// else we"re done with that number
this.state ^= this.state & SVGPathDataParser.STATE_NUMBER_MASK;
}
// Ended reading exponent digits
if (this.state & SVGPathDataParser.STATE_NUMBER_EXP) {
// we"re done with that number
this.state ^= this.state & SVGPathDataParser.STATE_NUMBER_MASK;
}
}
// New number
if (this.curNumber) {
// Horizontal move to command (x)
if (this.state & SVGPathDataParser.STATE_HORIZ_LINE_TO) {
if (undefined === this.curCommand) {
commands.push({
this.curArgs.push(val);
if (this.curArgs.length === COMMAND_ARG_COUNTS[this.curCommandType]) {
if (SVGPathData_1.SVGPathData.HORIZ_LINE_TO === this.curCommandType) {
finishCommand({
type: SVGPathData_1.SVGPathData.HORIZ_LINE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
x: Number(this.curNumber),
relative: this.curCommandRelative,
x: val,
});
}
else {
this.curCommand.x = Number(this.curNumber);
delete this.curCommand.invalid;
commands.push(this.curCommand);
this.curCommand = undefined;
}
this.state |= SVGPathDataParser.STATE_NUMBER;
// Vertical move to command (y)
}
else if (this.state & SVGPathDataParser.STATE_VERT_LINE_TO) {
if (undefined === this.curCommand) {
commands.push({
else if (SVGPathData_1.SVGPathData.VERT_LINE_TO === this.curCommandType) {
finishCommand({
type: SVGPathData_1.SVGPathData.VERT_LINE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
y: Number(this.curNumber),
relative: this.curCommandRelative,
y: val,
});
// Move to / line to / smooth quadratic curve to commands (x, y)
}
else {
this.curCommand.y = Number(this.curNumber);
delete this.curCommand.invalid;
commands.push(this.curCommand);
this.curCommand = undefined;
}
this.state |= SVGPathDataParser.STATE_NUMBER;
// Move to / line to / smooth quadratic curve to commands (x, y)
}
else if (this.state & SVGPathDataParser.STATE_MOVE_TO ||
this.state & SVGPathDataParser.STATE_LINE_TO ||
this.state & SVGPathDataParser.STATE_SMOOTH_QUAD_TO) {
if (undefined === this.curCommand) {
this.curCommand = {
type: this.state & SVGPathDataParser.STATE_MOVE_TO
? SVGPathData_1.SVGPathData.MOVE_TO
: this.state & SVGPathDataParser.STATE_LINE_TO
? SVGPathData_1.SVGPathData.LINE_TO
: SVGPathData_1.SVGPathData.SMOOTH_QUAD_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
x: Number(this.curNumber),
};
}
else if (undefined === this.curCommand.x) {
this.curCommand.x = Number(this.curNumber);
}
else {
delete this.curCommand.invalid;
this.curCommand.y = Number(this.curNumber);
commands.push(this.curCommand);
this.curCommand = undefined;
else if (this.curCommandType === SVGPathData_1.SVGPathData.MOVE_TO ||
this.curCommandType === SVGPathData_1.SVGPathData.LINE_TO ||
this.curCommandType === SVGPathData_1.SVGPathData.SMOOTH_QUAD_TO) {
finishCommand({
type: this.curCommandType,
relative: this.curCommandRelative,
x: this.curArgs[0],
y: this.curArgs[1],
});
// Switch to line to state
if (this.state & SVGPathDataParser.STATE_MOVE_TO) {
this.state ^= SVGPathDataParser.STATE_MOVE_TO;
this.state |= SVGPathDataParser.STATE_LINE_TO;
if (SVGPathData_1.SVGPathData.MOVE_TO === this.curCommandType) {
this.curCommandType = SVGPathData_1.SVGPathData.LINE_TO;
}
}
this.state |= SVGPathDataParser.STATE_NUMBER;
// Curve to commands (x1, y1, x2, y2, x, y)
}
else if (this.state & SVGPathDataParser.STATE_CURVE_TO) {
if (undefined === this.curCommand) {
this.curCommand = {
else if (this.curCommandType === SVGPathData_1.SVGPathData.CURVE_TO) {
finishCommand({
type: SVGPathData_1.SVGPathData.CURVE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
x1: Number(this.curNumber),
};
relative: this.curCommandRelative,
x1: this.curArgs[0],
y1: this.curArgs[1],
x2: this.curArgs[2],
y2: this.curArgs[3],
x: this.curArgs[4],
y: this.curArgs[5],
});
}
else if (undefined === this.curCommand.x1) {
this.curCommand.x1 = Number(this.curNumber);
}
else if (undefined === this.curCommand.y1) {
this.curCommand.y1 = Number(this.curNumber);
}
else if (undefined === this.curCommand.x2) {
this.curCommand.x2 = Number(this.curNumber);
}
else if (undefined === this.curCommand.y2) {
this.curCommand.y2 = Number(this.curNumber);
}
else if (undefined === this.curCommand.x) {
this.curCommand.x = Number(this.curNumber);
}
else if (undefined === this.curCommand.y) {
this.curCommand.y = Number(this.curNumber);
delete this.curCommand.invalid;
commands.push(this.curCommand);
this.curCommand = undefined;
}
this.state |= SVGPathDataParser.STATE_NUMBER;
// Smooth curve to commands (x1, y1, x, y)
}
else if (this.state & SVGPathDataParser.STATE_SMOOTH_CURVE_TO) {
if (undefined === this.curCommand) {
this.curCommand = {
else if (this.curCommandType === SVGPathData_1.SVGPathData.SMOOTH_CURVE_TO) {
finishCommand({
type: SVGPathData_1.SVGPathData.SMOOTH_CURVE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
x2: Number(this.curNumber),
};
relative: this.curCommandRelative,
x2: this.curArgs[0],
y2: this.curArgs[1],
x: this.curArgs[2],
y: this.curArgs[3],
});
}
else if (undefined === this.curCommand.x2) {
this.curCommand.x2 = Number(this.curNumber);
}
else if (undefined === this.curCommand.y2) {
this.curCommand.y2 = Number(this.curNumber);
}
else if (undefined === this.curCommand.x) {
this.curCommand.x = Number(this.curNumber);
}
else if (undefined === this.curCommand.y) {
this.curCommand.y = Number(this.curNumber);
delete this.curCommand.invalid;
commands.push(this.curCommand);
this.curCommand = undefined;
}
this.state |= SVGPathDataParser.STATE_NUMBER;
// Quadratic bezier curve to commands (x1, y1, x, y)
}
else if (this.state & SVGPathDataParser.STATE_QUAD_TO) {
if (undefined === this.curCommand) {
this.curCommand = {
else if (this.curCommandType === SVGPathData_1.SVGPathData.QUAD_TO) {
finishCommand({
type: SVGPathData_1.SVGPathData.QUAD_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
x1: Number(this.curNumber),
};
relative: this.curCommandRelative,
x1: this.curArgs[0],
y1: this.curArgs[1],
x: this.curArgs[2],
y: this.curArgs[3],
});
}
else if (undefined === this.curCommand.x1) {
this.curCommand.x1 = Number(this.curNumber);
}
else if (undefined === this.curCommand.y1) {
this.curCommand.y1 = Number(this.curNumber);
}
else if (undefined === this.curCommand.x) {
this.curCommand.x = Number(this.curNumber);
}
else if (undefined === this.curCommand.y) {
this.curCommand.y = Number(this.curNumber);
delete this.curCommand.invalid;
commands.push(this.curCommand);
this.curCommand = undefined;
}
this.state |= SVGPathDataParser.STATE_NUMBER;
// Elliptic arc commands (rX, rY, xRot, lArcFlag, sweepFlag, x, y)
}
else if (this.state & SVGPathDataParser.STATE_ARC) {
if (undefined === this.curCommand) {
this.curCommand = {
else if (this.curCommandType === SVGPathData_1.SVGPathData.ARC) {
finishCommand({
type: SVGPathData_1.SVGPathData.ARC,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
rX: Number(this.curNumber),
};
relative: this.curCommandRelative,
rX: this.curArgs[0],
rY: this.curArgs[1],
xRot: this.curArgs[2],
lArcFlag: this.curArgs[3],
sweepFlag: this.curArgs[4],
x: this.curArgs[5],
y: this.curArgs[6],
});
}
else if (undefined === this.curCommand.rX) {
if (0 > Number(this.curNumber)) {
throw new SyntaxError("Expected positive number, got \"" + this.curNumber + "\" at index \"" + i + "\"");
}
this.curCommand.rX = Number(this.curNumber);
}
else if (undefined === this.curCommand.rY) {
if (0 > Number(this.curNumber)) {
throw new SyntaxError("Expected positive number, got \"" + this.curNumber + "\" at index \"" + i + "\"");
}
this.curCommand.rY = Number(this.curNumber);
}
else if (undefined === this.curCommand.xRot) {
this.curCommand.xRot = Number(this.curNumber);
}
else if (undefined === this.curCommand.lArcFlag) {
if (-1 === FLAGS.indexOf(this.curNumber)) {
throw new SyntaxError("Expected a flag, got \"" + this.curNumber + "\" at index \"" + i + "\"");
}
this.curCommand.lArcFlag = Number(this.curNumber);
}
else if (undefined === this.curCommand.sweepFlag) {
if ("0" !== this.curNumber && "1" !== this.curNumber) {
throw new SyntaxError("Expected a flag, got \"" + this.curNumber + "\" at index \"" + i + "\"");
}
this.curCommand.sweepFlag = Number(this.curNumber);
}
else if (undefined === this.curCommand.x) {
this.curCommand.x = Number(this.curNumber);
}
else if (undefined === this.curCommand.y) {
this.curCommand.y = Number(this.curNumber);
delete this.curCommand.invalid;
commands.push(this.curCommand);
this.curCommand = undefined;
}
this.state |= SVGPathDataParser.STATE_NUMBER;
}
this.curNumber = "";
// Continue if a white space or a comma was detected
if (-1 !== WSP.indexOf(str[i]) || -1 !== COMMA.indexOf(str[i])) {
continue;
}
// if a sign is detected, then parse the new number
if (-1 !== SIGNS.indexOf(str[i])) {
this.curNumber = str[i];
this.state |=
SVGPathDataParser.STATE_NUMBER_INT |
SVGPathDataParser.STATE_NUMBER_DIGITS;
continue;
}
// if the decpoint is detected, then parse the new number
if (-1 !== DECPOINT.indexOf(str[i])) {
this.curNumber = str[i];
this.state |=
SVGPathDataParser.STATE_NUMBER_FLOAT |
SVGPathDataParser.STATE_NUMBER_DIGITS;
continue;
}
this.curNumberHasExpDigits = false;
this.curNumberHasExp = false;
this.curNumberHasDecimal = false;
this.canParseCommandOrComma = true;
}
// End of a command
if (-1 !== COMMANDS.indexOf(str[i])) {
// Adding residual command
if (undefined !== this.curCommand) {
if (this.curCommand.invalid) {
throw new SyntaxError("Unterminated command at index " + i + ".");
}
commands.push(this.curCommand);
this.curCommand = undefined;
this.state ^= this.state & SVGPathDataParser.STATE_COMMANDS_MASK;
}
// Continue if a white space or a comma was detected
if (isWhiteSpace(c)) {
continue;
}
// Detecting the next command
this.state ^= this.state & SVGPathDataParser.STATE_COMMANDS_MASK;
// Is the command relative
if (str[i] === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_RELATIVE;
if ("," === c && this.canParseCommandOrComma) {
// L 0,0, H is not valid:
this.canParseCommandOrComma = false;
continue;
}
else {
this.state ^= this.state & SVGPathDataParser.STATE_RELATIVE;
// if a sign is detected, then parse the new number
if ("+" === c || "-" === c || "." === c) {
this.curNumber = c;
this.curNumberHasDecimal = "." === c;
continue;
}
// Horizontal move to command
if ("z" === str[i].toLowerCase()) {
// Adding residual command
if (0 !== this.curArgs.length) {
throw new SyntaxError("Unterminated command at index " + i + ".");
}
if (!this.canParseCommandOrComma) {
throw new SyntaxError("Unexpected character \"" + c + "\" at index " + i + ". Command cannot follow comma");
}
this.canParseCommandOrComma = false;
// Detecting the next command
if ("z" === c || "Z" === c) {
commands.push({
type: SVGPathData_1.SVGPathData.CLOSE_PATH,
});
this.state = SVGPathDataParser.STATE_COMMAS_WSPS;
this.canParseCommandOrComma = true;
this.curCommandType = -1;
continue;
// Horizontal move to command
}
else if ("h" === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_HORIZ_LINE_TO;
this.curCommand = {
type: SVGPathData_1.SVGPathData.HORIZ_LINE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
else if ("h" === c || "H" === c) {
this.curCommandType = SVGPathData_1.SVGPathData.HORIZ_LINE_TO;
this.curCommandRelative = "h" === c;
// Vertical move to command
}
else if ("v" === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_VERT_LINE_TO;
this.curCommand = {
type: SVGPathData_1.SVGPathData.VERT_LINE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
else if ("v" === c || "V" === c) {
this.curCommandType = SVGPathData_1.SVGPathData.VERT_LINE_TO;
this.curCommandRelative = "v" === c;
// Move to command
}
else if ("m" === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_MOVE_TO;
this.curCommand = {
type: SVGPathData_1.SVGPathData.MOVE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
else if ("m" === c || "M" === c) {
this.curCommandType = SVGPathData_1.SVGPathData.MOVE_TO;
this.curCommandRelative = "m" === c;
// Line to command
}
else if ("l" === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_LINE_TO;
this.curCommand = {
type: SVGPathData_1.SVGPathData.LINE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
else if ("l" === c || "L" === c) {
this.curCommandType = SVGPathData_1.SVGPathData.LINE_TO;
this.curCommandRelative = "l" === c;
// Curve to command
}
else if ("c" === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_CURVE_TO;
this.curCommand = {
type: SVGPathData_1.SVGPathData.CURVE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
else if ("c" === c || "C" === c) {
this.curCommandType = SVGPathData_1.SVGPathData.CURVE_TO;
this.curCommandRelative = "c" === c;
// Smooth curve to command
}
else if ("s" === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_SMOOTH_CURVE_TO;
this.curCommand = {
type: SVGPathData_1.SVGPathData.SMOOTH_CURVE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
else if ("s" === c || "S" === c) {
this.curCommandType = SVGPathData_1.SVGPathData.SMOOTH_CURVE_TO;
this.curCommandRelative = "s" === c;
// Quadratic bezier curve to command
}
else if ("q" === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_QUAD_TO;
this.curCommand = {
type: SVGPathData_1.SVGPathData.QUAD_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
else if ("q" === c || "Q" === c) {
this.curCommandType = SVGPathData_1.SVGPathData.QUAD_TO;
this.curCommandRelative = "q" === c;
// Smooth quadratic bezier curve to command
}
else if ("t" === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_SMOOTH_QUAD_TO;
this.curCommand = {
type: SVGPathData_1.SVGPathData.SMOOTH_QUAD_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
else if ("t" === c || "T" === c) {
this.curCommandType = SVGPathData_1.SVGPathData.SMOOTH_QUAD_TO;
this.curCommandRelative = "t" === c;
// Elliptic arc command
}
else if ("a" === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_ARC;
this.curCommand = {
type: SVGPathData_1.SVGPathData.ARC,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
// Unkown command
else if ("a" === c || "A" === c) {
this.curCommandType = SVGPathData_1.SVGPathData.ARC;
this.curCommandRelative = "a" === c;
}
else {
throw new SyntaxError("Unexpected character \"" + str[i] + "\" at index " + i + ".");
throw new SyntaxError("Unexpected character \"" + c + "\" at index " + i + ".");
}
// White spaces can follow a command
this.state |=
SVGPathDataParser.STATE_COMMAS_WSPS | SVGPathDataParser.STATE_NUMBER;
}

@@ -545,43 +303,2 @@ return commands;

};
// Parsing states
SVGPathDataParser.STATE_WSP = 1;
SVGPathDataParser.STATE_WSPS = 2;
SVGPathDataParser.STATE_COMMA = 4;
SVGPathDataParser.STATE_COMMAS = 8;
SVGPathDataParser.STATE_COMMAS_WSPS = SVGPathDataParser.STATE_WSP |
SVGPathDataParser.STATE_WSPS |
SVGPathDataParser.STATE_COMMA |
SVGPathDataParser.STATE_COMMAS;
SVGPathDataParser.STATE_NUMBER = 16;
SVGPathDataParser.STATE_NUMBER_DIGITS = 32;
SVGPathDataParser.STATE_NUMBER_INT = 64;
SVGPathDataParser.STATE_NUMBER_FLOAT = 128;
SVGPathDataParser.STATE_NUMBER_EXP = 256;
SVGPathDataParser.STATE_NUMBER_EXPSIGN = 512;
SVGPathDataParser.STATE_NUMBER_MASK = SVGPathDataParser.STATE_NUMBER |
SVGPathDataParser.STATE_NUMBER_DIGITS |
SVGPathDataParser.STATE_NUMBER_INT |
SVGPathDataParser.STATE_NUMBER_EXP |
SVGPathDataParser.STATE_NUMBER_FLOAT;
SVGPathDataParser.STATE_RELATIVE = 1024;
SVGPathDataParser.STATE_CLOSE_PATH = 2048; // Close path command (z/Z)
SVGPathDataParser.STATE_MOVE_TO = 4096; // Move to command (m/M)
SVGPathDataParser.STATE_LINE_TO = 8192; // Line to command (l/L=)
SVGPathDataParser.STATE_HORIZ_LINE_TO = 16384; // Horizontal line to command (h/H)
SVGPathDataParser.STATE_VERT_LINE_TO = 32768; // Vertical line to command (v/V)
SVGPathDataParser.STATE_CURVE_TO = 65536; // Curve to command (c/C)
SVGPathDataParser.STATE_SMOOTH_CURVE_TO = 131072; // Smooth curve to command (s/S)
SVGPathDataParser.STATE_QUAD_TO = 262144; // Quadratic bezier curve to command (q/Q)
SVGPathDataParser.STATE_SMOOTH_QUAD_TO = 524288; // Smooth quadratic bezier curve to command (t/T)
SVGPathDataParser.STATE_ARC = 1048576; // Elliptic arc command (a/A)
SVGPathDataParser.STATE_COMMANDS_MASK = SVGPathDataParser.STATE_CLOSE_PATH |
SVGPathDataParser.STATE_MOVE_TO |
SVGPathDataParser.STATE_LINE_TO |
SVGPathDataParser.STATE_HORIZ_LINE_TO |
SVGPathDataParser.STATE_VERT_LINE_TO |
SVGPathDataParser.STATE_CURVE_TO |
SVGPathDataParser.STATE_SMOOTH_CURVE_TO |
SVGPathDataParser.STATE_QUAD_TO |
SVGPathDataParser.STATE_SMOOTH_QUAD_TO |
SVGPathDataParser.STATE_ARC;
return SVGPathDataParser;

@@ -588,0 +305,0 @@ }(TransformableSVG_1.TransformableSVG));

{
"name": "svg-pathdata",
"version": "5.0.0",
"version": "5.0.1",
"description": "Manipulate SVG path data (path[d] attribute content) simply and efficiently.",

@@ -5,0 +5,0 @@ "main": "lib/SVGPathData.js",

// Parse SVG PathData
// http://www.w3.org/TR/SVG/paths.html#PathDataBNF
import { Transform } from "stream";
import { SVGCommand, SVGPathData, TransformFunction } from "./SVGPathData";
import { TransformableSVG } from "./TransformableSVG";
// Private consts : Char groups
const WSP = [" ", "\t", "\r", "\n"];
const DIGITS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
const SIGNS = ["-", "+"];
const EXPONENTS = ["e", "E"];
const DECPOINT = ["."];
const FLAGS = ["0", "1"];
const COMMA = [","];
const COMMANDS = [
"m",
"M",
"z",
"Z",
"l",
"L",
"h",
"H",
"v",
"V",
"c",
"C",
"s",
"S",
"q",
"Q",
"t",
"T",
"a",
"A",
];
const isWhiteSpace = (c: string) => " " === c || "\t" === c || "\r" === c || "\n" === c;
const isDigit = (c: string) =>
"0".charCodeAt(0) <= c.charCodeAt(0) && c.charCodeAt(0) <= "9".charCodeAt(0);
const COMMANDS = "mMzZlLhHvVcCsSqQtTaA";
// @ts-ignore
const COMMAND_ARG_COUNTS = {
[SVGPathData.MOVE_TO]: 2,
[SVGPathData.LINE_TO]: 2,
[SVGPathData.HORIZ_LINE_TO]: 1,
[SVGPathData.VERT_LINE_TO]: 1,
[SVGPathData.CLOSE_PATH]: 0,
[SVGPathData.QUAD_TO]: 4,
[SVGPathData.SMOOTH_QUAD_TO]: 2,
[SVGPathData.CURVE_TO]: 6,
[SVGPathData.SMOOTH_CURVE_TO]: 4,
[SVGPathData.ARC]: 7,
};
export class SVGPathDataParser extends TransformableSVG {
curCommand: any = undefined;
state: number = SVGPathDataParser.STATE_COMMAS_WSPS;
curNumber: string = "";
private curNumber: string = "";
private curCommandType: SVGCommand["type"] | -1 = -1;
private curCommandRelative = false;
private canParseCommandOrComma = true;
private curNumberHasExp = false;
private curNumberHasExpDigits = false;
private curNumberHasDecimal = false;
private curArgs: number[] = [];

@@ -49,443 +39,215 @@ constructor() {

finish(commands: SVGCommand[] = []) {
const result = this.parse(" ", commands);
this.parse(" ", commands);
// Adding residual command
if (undefined !== this.curCommand) {
if (this.curCommand.invalid) {
throw new SyntaxError("Unterminated command at the path end.");
}
commands.push(this.curCommand);
this.curCommand = undefined;
this.state ^= this.state & SVGPathDataParser.STATE_COMMANDS_MASK;
if (0 !== this.curArgs.length || !this.canParseCommandOrComma) {
throw new SyntaxError("Unterminated command at the path end.");
}
return result;
return commands;
}
parse(str: string, commands: SVGCommand[] = []) {
const finishCommand = (command: SVGCommand) => {
commands.push(command);
this.curArgs.length = 0;
this.canParseCommandOrComma = true;
};
for (let i = 0; i < str.length; i++) {
const c = str[i];
// White spaces parsing
if (
this.state & SVGPathDataParser.STATE_WSP ||
this.state & SVGPathDataParser.STATE_WSPS
) {
if (-1 !== WSP.indexOf(str[i])) {
this.state ^= this.state & SVGPathDataParser.STATE_WSP;
// any space stops current number parsing
if ("" !== this.curNumber) {
this.state ^= this.state & SVGPathDataParser.STATE_NUMBER_MASK;
} else {
continue;
}
}
if (isDigit(c)) {
this.curNumber += c;
this.curNumberHasExpDigits = this.curNumberHasExp;
continue;
}
// Commas parsing
if (
this.state & SVGPathDataParser.STATE_COMMA ||
this.state & SVGPathDataParser.STATE_COMMAS
) {
if (-1 !== COMMA.indexOf(str[i])) {
this.state ^= this.state & SVGPathDataParser.STATE_COMMA;
// any comma stops current number parsing
if ("" !== this.curNumber) {
this.state ^= this.state & SVGPathDataParser.STATE_NUMBER_MASK;
} else {
continue;
}
}
if ("e" === c || "E" === c) {
this.curNumber += c;
this.curNumberHasExp = true;
continue;
}
// Numbers parsing : -125.25e-125
if (this.state & SVGPathDataParser.STATE_NUMBER) {
// Reading the sign
if (
(this.state & SVGPathDataParser.STATE_NUMBER_MASK) ===
SVGPathDataParser.STATE_NUMBER
) {
this.state |=
SVGPathDataParser.STATE_NUMBER_INT |
SVGPathDataParser.STATE_NUMBER_DIGITS;
if (-1 !== SIGNS.indexOf(str[i])) {
this.curNumber += str[i];
continue;
}
if (("-" === c || "+" === c) && this.curNumberHasExp && !this.curNumberHasExpDigits) {
this.curNumber += c;
continue;
}
// if we already have a ".", it means we are starting a new number
if ("." === c && !this.curNumberHasExp && !this.curNumberHasDecimal) {
this.curNumber += c;
this.curNumberHasDecimal = true;
continue;
}
// New number
if (this.curNumber && -1 !== this.curCommandType) {
const val = Number(this.curNumber);
if (isNaN(val)) {
throw new SyntaxError(`Invalid number ending at ${i}`);
}
// Reading the exponent sign
if (this.state & SVGPathDataParser.STATE_NUMBER_EXPSIGN) {
this.state ^= SVGPathDataParser.STATE_NUMBER_EXPSIGN;
this.state |= SVGPathDataParser.STATE_NUMBER_DIGITS;
if (-1 !== SIGNS.indexOf(str[i])) {
this.curNumber += str[i];
continue;
if (this.curCommandType === SVGPathData.ARC) {
if (0 === this.curArgs.length || 1 === this.curArgs.length) {
if (0 > val) {
throw new SyntaxError(`Expected positive number, got "${val}" at index "${i}"`);
}
} else if (3 === this.curArgs.length || 4 === this.curArgs.length) {
if ("0" !== this.curNumber && "1" !== this.curNumber) {
throw new SyntaxError(`Expected a flag, got "${this.curNumber}" at index "${i}"`);
}
}
}
// Reading digits
if (this.state & SVGPathDataParser.STATE_NUMBER_DIGITS) {
if (-1 !== DIGITS.indexOf(str[i])) {
this.curNumber += str[i];
continue;
}
this.state ^= SVGPathDataParser.STATE_NUMBER_DIGITS;
}
// Ended reading left side digits
if (this.state & SVGPathDataParser.STATE_NUMBER_INT) {
this.state ^= SVGPathDataParser.STATE_NUMBER_INT;
// if got a point, reading right side digits
if (-1 !== DECPOINT.indexOf(str[i])) {
this.curNumber += str[i];
this.state |=
SVGPathDataParser.STATE_NUMBER_FLOAT |
SVGPathDataParser.STATE_NUMBER_DIGITS;
continue;
// if got e/E, reading the exponent
} else if (-1 !== EXPONENTS.indexOf(str[i])) {
this.curNumber += str[i];
this.state |=
SVGPathDataParser.STATE_NUMBER_EXP |
SVGPathDataParser.STATE_NUMBER_EXPSIGN;
continue;
}
// else we"re done with that number
this.state ^= this.state & SVGPathDataParser.STATE_NUMBER_MASK;
}
// Ended reading decimal digits
if (this.state & SVGPathDataParser.STATE_NUMBER_FLOAT) {
this.state ^= SVGPathDataParser.STATE_NUMBER_FLOAT;
// if got e/E, reading the exponent
if (-1 !== EXPONENTS.indexOf(str[i])) {
this.curNumber += str[i];
this.state |=
SVGPathDataParser.STATE_NUMBER_EXP |
SVGPathDataParser.STATE_NUMBER_EXPSIGN;
continue;
}
// else we"re done with that number
this.state ^= this.state & SVGPathDataParser.STATE_NUMBER_MASK;
}
// Ended reading exponent digits
if (this.state & SVGPathDataParser.STATE_NUMBER_EXP) {
// we"re done with that number
this.state ^= this.state & SVGPathDataParser.STATE_NUMBER_MASK;
}
}
// New number
if (this.curNumber) {
// Horizontal move to command (x)
if (this.state & SVGPathDataParser.STATE_HORIZ_LINE_TO) {
if (undefined === this.curCommand) {
commands.push({
this.curArgs.push(val);
if (this.curArgs.length === COMMAND_ARG_COUNTS[this.curCommandType]) {
if (SVGPathData.HORIZ_LINE_TO === this.curCommandType) {
finishCommand({
type: SVGPathData.HORIZ_LINE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
x: Number(this.curNumber),
relative: this.curCommandRelative,
x: val,
});
} else {
this.curCommand.x = Number(this.curNumber);
delete this.curCommand.invalid;
commands.push(this.curCommand);
this.curCommand = undefined;
}
this.state |= SVGPathDataParser.STATE_NUMBER;
// Vertical move to command (y)
} else if (this.state & SVGPathDataParser.STATE_VERT_LINE_TO) {
if (undefined === this.curCommand) {
commands.push({
} else if (SVGPathData.VERT_LINE_TO === this.curCommandType) {
finishCommand({
type: SVGPathData.VERT_LINE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
y: Number(this.curNumber),
relative: this.curCommandRelative,
y: val,
});
} else {
this.curCommand.y = Number(this.curNumber);
delete this.curCommand.invalid;
commands.push(this.curCommand);
this.curCommand = undefined;
}
this.state |= SVGPathDataParser.STATE_NUMBER;
// Move to / line to / smooth quadratic curve to commands (x, y)
} else if (
this.state & SVGPathDataParser.STATE_MOVE_TO ||
this.state & SVGPathDataParser.STATE_LINE_TO ||
this.state & SVGPathDataParser.STATE_SMOOTH_QUAD_TO
) {
if (undefined === this.curCommand) {
this.curCommand = {
type:
this.state & SVGPathDataParser.STATE_MOVE_TO
? SVGPathData.MOVE_TO
: this.state & SVGPathDataParser.STATE_LINE_TO
? SVGPathData.LINE_TO
: SVGPathData.SMOOTH_QUAD_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
x: Number(this.curNumber),
};
} else if (undefined === this.curCommand.x) {
this.curCommand.x = Number(this.curNumber);
} else {
delete this.curCommand.invalid;
this.curCommand.y = Number(this.curNumber);
commands.push(this.curCommand);
this.curCommand = undefined;
// Move to / line to / smooth quadratic curve to commands (x, y)
} else if (
this.curCommandType === SVGPathData.MOVE_TO ||
this.curCommandType === SVGPathData.LINE_TO ||
this.curCommandType === SVGPathData.SMOOTH_QUAD_TO
) {
finishCommand({
type: this.curCommandType,
relative: this.curCommandRelative,
x: this.curArgs[0],
y: this.curArgs[1],
} as SVGCommand);
// Switch to line to state
if (this.state & SVGPathDataParser.STATE_MOVE_TO) {
this.state ^= SVGPathDataParser.STATE_MOVE_TO;
this.state |= SVGPathDataParser.STATE_LINE_TO;
if (SVGPathData.MOVE_TO === this.curCommandType) {
this.curCommandType = SVGPathData.LINE_TO;
}
}
this.state |= SVGPathDataParser.STATE_NUMBER;
// Curve to commands (x1, y1, x2, y2, x, y)
} else if (this.state & SVGPathDataParser.STATE_CURVE_TO) {
if (undefined === this.curCommand) {
this.curCommand = {
} else if (this.curCommandType === SVGPathData.CURVE_TO) {
finishCommand({
type: SVGPathData.CURVE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
x1: Number(this.curNumber),
};
} else if (undefined === this.curCommand.x1) {
this.curCommand.x1 = Number(this.curNumber);
} else if (undefined === this.curCommand.y1) {
this.curCommand.y1 = Number(this.curNumber);
} else if (undefined === this.curCommand.x2) {
this.curCommand.x2 = Number(this.curNumber);
} else if (undefined === this.curCommand.y2) {
this.curCommand.y2 = Number(this.curNumber);
} else if (undefined === this.curCommand.x) {
this.curCommand.x = Number(this.curNumber);
} else if (undefined === this.curCommand.y) {
this.curCommand.y = Number(this.curNumber);
delete this.curCommand.invalid;
commands.push(this.curCommand);
this.curCommand = undefined;
}
this.state |= SVGPathDataParser.STATE_NUMBER;
// Smooth curve to commands (x1, y1, x, y)
} else if (this.state & SVGPathDataParser.STATE_SMOOTH_CURVE_TO) {
if (undefined === this.curCommand) {
this.curCommand = {
relative: this.curCommandRelative,
x1: this.curArgs[0],
y1: this.curArgs[1],
x2: this.curArgs[2],
y2: this.curArgs[3],
x: this.curArgs[4],
y: this.curArgs[5],
});
} else if (this.curCommandType === SVGPathData.SMOOTH_CURVE_TO) {
finishCommand({
type: SVGPathData.SMOOTH_CURVE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
x2: Number(this.curNumber),
};
} else if (undefined === this.curCommand.x2) {
this.curCommand.x2 = Number(this.curNumber);
} else if (undefined === this.curCommand.y2) {
this.curCommand.y2 = Number(this.curNumber);
} else if (undefined === this.curCommand.x) {
this.curCommand.x = Number(this.curNumber);
} else if (undefined === this.curCommand.y) {
this.curCommand.y = Number(this.curNumber);
delete this.curCommand.invalid;
commands.push(this.curCommand);
this.curCommand = undefined;
}
this.state |= SVGPathDataParser.STATE_NUMBER;
// Quadratic bezier curve to commands (x1, y1, x, y)
} else if (this.state & SVGPathDataParser.STATE_QUAD_TO) {
if (undefined === this.curCommand) {
this.curCommand = {
relative: this.curCommandRelative,
x2: this.curArgs[0],
y2: this.curArgs[1],
x: this.curArgs[2],
y: this.curArgs[3],
});
} else if (this.curCommandType === SVGPathData.QUAD_TO) {
finishCommand({
type: SVGPathData.QUAD_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
x1: Number(this.curNumber),
};
} else if (undefined === this.curCommand.x1) {
this.curCommand.x1 = Number(this.curNumber);
} else if (undefined === this.curCommand.y1) {
this.curCommand.y1 = Number(this.curNumber);
} else if (undefined === this.curCommand.x) {
this.curCommand.x = Number(this.curNumber);
} else if (undefined === this.curCommand.y) {
this.curCommand.y = Number(this.curNumber);
delete this.curCommand.invalid;
commands.push(this.curCommand);
this.curCommand = undefined;
}
this.state |= SVGPathDataParser.STATE_NUMBER;
// Elliptic arc commands (rX, rY, xRot, lArcFlag, sweepFlag, x, y)
} else if (this.state & SVGPathDataParser.STATE_ARC) {
if (undefined === this.curCommand) {
this.curCommand = {
relative: this.curCommandRelative,
x1: this.curArgs[0],
y1: this.curArgs[1],
x: this.curArgs[2],
y: this.curArgs[3],
});
} else if (this.curCommandType === SVGPathData.ARC) {
finishCommand({
type: SVGPathData.ARC,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
rX: Number(this.curNumber),
};
} else if (undefined === this.curCommand.rX) {
if (0 > Number(this.curNumber)) {
throw new SyntaxError(
`Expected positive number, got "${
this.curNumber
}" at index "${i}"`,
);
}
this.curCommand.rX = Number(this.curNumber);
} else if (undefined === this.curCommand.rY) {
if (0 > Number(this.curNumber)) {
throw new SyntaxError(
`Expected positive number, got "${
this.curNumber
}" at index "${i}"`,
);
}
this.curCommand.rY = Number(this.curNumber);
} else if (undefined === this.curCommand.xRot) {
this.curCommand.xRot = Number(this.curNumber);
} else if (undefined === this.curCommand.lArcFlag) {
if (-1 === FLAGS.indexOf(this.curNumber)) {
throw new SyntaxError(
`Expected a flag, got "${this.curNumber}" at index "${i}"`,
);
}
this.curCommand.lArcFlag = Number(this.curNumber);
} else if (undefined === this.curCommand.sweepFlag) {
if ("0" !== this.curNumber && "1" !== this.curNumber) {
throw new SyntaxError(
`Expected a flag, got "${this.curNumber}" at index "${i}"`,
);
}
this.curCommand.sweepFlag = Number(this.curNumber);
} else if (undefined === this.curCommand.x) {
this.curCommand.x = Number(this.curNumber);
} else if (undefined === this.curCommand.y) {
this.curCommand.y = Number(this.curNumber);
delete this.curCommand.invalid;
commands.push(this.curCommand);
this.curCommand = undefined;
relative: this.curCommandRelative,
rX: this.curArgs[0],
rY: this.curArgs[1],
xRot: this.curArgs[2],
lArcFlag: this.curArgs[3] as 0 | 1,
sweepFlag: this.curArgs[4] as 0 | 1,
x: this.curArgs[5],
y: this.curArgs[6],
});
}
this.state |= SVGPathDataParser.STATE_NUMBER;
}
this.curNumber = "";
// Continue if a white space or a comma was detected
if (-1 !== WSP.indexOf(str[i]) || -1 !== COMMA.indexOf(str[i])) {
continue;
}
// if a sign is detected, then parse the new number
if (-1 !== SIGNS.indexOf(str[i])) {
this.curNumber = str[i];
this.state |=
SVGPathDataParser.STATE_NUMBER_INT |
SVGPathDataParser.STATE_NUMBER_DIGITS;
continue;
}
// if the decpoint is detected, then parse the new number
if (-1 !== DECPOINT.indexOf(str[i])) {
this.curNumber = str[i];
this.state |=
SVGPathDataParser.STATE_NUMBER_FLOAT |
SVGPathDataParser.STATE_NUMBER_DIGITS;
continue;
}
this.curNumberHasExpDigits = false;
this.curNumberHasExp = false;
this.curNumberHasDecimal = false;
this.canParseCommandOrComma = true;
}
// End of a command
if (-1 !== COMMANDS.indexOf(str[i])) {
// Adding residual command
if (undefined !== this.curCommand) {
if (this.curCommand.invalid) {
throw new SyntaxError(`Unterminated command at index ${i}.`);
}
commands.push(this.curCommand);
this.curCommand = undefined;
this.state ^= this.state & SVGPathDataParser.STATE_COMMANDS_MASK;
}
// Continue if a white space or a comma was detected
if (isWhiteSpace(c)) {
continue;
}
if ("," === c && this.canParseCommandOrComma) {
// L 0,0, H is not valid:
this.canParseCommandOrComma = false;
continue;
}
// if a sign is detected, then parse the new number
if ("+" === c || "-" === c || "." === c) {
this.curNumber = c;
this.curNumberHasDecimal = "." === c;
continue;
}
// Adding residual command
if (0 !== this.curArgs.length) {
throw new SyntaxError(`Unterminated command at index ${i}.`);
}
if (!this.canParseCommandOrComma) {
throw new SyntaxError(`Unexpected character "${c}" at index ${i}. Command cannot follow comma`);
}
this.canParseCommandOrComma = false;
// Detecting the next command
this.state ^= this.state & SVGPathDataParser.STATE_COMMANDS_MASK;
// Is the command relative
if (str[i] === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_RELATIVE;
} else {
this.state ^= this.state & SVGPathDataParser.STATE_RELATIVE;
}
// Horizontal move to command
if ("z" === str[i].toLowerCase()) {
if ("z" === c || "Z" === c) {
commands.push({
type: SVGPathData.CLOSE_PATH,
});
this.state = SVGPathDataParser.STATE_COMMAS_WSPS;
this.canParseCommandOrComma = true;
this.curCommandType = -1;
continue;
// Horizontal move to command
} else if ("h" === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_HORIZ_LINE_TO;
this.curCommand = {
type: SVGPathData.HORIZ_LINE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
} else if ("h" === c || "H" === c) {
this.curCommandType = SVGPathData.HORIZ_LINE_TO;
this.curCommandRelative = "h" === c;
// Vertical move to command
} else if ("v" === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_VERT_LINE_TO;
this.curCommand = {
type: SVGPathData.VERT_LINE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
} else if ("v" === c || "V" === c) {
this.curCommandType = SVGPathData.VERT_LINE_TO;
this.curCommandRelative = "v" === c;
// Move to command
} else if ("m" === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_MOVE_TO;
this.curCommand = {
type: SVGPathData.MOVE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
} else if ("m" === c || "M" === c) {
this.curCommandType = SVGPathData.MOVE_TO;
this.curCommandRelative = "m" === c;
// Line to command
} else if ("l" === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_LINE_TO;
this.curCommand = {
type: SVGPathData.LINE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
} else if ("l" === c || "L" === c) {
this.curCommandType = SVGPathData.LINE_TO;
this.curCommandRelative = "l" === c;
// Curve to command
} else if ("c" === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_CURVE_TO;
this.curCommand = {
type: SVGPathData.CURVE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
} else if ("c" === c || "C" === c) {
this.curCommandType = SVGPathData.CURVE_TO;
this.curCommandRelative = "c" === c;
// Smooth curve to command
} else if ("s" === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_SMOOTH_CURVE_TO;
this.curCommand = {
type: SVGPathData.SMOOTH_CURVE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
} else if ("s" === c || "S" === c) {
this.curCommandType = SVGPathData.SMOOTH_CURVE_TO;
this.curCommandRelative = "s" === c;
// Quadratic bezier curve to command
} else if ("q" === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_QUAD_TO;
this.curCommand = {
type: SVGPathData.QUAD_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
} else if ("q" === c || "Q" === c) {
this.curCommandType = SVGPathData.QUAD_TO;
this.curCommandRelative = "q" === c;
// Smooth quadratic bezier curve to command
} else if ("t" === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_SMOOTH_QUAD_TO;
this.curCommand = {
type: SVGPathData.SMOOTH_QUAD_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
} else if ("t" === c || "T" === c) {
this.curCommandType = SVGPathData.SMOOTH_QUAD_TO;
this.curCommandRelative = "t" === c;
// Elliptic arc command
} else if ("a" === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_ARC;
this.curCommand = {
type: SVGPathData.ARC,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
// Unkown command
} else if ("a" === c || "A" === c) {
this.curCommandType = SVGPathData.ARC;
this.curCommandRelative = "a" === c;
} else {
throw new SyntaxError(
`Unexpected character "${str[i]}" at index ${i}.`,
);
throw new SyntaxError(`Unexpected character "${c}" at index ${i}.`);
}
// White spaces can follow a command
this.state |=
SVGPathDataParser.STATE_COMMAS_WSPS | SVGPathDataParser.STATE_NUMBER;
}
return commands;
}
/**
* Return a wrapper around this parser which applies the transformation on parsed commands.
*/
/**
* Return a wrapper around this parser which applies the transformation on parsed commands.
*/
transform(transform: TransformFunction) {

@@ -495,3 +257,6 @@ const result = Object.create(this, {

value(chunk: string, commands: SVGCommand[] = []) {
const parsedCommands = Object.getPrototypeOf(this).parse.call(this, chunk);
const parsedCommands = Object.getPrototypeOf(this).parse.call(
this,
chunk,
);
for (const c of parsedCommands) {

@@ -511,44 +276,2 @@ const cT = transform(c);

}
// Parsing states
static readonly STATE_WSP = 1;
static readonly STATE_WSPS = 2;
static readonly STATE_COMMA = 4;
static readonly STATE_COMMAS = 8;
static readonly STATE_COMMAS_WSPS = SVGPathDataParser.STATE_WSP |
SVGPathDataParser.STATE_WSPS |
SVGPathDataParser.STATE_COMMA |
SVGPathDataParser.STATE_COMMAS;
static readonly STATE_NUMBER = 16;
static readonly STATE_NUMBER_DIGITS = 32;
static readonly STATE_NUMBER_INT = 64;
static readonly STATE_NUMBER_FLOAT = 128;
static readonly STATE_NUMBER_EXP = 256;
static readonly STATE_NUMBER_EXPSIGN = 512;
static readonly STATE_NUMBER_MASK = SVGPathDataParser.STATE_NUMBER |
SVGPathDataParser.STATE_NUMBER_DIGITS |
SVGPathDataParser.STATE_NUMBER_INT |
SVGPathDataParser.STATE_NUMBER_EXP |
SVGPathDataParser.STATE_NUMBER_FLOAT;
static readonly STATE_RELATIVE = 1024;
static readonly STATE_CLOSE_PATH = 2048; // Close path command (z/Z)
static readonly STATE_MOVE_TO = 4096; // Move to command (m/M)
static readonly STATE_LINE_TO = 8192; // Line to command (l/L=)
static readonly STATE_HORIZ_LINE_TO = 16384; // Horizontal line to command (h/H)
static readonly STATE_VERT_LINE_TO = 32768; // Vertical line to command (v/V)
static readonly STATE_CURVE_TO = 65536; // Curve to command (c/C)
static readonly STATE_SMOOTH_CURVE_TO = 131072; // Smooth curve to command (s/S)
static readonly STATE_QUAD_TO = 262144; // Quadratic bezier curve to command (q/Q)
static readonly STATE_SMOOTH_QUAD_TO = 524288; // Smooth quadratic bezier curve to command (t/T)
static readonly STATE_ARC = 1048576; // Elliptic arc command (a/A)
static readonly STATE_COMMANDS_MASK = SVGPathDataParser.STATE_CLOSE_PATH |
SVGPathDataParser.STATE_MOVE_TO |
SVGPathDataParser.STATE_LINE_TO |
SVGPathDataParser.STATE_HORIZ_LINE_TO |
SVGPathDataParser.STATE_VERT_LINE_TO |
SVGPathDataParser.STATE_CURVE_TO |
SVGPathDataParser.STATE_SMOOTH_CURVE_TO |
SVGPathDataParser.STATE_QUAD_TO |
SVGPathDataParser.STATE_SMOOTH_QUAD_TO |
SVGPathDataParser.STATE_ARC;
}

@@ -11,9 +11,9 @@ /* eslint-disable no-new */

it('should not work when badly declared', () => {
// assert.throw(() => {
// new SVGPathData('A');
// }, SyntaxError, 'Unterminated command at the path end.');
// assert.throw(() => {
// new SVGPathData('A 30');
// }, SyntaxError, 'Unterminated command at the path end.');
assert.throw(() => {
new SVGPathData('A');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(() => {
new SVGPathData('A 30');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(() => {
new SVGPathData('A 30 50');

@@ -40,4 +40,4 @@ }, SyntaxError, 'Unterminated command at the path end.');

assert.throw(() => {
new SVGPathData('A,-30,50,0,0,1,162.55,162.45');
}, SyntaxError, 'Expected positive number, got "-30" at index "5"');
new SVGPathData('A-30,50,0,0,1,162.55,162.45');
}, SyntaxError, 'Expected positive number, got "-30" at index "4"');
});

@@ -47,4 +47,4 @@

assert.throw(() => {
new SVGPathData('A,30,-50,0,0,1,162.55,162.45');
}, SyntaxError, 'Expected positive number, got "-50" at index "8"');
new SVGPathData('A30,-50,0,0,1,162.55,162.45');
}, SyntaxError, 'Expected positive number, got "-50" at index "7"');
});

@@ -54,4 +54,4 @@

assert.throw(() => {
new SVGPathData('A,30,50,0,15,1,162.55,162.45');
}, SyntaxError, 'Expected a flag, got "15" at index "12"');
new SVGPathData('A30,50,0,15,1,162.55,162.45');
}, SyntaxError, 'Expected a flag, got "15" at index "11"');
});

@@ -61,8 +61,8 @@

assert.throw(() => {
new SVGPathData('A,30,50,0,0,15,162.55,162.45');
}, SyntaxError, 'Expected a flag, got "15" at index "14"');
new SVGPathData('A30,50,0,0,15,162.55,162.45');
}, SyntaxError, 'Expected a flag, got "15" at index "13"');
});
it('should work with comma separated coordinates', () => {
const commands = new SVGPathData('A,30,50,0,0,1,162.55,162.45').commands;
const commands = new SVGPathData('A 30,50,0,0,1,162.55,162.45').commands;

@@ -79,2 +79,5 @@ assert.equal(commands[0].type, SVGPathData.ARC);

});
it('should not work with a comma immediately after A', () => {
assert.throw(() => new SVGPathData('A,30,50,0,0,1,162.55,162.45'));
});

@@ -81,0 +84,0 @@ it('should work with space separated coordinates', () => {

@@ -28,3 +28,3 @@ /* eslint max-len:0 */

it('should work before a command sequence', () => {
const commands = new SVGPathData(' Z M10,10 L10,10, H10, V10').commands;
const commands = new SVGPathData(' Z M10,10 L10,10 H10 V10').commands;

@@ -35,3 +35,3 @@ assert.equal(commands[0].type, SVGPathData.CLOSE_PATH);

it('should work after a command sequence', () => {
const commands = new SVGPathData('M10,10 L10,10, H10, V10 Z').commands;
const commands = new SVGPathData('M10,10 L10,10 H10 V10 Z').commands;

@@ -42,3 +42,3 @@ assert.equal(commands[4].type, SVGPathData.CLOSE_PATH);

it('should work in a command sequence', () => {
const commands = new SVGPathData('M10,10 L10,10, H10, V10 Z M10,10 L10,10, H10, V10').commands;
const commands = new SVGPathData('M10,10 L10,10 H10 V10 Z M10,10 L10,10 H10 V10').commands;

@@ -45,0 +45,0 @@ assert.equal(commands[4].type, SVGPathData.CLOSE_PATH);

@@ -221,3 +221,3 @@ /* eslint max-len:0 */

const commands = new SVGPathData(
'V100H100v0.12h0.12,V100,h100v-10e-5 H-10e-5').commands;
'V100H100v0.12h0.12V100h100v-10e-5 H-10e-5').commands;

@@ -224,0 +224,0 @@ assert.equal(commands[0].type, SVGPathData.VERT_LINE_TO);

@@ -93,2 +93,7 @@ /* eslint max-len:0 */

it('should fail with eE', () => {
assert.throws(() => new SVGPathData('H1ee2'));
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc