| import { removeLeadingZero } from '../lib/svgo/tools.js'; |
| |
| /** |
| * @typedef CleanupNumericValuesParams |
| * @property {number=} floatPrecision |
| * @property {boolean=} leadingZero |
| * @property {boolean=} defaultPx |
| * @property {boolean=} convertToPx |
| */ |
| |
| export const name = 'cleanupNumericValues'; |
| export const description = |
| 'rounds numeric values to the fixed precision, removes default "px" units'; |
| |
| const regNumericValues = |
| /^([-+]?\d*\.?\d+([eE][-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|em|ex|%)?$/; |
| |
| const absoluteLengths = { |
| // relative to px |
| cm: 96 / 2.54, |
| mm: 96 / 25.4, |
| in: 96, |
| pt: 4 / 3, |
| pc: 16, |
| px: 1, |
| }; |
| |
| /** |
| * Round numeric values to the fixed precision, remove default 'px' units. |
| * |
| * @author Kir Belevich |
| * |
| * @type {import('../lib/types.js').Plugin<CleanupNumericValuesParams>} |
| */ |
| export const fn = (_root, params) => { |
| const { |
| floatPrecision = 3, |
| leadingZero = true, |
| defaultPx = true, |
| convertToPx = true, |
| } = params; |
| |
| return { |
| element: { |
| enter: (node) => { |
| if (node.attributes.viewBox != null) { |
| const numbers = node.attributes.viewBox |
| .trim() |
| .split(/(?:\s,?|,)\s*/g); |
| node.attributes.viewBox = numbers |
| .map((value) => { |
| const num = Number(value); |
| return Number.isNaN(num) |
| ? value |
| : Number(num.toFixed(floatPrecision)); |
| }) |
| .join(' '); |
| } |
| |
| for (const [name, value] of Object.entries(node.attributes)) { |
| // The `version` attribute is a text string and cannot be rounded |
| if (name === 'version') { |
| continue; |
| } |
| |
| const match = regNumericValues.exec(value); |
| |
| // if attribute value matches regNumericValues |
| if (match) { |
| // round it to the fixed precision |
| let num = Number(Number(match[1]).toFixed(floatPrecision)); |
| /** @type {any} */ |
| const matchedUnit = match[3] || ''; |
| /** @type {'' | keyof typeof absoluteLengths} */ |
| let units = matchedUnit; |
| |
| // convert absolute values to pixels |
| if (convertToPx && units !== '' && units in absoluteLengths) { |
| const pxNum = Number( |
| (absoluteLengths[units] * Number(match[1])).toFixed( |
| floatPrecision, |
| ), |
| ); |
| if (pxNum.toString().length < match[0].length) { |
| num = pxNum; |
| units = 'px'; |
| } |
| } |
| |
| // and remove leading zero |
| let str; |
| if (leadingZero) { |
| str = removeLeadingZero(num); |
| } else { |
| str = num.toString(); |
| } |
| |
| // remove default 'px' units |
| if (defaultPx && units === 'px') { |
| units = ''; |
| } |
| |
| node.attributes[name] = str + units; |
| } |
| } |
| }, |
| }, |
| }; |
| }; |