mirror of
https://github.com/svg/svgo.git
synced 2025-07-31 07:44:22 +03:00
Implement path stringify (#1387)
Ref https://github.com/svg/svgo/issues/32 Added `stringifyPathData` utility to produce small as possible path by - matching leading moveto and line to commands (m -> l, M -> L) - combining moveto and lineto commands - avoiding space before decimal numbers if possible
This commit is contained in:
78
lib/path.js
78
lib/path.js
@ -208,3 +208,81 @@ const parsePathData = (string) => {
|
||||
return pathData;
|
||||
};
|
||||
exports.parsePathData = parsePathData;
|
||||
|
||||
const stringifyNumber = ({ number, precision }) => {
|
||||
let result;
|
||||
if (precision == null) {
|
||||
result = number.toString();
|
||||
} else {
|
||||
result = number.toFixed(precision);
|
||||
if (result.includes('.')) {
|
||||
result = result.replace(/\.?0+$/, '')
|
||||
}
|
||||
}
|
||||
// remove zero whole from decimal number
|
||||
if (result !== '0') {
|
||||
result = result.replace(/^0/, '').replace(/^-0/, '-');
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
// elliptical arc large-arc and sweep flags are rendered with spaces
|
||||
// because many non-browser environments are not able to parse such paths
|
||||
const stringifyArgs = ({ args, precision }) => {
|
||||
let result = '';
|
||||
let prev;
|
||||
for (let i = 0; i < args.length; i += 1) {
|
||||
const number = args[i];
|
||||
const numberString = stringifyNumber({ number, precision });
|
||||
// avoid space before first and negative numbers
|
||||
if (i === 0 || numberString.startsWith('-')) {
|
||||
result += numberString;
|
||||
} else if (prev.includes('.') && numberString.startsWith('.')) {
|
||||
result += numberString;
|
||||
} else {
|
||||
result += ` ${numberString}`;
|
||||
}
|
||||
prev = numberString;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const stringifyPathData = ({ pathData, precision }) => {
|
||||
// combine sequence of the same commands
|
||||
let combined = [];
|
||||
for (let i = 0; i < pathData.length; i += 1) {
|
||||
const { command, args } = pathData[i];
|
||||
if (i === 0) {
|
||||
combined.push({ command, args });
|
||||
} else {
|
||||
const last = combined[combined.length - 1];
|
||||
// match leading moveto with following lineto
|
||||
if (i === 1) {
|
||||
if (command === 'L') {
|
||||
last.command = 'M';
|
||||
}
|
||||
if (command === 'l') {
|
||||
last.command = 'm';
|
||||
}
|
||||
}
|
||||
if (
|
||||
(last.command === command &&
|
||||
last.command !== 'M' &&
|
||||
last.command !== 'm') ||
|
||||
// combine matching moveto and lineto sequences
|
||||
(last.command === 'M' && command === 'L') ||
|
||||
(last.command === 'm' && command === 'l')
|
||||
) {
|
||||
last.args = [...last.args, ...args];
|
||||
} else {
|
||||
combined.push({ command, args });
|
||||
}
|
||||
}
|
||||
}
|
||||
let result = '';
|
||||
for (const { command, args } of combined) {
|
||||
result += command + stringifyArgs({ args, precision });
|
||||
}
|
||||
return result;
|
||||
};
|
||||
exports.stringifyPathData = stringifyPathData;
|
||||
|
Reference in New Issue
Block a user