summaryrefslogtreecommitdiff
path: root/node_modules/svgo/plugins
diff options
context:
space:
mode:
authorlittleblack111 <littleblack11111@gmail.com>2023-10-14 15:05:55 +0800
committerlittleblack111 <littleblack11111@gmail.com>2023-10-14 15:05:55 +0800
commitc06f7ee0a40255214c477e13d9b956a7e07c2900 (patch)
treef5982a568ef8fab865d0142e71c7494fea3e4c1b /node_modules/svgo/plugins
parent3fd9400d4dccc25aa29b5702216732e0b7542073 (diff)
postcss,(plugins): nanocss, postcss-preset-env
Diffstat (limited to 'node_modules/svgo/plugins')
-rw-r--r--node_modules/svgo/plugins/_collections.js2168
-rw-r--r--node_modules/svgo/plugins/_path.js816
-rw-r--r--node_modules/svgo/plugins/_transforms.js379
-rw-r--r--node_modules/svgo/plugins/addAttributesToSVGElement.js82
-rw-r--r--node_modules/svgo/plugins/addClassesToSVGElement.js82
-rw-r--r--node_modules/svgo/plugins/applyTransforms.js379
-rw-r--r--node_modules/svgo/plugins/cleanupAttrs.js49
-rw-r--r--node_modules/svgo/plugins/cleanupEnableBackground.js73
-rw-r--r--node_modules/svgo/plugins/cleanupIds.js287
-rw-r--r--node_modules/svgo/plugins/cleanupListOfValues.js147
-rw-r--r--node_modules/svgo/plugins/cleanupNumericValues.js106
-rw-r--r--node_modules/svgo/plugins/collapseGroups.js134
-rw-r--r--node_modules/svgo/plugins/convertColors.js144
-rw-r--r--node_modules/svgo/plugins/convertEllipseToCircle.js37
-rw-r--r--node_modules/svgo/plugins/convertPathData.js1175
-rw-r--r--node_modules/svgo/plugins/convertShapeToPath.js170
-rw-r--r--node_modules/svgo/plugins/convertStyleToAttrs.js137
-rw-r--r--node_modules/svgo/plugins/convertTransform.js417
-rw-r--r--node_modules/svgo/plugins/inlineStyles.js386
-rw-r--r--node_modules/svgo/plugins/mergePaths.js98
-rw-r--r--node_modules/svgo/plugins/mergeStyles.js98
-rw-r--r--node_modules/svgo/plugins/minifyStyles.js139
-rw-r--r--node_modules/svgo/plugins/moveElemsAttrsToGroup.js128
-rw-r--r--node_modules/svgo/plugins/moveGroupAttrsToElems.js64
-rw-r--r--node_modules/svgo/plugins/plugins-types.ts251
-rw-r--r--node_modules/svgo/plugins/prefixIds.js229
-rw-r--r--node_modules/svgo/plugins/preset-default.js82
-rw-r--r--node_modules/svgo/plugins/removeAttributesBySelector.js97
-rw-r--r--node_modules/svgo/plugins/removeAttrs.js153
-rw-r--r--node_modules/svgo/plugins/removeComments.js29
-rw-r--r--node_modules/svgo/plugins/removeDesc.js39
-rw-r--r--node_modules/svgo/plugins/removeDimensions.js43
-rw-r--r--node_modules/svgo/plugins/removeDoctype.js40
-rw-r--r--node_modules/svgo/plugins/removeEditorsNSData.js64
-rw-r--r--node_modules/svgo/plugins/removeElementsByAttr.js73
-rw-r--r--node_modules/svgo/plugins/removeEmptyAttrs.js31
-rw-r--r--node_modules/svgo/plugins/removeEmptyContainers.js56
-rw-r--r--node_modules/svgo/plugins/removeEmptyText.js51
-rw-r--r--node_modules/svgo/plugins/removeHiddenElems.js310
-rw-r--r--node_modules/svgo/plugins/removeMetadata.js27
-rw-r--r--node_modules/svgo/plugins/removeNonInheritableGroupAttrs.js38
-rw-r--r--node_modules/svgo/plugins/removeOffCanvasPaths.js136
-rw-r--r--node_modules/svgo/plugins/removeRasterImages.js31
-rw-r--r--node_modules/svgo/plugins/removeScriptElement.js27
-rw-r--r--node_modules/svgo/plugins/removeStyleElement.js27
-rw-r--r--node_modules/svgo/plugins/removeTitle.js27
-rw-r--r--node_modules/svgo/plugins/removeUnknownsAndDefaults.js208
-rw-r--r--node_modules/svgo/plugins/removeUnusedNS.js59
-rw-r--r--node_modules/svgo/plugins/removeUselessDefs.js65
-rw-r--r--node_modules/svgo/plugins/removeUselessStrokeAndFill.js138
-rw-r--r--node_modules/svgo/plugins/removeViewBox.js49
-rw-r--r--node_modules/svgo/plugins/removeXMLNS.js30
-rw-r--r--node_modules/svgo/plugins/removeXMLProcInst.js28
-rw-r--r--node_modules/svgo/plugins/reusePaths.js111
-rw-r--r--node_modules/svgo/plugins/sortAttrs.js108
-rw-r--r--node_modules/svgo/plugins/sortDefsChildren.js58
56 files changed, 10410 insertions, 0 deletions
diff --git a/node_modules/svgo/plugins/_collections.js b/node_modules/svgo/plugins/_collections.js
new file mode 100644
index 00000000..dfa8bff6
--- /dev/null
+++ b/node_modules/svgo/plugins/_collections.js
@@ -0,0 +1,2168 @@
+'use strict';
+
+// https://www.w3.org/TR/SVG11/intro.html#Definitions
+
+/**
+ * @type {Record<string, Array<string>>}
+ */
+exports.elemsGroups = {
+ animation: [
+ 'animate',
+ 'animateColor',
+ 'animateMotion',
+ 'animateTransform',
+ 'set',
+ ],
+ descriptive: ['desc', 'metadata', 'title'],
+ shape: ['circle', 'ellipse', 'line', 'path', 'polygon', 'polyline', 'rect'],
+ structural: ['defs', 'g', 'svg', 'symbol', 'use'],
+ paintServer: [
+ 'solidColor',
+ 'linearGradient',
+ 'radialGradient',
+ 'meshGradient',
+ 'pattern',
+ 'hatch',
+ ],
+ nonRendering: [
+ 'linearGradient',
+ 'radialGradient',
+ 'pattern',
+ 'clipPath',
+ 'mask',
+ 'marker',
+ 'symbol',
+ 'filter',
+ 'solidColor',
+ ],
+ container: [
+ 'a',
+ 'defs',
+ 'g',
+ 'marker',
+ 'mask',
+ 'missing-glyph',
+ 'pattern',
+ 'svg',
+ 'switch',
+ 'symbol',
+ 'foreignObject',
+ ],
+ textContent: [
+ 'altGlyph',
+ 'altGlyphDef',
+ 'altGlyphItem',
+ 'glyph',
+ 'glyphRef',
+ 'textPath',
+ 'text',
+ 'tref',
+ 'tspan',
+ ],
+ textContentChild: ['altGlyph', 'textPath', 'tref', 'tspan'],
+ lightSource: [
+ 'feDiffuseLighting',
+ 'feSpecularLighting',
+ 'feDistantLight',
+ 'fePointLight',
+ 'feSpotLight',
+ ],
+ filterPrimitive: [
+ 'feBlend',
+ 'feColorMatrix',
+ 'feComponentTransfer',
+ 'feComposite',
+ 'feConvolveMatrix',
+ 'feDiffuseLighting',
+ 'feDisplacementMap',
+ 'feDropShadow',
+ 'feFlood',
+ 'feFuncA',
+ 'feFuncB',
+ 'feFuncG',
+ 'feFuncR',
+ 'feGaussianBlur',
+ 'feImage',
+ 'feMerge',
+ 'feMergeNode',
+ 'feMorphology',
+ 'feOffset',
+ 'feSpecularLighting',
+ 'feTile',
+ 'feTurbulence',
+ ],
+};
+
+exports.textElems = exports.elemsGroups.textContent.concat('title');
+
+exports.pathElems = ['path', 'glyph', 'missing-glyph'];
+
+// https://www.w3.org/TR/SVG11/intro.html#Definitions
+/**
+ * @type {Record<string, Array<string>>}
+ */
+exports.attrsGroups = {
+ animationAddition: ['additive', 'accumulate'],
+ animationAttributeTarget: ['attributeType', 'attributeName'],
+ animationEvent: ['onbegin', 'onend', 'onrepeat', 'onload'],
+ animationTiming: [
+ 'begin',
+ 'dur',
+ 'end',
+ 'min',
+ 'max',
+ 'restart',
+ 'repeatCount',
+ 'repeatDur',
+ 'fill',
+ ],
+ animationValue: [
+ 'calcMode',
+ 'values',
+ 'keyTimes',
+ 'keySplines',
+ 'from',
+ 'to',
+ 'by',
+ ],
+ conditionalProcessing: [
+ 'requiredFeatures',
+ 'requiredExtensions',
+ 'systemLanguage',
+ ],
+ core: ['id', 'tabindex', 'xml:base', 'xml:lang', 'xml:space'],
+ graphicalEvent: [
+ 'onfocusin',
+ 'onfocusout',
+ 'onactivate',
+ 'onclick',
+ 'onmousedown',
+ 'onmouseup',
+ 'onmouseover',
+ 'onmousemove',
+ 'onmouseout',
+ 'onload',
+ ],
+ presentation: [
+ 'alignment-baseline',
+ 'baseline-shift',
+ 'clip',
+ 'clip-path',
+ 'clip-rule',
+ 'color',
+ 'color-interpolation',
+ 'color-interpolation-filters',
+ 'color-profile',
+ 'color-rendering',
+ 'cursor',
+ 'direction',
+ 'display',
+ 'dominant-baseline',
+ 'enable-background',
+ 'fill',
+ 'fill-opacity',
+ 'fill-rule',
+ 'filter',
+ 'flood-color',
+ 'flood-opacity',
+ 'font-family',
+ 'font-size',
+ 'font-size-adjust',
+ 'font-stretch',
+ 'font-style',
+ 'font-variant',
+ 'font-weight',
+ 'glyph-orientation-horizontal',
+ 'glyph-orientation-vertical',
+ 'image-rendering',
+ 'letter-spacing',
+ 'lighting-color',
+ 'marker-end',
+ 'marker-mid',
+ 'marker-start',
+ 'mask',
+ 'opacity',
+ 'overflow',
+ 'paint-order',
+ 'pointer-events',
+ 'shape-rendering',
+ 'stop-color',
+ 'stop-opacity',
+ 'stroke',
+ 'stroke-dasharray',
+ 'stroke-dashoffset',
+ 'stroke-linecap',
+ 'stroke-linejoin',
+ 'stroke-miterlimit',
+ 'stroke-opacity',
+ 'stroke-width',
+ 'text-anchor',
+ 'text-decoration',
+ 'text-overflow',
+ 'text-rendering',
+ 'transform',
+ 'transform-origin',
+ 'unicode-bidi',
+ 'vector-effect',
+ 'visibility',
+ 'word-spacing',
+ 'writing-mode',
+ ],
+ xlink: [
+ 'xlink:href',
+ 'xlink:show',
+ 'xlink:actuate',
+ 'xlink:type',
+ 'xlink:role',
+ 'xlink:arcrole',
+ 'xlink:title',
+ ],
+ documentEvent: [
+ 'onunload',
+ 'onabort',
+ 'onerror',
+ 'onresize',
+ 'onscroll',
+ 'onzoom',
+ ],
+ filterPrimitive: ['x', 'y', 'width', 'height', 'result'],
+ transferFunction: [
+ 'type',
+ 'tableValues',
+ 'slope',
+ 'intercept',
+ 'amplitude',
+ 'exponent',
+ 'offset',
+ ],
+};
+
+/**
+ * @type {Record<string, Record<string, string>>}
+ */
+exports.attrsGroupsDefaults = {
+ core: { 'xml:space': 'default' },
+ presentation: {
+ clip: 'auto',
+ 'clip-path': 'none',
+ 'clip-rule': 'nonzero',
+ mask: 'none',
+ opacity: '1',
+ 'stop-color': '#000',
+ 'stop-opacity': '1',
+ 'fill-opacity': '1',
+ 'fill-rule': 'nonzero',
+ fill: '#000',
+ stroke: 'none',
+ 'stroke-width': '1',
+ 'stroke-linecap': 'butt',
+ 'stroke-linejoin': 'miter',
+ 'stroke-miterlimit': '4',
+ 'stroke-dasharray': 'none',
+ 'stroke-dashoffset': '0',
+ 'stroke-opacity': '1',
+ 'paint-order': 'normal',
+ 'vector-effect': 'none',
+ display: 'inline',
+ visibility: 'visible',
+ 'marker-start': 'none',
+ 'marker-mid': 'none',
+ 'marker-end': 'none',
+ 'color-interpolation': 'sRGB',
+ 'color-interpolation-filters': 'linearRGB',
+ 'color-rendering': 'auto',
+ 'shape-rendering': 'auto',
+ 'text-rendering': 'auto',
+ 'image-rendering': 'auto',
+ 'font-style': 'normal',
+ 'font-variant': 'normal',
+ 'font-weight': 'normal',
+ 'font-stretch': 'normal',
+ 'font-size': 'medium',
+ 'font-size-adjust': 'none',
+ kerning: 'auto',
+ 'letter-spacing': 'normal',
+ 'word-spacing': 'normal',
+ 'text-decoration': 'none',
+ 'text-anchor': 'start',
+ 'text-overflow': 'clip',
+ 'writing-mode': 'lr-tb',
+ 'glyph-orientation-vertical': 'auto',
+ 'glyph-orientation-horizontal': '0deg',
+ direction: 'ltr',
+ 'unicode-bidi': 'normal',
+ 'dominant-baseline': 'auto',
+ 'alignment-baseline': 'baseline',
+ 'baseline-shift': 'baseline',
+ },
+ transferFunction: {
+ slope: '1',
+ intercept: '0',
+ amplitude: '1',
+ exponent: '1',
+ offset: '0',
+ },
+};
+
+// https://www.w3.org/TR/SVG11/eltindex.html
+/**
+ * @type {Record<string, {
+ * attrsGroups: Array<string>,
+ * attrs?: Array<string>,
+ * defaults?: Record<string, string>,
+ * contentGroups?: Array<string>,
+ * content?: Array<string>,
+ * }>}
+ */
+exports.elems = {
+ a: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation',
+ 'xlink',
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'target',
+ ],
+ defaults: {
+ target: '_self',
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape',
+ 'structural',
+ 'paintServer',
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view',
+ // not spec compliant
+ 'tspan',
+ ],
+ },
+ altGlyph: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation',
+ 'xlink',
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'x',
+ 'y',
+ 'dx',
+ 'dy',
+ 'glyphRef',
+ 'format',
+ 'rotate',
+ ],
+ },
+ altGlyphDef: {
+ attrsGroups: ['core'],
+ content: ['glyphRef'],
+ },
+ altGlyphItem: {
+ attrsGroups: ['core'],
+ content: ['glyphRef', 'altGlyphItem'],
+ },
+ animate: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'animationAddition',
+ 'animationAttributeTarget',
+ 'animationEvent',
+ 'animationTiming',
+ 'animationValue',
+ 'presentation',
+ 'xlink',
+ ],
+ attrs: ['externalResourcesRequired'],
+ contentGroups: ['descriptive'],
+ },
+ animateColor: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'animationEvent',
+ 'xlink',
+ 'animationAttributeTarget',
+ 'animationTiming',
+ 'animationValue',
+ 'animationAddition',
+ 'presentation',
+ ],
+ attrs: ['externalResourcesRequired'],
+ contentGroups: ['descriptive'],
+ },
+ animateMotion: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'animationEvent',
+ 'xlink',
+ 'animationTiming',
+ 'animationValue',
+ 'animationAddition',
+ ],
+ attrs: [
+ 'externalResourcesRequired',
+ 'path',
+ 'keyPoints',
+ 'rotate',
+ 'origin',
+ ],
+ defaults: {
+ rotate: '0',
+ },
+ contentGroups: ['descriptive'],
+ content: ['mpath'],
+ },
+ animateTransform: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'animationEvent',
+ 'xlink',
+ 'animationAttributeTarget',
+ 'animationTiming',
+ 'animationValue',
+ 'animationAddition',
+ ],
+ attrs: ['externalResourcesRequired', 'type'],
+ contentGroups: ['descriptive'],
+ },
+ circle: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation',
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'cx',
+ 'cy',
+ 'r',
+ ],
+ defaults: {
+ cx: '0',
+ cy: '0',
+ },
+ contentGroups: ['animation', 'descriptive'],
+ },
+ clipPath: {
+ attrsGroups: ['conditionalProcessing', 'core', 'presentation'],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'clipPathUnits',
+ ],
+ defaults: {
+ clipPathUnits: 'userSpaceOnUse',
+ },
+ contentGroups: ['animation', 'descriptive', 'shape'],
+ content: ['text', 'use'],
+ },
+ 'color-profile': {
+ attrsGroups: ['core', 'xlink'],
+ attrs: ['local', 'name', 'rendering-intent'],
+ defaults: {
+ name: 'sRGB',
+ 'rendering-intent': 'auto',
+ },
+ contentGroups: ['descriptive'],
+ },
+ cursor: {
+ attrsGroups: ['core', 'conditionalProcessing', 'xlink'],
+ attrs: ['externalResourcesRequired', 'x', 'y'],
+ defaults: {
+ x: '0',
+ y: '0',
+ },
+ contentGroups: ['descriptive'],
+ },
+ defs: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation',
+ ],
+ attrs: ['class', 'style', 'externalResourcesRequired', 'transform'],
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape',
+ 'structural',
+ 'paintServer',
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view',
+ ],
+ },
+ desc: {
+ attrsGroups: ['core'],
+ attrs: ['class', 'style'],
+ },
+ ellipse: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation',
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'cx',
+ 'cy',
+ 'rx',
+ 'ry',
+ ],
+ defaults: {
+ cx: '0',
+ cy: '0',
+ },
+ contentGroups: ['animation', 'descriptive'],
+ },
+ feBlend: {
+ attrsGroups: ['core', 'presentation', 'filterPrimitive'],
+ attrs: [
+ 'class',
+ 'style',
+ // TODO: in - 'If no value is provided and this is the first filter primitive,
+ // then this filter primitive will use SourceGraphic as its input'
+ 'in',
+ 'in2',
+ 'mode',
+ ],
+ defaults: {
+ mode: 'normal',
+ },
+ content: ['animate', 'set'],
+ },
+ feColorMatrix: {
+ attrsGroups: ['core', 'presentation', 'filterPrimitive'],
+ attrs: ['class', 'style', 'in', 'type', 'values'],
+ defaults: {
+ type: 'matrix',
+ },
+ content: ['animate', 'set'],
+ },
+ feComponentTransfer: {
+ attrsGroups: ['core', 'presentation', 'filterPrimitive'],
+ attrs: ['class', 'style', 'in'],
+ content: ['feFuncA', 'feFuncB', 'feFuncG', 'feFuncR'],
+ },
+ feComposite: {
+ attrsGroups: ['core', 'presentation', 'filterPrimitive'],
+ attrs: ['class', 'style', 'in', 'in2', 'operator', 'k1', 'k2', 'k3', 'k4'],
+ defaults: {
+ operator: 'over',
+ k1: '0',
+ k2: '0',
+ k3: '0',
+ k4: '0',
+ },
+ content: ['animate', 'set'],
+ },
+ feConvolveMatrix: {
+ attrsGroups: ['core', 'presentation', 'filterPrimitive'],
+ attrs: [
+ 'class',
+ 'style',
+ 'in',
+ 'order',
+ 'kernelMatrix',
+ // TODO: divisor - 'The default value is the sum of all values in kernelMatrix,
+ // with the exception that if the sum is zero, then the divisor is set to 1'
+ 'divisor',
+ 'bias',
+ // TODO: targetX - 'By default, the convolution matrix is centered in X over each
+ // pixel of the input image (i.e., targetX = floor ( orderX / 2 ))'
+ 'targetX',
+ 'targetY',
+ 'edgeMode',
+ // TODO: kernelUnitLength - 'The first number is the <dx> value. The second number
+ // is the <dy> value. If the <dy> value is not specified, it defaults to the same value as <dx>'
+ 'kernelUnitLength',
+ 'preserveAlpha',
+ ],
+ defaults: {
+ order: '3',
+ bias: '0',
+ edgeMode: 'duplicate',
+ preserveAlpha: 'false',
+ },
+ content: ['animate', 'set'],
+ },
+ feDiffuseLighting: {
+ attrsGroups: ['core', 'presentation', 'filterPrimitive'],
+ attrs: [
+ 'class',
+ 'style',
+ 'in',
+ 'surfaceScale',
+ 'diffuseConstant',
+ 'kernelUnitLength',
+ ],
+ defaults: {
+ surfaceScale: '1',
+ diffuseConstant: '1',
+ },
+ contentGroups: ['descriptive'],
+ content: [
+ // TODO: 'exactly one light source element, in any order'
+ 'feDistantLight',
+ 'fePointLight',
+ 'feSpotLight',
+ ],
+ },
+ feDisplacementMap: {
+ attrsGroups: ['core', 'presentation', 'filterPrimitive'],
+ attrs: [
+ 'class',
+ 'style',
+ 'in',
+ 'in2',
+ 'scale',
+ 'xChannelSelector',
+ 'yChannelSelector',
+ ],
+ defaults: {
+ scale: '0',
+ xChannelSelector: 'A',
+ yChannelSelector: 'A',
+ },
+ content: ['animate', 'set'],
+ },
+ feDistantLight: {
+ attrsGroups: ['core'],
+ attrs: ['azimuth', 'elevation'],
+ defaults: {
+ azimuth: '0',
+ elevation: '0',
+ },
+ content: ['animate', 'set'],
+ },
+ feFlood: {
+ attrsGroups: ['core', 'presentation', 'filterPrimitive'],
+ attrs: ['class', 'style'],
+ content: ['animate', 'animateColor', 'set'],
+ },
+ feFuncA: {
+ attrsGroups: ['core', 'transferFunction'],
+ content: ['set', 'animate'],
+ },
+ feFuncB: {
+ attrsGroups: ['core', 'transferFunction'],
+ content: ['set', 'animate'],
+ },
+ feFuncG: {
+ attrsGroups: ['core', 'transferFunction'],
+ content: ['set', 'animate'],
+ },
+ feFuncR: {
+ attrsGroups: ['core', 'transferFunction'],
+ content: ['set', 'animate'],
+ },
+ feGaussianBlur: {
+ attrsGroups: ['core', 'presentation', 'filterPrimitive'],
+ attrs: ['class', 'style', 'in', 'stdDeviation'],
+ defaults: {
+ stdDeviation: '0',
+ },
+ content: ['set', 'animate'],
+ },
+ feImage: {
+ attrsGroups: ['core', 'presentation', 'filterPrimitive', 'xlink'],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'preserveAspectRatio',
+ 'href',
+ 'xlink:href',
+ ],
+ defaults: {
+ preserveAspectRatio: 'xMidYMid meet',
+ },
+ content: ['animate', 'animateTransform', 'set'],
+ },
+ feMerge: {
+ attrsGroups: ['core', 'presentation', 'filterPrimitive'],
+ attrs: ['class', 'style'],
+ content: ['feMergeNode'],
+ },
+ feMergeNode: {
+ attrsGroups: ['core'],
+ attrs: ['in'],
+ content: ['animate', 'set'],
+ },
+ feMorphology: {
+ attrsGroups: ['core', 'presentation', 'filterPrimitive'],
+ attrs: ['class', 'style', 'in', 'operator', 'radius'],
+ defaults: {
+ operator: 'erode',
+ radius: '0',
+ },
+ content: ['animate', 'set'],
+ },
+ feOffset: {
+ attrsGroups: ['core', 'presentation', 'filterPrimitive'],
+ attrs: ['class', 'style', 'in', 'dx', 'dy'],
+ defaults: {
+ dx: '0',
+ dy: '0',
+ },
+ content: ['animate', 'set'],
+ },
+ fePointLight: {
+ attrsGroups: ['core'],
+ attrs: ['x', 'y', 'z'],
+ defaults: {
+ x: '0',
+ y: '0',
+ z: '0',
+ },
+ content: ['animate', 'set'],
+ },
+ feSpecularLighting: {
+ attrsGroups: ['core', 'presentation', 'filterPrimitive'],
+ attrs: [
+ 'class',
+ 'style',
+ 'in',
+ 'surfaceScale',
+ 'specularConstant',
+ 'specularExponent',
+ 'kernelUnitLength',
+ ],
+ defaults: {
+ surfaceScale: '1',
+ specularConstant: '1',
+ specularExponent: '1',
+ },
+ contentGroups: [
+ 'descriptive',
+ // TODO: exactly one 'light source element'
+ 'lightSource',
+ ],
+ },
+ feSpotLight: {
+ attrsGroups: ['core'],
+ attrs: [
+ 'x',
+ 'y',
+ 'z',
+ 'pointsAtX',
+ 'pointsAtY',
+ 'pointsAtZ',
+ 'specularExponent',
+ 'limitingConeAngle',
+ ],
+ defaults: {
+ x: '0',
+ y: '0',
+ z: '0',
+ pointsAtX: '0',
+ pointsAtY: '0',
+ pointsAtZ: '0',
+ specularExponent: '1',
+ },
+ content: ['animate', 'set'],
+ },
+ feTile: {
+ attrsGroups: ['core', 'presentation', 'filterPrimitive'],
+ attrs: ['class', 'style', 'in'],
+ content: ['animate', 'set'],
+ },
+ feTurbulence: {
+ attrsGroups: ['core', 'presentation', 'filterPrimitive'],
+ attrs: [
+ 'class',
+ 'style',
+ 'baseFrequency',
+ 'numOctaves',
+ 'seed',
+ 'stitchTiles',
+ 'type',
+ ],
+ defaults: {
+ baseFrequency: '0',
+ numOctaves: '1',
+ seed: '0',
+ stitchTiles: 'noStitch',
+ type: 'turbulence',
+ },
+ content: ['animate', 'set'],
+ },
+ filter: {
+ attrsGroups: ['core', 'presentation', 'xlink'],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'x',
+ 'y',
+ 'width',
+ 'height',
+ 'filterRes',
+ 'filterUnits',
+ 'primitiveUnits',
+ 'href',
+ 'xlink:href',
+ ],
+ defaults: {
+ primitiveUnits: 'userSpaceOnUse',
+ x: '-10%',
+ y: '-10%',
+ width: '120%',
+ height: '120%',
+ },
+ contentGroups: ['descriptive', 'filterPrimitive'],
+ content: ['animate', 'set'],
+ },
+ font: {
+ attrsGroups: ['core', 'presentation'],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'horiz-origin-x',
+ 'horiz-origin-y',
+ 'horiz-adv-x',
+ 'vert-origin-x',
+ 'vert-origin-y',
+ 'vert-adv-y',
+ ],
+ defaults: {
+ 'horiz-origin-x': '0',
+ 'horiz-origin-y': '0',
+ },
+ contentGroups: ['descriptive'],
+ content: ['font-face', 'glyph', 'hkern', 'missing-glyph', 'vkern'],
+ },
+ 'font-face': {
+ attrsGroups: ['core'],
+ attrs: [
+ 'font-family',
+ 'font-style',
+ 'font-variant',
+ 'font-weight',
+ 'font-stretch',
+ 'font-size',
+ 'unicode-range',
+ 'units-per-em',
+ 'panose-1',
+ 'stemv',
+ 'stemh',
+ 'slope',
+ 'cap-height',
+ 'x-height',
+ 'accent-height',
+ 'ascent',
+ 'descent',
+ 'widths',
+ 'bbox',
+ 'ideographic',
+ 'alphabetic',
+ 'mathematical',
+ 'hanging',
+ 'v-ideographic',
+ 'v-alphabetic',
+ 'v-mathematical',
+ 'v-hanging',
+ 'underline-position',
+ 'underline-thickness',
+ 'strikethrough-position',
+ 'strikethrough-thickness',
+ 'overline-position',
+ 'overline-thickness',
+ ],
+ defaults: {
+ 'font-style': 'all',
+ 'font-variant': 'normal',
+ 'font-weight': 'all',
+ 'font-stretch': 'normal',
+ 'unicode-range': 'U+0-10FFFF',
+ 'units-per-em': '1000',
+ 'panose-1': '0 0 0 0 0 0 0 0 0 0',
+ slope: '0',
+ },
+ contentGroups: ['descriptive'],
+ content: [
+ // TODO: "at most one 'font-face-src' element"
+ 'font-face-src',
+ ],
+ },
+ // TODO: empty content
+ 'font-face-format': {
+ attrsGroups: ['core'],
+ attrs: ['string'],
+ },
+ 'font-face-name': {
+ attrsGroups: ['core'],
+ attrs: ['name'],
+ },
+ 'font-face-src': {
+ attrsGroups: ['core'],
+ content: ['font-face-name', 'font-face-uri'],
+ },
+ 'font-face-uri': {
+ attrsGroups: ['core', 'xlink'],
+ attrs: ['href', 'xlink:href'],
+ content: ['font-face-format'],
+ },
+ foreignObject: {
+ attrsGroups: [
+ 'core',
+ 'conditionalProcessing',
+ 'graphicalEvent',
+ 'presentation',
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'x',
+ 'y',
+ 'width',
+ 'height',
+ ],
+ defaults: {
+ x: '0',
+ y: '0',
+ },
+ },
+ g: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation',
+ ],
+ attrs: ['class', 'style', 'externalResourcesRequired', 'transform'],
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape',
+ 'structural',
+ 'paintServer',
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view',
+ ],
+ },
+ glyph: {
+ attrsGroups: ['core', 'presentation'],
+ attrs: [
+ 'class',
+ 'style',
+ 'd',
+ 'horiz-adv-x',
+ 'vert-origin-x',
+ 'vert-origin-y',
+ 'vert-adv-y',
+ 'unicode',
+ 'glyph-name',
+ 'orientation',
+ 'arabic-form',
+ 'lang',
+ ],
+ defaults: {
+ 'arabic-form': 'initial',
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape',
+ 'structural',
+ 'paintServer',
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view',
+ ],
+ },
+ glyphRef: {
+ attrsGroups: ['core', 'presentation'],
+ attrs: [
+ 'class',
+ 'style',
+ 'd',
+ 'horiz-adv-x',
+ 'vert-origin-x',
+ 'vert-origin-y',
+ 'vert-adv-y',
+ ],
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape',
+ 'structural',
+ 'paintServer',
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view',
+ ],
+ },
+ hatch: {
+ attrsGroups: ['core', 'presentation', 'xlink'],
+ attrs: [
+ 'class',
+ 'style',
+ 'x',
+ 'y',
+ 'pitch',
+ 'rotate',
+ 'hatchUnits',
+ 'hatchContentUnits',
+ 'transform',
+ ],
+ defaults: {
+ hatchUnits: 'objectBoundingBox',
+ hatchContentUnits: 'userSpaceOnUse',
+ x: '0',
+ y: '0',
+ pitch: '0',
+ rotate: '0',
+ },
+ contentGroups: ['animation', 'descriptive'],
+ content: ['hatchPath'],
+ },
+ hatchPath: {
+ attrsGroups: ['core', 'presentation', 'xlink'],
+ attrs: ['class', 'style', 'd', 'offset'],
+ defaults: {
+ offset: '0',
+ },
+ contentGroups: ['animation', 'descriptive'],
+ },
+ hkern: {
+ attrsGroups: ['core'],
+ attrs: ['u1', 'g1', 'u2', 'g2', 'k'],
+ },
+ image: {
+ attrsGroups: [
+ 'core',
+ 'conditionalProcessing',
+ 'graphicalEvent',
+ 'xlink',
+ 'presentation',
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'preserveAspectRatio',
+ 'transform',
+ 'x',
+ 'y',
+ 'width',
+ 'height',
+ 'href',
+ 'xlink:href',
+ ],
+ defaults: {
+ x: '0',
+ y: '0',
+ preserveAspectRatio: 'xMidYMid meet',
+ },
+ contentGroups: ['animation', 'descriptive'],
+ },
+ line: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation',
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'x1',
+ 'y1',
+ 'x2',
+ 'y2',
+ ],
+ defaults: {
+ x1: '0',
+ y1: '0',
+ x2: '0',
+ y2: '0',
+ },
+ contentGroups: ['animation', 'descriptive'],
+ },
+ linearGradient: {
+ attrsGroups: ['core', 'presentation', 'xlink'],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'x1',
+ 'y1',
+ 'x2',
+ 'y2',
+ 'gradientUnits',
+ 'gradientTransform',
+ 'spreadMethod',
+ 'href',
+ 'xlink:href',
+ ],
+ defaults: {
+ x1: '0',
+ y1: '0',
+ x2: '100%',
+ y2: '0',
+ spreadMethod: 'pad',
+ },
+ contentGroups: ['descriptive'],
+ content: ['animate', 'animateTransform', 'set', 'stop'],
+ },
+ marker: {
+ attrsGroups: ['core', 'presentation'],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'viewBox',
+ 'preserveAspectRatio',
+ 'refX',
+ 'refY',
+ 'markerUnits',
+ 'markerWidth',
+ 'markerHeight',
+ 'orient',
+ ],
+ defaults: {
+ markerUnits: 'strokeWidth',
+ refX: '0',
+ refY: '0',
+ markerWidth: '3',
+ markerHeight: '3',
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape',
+ 'structural',
+ 'paintServer',
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view',
+ ],
+ },
+ mask: {
+ attrsGroups: ['conditionalProcessing', 'core', 'presentation'],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'x',
+ 'y',
+ 'width',
+ 'height',
+ 'mask-type',
+ 'maskUnits',
+ 'maskContentUnits',
+ ],
+ defaults: {
+ maskUnits: 'objectBoundingBox',
+ maskContentUnits: 'userSpaceOnUse',
+ x: '-10%',
+ y: '-10%',
+ width: '120%',
+ height: '120%',
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape',
+ 'structural',
+ 'paintServer',
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view',
+ ],
+ },
+ metadata: {
+ attrsGroups: ['core'],
+ },
+ 'missing-glyph': {
+ attrsGroups: ['core', 'presentation'],
+ attrs: [
+ 'class',
+ 'style',
+ 'd',
+ 'horiz-adv-x',
+ 'vert-origin-x',
+ 'vert-origin-y',
+ 'vert-adv-y',
+ ],
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape',
+ 'structural',
+ 'paintServer',
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view',
+ ],
+ },
+ mpath: {
+ attrsGroups: ['core', 'xlink'],
+ attrs: ['externalResourcesRequired', 'href', 'xlink:href'],
+ contentGroups: ['descriptive'],
+ },
+ path: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation',
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'd',
+ 'pathLength',
+ ],
+ contentGroups: ['animation', 'descriptive'],
+ },
+ pattern: {
+ attrsGroups: ['conditionalProcessing', 'core', 'presentation', 'xlink'],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'viewBox',
+ 'preserveAspectRatio',
+ 'x',
+ 'y',
+ 'width',
+ 'height',
+ 'patternUnits',
+ 'patternContentUnits',
+ 'patternTransform',
+ 'href',
+ 'xlink:href',
+ ],
+ defaults: {
+ patternUnits: 'objectBoundingBox',
+ patternContentUnits: 'userSpaceOnUse',
+ x: '0',
+ y: '0',
+ width: '0',
+ height: '0',
+ preserveAspectRatio: 'xMidYMid meet',
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'paintServer',
+ 'shape',
+ 'structural',
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view',
+ ],
+ },
+ polygon: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation',
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'points',
+ ],
+ contentGroups: ['animation', 'descriptive'],
+ },
+ polyline: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation',
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'points',
+ ],
+ contentGroups: ['animation', 'descriptive'],
+ },
+ radialGradient: {
+ attrsGroups: ['core', 'presentation', 'xlink'],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'cx',
+ 'cy',
+ 'r',
+ 'fx',
+ 'fy',
+ 'fr',
+ 'gradientUnits',
+ 'gradientTransform',
+ 'spreadMethod',
+ 'href',
+ 'xlink:href',
+ ],
+ defaults: {
+ gradientUnits: 'objectBoundingBox',
+ cx: '50%',
+ cy: '50%',
+ r: '50%',
+ },
+ contentGroups: ['descriptive'],
+ content: ['animate', 'animateTransform', 'set', 'stop'],
+ },
+ meshGradient: {
+ attrsGroups: ['core', 'presentation', 'xlink'],
+ attrs: ['class', 'style', 'x', 'y', 'gradientUnits', 'transform'],
+ contentGroups: ['descriptive', 'paintServer', 'animation'],
+ content: ['meshRow'],
+ },
+ meshRow: {
+ attrsGroups: ['core', 'presentation'],
+ attrs: ['class', 'style'],
+ contentGroups: ['descriptive'],
+ content: ['meshPatch'],
+ },
+ meshPatch: {
+ attrsGroups: ['core', 'presentation'],
+ attrs: ['class', 'style'],
+ contentGroups: ['descriptive'],
+ content: ['stop'],
+ },
+ rect: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation',
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'x',
+ 'y',
+ 'width',
+ 'height',
+ 'rx',
+ 'ry',
+ ],
+ defaults: {
+ x: '0',
+ y: '0',
+ },
+ contentGroups: ['animation', 'descriptive'],
+ },
+ script: {
+ attrsGroups: ['core', 'xlink'],
+ attrs: ['externalResourcesRequired', 'type', 'href', 'xlink:href'],
+ },
+ set: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'animation',
+ 'xlink',
+ 'animationAttributeTarget',
+ 'animationTiming',
+ ],
+ attrs: ['externalResourcesRequired', 'to'],
+ contentGroups: ['descriptive'],
+ },
+ solidColor: {
+ attrsGroups: ['core', 'presentation'],
+ attrs: ['class', 'style'],
+ contentGroups: ['paintServer'],
+ },
+ stop: {
+ attrsGroups: ['core', 'presentation'],
+ attrs: ['class', 'style', 'offset', 'path'],
+ content: ['animate', 'animateColor', 'set'],
+ },
+ style: {
+ attrsGroups: ['core'],
+ attrs: ['type', 'media', 'title'],
+ defaults: {
+ type: 'text/css',
+ },
+ },
+ svg: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'documentEvent',
+ 'graphicalEvent',
+ 'presentation',
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'x',
+ 'y',
+ 'width',
+ 'height',
+ 'viewBox',
+ 'preserveAspectRatio',
+ 'zoomAndPan',
+ 'version',
+ 'baseProfile',
+ 'contentScriptType',
+ 'contentStyleType',
+ ],
+ defaults: {
+ x: '0',
+ y: '0',
+ width: '100%',
+ height: '100%',
+ preserveAspectRatio: 'xMidYMid meet',
+ zoomAndPan: 'magnify',
+ version: '1.1',
+ baseProfile: 'none',
+ contentScriptType: 'application/ecmascript',
+ contentStyleType: 'text/css',
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape',
+ 'structural',
+ 'paintServer',
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view',
+ ],
+ },
+ switch: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation',
+ ],
+ attrs: ['class', 'style', 'externalResourcesRequired', 'transform'],
+ contentGroups: ['animation', 'descriptive', 'shape'],
+ content: [
+ 'a',
+ 'foreignObject',
+ 'g',
+ 'image',
+ 'svg',
+ 'switch',
+ 'text',
+ 'use',
+ ],
+ },
+ symbol: {
+ attrsGroups: ['core', 'graphicalEvent', 'presentation'],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'preserveAspectRatio',
+ 'viewBox',
+ 'refX',
+ 'refY',
+ ],
+ defaults: {
+ refX: '0',
+ refY: '0',
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape',
+ 'structural',
+ 'paintServer',
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view',
+ ],
+ },
+ text: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation',
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'lengthAdjust',
+ 'x',
+ 'y',
+ 'dx',
+ 'dy',
+ 'rotate',
+ 'textLength',
+ ],
+ defaults: {
+ x: '0',
+ y: '0',
+ lengthAdjust: 'spacing',
+ },
+ contentGroups: ['animation', 'descriptive', 'textContentChild'],
+ content: ['a'],
+ },
+ textPath: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation',
+ 'xlink',
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'href',
+ 'xlink:href',
+ 'startOffset',
+ 'method',
+ 'spacing',
+ 'd',
+ ],
+ defaults: {
+ startOffset: '0',
+ method: 'align',
+ spacing: 'exact',
+ },
+ contentGroups: ['descriptive'],
+ content: [
+ 'a',
+ 'altGlyph',
+ 'animate',
+ 'animateColor',
+ 'set',
+ 'tref',
+ 'tspan',
+ ],
+ },
+ title: {
+ attrsGroups: ['core'],
+ attrs: ['class', 'style'],
+ },
+ tref: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation',
+ 'xlink',
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'href',
+ 'xlink:href',
+ ],
+ contentGroups: ['descriptive'],
+ content: ['animate', 'animateColor', 'set'],
+ },
+ tspan: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation',
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'x',
+ 'y',
+ 'dx',
+ 'dy',
+ 'rotate',
+ 'textLength',
+ 'lengthAdjust',
+ ],
+ contentGroups: ['descriptive'],
+ content: [
+ 'a',
+ 'altGlyph',
+ 'animate',
+ 'animateColor',
+ 'set',
+ 'tref',
+ 'tspan',
+ ],
+ },
+ use: {
+ attrsGroups: [
+ 'core',
+ 'conditionalProcessing',
+ 'graphicalEvent',
+ 'presentation',
+ 'xlink',
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'x',
+ 'y',
+ 'width',
+ 'height',
+ 'href',
+ 'xlink:href',
+ ],
+ defaults: {
+ x: '0',
+ y: '0',
+ },
+ contentGroups: ['animation', 'descriptive'],
+ },
+ view: {
+ attrsGroups: ['core'],
+ attrs: [
+ 'externalResourcesRequired',
+ 'viewBox',
+ 'preserveAspectRatio',
+ 'zoomAndPan',
+ 'viewTarget',
+ ],
+ contentGroups: ['descriptive'],
+ },
+ vkern: {
+ attrsGroups: ['core'],
+ attrs: ['u1', 'g1', 'u2', 'g2', 'k'],
+ },
+};
+
+// https://wiki.inkscape.org/wiki/index.php/Inkscape-specific_XML_attributes
+exports.editorNamespaces = [
+ 'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd',
+ 'http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd',
+ 'http://www.inkscape.org/namespaces/inkscape',
+ 'http://www.bohemiancoding.com/sketch/ns',
+ 'http://ns.adobe.com/AdobeIllustrator/10.0/',
+ 'http://ns.adobe.com/Graphs/1.0/',
+ 'http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/',
+ 'http://ns.adobe.com/Variables/1.0/',
+ 'http://ns.adobe.com/SaveForWeb/1.0/',
+ 'http://ns.adobe.com/Extensibility/1.0/',
+ 'http://ns.adobe.com/Flows/1.0/',
+ 'http://ns.adobe.com/ImageReplacement/1.0/',
+ 'http://ns.adobe.com/GenericCustomNamespace/1.0/',
+ 'http://ns.adobe.com/XPath/1.0/',
+ 'http://schemas.microsoft.com/visio/2003/SVGExtensions/',
+ 'http://taptrix.com/vectorillustrator/svg_extensions',
+ 'http://www.figma.com/figma/ns',
+ 'http://purl.org/dc/elements/1.1/',
+ 'http://creativecommons.org/ns#',
+ 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
+ 'http://www.serif.com/',
+ 'http://www.vector.evaxdesign.sk',
+];
+
+// https://www.w3.org/TR/SVG11/linking.html#processingIRI
+exports.referencesProps = [
+ 'clip-path',
+ 'color-profile',
+ 'fill',
+ 'filter',
+ 'marker-start',
+ 'marker-mid',
+ 'marker-end',
+ 'mask',
+ 'stroke',
+ 'style',
+];
+
+// https://www.w3.org/TR/SVG11/propidx.html
+exports.inheritableAttrs = [
+ 'clip-rule',
+ 'color',
+ 'color-interpolation',
+ 'color-interpolation-filters',
+ 'color-profile',
+ 'color-rendering',
+ 'cursor',
+ 'direction',
+ 'dominant-baseline',
+ 'fill',
+ 'fill-opacity',
+ 'fill-rule',
+ 'font',
+ 'font-family',
+ 'font-size',
+ 'font-size-adjust',
+ 'font-stretch',
+ 'font-style',
+ 'font-variant',
+ 'font-weight',
+ 'glyph-orientation-horizontal',
+ 'glyph-orientation-vertical',
+ 'image-rendering',
+ 'letter-spacing',
+ 'marker',
+ 'marker-end',
+ 'marker-mid',
+ 'marker-start',
+ 'paint-order',
+ 'pointer-events',
+ 'shape-rendering',
+ 'stroke',
+ 'stroke-dasharray',
+ 'stroke-dashoffset',
+ 'stroke-linecap',
+ 'stroke-linejoin',
+ 'stroke-miterlimit',
+ 'stroke-opacity',
+ 'stroke-width',
+ 'text-anchor',
+ 'text-rendering',
+ 'transform',
+ 'visibility',
+ 'word-spacing',
+ 'writing-mode',
+];
+
+exports.presentationNonInheritableGroupAttrs = [
+ 'display',
+ 'clip-path',
+ 'filter',
+ 'mask',
+ 'opacity',
+ 'text-decoration',
+ 'transform',
+ 'unicode-bidi',
+];
+
+/**
+ * https://www.w3.org/TR/SVG11/single-page.html#types-ColorKeywords
+ *
+ * @type {Record<string, string>}
+ */
+exports.colorsNames = {
+ aliceblue: '#f0f8ff',
+ antiquewhite: '#faebd7',
+ aqua: '#0ff',
+ aquamarine: '#7fffd4',
+ azure: '#f0ffff',
+ beige: '#f5f5dc',
+ bisque: '#ffe4c4',
+ black: '#000',
+ blanchedalmond: '#ffebcd',
+ blue: '#00f',
+ blueviolet: '#8a2be2',
+ brown: '#a52a2a',
+ burlywood: '#deb887',
+ cadetblue: '#5f9ea0',
+ chartreuse: '#7fff00',
+ chocolate: '#d2691e',
+ coral: '#ff7f50',
+ cornflowerblue: '#6495ed',
+ cornsilk: '#fff8dc',
+ crimson: '#dc143c',
+ cyan: '#0ff',
+ darkblue: '#00008b',
+ darkcyan: '#008b8b',
+ darkgoldenrod: '#b8860b',
+ darkgray: '#a9a9a9',
+ darkgreen: '#006400',
+ darkgrey: '#a9a9a9',
+ darkkhaki: '#bdb76b',
+ darkmagenta: '#8b008b',
+ darkolivegreen: '#556b2f',
+ darkorange: '#ff8c00',
+ darkorchid: '#9932cc',
+ darkred: '#8b0000',
+ darksalmon: '#e9967a',
+ darkseagreen: '#8fbc8f',
+ darkslateblue: '#483d8b',
+ darkslategray: '#2f4f4f',
+ darkslategrey: '#2f4f4f',
+ darkturquoise: '#00ced1',
+ darkviolet: '#9400d3',
+ deeppink: '#ff1493',
+ deepskyblue: '#00bfff',
+ dimgray: '#696969',
+ dimgrey: '#696969',
+ dodgerblue: '#1e90ff',
+ firebrick: '#b22222',
+ floralwhite: '#fffaf0',
+ forestgreen: '#228b22',
+ fuchsia: '#f0f',
+ gainsboro: '#dcdcdc',
+ ghostwhite: '#f8f8ff',
+ gold: '#ffd700',
+ goldenrod: '#daa520',
+ gray: '#808080',
+ green: '#008000',
+ greenyellow: '#adff2f',
+ grey: '#808080',
+ honeydew: '#f0fff0',
+ hotpink: '#ff69b4',
+ indianred: '#cd5c5c',
+ indigo: '#4b0082',
+ ivory: '#fffff0',
+ khaki: '#f0e68c',
+ lavender: '#e6e6fa',
+ lavenderblush: '#fff0f5',
+ lawngreen: '#7cfc00',
+ lemonchiffon: '#fffacd',
+ lightblue: '#add8e6',
+ lightcoral: '#f08080',
+ lightcyan: '#e0ffff',
+ lightgoldenrodyellow: '#fafad2',
+ lightgray: '#d3d3d3',
+ lightgreen: '#90ee90',
+ lightgrey: '#d3d3d3',
+ lightpink: '#ffb6c1',
+ lightsalmon: '#ffa07a',
+ lightseagreen: '#20b2aa',
+ lightskyblue: '#87cefa',
+ lightslategray: '#789',
+ lightslategrey: '#789',
+ lightsteelblue: '#b0c4de',
+ lightyellow: '#ffffe0',
+ lime: '#0f0',
+ limegreen: '#32cd32',
+ linen: '#faf0e6',
+ magenta: '#f0f',
+ maroon: '#800000',
+ mediumaquamarine: '#66cdaa',
+ mediumblue: '#0000cd',
+ mediumorchid: '#ba55d3',
+ mediumpurple: '#9370db',
+ mediumseagreen: '#3cb371',
+ mediumslateblue: '#7b68ee',
+ mediumspringgreen: '#00fa9a',
+ mediumturquoise: '#48d1cc',
+ mediumvioletred: '#c71585',
+ midnightblue: '#191970',
+ mintcream: '#f5fffa',
+ mistyrose: '#ffe4e1',
+ moccasin: '#ffe4b5',
+ navajowhite: '#ffdead',
+ navy: '#000080',
+ oldlace: '#fdf5e6',
+ olive: '#808000',
+ olivedrab: '#6b8e23',
+ orange: '#ffa500',
+ orangered: '#ff4500',
+ orchid: '#da70d6',
+ palegoldenrod: '#eee8aa',
+ palegreen: '#98fb98',
+ paleturquoise: '#afeeee',
+ palevioletred: '#db7093',
+ papayawhip: '#ffefd5',
+ peachpuff: '#ffdab9',
+ peru: '#cd853f',
+ pink: '#ffc0cb',
+ plum: '#dda0dd',
+ powderblue: '#b0e0e6',
+ purple: '#800080',
+ rebeccapurple: '#639',
+ red: '#f00',
+ rosybrown: '#bc8f8f',
+ royalblue: '#4169e1',
+ saddlebrown: '#8b4513',
+ salmon: '#fa8072',
+ sandybrown: '#f4a460',
+ seagreen: '#2e8b57',
+ seashell: '#fff5ee',
+ sienna: '#a0522d',
+ silver: '#c0c0c0',
+ skyblue: '#87ceeb',
+ slateblue: '#6a5acd',
+ slategray: '#708090',
+ slategrey: '#708090',
+ snow: '#fffafa',
+ springgreen: '#00ff7f',
+ steelblue: '#4682b4',
+ tan: '#d2b48c',
+ teal: '#008080',
+ thistle: '#d8bfd8',
+ tomato: '#ff6347',
+ turquoise: '#40e0d0',
+ violet: '#ee82ee',
+ wheat: '#f5deb3',
+ white: '#fff',
+ whitesmoke: '#f5f5f5',
+ yellow: '#ff0',
+ yellowgreen: '#9acd32',
+};
+
+/**
+ * @type {Record<string, string>}
+ */
+exports.colorsShortNames = {
+ '#f0ffff': 'azure',
+ '#f5f5dc': 'beige',
+ '#ffe4c4': 'bisque',
+ '#a52a2a': 'brown',
+ '#ff7f50': 'coral',
+ '#ffd700': 'gold',
+ '#808080': 'gray',
+ '#008000': 'green',
+ '#4b0082': 'indigo',
+ '#fffff0': 'ivory',
+ '#f0e68c': 'khaki',
+ '#faf0e6': 'linen',
+ '#800000': 'maroon',
+ '#000080': 'navy',
+ '#808000': 'olive',
+ '#ffa500': 'orange',
+ '#da70d6': 'orchid',
+ '#cd853f': 'peru',
+ '#ffc0cb': 'pink',
+ '#dda0dd': 'plum',
+ '#800080': 'purple',
+ '#f00': 'red',
+ '#ff0000': 'red',
+ '#fa8072': 'salmon',
+ '#a0522d': 'sienna',
+ '#c0c0c0': 'silver',
+ '#fffafa': 'snow',
+ '#d2b48c': 'tan',
+ '#008080': 'teal',
+ '#ff6347': 'tomato',
+ '#ee82ee': 'violet',
+ '#f5deb3': 'wheat',
+};
+
+// https://www.w3.org/TR/SVG11/single-page.html#types-DataTypeColor
+exports.colorsProps = [
+ 'color',
+ 'fill',
+ 'stroke',
+ 'stop-color',
+ 'flood-color',
+ 'lighting-color',
+];
diff --git a/node_modules/svgo/plugins/_path.js b/node_modules/svgo/plugins/_path.js
new file mode 100644
index 00000000..60002d7a
--- /dev/null
+++ b/node_modules/svgo/plugins/_path.js
@@ -0,0 +1,816 @@
+'use strict';
+
+/**
+ * @typedef {import('../lib/types').XastElement} XastElement
+ * @typedef {import('../lib/types').PathDataItem} PathDataItem
+ */
+
+const { parsePathData, stringifyPathData } = require('../lib/path.js');
+
+/**
+ * @type {[number, number]}
+ */
+var prevCtrlPoint;
+
+/**
+ * Convert path string to JS representation.
+ *
+ * @type {(path: XastElement) => Array<PathDataItem>}
+ */
+const path2js = (path) => {
+ // @ts-ignore legacy
+ if (path.pathJS) return path.pathJS;
+ /**
+ * @type {Array<PathDataItem>}
+ */
+ const pathData = []; // JS representation of the path data
+ const newPathData = parsePathData(path.attributes.d);
+ for (const { command, args } of newPathData) {
+ pathData.push({ command, args });
+ }
+ // First moveto is actually absolute. Subsequent coordinates were separated above.
+ if (pathData.length && pathData[0].command == 'm') {
+ pathData[0].command = 'M';
+ }
+ // @ts-ignore legacy
+ path.pathJS = pathData;
+ return pathData;
+};
+exports.path2js = path2js;
+
+/**
+ * Convert relative Path data to absolute.
+ *
+ * @type {(data: Array<PathDataItem>) => Array<PathDataItem>}
+ *
+ */
+const convertRelativeToAbsolute = (data) => {
+ /**
+ * @type {Array<PathDataItem>}
+ */
+ const newData = [];
+ let start = [0, 0];
+ let cursor = [0, 0];
+
+ for (let { command, args } of data) {
+ args = args.slice();
+
+ // moveto (x y)
+ if (command === 'm') {
+ args[0] += cursor[0];
+ args[1] += cursor[1];
+ command = 'M';
+ }
+ if (command === 'M') {
+ cursor[0] = args[0];
+ cursor[1] = args[1];
+ start[0] = cursor[0];
+ start[1] = cursor[1];
+ }
+
+ // horizontal lineto (x)
+ if (command === 'h') {
+ args[0] += cursor[0];
+ command = 'H';
+ }
+ if (command === 'H') {
+ cursor[0] = args[0];
+ }
+
+ // vertical lineto (y)
+ if (command === 'v') {
+ args[0] += cursor[1];
+ command = 'V';
+ }
+ if (command === 'V') {
+ cursor[1] = args[0];
+ }
+
+ // lineto (x y)
+ if (command === 'l') {
+ args[0] += cursor[0];
+ args[1] += cursor[1];
+ command = 'L';
+ }
+ if (command === 'L') {
+ cursor[0] = args[0];
+ cursor[1] = args[1];
+ }
+
+ // curveto (x1 y1 x2 y2 x y)
+ if (command === 'c') {
+ args[0] += cursor[0];
+ args[1] += cursor[1];
+ args[2] += cursor[0];
+ args[3] += cursor[1];
+ args[4] += cursor[0];
+ args[5] += cursor[1];
+ command = 'C';
+ }
+ if (command === 'C') {
+ cursor[0] = args[4];
+ cursor[1] = args[5];
+ }
+
+ // smooth curveto (x2 y2 x y)
+ if (command === 's') {
+ args[0] += cursor[0];
+ args[1] += cursor[1];
+ args[2] += cursor[0];
+ args[3] += cursor[1];
+ command = 'S';
+ }
+ if (command === 'S') {
+ cursor[0] = args[2];
+ cursor[1] = args[3];
+ }
+
+ // quadratic Bézier curveto (x1 y1 x y)
+ if (command === 'q') {
+ args[0] += cursor[0];
+ args[1] += cursor[1];
+ args[2] += cursor[0];
+ args[3] += cursor[1];
+ command = 'Q';
+ }
+ if (command === 'Q') {
+ cursor[0] = args[2];
+ cursor[1] = args[3];
+ }
+
+ // smooth quadratic Bézier curveto (x y)
+ if (command === 't') {
+ args[0] += cursor[0];
+ args[1] += cursor[1];
+ command = 'T';
+ }
+ if (command === 'T') {
+ cursor[0] = args[0];
+ cursor[1] = args[1];
+ }
+
+ // elliptical arc (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
+ if (command === 'a') {
+ args[5] += cursor[0];
+ args[6] += cursor[1];
+ command = 'A';
+ }
+ if (command === 'A') {
+ cursor[0] = args[5];
+ cursor[1] = args[6];
+ }
+
+ // closepath
+ if (command === 'z' || command === 'Z') {
+ cursor[0] = start[0];
+ cursor[1] = start[1];
+ command = 'z';
+ }
+
+ newData.push({ command, args });
+ }
+ return newData;
+};
+
+/**
+ * @typedef {{ floatPrecision?: number, noSpaceAfterFlags?: boolean }} Js2PathParams
+ */
+
+/**
+ * Convert path array to string.
+ *
+ * @type {(path: XastElement, data: Array<PathDataItem>, params: Js2PathParams) => void}
+ */
+exports.js2path = function (path, data, params) {
+ // @ts-ignore legacy
+ path.pathJS = data;
+
+ const pathData = [];
+ for (const item of data) {
+ // remove moveto commands which are followed by moveto commands
+ if (
+ pathData.length !== 0 &&
+ (item.command === 'M' || item.command === 'm')
+ ) {
+ const last = pathData[pathData.length - 1];
+ if (last.command === 'M' || last.command === 'm') {
+ pathData.pop();
+ }
+ }
+ pathData.push({
+ command: item.command,
+ args: item.args,
+ });
+ }
+
+ path.attributes.d = stringifyPathData({
+ pathData,
+ precision: params.floatPrecision,
+ disableSpaceAfterFlags: params.noSpaceAfterFlags,
+ });
+};
+
+/**
+ * @type {(dest: Array<number>, source: Array<number>) => Array<number>}
+ */
+function set(dest, source) {
+ dest[0] = source[source.length - 2];
+ dest[1] = source[source.length - 1];
+ return dest;
+}
+
+/**
+ * Checks if two paths have an intersection by checking convex hulls
+ * collision using Gilbert-Johnson-Keerthi distance algorithm
+ * https://web.archive.org/web/20180822200027/http://entropyinteractive.com/2011/04/gjk-algorithm/
+ *
+ * @type {(path1: Array<PathDataItem>, path2: Array<PathDataItem>) => boolean}
+ */
+exports.intersects = function (path1, path2) {
+ // Collect points of every subpath.
+ const points1 = gatherPoints(convertRelativeToAbsolute(path1));
+ const points2 = gatherPoints(convertRelativeToAbsolute(path2));
+
+ // Axis-aligned bounding box check.
+ if (
+ points1.maxX <= points2.minX ||
+ points2.maxX <= points1.minX ||
+ points1.maxY <= points2.minY ||
+ points2.maxY <= points1.minY ||
+ points1.list.every((set1) => {
+ return points2.list.every((set2) => {
+ return (
+ set1.list[set1.maxX][0] <= set2.list[set2.minX][0] ||
+ set2.list[set2.maxX][0] <= set1.list[set1.minX][0] ||
+ set1.list[set1.maxY][1] <= set2.list[set2.minY][1] ||
+ set2.list[set2.maxY][1] <= set1.list[set1.minY][1]
+ );
+ });
+ })
+ )
+ return false;
+
+ // Get a convex hull from points of each subpath. Has the most complexity O(n·log n).
+ const hullNest1 = points1.list.map(convexHull);
+ const hullNest2 = points2.list.map(convexHull);
+
+ // Check intersection of every subpath of the first path with every subpath of the second.
+ return hullNest1.some(function (hull1) {
+ if (hull1.list.length < 3) return false;
+
+ return hullNest2.some(function (hull2) {
+ if (hull2.list.length < 3) return false;
+
+ var simplex = [getSupport(hull1, hull2, [1, 0])], // create the initial simplex
+ direction = minus(simplex[0]); // set the direction to point towards the origin
+
+ var iterations = 1e4; // infinite loop protection, 10 000 iterations is more than enough
+ // eslint-disable-next-line no-constant-condition
+ while (true) {
+ // eslint-disable-next-line no-constant-condition
+ if (iterations-- == 0) {
+ console.error(
+ 'Error: infinite loop while processing mergePaths plugin.'
+ );
+ return true; // true is the safe value that means “do nothing with paths”
+ }
+ // add a new point
+ simplex.push(getSupport(hull1, hull2, direction));
+ // see if the new point was on the correct side of the origin
+ if (dot(direction, simplex[simplex.length - 1]) <= 0) return false;
+ // process the simplex
+ if (processSimplex(simplex, direction)) return true;
+ }
+ });
+ });
+
+ /**
+ * @type {(a: Point, b: Point, direction: Array<number>) => Array<number>}
+ */
+ function getSupport(a, b, direction) {
+ return sub(supportPoint(a, direction), supportPoint(b, minus(direction)));
+ }
+
+ // Computes farthest polygon point in particular direction.
+ // Thanks to knowledge of min/max x and y coordinates we can choose a quadrant to search in.
+ // Since we're working on convex hull, the dot product is increasing until we find the farthest point.
+ /**
+ * @type {(polygon: Point, direction: Array<number>) => Array<number>}
+ */
+ function supportPoint(polygon, direction) {
+ var index =
+ direction[1] >= 0
+ ? direction[0] < 0
+ ? polygon.maxY
+ : polygon.maxX
+ : direction[0] < 0
+ ? polygon.minX
+ : polygon.minY,
+ max = -Infinity,
+ value;
+ while ((value = dot(polygon.list[index], direction)) > max) {
+ max = value;
+ index = ++index % polygon.list.length;
+ }
+ return polygon.list[(index || polygon.list.length) - 1];
+ }
+};
+
+/**
+ * @type {(simplex: Array<Array<number>>, direction: Array<number>) => boolean}
+ */
+function processSimplex(simplex, direction) {
+ // we only need to handle to 1-simplex and 2-simplex
+ if (simplex.length == 2) {
+ // 1-simplex
+ let a = simplex[1],
+ b = simplex[0],
+ AO = minus(simplex[1]),
+ AB = sub(b, a);
+ // AO is in the same direction as AB
+ if (dot(AO, AB) > 0) {
+ // get the vector perpendicular to AB facing O
+ set(direction, orth(AB, a));
+ } else {
+ set(direction, AO);
+ // only A remains in the simplex
+ simplex.shift();
+ }
+ } else {
+ // 2-simplex
+ let a = simplex[2], // [a, b, c] = simplex
+ b = simplex[1],
+ c = simplex[0],
+ AB = sub(b, a),
+ AC = sub(c, a),
+ AO = minus(a),
+ ACB = orth(AB, AC), // the vector perpendicular to AB facing away from C
+ ABC = orth(AC, AB); // the vector perpendicular to AC facing away from B
+
+ if (dot(ACB, AO) > 0) {
+ if (dot(AB, AO) > 0) {
+ // region 4
+ set(direction, ACB);
+ simplex.shift(); // simplex = [b, a]
+ } else {
+ // region 5
+ set(direction, AO);
+ simplex.splice(0, 2); // simplex = [a]
+ }
+ } else if (dot(ABC, AO) > 0) {
+ if (dot(AC, AO) > 0) {
+ // region 6
+ set(direction, ABC);
+ simplex.splice(1, 1); // simplex = [c, a]
+ } else {
+ // region 5 (again)
+ set(direction, AO);
+ simplex.splice(0, 2); // simplex = [a]
+ }
+ } // region 7
+ else return true;
+ }
+ return false;
+}
+
+/**
+ * @type {(v: Array<number>) => Array<number>}
+ */
+function minus(v) {
+ return [-v[0], -v[1]];
+}
+
+/**
+ * @type {(v1: Array<number>, v2: Array<number>) => Array<number>}
+ */
+function sub(v1, v2) {
+ return [v1[0] - v2[0], v1[1] - v2[1]];
+}
+
+/**
+ * @type {(v1: Array<number>, v2: Array<number>) => number}
+ */
+function dot(v1, v2) {
+ return v1[0] * v2[0] + v1[1] * v2[1];
+}
+
+/**
+ * @type {(v1: Array<number>, v2: Array<number>) => Array<number>}
+ */
+function orth(v, from) {
+ var o = [-v[1], v[0]];
+ return dot(o, minus(from)) < 0 ? minus(o) : o;
+}
+
+/**
+ * @typedef {{
+ * list: Array<Array<number>>,
+ * minX: number,
+ * minY: number,
+ * maxX: number,
+ * maxY: number
+ * }} Point
+ */
+
+/**
+ * @typedef {{
+ * list: Array<Point>,
+ * minX: number,
+ * minY: number,
+ * maxX: number,
+ * maxY: number
+ * }} Points
+ */
+
+/**
+ * @type {(pathData: Array<PathDataItem>) => Points}
+ */
+function gatherPoints(pathData) {
+ /**
+ * @type {Points}
+ */
+ const points = { list: [], minX: 0, minY: 0, maxX: 0, maxY: 0 };
+
+ // Writes data about the extreme points on each axle
+ /**
+ * @type {(path: Point, point: Array<number>) => void}
+ */
+ const addPoint = (path, point) => {
+ if (!path.list.length || point[1] > path.list[path.maxY][1]) {
+ path.maxY = path.list.length;
+ points.maxY = points.list.length
+ ? Math.max(point[1], points.maxY)
+ : point[1];
+ }
+ if (!path.list.length || point[0] > path.list[path.maxX][0]) {
+ path.maxX = path.list.length;
+ points.maxX = points.list.length
+ ? Math.max(point[0], points.maxX)
+ : point[0];
+ }
+ if (!path.list.length || point[1] < path.list[path.minY][1]) {
+ path.minY = path.list.length;
+ points.minY = points.list.length
+ ? Math.min(point[1], points.minY)
+ : point[1];
+ }
+ if (!path.list.length || point[0] < path.list[path.minX][0]) {
+ path.minX = path.list.length;
+ points.minX = points.list.length
+ ? Math.min(point[0], points.minX)
+ : point[0];
+ }
+ path.list.push(point);
+ };
+
+ for (let i = 0; i < pathData.length; i += 1) {
+ const pathDataItem = pathData[i];
+ let subPath =
+ points.list.length === 0
+ ? { list: [], minX: 0, minY: 0, maxX: 0, maxY: 0 }
+ : points.list[points.list.length - 1];
+ let prev = i === 0 ? null : pathData[i - 1];
+ let basePoint =
+ subPath.list.length === 0 ? null : subPath.list[subPath.list.length - 1];
+ let data = pathDataItem.args;
+ let ctrlPoint = basePoint;
+
+ /**
+ * @type {(n: number, i: number) => number}
+ * TODO fix null hack
+ */
+ const toAbsolute = (n, i) => n + (basePoint == null ? 0 : basePoint[i % 2]);
+
+ switch (pathDataItem.command) {
+ case 'M':
+ subPath = { list: [], minX: 0, minY: 0, maxX: 0, maxY: 0 };
+ points.list.push(subPath);
+ break;
+
+ case 'H':
+ if (basePoint != null) {
+ addPoint(subPath, [data[0], basePoint[1]]);
+ }
+ break;
+
+ case 'V':
+ if (basePoint != null) {
+ addPoint(subPath, [basePoint[0], data[0]]);
+ }
+ break;
+
+ case 'Q':
+ addPoint(subPath, data.slice(0, 2));
+ prevCtrlPoint = [data[2] - data[0], data[3] - data[1]]; // Save control point for shorthand
+ break;
+
+ case 'T':
+ if (
+ basePoint != null &&
+ prev != null &&
+ (prev.command == 'Q' || prev.command == 'T')
+ ) {
+ ctrlPoint = [
+ basePoint[0] + prevCtrlPoint[0],
+ basePoint[1] + prevCtrlPoint[1],
+ ];
+ addPoint(subPath, ctrlPoint);
+ prevCtrlPoint = [data[0] - ctrlPoint[0], data[1] - ctrlPoint[1]];
+ }
+ break;
+
+ case 'C':
+ if (basePoint != null) {
+ // Approximate quibic Bezier curve with middle points between control points
+ addPoint(subPath, [
+ 0.5 * (basePoint[0] + data[0]),
+ 0.5 * (basePoint[1] + data[1]),
+ ]);
+ }
+ addPoint(subPath, [
+ 0.5 * (data[0] + data[2]),
+ 0.5 * (data[1] + data[3]),
+ ]);
+ addPoint(subPath, [
+ 0.5 * (data[2] + data[4]),
+ 0.5 * (data[3] + data[5]),
+ ]);
+ prevCtrlPoint = [data[4] - data[2], data[5] - data[3]]; // Save control point for shorthand
+ break;
+
+ case 'S':
+ if (
+ basePoint != null &&
+ prev != null &&
+ (prev.command == 'C' || prev.command == 'S')
+ ) {
+ addPoint(subPath, [
+ basePoint[0] + 0.5 * prevCtrlPoint[0],
+ basePoint[1] + 0.5 * prevCtrlPoint[1],
+ ]);
+ ctrlPoint = [
+ basePoint[0] + prevCtrlPoint[0],
+ basePoint[1] + prevCtrlPoint[1],
+ ];
+ }
+ if (ctrlPoint != null) {
+ addPoint(subPath, [
+ 0.5 * (ctrlPoint[0] + data[0]),
+ 0.5 * (ctrlPoint[1] + data[1]),
+ ]);
+ }
+ addPoint(subPath, [
+ 0.5 * (data[0] + data[2]),
+ 0.5 * (data[1] + data[3]),
+ ]);
+ prevCtrlPoint = [data[2] - data[0], data[3] - data[1]];
+ break;
+
+ case 'A':
+ if (basePoint != null) {
+ // Convert the arc to bezier curves and use the same approximation
+ // @ts-ignore no idea what's going on here
+ var curves = a2c.apply(0, basePoint.concat(data));
+ for (
+ var cData;
+ (cData = curves.splice(0, 6).map(toAbsolute)).length;
+
+ ) {
+ if (basePoint != null) {
+ addPoint(subPath, [
+ 0.5 * (basePoint[0] + cData[0]),
+ 0.5 * (basePoint[1] + cData[1]),
+ ]);
+ }
+ addPoint(subPath, [
+ 0.5 * (cData[0] + cData[2]),
+ 0.5 * (cData[1] + cData[3]),
+ ]);
+ addPoint(subPath, [
+ 0.5 * (cData[2] + cData[4]),
+ 0.5 * (cData[3] + cData[5]),
+ ]);
+ if (curves.length) addPoint(subPath, (basePoint = cData.slice(-2)));
+ }
+ }
+ break;
+ }
+
+ // Save final command coordinates
+ if (data.length >= 2) addPoint(subPath, data.slice(-2));
+ }
+
+ return points;
+}
+
+/**
+ * Forms a convex hull from set of points of every subpath using monotone chain convex hull algorithm.
+ * https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain
+ *
+ * @type {(points: Point) => Point}
+ */
+function convexHull(points) {
+ points.list.sort(function (a, b) {
+ return a[0] == b[0] ? a[1] - b[1] : a[0] - b[0];
+ });
+
+ var lower = [],
+ minY = 0,
+ bottom = 0;
+ for (let i = 0; i < points.list.length; i++) {
+ while (
+ lower.length >= 2 &&
+ cross(lower[lower.length - 2], lower[lower.length - 1], points.list[i]) <=
+ 0
+ ) {
+ lower.pop();
+ }
+ if (points.list[i][1] < points.list[minY][1]) {
+ minY = i;
+ bottom = lower.length;
+ }
+ lower.push(points.list[i]);
+ }
+
+ var upper = [],
+ maxY = points.list.length - 1,
+ top = 0;
+ for (let i = points.list.length; i--; ) {
+ while (
+ upper.length >= 2 &&
+ cross(upper[upper.length - 2], upper[upper.length - 1], points.list[i]) <=
+ 0
+ ) {
+ upper.pop();
+ }
+ if (points.list[i][1] > points.list[maxY][1]) {
+ maxY = i;
+ top = upper.length;
+ }
+ upper.push(points.list[i]);
+ }
+
+ // last points are equal to starting points of the other part
+ upper.pop();
+ lower.pop();
+
+ const hullList = lower.concat(upper);
+
+ /**
+ * @type {Point}
+ */
+ const hull = {
+ list: hullList,
+ minX: 0, // by sorting
+ maxX: lower.length,
+ minY: bottom,
+ maxY: (lower.length + top) % hullList.length,
+ };
+
+ return hull;
+}
+
+/**
+ * @type {(o: Array<number>, a: Array<number>, b: Array<number>) => number}
+ */
+function cross(o, a, b) {
+ return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
+}
+
+/**
+ * Based on code from Snap.svg (Apache 2 license). http://snapsvg.io/
+ * Thanks to Dmitry Baranovskiy for his great work!
+ *
+ * @type {(
+ * x1: number,
+ * y1: number,
+ * rx: number,
+ * ry: number,
+ * angle: number,
+ * large_arc_flag: number,
+ * sweep_flag: number,
+ * x2: number,
+ * y2: number,
+ * recursive: Array<number>
+ * ) => Array<number>}
+ */
+const a2c = (
+ x1,
+ y1,
+ rx,
+ ry,
+ angle,
+ large_arc_flag,
+ sweep_flag,
+ x2,
+ y2,
+ recursive
+) => {
+ // for more information of where this Math came from visit:
+ // https://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
+ const _120 = (Math.PI * 120) / 180;
+ const rad = (Math.PI / 180) * (+angle || 0);
+ /**
+ * @type {Array<number>}
+ */
+ let res = [];
+ /**
+ * @type {(x: number, y: number, rad: number) => number}
+ */
+ const rotateX = (x, y, rad) => {
+ return x * Math.cos(rad) - y * Math.sin(rad);
+ };
+ /**
+ * @type {(x: number, y: number, rad: number) => number}
+ */
+ const rotateY = (x, y, rad) => {
+ return x * Math.sin(rad) + y * Math.cos(rad);
+ };
+ if (!recursive) {
+ x1 = rotateX(x1, y1, -rad);
+ y1 = rotateY(x1, y1, -rad);
+ x2 = rotateX(x2, y2, -rad);
+ y2 = rotateY(x2, y2, -rad);
+ var x = (x1 - x2) / 2,
+ y = (y1 - y2) / 2;
+ var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
+ if (h > 1) {
+ h = Math.sqrt(h);
+ rx = h * rx;
+ ry = h * ry;
+ }
+ var rx2 = rx * rx;
+ var ry2 = ry * ry;
+ var k =
+ (large_arc_flag == sweep_flag ? -1 : 1) *
+ Math.sqrt(
+ Math.abs(
+ (rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x)
+ )
+ );
+ var cx = (k * rx * y) / ry + (x1 + x2) / 2;
+ var cy = (k * -ry * x) / rx + (y1 + y2) / 2;
+ var f1 = Math.asin(Number(((y1 - cy) / ry).toFixed(9)));
+ var f2 = Math.asin(Number(((y2 - cy) / ry).toFixed(9)));
+
+ f1 = x1 < cx ? Math.PI - f1 : f1;
+ f2 = x2 < cx ? Math.PI - f2 : f2;
+ f1 < 0 && (f1 = Math.PI * 2 + f1);
+ f2 < 0 && (f2 = Math.PI * 2 + f2);
+ if (sweep_flag && f1 > f2) {
+ f1 = f1 - Math.PI * 2;
+ }
+ if (!sweep_flag && f2 > f1) {
+ f2 = f2 - Math.PI * 2;
+ }
+ } else {
+ f1 = recursive[0];
+ f2 = recursive[1];
+ cx = recursive[2];
+ cy = recursive[3];
+ }
+ var df = f2 - f1;
+ if (Math.abs(df) > _120) {
+ var f2old = f2,
+ x2old = x2,
+ y2old = y2;
+ f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
+ x2 = cx + rx * Math.cos(f2);
+ y2 = cy + ry * Math.sin(f2);
+ res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [
+ f2,
+ f2old,
+ cx,
+ cy,
+ ]);
+ }
+ df = f2 - f1;
+ var c1 = Math.cos(f1),
+ s1 = Math.sin(f1),
+ c2 = Math.cos(f2),
+ s2 = Math.sin(f2),
+ t = Math.tan(df / 4),
+ hx = (4 / 3) * rx * t,
+ hy = (4 / 3) * ry * t,
+ m = [
+ -hx * s1,
+ hy * c1,
+ x2 + hx * s2 - x1,
+ y2 - hy * c2 - y1,
+ x2 - x1,
+ y2 - y1,
+ ];
+ if (recursive) {
+ return m.concat(res);
+ } else {
+ res = m.concat(res);
+ var newres = [];
+ for (var i = 0, n = res.length; i < n; i++) {
+ newres[i] =
+ i % 2
+ ? rotateY(res[i - 1], res[i], rad)
+ : rotateX(res[i], res[i + 1], rad);
+ }
+ return newres;
+ }
+};
diff --git a/node_modules/svgo/plugins/_transforms.js b/node_modules/svgo/plugins/_transforms.js
new file mode 100644
index 00000000..159f1a3b
--- /dev/null
+++ b/node_modules/svgo/plugins/_transforms.js
@@ -0,0 +1,379 @@
+'use strict';
+
+const regTransformTypes = /matrix|translate|scale|rotate|skewX|skewY/;
+const regTransformSplit =
+ /\s*(matrix|translate|scale|rotate|skewX|skewY)\s*\(\s*(.+?)\s*\)[\s,]*/;
+const regNumericValues = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g;
+
+/**
+ * @typedef {{ name: string, data: Array<number> }} TransformItem
+ */
+
+/**
+ * Convert transform string to JS representation.
+ *
+ * @type {(transformString: string) => Array<TransformItem>}
+ */
+exports.transform2js = (transformString) => {
+ // JS representation of the transform data
+ /**
+ * @type {Array<TransformItem>}
+ */
+ const transforms = [];
+ // current transform context
+ /**
+ * @type {null | TransformItem}
+ */
+ let current = null;
+ // split value into ['', 'translate', '10 50', '', 'scale', '2', '', 'rotate', '-45', '']
+ for (const item of transformString.split(regTransformSplit)) {
+ var num;
+ if (item) {
+ // if item is a translate function
+ if (regTransformTypes.test(item)) {
+ // then collect it and change current context
+ current = { name: item, data: [] };
+ transforms.push(current);
+ // else if item is data
+ } else {
+ // then split it into [10, 50] and collect as context.data
+ // eslint-disable-next-line no-cond-assign
+ while ((num = regNumericValues.exec(item))) {
+ num = Number(num);
+ if (current != null) {
+ current.data.push(num);
+ }
+ }
+ }
+ }
+ }
+ // return empty array if broken transform (no data)
+ return current == null || current.data.length == 0 ? [] : transforms;
+};
+
+/**
+ * Multiply transforms into one.
+ *
+ * @type {(transforms: Array<TransformItem>) => TransformItem}
+ */
+exports.transformsMultiply = (transforms) => {
+ // convert transforms objects to the matrices
+ const matrixData = transforms.map((transform) => {
+ if (transform.name === 'matrix') {
+ return transform.data;
+ }
+ return transformToMatrix(transform);
+ });
+ // multiply all matrices into one
+ const matrixTransform = {
+ name: 'matrix',
+ data:
+ matrixData.length > 0 ? matrixData.reduce(multiplyTransformMatrices) : [],
+ };
+ return matrixTransform;
+};
+
+/**
+ * math utilities in radians.
+ */
+const mth = {
+ /**
+ * @type {(deg: number) => number}
+ */
+ rad: (deg) => {
+ return (deg * Math.PI) / 180;
+ },
+
+ /**
+ * @type {(rad: number) => number}
+ */
+ deg: (rad) => {
+ return (rad * 180) / Math.PI;
+ },
+
+ /**
+ * @type {(deg: number) => number}
+ */
+ cos: (deg) => {
+ return Math.cos(mth.rad(deg));
+ },
+
+ /**
+ * @type {(val: number, floatPrecision: number) => number}
+ */
+ acos: (val, floatPrecision) => {
+ return Number(mth.deg(Math.acos(val)).toFixed(floatPrecision));
+ },
+
+ /**
+ * @type {(deg: number) => number}
+ */
+ sin: (deg) => {
+ return Math.sin(mth.rad(deg));
+ },
+
+ /**
+ * @type {(val: number, floatPrecision: number) => number}
+ */
+ asin: (val, floatPrecision) => {
+ return Number(mth.deg(Math.asin(val)).toFixed(floatPrecision));
+ },
+
+ /**
+ * @type {(deg: number) => number}
+ */
+ tan: (deg) => {
+ return Math.tan(mth.rad(deg));
+ },
+
+ /**
+ * @type {(val: number, floatPrecision: number) => number}
+ */
+ atan: (val, floatPrecision) => {
+ return Number(mth.deg(Math.atan(val)).toFixed(floatPrecision));
+ },
+};
+
+/**
+ * @typedef {{
+ * convertToShorts: boolean,
+ * floatPrecision: number,
+ * transformPrecision: number,
+ * matrixToTransform: boolean,
+ * shortTranslate: boolean,
+ * shortScale: boolean,
+ * shortRotate: boolean,
+ * removeUseless: boolean,
+ * collapseIntoOne: boolean,
+ * leadingZero: boolean,
+ * negativeExtraSpace: boolean,
+ * }} TransformParams
+ */
+
+/**
+ * Decompose matrix into simple transforms. See
+ * https://frederic-wang.fr/decomposition-of-2d-transform-matrices.html
+ *
+ * @type {(transform: TransformItem, params: TransformParams) => Array<TransformItem>}
+ */
+exports.matrixToTransform = (transform, params) => {
+ let floatPrecision = params.floatPrecision;
+ let data = transform.data;
+ let transforms = [];
+ let sx = Number(
+ Math.hypot(data[0], data[1]).toFixed(params.transformPrecision)
+ );
+ let sy = Number(
+ ((data[0] * data[3] - data[1] * data[2]) / sx).toFixed(
+ params.transformPrecision
+ )
+ );
+ let colsSum = data[0] * data[2] + data[1] * data[3];
+ let rowsSum = data[0] * data[1] + data[2] * data[3];
+ let scaleBefore = rowsSum != 0 || sx == sy;
+
+ // [..., ..., ..., ..., tx, ty] → translate(tx, ty)
+ if (data[4] || data[5]) {
+ transforms.push({
+ name: 'translate',
+ data: data.slice(4, data[5] ? 6 : 5),
+ });
+ }
+
+ // [sx, 0, tan(a)·sy, sy, 0, 0] → skewX(a)·scale(sx, sy)
+ if (!data[1] && data[2]) {
+ transforms.push({
+ name: 'skewX',
+ data: [mth.atan(data[2] / sy, floatPrecision)],
+ });
+
+ // [sx, sx·tan(a), 0, sy, 0, 0] → skewY(a)·scale(sx, sy)
+ } else if (data[1] && !data[2]) {
+ transforms.push({
+ name: 'skewY',
+ data: [mth.atan(data[1] / data[0], floatPrecision)],
+ });
+ sx = data[0];
+ sy = data[3];
+
+ // [sx·cos(a), sx·sin(a), sy·-sin(a), sy·cos(a), x, y] → rotate(a[, cx, cy])·(scale or skewX) or
+ // [sx·cos(a), sy·sin(a), sx·-sin(a), sy·cos(a), x, y] → scale(sx, sy)·rotate(a[, cx, cy]) (if !scaleBefore)
+ } else if (!colsSum || (sx == 1 && sy == 1) || !scaleBefore) {
+ if (!scaleBefore) {
+ sx = (data[0] < 0 ? -1 : 1) * Math.hypot(data[0], data[2]);
+ sy = (data[3] < 0 ? -1 : 1) * Math.hypot(data[1], data[3]);
+ transforms.push({ name: 'scale', data: [sx, sy] });
+ }
+ var angle = Math.min(Math.max(-1, data[0] / sx), 1),
+ rotate = [
+ mth.acos(angle, floatPrecision) *
+ ((scaleBefore ? 1 : sy) * data[1] < 0 ? -1 : 1),
+ ];
+
+ if (rotate[0]) transforms.push({ name: 'rotate', data: rotate });
+
+ if (rowsSum && colsSum)
+ transforms.push({
+ name: 'skewX',
+ data: [mth.atan(colsSum / (sx * sx), floatPrecision)],
+ });
+
+ // rotate(a, cx, cy) can consume translate() within optional arguments cx, cy (rotation point)
+ if (rotate[0] && (data[4] || data[5])) {
+ transforms.shift();
+ var cos = data[0] / sx,
+ sin = data[1] / (scaleBefore ? sx : sy),
+ x = data[4] * (scaleBefore ? 1 : sy),
+ y = data[5] * (scaleBefore ? 1 : sx),
+ denom =
+ (Math.pow(1 - cos, 2) + Math.pow(sin, 2)) *
+ (scaleBefore ? 1 : sx * sy);
+ rotate.push(((1 - cos) * x - sin * y) / denom);
+ rotate.push(((1 - cos) * y + sin * x) / denom);
+ }
+
+ // Too many transformations, return original matrix if it isn't just a scale/translate
+ } else if (data[1] || data[2]) {
+ return [transform];
+ }
+
+ if ((scaleBefore && (sx != 1 || sy != 1)) || !transforms.length)
+ transforms.push({
+ name: 'scale',
+ data: sx == sy ? [sx] : [sx, sy],
+ });
+
+ return transforms;
+};
+
+/**
+ * Convert transform to the matrix data.
+ *
+ * @type {(transform: TransformItem) => Array<number> }
+ */
+const transformToMatrix = (transform) => {
+ if (transform.name === 'matrix') {
+ return transform.data;
+ }
+ switch (transform.name) {
+ case 'translate':
+ // [1, 0, 0, 1, tx, ty]
+ return [1, 0, 0, 1, transform.data[0], transform.data[1] || 0];
+ case 'scale':
+ // [sx, 0, 0, sy, 0, 0]
+ return [
+ transform.data[0],
+ 0,
+ 0,
+ transform.data[1] || transform.data[0],
+ 0,
+ 0,
+ ];
+ case 'rotate':
+ // [cos(a), sin(a), -sin(a), cos(a), x, y]
+ var cos = mth.cos(transform.data[0]),
+ sin = mth.sin(transform.data[0]),
+ cx = transform.data[1] || 0,
+ cy = transform.data[2] || 0;
+ return [
+ cos,
+ sin,
+ -sin,
+ cos,
+ (1 - cos) * cx + sin * cy,
+ (1 - cos) * cy - sin * cx,
+ ];
+ case 'skewX':
+ // [1, 0, tan(a), 1, 0, 0]
+ return [1, 0, mth.tan(transform.data[0]), 1, 0, 0];
+ case 'skewY':
+ // [1, tan(a), 0, 1, 0, 0]
+ return [1, mth.tan(transform.data[0]), 0, 1, 0, 0];
+ default:
+ throw Error(`Unknown transform ${transform.name}`);
+ }
+};
+
+/**
+ * Applies transformation to an arc. To do so, we represent ellipse as a matrix, multiply it
+ * by the transformation matrix and use a singular value decomposition to represent in a form
+ * rotate(θ)·scale(a b)·rotate(φ). This gives us new ellipse params a, b and θ.
+ * SVD is being done with the formulae provided by Wolffram|Alpha (svd {{m0, m2}, {m1, m3}})
+ *
+ * @type {(
+ * cursor: [x: number, y: number],
+ * arc: Array<number>,
+ * transform: Array<number>
+ * ) => Array<number>}
+ */
+exports.transformArc = (cursor, arc, transform) => {
+ const x = arc[5] - cursor[0];
+ const y = arc[6] - cursor[1];
+ let a = arc[0];
+ let b = arc[1];
+ const rot = (arc[2] * Math.PI) / 180;
+ const cos = Math.cos(rot);
+ const sin = Math.sin(rot);
+ // skip if radius is 0
+ if (a > 0 && b > 0) {
+ let h =
+ Math.pow(x * cos + y * sin, 2) / (4 * a * a) +
+ Math.pow(y * cos - x * sin, 2) / (4 * b * b);
+ if (h > 1) {
+ h = Math.sqrt(h);
+ a *= h;
+ b *= h;
+ }
+ }
+ const ellipse = [a * cos, a * sin, -b * sin, b * cos, 0, 0];
+ const m = multiplyTransformMatrices(transform, ellipse);
+ // Decompose the new ellipse matrix
+ const lastCol = m[2] * m[2] + m[3] * m[3];
+ const squareSum = m[0] * m[0] + m[1] * m[1] + lastCol;
+ const root =
+ Math.hypot(m[0] - m[3], m[1] + m[2]) * Math.hypot(m[0] + m[3], m[1] - m[2]);
+
+ if (!root) {
+ // circle
+ arc[0] = arc[1] = Math.sqrt(squareSum / 2);
+ arc[2] = 0;
+ } else {
+ const majorAxisSqr = (squareSum + root) / 2;
+ const minorAxisSqr = (squareSum - root) / 2;
+ const major = Math.abs(majorAxisSqr - lastCol) > 1e-6;
+ const sub = (major ? majorAxisSqr : minorAxisSqr) - lastCol;
+ const rowsSum = m[0] * m[2] + m[1] * m[3];
+ const term1 = m[0] * sub + m[2] * rowsSum;
+ const term2 = m[1] * sub + m[3] * rowsSum;
+ arc[0] = Math.sqrt(majorAxisSqr);
+ arc[1] = Math.sqrt(minorAxisSqr);
+ arc[2] =
+ (((major ? term2 < 0 : term1 > 0) ? -1 : 1) *
+ Math.acos((major ? term1 : term2) / Math.hypot(term1, term2)) *
+ 180) /
+ Math.PI;
+ }
+
+ if (transform[0] < 0 !== transform[3] < 0) {
+ // Flip the sweep flag if coordinates are being flipped horizontally XOR vertically
+ arc[4] = 1 - arc[4];
+ }
+
+ return arc;
+};
+
+/**
+ * Multiply transformation matrices.
+ *
+ * @type {(a: Array<number>, b: Array<number>) => Array<number>}
+ */
+const multiplyTransformMatrices = (a, b) => {
+ return [
+ a[0] * b[0] + a[2] * b[1],
+ a[1] * b[0] + a[3] * b[1],
+ a[0] * b[2] + a[2] * b[3],
+ a[1] * b[2] + a[3] * b[3],
+ a[0] * b[4] + a[2] * b[5] + a[4],
+ a[1] * b[4] + a[3] * b[5] + a[5],
+ ];
+};
diff --git a/node_modules/svgo/plugins/addAttributesToSVGElement.js b/node_modules/svgo/plugins/addAttributesToSVGElement.js
new file mode 100644
index 00000000..845cb056
--- /dev/null
+++ b/node_modules/svgo/plugins/addAttributesToSVGElement.js
@@ -0,0 +1,82 @@
+'use strict';
+
+exports.name = 'addAttributesToSVGElement';
+exports.description = 'adds attributes to an outer <svg> element';
+
+var ENOCLS = `Error in plugin "addAttributesToSVGElement": absent parameters.
+It should have a list of "attributes" or one "attribute".
+Config example:
+
+plugins: [
+ {
+ name: 'addAttributesToSVGElement',
+ params: {
+ attribute: "mySvg"
+ }
+ }
+]
+
+plugins: [
+ {
+ name: 'addAttributesToSVGElement',
+ params: {
+ attributes: ["mySvg", "size-big"]
+ }
+ }
+]
+
+plugins: [
+ {
+ name: 'addAttributesToSVGElement',
+ params: {
+ attributes: [
+ {
+ focusable: false
+ },
+ {
+ 'data-image': icon
+ }
+ ]
+ }
+ }
+]
+`;
+
+/**
+ * Add attributes to an outer <svg> element. Example config:
+ *
+ * @author April Arcus
+ *
+ * @type {import('./plugins-types').Plugin<'addAttributesToSVGElement'>}
+ */
+exports.fn = (root, params) => {
+ if (!Array.isArray(params.attributes) && !params.attribute) {
+ console.error(ENOCLS);
+ return null;
+ }
+ const attributes = params.attributes || [params.attribute];
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ if (node.name === 'svg' && parentNode.type === 'root') {
+ for (const attribute of attributes) {
+ if (typeof attribute === 'string') {
+ if (node.attributes[attribute] == null) {
+ // @ts-ignore disallow explicit nullable attribute value
+ node.attributes[attribute] = undefined;
+ }
+ }
+ if (typeof attribute === 'object') {
+ for (const key of Object.keys(attribute)) {
+ if (node.attributes[key] == null) {
+ // @ts-ignore disallow explicit nullable attribute value
+ node.attributes[key] = attribute[key];
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/addClassesToSVGElement.js b/node_modules/svgo/plugins/addClassesToSVGElement.js
new file mode 100644
index 00000000..9a015c7b
--- /dev/null
+++ b/node_modules/svgo/plugins/addClassesToSVGElement.js
@@ -0,0 +1,82 @@
+'use strict';
+
+exports.name = 'addClassesToSVGElement';
+exports.description = 'adds classnames to an outer <svg> element';
+
+var ENOCLS = `Error in plugin "addClassesToSVGElement": absent parameters.
+It should have a list of classes in "classNames" or one "className".
+Config example:
+
+plugins: [
+ {
+ name: "addClassesToSVGElement",
+ params: {
+ className: "mySvg"
+ }
+ }
+]
+
+plugins: [
+ {
+ name: "addClassesToSVGElement",
+ params: {
+ classNames: ["mySvg", "size-big"]
+ }
+ }
+]
+`;
+
+/**
+ * Add classnames to an outer <svg> element. Example config:
+ *
+ * plugins: [
+ * {
+ * name: "addClassesToSVGElement",
+ * params: {
+ * className: "mySvg"
+ * }
+ * }
+ * ]
+ *
+ * plugins: [
+ * {
+ * name: "addClassesToSVGElement",
+ * params: {
+ * classNames: ["mySvg", "size-big"]
+ * }
+ * }
+ * ]
+ *
+ * @author April Arcus
+ *
+ * @type {import('./plugins-types').Plugin<'addClassesToSVGElement'>}
+ */
+exports.fn = (root, params) => {
+ if (
+ !(Array.isArray(params.classNames) && params.classNames.some(String)) &&
+ !params.className
+ ) {
+ console.error(ENOCLS);
+ return null;
+ }
+ const classNames = params.classNames || [params.className];
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ if (node.name === 'svg' && parentNode.type === 'root') {
+ const classList = new Set(
+ node.attributes.class == null
+ ? null
+ : node.attributes.class.split(' ')
+ );
+ for (const className of classNames) {
+ if (className != null) {
+ classList.add(className);
+ }
+ }
+ node.attributes.class = Array.from(classList).join(' ');
+ }
+ },
+ },
+ };
+};
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;
+ }
+};
diff --git a/node_modules/svgo/plugins/cleanupAttrs.js b/node_modules/svgo/plugins/cleanupAttrs.js
new file mode 100644
index 00000000..399de194
--- /dev/null
+++ b/node_modules/svgo/plugins/cleanupAttrs.js
@@ -0,0 +1,49 @@
+'use strict';
+
+exports.name = 'cleanupAttrs';
+exports.description =
+ 'cleanups attributes from newlines, trailing and repeating spaces';
+
+const regNewlinesNeedSpace = /(\S)\r?\n(\S)/g;
+const regNewlines = /\r?\n/g;
+const regSpaces = /\s{2,}/g;
+
+/**
+ * Cleanup attributes values from newlines, trailing and repeating spaces.
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'cleanupAttrs'>}
+ */
+exports.fn = (root, params) => {
+ const { newlines = true, trim = true, spaces = true } = params;
+ return {
+ element: {
+ enter: (node) => {
+ for (const name of Object.keys(node.attributes)) {
+ if (newlines) {
+ // new line which requires a space instead of themselve
+ node.attributes[name] = node.attributes[name].replace(
+ regNewlinesNeedSpace,
+ (match, p1, p2) => p1 + ' ' + p2
+ );
+ // simple new line
+ node.attributes[name] = node.attributes[name].replace(
+ regNewlines,
+ ''
+ );
+ }
+ if (trim) {
+ node.attributes[name] = node.attributes[name].trim();
+ }
+ if (spaces) {
+ node.attributes[name] = node.attributes[name].replace(
+ regSpaces,
+ ' '
+ );
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/cleanupEnableBackground.js b/node_modules/svgo/plugins/cleanupEnableBackground.js
new file mode 100644
index 00000000..80be32be
--- /dev/null
+++ b/node_modules/svgo/plugins/cleanupEnableBackground.js
@@ -0,0 +1,73 @@
+'use strict';
+
+const { visit } = require('../lib/xast.js');
+
+exports.name = 'cleanupEnableBackground';
+exports.description =
+ 'remove or cleanup enable-background attribute when possible';
+
+/**
+ * Remove or cleanup enable-background attr which coincides with a width/height box.
+ *
+ * @see https://www.w3.org/TR/SVG11/filters.html#EnableBackgroundProperty
+ *
+ * @example
+ * <svg width="100" height="50" enable-background="new 0 0 100 50">
+ * ⬇
+ * <svg width="100" height="50">
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'cleanupEnableBackground'>}
+ */
+exports.fn = (root) => {
+ const regEnableBackground =
+ /^new\s0\s0\s([-+]?\d*\.?\d+([eE][-+]?\d+)?)\s([-+]?\d*\.?\d+([eE][-+]?\d+)?)$/;
+
+ let hasFilter = false;
+ visit(root, {
+ element: {
+ enter: (node) => {
+ if (node.name === 'filter') {
+ hasFilter = true;
+ }
+ },
+ },
+ });
+
+ return {
+ element: {
+ enter: (node) => {
+ if (node.attributes['enable-background'] == null) {
+ return;
+ }
+ if (hasFilter) {
+ if (
+ (node.name === 'svg' ||
+ node.name === 'mask' ||
+ node.name === 'pattern') &&
+ node.attributes.width != null &&
+ node.attributes.height != null
+ ) {
+ const match =
+ node.attributes['enable-background'].match(regEnableBackground);
+ if (
+ match != null &&
+ node.attributes.width === match[1] &&
+ node.attributes.height === match[3]
+ ) {
+ if (node.name === 'svg') {
+ delete node.attributes['enable-background'];
+ } else {
+ node.attributes['enable-background'] = 'new';
+ }
+ }
+ }
+ } else {
+ //we don't need 'enable-background' if we have no filters
+ delete node.attributes['enable-background'];
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/cleanupIds.js b/node_modules/svgo/plugins/cleanupIds.js
new file mode 100644
index 00000000..1d2532b2
--- /dev/null
+++ b/node_modules/svgo/plugins/cleanupIds.js
@@ -0,0 +1,287 @@
+'use strict';
+
+/**
+ * @typedef {import('../lib/types').XastElement} XastElement
+ */
+
+const { visitSkip } = require('../lib/xast.js');
+const { referencesProps } = require('./_collections.js');
+
+exports.name = 'cleanupIds';
+exports.description = 'removes unused IDs and minifies used';
+
+const regReferencesUrl = /\burl\((["'])?#(.+?)\1\)/;
+const regReferencesHref = /^#(.+?)$/;
+const regReferencesBegin = /(\D+)\./;
+const generateIdChars = [
+ 'a',
+ 'b',
+ 'c',
+ 'd',
+ 'e',
+ 'f',
+ 'g',
+ 'h',
+ 'i',
+ 'j',
+ 'k',
+ 'l',
+ 'm',
+ 'n',
+ 'o',
+ 'p',
+ 'q',
+ 'r',
+ 's',
+ 't',
+ 'u',
+ 'v',
+ 'w',
+ 'x',
+ 'y',
+ 'z',
+ 'A',
+ 'B',
+ 'C',
+ 'D',
+ 'E',
+ 'F',
+ 'G',
+ 'H',
+ 'I',
+ 'J',
+ 'K',
+ 'L',
+ 'M',
+ 'N',
+ 'O',
+ 'P',
+ 'Q',
+ 'R',
+ 'S',
+ 'T',
+ 'U',
+ 'V',
+ 'W',
+ 'X',
+ 'Y',
+ 'Z',
+];
+const maxIdIndex = generateIdChars.length - 1;
+
+/**
+ * Check if an ID starts with any one of a list of strings.
+ *
+ * @type {(string: string, prefixes: Array<string>) => boolean}
+ */
+const hasStringPrefix = (string, prefixes) => {
+ for (const prefix of prefixes) {
+ if (string.startsWith(prefix)) {
+ return true;
+ }
+ }
+ return false;
+};
+
+/**
+ * Generate unique minimal ID.
+ *
+ * @type {(currentId: null | Array<number>) => Array<number>}
+ */
+const generateId = (currentId) => {
+ if (currentId == null) {
+ return [0];
+ }
+ currentId[currentId.length - 1] += 1;
+ for (let i = currentId.length - 1; i > 0; i--) {
+ if (currentId[i] > maxIdIndex) {
+ currentId[i] = 0;
+ if (currentId[i - 1] !== undefined) {
+ currentId[i - 1]++;
+ }
+ }
+ }
+ if (currentId[0] > maxIdIndex) {
+ currentId[0] = 0;
+ currentId.unshift(0);
+ }
+ return currentId;
+};
+
+/**
+ * Get string from generated ID array.
+ *
+ * @type {(arr: Array<number>) => string}
+ */
+const getIdString = (arr) => {
+ return arr.map((i) => generateIdChars[i]).join('');
+};
+
+/**
+ * Remove unused and minify used IDs
+ * (only if there are no any <style> or <script>).
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'cleanupIds'>}
+ */
+exports.fn = (_root, params) => {
+ const {
+ remove = true,
+ minify = true,
+ preserve = [],
+ preservePrefixes = [],
+ force = false,
+ } = params;
+ const preserveIds = new Set(
+ Array.isArray(preserve) ? preserve : preserve ? [preserve] : []
+ );
+ const preserveIdPrefixes = Array.isArray(preservePrefixes)
+ ? preservePrefixes
+ : preservePrefixes
+ ? [preservePrefixes]
+ : [];
+ /**
+ * @type {Map<string, XastElement>}
+ */
+ const nodeById = new Map();
+ /**
+ * @type {Map<string, Array<{element: XastElement, name: string, value: string }>>}
+ */
+ const referencesById = new Map();
+ let deoptimized = false;
+
+ return {
+ element: {
+ enter: (node) => {
+ if (force == false) {
+ // deoptimize if style or script elements are present
+ if (
+ (node.name === 'style' || node.name === 'script') &&
+ node.children.length !== 0
+ ) {
+ deoptimized = true;
+ return;
+ }
+
+ // avoid removing IDs if the whole SVG consists only of defs
+ if (node.name === 'svg') {
+ let hasDefsOnly = true;
+ for (const child of node.children) {
+ if (child.type !== 'element' || child.name !== 'defs') {
+ hasDefsOnly = false;
+ break;
+ }
+ }
+ if (hasDefsOnly) {
+ return visitSkip;
+ }
+ }
+ }
+
+ for (const [name, value] of Object.entries(node.attributes)) {
+ if (name === 'id') {
+ // collect all ids
+ const id = value;
+ if (nodeById.has(id)) {
+ delete node.attributes.id; // remove repeated id
+ } else {
+ nodeById.set(id, node);
+ }
+ } else {
+ // collect all references
+ /**
+ * @type {null | string}
+ */
+ let id = null;
+ if (referencesProps.includes(name)) {
+ const match = value.match(regReferencesUrl);
+ if (match != null) {
+ id = match[2]; // url() reference
+ }
+ }
+ if (name === 'href' || name.endsWith(':href')) {
+ const match = value.match(regReferencesHref);
+ if (match != null) {
+ id = match[1]; // href reference
+ }
+ }
+ if (name === 'begin') {
+ const match = value.match(regReferencesBegin);
+ if (match != null) {
+ id = match[1]; // href reference
+ }
+ }
+ if (id != null) {
+ let refs = referencesById.get(id);
+ if (refs == null) {
+ refs = [];
+ referencesById.set(id, refs);
+ }
+ refs.push({ element: node, name, value });
+ }
+ }
+ }
+ },
+ },
+
+ root: {
+ exit: () => {
+ if (deoptimized) {
+ return;
+ }
+ /**
+ * @type {(id: string) => boolean}
+ **/
+ const isIdPreserved = (id) =>
+ preserveIds.has(id) || hasStringPrefix(id, preserveIdPrefixes);
+ /**
+ * @type {null | Array<number>}
+ */
+ let currentId = null;
+ for (const [id, refs] of referencesById) {
+ const node = nodeById.get(id);
+ if (node != null) {
+ // replace referenced IDs with the minified ones
+ if (minify && isIdPreserved(id) === false) {
+ /**
+ * @type {null | string}
+ */
+ let currentIdString = null;
+ do {
+ currentId = generateId(currentId);
+ currentIdString = getIdString(currentId);
+ } while (isIdPreserved(currentIdString));
+ node.attributes.id = currentIdString;
+ for (const { element, name, value } of refs) {
+ if (value.includes('#')) {
+ // replace id in href and url()
+ element.attributes[name] = value.replace(
+ `#${id}`,
+ `#${currentIdString}`
+ );
+ } else {
+ // replace id in begin attribute
+ element.attributes[name] = value.replace(
+ `${id}.`,
+ `${currentIdString}.`
+ );
+ }
+ }
+ }
+ // keep referenced node
+ nodeById.delete(id);
+ }
+ }
+ // remove non-referenced IDs attributes from elements
+ if (remove) {
+ for (const [id, node] of nodeById) {
+ if (isIdPreserved(id) === false) {
+ delete node.attributes.id;
+ }
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/cleanupListOfValues.js b/node_modules/svgo/plugins/cleanupListOfValues.js
new file mode 100644
index 00000000..7150449e
--- /dev/null
+++ b/node_modules/svgo/plugins/cleanupListOfValues.js
@@ -0,0 +1,147 @@
+'use strict';
+
+const { removeLeadingZero } = require('../lib/svgo/tools.js');
+
+exports.name = 'cleanupListOfValues';
+exports.description = 'rounds list of values to the fixed precision';
+
+const regNumericValues =
+ /^([-+]?\d*\.?\d+([eE][-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|em|ex|%)?$/;
+const regSeparator = /\s+,?\s*|,\s*/;
+const absoluteLengths = {
+ // relative to px
+ cm: 96 / 2.54,
+ mm: 96 / 25.4,
+ in: 96,
+ pt: 4 / 3,
+ pc: 16,
+ px: 1,
+};
+
+/**
+ * Round list of values to the fixed precision.
+ *
+ * @example
+ * <svg viewBox="0 0 200.28423 200.28423" enable-background="new 0 0 200.28423 200.28423">
+ * ⬇
+ * <svg viewBox="0 0 200.284 200.284" enable-background="new 0 0 200.284 200.284">
+ *
+ * <polygon points="208.250977 77.1308594 223.069336 ... "/>
+ * ⬇
+ * <polygon points="208.251 77.131 223.069 ... "/>
+ *
+ * @author kiyopikko
+ *
+ * @type {import('./plugins-types').Plugin<'cleanupListOfValues'>}
+ */
+exports.fn = (_root, params) => {
+ const {
+ floatPrecision = 3,
+ leadingZero = true,
+ defaultPx = true,
+ convertToPx = true,
+ } = params;
+
+ /**
+ * @type {(lists: string) => string}
+ */
+ const roundValues = (lists) => {
+ const roundedList = [];
+
+ for (const elem of lists.split(regSeparator)) {
+ const match = elem.match(regNumericValues);
+ const matchNew = elem.match(/new/);
+
+ // if attribute value matches regNumericValues
+ if (match) {
+ // round it to the fixed precision
+ let num = Number(Number(match[1]).toFixed(floatPrecision));
+ /**
+ * @type {any}
+ */
+ let 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 = '';
+ }
+
+ roundedList.push(str + units);
+ }
+ // if attribute value is "new"(only enable-background).
+ else if (matchNew) {
+ roundedList.push('new');
+ } else if (elem) {
+ roundedList.push(elem);
+ }
+ }
+
+ return roundedList.join(' ');
+ };
+
+ return {
+ element: {
+ enter: (node) => {
+ if (node.attributes.points != null) {
+ node.attributes.points = roundValues(node.attributes.points);
+ }
+
+ if (node.attributes['enable-background'] != null) {
+ node.attributes['enable-background'] = roundValues(
+ node.attributes['enable-background']
+ );
+ }
+
+ if (node.attributes.viewBox != null) {
+ node.attributes.viewBox = roundValues(node.attributes.viewBox);
+ }
+
+ if (node.attributes['stroke-dasharray'] != null) {
+ node.attributes['stroke-dasharray'] = roundValues(
+ node.attributes['stroke-dasharray']
+ );
+ }
+
+ if (node.attributes.dx != null) {
+ node.attributes.dx = roundValues(node.attributes.dx);
+ }
+
+ if (node.attributes.dy != null) {
+ node.attributes.dy = roundValues(node.attributes.dy);
+ }
+
+ if (node.attributes.x != null) {
+ node.attributes.x = roundValues(node.attributes.x);
+ }
+
+ if (node.attributes.y != null) {
+ node.attributes.y = roundValues(node.attributes.y);
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/cleanupNumericValues.js b/node_modules/svgo/plugins/cleanupNumericValues.js
new file mode 100644
index 00000000..6a15dddd
--- /dev/null
+++ b/node_modules/svgo/plugins/cleanupNumericValues.js
@@ -0,0 +1,106 @@
+'use strict';
+
+const { removeLeadingZero } = require('../lib/svgo/tools');
+
+exports.name = 'cleanupNumericValues';
+exports.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('./plugins-types').Plugin<'cleanupNumericValues'>}
+ */
+exports.fn = (_root, params) => {
+ const {
+ floatPrecision = 3,
+ leadingZero = true,
+ defaultPx = true,
+ convertToPx = true,
+ } = params;
+
+ return {
+ element: {
+ enter: (node) => {
+ if (node.attributes.viewBox != null) {
+ const nums = node.attributes.viewBox.split(/\s,?\s*|,\s*/g);
+ node.attributes.viewBox = nums
+ .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 = value.match(regNumericValues);
+
+ // if attribute value matches regNumericValues
+ if (match) {
+ // round it to the fixed precision
+ let num = Number(Number(match[1]).toFixed(floatPrecision));
+ /**
+ * @type {any}
+ */
+ let 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;
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/collapseGroups.js b/node_modules/svgo/plugins/collapseGroups.js
new file mode 100644
index 00000000..ff38caff
--- /dev/null
+++ b/node_modules/svgo/plugins/collapseGroups.js
@@ -0,0 +1,134 @@
+'use strict';
+
+/**
+ * @typedef {import('../lib/types').XastNode} XastNode
+ */
+
+const { inheritableAttrs, elemsGroups } = require('./_collections.js');
+
+exports.name = 'collapseGroups';
+exports.description = 'collapses useless groups';
+
+/**
+ * @type {(node: XastNode, name: string) => boolean}
+ */
+const hasAnimatedAttr = (node, name) => {
+ if (node.type === 'element') {
+ if (
+ elemsGroups.animation.includes(node.name) &&
+ node.attributes.attributeName === name
+ ) {
+ return true;
+ }
+ for (const child of node.children) {
+ if (hasAnimatedAttr(child, name)) {
+ return true;
+ }
+ }
+ }
+ return false;
+};
+
+/**
+ * Collapse useless groups.
+ *
+ * @example
+ * <g>
+ * <g attr1="val1">
+ * <path d="..."/>
+ * </g>
+ * </g>
+ * ⬇
+ * <g>
+ * <g>
+ * <path attr1="val1" d="..."/>
+ * </g>
+ * </g>
+ * ⬇
+ * <path attr1="val1" d="..."/>
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'collapseGroups'>}
+ */
+exports.fn = () => {
+ return {
+ element: {
+ exit: (node, parentNode) => {
+ if (parentNode.type === 'root' || parentNode.name === 'switch') {
+ return;
+ }
+ // non-empty groups
+ if (node.name !== 'g' || node.children.length === 0) {
+ return;
+ }
+
+ // move group attibutes to the single child element
+ if (
+ Object.keys(node.attributes).length !== 0 &&
+ node.children.length === 1
+ ) {
+ const firstChild = node.children[0];
+ // TODO untangle this mess
+ if (
+ firstChild.type === 'element' &&
+ firstChild.attributes.id == null &&
+ node.attributes.filter == null &&
+ (node.attributes.class == null ||
+ firstChild.attributes.class == null) &&
+ ((node.attributes['clip-path'] == null &&
+ node.attributes.mask == null) ||
+ (firstChild.name === 'g' &&
+ node.attributes.transform == null &&
+ firstChild.attributes.transform == null))
+ ) {
+ for (const [name, value] of Object.entries(node.attributes)) {
+ // avoid copying to not conflict with animated attribute
+ if (hasAnimatedAttr(firstChild, name)) {
+ return;
+ }
+ if (firstChild.attributes[name] == null) {
+ firstChild.attributes[name] = value;
+ } else if (name === 'transform') {
+ firstChild.attributes[name] =
+ value + ' ' + firstChild.attributes[name];
+ } else if (firstChild.attributes[name] === 'inherit') {
+ firstChild.attributes[name] = value;
+ } else if (
+ inheritableAttrs.includes(name) === false &&
+ firstChild.attributes[name] !== value
+ ) {
+ return;
+ }
+ delete node.attributes[name];
+ }
+ }
+ }
+
+ // collapse groups without attributes
+ if (Object.keys(node.attributes).length === 0) {
+ // animation elements "add" attributes to group
+ // group should be preserved
+ for (const child of node.children) {
+ if (
+ child.type === 'element' &&
+ elemsGroups.animation.includes(child.name)
+ ) {
+ return;
+ }
+ }
+ // replace current node with all its children
+ const index = parentNode.children.indexOf(node);
+ parentNode.children.splice(index, 1, ...node.children);
+ // TODO remove legacy parentNode in v4
+ for (const child of node.children) {
+ Object.defineProperty(child, 'parentNode', {
+ writable: true,
+ value: parentNode,
+ });
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/convertColors.js b/node_modules/svgo/plugins/convertColors.js
new file mode 100644
index 00000000..c9609d8b
--- /dev/null
+++ b/node_modules/svgo/plugins/convertColors.js
@@ -0,0 +1,144 @@
+'use strict';
+
+const collections = require('./_collections.js');
+
+exports.name = 'convertColors';
+exports.description = 'converts colors: rgb() to #rrggbb and #rrggbb to #rgb';
+
+const rNumber = '([+-]?(?:\\d*\\.\\d+|\\d+\\.?)%?)';
+const rComma = '\\s*,\\s*';
+const regRGB = new RegExp(
+ '^rgb\\(\\s*' + rNumber + rComma + rNumber + rComma + rNumber + '\\s*\\)$'
+);
+const regHEX = /^#(([a-fA-F0-9])\2){3}$/;
+
+/**
+ * Convert [r, g, b] to #rrggbb.
+ *
+ * @see https://gist.github.com/983535
+ *
+ * @example
+ * rgb2hex([255, 255, 255]) // '#ffffff'
+ *
+ * @author Jed Schmidt
+ *
+ * @type {(rgb: Array<number>) => string}
+ */
+const convertRgbToHex = ([r, g, b]) => {
+ // combine the octets into a 32-bit integer as: [1][r][g][b]
+ const hexNumber =
+ // operator precedence is (+) > (<<) > (|)
+ ((((256 + // [1][0]
+ r) << // [1][r]
+ 8) | // [1][r][0]
+ g) << // [1][r][g]
+ 8) | // [1][r][g][0]
+ b;
+ // serialize [1][r][g][b] to a hex string, and
+ // remove the 1 to get the number with 0s intact
+ return '#' + hexNumber.toString(16).slice(1).toUpperCase();
+};
+
+/**
+ * Convert different colors formats in element attributes to hex.
+ *
+ * @see https://www.w3.org/TR/SVG11/types.html#DataTypeColor
+ * @see https://www.w3.org/TR/SVG11/single-page.html#types-ColorKeywords
+ *
+ * @example
+ * Convert color name keyword to long hex:
+ * fuchsia ➡ #ff00ff
+ *
+ * Convert rgb() to long hex:
+ * rgb(255, 0, 255) ➡ #ff00ff
+ * rgb(50%, 100, 100%) ➡ #7f64ff
+ *
+ * Convert long hex to short hex:
+ * #aabbcc ➡ #abc
+ *
+ * Convert hex to short name
+ * #000080 ➡ navy
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'convertColors'>}
+ */
+exports.fn = (_root, params) => {
+ const {
+ currentColor = false,
+ names2hex = true,
+ rgb2hex = true,
+ shorthex = true,
+ shortname = true,
+ } = params;
+
+ return {
+ element: {
+ enter: (node) => {
+ for (const [name, value] of Object.entries(node.attributes)) {
+ if (collections.colorsProps.includes(name)) {
+ let val = value;
+
+ // convert colors to currentColor
+ if (currentColor) {
+ let matched;
+ if (typeof currentColor === 'string') {
+ matched = val === currentColor;
+ } else if (currentColor instanceof RegExp) {
+ matched = currentColor.exec(val) != null;
+ } else {
+ matched = val !== 'none';
+ }
+ if (matched) {
+ val = 'currentColor';
+ }
+ }
+
+ // convert color name keyword to long hex
+ if (names2hex) {
+ const colorName = val.toLowerCase();
+ if (collections.colorsNames[colorName] != null) {
+ val = collections.colorsNames[colorName];
+ }
+ }
+
+ // convert rgb() to long hex
+ if (rgb2hex) {
+ let match = val.match(regRGB);
+ if (match != null) {
+ let nums = match.slice(1, 4).map((m) => {
+ let n;
+ if (m.indexOf('%') > -1) {
+ n = Math.round(parseFloat(m) * 2.55);
+ } else {
+ n = Number(m);
+ }
+ return Math.max(0, Math.min(n, 255));
+ });
+ val = convertRgbToHex(nums);
+ }
+ }
+
+ // convert long hex to short hex
+ if (shorthex) {
+ let match = val.match(regHEX);
+ if (match != null) {
+ val = '#' + match[0][1] + match[0][3] + match[0][5];
+ }
+ }
+
+ // convert hex to short name
+ if (shortname) {
+ const colorName = val.toLowerCase();
+ if (collections.colorsShortNames[colorName] != null) {
+ val = collections.colorsShortNames[colorName];
+ }
+ }
+
+ node.attributes[name] = val;
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/convertEllipseToCircle.js b/node_modules/svgo/plugins/convertEllipseToCircle.js
new file mode 100644
index 00000000..50fa20db
--- /dev/null
+++ b/node_modules/svgo/plugins/convertEllipseToCircle.js
@@ -0,0 +1,37 @@
+'use strict';
+
+exports.name = 'convertEllipseToCircle';
+exports.description = 'converts non-eccentric <ellipse>s to <circle>s';
+
+/**
+ * Converts non-eccentric <ellipse>s to <circle>s.
+ *
+ * @see https://www.w3.org/TR/SVG11/shapes.html
+ *
+ * @author Taylor Hunt
+ *
+ * @type {import('./plugins-types').Plugin<'convertEllipseToCircle'>}
+ */
+exports.fn = () => {
+ return {
+ element: {
+ enter: (node) => {
+ if (node.name === 'ellipse') {
+ const rx = node.attributes.rx || '0';
+ const ry = node.attributes.ry || '0';
+ if (
+ rx === ry ||
+ rx === 'auto' ||
+ ry === 'auto' // SVG2
+ ) {
+ node.name = 'circle';
+ const radius = rx === 'auto' ? ry : rx;
+ delete node.attributes.rx;
+ delete node.attributes.ry;
+ node.attributes.r = radius;
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/convertPathData.js b/node_modules/svgo/plugins/convertPathData.js
new file mode 100644
index 00000000..a5f375ba
--- /dev/null
+++ b/node_modules/svgo/plugins/convertPathData.js
@@ -0,0 +1,1175 @@
+'use strict';
+
+/**
+ * @typedef {import('../lib//types').PathDataItem} PathDataItem
+ */
+
+const { collectStylesheet, computeStyle } = require('../lib/style.js');
+const { visit } = require('../lib/xast.js');
+const { pathElems } = require('./_collections.js');
+const { path2js, js2path } = require('./_path.js');
+const { applyTransforms } = require('./applyTransforms.js');
+const { cleanupOutData } = require('../lib/svgo/tools');
+
+exports.name = 'convertPathData';
+exports.description =
+ 'optimizes path data: writes in shorter form, applies transformations';
+
+/**
+ * @type {(data: number[]) => number[]}
+ */
+let roundData;
+/**
+ * @type {number | false}
+ */
+let precision;
+/**
+ * @type {number}
+ */
+let error;
+/**
+ * @type {number}
+ */
+let arcThreshold;
+/**
+ * @type {number}
+ */
+let arcTolerance;
+
+/**
+ * @typedef {{
+ * applyTransforms: boolean,
+ * applyTransformsStroked: boolean,
+ * makeArcs: {
+ * threshold: number,
+ * tolerance: number,
+ * },
+ * straightCurves: boolean,
+ * lineShorthands: boolean,
+ * curveSmoothShorthands: boolean,
+ * floatPrecision: number | false,
+ * transformPrecision: number,
+ * removeUseless: boolean,
+ * collapseRepeated: boolean,
+ * utilizeAbsolute: boolean,
+ * leadingZero: boolean,
+ * negativeExtraSpace: boolean,
+ * noSpaceAfterFlags: boolean,
+ * forceAbsolutePath: boolean,
+ * }} InternalParams
+ */
+
+/**
+ * @typedef {[number, number]} Point
+ */
+
+/**
+ * @typedef {{
+ * center: Point,
+ * radius: number
+ * }} Circle
+ */
+
+/**
+ * Convert absolute Path to relative,
+ * collapse repeated instructions,
+ * detect and convert Lineto shorthands,
+ * remove useless instructions like "l0,0",
+ * trim useless delimiters and leading zeros,
+ * decrease accuracy of floating-point numbers.
+ *
+ * @see https://www.w3.org/TR/SVG11/paths.html#PathData
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'convertPathData'>}
+ */
+exports.fn = (root, params) => {
+ const {
+ // TODO convert to separate plugin in v3
+ applyTransforms: _applyTransforms = true,
+ applyTransformsStroked = true,
+ makeArcs = {
+ threshold: 2.5, // coefficient of rounding error
+ tolerance: 0.5, // percentage of radius
+ },
+ straightCurves = true,
+ lineShorthands = true,
+ curveSmoothShorthands = true,
+ floatPrecision = 3,
+ transformPrecision = 5,
+ removeUseless = true,
+ collapseRepeated = true,
+ utilizeAbsolute = true,
+ leadingZero = true,
+ negativeExtraSpace = true,
+ noSpaceAfterFlags = false, // a20 60 45 0 1 30 20 → a20 60 45 0130 20
+ forceAbsolutePath = false,
+ } = params;
+
+ /**
+ * @type {InternalParams}
+ */
+ const newParams = {
+ applyTransforms: _applyTransforms,
+ applyTransformsStroked,
+ makeArcs,
+ straightCurves,
+ lineShorthands,
+ curveSmoothShorthands,
+ floatPrecision,
+ transformPrecision,
+ removeUseless,
+ collapseRepeated,
+ utilizeAbsolute,
+ leadingZero,
+ negativeExtraSpace,
+ noSpaceAfterFlags,
+ forceAbsolutePath,
+ };
+
+ // invoke applyTransforms plugin
+ if (_applyTransforms) {
+ visit(
+ root,
+ // @ts-ignore
+ applyTransforms(root, {
+ transformPrecision,
+ applyTransformsStroked,
+ })
+ );
+ }
+
+ const stylesheet = collectStylesheet(root);
+ return {
+ element: {
+ enter: (node) => {
+ if (pathElems.includes(node.name) && node.attributes.d != null) {
+ const computedStyle = computeStyle(stylesheet, node);
+ precision = floatPrecision;
+ error =
+ precision !== false
+ ? +Math.pow(0.1, precision).toFixed(precision)
+ : 1e-2;
+ roundData = precision > 0 && precision < 20 ? strongRound : round;
+ if (makeArcs) {
+ arcThreshold = makeArcs.threshold;
+ arcTolerance = makeArcs.tolerance;
+ }
+ const hasMarkerMid = computedStyle['marker-mid'] != null;
+
+ const maybeHasStroke =
+ computedStyle.stroke &&
+ (computedStyle.stroke.type === 'dynamic' ||
+ computedStyle.stroke.value !== 'none');
+ const maybeHasLinecap =
+ computedStyle['stroke-linecap'] &&
+ (computedStyle['stroke-linecap'].type === 'dynamic' ||
+ computedStyle['stroke-linecap'].value !== 'butt');
+ const maybeHasStrokeAndLinecap = maybeHasStroke && maybeHasLinecap;
+
+ var data = path2js(node);
+
+ // TODO: get rid of functions returns
+ if (data.length) {
+ convertToRelative(data);
+
+ data = filters(data, newParams, {
+ maybeHasStrokeAndLinecap,
+ hasMarkerMid,
+ });
+
+ if (utilizeAbsolute) {
+ data = convertToMixed(data, newParams);
+ }
+
+ // @ts-ignore
+ js2path(node, data, newParams);
+ }
+ }
+ },
+ },
+ };
+};
+
+/**
+ * Convert absolute path data coordinates to relative.
+ *
+ * @type {(pathData: PathDataItem[]) => PathDataItem[]}
+ */
+const convertToRelative = (pathData) => {
+ let start = [0, 0];
+ let cursor = [0, 0];
+ let prevCoords = [0, 0];
+
+ for (let i = 0; i < pathData.length; i += 1) {
+ const pathItem = pathData[i];
+ let { command, args } = pathItem;
+
+ // moveto (x y)
+ if (command === 'm') {
+ // update start and cursor
+ cursor[0] += args[0];
+ cursor[1] += args[1];
+ start[0] = cursor[0];
+ start[1] = cursor[1];
+ }
+ if (command === 'M') {
+ // M → m
+ // skip first moveto
+ if (i !== 0) {
+ command = 'm';
+ }
+ args[0] -= cursor[0];
+ args[1] -= cursor[1];
+ // update start and cursor
+ cursor[0] += args[0];
+ cursor[1] += args[1];
+ start[0] = cursor[0];
+ start[1] = cursor[1];
+ }
+
+ // lineto (x y)
+ if (command === 'l') {
+ cursor[0] += args[0];
+ cursor[1] += args[1];
+ }
+ if (command === 'L') {
+ // L → l
+ command = 'l';
+ args[0] -= cursor[0];
+ args[1] -= cursor[1];
+ cursor[0] += args[0];
+ cursor[1] += args[1];
+ }
+
+ // horizontal lineto (x)
+ if (command === 'h') {
+ cursor[0] += args[0];
+ }
+ if (command === 'H') {
+ // H → h
+ command = 'h';
+ args[0] -= cursor[0];
+ cursor[0] += args[0];
+ }
+
+ // vertical lineto (y)
+ if (command === 'v') {
+ cursor[1] += args[0];
+ }
+ if (command === 'V') {
+ // V → v
+ command = 'v';
+ args[0] -= cursor[1];
+ cursor[1] += args[0];
+ }
+
+ // curveto (x1 y1 x2 y2 x y)
+ if (command === 'c') {
+ cursor[0] += args[4];
+ cursor[1] += args[5];
+ }
+ if (command === 'C') {
+ // C → c
+ command = 'c';
+ args[0] -= cursor[0];
+ args[1] -= cursor[1];
+ args[2] -= cursor[0];
+ args[3] -= cursor[1];
+ args[4] -= cursor[0];
+ args[5] -= cursor[1];
+ cursor[0] += args[4];
+ cursor[1] += args[5];
+ }
+
+ // smooth curveto (x2 y2 x y)
+ if (command === 's') {
+ cursor[0] += args[2];
+ cursor[1] += args[3];
+ }
+ if (command === 'S') {
+ // S → s
+ command = 's';
+ args[0] -= cursor[0];
+ args[1] -= cursor[1];
+ args[2] -= cursor[0];
+ args[3] -= cursor[1];
+ cursor[0] += args[2];
+ cursor[1] += args[3];
+ }
+
+ // quadratic Bézier curveto (x1 y1 x y)
+ if (command === 'q') {
+ cursor[0] += args[2];
+ cursor[1] += args[3];
+ }
+ if (command === 'Q') {
+ // Q → q
+ command = 'q';
+ args[0] -= cursor[0];
+ args[1] -= cursor[1];
+ args[2] -= cursor[0];
+ args[3] -= cursor[1];
+ cursor[0] += args[2];
+ cursor[1] += args[3];
+ }
+
+ // smooth quadratic Bézier curveto (x y)
+ if (command === 't') {
+ cursor[0] += args[0];
+ cursor[1] += args[1];
+ }
+ if (command === 'T') {
+ // T → t
+ command = 't';
+ args[0] -= cursor[0];
+ args[1] -= cursor[1];
+ cursor[0] += args[0];
+ cursor[1] += args[1];
+ }
+
+ // elliptical arc (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
+ if (command === 'a') {
+ cursor[0] += args[5];
+ cursor[1] += args[6];
+ }
+ if (command === 'A') {
+ // A → a
+ command = 'a';
+ args[5] -= cursor[0];
+ args[6] -= cursor[1];
+ cursor[0] += args[5];
+ cursor[1] += args[6];
+ }
+
+ // closepath
+ if (command === 'Z' || command === 'z') {
+ // reset cursor
+ cursor[0] = start[0];
+ cursor[1] = start[1];
+ }
+
+ pathItem.command = command;
+ pathItem.args = args;
+ // store absolute coordinates for later use
+ // base should preserve reference from other element
+ // @ts-ignore
+ pathItem.base = prevCoords;
+ // @ts-ignore
+ pathItem.coords = [cursor[0], cursor[1]];
+ // @ts-ignore
+ prevCoords = pathItem.coords;
+ }
+
+ return pathData;
+};
+
+/**
+ * Main filters loop.
+ *
+ * @type {(
+ * path: PathDataItem[],
+ * params: InternalParams,
+ * aux: { maybeHasStrokeAndLinecap: boolean, hasMarkerMid: boolean }
+ * ) => PathDataItem[]}
+ */
+function filters(path, params, { maybeHasStrokeAndLinecap, hasMarkerMid }) {
+ var stringify = data2Path.bind(null, params),
+ relSubpoint = [0, 0],
+ pathBase = [0, 0],
+ prev = {};
+
+ path = path.filter(function (item, index, path) {
+ let command = item.command;
+ let data = item.args;
+ let next = path[index + 1];
+
+ if (command !== 'Z' && command !== 'z') {
+ var sdata = data,
+ circle;
+
+ if (command === 's') {
+ sdata = [0, 0].concat(data);
+
+ // @ts-ignore
+ var pdata = prev.args,
+ n = pdata.length;
+
+ // (-x, -y) of the prev tangent point relative to the current point
+ sdata[0] = pdata[n - 2] - pdata[n - 4];
+ sdata[1] = pdata[n - 1] - pdata[n - 3];
+ }
+
+ // convert curves to arcs if possible
+ if (
+ params.makeArcs &&
+ (command == 'c' || command == 's') &&
+ isConvex(sdata) &&
+ (circle = findCircle(sdata))
+ ) {
+ var r = roundData([circle.radius])[0],
+ angle = findArcAngle(sdata, circle),
+ sweep = sdata[5] * sdata[0] - sdata[4] * sdata[1] > 0 ? 1 : 0,
+ /**
+ * @type {PathDataItem}
+ */
+ arc = {
+ command: 'a',
+ args: [r, r, 0, 0, sweep, sdata[4], sdata[5]],
+ // @ts-ignore
+ coords: item.coords.slice(),
+ // @ts-ignore
+ base: item.base,
+ },
+ /**
+ * @type {PathDataItem[]}
+ */
+ output = [arc],
+ // relative coordinates to adjust the found circle
+ /**
+ * @type {Point}
+ */
+ relCenter = [
+ circle.center[0] - sdata[4],
+ circle.center[1] - sdata[5],
+ ],
+ relCircle = { center: relCenter, radius: circle.radius },
+ arcCurves = [item],
+ hasPrev = 0,
+ suffix = '',
+ nextLonghand;
+
+ if (
+ // @ts-ignore
+ (prev.command == 'c' &&
+ // @ts-ignore
+ isConvex(prev.args) &&
+ // @ts-ignore
+ isArcPrev(prev.args, circle)) ||
+ // @ts-ignore
+ (prev.command == 'a' && prev.sdata && isArcPrev(prev.sdata, circle))
+ ) {
+ // @ts-ignore
+ arcCurves.unshift(prev);
+ // @ts-ignore
+ arc.base = prev.base;
+ // @ts-ignore
+ arc.args[5] = arc.coords[0] - arc.base[0];
+ // @ts-ignore
+ arc.args[6] = arc.coords[1] - arc.base[1];
+ // @ts-ignore
+ var prevData = prev.command == 'a' ? prev.sdata : prev.args;
+ var prevAngle = findArcAngle(prevData, {
+ center: [
+ prevData[4] + circle.center[0],
+ prevData[5] + circle.center[1],
+ ],
+ radius: circle.radius,
+ });
+ angle += prevAngle;
+ if (angle > Math.PI) arc.args[3] = 1;
+ hasPrev = 1;
+ }
+
+ // check if next curves are fitting the arc
+ for (
+ var j = index;
+ (next = path[++j]) && ~'cs'.indexOf(next.command);
+
+ ) {
+ var nextData = next.args;
+ if (next.command == 's') {
+ nextLonghand = makeLonghand(
+ { command: 's', args: next.args.slice() },
+ path[j - 1].args
+ );
+ nextData = nextLonghand.args;
+ nextLonghand.args = nextData.slice(0, 2);
+ suffix = stringify([nextLonghand]);
+ }
+ if (isConvex(nextData) && isArc(nextData, relCircle)) {
+ angle += findArcAngle(nextData, relCircle);
+ if (angle - 2 * Math.PI > 1e-3) break; // more than 360°
+ if (angle > Math.PI) arc.args[3] = 1;
+ arcCurves.push(next);
+ if (2 * Math.PI - angle > 1e-3) {
+ // less than 360°
+ // @ts-ignore
+ arc.coords = next.coords;
+ // @ts-ignore
+ arc.args[5] = arc.coords[0] - arc.base[0];
+ // @ts-ignore
+ arc.args[6] = arc.coords[1] - arc.base[1];
+ } else {
+ // full circle, make a half-circle arc and add a second one
+ arc.args[5] = 2 * (relCircle.center[0] - nextData[4]);
+ arc.args[6] = 2 * (relCircle.center[1] - nextData[5]);
+ // @ts-ignore
+ arc.coords = [
+ // @ts-ignore
+ arc.base[0] + arc.args[5],
+ // @ts-ignore
+ arc.base[1] + arc.args[6],
+ ];
+ arc = {
+ command: 'a',
+ args: [
+ r,
+ r,
+ 0,
+ 0,
+ sweep,
+ // @ts-ignore
+ next.coords[0] - arc.coords[0],
+ // @ts-ignore
+ next.coords[1] - arc.coords[1],
+ ],
+ // @ts-ignore
+ coords: next.coords,
+ // @ts-ignore
+ base: arc.coords,
+ };
+ output.push(arc);
+ j++;
+ break;
+ }
+ relCenter[0] -= nextData[4];
+ relCenter[1] -= nextData[5];
+ } else break;
+ }
+
+ if ((stringify(output) + suffix).length < stringify(arcCurves).length) {
+ if (path[j] && path[j].command == 's') {
+ makeLonghand(path[j], path[j - 1].args);
+ }
+ if (hasPrev) {
+ var prevArc = output.shift();
+ // @ts-ignore
+ roundData(prevArc.args);
+ // @ts-ignore
+ relSubpoint[0] += prevArc.args[5] - prev.args[prev.args.length - 2];
+ // @ts-ignore
+ relSubpoint[1] += prevArc.args[6] - prev.args[prev.args.length - 1];
+ // @ts-ignore
+ prev.command = 'a';
+ // @ts-ignore
+ prev.args = prevArc.args;
+ // @ts-ignore
+ item.base = prev.coords = prevArc.coords;
+ }
+ // @ts-ignore
+ arc = output.shift();
+ if (arcCurves.length == 1) {
+ // @ts-ignore
+ item.sdata = sdata.slice(); // preserve curve data for future checks
+ } else if (arcCurves.length - 1 - hasPrev > 0) {
+ // filter out consumed next items
+ path.splice.apply(
+ path,
+ // @ts-ignore
+ [index + 1, arcCurves.length - 1 - hasPrev].concat(output)
+ );
+ }
+ if (!arc) return false;
+ command = 'a';
+ data = arc.args;
+ // @ts-ignore
+ item.coords = arc.coords;
+ }
+ }
+
+ // Rounding relative coordinates, taking in account accummulating error
+ // to get closer to absolute coordinates. Sum of rounded value remains same:
+ // l .25 3 .25 2 .25 3 .25 2 -> l .3 3 .2 2 .3 3 .2 2
+ if (precision !== false) {
+ if (
+ command === 'm' ||
+ command === 'l' ||
+ command === 't' ||
+ command === 'q' ||
+ command === 's' ||
+ command === 'c'
+ ) {
+ for (var i = data.length; i--; ) {
+ // @ts-ignore
+ data[i] += item.base[i % 2] - relSubpoint[i % 2];
+ }
+ } else if (command == 'h') {
+ // @ts-ignore
+ data[0] += item.base[0] - relSubpoint[0];
+ } else if (command == 'v') {
+ // @ts-ignore
+ data[0] += item.base[1] - relSubpoint[1];
+ } else if (command == 'a') {
+ // @ts-ignore
+ data[5] += item.base[0] - relSubpoint[0];
+ // @ts-ignore
+ data[6] += item.base[1] - relSubpoint[1];
+ }
+ roundData(data);
+
+ if (command == 'h') relSubpoint[0] += data[0];
+ else if (command == 'v') relSubpoint[1] += data[0];
+ else {
+ relSubpoint[0] += data[data.length - 2];
+ relSubpoint[1] += data[data.length - 1];
+ }
+ roundData(relSubpoint);
+
+ if (command === 'M' || command === 'm') {
+ pathBase[0] = relSubpoint[0];
+ pathBase[1] = relSubpoint[1];
+ }
+ }
+
+ // convert straight curves into lines segments
+ if (params.straightCurves) {
+ if (
+ (command === 'c' && isCurveStraightLine(data)) ||
+ (command === 's' && isCurveStraightLine(sdata))
+ ) {
+ if (next && next.command == 's') makeLonghand(next, data); // fix up next curve
+ command = 'l';
+ data = data.slice(-2);
+ } else if (command === 'q' && isCurveStraightLine(data)) {
+ if (next && next.command == 't') makeLonghand(next, data); // fix up next curve
+ command = 'l';
+ data = data.slice(-2);
+ } else if (
+ command === 't' &&
+ // @ts-ignore
+ prev.command !== 'q' &&
+ // @ts-ignore
+ prev.command !== 't'
+ ) {
+ command = 'l';
+ data = data.slice(-2);
+ } else if (command === 'a' && (data[0] === 0 || data[1] === 0)) {
+ command = 'l';
+ data = data.slice(-2);
+ }
+ }
+
+ // horizontal and vertical line shorthands
+ // l 50 0 → h 50
+ // l 0 50 → v 50
+ if (params.lineShorthands && command === 'l') {
+ if (data[1] === 0) {
+ command = 'h';
+ data.pop();
+ } else if (data[0] === 0) {
+ command = 'v';
+ data.shift();
+ }
+ }
+
+ // collapse repeated commands
+ // h 20 h 30 -> h 50
+ if (
+ params.collapseRepeated &&
+ hasMarkerMid === false &&
+ (command === 'm' || command === 'h' || command === 'v') &&
+ // @ts-ignore
+ prev.command &&
+ // @ts-ignore
+ command == prev.command.toLowerCase() &&
+ ((command != 'h' && command != 'v') ||
+ // @ts-ignore
+ prev.args[0] >= 0 == data[0] >= 0)
+ ) {
+ // @ts-ignore
+ prev.args[0] += data[0];
+ if (command != 'h' && command != 'v') {
+ // @ts-ignore
+ prev.args[1] += data[1];
+ }
+ // @ts-ignore
+ prev.coords = item.coords;
+ // @ts-ignore
+ path[index] = prev;
+ return false;
+ }
+
+ // convert curves into smooth shorthands
+ // @ts-ignore
+ if (params.curveSmoothShorthands && prev.command) {
+ // curveto
+ if (command === 'c') {
+ // c + c → c + s
+ if (
+ // @ts-ignore
+ prev.command === 'c' &&
+ // @ts-ignore
+ data[0] === -(prev.args[2] - prev.args[4]) &&
+ // @ts-ignore
+ data[1] === -(prev.args[3] - prev.args[5])
+ ) {
+ command = 's';
+ data = data.slice(2);
+ }
+
+ // s + c → s + s
+ else if (
+ // @ts-ignore
+ prev.command === 's' &&
+ // @ts-ignore
+ data[0] === -(prev.args[0] - prev.args[2]) &&
+ // @ts-ignore
+ data[1] === -(prev.args[1] - prev.args[3])
+ ) {
+ command = 's';
+ data = data.slice(2);
+ }
+
+ // [^cs] + c → [^cs] + s
+ else if (
+ // @ts-ignore
+ prev.command !== 'c' &&
+ // @ts-ignore
+ prev.command !== 's' &&
+ data[0] === 0 &&
+ data[1] === 0
+ ) {
+ command = 's';
+ data = data.slice(2);
+ }
+ }
+
+ // quadratic Bézier curveto
+ else if (command === 'q') {
+ // q + q → q + t
+ if (
+ // @ts-ignore
+ prev.command === 'q' &&
+ // @ts-ignore
+ data[0] === prev.args[2] - prev.args[0] &&
+ // @ts-ignore
+ data[1] === prev.args[3] - prev.args[1]
+ ) {
+ command = 't';
+ data = data.slice(2);
+ }
+
+ // t + q → t + t
+ else if (
+ // @ts-ignore
+ prev.command === 't' &&
+ // @ts-ignore
+ data[2] === prev.args[0] &&
+ // @ts-ignore
+ data[3] === prev.args[1]
+ ) {
+ command = 't';
+ data = data.slice(2);
+ }
+ }
+ }
+
+ // remove useless non-first path segments
+ if (params.removeUseless && !maybeHasStrokeAndLinecap) {
+ // l 0,0 / h 0 / v 0 / q 0,0 0,0 / t 0,0 / c 0,0 0,0 0,0 / s 0,0 0,0
+ if (
+ (command === 'l' ||
+ command === 'h' ||
+ command === 'v' ||
+ command === 'q' ||
+ command === 't' ||
+ command === 'c' ||
+ command === 's') &&
+ data.every(function (i) {
+ return i === 0;
+ })
+ ) {
+ // @ts-ignore
+ path[index] = prev;
+ return false;
+ }
+
+ // a 25,25 -30 0,1 0,0
+ if (command === 'a' && data[5] === 0 && data[6] === 0) {
+ // @ts-ignore
+ path[index] = prev;
+ return false;
+ }
+ }
+
+ item.command = command;
+ item.args = data;
+
+ prev = item;
+ } else {
+ // z resets coordinates
+ relSubpoint[0] = pathBase[0];
+ relSubpoint[1] = pathBase[1];
+ // @ts-ignore
+ if (prev.command === 'Z' || prev.command === 'z') return false;
+ prev = item;
+ }
+
+ return true;
+ });
+
+ return path;
+}
+
+/**
+ * Writes data in shortest form using absolute or relative coordinates.
+ *
+ * @type {(path: PathDataItem[], params: InternalParams) => PathDataItem[]}
+ */
+function convertToMixed(path, params) {
+ var prev = path[0];
+
+ path = path.filter(function (item, index) {
+ if (index == 0) return true;
+ if (item.command === 'Z' || item.command === 'z') {
+ prev = item;
+ return true;
+ }
+
+ var command = item.command,
+ data = item.args,
+ adata = data.slice();
+
+ if (
+ command === 'm' ||
+ command === 'l' ||
+ command === 't' ||
+ command === 'q' ||
+ command === 's' ||
+ command === 'c'
+ ) {
+ for (var i = adata.length; i--; ) {
+ // @ts-ignore
+ adata[i] += item.base[i % 2];
+ }
+ } else if (command == 'h') {
+ // @ts-ignore
+ adata[0] += item.base[0];
+ } else if (command == 'v') {
+ // @ts-ignore
+ adata[0] += item.base[1];
+ } else if (command == 'a') {
+ // @ts-ignore
+ adata[5] += item.base[0];
+ // @ts-ignore
+ adata[6] += item.base[1];
+ }
+
+ roundData(adata);
+
+ var absoluteDataStr = cleanupOutData(adata, params),
+ relativeDataStr = cleanupOutData(data, params);
+
+ // Convert to absolute coordinates if it's shorter or forceAbsolutePath is true.
+ // v-20 -> V0
+ // Don't convert if it fits following previous command.
+ // l20 30-10-50 instead of l20 30L20 30
+ if (
+ params.forceAbsolutePath ||
+ (absoluteDataStr.length < relativeDataStr.length &&
+ !(
+ params.negativeExtraSpace &&
+ command == prev.command &&
+ prev.command.charCodeAt(0) > 96 &&
+ absoluteDataStr.length == relativeDataStr.length - 1 &&
+ (data[0] < 0 ||
+ // @ts-ignore
+ (/^0\./.test(data[0]) && prev.args[prev.args.length - 1] % 1))
+ ))
+ ) {
+ // @ts-ignore
+ item.command = command.toUpperCase();
+ item.args = adata;
+ }
+
+ prev = item;
+
+ return true;
+ });
+
+ return path;
+}
+
+/**
+ * Checks if curve is convex. Control points of such a curve must form
+ * a convex quadrilateral with diagonals crosspoint inside of it.
+ *
+ * @type {(data: number[]) => boolean}
+ */
+function isConvex(data) {
+ var center = getIntersection([
+ 0,
+ 0,
+ data[2],
+ data[3],
+ data[0],
+ data[1],
+ data[4],
+ data[5],
+ ]);
+
+ return (
+ center != null &&
+ data[2] < center[0] == center[0] < 0 &&
+ data[3] < center[1] == center[1] < 0 &&
+ data[4] < center[0] == center[0] < data[0] &&
+ data[5] < center[1] == center[1] < data[1]
+ );
+}
+
+/**
+ * Computes lines equations by two points and returns their intersection point.
+ *
+ * @type {(coords: number[]) => undefined | Point}
+ */
+function getIntersection(coords) {
+ // Prev line equation parameters.
+ var a1 = coords[1] - coords[3], // y1 - y2
+ b1 = coords[2] - coords[0], // x2 - x1
+ c1 = coords[0] * coords[3] - coords[2] * coords[1], // x1 * y2 - x2 * y1
+ // Next line equation parameters
+ a2 = coords[5] - coords[7], // y1 - y2
+ b2 = coords[6] - coords[4], // x2 - x1
+ c2 = coords[4] * coords[7] - coords[5] * coords[6], // x1 * y2 - x2 * y1
+ denom = a1 * b2 - a2 * b1;
+
+ if (!denom) return; // parallel lines havn't an intersection
+
+ /**
+ * @type {Point}
+ */
+ var cross = [(b1 * c2 - b2 * c1) / denom, (a1 * c2 - a2 * c1) / -denom];
+ if (
+ !isNaN(cross[0]) &&
+ !isNaN(cross[1]) &&
+ isFinite(cross[0]) &&
+ isFinite(cross[1])
+ ) {
+ return cross;
+ }
+}
+
+/**
+ * Decrease accuracy of floating-point numbers
+ * in path data keeping a specified number of decimals.
+ * Smart rounds values like 2.3491 to 2.35 instead of 2.349.
+ * Doesn't apply "smartness" if the number precision fits already.
+ *
+ * @type {(data: number[]) => number[]}
+ */
+function strongRound(data) {
+ for (var i = data.length; i-- > 0; ) {
+ // @ts-ignore
+ if (data[i].toFixed(precision) != data[i]) {
+ // @ts-ignore
+ var rounded = +data[i].toFixed(precision - 1);
+ data[i] =
+ // @ts-ignore
+ +Math.abs(rounded - data[i]).toFixed(precision + 1) >= error
+ ? // @ts-ignore
+ +data[i].toFixed(precision)
+ : rounded;
+ }
+ }
+ return data;
+}
+
+/**
+ * Simple rounding function if precision is 0.
+ *
+ * @type {(data: number[]) => number[]}
+ */
+function round(data) {
+ for (var i = data.length; i-- > 0; ) {
+ data[i] = Math.round(data[i]);
+ }
+ return data;
+}
+
+/**
+ * Checks if a curve is a straight line by measuring distance
+ * from middle points to the line formed by end points.
+ *
+ * @type {(data: number[]) => boolean}
+ */
+
+function isCurveStraightLine(data) {
+ // Get line equation a·x + b·y + c = 0 coefficients a, b (c = 0) by start and end points.
+ var i = data.length - 2,
+ a = -data[i + 1], // y1 − y2 (y1 = 0)
+ b = data[i], // x2 − x1 (x1 = 0)
+ d = 1 / (a * a + b * b); // same part for all points
+
+ if (i <= 1 || !isFinite(d)) return false; // curve that ends at start point isn't the case
+
+ // Distance from point (x0, y0) to the line is sqrt((c − a·x0 − b·y0)² / (a² + b²))
+ while ((i -= 2) >= 0) {
+ if (Math.sqrt(Math.pow(a * data[i] + b * data[i + 1], 2) * d) > error)
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Converts next curve from shorthand to full form using the current curve data.
+ *
+ * @type {(item: PathDataItem, data: number[]) => PathDataItem}
+ */
+
+function makeLonghand(item, data) {
+ switch (item.command) {
+ case 's':
+ item.command = 'c';
+ break;
+ case 't':
+ item.command = 'q';
+ break;
+ }
+ item.args.unshift(
+ data[data.length - 2] - data[data.length - 4],
+ data[data.length - 1] - data[data.length - 3]
+ );
+ return item;
+}
+
+/**
+ * Returns distance between two points
+ *
+ * @type {(point1: Point, point2: Point) => number}
+ */
+
+function getDistance(point1, point2) {
+ return Math.hypot(point1[0] - point2[0], point1[1] - point2[1]);
+}
+
+/**
+ * Returns coordinates of the curve point corresponding to the certain t
+ * a·(1 - t)³·p1 + b·(1 - t)²·t·p2 + c·(1 - t)·t²·p3 + d·t³·p4,
+ * where pN are control points and p1 is zero due to relative coordinates.
+ *
+ * @type {(curve: number[], t: number) => Point}
+ */
+
+function getCubicBezierPoint(curve, t) {
+ var sqrT = t * t,
+ cubT = sqrT * t,
+ mt = 1 - t,
+ sqrMt = mt * mt;
+
+ return [
+ 3 * sqrMt * t * curve[0] + 3 * mt * sqrT * curve[2] + cubT * curve[4],
+ 3 * sqrMt * t * curve[1] + 3 * mt * sqrT * curve[3] + cubT * curve[5],
+ ];
+}
+
+/**
+ * Finds circle by 3 points of the curve and checks if the curve fits the found circle.
+ *
+ * @type {(curve: number[]) => undefined | Circle}
+ */
+
+function findCircle(curve) {
+ var midPoint = getCubicBezierPoint(curve, 1 / 2),
+ m1 = [midPoint[0] / 2, midPoint[1] / 2],
+ m2 = [(midPoint[0] + curve[4]) / 2, (midPoint[1] + curve[5]) / 2],
+ center = getIntersection([
+ m1[0],
+ m1[1],
+ m1[0] + m1[1],
+ m1[1] - m1[0],
+ m2[0],
+ m2[1],
+ m2[0] + (m2[1] - midPoint[1]),
+ m2[1] - (m2[0] - midPoint[0]),
+ ]),
+ radius = center && getDistance([0, 0], center),
+ // @ts-ignore
+ tolerance = Math.min(arcThreshold * error, (arcTolerance * radius) / 100);
+
+ if (
+ center &&
+ // @ts-ignore
+ radius < 1e15 &&
+ [1 / 4, 3 / 4].every(function (point) {
+ return (
+ Math.abs(
+ // @ts-ignore
+ getDistance(getCubicBezierPoint(curve, point), center) - radius
+ ) <= tolerance
+ );
+ })
+ )
+ // @ts-ignore
+ return { center: center, radius: radius };
+}
+
+/**
+ * Checks if a curve fits the given circle.
+ *
+ * @type {(curve: number[], circle: Circle) => boolean}
+ */
+
+function isArc(curve, circle) {
+ var tolerance = Math.min(
+ arcThreshold * error,
+ (arcTolerance * circle.radius) / 100
+ );
+
+ return [0, 1 / 4, 1 / 2, 3 / 4, 1].every(function (point) {
+ return (
+ Math.abs(
+ getDistance(getCubicBezierPoint(curve, point), circle.center) -
+ circle.radius
+ ) <= tolerance
+ );
+ });
+}
+
+/**
+ * Checks if a previous curve fits the given circle.
+ *
+ * @type {(curve: number[], circle: Circle) => boolean}
+ */
+
+function isArcPrev(curve, circle) {
+ return isArc(curve, {
+ center: [circle.center[0] + curve[4], circle.center[1] + curve[5]],
+ radius: circle.radius,
+ });
+}
+
+/**
+ * Finds angle of a curve fitting the given arc.
+
+ * @type {(curve: number[], relCircle: Circle) => number}
+ */
+
+function findArcAngle(curve, relCircle) {
+ var x1 = -relCircle.center[0],
+ y1 = -relCircle.center[1],
+ x2 = curve[4] - relCircle.center[0],
+ y2 = curve[5] - relCircle.center[1];
+
+ return Math.acos(
+ (x1 * x2 + y1 * y2) / Math.sqrt((x1 * x1 + y1 * y1) * (x2 * x2 + y2 * y2))
+ );
+}
+
+/**
+ * Converts given path data to string.
+ *
+ * @type {(params: InternalParams, pathData: PathDataItem[]) => string}
+ */
+
+function data2Path(params, pathData) {
+ return pathData.reduce(function (pathString, item) {
+ var strData = '';
+ if (item.args) {
+ strData = cleanupOutData(roundData(item.args.slice()), params);
+ }
+ return pathString + item.command + strData;
+ }, '');
+}
diff --git a/node_modules/svgo/plugins/convertShapeToPath.js b/node_modules/svgo/plugins/convertShapeToPath.js
new file mode 100644
index 00000000..7705fa97
--- /dev/null
+++ b/node_modules/svgo/plugins/convertShapeToPath.js
@@ -0,0 +1,170 @@
+'use strict';
+
+/**
+ * @typedef {import('../lib/types').PathDataItem} PathDataItem
+ */
+
+const { stringifyPathData } = require('../lib/path.js');
+const { detachNodeFromParent } = require('../lib/xast.js');
+
+exports.name = 'convertShapeToPath';
+exports.description = 'converts basic shapes to more compact path form';
+
+const regNumber = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g;
+
+/**
+ * Converts basic shape to more compact path.
+ * It also allows further optimizations like
+ * combining paths with similar attributes.
+ *
+ * @see https://www.w3.org/TR/SVG11/shapes.html
+ *
+ * @author Lev Solntsev
+ *
+ * @type {import('./plugins-types').Plugin<'convertShapeToPath'>}
+ */
+exports.fn = (root, params) => {
+ const { convertArcs = false, floatPrecision: precision } = params;
+
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ // convert rect to path
+ if (
+ node.name === 'rect' &&
+ node.attributes.width != null &&
+ node.attributes.height != null &&
+ node.attributes.rx == null &&
+ node.attributes.ry == null
+ ) {
+ const x = Number(node.attributes.x || '0');
+ const y = Number(node.attributes.y || '0');
+ const width = Number(node.attributes.width);
+ const height = Number(node.attributes.height);
+ // Values like '100%' compute to NaN, thus running after
+ // cleanupNumericValues when 'px' units has already been removed.
+ // TODO: Calculate sizes from % and non-px units if possible.
+ if (Number.isNaN(x - y + width - height)) return;
+ /**
+ * @type {Array<PathDataItem>}
+ */
+ const pathData = [
+ { command: 'M', args: [x, y] },
+ { command: 'H', args: [x + width] },
+ { command: 'V', args: [y + height] },
+ { command: 'H', args: [x] },
+ { command: 'z', args: [] },
+ ];
+ node.name = 'path';
+ node.attributes.d = stringifyPathData({ pathData, precision });
+ delete node.attributes.x;
+ delete node.attributes.y;
+ delete node.attributes.width;
+ delete node.attributes.height;
+ }
+
+ // convert line to path
+ if (node.name === 'line') {
+ const x1 = Number(node.attributes.x1 || '0');
+ const y1 = Number(node.attributes.y1 || '0');
+ const x2 = Number(node.attributes.x2 || '0');
+ const y2 = Number(node.attributes.y2 || '0');
+ if (Number.isNaN(x1 - y1 + x2 - y2)) return;
+ /**
+ * @type {Array<PathDataItem>}
+ */
+ const pathData = [
+ { command: 'M', args: [x1, y1] },
+ { command: 'L', args: [x2, y2] },
+ ];
+ node.name = 'path';
+ node.attributes.d = stringifyPathData({ pathData, precision });
+ delete node.attributes.x1;
+ delete node.attributes.y1;
+ delete node.attributes.x2;
+ delete node.attributes.y2;
+ }
+
+ // convert polyline and polygon to path
+ if (
+ (node.name === 'polyline' || node.name === 'polygon') &&
+ node.attributes.points != null
+ ) {
+ const coords = (node.attributes.points.match(regNumber) || []).map(
+ Number
+ );
+ if (coords.length < 4) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+ /**
+ * @type {Array<PathDataItem>}
+ */
+ const pathData = [];
+ for (let i = 0; i < coords.length; i += 2) {
+ pathData.push({
+ command: i === 0 ? 'M' : 'L',
+ args: coords.slice(i, i + 2),
+ });
+ }
+ if (node.name === 'polygon') {
+ pathData.push({ command: 'z', args: [] });
+ }
+ node.name = 'path';
+ node.attributes.d = stringifyPathData({ pathData, precision });
+ delete node.attributes.points;
+ }
+
+ // optionally convert circle
+ if (node.name === 'circle' && convertArcs) {
+ const cx = Number(node.attributes.cx || '0');
+ const cy = Number(node.attributes.cy || '0');
+ const r = Number(node.attributes.r || '0');
+ if (Number.isNaN(cx - cy + r)) {
+ return;
+ }
+ /**
+ * @type {Array<PathDataItem>}
+ */
+ const pathData = [
+ { command: 'M', args: [cx, cy - r] },
+ { command: 'A', args: [r, r, 0, 1, 0, cx, cy + r] },
+ { command: 'A', args: [r, r, 0, 1, 0, cx, cy - r] },
+ { command: 'z', args: [] },
+ ];
+ node.name = 'path';
+ node.attributes.d = stringifyPathData({ pathData, precision });
+ delete node.attributes.cx;
+ delete node.attributes.cy;
+ delete node.attributes.r;
+ }
+
+ // optionally covert ellipse
+ if (node.name === 'ellipse' && convertArcs) {
+ const ecx = Number(node.attributes.cx || '0');
+ const ecy = Number(node.attributes.cy || '0');
+ const rx = Number(node.attributes.rx || '0');
+ const ry = Number(node.attributes.ry || '0');
+ if (Number.isNaN(ecx - ecy + rx - ry)) {
+ return;
+ }
+ /**
+ * @type {Array<PathDataItem>}
+ */
+ const pathData = [
+ { command: 'M', args: [ecx, ecy - ry] },
+ { command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy + ry] },
+ { command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy - ry] },
+ { command: 'z', args: [] },
+ ];
+ node.name = 'path';
+ node.attributes.d = stringifyPathData({ pathData, precision });
+ delete node.attributes.cx;
+ delete node.attributes.cy;
+ delete node.attributes.rx;
+ delete node.attributes.ry;
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/convertStyleToAttrs.js b/node_modules/svgo/plugins/convertStyleToAttrs.js
new file mode 100644
index 00000000..5d5107d6
--- /dev/null
+++ b/node_modules/svgo/plugins/convertStyleToAttrs.js
@@ -0,0 +1,137 @@
+'use strict';
+
+const { attrsGroups } = require('./_collections');
+
+exports.name = 'convertStyleToAttrs';
+exports.description = 'converts style to attributes';
+
+/**
+ * @type {(...args: string[]) => string}
+ */
+const g = (...args) => {
+ return '(?:' + args.join('|') + ')';
+};
+
+const stylingProps = attrsGroups.presentation;
+const rEscape = '\\\\(?:[0-9a-f]{1,6}\\s?|\\r\\n|.)'; // Like \" or \2051. Code points consume one space.
+const rAttr = '\\s*(' + g('[^:;\\\\]', rEscape) + '*?)\\s*'; // attribute name like ‘fill’
+const rSingleQuotes = "'(?:[^'\\n\\r\\\\]|" + rEscape + ")*?(?:'|$)"; // string in single quotes: 'smth'
+const rQuotes = '"(?:[^"\\n\\r\\\\]|' + rEscape + ')*?(?:"|$)'; // string in double quotes: "smth"
+const rQuotedString = new RegExp('^' + g(rSingleQuotes, rQuotes) + '$');
+// Parentheses, E.g.: url(data:image/png;base64,iVBO...).
+// ':' and ';' inside of it should be threated as is. (Just like in strings.)
+const rParenthesis =
+ '\\(' + g('[^\'"()\\\\]+', rEscape, rSingleQuotes, rQuotes) + '*?' + '\\)';
+// The value. It can have strings and parentheses (see above). Fallbacks to anything in case of unexpected input.
+const rValue =
+ '\\s*(' +
+ g(
+ '[^!\'"();\\\\]+?',
+ rEscape,
+ rSingleQuotes,
+ rQuotes,
+ rParenthesis,
+ '[^;]*?'
+ ) +
+ '*?' +
+ ')';
+// End of declaration. Spaces outside of capturing groups help to do natural trimming.
+const rDeclEnd = '\\s*(?:;\\s*|$)';
+// Important rule
+const rImportant = '(\\s*!important(?![-(\\w]))?';
+// Final RegExp to parse CSS declarations.
+const regDeclarationBlock = new RegExp(
+ rAttr + ':' + rValue + rImportant + rDeclEnd,
+ 'ig'
+);
+// Comments expression. Honors escape sequences and strings.
+const regStripComments = new RegExp(
+ g(rEscape, rSingleQuotes, rQuotes, '/\\*[^]*?\\*/'),
+ 'ig'
+);
+
+/**
+ * Convert style in attributes. Cleanups comments and illegal declarations (without colon) as a side effect.
+ *
+ * @example
+ * <g style="fill:#000; color: #fff;">
+ * ⬇
+ * <g fill="#000" color="#fff">
+ *
+ * @example
+ * <g style="fill:#000; color: #fff; -webkit-blah: blah">
+ * ⬇
+ * <g fill="#000" color="#fff" style="-webkit-blah: blah">
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'convertStyleToAttrs'>}
+ */
+exports.fn = (_root, params) => {
+ const { keepImportant = false } = params;
+ return {
+ element: {
+ enter: (node) => {
+ if (node.attributes.style != null) {
+ // ['opacity: 1', 'color: #000']
+ let styles = [];
+ /**
+ * @type {Record<string, string>}
+ */
+ const newAttributes = {};
+
+ // Strip CSS comments preserving escape sequences and strings.
+ const styleValue = node.attributes.style.replace(
+ regStripComments,
+ (match) => {
+ return match[0] == '/'
+ ? ''
+ : match[0] == '\\' && /[-g-z]/i.test(match[1])
+ ? match[1]
+ : match;
+ }
+ );
+
+ regDeclarationBlock.lastIndex = 0;
+ // eslint-disable-next-line no-cond-assign
+ for (var rule; (rule = regDeclarationBlock.exec(styleValue)); ) {
+ if (!keepImportant || !rule[3]) {
+ styles.push([rule[1], rule[2]]);
+ }
+ }
+
+ if (styles.length) {
+ styles = styles.filter(function (style) {
+ if (style[0]) {
+ var prop = style[0].toLowerCase(),
+ val = style[1];
+
+ if (rQuotedString.test(val)) {
+ val = val.slice(1, -1);
+ }
+
+ if (stylingProps.includes(prop)) {
+ newAttributes[prop] = val;
+
+ return false;
+ }
+ }
+
+ return true;
+ });
+
+ Object.assign(node.attributes, newAttributes);
+
+ if (styles.length) {
+ node.attributes.style = styles
+ .map((declaration) => declaration.join(':'))
+ .join(';');
+ } else {
+ delete node.attributes.style;
+ }
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/convertTransform.js b/node_modules/svgo/plugins/convertTransform.js
new file mode 100644
index 00000000..d90c3efd
--- /dev/null
+++ b/node_modules/svgo/plugins/convertTransform.js
@@ -0,0 +1,417 @@
+'use strict';
+
+/**
+ * @typedef {import('../lib/types').XastElement} XastElement
+ */
+
+const { cleanupOutData } = require('../lib/svgo/tools.js');
+const {
+ transform2js,
+ transformsMultiply,
+ matrixToTransform,
+} = require('./_transforms.js');
+
+exports.name = 'convertTransform';
+exports.description = 'collapses multiple transformations and optimizes it';
+
+/**
+ * Convert matrices to the short aliases,
+ * convert long translate, scale or rotate transform notations to the shorts ones,
+ * convert transforms to the matrices and multiply them all into one,
+ * remove useless transforms.
+ *
+ * @see https://www.w3.org/TR/SVG11/coords.html#TransformMatrixDefined
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'convertTransform'>}
+ */
+exports.fn = (_root, params) => {
+ const {
+ convertToShorts = true,
+ // degPrecision = 3, // transformPrecision (or matrix precision) - 2 by default
+ degPrecision,
+ floatPrecision = 3,
+ transformPrecision = 5,
+ matrixToTransform = true,
+ shortTranslate = true,
+ shortScale = true,
+ shortRotate = true,
+ removeUseless = true,
+ collapseIntoOne = true,
+ leadingZero = true,
+ negativeExtraSpace = false,
+ } = params;
+ const newParams = {
+ convertToShorts,
+ degPrecision,
+ floatPrecision,
+ transformPrecision,
+ matrixToTransform,
+ shortTranslate,
+ shortScale,
+ shortRotate,
+ removeUseless,
+ collapseIntoOne,
+ leadingZero,
+ negativeExtraSpace,
+ };
+ return {
+ element: {
+ enter: (node) => {
+ // transform
+ if (node.attributes.transform != null) {
+ convertTransform(node, 'transform', newParams);
+ }
+ // gradientTransform
+ if (node.attributes.gradientTransform != null) {
+ convertTransform(node, 'gradientTransform', newParams);
+ }
+ // patternTransform
+ if (node.attributes.patternTransform != null) {
+ convertTransform(node, 'patternTransform', newParams);
+ }
+ },
+ },
+ };
+};
+
+/**
+ * @typedef {{
+ * convertToShorts: boolean,
+ * degPrecision?: number,
+ * floatPrecision: number,
+ * transformPrecision: number,
+ * matrixToTransform: boolean,
+ * shortTranslate: boolean,
+ * shortScale: boolean,
+ * shortRotate: boolean,
+ * removeUseless: boolean,
+ * collapseIntoOne: boolean,
+ * leadingZero: boolean,
+ * negativeExtraSpace: boolean,
+ * }} TransformParams
+ */
+
+/**
+ * @typedef {{ name: string, data: Array<number> }} TransformItem
+ */
+
+/**
+ * Main function.
+ *
+ * @type {(item: XastElement, attrName: string, params: TransformParams) => void}
+ */
+const convertTransform = (item, attrName, params) => {
+ let data = transform2js(item.attributes[attrName]);
+ params = definePrecision(data, params);
+
+ if (params.collapseIntoOne && data.length > 1) {
+ data = [transformsMultiply(data)];
+ }
+
+ if (params.convertToShorts) {
+ data = convertToShorts(data, params);
+ } else {
+ data.forEach((item) => roundTransform(item, params));
+ }
+
+ if (params.removeUseless) {
+ data = removeUseless(data);
+ }
+
+ if (data.length) {
+ item.attributes[attrName] = js2transform(data, params);
+ } else {
+ delete item.attributes[attrName];
+ }
+};
+
+/**
+ * Defines precision to work with certain parts.
+ * transformPrecision - for scale and four first matrix parameters (needs a better precision due to multiplying),
+ * floatPrecision - for translate including two last matrix and rotate parameters,
+ * degPrecision - for rotate and skew. By default it's equal to (rougly)
+ * transformPrecision - 2 or floatPrecision whichever is lower. Can be set in params.
+ *
+ * @type {(data: Array<TransformItem>, params: TransformParams) => TransformParams}
+ *
+ * clone params so it don't affect other elements transformations.
+ */
+const definePrecision = (data, { ...newParams }) => {
+ const matrixData = [];
+ for (const item of data) {
+ if (item.name == 'matrix') {
+ matrixData.push(...item.data.slice(0, 4));
+ }
+ }
+ let significantDigits = newParams.transformPrecision;
+ // Limit transform precision with matrix one. Calculating with larger precision doesn't add any value.
+ if (matrixData.length) {
+ newParams.transformPrecision = Math.min(
+ newParams.transformPrecision,
+ Math.max.apply(Math, matrixData.map(floatDigits)) ||
+ newParams.transformPrecision
+ );
+ significantDigits = Math.max.apply(
+ Math,
+ matrixData.map(
+ (n) => n.toString().replace(/\D+/g, '').length // Number of digits in a number. 123.45 → 5
+ )
+ );
+ }
+ // No sense in angle precision more then number of significant digits in matrix.
+ if (newParams.degPrecision == null) {
+ newParams.degPrecision = Math.max(
+ 0,
+ Math.min(newParams.floatPrecision, significantDigits - 2)
+ );
+ }
+ return newParams;
+};
+
+/**
+ * @type {(data: Array<number>, params: TransformParams) => Array<number>}
+ */
+const degRound = (data, params) => {
+ if (
+ params.degPrecision != null &&
+ params.degPrecision >= 1 &&
+ params.floatPrecision < 20
+ ) {
+ return smartRound(params.degPrecision, data);
+ } else {
+ return round(data);
+ }
+};
+/**
+ * @type {(data: Array<number>, params: TransformParams) => Array<number>}
+ */
+const floatRound = (data, params) => {
+ if (params.floatPrecision >= 1 && params.floatPrecision < 20) {
+ return smartRound(params.floatPrecision, data);
+ } else {
+ return round(data);
+ }
+};
+
+/**
+ * @type {(data: Array<number>, params: TransformParams) => Array<number>}
+ */
+const transformRound = (data, params) => {
+ if (params.transformPrecision >= 1 && params.floatPrecision < 20) {
+ return smartRound(params.transformPrecision, data);
+ } else {
+ return round(data);
+ }
+};
+
+/**
+ * Returns number of digits after the point. 0.125 → 3
+ *
+ * @type {(n: number) => number}
+ */
+const floatDigits = (n) => {
+ const str = n.toString();
+ return str.slice(str.indexOf('.')).length - 1;
+};
+
+/**
+ * Convert transforms to the shorthand alternatives.
+ *
+ * @type {(transforms: Array<TransformItem>, params: TransformParams) => Array<TransformItem>}
+ */
+const convertToShorts = (transforms, params) => {
+ for (var i = 0; i < transforms.length; i++) {
+ var transform = transforms[i];
+
+ // convert matrix to the short aliases
+ if (params.matrixToTransform && transform.name === 'matrix') {
+ var decomposed = matrixToTransform(transform, params);
+ if (
+ js2transform(decomposed, params).length <=
+ js2transform([transform], params).length
+ ) {
+ transforms.splice(i, 1, ...decomposed);
+ }
+ transform = transforms[i];
+ }
+
+ // fixed-point numbers
+ // 12.754997 → 12.755
+ roundTransform(transform, params);
+
+ // convert long translate transform notation to the shorts one
+ // translate(10 0) → translate(10)
+ if (
+ params.shortTranslate &&
+ transform.name === 'translate' &&
+ transform.data.length === 2 &&
+ !transform.data[1]
+ ) {
+ transform.data.pop();
+ }
+
+ // convert long scale transform notation to the shorts one
+ // scale(2 2) → scale(2)
+ if (
+ params.shortScale &&
+ transform.name === 'scale' &&
+ transform.data.length === 2 &&
+ transform.data[0] === transform.data[1]
+ ) {
+ transform.data.pop();
+ }
+
+ // convert long rotate transform notation to the short one
+ // translate(cx cy) rotate(a) translate(-cx -cy) → rotate(a cx cy)
+ if (
+ params.shortRotate &&
+ transforms[i - 2] &&
+ transforms[i - 2].name === 'translate' &&
+ transforms[i - 1].name === 'rotate' &&
+ transforms[i].name === 'translate' &&
+ transforms[i - 2].data[0] === -transforms[i].data[0] &&
+ transforms[i - 2].data[1] === -transforms[i].data[1]
+ ) {
+ transforms.splice(i - 2, 3, {
+ name: 'rotate',
+ data: [
+ transforms[i - 1].data[0],
+ transforms[i - 2].data[0],
+ transforms[i - 2].data[1],
+ ],
+ });
+
+ // splice compensation
+ i -= 2;
+ }
+ }
+
+ return transforms;
+};
+
+/**
+ * Remove useless transforms.
+ *
+ * @type {(trasforms: Array<TransformItem>) => Array<TransformItem>}
+ */
+const removeUseless = (transforms) => {
+ return transforms.filter((transform) => {
+ // translate(0), rotate(0[, cx, cy]), skewX(0), skewY(0)
+ if (
+ (['translate', 'rotate', 'skewX', 'skewY'].indexOf(transform.name) > -1 &&
+ (transform.data.length == 1 || transform.name == 'rotate') &&
+ !transform.data[0]) ||
+ // translate(0, 0)
+ (transform.name == 'translate' &&
+ !transform.data[0] &&
+ !transform.data[1]) ||
+ // scale(1)
+ (transform.name == 'scale' &&
+ transform.data[0] == 1 &&
+ (transform.data.length < 2 || transform.data[1] == 1)) ||
+ // matrix(1 0 0 1 0 0)
+ (transform.name == 'matrix' &&
+ transform.data[0] == 1 &&
+ transform.data[3] == 1 &&
+ !(
+ transform.data[1] ||
+ transform.data[2] ||
+ transform.data[4] ||
+ transform.data[5]
+ ))
+ ) {
+ return false;
+ }
+
+ return true;
+ });
+};
+
+/**
+ * Convert transforms JS representation to string.
+ *
+ * @type {(transformJS: Array<TransformItem>, params: TransformParams) => string}
+ */
+const js2transform = (transformJS, params) => {
+ var transformString = '';
+
+ // collect output value string
+ transformJS.forEach((transform) => {
+ roundTransform(transform, params);
+ transformString +=
+ (transformString && ' ') +
+ transform.name +
+ '(' +
+ cleanupOutData(transform.data, params) +
+ ')';
+ });
+
+ return transformString;
+};
+
+/**
+ * @type {(transform: TransformItem, params: TransformParams) => TransformItem}
+ */
+const roundTransform = (transform, params) => {
+ switch (transform.name) {
+ case 'translate':
+ transform.data = floatRound(transform.data, params);
+ break;
+ case 'rotate':
+ transform.data = [
+ ...degRound(transform.data.slice(0, 1), params),
+ ...floatRound(transform.data.slice(1), params),
+ ];
+ break;
+ case 'skewX':
+ case 'skewY':
+ transform.data = degRound(transform.data, params);
+ break;
+ case 'scale':
+ transform.data = transformRound(transform.data, params);
+ break;
+ case 'matrix':
+ transform.data = [
+ ...transformRound(transform.data.slice(0, 4), params),
+ ...floatRound(transform.data.slice(4), params),
+ ];
+ break;
+ }
+ return transform;
+};
+
+/**
+ * Rounds numbers in array.
+ *
+ * @type {(data: Array<number>) => Array<number>}
+ */
+const round = (data) => {
+ return data.map(Math.round);
+};
+
+/**
+ * Decrease accuracy of floating-point numbers
+ * in transforms keeping a specified number of decimals.
+ * Smart rounds values like 2.349 to 2.35.
+ *
+ * @type {(precision: number, data: Array<number>) => Array<number>}
+ */
+const smartRound = (precision, data) => {
+ for (
+ var i = data.length,
+ tolerance = +Math.pow(0.1, precision).toFixed(precision);
+ i--;
+
+ ) {
+ if (Number(data[i].toFixed(precision)) !== data[i]) {
+ var rounded = +data[i].toFixed(precision - 1);
+ data[i] =
+ +Math.abs(rounded - data[i]).toFixed(precision + 1) >= tolerance
+ ? +data[i].toFixed(precision)
+ : rounded;
+ }
+ }
+ return data;
+};
diff --git a/node_modules/svgo/plugins/inlineStyles.js b/node_modules/svgo/plugins/inlineStyles.js
new file mode 100644
index 00000000..e931b5f6
--- /dev/null
+++ b/node_modules/svgo/plugins/inlineStyles.js
@@ -0,0 +1,386 @@
+'use strict';
+
+/**
+ * @typedef {import('../lib/types').Specificity} Specificity
+ * @typedef {import('../lib/types').XastElement} XastElement
+ * @typedef {import('../lib/types').XastParent} XastParent
+ */
+
+const csstree = require('css-tree');
+const {
+ // @ts-ignore not defined in @types/csso
+ syntax: { specificity },
+} = require('csso');
+const {
+ visitSkip,
+ querySelectorAll,
+ detachNodeFromParent,
+} = require('../lib/xast.js');
+
+exports.name = 'inlineStyles';
+exports.description = 'inline styles (additional options)';
+
+/**
+ * Compares two selector specificities.
+ * extracted from https://github.com/keeganstreet/specificity/blob/main/specificity.js#L211
+ *
+ * @type {(a: Specificity, b: Specificity) => number}
+ */
+const compareSpecificity = (a, b) => {
+ for (var i = 0; i < 4; i += 1) {
+ if (a[i] < b[i]) {
+ return -1;
+ } else if (a[i] > b[i]) {
+ return 1;
+ }
+ }
+ return 0;
+};
+
+/**
+ * @type {(value: any) => any}
+ */
+const toAny = (value) => value;
+
+/**
+ * Moves + merges styles from style elements to element styles
+ *
+ * Options
+ * onlyMatchedOnce (default: true)
+ * inline only selectors that match once
+ *
+ * removeMatchedSelectors (default: true)
+ * clean up matched selectors,
+ * leave selectors that hadn't matched
+ *
+ * useMqs (default: ['', 'screen'])
+ * what media queries to be used
+ * empty string element for styles outside media queries
+ *
+ * usePseudos (default: [''])
+ * what pseudo-classes/-elements to be used
+ * empty string element for all non-pseudo-classes and/or -elements
+ *
+ * @author strarsis <strarsis@gmail.com>
+ *
+ * @type {import('./plugins-types').Plugin<'inlineStyles'>}
+ */
+exports.fn = (root, params) => {
+ const {
+ onlyMatchedOnce = true,
+ removeMatchedSelectors = true,
+ useMqs = ['', 'screen'],
+ usePseudos = [''],
+ } = params;
+
+ /**
+ * @type {Array<{ node: XastElement, parentNode: XastParent, cssAst: csstree.StyleSheet }>}
+ */
+ const styles = [];
+ /**
+ * @type {Array<{
+ * node: csstree.Selector,
+ * item: csstree.ListItem<csstree.CssNode>,
+ * rule: csstree.Rule,
+ * matchedElements?: Array<XastElement>
+ * }>}
+ */
+ let selectors = [];
+
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ // skip <foreignObject /> content
+ if (node.name === 'foreignObject') {
+ return visitSkip;
+ }
+ // collect only non-empty <style /> elements
+ if (node.name !== 'style' || node.children.length === 0) {
+ return;
+ }
+ // values other than the empty string or text/css are not used
+ if (
+ node.attributes.type != null &&
+ node.attributes.type !== '' &&
+ node.attributes.type !== 'text/css'
+ ) {
+ return;
+ }
+ // parse css in style element
+ let cssText = '';
+ for (const child of node.children) {
+ if (child.type === 'text' || child.type === 'cdata') {
+ cssText += child.value;
+ }
+ }
+ /**
+ * @type {null | csstree.CssNode}
+ */
+ let cssAst = null;
+ try {
+ cssAst = csstree.parse(cssText, {
+ parseValue: false,
+ parseCustomProperty: false,
+ });
+ } catch {
+ return;
+ }
+ if (cssAst.type === 'StyleSheet') {
+ styles.push({ node, parentNode, cssAst });
+ }
+
+ // collect selectors
+ csstree.walk(cssAst, {
+ visit: 'Selector',
+ enter(node, item) {
+ const atrule = this.atrule;
+ const rule = this.rule;
+ if (rule == null) {
+ return;
+ }
+
+ // skip media queries not included into useMqs param
+ let mq = '';
+ if (atrule != null) {
+ mq = atrule.name;
+ if (atrule.prelude != null) {
+ mq += ` ${csstree.generate(atrule.prelude)}`;
+ }
+ }
+ if (useMqs.includes(mq) === false) {
+ return;
+ }
+
+ /**
+ * @type {Array<{
+ * item: csstree.ListItem<csstree.CssNode>,
+ * list: csstree.List<csstree.CssNode>
+ * }>}
+ */
+ const pseudos = [];
+ if (node.type === 'Selector') {
+ node.children.forEach((childNode, childItem, childList) => {
+ if (
+ childNode.type === 'PseudoClassSelector' ||
+ childNode.type === 'PseudoElementSelector'
+ ) {
+ pseudos.push({ item: childItem, list: childList });
+ }
+ });
+ }
+
+ // skip pseudo classes and pseudo elements not includes into usePseudos param
+ const pseudoSelectors = csstree.generate({
+ type: 'Selector',
+ children: new csstree.List().fromArray(
+ pseudos.map((pseudo) => pseudo.item.data)
+ ),
+ });
+ if (usePseudos.includes(pseudoSelectors) === false) {
+ return;
+ }
+
+ // remove pseudo classes and elements to allow querySelector match elements
+ // TODO this is not very accurate since some pseudo classes like first-child
+ // are used for selection
+ for (const pseudo of pseudos) {
+ pseudo.list.remove(pseudo.item);
+ }
+
+ selectors.push({ node, item, rule });
+ },
+ });
+ },
+ },
+
+ root: {
+ exit: () => {
+ if (styles.length === 0) {
+ return;
+ }
+ // stable sort selectors
+ const sortedSelectors = [...selectors]
+ .sort((a, b) => {
+ const aSpecificity = specificity(a.item.data);
+ const bSpecificity = specificity(b.item.data);
+ return compareSpecificity(aSpecificity, bSpecificity);
+ })
+ .reverse();
+
+ for (const selector of sortedSelectors) {
+ // match selectors
+ const selectorText = csstree.generate(selector.item.data);
+ /**
+ * @type {Array<XastElement>}
+ */
+ const matchedElements = [];
+ try {
+ for (const node of querySelectorAll(root, selectorText)) {
+ if (node.type === 'element') {
+ matchedElements.push(node);
+ }
+ }
+ } catch (selectError) {
+ continue;
+ }
+ // nothing selected
+ if (matchedElements.length === 0) {
+ continue;
+ }
+
+ // apply styles to matched elements
+ // skip selectors that match more than once if option onlyMatchedOnce is enabled
+ if (onlyMatchedOnce && matchedElements.length > 1) {
+ continue;
+ }
+
+ // apply <style/> to matched elements
+ for (const selectedEl of matchedElements) {
+ const styleDeclarationList = csstree.parse(
+ selectedEl.attributes.style == null
+ ? ''
+ : selectedEl.attributes.style,
+ {
+ context: 'declarationList',
+ parseValue: false,
+ }
+ );
+ if (styleDeclarationList.type !== 'DeclarationList') {
+ continue;
+ }
+ const styleDeclarationItems = new Map();
+ csstree.walk(styleDeclarationList, {
+ visit: 'Declaration',
+ enter(node, item) {
+ styleDeclarationItems.set(node.property, item);
+ },
+ });
+ // merge declarations
+ csstree.walk(selector.rule, {
+ visit: 'Declaration',
+ enter(ruleDeclaration) {
+ // existing inline styles have higher priority
+ // no inline styles, external styles, external styles used
+ // inline styles, external styles same priority as inline styles, inline styles used
+ // inline styles, external styles higher priority than inline styles, external styles used
+ const matchedItem = styleDeclarationItems.get(
+ ruleDeclaration.property
+ );
+ const ruleDeclarationItem =
+ styleDeclarationList.children.createItem(ruleDeclaration);
+ if (matchedItem == null) {
+ styleDeclarationList.children.append(ruleDeclarationItem);
+ } else if (
+ matchedItem.data.important !== true &&
+ ruleDeclaration.important === true
+ ) {
+ styleDeclarationList.children.replace(
+ matchedItem,
+ ruleDeclarationItem
+ );
+ styleDeclarationItems.set(
+ ruleDeclaration.property,
+ ruleDeclarationItem
+ );
+ }
+ },
+ });
+ selectedEl.attributes.style =
+ csstree.generate(styleDeclarationList);
+ }
+
+ if (
+ removeMatchedSelectors &&
+ matchedElements.length !== 0 &&
+ selector.rule.prelude.type === 'SelectorList'
+ ) {
+ // clean up matching simple selectors if option removeMatchedSelectors is enabled
+ selector.rule.prelude.children.remove(selector.item);
+ }
+ selector.matchedElements = matchedElements;
+ }
+
+ // no further processing required
+ if (removeMatchedSelectors === false) {
+ return;
+ }
+
+ // clean up matched class + ID attribute values
+ for (const selector of sortedSelectors) {
+ if (selector.matchedElements == null) {
+ continue;
+ }
+
+ if (onlyMatchedOnce && selector.matchedElements.length > 1) {
+ // skip selectors that match more than once if option onlyMatchedOnce is enabled
+ continue;
+ }
+
+ for (const selectedEl of selector.matchedElements) {
+ // class
+ const classList = new Set(
+ selectedEl.attributes.class == null
+ ? null
+ : selectedEl.attributes.class.split(' ')
+ );
+ /**
+ * csstree v2 changed this type
+ * @type {csstree.CssNode}
+ */
+ const firstSubSelector = toAny(selector.node.children.first);
+ if (
+ firstSubSelector != null &&
+ firstSubSelector.type === 'ClassSelector'
+ ) {
+ classList.delete(firstSubSelector.name);
+ }
+ if (classList.size === 0) {
+ delete selectedEl.attributes.class;
+ } else {
+ selectedEl.attributes.class = Array.from(classList).join(' ');
+ }
+
+ // ID
+ if (
+ firstSubSelector != null &&
+ firstSubSelector.type === 'IdSelector'
+ ) {
+ if (selectedEl.attributes.id === firstSubSelector.name) {
+ delete selectedEl.attributes.id;
+ }
+ }
+ }
+ }
+
+ for (const style of styles) {
+ csstree.walk(style.cssAst, {
+ visit: 'Rule',
+ enter: function (node, item, list) {
+ // clean up <style/> rulesets without any css selectors left
+ if (
+ node.type === 'Rule' &&
+ node.prelude.type === 'SelectorList' &&
+ // csstree v2 changed this type
+ toAny(node.prelude.children.isEmpty)
+ ) {
+ list.remove(item);
+ }
+ },
+ });
+
+ // csstree v2 changed this type
+ if (toAny(style.cssAst.children.isEmpty)) {
+ // remove emtpy style element
+ detachNodeFromParent(style.node, style.parentNode);
+ } else {
+ // update style element if any styles left
+ const firstChild = style.node.children[0];
+ if (firstChild.type === 'text' || firstChild.type === 'cdata') {
+ firstChild.value = csstree.generate(style.cssAst);
+ }
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/mergePaths.js b/node_modules/svgo/plugins/mergePaths.js
new file mode 100644
index 00000000..05c7ac70
--- /dev/null
+++ b/node_modules/svgo/plugins/mergePaths.js
@@ -0,0 +1,98 @@
+'use strict';
+
+const { detachNodeFromParent } = require('../lib/xast.js');
+const { collectStylesheet, computeStyle } = require('../lib/style.js');
+const { path2js, js2path, intersects } = require('./_path.js');
+
+exports.name = 'mergePaths';
+exports.description = 'merges multiple paths in one if possible';
+
+/**
+ * Merge multiple Paths into one.
+ *
+ * @author Kir Belevich, Lev Solntsev
+ *
+ * @type {import('./plugins-types').Plugin<'mergePaths'>}
+ */
+exports.fn = (root, params) => {
+ const {
+ force = false,
+ floatPrecision,
+ noSpaceAfterFlags = false, // a20 60 45 0 1 30 20 → a20 60 45 0130 20
+ } = params;
+ const stylesheet = collectStylesheet(root);
+
+ return {
+ element: {
+ enter: (node) => {
+ let prevChild = null;
+
+ for (const child of node.children) {
+ // skip if previous element is not path or contains animation elements
+ if (
+ prevChild == null ||
+ prevChild.type !== 'element' ||
+ prevChild.name !== 'path' ||
+ prevChild.children.length !== 0 ||
+ prevChild.attributes.d == null
+ ) {
+ prevChild = child;
+ continue;
+ }
+
+ // skip if element is not path or contains animation elements
+ if (
+ child.type !== 'element' ||
+ child.name !== 'path' ||
+ child.children.length !== 0 ||
+ child.attributes.d == null
+ ) {
+ prevChild = child;
+ continue;
+ }
+
+ // preserve paths with markers
+ const computedStyle = computeStyle(stylesheet, child);
+ if (
+ computedStyle['marker-start'] ||
+ computedStyle['marker-mid'] ||
+ computedStyle['marker-end']
+ ) {
+ prevChild = child;
+ continue;
+ }
+
+ const prevChildAttrs = Object.keys(prevChild.attributes);
+ const childAttrs = Object.keys(child.attributes);
+ let attributesAreEqual = prevChildAttrs.length === childAttrs.length;
+ for (const name of childAttrs) {
+ if (name !== 'd') {
+ if (
+ prevChild.attributes[name] == null ||
+ prevChild.attributes[name] !== child.attributes[name]
+ ) {
+ attributesAreEqual = false;
+ }
+ }
+ }
+ const prevPathJS = path2js(prevChild);
+ const curPathJS = path2js(child);
+
+ if (
+ attributesAreEqual &&
+ (force || !intersects(prevPathJS, curPathJS))
+ ) {
+ js2path(prevChild, prevPathJS.concat(curPathJS), {
+ floatPrecision,
+ noSpaceAfterFlags,
+ });
+ detachNodeFromParent(child, node);
+ continue;
+ }
+
+ prevChild = child;
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/mergeStyles.js b/node_modules/svgo/plugins/mergeStyles.js
new file mode 100644
index 00000000..1cc32015
--- /dev/null
+++ b/node_modules/svgo/plugins/mergeStyles.js
@@ -0,0 +1,98 @@
+'use strict';
+
+/**
+ * @typedef {import('../lib/types').XastElement} XastElement
+ * @typedef {import('../lib/types').XastChild} XastChild
+ */
+
+const { visitSkip, detachNodeFromParent } = require('../lib/xast.js');
+
+exports.name = 'mergeStyles';
+exports.description = 'merge multiple style elements into one';
+
+/**
+ * Merge multiple style elements into one.
+ *
+ * @author strarsis <strarsis@gmail.com>
+ *
+ * @type {import('./plugins-types').Plugin<'mergeStyles'>}
+ */
+exports.fn = () => {
+ /**
+ * @type {null | XastElement}
+ */
+ let firstStyleElement = null;
+ let collectedStyles = '';
+ /**
+ * @type {'text' | 'cdata'}
+ */
+ let styleContentType = 'text';
+
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ // skip <foreignObject> content
+ if (node.name === 'foreignObject') {
+ return visitSkip;
+ }
+
+ // collect style elements
+ if (node.name !== 'style') {
+ return;
+ }
+
+ // skip <style> with invalid type attribute
+ if (
+ node.attributes.type != null &&
+ node.attributes.type !== '' &&
+ node.attributes.type !== 'text/css'
+ ) {
+ return;
+ }
+
+ // extract style element content
+ let css = '';
+ for (const child of node.children) {
+ if (child.type === 'text') {
+ css += child.value;
+ }
+ if (child.type === 'cdata') {
+ styleContentType = 'cdata';
+ css += child.value;
+ }
+ }
+
+ // remove empty style elements
+ if (css.trim().length === 0) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+
+ // collect css and wrap with media query if present in attribute
+ if (node.attributes.media == null) {
+ collectedStyles += css;
+ } else {
+ collectedStyles += `@media ${node.attributes.media}{${css}}`;
+ delete node.attributes.media;
+ }
+
+ // combine collected styles in the first style element
+ if (firstStyleElement == null) {
+ firstStyleElement = node;
+ } else {
+ detachNodeFromParent(node, parentNode);
+ /**
+ * @type {XastChild}
+ */
+ const child = { type: styleContentType, value: collectedStyles };
+ // TODO remove legacy parentNode in v4
+ Object.defineProperty(child, 'parentNode', {
+ writable: true,
+ value: firstStyleElement,
+ });
+ firstStyleElement.children = [child];
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/minifyStyles.js b/node_modules/svgo/plugins/minifyStyles.js
new file mode 100644
index 00000000..a10b8d20
--- /dev/null
+++ b/node_modules/svgo/plugins/minifyStyles.js
@@ -0,0 +1,139 @@
+'use strict';
+
+/**
+ * @typedef {import('../lib/types').XastElement} XastElement
+ */
+
+const csso = require('csso');
+
+exports.name = 'minifyStyles';
+exports.description =
+ 'minifies styles and removes unused styles based on usage data';
+
+/**
+ * Minifies styles (<style> element + style attribute) using CSSO
+ *
+ * @author strarsis <strarsis@gmail.com>
+ *
+ * @type {import('./plugins-types').Plugin<'minifyStyles'>}
+ */
+exports.fn = (_root, { usage, ...params }) => {
+ let enableTagsUsage = true;
+ let enableIdsUsage = true;
+ let enableClassesUsage = true;
+ // force to use usage data even if it unsafe (document contains <script> or on* attributes)
+ let forceUsageDeoptimized = false;
+ if (typeof usage === 'boolean') {
+ enableTagsUsage = usage;
+ enableIdsUsage = usage;
+ enableClassesUsage = usage;
+ } else if (usage) {
+ enableTagsUsage = usage.tags == null ? true : usage.tags;
+ enableIdsUsage = usage.ids == null ? true : usage.ids;
+ enableClassesUsage = usage.classes == null ? true : usage.classes;
+ forceUsageDeoptimized = usage.force == null ? false : usage.force;
+ }
+ /**
+ * @type {Array<XastElement>}
+ */
+ const styleElements = [];
+ /**
+ * @type {Array<XastElement>}
+ */
+ const elementsWithStyleAttributes = [];
+ let deoptimized = false;
+ /**
+ * @type {Set<string>}
+ */
+ const tagsUsage = new Set();
+ /**
+ * @type {Set<string>}
+ */
+ const idsUsage = new Set();
+ /**
+ * @type {Set<string>}
+ */
+ const classesUsage = new Set();
+
+ return {
+ element: {
+ enter: (node) => {
+ // detect deoptimisations
+ if (node.name === 'script') {
+ deoptimized = true;
+ }
+ for (const name of Object.keys(node.attributes)) {
+ if (name.startsWith('on')) {
+ deoptimized = true;
+ }
+ }
+ // collect tags, ids and classes usage
+ tagsUsage.add(node.name);
+ if (node.attributes.id != null) {
+ idsUsage.add(node.attributes.id);
+ }
+ if (node.attributes.class != null) {
+ for (const className of node.attributes.class.split(/\s+/)) {
+ classesUsage.add(className);
+ }
+ }
+ // collect style elements or elements with style attribute
+ if (node.name === 'style' && node.children.length !== 0) {
+ styleElements.push(node);
+ } else if (node.attributes.style != null) {
+ elementsWithStyleAttributes.push(node);
+ }
+ },
+ },
+
+ root: {
+ exit: () => {
+ /**
+ * @type {csso.Usage}
+ */
+ const cssoUsage = {};
+ if (deoptimized === false || forceUsageDeoptimized === true) {
+ if (enableTagsUsage && tagsUsage.size !== 0) {
+ cssoUsage.tags = Array.from(tagsUsage);
+ }
+ if (enableIdsUsage && idsUsage.size !== 0) {
+ cssoUsage.ids = Array.from(idsUsage);
+ }
+ if (enableClassesUsage && classesUsage.size !== 0) {
+ cssoUsage.classes = Array.from(classesUsage);
+ }
+ }
+ // minify style elements
+ for (const node of styleElements) {
+ if (
+ node.children[0].type === 'text' ||
+ node.children[0].type === 'cdata'
+ ) {
+ const cssText = node.children[0].value;
+ const minified = csso.minify(cssText, {
+ ...params,
+ usage: cssoUsage,
+ }).css;
+ // preserve cdata if necessary
+ // TODO split cdata -> text optimisation into separate plugin
+ if (cssText.indexOf('>') >= 0 || cssText.indexOf('<') >= 0) {
+ node.children[0].type = 'cdata';
+ node.children[0].value = minified;
+ } else {
+ node.children[0].type = 'text';
+ node.children[0].value = minified;
+ }
+ }
+ }
+ // minify style attributes
+ for (const node of elementsWithStyleAttributes) {
+ // style attribute
+ const elemStyle = node.attributes.style;
+ node.attributes.style = csso.minifyBlock(elemStyle, {
+ ...params,
+ }).css;
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/moveElemsAttrsToGroup.js b/node_modules/svgo/plugins/moveElemsAttrsToGroup.js
new file mode 100644
index 00000000..5f46e632
--- /dev/null
+++ b/node_modules/svgo/plugins/moveElemsAttrsToGroup.js
@@ -0,0 +1,128 @@
+'use strict';
+
+const { visit } = require('../lib/xast.js');
+const { inheritableAttrs, pathElems } = require('./_collections.js');
+
+exports.name = 'moveElemsAttrsToGroup';
+exports.description = 'Move common attributes of group children to the group';
+
+/**
+ * Move common attributes of group children to the group
+ *
+ * @example
+ * <g attr1="val1">
+ * <g attr2="val2">
+ * text
+ * </g>
+ * <circle attr2="val2" attr3="val3"/>
+ * </g>
+ * ⬇
+ * <g attr1="val1" attr2="val2">
+ * <g>
+ * text
+ * </g>
+ * <circle attr3="val3"/>
+ * </g>
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'moveElemsAttrsToGroup'>}
+ */
+exports.fn = (root) => {
+ // find if any style element is present
+ let deoptimizedWithStyles = false;
+ visit(root, {
+ element: {
+ enter: (node) => {
+ if (node.name === 'style') {
+ deoptimizedWithStyles = true;
+ }
+ },
+ },
+ });
+
+ return {
+ element: {
+ exit: (node) => {
+ // process only groups with more than 1 children
+ if (node.name !== 'g' || node.children.length <= 1) {
+ return;
+ }
+
+ // deoptimize the plugin when style elements are present
+ // selectors may rely on id, classes or tag names
+ if (deoptimizedWithStyles) {
+ return;
+ }
+
+ /**
+ * find common attributes in group children
+ * @type {Map<string, string>}
+ */
+ const commonAttributes = new Map();
+ let initial = true;
+ let everyChildIsPath = true;
+ for (const child of node.children) {
+ if (child.type === 'element') {
+ if (pathElems.includes(child.name) === false) {
+ everyChildIsPath = false;
+ }
+ if (initial) {
+ initial = false;
+ // collect all inheritable attributes from first child element
+ for (const [name, value] of Object.entries(child.attributes)) {
+ // consider only inheritable attributes
+ if (inheritableAttrs.includes(name)) {
+ commonAttributes.set(name, value);
+ }
+ }
+ } else {
+ // exclude uncommon attributes from initial list
+ for (const [name, value] of commonAttributes) {
+ if (child.attributes[name] !== value) {
+ commonAttributes.delete(name);
+ }
+ }
+ }
+ }
+ }
+
+ // preserve transform on children when group has clip-path or mask
+ if (
+ node.attributes['clip-path'] != null ||
+ node.attributes.mask != null
+ ) {
+ commonAttributes.delete('transform');
+ }
+
+ // preserve transform when all children are paths
+ // so the transform could be applied to path data by other plugins
+ if (everyChildIsPath) {
+ commonAttributes.delete('transform');
+ }
+
+ // add common children attributes to group
+ for (const [name, value] of commonAttributes) {
+ if (name === 'transform') {
+ if (node.attributes.transform != null) {
+ node.attributes.transform = `${node.attributes.transform} ${value}`;
+ } else {
+ node.attributes.transform = value;
+ }
+ } else {
+ node.attributes[name] = value;
+ }
+ }
+
+ // delete common attributes from children
+ for (const child of node.children) {
+ if (child.type === 'element') {
+ for (const [name] of commonAttributes) {
+ delete child.attributes[name];
+ }
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/moveGroupAttrsToElems.js b/node_modules/svgo/plugins/moveGroupAttrsToElems.js
new file mode 100644
index 00000000..60b55676
--- /dev/null
+++ b/node_modules/svgo/plugins/moveGroupAttrsToElems.js
@@ -0,0 +1,64 @@
+'use strict';
+
+const { pathElems, referencesProps } = require('./_collections.js');
+
+exports.name = 'moveGroupAttrsToElems';
+exports.description = 'moves some group attributes to the content elements';
+
+const pathElemsWithGroupsAndText = [...pathElems, 'g', 'text'];
+
+/**
+ * Move group attrs to the content elements.
+ *
+ * @example
+ * <g transform="scale(2)">
+ * <path transform="rotate(45)" d="M0,0 L10,20"/>
+ * <path transform="translate(10, 20)" d="M0,10 L20,30"/>
+ * </g>
+ * ⬇
+ * <g>
+ * <path transform="scale(2) rotate(45)" d="M0,0 L10,20"/>
+ * <path transform="scale(2) translate(10, 20)" d="M0,10 L20,30"/>
+ * </g>
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'moveGroupAttrsToElems'>}
+ */
+exports.fn = () => {
+ return {
+ element: {
+ enter: (node) => {
+ // move group transform attr to content's pathElems
+ if (
+ node.name === 'g' &&
+ node.children.length !== 0 &&
+ node.attributes.transform != null &&
+ Object.entries(node.attributes).some(
+ ([name, value]) =>
+ referencesProps.includes(name) && value.includes('url(')
+ ) === false &&
+ node.children.every(
+ (child) =>
+ child.type === 'element' &&
+ pathElemsWithGroupsAndText.includes(child.name) &&
+ child.attributes.id == null
+ )
+ ) {
+ for (const child of node.children) {
+ const value = node.attributes.transform;
+ if (child.type === 'element') {
+ if (child.attributes.transform != null) {
+ child.attributes.transform = `${value} ${child.attributes.transform}`;
+ } else {
+ child.attributes.transform = value;
+ }
+ }
+ }
+
+ delete node.attributes.transform;
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/plugins-types.ts b/node_modules/svgo/plugins/plugins-types.ts
new file mode 100644
index 00000000..a4cca210
--- /dev/null
+++ b/node_modules/svgo/plugins/plugins-types.ts
@@ -0,0 +1,251 @@
+import type {
+ Plugin as PluginDef,
+ PluginInfo,
+ XastElement,
+} from '../lib/types';
+
+type DefaultPlugins = {
+ cleanupAttrs: {
+ newlines?: boolean;
+ trim?: boolean;
+ spaces?: boolean;
+ };
+ cleanupEnableBackground: void;
+ cleanupIds: {
+ remove?: boolean;
+ minify?: boolean;
+ preserve?: Array<string>;
+ preservePrefixes?: Array<string>;
+ force?: boolean;
+ };
+ cleanupNumericValues: {
+ floatPrecision?: number;
+ leadingZero?: boolean;
+ defaultPx?: boolean;
+ convertToPx?: boolean;
+ };
+ collapseGroups: void;
+ convertColors: {
+ currentColor?: boolean | string | RegExp;
+ names2hex?: boolean;
+ rgb2hex?: boolean;
+ shorthex?: boolean;
+ shortname?: boolean;
+ };
+ convertEllipseToCircle: void;
+ convertPathData: {
+ applyTransforms?: boolean;
+ applyTransformsStroked?: boolean;
+ makeArcs?: {
+ threshold: number;
+ tolerance: number;
+ };
+ straightCurves?: boolean;
+ lineShorthands?: boolean;
+ curveSmoothShorthands?: boolean;
+ floatPrecision?: number | false;
+ transformPrecision?: number;
+ removeUseless?: boolean;
+ collapseRepeated?: boolean;
+ utilizeAbsolute?: boolean;
+ leadingZero?: boolean;
+ negativeExtraSpace?: boolean;
+ noSpaceAfterFlags?: boolean;
+ forceAbsolutePath?: boolean;
+ };
+ convertShapeToPath: {
+ convertArcs?: boolean;
+ floatPrecision?: number;
+ };
+ convertTransform: {
+ convertToShorts?: boolean;
+ degPrecision?: number;
+ floatPrecision?: number;
+ transformPrecision?: number;
+ matrixToTransform?: boolean;
+ shortTranslate?: boolean;
+ shortScale?: boolean;
+ shortRotate?: boolean;
+ removeUseless?: boolean;
+ collapseIntoOne?: boolean;
+ leadingZero?: boolean;
+ negativeExtraSpace?: boolean;
+ };
+ mergeStyles: void;
+ inlineStyles: {
+ onlyMatchedOnce?: boolean;
+ removeMatchedSelectors?: boolean;
+ useMqs?: Array<string>;
+ usePseudos?: Array<string>;
+ };
+ mergePaths: {
+ force?: boolean;
+ floatPrecision?: number;
+ noSpaceAfterFlags?: boolean;
+ };
+
+ minifyStyles: {
+ /**
+ * Disable or enable a structure optimisations.
+ * @default true
+ */
+ restructure?: boolean | undefined;
+ /**
+ * Enables merging of @media rules with the same media query by splitted by other rules.
+ * The optimisation is unsafe in general, but should work fine in most cases. Use it on your own risk.
+ * @default false
+ */
+ forceMediaMerge?: boolean | undefined;
+ /**
+ * Specify what comments to leave:
+ * - 'exclamation' or true – leave all exclamation comments
+ * - 'first-exclamation' – remove every comment except first one
+ * - false – remove all comments
+ * @default true
+ */
+ comments?: string | boolean | undefined;
+ /**
+ * Advanced optimizations
+ */
+ usage?:
+ | boolean
+ | {
+ force?: boolean;
+ ids?: boolean;
+ classes?: boolean;
+ tags?: boolean;
+ };
+ };
+
+ moveElemsAttrsToGroup: void;
+ moveGroupAttrsToElems: void;
+ removeComments: void;
+ removeDesc: {
+ removeAny?: boolean;
+ };
+ removeDoctype: void;
+ removeEditorsNSData: {
+ additionalNamespaces?: Array<string>;
+ };
+ removeEmptyAttrs: void;
+ removeEmptyContainers: void;
+ removeEmptyText: {
+ text?: boolean;
+ tspan?: boolean;
+ tref?: boolean;
+ };
+ removeHiddenElems: {
+ isHidden?: boolean;
+ displayNone?: boolean;
+ opacity0?: boolean;
+ circleR0?: boolean;
+ ellipseRX0?: boolean;
+ ellipseRY0?: boolean;
+ rectWidth0?: boolean;
+ rectHeight0?: boolean;
+ patternWidth0?: boolean;
+ patternHeight0?: boolean;
+ imageWidth0?: boolean;
+ imageHeight0?: boolean;
+ pathEmptyD?: boolean;
+ polylineEmptyPoints?: boolean;
+ polygonEmptyPoints?: boolean;
+ };
+ removeMetadata: void;
+ removeNonInheritableGroupAttrs: void;
+ removeTitle: void;
+ removeUnknownsAndDefaults: {
+ unknownContent?: boolean;
+ unknownAttrs?: boolean;
+ defaultAttrs?: boolean;
+ uselessOverrides?: boolean;
+ keepDataAttrs?: boolean;
+ keepAriaAttrs?: boolean;
+ keepRoleAttr?: boolean;
+ };
+ removeUnusedNS: void;
+ removeUselessDefs: void;
+ removeUselessStrokeAndFill: {
+ stroke?: boolean;
+ fill?: boolean;
+ removeNone?: boolean;
+ };
+ removeViewBox: void;
+ removeXMLProcInst: void;
+ sortAttrs: {
+ order?: Array<string>;
+ xmlnsOrder?: 'front' | 'alphabetical';
+ };
+ sortDefsChildren: void;
+};
+
+type PresetDefaultOverrides = {
+ [Name in keyof DefaultPlugins]?: DefaultPlugins[Name] | false;
+};
+
+export type BuiltinsWithOptionalParams = DefaultPlugins & {
+ 'preset-default': {
+ floatPrecision?: number;
+ /**
+ * All default plugins can be customized or disabled here
+ * for example
+ * {
+ * sortAttrs: { xmlnsOrder: "alphabetical" },
+ * cleanupAttrs: false,
+ * }
+ */
+ overrides?: PresetDefaultOverrides;
+ };
+ cleanupListOfValues: {
+ floatPrecision?: number;
+ leadingZero?: boolean;
+ defaultPx?: boolean;
+ convertToPx?: boolean;
+ };
+ convertStyleToAttrs: {
+ keepImportant?: boolean;
+ };
+ prefixIds: {
+ prefix?:
+ | boolean
+ | string
+ | ((node: XastElement, info: PluginInfo) => string);
+ delim?: string;
+ prefixIds?: boolean;
+ prefixClassNames?: boolean;
+ };
+ removeDimensions: void;
+ removeOffCanvasPaths: void;
+ removeRasterImages: void;
+ removeScriptElement: void;
+ removeStyleElement: void;
+ removeXMLNS: void;
+ reusePaths: void;
+};
+
+export type BuiltinsWithRequiredParams = {
+ addAttributesToSVGElement: {
+ attribute?: string | Record<string, null | string>;
+ attributes?: Array<string | Record<string, null | string>>;
+ };
+ addClassesToSVGElement: {
+ className?: string;
+ classNames?: Array<string>;
+ };
+ removeAttributesBySelector: any;
+ removeAttrs: {
+ elemSeparator?: string;
+ preserveCurrentColor?: boolean;
+ attrs: string | Array<string>;
+ };
+ removeElementsByAttr: {
+ id?: string | Array<string>;
+ class?: string | Array<string>;
+ };
+};
+
+type PluginsParams = BuiltinsWithOptionalParams & BuiltinsWithRequiredParams;
+
+export type Plugin<Name extends keyof PluginsParams> = PluginDef<
+ PluginsParams[Name]
+>;
diff --git a/node_modules/svgo/plugins/prefixIds.js b/node_modules/svgo/plugins/prefixIds.js
new file mode 100644
index 00000000..66762936
--- /dev/null
+++ b/node_modules/svgo/plugins/prefixIds.js
@@ -0,0 +1,229 @@
+'use strict';
+
+const csstree = require('css-tree');
+const { referencesProps } = require('./_collections.js');
+
+exports.name = 'prefixIds';
+exports.description = 'prefix IDs';
+
+/**
+ * extract basename from path
+ * @type {(path: string) => string}
+ */
+const getBasename = (path) => {
+ // extract everything after latest slash or backslash
+ const matched = path.match(/[/\\]?([^/\\]+)$/);
+ if (matched) {
+ return matched[1];
+ }
+ return '';
+};
+
+/**
+ * escapes a string for being used as ID
+ * @type {(string: string) => string}
+ */
+const escapeIdentifierName = (str) => {
+ return str.replace(/[. ]/g, '_');
+};
+
+/**
+ * @type {(string: string) => string}
+ */
+const unquote = (string) => {
+ if (
+ (string.startsWith('"') && string.endsWith('"')) ||
+ (string.startsWith("'") && string.endsWith("'"))
+ ) {
+ return string.slice(1, -1);
+ }
+ return string;
+};
+
+/**
+ * prefix an ID
+ * @type {(prefix: string, name: string) => string}
+ */
+const prefixId = (prefix, value) => {
+ if (value.startsWith(prefix)) {
+ return value;
+ }
+ return prefix + value;
+};
+
+/**
+ * prefix an #ID
+ * @type {(prefix: string, name: string) => string | null}
+ */
+const prefixReference = (prefix, value) => {
+ if (value.startsWith('#')) {
+ return '#' + prefixId(prefix, value.slice(1));
+ }
+ return null;
+};
+
+/** @type {(value: any) => any} */
+const toAny = (value) => value;
+
+/**
+ * Prefixes identifiers
+ *
+ * @author strarsis <strarsis@gmail.com>
+ *
+ * @type {import('./plugins-types').Plugin<'prefixIds'>}
+ */
+exports.fn = (_root, params, info) => {
+ const { delim = '__', prefixIds = true, prefixClassNames = true } = params;
+
+ return {
+ element: {
+ enter: (node) => {
+ /**
+ * prefix, from file name or option
+ * @type {string}
+ */
+ let prefix = 'prefix' + delim;
+ if (typeof params.prefix === 'function') {
+ prefix = params.prefix(node, info) + delim;
+ } else if (typeof params.prefix === 'string') {
+ prefix = params.prefix + delim;
+ } else if (params.prefix === false) {
+ prefix = '';
+ } else if (info.path != null && info.path.length > 0) {
+ prefix = escapeIdentifierName(getBasename(info.path)) + delim;
+ }
+
+ // prefix id/class selectors and url() references in styles
+ if (node.name === 'style') {
+ // skip empty <style/> elements
+ if (node.children.length === 0) {
+ return;
+ }
+
+ // parse styles
+ let cssText = '';
+ if (
+ node.children[0].type === 'text' ||
+ node.children[0].type === 'cdata'
+ ) {
+ cssText = node.children[0].value;
+ }
+ /**
+ * @type {null | csstree.CssNode}
+ */
+ let cssAst = null;
+ try {
+ cssAst = csstree.parse(cssText, {
+ parseValue: true,
+ parseCustomProperty: false,
+ });
+ } catch {
+ return;
+ }
+
+ csstree.walk(cssAst, (node) => {
+ // #ID, .class selectors
+ if (
+ (prefixIds && node.type === 'IdSelector') ||
+ (prefixClassNames && node.type === 'ClassSelector')
+ ) {
+ node.name = prefixId(prefix, node.name);
+ return;
+ }
+ // url(...) references
+ // csstree v2 changed this type
+ if (node.type === 'Url' && toAny(node.value).length > 0) {
+ const prefixed = prefixReference(
+ prefix,
+ unquote(toAny(node.value))
+ );
+ if (prefixed != null) {
+ toAny(node).value = prefixed;
+ }
+ }
+ });
+
+ // update styles
+ if (
+ node.children[0].type === 'text' ||
+ node.children[0].type === 'cdata'
+ ) {
+ node.children[0].value = csstree.generate(cssAst);
+ }
+ return;
+ }
+
+ // prefix an ID attribute value
+ if (
+ prefixIds &&
+ node.attributes.id != null &&
+ node.attributes.id.length !== 0
+ ) {
+ node.attributes.id = prefixId(prefix, node.attributes.id);
+ }
+
+ // prefix a class attribute value
+ if (
+ prefixClassNames &&
+ node.attributes.class != null &&
+ node.attributes.class.length !== 0
+ ) {
+ node.attributes.class = node.attributes.class
+ .split(/\s+/)
+ .map((name) => prefixId(prefix, name))
+ .join(' ');
+ }
+
+ // prefix a href attribute value
+ // xlink:href is deprecated, must be still supported
+ for (const name of ['href', 'xlink:href']) {
+ if (
+ node.attributes[name] != null &&
+ node.attributes[name].length !== 0
+ ) {
+ const prefixed = prefixReference(prefix, node.attributes[name]);
+ if (prefixed != null) {
+ node.attributes[name] = prefixed;
+ }
+ }
+ }
+
+ // prefix an URL attribute value
+ for (const name of referencesProps) {
+ if (
+ node.attributes[name] != null &&
+ node.attributes[name].length !== 0
+ ) {
+ node.attributes[name] = node.attributes[name].replace(
+ /url\((.*?)\)/gi,
+ (match, url) => {
+ const prefixed = prefixReference(prefix, url);
+ if (prefixed == null) {
+ return match;
+ }
+ return `url(${prefixed})`;
+ }
+ );
+ }
+ }
+
+ // prefix begin/end attribute value
+ for (const name of ['begin', 'end']) {
+ if (
+ node.attributes[name] != null &&
+ node.attributes[name].length !== 0
+ ) {
+ const parts = node.attributes[name].split(/\s*;\s+/).map((val) => {
+ if (val.endsWith('.end') || val.endsWith('.start')) {
+ const [id, postfix] = val.split('.');
+ return `${prefixId(prefix, id)}.${postfix}`;
+ }
+ return val;
+ });
+ node.attributes[name] = parts.join('; ');
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/preset-default.js b/node_modules/svgo/plugins/preset-default.js
new file mode 100644
index 00000000..86516fd7
--- /dev/null
+++ b/node_modules/svgo/plugins/preset-default.js
@@ -0,0 +1,82 @@
+'use strict';
+
+const { createPreset } = require('../lib/svgo/plugins.js');
+
+const removeDoctype = require('./removeDoctype.js');
+const removeXMLProcInst = require('./removeXMLProcInst.js');
+const removeComments = require('./removeComments.js');
+const removeMetadata = require('./removeMetadata.js');
+const removeEditorsNSData = require('./removeEditorsNSData.js');
+const cleanupAttrs = require('./cleanupAttrs.js');
+const mergeStyles = require('./mergeStyles.js');
+const inlineStyles = require('./inlineStyles.js');
+const minifyStyles = require('./minifyStyles.js');
+const cleanupIds = require('./cleanupIds.js');
+const removeUselessDefs = require('./removeUselessDefs.js');
+const cleanupNumericValues = require('./cleanupNumericValues.js');
+const convertColors = require('./convertColors.js');
+const removeUnknownsAndDefaults = require('./removeUnknownsAndDefaults.js');
+const removeNonInheritableGroupAttrs = require('./removeNonInheritableGroupAttrs.js');
+const removeUselessStrokeAndFill = require('./removeUselessStrokeAndFill.js');
+const removeViewBox = require('./removeViewBox.js');
+const cleanupEnableBackground = require('./cleanupEnableBackground.js');
+const removeHiddenElems = require('./removeHiddenElems.js');
+const removeEmptyText = require('./removeEmptyText.js');
+const convertShapeToPath = require('./convertShapeToPath.js');
+const convertEllipseToCircle = require('./convertEllipseToCircle.js');
+const moveElemsAttrsToGroup = require('./moveElemsAttrsToGroup.js');
+const moveGroupAttrsToElems = require('./moveGroupAttrsToElems.js');
+const collapseGroups = require('./collapseGroups.js');
+const convertPathData = require('./convertPathData.js');
+const convertTransform = require('./convertTransform.js');
+const removeEmptyAttrs = require('./removeEmptyAttrs.js');
+const removeEmptyContainers = require('./removeEmptyContainers.js');
+const mergePaths = require('./mergePaths.js');
+const removeUnusedNS = require('./removeUnusedNS.js');
+const sortAttrs = require('./sortAttrs.js');
+const sortDefsChildren = require('./sortDefsChildren.js');
+const removeTitle = require('./removeTitle.js');
+const removeDesc = require('./removeDesc.js');
+
+const presetDefault = createPreset({
+ name: 'preset-default',
+ plugins: [
+ removeDoctype,
+ removeXMLProcInst,
+ removeComments,
+ removeMetadata,
+ removeEditorsNSData,
+ cleanupAttrs,
+ mergeStyles,
+ inlineStyles,
+ minifyStyles,
+ cleanupIds,
+ removeUselessDefs,
+ cleanupNumericValues,
+ convertColors,
+ removeUnknownsAndDefaults,
+ removeNonInheritableGroupAttrs,
+ removeUselessStrokeAndFill,
+ removeViewBox,
+ cleanupEnableBackground,
+ removeHiddenElems,
+ removeEmptyText,
+ convertShapeToPath,
+ convertEllipseToCircle,
+ moveElemsAttrsToGroup,
+ moveGroupAttrsToElems,
+ collapseGroups,
+ convertPathData,
+ convertTransform,
+ removeEmptyAttrs,
+ removeEmptyContainers,
+ mergePaths,
+ removeUnusedNS,
+ sortAttrs,
+ sortDefsChildren,
+ removeTitle,
+ removeDesc,
+ ],
+});
+
+module.exports = presetDefault;
diff --git a/node_modules/svgo/plugins/removeAttributesBySelector.js b/node_modules/svgo/plugins/removeAttributesBySelector.js
new file mode 100644
index 00000000..09db8a8d
--- /dev/null
+++ b/node_modules/svgo/plugins/removeAttributesBySelector.js
@@ -0,0 +1,97 @@
+'use strict';
+
+const { querySelectorAll } = require('../lib/xast.js');
+
+exports.name = 'removeAttributesBySelector';
+exports.description =
+ 'removes attributes of elements that match a css selector';
+
+/**
+ * Removes attributes of elements that match a css selector.
+ *
+ * @example
+ * <caption>A selector removing a single attribute</caption>
+ * plugins: [
+ * {
+ * name: "removeAttributesBySelector",
+ * params: {
+ * selector: "[fill='#00ff00']"
+ * attributes: "fill"
+ * }
+ * }
+ * ]
+ *
+ * <rect x="0" y="0" width="100" height="100" fill="#00ff00" stroke="#00ff00"/>
+ * ↓
+ * <rect x="0" y="0" width="100" height="100" stroke="#00ff00"/>
+ *
+ * <caption>A selector removing multiple attributes</caption>
+ * plugins: [
+ * {
+ * name: "removeAttributesBySelector",
+ * params: {
+ * selector: "[fill='#00ff00']",
+ * attributes: [
+ * "fill",
+ * "stroke"
+ * ]
+ * }
+ * }
+ * ]
+ *
+ * <rect x="0" y="0" width="100" height="100" fill="#00ff00" stroke="#00ff00"/>
+ * ↓
+ * <rect x="0" y="0" width="100" height="100"/>
+ *
+ * <caption>Multiple selectors removing attributes</caption>
+ * plugins: [
+ * {
+ * name: "removeAttributesBySelector",
+ * params: {
+ * selectors: [
+ * {
+ * selector: "[fill='#00ff00']",
+ * attributes: "fill"
+ * },
+ * {
+ * selector: "#remove",
+ * attributes: [
+ * "stroke",
+ * "id"
+ * ]
+ * }
+ * ]
+ * }
+ * }
+ * ]
+ *
+ * <rect x="0" y="0" width="100" height="100" fill="#00ff00" stroke="#00ff00"/>
+ * ↓
+ * <rect x="0" y="0" width="100" height="100"/>
+ *
+ * @link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors|MDN CSS Selectors
+ *
+ * @author Bradley Mease
+ *
+ * @type {import('./plugins-types').Plugin<'removeAttributesBySelector'>}
+ */
+exports.fn = (root, params) => {
+ const selectors = Array.isArray(params.selectors)
+ ? params.selectors
+ : [params];
+ for (const { selector, attributes } of selectors) {
+ const nodes = querySelectorAll(root, selector);
+ for (const node of nodes) {
+ if (node.type === 'element') {
+ if (Array.isArray(attributes)) {
+ for (const name of attributes) {
+ delete node.attributes[name];
+ }
+ } else {
+ delete node.attributes[attributes];
+ }
+ }
+ }
+ }
+ return {};
+};
diff --git a/node_modules/svgo/plugins/removeAttrs.js b/node_modules/svgo/plugins/removeAttrs.js
new file mode 100644
index 00000000..c289d8ca
--- /dev/null
+++ b/node_modules/svgo/plugins/removeAttrs.js
@@ -0,0 +1,153 @@
+'use strict';
+
+exports.name = 'removeAttrs';
+exports.description = 'removes specified attributes';
+
+const DEFAULT_SEPARATOR = ':';
+const ENOATTRS = `Warning: The plugin "removeAttrs" requires the "attrs" parameter.
+It should have a pattern to remove, otherwise the plugin is a noop.
+Config example:
+
+plugins: [
+ {
+ name: "removeAttrs",
+ params: {
+ attrs: "(fill|stroke)"
+ }
+ }
+]
+`;
+
+/**
+ * Remove attributes
+ *
+ * @example elemSeparator
+ * format: string
+ *
+ * @example preserveCurrentColor
+ * format: boolean
+ *
+ * @example attrs:
+ *
+ * format: [ element* : attribute* : value* ]
+ *
+ * element : regexp (wrapped into ^...$), single * or omitted > all elements (must be present when value is used)
+ * attribute : regexp (wrapped into ^...$)
+ * value : regexp (wrapped into ^...$), single * or omitted > all values
+ *
+ * examples:
+ *
+ * > basic: remove fill attribute
+ * ---
+ * removeAttrs:
+ * attrs: 'fill'
+ *
+ * > remove fill attribute on path element
+ * ---
+ * attrs: 'path:fill'
+ *
+ * > remove fill attribute on path element where value is none
+ * ---
+ * attrs: 'path:fill:none'
+ *
+ *
+ * > remove all fill and stroke attribute
+ * ---
+ * attrs:
+ * - 'fill'
+ * - 'stroke'
+ *
+ * [is same as]
+ *
+ * attrs: '(fill|stroke)'
+ *
+ * [is same as]
+ *
+ * attrs: '*:(fill|stroke)'
+ *
+ * [is same as]
+ *
+ * attrs: '.*:(fill|stroke)'
+ *
+ * [is same as]
+ *
+ * attrs: '.*:(fill|stroke):.*'
+ *
+ *
+ * > remove all stroke related attributes
+ * ----
+ * attrs: 'stroke.*'
+ *
+ *
+ * @author Benny Schudel
+ *
+ * @type {import('./plugins-types').Plugin<'removeAttrs'>}
+ */
+exports.fn = (root, params) => {
+ if (typeof params.attrs == 'undefined') {
+ console.warn(ENOATTRS);
+ return null;
+ }
+
+ const elemSeparator =
+ typeof params.elemSeparator == 'string'
+ ? params.elemSeparator
+ : DEFAULT_SEPARATOR;
+ const preserveCurrentColor =
+ typeof params.preserveCurrentColor == 'boolean'
+ ? params.preserveCurrentColor
+ : false;
+ const attrs = Array.isArray(params.attrs) ? params.attrs : [params.attrs];
+
+ return {
+ element: {
+ enter: (node) => {
+ for (let pattern of attrs) {
+ // if no element separators (:), assume it's attribute name, and apply to all elements *regardless of value*
+ if (pattern.includes(elemSeparator) === false) {
+ pattern = ['.*', elemSeparator, pattern, elemSeparator, '.*'].join(
+ ''
+ );
+ // if only 1 separator, assume it's element and attribute name, and apply regardless of attribute value
+ } else if (pattern.split(elemSeparator).length < 3) {
+ pattern = [pattern, elemSeparator, '.*'].join('');
+ }
+
+ // create regexps for element, attribute name, and attribute value
+ const list = pattern.split(elemSeparator).map((value) => {
+ // adjust single * to match anything
+ if (value === '*') {
+ value = '.*';
+ }
+ return new RegExp(['^', value, '$'].join(''), 'i');
+ });
+
+ // matches element
+ if (list[0].test(node.name)) {
+ // loop attributes
+ for (const [name, value] of Object.entries(node.attributes)) {
+ const isFillCurrentColor =
+ preserveCurrentColor &&
+ name == 'fill' &&
+ value == 'currentColor';
+ const isStrokeCurrentColor =
+ preserveCurrentColor &&
+ name == 'stroke' &&
+ value == 'currentColor';
+ if (
+ !isFillCurrentColor &&
+ !isStrokeCurrentColor &&
+ // matches attribute name
+ list[1].test(name) &&
+ // matches attribute value
+ list[2].test(value)
+ ) {
+ delete node.attributes[name];
+ }
+ }
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeComments.js b/node_modules/svgo/plugins/removeComments.js
new file mode 100644
index 00000000..70e46271
--- /dev/null
+++ b/node_modules/svgo/plugins/removeComments.js
@@ -0,0 +1,29 @@
+'use strict';
+
+const { detachNodeFromParent } = require('../lib/xast.js');
+
+exports.name = 'removeComments';
+exports.description = 'removes comments';
+
+/**
+ * Remove comments.
+ *
+ * @example
+ * <!-- Generator: Adobe Illustrator 15.0.0, SVG Export
+ * Plug-In . SVG Version: 6.00 Build 0) -->
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'removeComments'>}
+ */
+exports.fn = () => {
+ return {
+ comment: {
+ enter: (node, parentNode) => {
+ if (node.value.charAt(0) !== '!') {
+ detachNodeFromParent(node, parentNode);
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeDesc.js b/node_modules/svgo/plugins/removeDesc.js
new file mode 100644
index 00000000..5d9e57ae
--- /dev/null
+++ b/node_modules/svgo/plugins/removeDesc.js
@@ -0,0 +1,39 @@
+'use strict';
+
+const { detachNodeFromParent } = require('../lib/xast.js');
+
+exports.name = 'removeDesc';
+exports.description = 'removes <desc>';
+
+const standardDescs = /^(Created with|Created using)/;
+
+/**
+ * Removes <desc>.
+ * Removes only standard editors content or empty elements 'cause it can be used for accessibility.
+ * Enable parameter 'removeAny' to remove any description.
+ *
+ * https://developer.mozilla.org/en-US/docs/Web/SVG/Element/desc
+ *
+ * @author Daniel Wabyick
+ *
+ * @type {import('./plugins-types').Plugin<'removeDesc'>}
+ */
+exports.fn = (root, params) => {
+ const { removeAny = true } = params;
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ if (node.name === 'desc') {
+ if (
+ removeAny ||
+ node.children.length === 0 ||
+ (node.children[0].type === 'text' &&
+ standardDescs.test(node.children[0].value))
+ ) {
+ detachNodeFromParent(node, parentNode);
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeDimensions.js b/node_modules/svgo/plugins/removeDimensions.js
new file mode 100644
index 00000000..4f4f3984
--- /dev/null
+++ b/node_modules/svgo/plugins/removeDimensions.js
@@ -0,0 +1,43 @@
+'use strict';
+
+exports.name = 'removeDimensions';
+exports.description =
+ 'removes width and height in presence of viewBox (opposite to removeViewBox, disable it first)';
+
+/**
+ * Remove width/height attributes and add the viewBox attribute if it's missing
+ *
+ * @example
+ * <svg width="100" height="50" />
+ * ↓
+ * <svg viewBox="0 0 100 50" />
+ *
+ * @author Benny Schudel
+ *
+ * @type {import('./plugins-types').Plugin<'removeDimensions'>}
+ */
+exports.fn = () => {
+ return {
+ element: {
+ enter: (node) => {
+ if (node.name === 'svg') {
+ if (node.attributes.viewBox != null) {
+ delete node.attributes.width;
+ delete node.attributes.height;
+ } else if (
+ node.attributes.width != null &&
+ node.attributes.height != null &&
+ Number.isNaN(Number(node.attributes.width)) === false &&
+ Number.isNaN(Number(node.attributes.height)) === false
+ ) {
+ const width = Number(node.attributes.width);
+ const height = Number(node.attributes.height);
+ node.attributes.viewBox = `0 0 ${width} ${height}`;
+ delete node.attributes.width;
+ delete node.attributes.height;
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeDoctype.js b/node_modules/svgo/plugins/removeDoctype.js
new file mode 100644
index 00000000..cbdcd013
--- /dev/null
+++ b/node_modules/svgo/plugins/removeDoctype.js
@@ -0,0 +1,40 @@
+'use strict';
+
+const { detachNodeFromParent } = require('../lib/xast.js');
+
+exports.name = 'removeDoctype';
+exports.description = 'removes doctype declaration';
+
+/**
+ * Remove DOCTYPE declaration.
+ *
+ * "Unfortunately the SVG DTDs are a source of so many
+ * issues that the SVG WG has decided not to write one
+ * for the upcoming SVG 1.2 standard. In fact SVG WG
+ * members are even telling people not to use a DOCTYPE
+ * declaration in SVG 1.0 and 1.1 documents"
+ * https://jwatt.org/svg/authoring/#doctype-declaration
+ *
+ * @example
+ * <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ * q"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+ *
+ * @example
+ * <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ * "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" [
+ * <!-- an internal subset can be embedded here -->
+ * ]>
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'removeDoctype'>}
+ */
+exports.fn = () => {
+ return {
+ doctype: {
+ enter: (node, parentNode) => {
+ detachNodeFromParent(node, parentNode);
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeEditorsNSData.js b/node_modules/svgo/plugins/removeEditorsNSData.js
new file mode 100644
index 00000000..cf8016cd
--- /dev/null
+++ b/node_modules/svgo/plugins/removeEditorsNSData.js
@@ -0,0 +1,64 @@
+'use strict';
+
+const { detachNodeFromParent } = require('../lib/xast.js');
+const { editorNamespaces } = require('./_collections.js');
+
+exports.name = 'removeEditorsNSData';
+exports.description = 'removes editors namespaces, elements and attributes';
+
+/**
+ * Remove editors namespaces, elements and attributes.
+ *
+ * @example
+ * <svg xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd">
+ * <sodipodi:namedview/>
+ * <path sodipodi:nodetypes="cccc"/>
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'removeEditorsNSData'>}
+ */
+exports.fn = (_root, params) => {
+ let namespaces = editorNamespaces;
+ if (Array.isArray(params.additionalNamespaces)) {
+ namespaces = [...editorNamespaces, ...params.additionalNamespaces];
+ }
+ /**
+ * @type {Array<string>}
+ */
+ const prefixes = [];
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ // collect namespace aliases from svg element
+ if (node.name === 'svg') {
+ for (const [name, value] of Object.entries(node.attributes)) {
+ if (name.startsWith('xmlns:') && namespaces.includes(value)) {
+ prefixes.push(name.slice('xmlns:'.length));
+ // <svg xmlns:sodipodi="">
+ delete node.attributes[name];
+ }
+ }
+ }
+ // remove editor attributes, for example
+ // <* sodipodi:*="">
+ for (const name of Object.keys(node.attributes)) {
+ if (name.includes(':')) {
+ const [prefix] = name.split(':');
+ if (prefixes.includes(prefix)) {
+ delete node.attributes[name];
+ }
+ }
+ }
+ // remove editor elements, for example
+ // <sodipodi:*>
+ if (node.name.includes(':')) {
+ const [prefix] = node.name.split(':');
+ if (prefixes.includes(prefix)) {
+ detachNodeFromParent(node, parentNode);
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeElementsByAttr.js b/node_modules/svgo/plugins/removeElementsByAttr.js
new file mode 100644
index 00000000..b5fd91ba
--- /dev/null
+++ b/node_modules/svgo/plugins/removeElementsByAttr.js
@@ -0,0 +1,73 @@
+'use strict';
+
+const { detachNodeFromParent } = require('../lib/xast.js');
+
+exports.name = 'removeElementsByAttr';
+exports.description =
+ 'removes arbitrary elements by ID or className (disabled by default)';
+
+/**
+ * Remove arbitrary SVG elements by ID or className.
+ *
+ * @example id
+ * > single: remove element with ID of `elementID`
+ * ---
+ * removeElementsByAttr:
+ * id: 'elementID'
+ *
+ * > list: remove multiple elements by ID
+ * ---
+ * removeElementsByAttr:
+ * id:
+ * - 'elementID'
+ * - 'anotherID'
+ *
+ * @example class
+ * > single: remove all elements with class of `elementClass`
+ * ---
+ * removeElementsByAttr:
+ * class: 'elementClass'
+ *
+ * > list: remove all elements with class of `elementClass` or `anotherClass`
+ * ---
+ * removeElementsByAttr:
+ * class:
+ * - 'elementClass'
+ * - 'anotherClass'
+ *
+ * @author Eli Dupuis (@elidupuis)
+ *
+ * @type {import('./plugins-types').Plugin<'removeElementsByAttr'>}
+ */
+exports.fn = (root, params) => {
+ const ids =
+ params.id == null ? [] : Array.isArray(params.id) ? params.id : [params.id];
+ const classes =
+ params.class == null
+ ? []
+ : Array.isArray(params.class)
+ ? params.class
+ : [params.class];
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ // remove element if it's `id` matches configured `id` params
+ if (node.attributes.id != null && ids.length !== 0) {
+ if (ids.includes(node.attributes.id)) {
+ detachNodeFromParent(node, parentNode);
+ }
+ }
+ // remove element if it's `class` contains any of the configured `class` params
+ if (node.attributes.class && classes.length !== 0) {
+ const classList = node.attributes.class.split(' ');
+ for (const item of classes) {
+ if (classList.includes(item)) {
+ detachNodeFromParent(node, parentNode);
+ break;
+ }
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeEmptyAttrs.js b/node_modules/svgo/plugins/removeEmptyAttrs.js
new file mode 100644
index 00000000..83f567c3
--- /dev/null
+++ b/node_modules/svgo/plugins/removeEmptyAttrs.js
@@ -0,0 +1,31 @@
+'use strict';
+
+const { attrsGroups } = require('./_collections.js');
+
+exports.name = 'removeEmptyAttrs';
+exports.description = 'removes empty attributes';
+
+/**
+ * Remove attributes with empty values.
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'removeEmptyAttrs'>}
+ */
+exports.fn = () => {
+ return {
+ element: {
+ enter: (node) => {
+ for (const [name, value] of Object.entries(node.attributes)) {
+ if (
+ value === '' &&
+ // empty conditional processing attributes prevents elements from rendering
+ attrsGroups.conditionalProcessing.includes(name) === false
+ ) {
+ delete node.attributes[name];
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeEmptyContainers.js b/node_modules/svgo/plugins/removeEmptyContainers.js
new file mode 100644
index 00000000..d803fc9c
--- /dev/null
+++ b/node_modules/svgo/plugins/removeEmptyContainers.js
@@ -0,0 +1,56 @@
+'use strict';
+
+const { detachNodeFromParent } = require('../lib/xast.js');
+const { elemsGroups } = require('./_collections.js');
+
+exports.name = 'removeEmptyContainers';
+exports.description = 'removes empty container elements';
+
+/**
+ * Remove empty containers.
+ *
+ * @see https://www.w3.org/TR/SVG11/intro.html#TermContainerElement
+ *
+ * @example
+ * <defs/>
+ *
+ * @example
+ * <g><marker><a/></marker></g>
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'removeEmptyContainers'>}
+ */
+exports.fn = () => {
+ return {
+ element: {
+ exit: (node, parentNode) => {
+ // remove only empty non-svg containers
+ if (
+ node.name === 'svg' ||
+ elemsGroups.container.includes(node.name) === false ||
+ node.children.length !== 0
+ ) {
+ return;
+ }
+ // empty patterns may contain reusable configuration
+ if (
+ node.name === 'pattern' &&
+ Object.keys(node.attributes).length !== 0
+ ) {
+ return;
+ }
+ // The <g> may not have content, but the filter may cause a rectangle
+ // to be created and filled with pattern.
+ if (node.name === 'g' && node.attributes.filter != null) {
+ return;
+ }
+ // empty <mask> hides masked element
+ if (node.name === 'mask' && node.attributes.id != null) {
+ return;
+ }
+ detachNodeFromParent(node, parentNode);
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeEmptyText.js b/node_modules/svgo/plugins/removeEmptyText.js
new file mode 100644
index 00000000..5f6db3d3
--- /dev/null
+++ b/node_modules/svgo/plugins/removeEmptyText.js
@@ -0,0 +1,51 @@
+'use strict';
+
+const { detachNodeFromParent } = require('../lib/xast.js');
+
+exports.name = 'removeEmptyText';
+exports.description = 'removes empty <text> elements';
+
+/**
+ * Remove empty Text elements.
+ *
+ * @see https://www.w3.org/TR/SVG11/text.html
+ *
+ * @example
+ * Remove empty text element:
+ * <text/>
+ *
+ * Remove empty tspan element:
+ * <tspan/>
+ *
+ * Remove tref with empty xlink:href attribute:
+ * <tref xlink:href=""/>
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'removeEmptyText'>}
+ */
+exports.fn = (root, params) => {
+ const { text = true, tspan = true, tref = true } = params;
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ // Remove empty text element
+ if (text && node.name === 'text' && node.children.length === 0) {
+ detachNodeFromParent(node, parentNode);
+ }
+ // Remove empty tspan element
+ if (tspan && node.name === 'tspan' && node.children.length === 0) {
+ detachNodeFromParent(node, parentNode);
+ }
+ // Remove tref with empty xlink:href attribute
+ if (
+ tref &&
+ node.name === 'tref' &&
+ node.attributes['xlink:href'] == null
+ ) {
+ detachNodeFromParent(node, parentNode);
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeHiddenElems.js b/node_modules/svgo/plugins/removeHiddenElems.js
new file mode 100644
index 00000000..5c1a0135
--- /dev/null
+++ b/node_modules/svgo/plugins/removeHiddenElems.js
@@ -0,0 +1,310 @@
+'use strict';
+
+const {
+ visit,
+ visitSkip,
+ querySelector,
+ detachNodeFromParent,
+} = require('../lib/xast.js');
+const { collectStylesheet, computeStyle } = require('../lib/style.js');
+const { parsePathData } = require('../lib/path.js');
+
+exports.name = 'removeHiddenElems';
+exports.description =
+ 'removes hidden elements (zero sized, with absent attributes)';
+
+/**
+ * Remove hidden elements with disabled rendering:
+ * - display="none"
+ * - opacity="0"
+ * - circle with zero radius
+ * - ellipse with zero x-axis or y-axis radius
+ * - rectangle with zero width or height
+ * - pattern with zero width or height
+ * - image with zero width or height
+ * - path with empty data
+ * - polyline with empty points
+ * - polygon with empty points
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'removeHiddenElems'>}
+ */
+exports.fn = (root, params) => {
+ const {
+ isHidden = true,
+ displayNone = true,
+ opacity0 = true,
+ circleR0 = true,
+ ellipseRX0 = true,
+ ellipseRY0 = true,
+ rectWidth0 = true,
+ rectHeight0 = true,
+ patternWidth0 = true,
+ patternHeight0 = true,
+ imageWidth0 = true,
+ imageHeight0 = true,
+ pathEmptyD = true,
+ polylineEmptyPoints = true,
+ polygonEmptyPoints = true,
+ } = params;
+ const stylesheet = collectStylesheet(root);
+
+ visit(root, {
+ element: {
+ enter: (node, parentNode) => {
+ // transparent element inside clipPath still affect clipped elements
+ if (node.name === 'clipPath') {
+ return visitSkip;
+ }
+ const computedStyle = computeStyle(stylesheet, node);
+ // opacity="0"
+ //
+ // https://www.w3.org/TR/SVG11/masking.html#ObjectAndGroupOpacityProperties
+ if (
+ opacity0 &&
+ computedStyle.opacity &&
+ computedStyle.opacity.type === 'static' &&
+ computedStyle.opacity.value === '0'
+ ) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+ },
+ },
+ });
+
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ // Removes hidden elements
+ // https://www.w3schools.com/cssref/pr_class_visibility.asp
+ const computedStyle = computeStyle(stylesheet, node);
+ if (
+ isHidden &&
+ computedStyle.visibility &&
+ computedStyle.visibility.type === 'static' &&
+ computedStyle.visibility.value === 'hidden' &&
+ // keep if any descendant enables visibility
+ querySelector(node, '[visibility=visible]') == null
+ ) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+
+ // display="none"
+ //
+ // https://www.w3.org/TR/SVG11/painting.html#DisplayProperty
+ // "A value of display: none indicates that the given element
+ // and its children shall not be rendered directly"
+ if (
+ displayNone &&
+ computedStyle.display &&
+ computedStyle.display.type === 'static' &&
+ computedStyle.display.value === 'none' &&
+ // markers with display: none still rendered
+ node.name !== 'marker'
+ ) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+
+ // Circles with zero radius
+ //
+ // https://www.w3.org/TR/SVG11/shapes.html#CircleElementRAttribute
+ // "A value of zero disables rendering of the element"
+ //
+ // <circle r="0">
+ if (
+ circleR0 &&
+ node.name === 'circle' &&
+ node.children.length === 0 &&
+ node.attributes.r === '0'
+ ) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+
+ // Ellipse with zero x-axis radius
+ //
+ // https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRXAttribute
+ // "A value of zero disables rendering of the element"
+ //
+ // <ellipse rx="0">
+ if (
+ ellipseRX0 &&
+ node.name === 'ellipse' &&
+ node.children.length === 0 &&
+ node.attributes.rx === '0'
+ ) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+
+ // Ellipse with zero y-axis radius
+ //
+ // https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRYAttribute
+ // "A value of zero disables rendering of the element"
+ //
+ // <ellipse ry="0">
+ if (
+ ellipseRY0 &&
+ node.name === 'ellipse' &&
+ node.children.length === 0 &&
+ node.attributes.ry === '0'
+ ) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+
+ // Rectangle with zero width
+ //
+ // https://www.w3.org/TR/SVG11/shapes.html#RectElementWidthAttribute
+ // "A value of zero disables rendering of the element"
+ //
+ // <rect width="0">
+ if (
+ rectWidth0 &&
+ node.name === 'rect' &&
+ node.children.length === 0 &&
+ node.attributes.width === '0'
+ ) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+
+ // Rectangle with zero height
+ //
+ // https://www.w3.org/TR/SVG11/shapes.html#RectElementHeightAttribute
+ // "A value of zero disables rendering of the element"
+ //
+ // <rect height="0">
+ if (
+ rectHeight0 &&
+ rectWidth0 &&
+ node.name === 'rect' &&
+ node.children.length === 0 &&
+ node.attributes.height === '0'
+ ) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+
+ // Pattern with zero width
+ //
+ // https://www.w3.org/TR/SVG11/pservers.html#PatternElementWidthAttribute
+ // "A value of zero disables rendering of the element (i.e., no paint is applied)"
+ //
+ // <pattern width="0">
+ if (
+ patternWidth0 &&
+ node.name === 'pattern' &&
+ node.attributes.width === '0'
+ ) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+
+ // Pattern with zero height
+ //
+ // https://www.w3.org/TR/SVG11/pservers.html#PatternElementHeightAttribute
+ // "A value of zero disables rendering of the element (i.e., no paint is applied)"
+ //
+ // <pattern height="0">
+ if (
+ patternHeight0 &&
+ node.name === 'pattern' &&
+ node.attributes.height === '0'
+ ) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+
+ // Image with zero width
+ //
+ // https://www.w3.org/TR/SVG11/struct.html#ImageElementWidthAttribute
+ // "A value of zero disables rendering of the element"
+ //
+ // <image width="0">
+ if (
+ imageWidth0 &&
+ node.name === 'image' &&
+ node.attributes.width === '0'
+ ) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+
+ // Image with zero height
+ //
+ // https://www.w3.org/TR/SVG11/struct.html#ImageElementHeightAttribute
+ // "A value of zero disables rendering of the element"
+ //
+ // <image height="0">
+ if (
+ imageHeight0 &&
+ node.name === 'image' &&
+ node.attributes.height === '0'
+ ) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+
+ // Path with empty data
+ //
+ // https://www.w3.org/TR/SVG11/paths.html#DAttribute
+ //
+ // <path d=""/>
+ if (pathEmptyD && node.name === 'path') {
+ if (node.attributes.d == null) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+ const pathData = parsePathData(node.attributes.d);
+ if (pathData.length === 0) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+ // keep single point paths for markers
+ if (
+ pathData.length === 1 &&
+ computedStyle['marker-start'] == null &&
+ computedStyle['marker-end'] == null
+ ) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+ return;
+ }
+
+ // Polyline with empty points
+ //
+ // https://www.w3.org/TR/SVG11/shapes.html#PolylineElementPointsAttribute
+ //
+ // <polyline points="">
+ if (
+ polylineEmptyPoints &&
+ node.name === 'polyline' &&
+ node.attributes.points == null
+ ) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+
+ // Polygon with empty points
+ //
+ // https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute
+ //
+ // <polygon points="">
+ if (
+ polygonEmptyPoints &&
+ node.name === 'polygon' &&
+ node.attributes.points == null
+ ) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeMetadata.js b/node_modules/svgo/plugins/removeMetadata.js
new file mode 100644
index 00000000..8c9de27e
--- /dev/null
+++ b/node_modules/svgo/plugins/removeMetadata.js
@@ -0,0 +1,27 @@
+'use strict';
+
+const { detachNodeFromParent } = require('../lib/xast.js');
+
+exports.name = 'removeMetadata';
+exports.description = 'removes <metadata>';
+
+/**
+ * Remove <metadata>.
+ *
+ * https://www.w3.org/TR/SVG11/metadata.html
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'removeMetadata'>}
+ */
+exports.fn = () => {
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ if (node.name === 'metadata') {
+ detachNodeFromParent(node, parentNode);
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeNonInheritableGroupAttrs.js b/node_modules/svgo/plugins/removeNonInheritableGroupAttrs.js
new file mode 100644
index 00000000..faa93dff
--- /dev/null
+++ b/node_modules/svgo/plugins/removeNonInheritableGroupAttrs.js
@@ -0,0 +1,38 @@
+'use strict';
+
+const {
+ inheritableAttrs,
+ attrsGroups,
+ presentationNonInheritableGroupAttrs,
+} = require('./_collections');
+
+exports.name = 'removeNonInheritableGroupAttrs';
+exports.description =
+ 'removes non-inheritable group’s presentational attributes';
+
+/**
+ * Remove non-inheritable group's "presentation" attributes.
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'removeNonInheritableGroupAttrs'>}
+ */
+exports.fn = () => {
+ return {
+ element: {
+ enter: (node) => {
+ if (node.name === 'g') {
+ for (const name of Object.keys(node.attributes)) {
+ if (
+ attrsGroups.presentation.includes(name) === true &&
+ inheritableAttrs.includes(name) === false &&
+ presentationNonInheritableGroupAttrs.includes(name) === false
+ ) {
+ delete node.attributes[name];
+ }
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeOffCanvasPaths.js b/node_modules/svgo/plugins/removeOffCanvasPaths.js
new file mode 100644
index 00000000..7a3d131d
--- /dev/null
+++ b/node_modules/svgo/plugins/removeOffCanvasPaths.js
@@ -0,0 +1,136 @@
+'use strict';
+
+/**
+ * @typedef {import('../lib/types').PathDataItem} PathDataItem
+ */
+
+const { visitSkip, detachNodeFromParent } = require('../lib/xast.js');
+const { parsePathData } = require('../lib/path.js');
+const { intersects } = require('./_path.js');
+
+exports.name = 'removeOffCanvasPaths';
+exports.description =
+ 'removes elements that are drawn outside of the viewbox (disabled by default)';
+
+/**
+ * Remove elements that are drawn outside of the viewbox.
+ *
+ * @author JoshyPHP
+ *
+ * @type {import('./plugins-types').Plugin<'removeOffCanvasPaths'>}
+ */
+exports.fn = () => {
+ /**
+ * @type {null | {
+ * top: number,
+ * right: number,
+ * bottom: number,
+ * left: number,
+ * width: number,
+ * height: number
+ * }}
+ */
+ let viewBoxData = null;
+
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ if (node.name === 'svg' && parentNode.type === 'root') {
+ let viewBox = '';
+ // find viewbox
+ if (node.attributes.viewBox != null) {
+ // remove commas and plus signs, normalize and trim whitespace
+ viewBox = node.attributes.viewBox;
+ } else if (
+ node.attributes.height != null &&
+ node.attributes.width != null
+ ) {
+ viewBox = `0 0 ${node.attributes.width} ${node.attributes.height}`;
+ }
+
+ // parse viewbox
+ // remove commas and plus signs, normalize and trim whitespace
+ viewBox = viewBox
+ .replace(/[,+]|px/g, ' ')
+ .replace(/\s+/g, ' ')
+ .replace(/^\s*|\s*$/g, '');
+ // ensure that the dimensions are 4 values separated by space
+ const m =
+ /^(-?\d*\.?\d+) (-?\d*\.?\d+) (\d*\.?\d+) (\d*\.?\d+)$/.exec(
+ viewBox
+ );
+ if (m == null) {
+ return;
+ }
+ const left = Number.parseFloat(m[1]);
+ const top = Number.parseFloat(m[2]);
+ const width = Number.parseFloat(m[3]);
+ const height = Number.parseFloat(m[4]);
+
+ // store the viewBox boundaries
+ viewBoxData = {
+ left,
+ top,
+ right: left + width,
+ bottom: top + height,
+ width,
+ height,
+ };
+ }
+
+ // consider that any item with a transform attribute is visible
+ if (node.attributes.transform != null) {
+ return visitSkip;
+ }
+
+ if (
+ node.name === 'path' &&
+ node.attributes.d != null &&
+ viewBoxData != null
+ ) {
+ const pathData = parsePathData(node.attributes.d);
+
+ // consider that a M command within the viewBox is visible
+ let visible = false;
+ for (const pathDataItem of pathData) {
+ if (pathDataItem.command === 'M') {
+ const [x, y] = pathDataItem.args;
+ if (
+ x >= viewBoxData.left &&
+ x <= viewBoxData.right &&
+ y >= viewBoxData.top &&
+ y <= viewBoxData.bottom
+ ) {
+ visible = true;
+ }
+ }
+ }
+ if (visible) {
+ return;
+ }
+
+ if (pathData.length === 2) {
+ // close the path too short for intersects()
+ pathData.push({ command: 'z', args: [] });
+ }
+
+ const { left, top, width, height } = viewBoxData;
+ /**
+ * @type {Array<PathDataItem>}
+ */
+ const viewBoxPathData = [
+ { command: 'M', args: [left, top] },
+ { command: 'h', args: [width] },
+ { command: 'v', args: [height] },
+ { command: 'H', args: [left] },
+ { command: 'z', args: [] },
+ ];
+
+ if (intersects(viewBoxPathData, pathData) === false) {
+ detachNodeFromParent(node, parentNode);
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeRasterImages.js b/node_modules/svgo/plugins/removeRasterImages.js
new file mode 100644
index 00000000..8a363531
--- /dev/null
+++ b/node_modules/svgo/plugins/removeRasterImages.js
@@ -0,0 +1,31 @@
+'use strict';
+
+const { detachNodeFromParent } = require('../lib/xast.js');
+
+exports.name = 'removeRasterImages';
+exports.description = 'removes raster images (disabled by default)';
+
+/**
+ * Remove raster images references in <image>.
+ *
+ * @see https://bugs.webkit.org/show_bug.cgi?id=63548
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'removeRasterImages'>}
+ */
+exports.fn = () => {
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ if (
+ node.name === 'image' &&
+ node.attributes['xlink:href'] != null &&
+ /(\.|image\/)(jpg|png|gif)/.test(node.attributes['xlink:href'])
+ ) {
+ detachNodeFromParent(node, parentNode);
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeScriptElement.js b/node_modules/svgo/plugins/removeScriptElement.js
new file mode 100644
index 00000000..0e42de4c
--- /dev/null
+++ b/node_modules/svgo/plugins/removeScriptElement.js
@@ -0,0 +1,27 @@
+'use strict';
+
+const { detachNodeFromParent } = require('../lib/xast.js');
+
+exports.name = 'removeScriptElement';
+exports.description = 'removes <script> elements (disabled by default)';
+
+/**
+ * Remove <script>.
+ *
+ * https://www.w3.org/TR/SVG11/script.html
+ *
+ * @author Patrick Klingemann
+ *
+ * @type {import('./plugins-types').Plugin<'removeScriptElement'>}
+ */
+exports.fn = () => {
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ if (node.name === 'script') {
+ detachNodeFromParent(node, parentNode);
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeStyleElement.js b/node_modules/svgo/plugins/removeStyleElement.js
new file mode 100644
index 00000000..d869a50a
--- /dev/null
+++ b/node_modules/svgo/plugins/removeStyleElement.js
@@ -0,0 +1,27 @@
+'use strict';
+
+const { detachNodeFromParent } = require('../lib/xast.js');
+
+exports.name = 'removeStyleElement';
+exports.description = 'removes <style> element (disabled by default)';
+
+/**
+ * Remove <style>.
+ *
+ * https://www.w3.org/TR/SVG11/styling.html#StyleElement
+ *
+ * @author Betsy Dupuis
+ *
+ * @type {import('./plugins-types').Plugin<'removeStyleElement'>}
+ */
+exports.fn = () => {
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ if (node.name === 'style') {
+ detachNodeFromParent(node, parentNode);
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeTitle.js b/node_modules/svgo/plugins/removeTitle.js
new file mode 100644
index 00000000..ee136cd5
--- /dev/null
+++ b/node_modules/svgo/plugins/removeTitle.js
@@ -0,0 +1,27 @@
+'use strict';
+
+const { detachNodeFromParent } = require('../lib/xast.js');
+
+exports.name = 'removeTitle';
+exports.description = 'removes <title>';
+
+/**
+ * Remove <title>.
+ *
+ * https://developer.mozilla.org/en-US/docs/Web/SVG/Element/title
+ *
+ * @author Igor Kalashnikov
+ *
+ * @type {import('./plugins-types').Plugin<'removeTitle'>}
+ */
+exports.fn = () => {
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ if (node.name === 'title') {
+ detachNodeFromParent(node, parentNode);
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeUnknownsAndDefaults.js b/node_modules/svgo/plugins/removeUnknownsAndDefaults.js
new file mode 100644
index 00000000..c67845cd
--- /dev/null
+++ b/node_modules/svgo/plugins/removeUnknownsAndDefaults.js
@@ -0,0 +1,208 @@
+'use strict';
+
+const { visitSkip, detachNodeFromParent } = require('../lib/xast.js');
+const { collectStylesheet, computeStyle } = require('../lib/style.js');
+const {
+ elems,
+ attrsGroups,
+ elemsGroups,
+ attrsGroupsDefaults,
+ presentationNonInheritableGroupAttrs,
+} = require('./_collections');
+
+exports.name = 'removeUnknownsAndDefaults';
+exports.description =
+ 'removes unknown elements content and attributes, removes attrs with default values';
+
+// resolve all groups references
+
+/**
+ * @type {Map<string, Set<string>>}
+ */
+const allowedChildrenPerElement = new Map();
+/**
+ * @type {Map<string, Set<string>>}
+ */
+const allowedAttributesPerElement = new Map();
+/**
+ * @type {Map<string, Map<string, string>>}
+ */
+const attributesDefaultsPerElement = new Map();
+
+for (const [name, config] of Object.entries(elems)) {
+ /**
+ * @type {Set<string>}
+ */
+ const allowedChildren = new Set();
+ if (config.content) {
+ for (const elementName of config.content) {
+ allowedChildren.add(elementName);
+ }
+ }
+ if (config.contentGroups) {
+ for (const contentGroupName of config.contentGroups) {
+ const elemsGroup = elemsGroups[contentGroupName];
+ if (elemsGroup) {
+ for (const elementName of elemsGroup) {
+ allowedChildren.add(elementName);
+ }
+ }
+ }
+ }
+ /**
+ * @type {Set<string>}
+ */
+ const allowedAttributes = new Set();
+ if (config.attrs) {
+ for (const attrName of config.attrs) {
+ allowedAttributes.add(attrName);
+ }
+ }
+ /**
+ * @type {Map<string, string>}
+ */
+ const attributesDefaults = new Map();
+ if (config.defaults) {
+ for (const [attrName, defaultValue] of Object.entries(config.defaults)) {
+ attributesDefaults.set(attrName, defaultValue);
+ }
+ }
+ for (const attrsGroupName of config.attrsGroups) {
+ const attrsGroup = attrsGroups[attrsGroupName];
+ if (attrsGroup) {
+ for (const attrName of attrsGroup) {
+ allowedAttributes.add(attrName);
+ }
+ }
+ const groupDefaults = attrsGroupsDefaults[attrsGroupName];
+ if (groupDefaults) {
+ for (const [attrName, defaultValue] of Object.entries(groupDefaults)) {
+ attributesDefaults.set(attrName, defaultValue);
+ }
+ }
+ }
+ allowedChildrenPerElement.set(name, allowedChildren);
+ allowedAttributesPerElement.set(name, allowedAttributes);
+ attributesDefaultsPerElement.set(name, attributesDefaults);
+}
+
+/**
+ * Remove unknown elements content and attributes,
+ * remove attributes with default values.
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'removeUnknownsAndDefaults'>}
+ */
+exports.fn = (root, params) => {
+ const {
+ unknownContent = true,
+ unknownAttrs = true,
+ defaultAttrs = true,
+ uselessOverrides = true,
+ keepDataAttrs = true,
+ keepAriaAttrs = true,
+ keepRoleAttr = false,
+ } = params;
+ const stylesheet = collectStylesheet(root);
+
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ // skip namespaced elements
+ if (node.name.includes(':')) {
+ return;
+ }
+ // skip visiting foreignObject subtree
+ if (node.name === 'foreignObject') {
+ return visitSkip;
+ }
+
+ // remove unknown element's content
+ if (unknownContent && parentNode.type === 'element') {
+ const allowedChildren = allowedChildrenPerElement.get(
+ parentNode.name
+ );
+ if (allowedChildren == null || allowedChildren.size === 0) {
+ // remove unknown elements
+ if (allowedChildrenPerElement.get(node.name) == null) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+ } else {
+ // remove not allowed children
+ if (allowedChildren.has(node.name) === false) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+ }
+ }
+
+ const allowedAttributes = allowedAttributesPerElement.get(node.name);
+ const attributesDefaults = attributesDefaultsPerElement.get(node.name);
+ const computedParentStyle =
+ parentNode.type === 'element'
+ ? computeStyle(stylesheet, parentNode)
+ : null;
+
+ // remove element's unknown attrs and attrs with default values
+ for (const [name, value] of Object.entries(node.attributes)) {
+ if (keepDataAttrs && name.startsWith('data-')) {
+ continue;
+ }
+ if (keepAriaAttrs && name.startsWith('aria-')) {
+ continue;
+ }
+ if (keepRoleAttr && name === 'role') {
+ continue;
+ }
+ // skip xmlns attribute
+ if (name === 'xmlns') {
+ continue;
+ }
+ // skip namespaced attributes except xml:* and xlink:*
+ if (name.includes(':')) {
+ const [prefix] = name.split(':');
+ if (prefix !== 'xml' && prefix !== 'xlink') {
+ continue;
+ }
+ }
+
+ if (
+ unknownAttrs &&
+ allowedAttributes &&
+ allowedAttributes.has(name) === false
+ ) {
+ delete node.attributes[name];
+ }
+ if (
+ defaultAttrs &&
+ node.attributes.id == null &&
+ attributesDefaults &&
+ attributesDefaults.get(name) === value
+ ) {
+ // keep defaults if parent has own or inherited style
+ if (
+ computedParentStyle == null ||
+ computedParentStyle[name] == null
+ ) {
+ delete node.attributes[name];
+ }
+ }
+ if (uselessOverrides && node.attributes.id == null) {
+ const style =
+ computedParentStyle == null ? null : computedParentStyle[name];
+ if (
+ presentationNonInheritableGroupAttrs.includes(name) === false &&
+ style != null &&
+ style.type === 'static' &&
+ style.value === value
+ ) {
+ delete node.attributes[name];
+ }
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeUnusedNS.js b/node_modules/svgo/plugins/removeUnusedNS.js
new file mode 100644
index 00000000..ac81eeaf
--- /dev/null
+++ b/node_modules/svgo/plugins/removeUnusedNS.js
@@ -0,0 +1,59 @@
+'use strict';
+
+exports.name = 'removeUnusedNS';
+exports.description = 'removes unused namespaces declaration';
+
+/**
+ * Remove unused namespaces declaration from svg element
+ * which are not used in elements or attributes
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'removeUnusedNS'>}
+ */
+exports.fn = () => {
+ /**
+ * @type {Set<string>}
+ */
+ const unusedNamespaces = new Set();
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ // collect all namespaces from svg element
+ // (such as xmlns:xlink="http://www.w3.org/1999/xlink")
+ if (node.name === 'svg' && parentNode.type === 'root') {
+ for (const name of Object.keys(node.attributes)) {
+ if (name.startsWith('xmlns:')) {
+ const local = name.slice('xmlns:'.length);
+ unusedNamespaces.add(local);
+ }
+ }
+ }
+ if (unusedNamespaces.size !== 0) {
+ // preserve namespace used in nested elements names
+ if (node.name.includes(':')) {
+ const [ns] = node.name.split(':');
+ if (unusedNamespaces.has(ns)) {
+ unusedNamespaces.delete(ns);
+ }
+ }
+ // preserve namespace used in nested elements attributes
+ for (const name of Object.keys(node.attributes)) {
+ if (name.includes(':')) {
+ const [ns] = name.split(':');
+ unusedNamespaces.delete(ns);
+ }
+ }
+ }
+ },
+ exit: (node, parentNode) => {
+ // remove unused namespace attributes from svg element
+ if (node.name === 'svg' && parentNode.type === 'root') {
+ for (const name of unusedNamespaces) {
+ delete node.attributes[`xmlns:${name}`];
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeUselessDefs.js b/node_modules/svgo/plugins/removeUselessDefs.js
new file mode 100644
index 00000000..46461a98
--- /dev/null
+++ b/node_modules/svgo/plugins/removeUselessDefs.js
@@ -0,0 +1,65 @@
+'use strict';
+
+/**
+ * @typedef {import('../lib/types').XastElement} XastElement
+ */
+
+const { detachNodeFromParent } = require('../lib/xast.js');
+const { elemsGroups } = require('./_collections.js');
+
+exports.name = 'removeUselessDefs';
+exports.description = 'removes elements in <defs> without id';
+
+/**
+ * Removes content of defs and properties that aren't rendered directly without ids.
+ *
+ * @author Lev Solntsev
+ *
+ * @type {import('./plugins-types').Plugin<'removeUselessDefs'>}
+ */
+exports.fn = () => {
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ if (node.name === 'defs') {
+ /**
+ * @type {Array<XastElement>}
+ */
+ const usefulNodes = [];
+ collectUsefulNodes(node, usefulNodes);
+ if (usefulNodes.length === 0) {
+ detachNodeFromParent(node, parentNode);
+ }
+ // TODO remove legacy parentNode in v4
+ for (const usefulNode of usefulNodes) {
+ Object.defineProperty(usefulNode, 'parentNode', {
+ writable: true,
+ value: node,
+ });
+ }
+ node.children = usefulNodes;
+ } else if (
+ elemsGroups.nonRendering.includes(node.name) &&
+ node.attributes.id == null
+ ) {
+ detachNodeFromParent(node, parentNode);
+ }
+ },
+ },
+ };
+};
+
+/**
+ * @type {(node: XastElement, usefulNodes: Array<XastElement>) => void}
+ */
+const collectUsefulNodes = (node, usefulNodes) => {
+ for (const child of node.children) {
+ if (child.type === 'element') {
+ if (child.attributes.id != null || child.name === 'style') {
+ usefulNodes.push(child);
+ } else {
+ collectUsefulNodes(child, usefulNodes);
+ }
+ }
+ }
+};
diff --git a/node_modules/svgo/plugins/removeUselessStrokeAndFill.js b/node_modules/svgo/plugins/removeUselessStrokeAndFill.js
new file mode 100644
index 00000000..1dff61cc
--- /dev/null
+++ b/node_modules/svgo/plugins/removeUselessStrokeAndFill.js
@@ -0,0 +1,138 @@
+'use strict';
+
+const { visit, visitSkip, detachNodeFromParent } = require('../lib/xast.js');
+const { collectStylesheet, computeStyle } = require('../lib/style.js');
+const { elemsGroups } = require('./_collections.js');
+
+exports.name = 'removeUselessStrokeAndFill';
+exports.description = 'removes useless stroke and fill attributes';
+
+/**
+ * Remove useless stroke and fill attrs.
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'removeUselessStrokeAndFill'>}
+ */
+exports.fn = (root, params) => {
+ const {
+ stroke: removeStroke = true,
+ fill: removeFill = true,
+ removeNone = false,
+ } = params;
+
+ // style and script elements deoptimise this plugin
+ let hasStyleOrScript = false;
+ visit(root, {
+ element: {
+ enter: (node) => {
+ if (node.name === 'style' || node.name === 'script') {
+ hasStyleOrScript = true;
+ }
+ },
+ },
+ });
+ if (hasStyleOrScript) {
+ return null;
+ }
+
+ const stylesheet = collectStylesheet(root);
+
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ // id attribute deoptimise the whole subtree
+ if (node.attributes.id != null) {
+ return visitSkip;
+ }
+ if (elemsGroups.shape.includes(node.name) == false) {
+ return;
+ }
+ const computedStyle = computeStyle(stylesheet, node);
+ const stroke = computedStyle.stroke;
+ const strokeOpacity = computedStyle['stroke-opacity'];
+ const strokeWidth = computedStyle['stroke-width'];
+ const markerEnd = computedStyle['marker-end'];
+ const fill = computedStyle.fill;
+ const fillOpacity = computedStyle['fill-opacity'];
+ const computedParentStyle =
+ parentNode.type === 'element'
+ ? computeStyle(stylesheet, parentNode)
+ : null;
+ const parentStroke =
+ computedParentStyle == null ? null : computedParentStyle.stroke;
+
+ // remove stroke*
+ if (removeStroke) {
+ if (
+ stroke == null ||
+ (stroke.type === 'static' && stroke.value == 'none') ||
+ (strokeOpacity != null &&
+ strokeOpacity.type === 'static' &&
+ strokeOpacity.value === '0') ||
+ (strokeWidth != null &&
+ strokeWidth.type === 'static' &&
+ strokeWidth.value === '0')
+ ) {
+ // stroke-width may affect the size of marker-end
+ // marker is not visible when stroke-width is 0
+ if (
+ (strokeWidth != null &&
+ strokeWidth.type === 'static' &&
+ strokeWidth.value === '0') ||
+ markerEnd == null
+ ) {
+ for (const name of Object.keys(node.attributes)) {
+ if (name.startsWith('stroke')) {
+ delete node.attributes[name];
+ }
+ }
+ // set explicit none to not inherit from parent
+ if (
+ parentStroke != null &&
+ parentStroke.type === 'static' &&
+ parentStroke.value !== 'none'
+ ) {
+ node.attributes.stroke = 'none';
+ }
+ }
+ }
+ }
+
+ // remove fill*
+ if (removeFill) {
+ if (
+ (fill != null && fill.type === 'static' && fill.value === 'none') ||
+ (fillOpacity != null &&
+ fillOpacity.type === 'static' &&
+ fillOpacity.value === '0')
+ ) {
+ for (const name of Object.keys(node.attributes)) {
+ if (name.startsWith('fill-')) {
+ delete node.attributes[name];
+ }
+ }
+ if (
+ fill == null ||
+ (fill.type === 'static' && fill.value !== 'none')
+ ) {
+ node.attributes.fill = 'none';
+ }
+ }
+ }
+
+ if (removeNone) {
+ if (
+ (stroke == null || node.attributes.stroke === 'none') &&
+ ((fill != null &&
+ fill.type === 'static' &&
+ fill.value === 'none') ||
+ node.attributes.fill === 'none')
+ ) {
+ detachNodeFromParent(node, parentNode);
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeViewBox.js b/node_modules/svgo/plugins/removeViewBox.js
new file mode 100644
index 00000000..5618d8bd
--- /dev/null
+++ b/node_modules/svgo/plugins/removeViewBox.js
@@ -0,0 +1,49 @@
+'use strict';
+
+exports.name = 'removeViewBox';
+exports.description = 'removes viewBox attribute when possible';
+
+const viewBoxElems = ['svg', 'pattern', 'symbol'];
+
+/**
+ * Remove viewBox attr which coincides with a width/height box.
+ *
+ * @see https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute
+ *
+ * @example
+ * <svg width="100" height="50" viewBox="0 0 100 50">
+ * ⬇
+ * <svg width="100" height="50">
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'removeViewBox'>}
+ */
+exports.fn = () => {
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ if (
+ viewBoxElems.includes(node.name) &&
+ node.attributes.viewBox != null &&
+ node.attributes.width != null &&
+ node.attributes.height != null
+ ) {
+ // TODO remove width/height for such case instead
+ if (node.name === 'svg' && parentNode.type !== 'root') {
+ return;
+ }
+ const nums = node.attributes.viewBox.split(/[ ,]+/g);
+ if (
+ nums[0] === '0' &&
+ nums[1] === '0' &&
+ node.attributes.width.replace(/px$/, '') === nums[2] && // could use parseFloat too
+ node.attributes.height.replace(/px$/, '') === nums[3]
+ ) {
+ delete node.attributes.viewBox;
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeXMLNS.js b/node_modules/svgo/plugins/removeXMLNS.js
new file mode 100644
index 00000000..6312629d
--- /dev/null
+++ b/node_modules/svgo/plugins/removeXMLNS.js
@@ -0,0 +1,30 @@
+'use strict';
+
+exports.name = 'removeXMLNS';
+exports.description =
+ 'removes xmlns attribute (for inline svg, disabled by default)';
+
+/**
+ * Remove the xmlns attribute when present.
+ *
+ * @example
+ * <svg viewBox="0 0 100 50" xmlns="http://www.w3.org/2000/svg">
+ * ↓
+ * <svg viewBox="0 0 100 50">
+ *
+ * @author Ricardo Tomasi
+ *
+ * @type {import('./plugins-types').Plugin<'removeXMLNS'>}
+ */
+exports.fn = () => {
+ return {
+ element: {
+ enter: (node) => {
+ if (node.name === 'svg') {
+ delete node.attributes.xmlns;
+ delete node.attributes['xmlns:xlink'];
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/removeXMLProcInst.js b/node_modules/svgo/plugins/removeXMLProcInst.js
new file mode 100644
index 00000000..73ec2335
--- /dev/null
+++ b/node_modules/svgo/plugins/removeXMLProcInst.js
@@ -0,0 +1,28 @@
+'use strict';
+
+const { detachNodeFromParent } = require('../lib/xast.js');
+
+exports.name = 'removeXMLProcInst';
+exports.description = 'removes XML processing instructions';
+
+/**
+ * Remove XML Processing Instruction.
+ *
+ * @example
+ * <?xml version="1.0" encoding="utf-8"?>
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'removeXMLProcInst'>}
+ */
+exports.fn = () => {
+ return {
+ instruction: {
+ enter: (node, parentNode) => {
+ if (node.name === 'xml') {
+ detachNodeFromParent(node, parentNode);
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/reusePaths.js b/node_modules/svgo/plugins/reusePaths.js
new file mode 100644
index 00000000..4d91d6f9
--- /dev/null
+++ b/node_modules/svgo/plugins/reusePaths.js
@@ -0,0 +1,111 @@
+'use strict';
+
+/**
+ * @typedef {import('../lib/types').XastElement} XastElement
+ * @typedef {import('../lib/types').XastParent} XastParent
+ * @typedef {import('../lib/types').XastNode} XastNode
+ */
+
+exports.name = 'reusePaths';
+exports.description =
+ 'Finds <path> elements with the same d, fill, and ' +
+ 'stroke, and converts them to <use> elements ' +
+ 'referencing a single <path> def.';
+
+/**
+ * Finds <path> elements with the same d, fill, and stroke, and converts them to
+ * <use> elements referencing a single <path> def.
+ *
+ * @author Jacob Howcroft
+ *
+ * @type {import('./plugins-types').Plugin<'reusePaths'>}
+ */
+exports.fn = () => {
+ /**
+ * @type {Map<string, Array<XastElement>>}
+ */
+ const paths = new Map();
+
+ return {
+ element: {
+ enter: (node) => {
+ if (node.name === 'path' && node.attributes.d != null) {
+ const d = node.attributes.d;
+ const fill = node.attributes.fill || '';
+ const stroke = node.attributes.stroke || '';
+ const key = d + ';s:' + stroke + ';f:' + fill;
+ let list = paths.get(key);
+ if (list == null) {
+ list = [];
+ paths.set(key, list);
+ }
+ list.push(node);
+ }
+ },
+
+ exit: (node, parentNode) => {
+ if (node.name === 'svg' && parentNode.type === 'root') {
+ /**
+ * @type {XastElement}
+ */
+ const defsTag = {
+ type: 'element',
+ name: 'defs',
+ attributes: {},
+ children: [],
+ };
+ // TODO remove legacy parentNode in v4
+ Object.defineProperty(defsTag, 'parentNode', {
+ writable: true,
+ value: node,
+ });
+ let index = 0;
+ for (const list of paths.values()) {
+ if (list.length > 1) {
+ // add reusable path to defs
+ /**
+ * @type {XastElement}
+ */
+ const reusablePath = {
+ type: 'element',
+ name: 'path',
+ attributes: { ...list[0].attributes },
+ children: [],
+ };
+ delete reusablePath.attributes.transform;
+ let id;
+ if (reusablePath.attributes.id == null) {
+ id = 'reuse-' + index;
+ index += 1;
+ reusablePath.attributes.id = id;
+ } else {
+ id = reusablePath.attributes.id;
+ delete list[0].attributes.id;
+ }
+ // TODO remove legacy parentNode in v4
+ Object.defineProperty(reusablePath, 'parentNode', {
+ writable: true,
+ value: defsTag,
+ });
+ defsTag.children.push(reusablePath);
+ // convert paths to <use>
+ for (const pathNode of list) {
+ pathNode.name = 'use';
+ pathNode.attributes['xlink:href'] = '#' + id;
+ delete pathNode.attributes.d;
+ delete pathNode.attributes.stroke;
+ delete pathNode.attributes.fill;
+ }
+ }
+ }
+ if (defsTag.children.length !== 0) {
+ if (node.attributes['xmlns:xlink'] == null) {
+ node.attributes['xmlns:xlink'] = 'http://www.w3.org/1999/xlink';
+ }
+ node.children.unshift(defsTag);
+ }
+ }
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/sortAttrs.js b/node_modules/svgo/plugins/sortAttrs.js
new file mode 100644
index 00000000..6347f65e
--- /dev/null
+++ b/node_modules/svgo/plugins/sortAttrs.js
@@ -0,0 +1,108 @@
+'use strict';
+
+exports.name = 'sortAttrs';
+exports.description = 'Sort element attributes for better compression';
+
+/**
+ * Sort element attributes for better compression
+ *
+ * @author Nikolay Frantsev
+ *
+ * @type {import('./plugins-types').Plugin<'sortAttrs'>}
+ */
+exports.fn = (_root, params) => {
+ const {
+ order = [
+ 'id',
+ 'width',
+ 'height',
+ 'x',
+ 'x1',
+ 'x2',
+ 'y',
+ 'y1',
+ 'y2',
+ 'cx',
+ 'cy',
+ 'r',
+ 'fill',
+ 'stroke',
+ 'marker',
+ 'd',
+ 'points',
+ ],
+ xmlnsOrder = 'front',
+ } = params;
+
+ /**
+ * @type {(name: string) => number}
+ */
+ const getNsPriority = (name) => {
+ if (xmlnsOrder === 'front') {
+ // put xmlns first
+ if (name === 'xmlns') {
+ return 3;
+ }
+ // xmlns:* attributes second
+ if (name.startsWith('xmlns:')) {
+ return 2;
+ }
+ }
+ // other namespaces after and sort them alphabetically
+ if (name.includes(':')) {
+ return 1;
+ }
+ // other attributes
+ return 0;
+ };
+
+ /**
+ * @type {(a: [string, string], b: [string, string]) => number}
+ */
+ const compareAttrs = ([aName], [bName]) => {
+ // sort namespaces
+ const aPriority = getNsPriority(aName);
+ const bPriority = getNsPriority(bName);
+ const priorityNs = bPriority - aPriority;
+ if (priorityNs !== 0) {
+ return priorityNs;
+ }
+ // extract the first part from attributes
+ // for example "fill" from "fill" and "fill-opacity"
+ const [aPart] = aName.split('-');
+ const [bPart] = bName.split('-');
+ // rely on alphabetical sort when the first part is the same
+ if (aPart !== bPart) {
+ const aInOrderFlag = order.includes(aPart) ? 1 : 0;
+ const bInOrderFlag = order.includes(bPart) ? 1 : 0;
+ // sort by position in order param
+ if (aInOrderFlag === 1 && bInOrderFlag === 1) {
+ return order.indexOf(aPart) - order.indexOf(bPart);
+ }
+ // put attributes from order param before others
+ const priorityOrder = bInOrderFlag - aInOrderFlag;
+ if (priorityOrder !== 0) {
+ return priorityOrder;
+ }
+ }
+ // sort alphabetically
+ return aName < bName ? -1 : 1;
+ };
+
+ return {
+ element: {
+ enter: (node) => {
+ const attrs = Object.entries(node.attributes);
+ attrs.sort(compareAttrs);
+ /**
+ * @type {Record<string, string>}
+ */
+ const sortedAttributes = {};
+ for (const [name, value] of attrs) {
+ sortedAttributes[name] = value;
+ }
+ node.attributes = sortedAttributes;
+ },
+ },
+ };
+};
diff --git a/node_modules/svgo/plugins/sortDefsChildren.js b/node_modules/svgo/plugins/sortDefsChildren.js
new file mode 100644
index 00000000..6ae5d421
--- /dev/null
+++ b/node_modules/svgo/plugins/sortDefsChildren.js
@@ -0,0 +1,58 @@
+'use strict';
+
+exports.name = 'sortDefsChildren';
+exports.description = 'Sorts children of <defs> to improve compression';
+
+/**
+ * Sorts children of defs in order to improve compression.
+ * Sorted first by frequency then by element name length then by element name (to ensure grouping).
+ *
+ * @author David Leston
+ *
+ * @type {import('./plugins-types').Plugin<'sortDefsChildren'>}
+ */
+exports.fn = () => {
+ return {
+ element: {
+ enter: (node) => {
+ if (node.name === 'defs') {
+ /**
+ * @type {Map<string, number>}
+ */
+ const frequencies = new Map();
+ for (const child of node.children) {
+ if (child.type === 'element') {
+ const frequency = frequencies.get(child.name);
+ if (frequency == null) {
+ frequencies.set(child.name, 1);
+ } else {
+ frequencies.set(child.name, frequency + 1);
+ }
+ }
+ }
+ node.children.sort((a, b) => {
+ if (a.type !== 'element' || b.type !== 'element') {
+ return 0;
+ }
+ const aFrequency = frequencies.get(a.name);
+ const bFrequency = frequencies.get(b.name);
+ if (aFrequency != null && bFrequency != null) {
+ const frequencyComparison = bFrequency - aFrequency;
+ if (frequencyComparison !== 0) {
+ return frequencyComparison;
+ }
+ }
+ const lengthComparison = b.name.length - a.name.length;
+ if (lengthComparison !== 0) {
+ return lengthComparison;
+ }
+ if (a.name !== b.name) {
+ return a.name > b.name ? -1 : 1;
+ }
+ return 0;
+ });
+ }
+ },
+ },
+ };
+};