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.
Files changed (80) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +294 -0
  3. package/bin/svgo +10 -0
  4. package/dist/svgo.browser.js +1 -0
  5. package/lib/css-tools.js +239 -0
  6. package/lib/parser.js +259 -0
  7. package/lib/path.js +347 -0
  8. package/lib/stringifier.js +326 -0
  9. package/lib/style.js +283 -0
  10. package/lib/svgo/coa.js +517 -0
  11. package/lib/svgo/config.js +138 -0
  12. package/lib/svgo/css-class-list.js +72 -0
  13. package/lib/svgo/css-select-adapter.d.ts +2 -0
  14. package/lib/svgo/css-select-adapter.js +120 -0
  15. package/lib/svgo/css-style-declaration.js +232 -0
  16. package/lib/svgo/jsAPI.d.ts +2 -0
  17. package/lib/svgo/jsAPI.js +443 -0
  18. package/lib/svgo/plugins.js +109 -0
  19. package/lib/svgo/tools.js +137 -0
  20. package/lib/svgo-node.js +106 -0
  21. package/lib/svgo.js +83 -0
  22. package/lib/types.ts +172 -0
  23. package/lib/xast.js +102 -0
  24. package/package.json +130 -0
  25. package/plugins/_applyTransforms.js +335 -0
  26. package/plugins/_collections.js +2168 -0
  27. package/plugins/_path.js +816 -0
  28. package/plugins/_transforms.js +379 -0
  29. package/plugins/addAttributesToSVGElement.js +87 -0
  30. package/plugins/addClassesToSVGElement.js +87 -0
  31. package/plugins/cleanupAttrs.js +55 -0
  32. package/plugins/cleanupEnableBackground.js +75 -0
  33. package/plugins/cleanupIDs.js +297 -0
  34. package/plugins/cleanupListOfValues.js +154 -0
  35. package/plugins/cleanupNumericValues.js +113 -0
  36. package/plugins/collapseGroups.js +135 -0
  37. package/plugins/convertColors.js +152 -0
  38. package/plugins/convertEllipseToCircle.js +39 -0
  39. package/plugins/convertPathData.js +1023 -0
  40. package/plugins/convertShapeToPath.js +175 -0
  41. package/plugins/convertStyleToAttrs.js +132 -0
  42. package/plugins/convertTransform.js +432 -0
  43. package/plugins/inlineStyles.js +379 -0
  44. package/plugins/mergePaths.js +104 -0
  45. package/plugins/mergeStyles.js +93 -0
  46. package/plugins/minifyStyles.js +148 -0
  47. package/plugins/moveElemsAttrsToGroup.js +130 -0
  48. package/plugins/moveGroupAttrsToElems.js +62 -0
  49. package/plugins/plugins.js +56 -0
  50. package/plugins/prefixIds.js +241 -0
  51. package/plugins/preset-default.js +80 -0
  52. package/plugins/removeAttributesBySelector.js +99 -0
  53. package/plugins/removeAttrs.js +159 -0
  54. package/plugins/removeComments.js +31 -0
  55. package/plugins/removeDesc.js +41 -0
  56. package/plugins/removeDimensions.js +43 -0
  57. package/plugins/removeDoctype.js +42 -0
  58. package/plugins/removeEditorsNSData.js +68 -0
  59. package/plugins/removeElementsByAttr.js +78 -0
  60. package/plugins/removeEmptyAttrs.js +33 -0
  61. package/plugins/removeEmptyContainers.js +58 -0
  62. package/plugins/removeEmptyText.js +57 -0
  63. package/plugins/removeHiddenElems.js +318 -0
  64. package/plugins/removeMetadata.js +29 -0
  65. package/plugins/removeNonInheritableGroupAttrs.js +38 -0
  66. package/plugins/removeOffCanvasPaths.js +138 -0
  67. package/plugins/removeRasterImages.js +33 -0
  68. package/plugins/removeScriptElement.js +29 -0
  69. package/plugins/removeStyleElement.js +29 -0
  70. package/plugins/removeTitle.js +29 -0
  71. package/plugins/removeUnknownsAndDefaults.js +218 -0
  72. package/plugins/removeUnusedNS.js +61 -0
  73. package/plugins/removeUselessDefs.js +65 -0
  74. package/plugins/removeUselessStrokeAndFill.js +144 -0
  75. package/plugins/removeViewBox.js +51 -0
  76. package/plugins/removeXMLNS.js +30 -0
  77. package/plugins/removeXMLProcInst.js +30 -0
  78. package/plugins/reusePaths.js +113 -0
  79. package/plugins/sortAttrs.js +113 -0
  80. 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
+ };