From 63234de9bb2407849563b6f5781849bb72b20f7f Mon Sep 17 00:00:00 2001 From: Seth Falco Date: Sun, 24 Dec 2023 07:27:07 +0000 Subject: [PATCH] refactor path utils --- lib/path.js | 80 ++++++++++++++++++++++++++--------------------- lib/svgo/tools.js | 23 +++++++------- 2 files changed, 56 insertions(+), 47 deletions(-) diff --git a/lib/path.js b/lib/path.js index 1e383dd98..36f425fdb 100644 --- a/lib/path.js +++ b/lib/path.js @@ -243,15 +243,21 @@ const parsePathData = (string) => { exports.parsePathData = parsePathData; /** - * @type {(number: number, precision?: number) => string} + * @type {(number: number, precision?: number) => { + * roundedStr: string, + * rounded: number + * }} */ -const stringifyNumber = (number, precision) => { +const roundAndStringify = (number, precision) => { if (precision != null) { const ratio = 10 ** precision; number = Math.round(number * ratio) / ratio; } - // remove zero whole from decimal number - return removeLeadingZero(number); + + return { + roundedStr: removeLeadingZero(number), + rounded: number, + }; }; /** @@ -267,29 +273,35 @@ const stringifyNumber = (number, precision) => { */ const stringifyArgs = (command, args, precision, disableSpaceAfterFlags) => { let result = ''; - let prev = ''; - for (let i = 0; i < args.length; i += 1) { - const number = args[i]; - const numberString = stringifyNumber(number, precision); + let previous; + + for (let i = 0; i < args.length; i++) { + const { roundedStr, rounded } = roundAndStringify(args[i], precision); if ( disableSpaceAfterFlags && (command === 'A' || command === 'a') && // consider combined arcs (i % 7 === 4 || i % 7 === 5) ) { - result += numberString; - } else if (i === 0 || numberString.startsWith('-')) { + result += roundedStr; + } else if (i === 0 || rounded < 0) { // avoid space before first and negative numbers - result += numberString; - } else if (prev.includes('.') && numberString.startsWith('.')) { + result += roundedStr; + } else if ( + !Number.isInteger(previous) && + rounded != 0 && + rounded < 1 && + rounded > -1 + ) { // remove space before decimal with zero whole // only when previous number is also decimal - result += numberString; + result += roundedStr; } else { - result += ` ${numberString}`; + result += ` ${roundedStr}`; } - prev = numberString; + previous = rounded; } + return result; }; @@ -302,45 +314,41 @@ const stringifyArgs = (command, args, precision, disableSpaceAfterFlags) => { */ /** - * @type {(options: StringifyPathDataOptions) => string} + * @param {StringifyPathDataOptions} options + * @returns {string} */ const stringifyPathData = ({ pathData, precision, disableSpaceAfterFlags }) => { - // combine sequence of the same commands - let combined = []; - for (let i = 0; i < pathData.length; i += 1) { + const normalized = []; + for (let i = 0; i < pathData.length; i++) { const { command, args } = pathData[i]; if (i === 0) { - combined.push({ command, args }); + normalized.push({ command, args }); } else { - /** - * @type {PathDataItem} - */ - const last = combined[combined.length - 1]; + const prev = normalized[normalized.length - 1]; // match leading moveto with following lineto if (i === 1) { if (command === 'L') { - last.command = 'M'; - } - if (command === 'l') { - last.command = 'm'; + prev.command = 'M'; + } else if (command === 'l') { + prev.command = 'm'; } } if ( - (last.command === command && - last.command !== 'M' && - last.command !== 'm') || + (prev.command === command && + prev.command !== 'M' && + prev.command !== 'm') || // combine matching moveto and lineto sequences - (last.command === 'M' && command === 'L') || - (last.command === 'm' && command === 'l') + (prev.command === 'M' && command === 'L') || + (prev.command === 'm' && command === 'l') ) { - last.args = [...last.args, ...args]; + prev.args = [...prev.args, ...args]; } else { - combined.push({ command, args }); + normalized.push({ command, args }); } } } let result = ''; - for (const { command, args } of combined) { + for (const { command, args } of normalized) { result += command + stringifyArgs(command, args, precision, disableSpaceAfterFlags); } diff --git a/lib/svgo/tools.js b/lib/svgo/tools.js index e778034dc..afbda50c0 100644 --- a/lib/svgo/tools.js +++ b/lib/svgo/tools.js @@ -124,23 +124,24 @@ exports.cleanupOutData = (data, params, command) => { /** * Remove floating-point numbers leading zero. * + * @param {number} value + * @returns {string} * @example * 0.5 → .5 - * - * @example * -0.5 → -.5 - * - * @type {(num: number) => string} */ -const removeLeadingZero = (num) => { - var strNum = num.toString(); +const removeLeadingZero = (value) => { + const strValue = value.toString(); - if (0 < num && num < 1 && strNum.charAt(0) === '0') { - strNum = strNum.slice(1); - } else if (-1 < num && num < 0 && strNum.charAt(1) === '0') { - strNum = strNum.charAt(0) + strNum.slice(2); + if (0 < value && value < 1 && strValue.startsWith('0')) { + return strValue.slice(1); } - return strNum; + + if (-1 < value && value < 0 && strValue[1] === '0') { + return strValue[0] + strValue.slice(2); + } + + return strValue; }; exports.removeLeadingZero = removeLeadingZero;