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,58 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { detachNodeFromParent } = require('../lib/xast.js');
|
|
4
|
+
const { elemsGroups } = require('./_collections.js');
|
|
5
|
+
|
|
6
|
+
exports.type = 'visitor';
|
|
7
|
+
exports.name = 'removeEmptyContainers';
|
|
8
|
+
exports.active = true;
|
|
9
|
+
exports.description = 'removes empty container elements';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Remove empty containers.
|
|
13
|
+
*
|
|
14
|
+
* @see https://www.w3.org/TR/SVG11/intro.html#TermContainerElement
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* <defs/>
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* <g><marker><a/></marker></g>
|
|
21
|
+
*
|
|
22
|
+
* @author Kir Belevich
|
|
23
|
+
*
|
|
24
|
+
* @type {import('../lib/types').Plugin<void>}
|
|
25
|
+
*/
|
|
26
|
+
exports.fn = () => {
|
|
27
|
+
return {
|
|
28
|
+
element: {
|
|
29
|
+
exit: (node, parentNode) => {
|
|
30
|
+
// remove only empty non-svg containers
|
|
31
|
+
if (
|
|
32
|
+
node.name === 'svg' ||
|
|
33
|
+
elemsGroups.container.includes(node.name) === false ||
|
|
34
|
+
node.children.length !== 0
|
|
35
|
+
) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// empty patterns may contain reusable configuration
|
|
39
|
+
if (
|
|
40
|
+
node.name === 'pattern' &&
|
|
41
|
+
Object.keys(node.attributes).length !== 0
|
|
42
|
+
) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
// The <g> may not have content, but the filter may cause a rectangle
|
|
46
|
+
// to be created and filled with pattern.
|
|
47
|
+
if (node.name === 'g' && node.attributes.filter != null) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// empty <mask> hides masked element
|
|
51
|
+
if (node.name === 'mask' && node.attributes.id != null) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
detachNodeFromParent(node, parentNode);
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { detachNodeFromParent } = require('../lib/xast.js');
|
|
4
|
+
|
|
5
|
+
exports.name = 'removeEmptyText';
|
|
6
|
+
exports.type = 'visitor';
|
|
7
|
+
exports.active = true;
|
|
8
|
+
exports.description = 'removes empty <text> elements';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Remove empty Text elements.
|
|
12
|
+
*
|
|
13
|
+
* @see https://www.w3.org/TR/SVG11/text.html
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* Remove empty text element:
|
|
17
|
+
* <text/>
|
|
18
|
+
*
|
|
19
|
+
* Remove empty tspan element:
|
|
20
|
+
* <tspan/>
|
|
21
|
+
*
|
|
22
|
+
* Remove tref with empty xlink:href attribute:
|
|
23
|
+
* <tref xlink:href=""/>
|
|
24
|
+
*
|
|
25
|
+
* @author Kir Belevich
|
|
26
|
+
*
|
|
27
|
+
* @type {import('../lib/types').Plugin<{
|
|
28
|
+
* text?: boolean,
|
|
29
|
+
* tspan?: boolean,
|
|
30
|
+
* tref?: boolean
|
|
31
|
+
* }>}
|
|
32
|
+
*/
|
|
33
|
+
exports.fn = (root, params) => {
|
|
34
|
+
const { text = true, tspan = true, tref = true } = params;
|
|
35
|
+
return {
|
|
36
|
+
element: {
|
|
37
|
+
enter: (node, parentNode) => {
|
|
38
|
+
// Remove empty text element
|
|
39
|
+
if (text && node.name === 'text' && node.children.length === 0) {
|
|
40
|
+
detachNodeFromParent(node, parentNode);
|
|
41
|
+
}
|
|
42
|
+
// Remove empty tspan element
|
|
43
|
+
if (tspan && node.name === 'tspan' && node.children.length === 0) {
|
|
44
|
+
detachNodeFromParent(node, parentNode);
|
|
45
|
+
}
|
|
46
|
+
// Remove tref with empty xlink:href attribute
|
|
47
|
+
if (
|
|
48
|
+
tref &&
|
|
49
|
+
node.name === 'tref' &&
|
|
50
|
+
node.attributes['xlink:href'] == null
|
|
51
|
+
) {
|
|
52
|
+
detachNodeFromParent(node, parentNode);
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
};
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
querySelector,
|
|
5
|
+
closestByName,
|
|
6
|
+
detachNodeFromParent,
|
|
7
|
+
} = require('../lib/xast.js');
|
|
8
|
+
const { collectStylesheet, computeStyle } = require('../lib/style.js');
|
|
9
|
+
const { parsePathData } = require('../lib/path.js');
|
|
10
|
+
|
|
11
|
+
exports.name = 'removeHiddenElems';
|
|
12
|
+
exports.type = 'visitor';
|
|
13
|
+
exports.active = true;
|
|
14
|
+
exports.description =
|
|
15
|
+
'removes hidden elements (zero sized, with absent attributes)';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Remove hidden elements with disabled rendering:
|
|
19
|
+
* - display="none"
|
|
20
|
+
* - opacity="0"
|
|
21
|
+
* - circle with zero radius
|
|
22
|
+
* - ellipse with zero x-axis or y-axis radius
|
|
23
|
+
* - rectangle with zero width or height
|
|
24
|
+
* - pattern with zero width or height
|
|
25
|
+
* - image with zero width or height
|
|
26
|
+
* - path with empty data
|
|
27
|
+
* - polyline with empty points
|
|
28
|
+
* - polygon with empty points
|
|
29
|
+
*
|
|
30
|
+
* @author Kir Belevich
|
|
31
|
+
*
|
|
32
|
+
* @type {import('../lib/types').Plugin<{
|
|
33
|
+
* isHidden: boolean,
|
|
34
|
+
* displayNone: boolean,
|
|
35
|
+
* opacity0: boolean,
|
|
36
|
+
* circleR0: boolean,
|
|
37
|
+
* ellipseRX0: boolean,
|
|
38
|
+
* ellipseRY0: boolean,
|
|
39
|
+
* rectWidth0: boolean,
|
|
40
|
+
* rectHeight0: boolean,
|
|
41
|
+
* patternWidth0: boolean,
|
|
42
|
+
* patternHeight0: boolean,
|
|
43
|
+
* imageWidth0: boolean,
|
|
44
|
+
* imageHeight0: boolean,
|
|
45
|
+
* pathEmptyD: boolean,
|
|
46
|
+
* polylineEmptyPoints: boolean,
|
|
47
|
+
* polygonEmptyPoints: boolean,
|
|
48
|
+
* }>}
|
|
49
|
+
*/
|
|
50
|
+
exports.fn = (root, params) => {
|
|
51
|
+
const {
|
|
52
|
+
isHidden = true,
|
|
53
|
+
displayNone = true,
|
|
54
|
+
opacity0 = true,
|
|
55
|
+
circleR0 = true,
|
|
56
|
+
ellipseRX0 = true,
|
|
57
|
+
ellipseRY0 = true,
|
|
58
|
+
rectWidth0 = true,
|
|
59
|
+
rectHeight0 = true,
|
|
60
|
+
patternWidth0 = true,
|
|
61
|
+
patternHeight0 = true,
|
|
62
|
+
imageWidth0 = true,
|
|
63
|
+
imageHeight0 = true,
|
|
64
|
+
pathEmptyD = true,
|
|
65
|
+
polylineEmptyPoints = true,
|
|
66
|
+
polygonEmptyPoints = true,
|
|
67
|
+
} = params;
|
|
68
|
+
const stylesheet = collectStylesheet(root);
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
element: {
|
|
72
|
+
enter: (node, parentNode) => {
|
|
73
|
+
// Removes hidden elements
|
|
74
|
+
// https://www.w3schools.com/cssref/pr_class_visibility.asp
|
|
75
|
+
const computedStyle = computeStyle(stylesheet, node);
|
|
76
|
+
if (
|
|
77
|
+
isHidden &&
|
|
78
|
+
computedStyle.visibility &&
|
|
79
|
+
computedStyle.visibility.type === 'static' &&
|
|
80
|
+
computedStyle.visibility.value === 'hidden' &&
|
|
81
|
+
// keep if any descendant enables visibility
|
|
82
|
+
querySelector(node, '[visibility=visible]') == null
|
|
83
|
+
) {
|
|
84
|
+
detachNodeFromParent(node, parentNode);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// display="none"
|
|
89
|
+
//
|
|
90
|
+
// https://www.w3.org/TR/SVG11/painting.html#DisplayProperty
|
|
91
|
+
// "A value of display: none indicates that the given element
|
|
92
|
+
// and its children shall not be rendered directly"
|
|
93
|
+
if (
|
|
94
|
+
displayNone &&
|
|
95
|
+
computedStyle.display &&
|
|
96
|
+
computedStyle.display.type === 'static' &&
|
|
97
|
+
computedStyle.display.value === 'none' &&
|
|
98
|
+
// markers with display: none still rendered
|
|
99
|
+
node.name !== 'marker'
|
|
100
|
+
) {
|
|
101
|
+
detachNodeFromParent(node, parentNode);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// opacity="0"
|
|
106
|
+
//
|
|
107
|
+
// https://www.w3.org/TR/SVG11/masking.html#ObjectAndGroupOpacityProperties
|
|
108
|
+
if (
|
|
109
|
+
opacity0 &&
|
|
110
|
+
computedStyle.opacity &&
|
|
111
|
+
computedStyle.opacity.type === 'static' &&
|
|
112
|
+
computedStyle.opacity.value === '0' &&
|
|
113
|
+
// transparent element inside clipPath still affect clipped elements
|
|
114
|
+
closestByName(node, 'clipPath') == null
|
|
115
|
+
) {
|
|
116
|
+
detachNodeFromParent(node, parentNode);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Circles with zero radius
|
|
121
|
+
//
|
|
122
|
+
// https://www.w3.org/TR/SVG11/shapes.html#CircleElementRAttribute
|
|
123
|
+
// "A value of zero disables rendering of the element"
|
|
124
|
+
//
|
|
125
|
+
// <circle r="0">
|
|
126
|
+
if (
|
|
127
|
+
circleR0 &&
|
|
128
|
+
node.name === 'circle' &&
|
|
129
|
+
node.children.length === 0 &&
|
|
130
|
+
node.attributes.r === '0'
|
|
131
|
+
) {
|
|
132
|
+
detachNodeFromParent(node, parentNode);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Ellipse with zero x-axis radius
|
|
137
|
+
//
|
|
138
|
+
// https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRXAttribute
|
|
139
|
+
// "A value of zero disables rendering of the element"
|
|
140
|
+
//
|
|
141
|
+
// <ellipse rx="0">
|
|
142
|
+
if (
|
|
143
|
+
ellipseRX0 &&
|
|
144
|
+
node.name === 'ellipse' &&
|
|
145
|
+
node.children.length === 0 &&
|
|
146
|
+
node.attributes.rx === '0'
|
|
147
|
+
) {
|
|
148
|
+
detachNodeFromParent(node, parentNode);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Ellipse with zero y-axis radius
|
|
153
|
+
//
|
|
154
|
+
// https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRYAttribute
|
|
155
|
+
// "A value of zero disables rendering of the element"
|
|
156
|
+
//
|
|
157
|
+
// <ellipse ry="0">
|
|
158
|
+
if (
|
|
159
|
+
ellipseRY0 &&
|
|
160
|
+
node.name === 'ellipse' &&
|
|
161
|
+
node.children.length === 0 &&
|
|
162
|
+
node.attributes.ry === '0'
|
|
163
|
+
) {
|
|
164
|
+
detachNodeFromParent(node, parentNode);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Rectangle with zero width
|
|
169
|
+
//
|
|
170
|
+
// https://www.w3.org/TR/SVG11/shapes.html#RectElementWidthAttribute
|
|
171
|
+
// "A value of zero disables rendering of the element"
|
|
172
|
+
//
|
|
173
|
+
// <rect width="0">
|
|
174
|
+
if (
|
|
175
|
+
rectWidth0 &&
|
|
176
|
+
node.name === 'rect' &&
|
|
177
|
+
node.children.length === 0 &&
|
|
178
|
+
node.attributes.width === '0'
|
|
179
|
+
) {
|
|
180
|
+
detachNodeFromParent(node, parentNode);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Rectangle with zero height
|
|
185
|
+
//
|
|
186
|
+
// https://www.w3.org/TR/SVG11/shapes.html#RectElementHeightAttribute
|
|
187
|
+
// "A value of zero disables rendering of the element"
|
|
188
|
+
//
|
|
189
|
+
// <rect height="0">
|
|
190
|
+
if (
|
|
191
|
+
rectHeight0 &&
|
|
192
|
+
rectWidth0 &&
|
|
193
|
+
node.name === 'rect' &&
|
|
194
|
+
node.children.length === 0 &&
|
|
195
|
+
node.attributes.height === '0'
|
|
196
|
+
) {
|
|
197
|
+
detachNodeFromParent(node, parentNode);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Pattern with zero width
|
|
202
|
+
//
|
|
203
|
+
// https://www.w3.org/TR/SVG11/pservers.html#PatternElementWidthAttribute
|
|
204
|
+
// "A value of zero disables rendering of the element (i.e., no paint is applied)"
|
|
205
|
+
//
|
|
206
|
+
// <pattern width="0">
|
|
207
|
+
if (
|
|
208
|
+
patternWidth0 &&
|
|
209
|
+
node.name === 'pattern' &&
|
|
210
|
+
node.attributes.width === '0'
|
|
211
|
+
) {
|
|
212
|
+
detachNodeFromParent(node, parentNode);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Pattern with zero height
|
|
217
|
+
//
|
|
218
|
+
// https://www.w3.org/TR/SVG11/pservers.html#PatternElementHeightAttribute
|
|
219
|
+
// "A value of zero disables rendering of the element (i.e., no paint is applied)"
|
|
220
|
+
//
|
|
221
|
+
// <pattern height="0">
|
|
222
|
+
if (
|
|
223
|
+
patternHeight0 &&
|
|
224
|
+
node.name === 'pattern' &&
|
|
225
|
+
node.attributes.height === '0'
|
|
226
|
+
) {
|
|
227
|
+
detachNodeFromParent(node, parentNode);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Image with zero width
|
|
232
|
+
//
|
|
233
|
+
// https://www.w3.org/TR/SVG11/struct.html#ImageElementWidthAttribute
|
|
234
|
+
// "A value of zero disables rendering of the element"
|
|
235
|
+
//
|
|
236
|
+
// <image width="0">
|
|
237
|
+
if (
|
|
238
|
+
imageWidth0 &&
|
|
239
|
+
node.name === 'image' &&
|
|
240
|
+
node.attributes.width === '0'
|
|
241
|
+
) {
|
|
242
|
+
detachNodeFromParent(node, parentNode);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Image with zero height
|
|
247
|
+
//
|
|
248
|
+
// https://www.w3.org/TR/SVG11/struct.html#ImageElementHeightAttribute
|
|
249
|
+
// "A value of zero disables rendering of the element"
|
|
250
|
+
//
|
|
251
|
+
// <image height="0">
|
|
252
|
+
if (
|
|
253
|
+
imageHeight0 &&
|
|
254
|
+
node.name === 'image' &&
|
|
255
|
+
node.attributes.height === '0'
|
|
256
|
+
) {
|
|
257
|
+
detachNodeFromParent(node, parentNode);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Path with empty data
|
|
262
|
+
//
|
|
263
|
+
// https://www.w3.org/TR/SVG11/paths.html#DAttribute
|
|
264
|
+
//
|
|
265
|
+
// <path d=""/>
|
|
266
|
+
if (pathEmptyD && node.name === 'path') {
|
|
267
|
+
if (node.attributes.d == null) {
|
|
268
|
+
detachNodeFromParent(node, parentNode);
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
const pathData = parsePathData(node.attributes.d);
|
|
272
|
+
if (pathData.length === 0) {
|
|
273
|
+
detachNodeFromParent(node, parentNode);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
// keep single point paths for markers
|
|
277
|
+
if (
|
|
278
|
+
pathData.length === 1 &&
|
|
279
|
+
computedStyle['marker-start'] == null &&
|
|
280
|
+
computedStyle['marker-end'] == null
|
|
281
|
+
) {
|
|
282
|
+
detachNodeFromParent(node, parentNode);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Polyline with empty points
|
|
289
|
+
//
|
|
290
|
+
// https://www.w3.org/TR/SVG11/shapes.html#PolylineElementPointsAttribute
|
|
291
|
+
//
|
|
292
|
+
// <polyline points="">
|
|
293
|
+
if (
|
|
294
|
+
polylineEmptyPoints &&
|
|
295
|
+
node.name === 'polyline' &&
|
|
296
|
+
node.attributes.points == null
|
|
297
|
+
) {
|
|
298
|
+
detachNodeFromParent(node, parentNode);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Polygon with empty points
|
|
303
|
+
//
|
|
304
|
+
// https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute
|
|
305
|
+
//
|
|
306
|
+
// <polygon points="">
|
|
307
|
+
if (
|
|
308
|
+
polygonEmptyPoints &&
|
|
309
|
+
node.name === 'polygon' &&
|
|
310
|
+
node.attributes.points == null
|
|
311
|
+
) {
|
|
312
|
+
detachNodeFromParent(node, parentNode);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { detachNodeFromParent } = require('../lib/xast.js');
|
|
4
|
+
|
|
5
|
+
exports.name = 'removeMetadata';
|
|
6
|
+
exports.type = 'visitor';
|
|
7
|
+
exports.active = true;
|
|
8
|
+
exports.description = 'removes <metadata>';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Remove <metadata>.
|
|
12
|
+
*
|
|
13
|
+
* https://www.w3.org/TR/SVG11/metadata.html
|
|
14
|
+
*
|
|
15
|
+
* @author Kir Belevich
|
|
16
|
+
*
|
|
17
|
+
* @type {import('../lib/types').Plugin<void>}
|
|
18
|
+
*/
|
|
19
|
+
exports.fn = () => {
|
|
20
|
+
return {
|
|
21
|
+
element: {
|
|
22
|
+
enter: (node, parentNode) => {
|
|
23
|
+
if (node.name === 'metadata') {
|
|
24
|
+
detachNodeFromParent(node, parentNode);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
exports.name = 'removeNonInheritableGroupAttrs';
|
|
4
|
+
|
|
5
|
+
exports.type = 'perItem';
|
|
6
|
+
|
|
7
|
+
exports.active = true;
|
|
8
|
+
|
|
9
|
+
exports.description =
|
|
10
|
+
'removes non-inheritable group’s presentational attributes';
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
inheritableAttrs,
|
|
14
|
+
attrsGroups,
|
|
15
|
+
presentationNonInheritableGroupAttrs,
|
|
16
|
+
} = require('./_collections');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Remove non-inheritable group's "presentation" attributes.
|
|
20
|
+
*
|
|
21
|
+
* @param {Object} item current iteration item
|
|
22
|
+
* @return {Boolean} if false, item will be filtered out
|
|
23
|
+
*
|
|
24
|
+
* @author Kir Belevich
|
|
25
|
+
*/
|
|
26
|
+
exports.fn = function (item) {
|
|
27
|
+
if (item.type === 'element' && item.name === 'g') {
|
|
28
|
+
for (const name of Object.keys(item.attributes)) {
|
|
29
|
+
if (
|
|
30
|
+
attrsGroups.presentation.includes(name) === true &&
|
|
31
|
+
inheritableAttrs.includes(name) === false &&
|
|
32
|
+
presentationNonInheritableGroupAttrs.includes(name) === false
|
|
33
|
+
) {
|
|
34
|
+
delete item.attributes[name];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {import('../lib/types').PathDataItem} PathDataItem
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { visitSkip, detachNodeFromParent } = require('../lib/xast.js');
|
|
8
|
+
const { parsePathData } = require('../lib/path.js');
|
|
9
|
+
const { intersects } = require('./_path.js');
|
|
10
|
+
|
|
11
|
+
exports.type = 'visitor';
|
|
12
|
+
exports.name = 'removeOffCanvasPaths';
|
|
13
|
+
exports.active = false;
|
|
14
|
+
exports.description =
|
|
15
|
+
'removes elements that are drawn outside of the viewbox (disabled by default)';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Remove elements that are drawn outside of the viewbox.
|
|
19
|
+
*
|
|
20
|
+
* @author JoshyPHP
|
|
21
|
+
*
|
|
22
|
+
* @type {import('../lib/types').Plugin<void>}
|
|
23
|
+
*/
|
|
24
|
+
exports.fn = () => {
|
|
25
|
+
/**
|
|
26
|
+
* @type {null | {
|
|
27
|
+
* top: number,
|
|
28
|
+
* right: number,
|
|
29
|
+
* bottom: number,
|
|
30
|
+
* left: number,
|
|
31
|
+
* width: number,
|
|
32
|
+
* height: number
|
|
33
|
+
* }}
|
|
34
|
+
*/
|
|
35
|
+
let viewBoxData = null;
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
element: {
|
|
39
|
+
enter: (node, parentNode) => {
|
|
40
|
+
if (node.name === 'svg' && parentNode.type === 'root') {
|
|
41
|
+
let viewBox = '';
|
|
42
|
+
// find viewbox
|
|
43
|
+
if (node.attributes.viewBox != null) {
|
|
44
|
+
// remove commas and plus signs, normalize and trim whitespace
|
|
45
|
+
viewBox = node.attributes.viewBox;
|
|
46
|
+
} else if (
|
|
47
|
+
node.attributes.height != null &&
|
|
48
|
+
node.attributes.width != null
|
|
49
|
+
) {
|
|
50
|
+
viewBox = `0 0 ${node.attributes.width} ${node.attributes.height}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// parse viewbox
|
|
54
|
+
// remove commas and plus signs, normalize and trim whitespace
|
|
55
|
+
viewBox = viewBox
|
|
56
|
+
.replace(/[,+]|px/g, ' ')
|
|
57
|
+
.replace(/\s+/g, ' ')
|
|
58
|
+
.replace(/^\s*|\s*$/g, '');
|
|
59
|
+
// ensure that the dimensions are 4 values separated by space
|
|
60
|
+
const m =
|
|
61
|
+
/^(-?\d*\.?\d+) (-?\d*\.?\d+) (\d*\.?\d+) (\d*\.?\d+)$/.exec(
|
|
62
|
+
viewBox
|
|
63
|
+
);
|
|
64
|
+
if (m == null) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const left = Number.parseFloat(m[1]);
|
|
68
|
+
const top = Number.parseFloat(m[2]);
|
|
69
|
+
const width = Number.parseFloat(m[3]);
|
|
70
|
+
const height = Number.parseFloat(m[4]);
|
|
71
|
+
|
|
72
|
+
// store the viewBox boundaries
|
|
73
|
+
viewBoxData = {
|
|
74
|
+
left,
|
|
75
|
+
top,
|
|
76
|
+
right: left + width,
|
|
77
|
+
bottom: top + height,
|
|
78
|
+
width,
|
|
79
|
+
height,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// consider that any item with a transform attribute is visible
|
|
84
|
+
if (node.attributes.transform != null) {
|
|
85
|
+
return visitSkip;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (
|
|
89
|
+
node.name === 'path' &&
|
|
90
|
+
node.attributes.d != null &&
|
|
91
|
+
viewBoxData != null
|
|
92
|
+
) {
|
|
93
|
+
const pathData = parsePathData(node.attributes.d);
|
|
94
|
+
|
|
95
|
+
// consider that a M command within the viewBox is visible
|
|
96
|
+
let visible = false;
|
|
97
|
+
for (const pathDataItem of pathData) {
|
|
98
|
+
if (pathDataItem.command === 'M') {
|
|
99
|
+
const [x, y] = pathDataItem.args;
|
|
100
|
+
if (
|
|
101
|
+
x >= viewBoxData.left &&
|
|
102
|
+
x <= viewBoxData.right &&
|
|
103
|
+
y >= viewBoxData.top &&
|
|
104
|
+
y <= viewBoxData.bottom
|
|
105
|
+
) {
|
|
106
|
+
visible = true;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (visible) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (pathData.length === 2) {
|
|
115
|
+
// close the path too short for intersects()
|
|
116
|
+
pathData.push({ command: 'z', args: [] });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const { left, top, width, height } = viewBoxData;
|
|
120
|
+
/**
|
|
121
|
+
* @type {Array<PathDataItem>}
|
|
122
|
+
*/
|
|
123
|
+
const viewBoxPathData = [
|
|
124
|
+
{ command: 'M', args: [left, top] },
|
|
125
|
+
{ command: 'h', args: [width] },
|
|
126
|
+
{ command: 'v', args: [height] },
|
|
127
|
+
{ command: 'H', args: [left] },
|
|
128
|
+
{ command: 'z', args: [] },
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
if (intersects(viewBoxPathData, pathData) === false) {
|
|
132
|
+
detachNodeFromParent(node, parentNode);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { detachNodeFromParent } = require('../lib/xast.js');
|
|
4
|
+
|
|
5
|
+
exports.name = 'removeRasterImages';
|
|
6
|
+
exports.type = 'visitor';
|
|
7
|
+
exports.active = false;
|
|
8
|
+
exports.description = 'removes raster images (disabled by default)';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Remove raster images references in <image>.
|
|
12
|
+
*
|
|
13
|
+
* @see https://bugs.webkit.org/show_bug.cgi?id=63548
|
|
14
|
+
*
|
|
15
|
+
* @author Kir Belevich
|
|
16
|
+
*
|
|
17
|
+
* @type {import('../lib/types').Plugin<void>}
|
|
18
|
+
*/
|
|
19
|
+
exports.fn = () => {
|
|
20
|
+
return {
|
|
21
|
+
element: {
|
|
22
|
+
enter: (node, parentNode) => {
|
|
23
|
+
if (
|
|
24
|
+
node.name === 'image' &&
|
|
25
|
+
node.attributes['xlink:href'] != null &&
|
|
26
|
+
/(\.|image\/)(jpg|png|gif)/.test(node.attributes['xlink:href'])
|
|
27
|
+
) {
|
|
28
|
+
detachNodeFromParent(node, parentNode);
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { detachNodeFromParent } = require('../lib/xast.js');
|
|
4
|
+
|
|
5
|
+
exports.name = 'removeScriptElement';
|
|
6
|
+
exports.type = 'visitor';
|
|
7
|
+
exports.active = false;
|
|
8
|
+
exports.description = 'removes <script> elements (disabled by default)';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Remove <script>.
|
|
12
|
+
*
|
|
13
|
+
* https://www.w3.org/TR/SVG11/script.html
|
|
14
|
+
*
|
|
15
|
+
* @author Patrick Klingemann
|
|
16
|
+
*
|
|
17
|
+
* @type {import('../lib/types').Plugin<void>}
|
|
18
|
+
*/
|
|
19
|
+
exports.fn = () => {
|
|
20
|
+
return {
|
|
21
|
+
element: {
|
|
22
|
+
enter: (node, parentNode) => {
|
|
23
|
+
if (node.name === 'script') {
|
|
24
|
+
detachNodeFromParent(node, parentNode);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
};
|