diff options
| author | littleblack111 <littleblack11111@gmail.com> | 2023-10-14 15:05:55 +0800 |
|---|---|---|
| committer | littleblack111 <littleblack11111@gmail.com> | 2023-10-14 15:05:55 +0800 |
| commit | c06f7ee0a40255214c477e13d9b956a7e07c2900 (patch) | |
| tree | f5982a568ef8fab865d0142e71c7494fea3e4c1b /node_modules/svgo/plugins/applyTransforms.js | |
| parent | 3fd9400d4dccc25aa29b5702216732e0b7542073 (diff) | |
postcss,(plugins): nanocss, postcss-preset-env
Diffstat (limited to 'node_modules/svgo/plugins/applyTransforms.js')
| -rw-r--r-- | node_modules/svgo/plugins/applyTransforms.js | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/node_modules/svgo/plugins/applyTransforms.js b/node_modules/svgo/plugins/applyTransforms.js new file mode 100644 index 00000000..bf60ac89 --- /dev/null +++ b/node_modules/svgo/plugins/applyTransforms.js @@ -0,0 +1,379 @@ +'use strict'; + +/** + * @typedef {import('../lib/types').PathDataItem} PathDataItem + * @typedef {import('../lib/types').XastElement} XastElement + */ + +const { collectStylesheet, computeStyle } = require('../lib/style.js'); +const { + transformsMultiply, + transform2js, + transformArc, +} = require('./_transforms.js'); +const { path2js } = require('./_path.js'); +const { removeLeadingZero } = require('../lib/svgo/tools.js'); +const { referencesProps, attrsGroupsDefaults } = require('./_collections.js'); + +/** + * @typedef {Array<PathDataItem>} PathData + * @typedef {Array<number>} Matrix + */ + +const regNumericValues = /[-+]?(\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g; + +/** + * Apply transformation(s) to the Path data. + * + * @type {import('../lib/types').Plugin<{ + * transformPrecision: number, + * applyTransformsStroked: boolean, + * }>} + */ +const applyTransforms = (root, params) => { + const stylesheet = collectStylesheet(root); + return { + element: { + enter: (node) => { + const computedStyle = computeStyle(stylesheet, node); + + // used only for paths for now + if (node.attributes.d == null) { + return; + } + + // stroke and stroke-width can be redefined with <use> + if (node.attributes.id != null) { + return; + } + + // if there are no 'stroke' attr and references to other objects such as + // gradiends or clip-path which are also subjects to transform. + if ( + node.attributes.transform == null || + node.attributes.transform === '' || + // styles are not considered when applying transform + // can be fixed properly with new style engine + node.attributes.style != null || + Object.entries(node.attributes).some( + ([name, value]) => + referencesProps.includes(name) && value.includes('url(') + ) + ) { + return; + } + + const matrix = transformsMultiply( + transform2js(node.attributes.transform) + ); + const stroke = + computedStyle.stroke != null && computedStyle.stroke.type === 'static' + ? computedStyle.stroke.value + : null; + + const strokeWidth = + computedStyle['stroke-width'] != null && + computedStyle['stroke-width'].type === 'static' + ? computedStyle['stroke-width'].value + : null; + const transformPrecision = params.transformPrecision; + + if ( + (computedStyle.stroke != null && + computedStyle.stroke.type === 'dynamic') || + (computedStyle.strokeWidth != null && + computedStyle['stroke-width'].type === 'dynamic') + ) { + return; + } + + const scale = Number( + Math.sqrt( + matrix.data[0] * matrix.data[0] + matrix.data[1] * matrix.data[1] + ).toFixed(transformPrecision) + ); + + if (stroke && stroke != 'none') { + if (params.applyTransformsStroked === false) { + return; + } + + // stroke cannot be transformed with different vertical and horizontal scale or skew + if ( + (matrix.data[0] !== matrix.data[3] || + matrix.data[1] !== -matrix.data[2]) && + (matrix.data[0] !== -matrix.data[3] || + matrix.data[1] !== matrix.data[2]) + ) { + return; + } + + // apply transform to stroke-width, stroke-dashoffset and stroke-dasharray + if (scale !== 1) { + if (node.attributes['vector-effect'] !== 'non-scaling-stroke') { + node.attributes['stroke-width'] = ( + strokeWidth || attrsGroupsDefaults.presentation['stroke-width'] + ) + .trim() + .replace(regNumericValues, (num) => + removeLeadingZero(Number(num) * scale) + ); + + if (node.attributes['stroke-dashoffset'] != null) { + node.attributes['stroke-dashoffset'] = node.attributes[ + 'stroke-dashoffset' + ] + .trim() + .replace(regNumericValues, (num) => + removeLeadingZero(Number(num) * scale) + ); + } + + if (node.attributes['stroke-dasharray'] != null) { + node.attributes['stroke-dasharray'] = node.attributes[ + 'stroke-dasharray' + ] + .trim() + .replace(regNumericValues, (num) => + removeLeadingZero(Number(num) * scale) + ); + } + } + } + } + + const pathData = path2js(node); + applyMatrixToPathData(pathData, matrix.data); + + // remove transform attr + delete node.attributes.transform; + }, + }, + }; +}; +exports.applyTransforms = applyTransforms; + +/** + * @type {(matrix: Matrix, x: number, y: number) => [number, number]} + */ +const transformAbsolutePoint = (matrix, x, y) => { + const newX = matrix[0] * x + matrix[2] * y + matrix[4]; + const newY = matrix[1] * x + matrix[3] * y + matrix[5]; + return [newX, newY]; +}; + +/** + * @type {(matrix: Matrix, x: number, y: number) => [number, number]} + */ +const transformRelativePoint = (matrix, x, y) => { + const newX = matrix[0] * x + matrix[2] * y; + const newY = matrix[1] * x + matrix[3] * y; + return [newX, newY]; +}; + +/** + * @type {(pathData: PathData, matrix: Matrix) => void} + */ +const applyMatrixToPathData = (pathData, matrix) => { + /** + * @type {[number, number]} + */ + const start = [0, 0]; + /** + * @type {[number, number]} + */ + const cursor = [0, 0]; + + for (const pathItem of pathData) { + let { command, args } = pathItem; + + // moveto (x y) + if (command === 'M') { + cursor[0] = args[0]; + cursor[1] = args[1]; + start[0] = cursor[0]; + start[1] = cursor[1]; + const [x, y] = transformAbsolutePoint(matrix, args[0], args[1]); + args[0] = x; + args[1] = y; + } + if (command === 'm') { + cursor[0] += args[0]; + cursor[1] += args[1]; + start[0] = cursor[0]; + start[1] = cursor[1]; + const [x, y] = transformRelativePoint(matrix, args[0], args[1]); + args[0] = x; + args[1] = y; + } + + // horizontal lineto (x) + // convert to lineto to handle two-dimentional transforms + if (command === 'H') { + command = 'L'; + args = [args[0], cursor[1]]; + } + if (command === 'h') { + command = 'l'; + args = [args[0], 0]; + } + + // vertical lineto (y) + // convert to lineto to handle two-dimentional transforms + if (command === 'V') { + command = 'L'; + args = [cursor[0], args[0]]; + } + if (command === 'v') { + command = 'l'; + args = [0, args[0]]; + } + + // lineto (x y) + if (command === 'L') { + cursor[0] = args[0]; + cursor[1] = args[1]; + const [x, y] = transformAbsolutePoint(matrix, args[0], args[1]); + args[0] = x; + args[1] = y; + } + if (command === 'l') { + cursor[0] += args[0]; + cursor[1] += args[1]; + const [x, y] = transformRelativePoint(matrix, args[0], args[1]); + args[0] = x; + args[1] = y; + } + + // curveto (x1 y1 x2 y2 x y) + if (command === 'C') { + cursor[0] = args[4]; + cursor[1] = args[5]; + const [x1, y1] = transformAbsolutePoint(matrix, args[0], args[1]); + const [x2, y2] = transformAbsolutePoint(matrix, args[2], args[3]); + const [x, y] = transformAbsolutePoint(matrix, args[4], args[5]); + args[0] = x1; + args[1] = y1; + args[2] = x2; + args[3] = y2; + args[4] = x; + args[5] = y; + } + if (command === 'c') { + cursor[0] += args[4]; + cursor[1] += args[5]; + const [x1, y1] = transformRelativePoint(matrix, args[0], args[1]); + const [x2, y2] = transformRelativePoint(matrix, args[2], args[3]); + const [x, y] = transformRelativePoint(matrix, args[4], args[5]); + args[0] = x1; + args[1] = y1; + args[2] = x2; + args[3] = y2; + args[4] = x; + args[5] = y; + } + + // smooth curveto (x2 y2 x y) + if (command === 'S') { + cursor[0] = args[2]; + cursor[1] = args[3]; + const [x2, y2] = transformAbsolutePoint(matrix, args[0], args[1]); + const [x, y] = transformAbsolutePoint(matrix, args[2], args[3]); + args[0] = x2; + args[1] = y2; + args[2] = x; + args[3] = y; + } + if (command === 's') { + cursor[0] += args[2]; + cursor[1] += args[3]; + const [x2, y2] = transformRelativePoint(matrix, args[0], args[1]); + const [x, y] = transformRelativePoint(matrix, args[2], args[3]); + args[0] = x2; + args[1] = y2; + args[2] = x; + args[3] = y; + } + + // quadratic Bézier curveto (x1 y1 x y) + if (command === 'Q') { + cursor[0] = args[2]; + cursor[1] = args[3]; + const [x1, y1] = transformAbsolutePoint(matrix, args[0], args[1]); + const [x, y] = transformAbsolutePoint(matrix, args[2], args[3]); + args[0] = x1; + args[1] = y1; + args[2] = x; + args[3] = y; + } + if (command === 'q') { + cursor[0] += args[2]; + cursor[1] += args[3]; + const [x1, y1] = transformRelativePoint(matrix, args[0], args[1]); + const [x, y] = transformRelativePoint(matrix, args[2], args[3]); + args[0] = x1; + args[1] = y1; + args[2] = x; + args[3] = y; + } + + // smooth quadratic Bézier curveto (x y) + if (command === 'T') { + cursor[0] = args[0]; + cursor[1] = args[1]; + const [x, y] = transformAbsolutePoint(matrix, args[0], args[1]); + args[0] = x; + args[1] = y; + } + if (command === 't') { + cursor[0] += args[0]; + cursor[1] += args[1]; + const [x, y] = transformRelativePoint(matrix, args[0], args[1]); + args[0] = x; + args[1] = y; + } + + // elliptical arc (rx ry x-axis-rotation large-arc-flag sweep-flag x y) + if (command === 'A') { + transformArc(cursor, args, matrix); + cursor[0] = args[5]; + cursor[1] = args[6]; + // reduce number of digits in rotation angle + if (Math.abs(args[2]) > 80) { + const a = args[0]; + const rotation = args[2]; + args[0] = args[1]; + args[1] = a; + args[2] = rotation + (rotation > 0 ? -90 : 90); + } + const [x, y] = transformAbsolutePoint(matrix, args[5], args[6]); + args[5] = x; + args[6] = y; + } + if (command === 'a') { + transformArc([0, 0], args, matrix); + cursor[0] += args[5]; + cursor[1] += args[6]; + // reduce number of digits in rotation angle + if (Math.abs(args[2]) > 80) { + const a = args[0]; + const rotation = args[2]; + args[0] = args[1]; + args[1] = a; + args[2] = rotation + (rotation > 0 ? -90 : 90); + } + const [x, y] = transformRelativePoint(matrix, args[5], args[6]); + args[5] = x; + args[6] = y; + } + + // closepath + if (command === 'z' || command === 'Z') { + cursor[0] = start[0]; + cursor[1] = start[1]; + } + + pathItem.command = command; + pathItem.args = args; + } +}; |
