svgo-v2 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +294 -0
- package/bin/svgo +10 -0
- package/dist/svgo.browser.js +1 -0
- package/lib/css-tools.js +239 -0
- package/lib/parser.js +259 -0
- package/lib/path.js +347 -0
- package/lib/stringifier.js +326 -0
- package/lib/style.js +283 -0
- package/lib/svgo/coa.js +517 -0
- package/lib/svgo/config.js +138 -0
- package/lib/svgo/css-class-list.js +72 -0
- package/lib/svgo/css-select-adapter.d.ts +2 -0
- package/lib/svgo/css-select-adapter.js +120 -0
- package/lib/svgo/css-style-declaration.js +232 -0
- package/lib/svgo/jsAPI.d.ts +2 -0
- package/lib/svgo/jsAPI.js +443 -0
- package/lib/svgo/plugins.js +109 -0
- package/lib/svgo/tools.js +137 -0
- package/lib/svgo-node.js +106 -0
- package/lib/svgo.js +83 -0
- package/lib/types.ts +172 -0
- package/lib/xast.js +102 -0
- package/package.json +130 -0
- package/plugins/_applyTransforms.js +335 -0
- package/plugins/_collections.js +2168 -0
- package/plugins/_path.js +816 -0
- package/plugins/_transforms.js +379 -0
- package/plugins/addAttributesToSVGElement.js +87 -0
- package/plugins/addClassesToSVGElement.js +87 -0
- package/plugins/cleanupAttrs.js +55 -0
- package/plugins/cleanupEnableBackground.js +75 -0
- package/plugins/cleanupIDs.js +297 -0
- package/plugins/cleanupListOfValues.js +154 -0
- package/plugins/cleanupNumericValues.js +113 -0
- package/plugins/collapseGroups.js +135 -0
- package/plugins/convertColors.js +152 -0
- package/plugins/convertEllipseToCircle.js +39 -0
- package/plugins/convertPathData.js +1023 -0
- package/plugins/convertShapeToPath.js +175 -0
- package/plugins/convertStyleToAttrs.js +132 -0
- package/plugins/convertTransform.js +432 -0
- package/plugins/inlineStyles.js +379 -0
- package/plugins/mergePaths.js +104 -0
- package/plugins/mergeStyles.js +93 -0
- package/plugins/minifyStyles.js +148 -0
- package/plugins/moveElemsAttrsToGroup.js +130 -0
- package/plugins/moveGroupAttrsToElems.js +62 -0
- package/plugins/plugins.js +56 -0
- package/plugins/prefixIds.js +241 -0
- package/plugins/preset-default.js +80 -0
- package/plugins/removeAttributesBySelector.js +99 -0
- package/plugins/removeAttrs.js +159 -0
- package/plugins/removeComments.js +31 -0
- package/plugins/removeDesc.js +41 -0
- package/plugins/removeDimensions.js +43 -0
- package/plugins/removeDoctype.js +42 -0
- package/plugins/removeEditorsNSData.js +68 -0
- package/plugins/removeElementsByAttr.js +78 -0
- package/plugins/removeEmptyAttrs.js +33 -0
- package/plugins/removeEmptyContainers.js +58 -0
- package/plugins/removeEmptyText.js +57 -0
- package/plugins/removeHiddenElems.js +318 -0
- package/plugins/removeMetadata.js +29 -0
- package/plugins/removeNonInheritableGroupAttrs.js +38 -0
- package/plugins/removeOffCanvasPaths.js +138 -0
- package/plugins/removeRasterImages.js +33 -0
- package/plugins/removeScriptElement.js +29 -0
- package/plugins/removeStyleElement.js +29 -0
- package/plugins/removeTitle.js +29 -0
- package/plugins/removeUnknownsAndDefaults.js +218 -0
- package/plugins/removeUnusedNS.js +61 -0
- package/plugins/removeUselessDefs.js +65 -0
- package/plugins/removeUselessStrokeAndFill.js +144 -0
- package/plugins/removeViewBox.js +51 -0
- package/plugins/removeXMLNS.js +30 -0
- package/plugins/removeXMLProcInst.js +30 -0
- package/plugins/reusePaths.js +113 -0
- package/plugins/sortAttrs.js +113 -0
- package/plugins/sortDefsChildren.js +60 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {import('../lib/types').PathDataItem} PathDataItem
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { stringifyPathData } = require('../lib/path.js');
|
|
8
|
+
const { detachNodeFromParent } = require('../lib/xast.js');
|
|
9
|
+
|
|
10
|
+
exports.name = 'convertShapeToPath';
|
|
11
|
+
exports.type = 'visitor';
|
|
12
|
+
exports.active = true;
|
|
13
|
+
exports.description = 'converts basic shapes to more compact path form';
|
|
14
|
+
|
|
15
|
+
const regNumber = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Converts basic shape to more compact path.
|
|
19
|
+
* It also allows further optimizations like
|
|
20
|
+
* combining paths with similar attributes.
|
|
21
|
+
*
|
|
22
|
+
* @see https://www.w3.org/TR/SVG11/shapes.html
|
|
23
|
+
*
|
|
24
|
+
* @author Lev Solntsev
|
|
25
|
+
*
|
|
26
|
+
* @type {import('../lib/types').Plugin<{
|
|
27
|
+
* convertArcs?: boolean,
|
|
28
|
+
* floatPrecision?: number
|
|
29
|
+
* }>}
|
|
30
|
+
*/
|
|
31
|
+
exports.fn = (root, params) => {
|
|
32
|
+
const { convertArcs = false, floatPrecision: precision } = params;
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
element: {
|
|
36
|
+
enter: (node, parentNode) => {
|
|
37
|
+
// convert rect to path
|
|
38
|
+
if (
|
|
39
|
+
node.name === 'rect' &&
|
|
40
|
+
node.attributes.width != null &&
|
|
41
|
+
node.attributes.height != null &&
|
|
42
|
+
node.attributes.rx == null &&
|
|
43
|
+
node.attributes.ry == null
|
|
44
|
+
) {
|
|
45
|
+
const x = Number(node.attributes.x || '0');
|
|
46
|
+
const y = Number(node.attributes.y || '0');
|
|
47
|
+
const width = Number(node.attributes.width);
|
|
48
|
+
const height = Number(node.attributes.height);
|
|
49
|
+
// Values like '100%' compute to NaN, thus running after
|
|
50
|
+
// cleanupNumericValues when 'px' units has already been removed.
|
|
51
|
+
// TODO: Calculate sizes from % and non-px units if possible.
|
|
52
|
+
if (Number.isNaN(x - y + width - height)) return;
|
|
53
|
+
/**
|
|
54
|
+
* @type {Array<PathDataItem>}
|
|
55
|
+
*/
|
|
56
|
+
const pathData = [
|
|
57
|
+
{ command: 'M', args: [x, y] },
|
|
58
|
+
{ command: 'H', args: [x + width] },
|
|
59
|
+
{ command: 'V', args: [y + height] },
|
|
60
|
+
{ command: 'H', args: [x] },
|
|
61
|
+
{ command: 'z', args: [] },
|
|
62
|
+
];
|
|
63
|
+
node.name = 'path';
|
|
64
|
+
node.attributes.d = stringifyPathData({ pathData, precision });
|
|
65
|
+
delete node.attributes.x;
|
|
66
|
+
delete node.attributes.y;
|
|
67
|
+
delete node.attributes.width;
|
|
68
|
+
delete node.attributes.height;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// convert line to path
|
|
72
|
+
if (node.name === 'line') {
|
|
73
|
+
const x1 = Number(node.attributes.x1 || '0');
|
|
74
|
+
const y1 = Number(node.attributes.y1 || '0');
|
|
75
|
+
const x2 = Number(node.attributes.x2 || '0');
|
|
76
|
+
const y2 = Number(node.attributes.y2 || '0');
|
|
77
|
+
if (Number.isNaN(x1 - y1 + x2 - y2)) return;
|
|
78
|
+
/**
|
|
79
|
+
* @type {Array<PathDataItem>}
|
|
80
|
+
*/
|
|
81
|
+
const pathData = [
|
|
82
|
+
{ command: 'M', args: [x1, y1] },
|
|
83
|
+
{ command: 'L', args: [x2, y2] },
|
|
84
|
+
];
|
|
85
|
+
node.name = 'path';
|
|
86
|
+
node.attributes.d = stringifyPathData({ pathData, precision });
|
|
87
|
+
delete node.attributes.x1;
|
|
88
|
+
delete node.attributes.y1;
|
|
89
|
+
delete node.attributes.x2;
|
|
90
|
+
delete node.attributes.y2;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// convert polyline and polygon to path
|
|
94
|
+
if (
|
|
95
|
+
(node.name === 'polyline' || node.name === 'polygon') &&
|
|
96
|
+
node.attributes.points != null
|
|
97
|
+
) {
|
|
98
|
+
const coords = (node.attributes.points.match(regNumber) || []).map(
|
|
99
|
+
Number
|
|
100
|
+
);
|
|
101
|
+
if (coords.length < 4) {
|
|
102
|
+
detachNodeFromParent(node, parentNode);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* @type {Array<PathDataItem>}
|
|
107
|
+
*/
|
|
108
|
+
const pathData = [];
|
|
109
|
+
for (let i = 0; i < coords.length; i += 2) {
|
|
110
|
+
pathData.push({
|
|
111
|
+
command: i === 0 ? 'M' : 'L',
|
|
112
|
+
args: coords.slice(i, i + 2),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (node.name === 'polygon') {
|
|
116
|
+
pathData.push({ command: 'z', args: [] });
|
|
117
|
+
}
|
|
118
|
+
node.name = 'path';
|
|
119
|
+
node.attributes.d = stringifyPathData({ pathData, precision });
|
|
120
|
+
delete node.attributes.points;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// optionally convert circle
|
|
124
|
+
if (node.name === 'circle' && convertArcs) {
|
|
125
|
+
const cx = Number(node.attributes.cx || '0');
|
|
126
|
+
const cy = Number(node.attributes.cy || '0');
|
|
127
|
+
const r = Number(node.attributes.r || '0');
|
|
128
|
+
if (Number.isNaN(cx - cy + r)) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* @type {Array<PathDataItem>}
|
|
133
|
+
*/
|
|
134
|
+
const pathData = [
|
|
135
|
+
{ command: 'M', args: [cx, cy - r] },
|
|
136
|
+
{ command: 'A', args: [r, r, 0, 1, 0, cx, cy + r] },
|
|
137
|
+
{ command: 'A', args: [r, r, 0, 1, 0, cx, cy - r] },
|
|
138
|
+
{ command: 'z', args: [] },
|
|
139
|
+
];
|
|
140
|
+
node.name = 'path';
|
|
141
|
+
node.attributes.d = stringifyPathData({ pathData, precision });
|
|
142
|
+
delete node.attributes.cx;
|
|
143
|
+
delete node.attributes.cy;
|
|
144
|
+
delete node.attributes.r;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// optionally covert ellipse
|
|
148
|
+
if (node.name === 'ellipse' && convertArcs) {
|
|
149
|
+
const ecx = Number(node.attributes.cx || '0');
|
|
150
|
+
const ecy = Number(node.attributes.cy || '0');
|
|
151
|
+
const rx = Number(node.attributes.rx || '0');
|
|
152
|
+
const ry = Number(node.attributes.ry || '0');
|
|
153
|
+
if (Number.isNaN(ecx - ecy + rx - ry)) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* @type {Array<PathDataItem>}
|
|
158
|
+
*/
|
|
159
|
+
const pathData = [
|
|
160
|
+
{ command: 'M', args: [ecx, ecy - ry] },
|
|
161
|
+
{ command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy + ry] },
|
|
162
|
+
{ command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy - ry] },
|
|
163
|
+
{ command: 'z', args: [] },
|
|
164
|
+
];
|
|
165
|
+
node.name = 'path';
|
|
166
|
+
node.attributes.d = stringifyPathData({ pathData, precision });
|
|
167
|
+
delete node.attributes.cx;
|
|
168
|
+
delete node.attributes.cy;
|
|
169
|
+
delete node.attributes.rx;
|
|
170
|
+
delete node.attributes.ry;
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
exports.name = 'convertStyleToAttrs';
|
|
4
|
+
|
|
5
|
+
exports.type = 'perItem';
|
|
6
|
+
|
|
7
|
+
exports.active = false;
|
|
8
|
+
|
|
9
|
+
exports.description = 'converts style to attributes';
|
|
10
|
+
|
|
11
|
+
exports.params = {
|
|
12
|
+
keepImportant: false,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
var stylingProps = require('./_collections').attrsGroups.presentation,
|
|
16
|
+
rEscape = '\\\\(?:[0-9a-f]{1,6}\\s?|\\r\\n|.)', // Like \" or \2051. Code points consume one space.
|
|
17
|
+
rAttr = '\\s*(' + g('[^:;\\\\]', rEscape) + '*?)\\s*', // attribute name like ‘fill’
|
|
18
|
+
rSingleQuotes = "'(?:[^'\\n\\r\\\\]|" + rEscape + ")*?(?:'|$)", // string in single quotes: 'smth'
|
|
19
|
+
rQuotes = '"(?:[^"\\n\\r\\\\]|' + rEscape + ')*?(?:"|$)', // string in double quotes: "smth"
|
|
20
|
+
rQuotedString = new RegExp('^' + g(rSingleQuotes, rQuotes) + '$'),
|
|
21
|
+
// Parentheses, E.g.: url(data:image/png;base64,iVBO...).
|
|
22
|
+
// ':' and ';' inside of it should be threated as is. (Just like in strings.)
|
|
23
|
+
rParenthesis =
|
|
24
|
+
'\\(' + g('[^\'"()\\\\]+', rEscape, rSingleQuotes, rQuotes) + '*?' + '\\)',
|
|
25
|
+
// The value. It can have strings and parentheses (see above). Fallbacks to anything in case of unexpected input.
|
|
26
|
+
rValue =
|
|
27
|
+
'\\s*(' +
|
|
28
|
+
g(
|
|
29
|
+
'[^!\'"();\\\\]+?',
|
|
30
|
+
rEscape,
|
|
31
|
+
rSingleQuotes,
|
|
32
|
+
rQuotes,
|
|
33
|
+
rParenthesis,
|
|
34
|
+
'[^;]*?'
|
|
35
|
+
) +
|
|
36
|
+
'*?' +
|
|
37
|
+
')',
|
|
38
|
+
// End of declaration. Spaces outside of capturing groups help to do natural trimming.
|
|
39
|
+
rDeclEnd = '\\s*(?:;\\s*|$)',
|
|
40
|
+
// Important rule
|
|
41
|
+
rImportant = '(\\s*!important(?![-(\\w]))?',
|
|
42
|
+
// Final RegExp to parse CSS declarations.
|
|
43
|
+
regDeclarationBlock = new RegExp(
|
|
44
|
+
rAttr + ':' + rValue + rImportant + rDeclEnd,
|
|
45
|
+
'ig'
|
|
46
|
+
),
|
|
47
|
+
// Comments expression. Honors escape sequences and strings.
|
|
48
|
+
regStripComments = new RegExp(
|
|
49
|
+
g(rEscape, rSingleQuotes, rQuotes, '/\\*[^]*?\\*/'),
|
|
50
|
+
'ig'
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Convert style in attributes. Cleanups comments and illegal declarations (without colon) as a side effect.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* <g style="fill:#000; color: #fff;">
|
|
58
|
+
* ⬇
|
|
59
|
+
* <g fill="#000" color="#fff">
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* <g style="fill:#000; color: #fff; -webkit-blah: blah">
|
|
63
|
+
* ⬇
|
|
64
|
+
* <g fill="#000" color="#fff" style="-webkit-blah: blah">
|
|
65
|
+
*
|
|
66
|
+
* @param {Object} item current iteration item
|
|
67
|
+
* @return {Boolean} if false, item will be filtered out
|
|
68
|
+
*
|
|
69
|
+
* @author Kir Belevich
|
|
70
|
+
*/
|
|
71
|
+
exports.fn = function (item, params) {
|
|
72
|
+
if (item.type === 'element' && item.attributes.style != null) {
|
|
73
|
+
// ['opacity: 1', 'color: #000']
|
|
74
|
+
let styles = [];
|
|
75
|
+
const newAttributes = {};
|
|
76
|
+
|
|
77
|
+
// Strip CSS comments preserving escape sequences and strings.
|
|
78
|
+
const styleValue = item.attributes.style.replace(
|
|
79
|
+
regStripComments,
|
|
80
|
+
(match) => {
|
|
81
|
+
return match[0] == '/'
|
|
82
|
+
? ''
|
|
83
|
+
: match[0] == '\\' && /[-g-z]/i.test(match[1])
|
|
84
|
+
? match[1]
|
|
85
|
+
: match;
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
regDeclarationBlock.lastIndex = 0;
|
|
90
|
+
// eslint-disable-next-line no-cond-assign
|
|
91
|
+
for (var rule; (rule = regDeclarationBlock.exec(styleValue)); ) {
|
|
92
|
+
if (!params.keepImportant || !rule[3]) {
|
|
93
|
+
styles.push([rule[1], rule[2]]);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (styles.length) {
|
|
98
|
+
styles = styles.filter(function (style) {
|
|
99
|
+
if (style[0]) {
|
|
100
|
+
var prop = style[0].toLowerCase(),
|
|
101
|
+
val = style[1];
|
|
102
|
+
|
|
103
|
+
if (rQuotedString.test(val)) {
|
|
104
|
+
val = val.slice(1, -1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (stylingProps.includes(prop)) {
|
|
108
|
+
newAttributes[prop] = val;
|
|
109
|
+
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return true;
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
Object.assign(item.attributes, newAttributes);
|
|
118
|
+
|
|
119
|
+
if (styles.length) {
|
|
120
|
+
item.attributes.style = styles
|
|
121
|
+
.map((declaration) => declaration.join(':'))
|
|
122
|
+
.join(';');
|
|
123
|
+
} else {
|
|
124
|
+
delete item.attributes.style;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
function g() {
|
|
131
|
+
return '(?:' + Array.prototype.join.call(arguments, '|') + ')';
|
|
132
|
+
}
|