svg-path-simplify 0.4.2 → 0.4.4

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 (61) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +7 -4
  3. package/dist/svg-path-simplify.esm.js +3593 -1279
  4. package/dist/svg-path-simplify.esm.min.js +2 -2
  5. package/dist/svg-path-simplify.js +3594 -1278
  6. package/dist/svg-path-simplify.min.js +2 -2
  7. package/dist/svg-path-simplify.pathdata.esm.js +1017 -538
  8. package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
  9. package/dist/svg-path-simplify.poly.cjs +9 -8
  10. package/docs/privacy-webapp.md +24 -0
  11. package/index.html +331 -152
  12. package/package.json +1 -1
  13. package/src/constants.js +4 -0
  14. package/src/css_parse.js +317 -0
  15. package/src/detect_input.js +76 -28
  16. package/src/index.js +8 -0
  17. package/src/pathData_simplify_cubic.js +26 -16
  18. package/src/pathData_simplify_harmonize_cpts.js +77 -1
  19. package/src/pathData_simplify_revertToquadratics.js +0 -1
  20. package/src/pathSimplify-main.js +304 -276
  21. package/src/pathSimplify-only-pathdata.js +7 -2
  22. package/src/pathSimplify-presets.js +254 -0
  23. package/src/poly-fit-curve-schneider.js +14 -7
  24. package/src/simplify_poly_RC.js +102 -0
  25. package/src/simplify_poly_RDP.js +109 -1
  26. package/src/simplify_poly_radial_distance.js +3 -3
  27. package/src/string_helpers.js +130 -4
  28. package/src/svg-getAttributes.js +4 -2
  29. package/src/svgii/convert_units.js +1 -1
  30. package/src/svgii/geometry.js +322 -5
  31. package/src/svgii/geometry_bbox_element.js +1 -1
  32. package/src/svgii/geometry_deduceRadius.js +116 -27
  33. package/src/svgii/geometry_length.js +253 -0
  34. package/src/svgii/pathData_analyze.js +18 -0
  35. package/src/svgii/pathData_convert.js +193 -89
  36. package/src/svgii/pathData_fix_directions.js +12 -14
  37. package/src/svgii/pathData_fromPoly.js +3 -3
  38. package/src/svgii/pathData_getLength.js +86 -0
  39. package/src/svgii/pathData_parse.js +2 -0
  40. package/src/svgii/pathData_parse_els.js +66 -68
  41. package/src/svgii/pathData_reorder.js +122 -16
  42. package/src/svgii/pathData_simplify_refineCorners.js +130 -35
  43. package/src/svgii/pathData_simplify_refine_round.js +420 -0
  44. package/src/svgii/pathData_split_to_groups.js +168 -0
  45. package/src/svgii/pathData_stringify.js +26 -64
  46. package/src/svgii/pathData_toPolygon.js +3 -4
  47. package/src/svgii/poly_analyze.js +61 -0
  48. package/src/svgii/poly_normalize.js +11 -2
  49. package/src/svgii/poly_to_pathdata.js +85 -24
  50. package/src/svgii/rounding.js +80 -78
  51. package/src/svgii/svg_cleanup.js +421 -619
  52. package/src/svgii/svg_cleanup_convertPathLength.js +39 -0
  53. package/src/svgii/svg_cleanup_general_svg_atts.js +97 -0
  54. package/src/svgii/svg_cleanup_normalize_transforms.js +83 -0
  55. package/src/svgii/svg_cleanup_remove_els_and_atts.js +77 -0
  56. package/src/svgii/svg_cleanup_ungroup.js +36 -0
  57. package/src/svgii/svg_el_parse_style_props.js +72 -47
  58. package/src/svgii/svg_getElementLength.js +67 -0
  59. package/src/svgii/svg_validate.js +220 -0
  60. package/tests/testSVG.js +14 -1
  61. package/src/svgii/pathData_refine_round.js +0 -222
@@ -11,21 +11,18 @@ import { pathElToShape, shapeElToPath } from "./pathData_parse_els";
11
11
  //import { scaleProps } from "./svg-styles-to-attributes";
12
12
  import { geometryEls, geometryProps, renderedEls, shapeEls, strokeAtts } from "./svg-styles-to-attributes-const";
13
13
  import { addTransFormProps, filterSvgElProps, parseStylesProperties } from "./svg_el_parse_style_props";
14
- import { autoRound } from "./rounding";
14
+ import { autoRound, roundTo } from "./rounding";
15
15
  import { getMatrixFromTransform } from "./svg-styles-getTransforms";
16
16
  import { qrDecomposeMatrix } from "./transform_qr_decompose";
17
17
  import { svgNs } from "../constants";
18
18
  import { toCamelCase, toShortStr } from "../string_helpers";
19
-
20
-
21
- export function removeEmptySVGEls(svg) {
22
- let els = svg.querySelectorAll('g, defs');
23
- els.forEach(el => {
24
- if (!el.children.length) el.remove()
25
- })
26
- }
27
-
28
- //const DOMParserPoly = globalThis.DOMParser;
19
+ import { getElementLength } from "./svg_getElementLength";
20
+ import { removeAtts, removeHiddenSvgEls, removeSvgAtts, removeSvgChildAtts, removeSvgEls } from "./svg_cleanup_remove_els_and_atts";
21
+ import { cleanupSVGAttributes, removeElAtts } from "./svg_cleanup_general_svg_atts";
22
+ import { convertPathLengthAtt } from "./svg_cleanup_convertPathLength";
23
+ import { removeGroupProps, ungroupElements } from "./svg_cleanup_ungroup";
24
+ import { parseSvgCss } from "../css_parse";
25
+ import { setNormalizedTransformsToEl } from "./svg_cleanup_normalize_transforms";
29
26
 
30
27
 
31
28
  export function cleanUpSVG(svgMarkup, {
@@ -56,6 +53,11 @@ export function cleanUpSVG(svgMarkup, {
56
53
 
57
54
  cleanupSVGAtts = true,
58
55
  removeNameSpaced = true,
56
+ removeNameSpacedAtts = true,
57
+
58
+ // unit conversions
59
+ convertPathLength = false,
60
+ toAbsoluteUnits = false,
59
61
 
60
62
  // meta
61
63
  allowMeta = false,
@@ -64,10 +66,14 @@ export function cleanUpSVG(svgMarkup, {
64
66
 
65
67
  //shapesToPaths = false,
66
68
  shapeConvert = false,
67
- convert_rects = false,
68
- convert_ellipses = false,
69
- convert_poly = false,
70
- convert_lines = false,
69
+ convertShapes = [],
70
+
71
+ // remove elements
72
+ removeElements = [],
73
+
74
+ // remove attributes
75
+ removeSVGAttributes = [],
76
+ removeElAttributes = [],
71
77
 
72
78
  convertTransforms = false,
73
79
  removeDefaults = true,
@@ -76,8 +82,13 @@ export function cleanUpSVG(svgMarkup, {
76
82
  excludedEls = [],
77
83
  } = {}) {
78
84
 
79
- //attributesToGroup = cleanupSVGAtts ? true : false;
80
- if (attributesToGroup) stylesToAttributes = true;
85
+
86
+
87
+ // resolve dependencies
88
+ if (unGroup || convertTransforms || minifyRgbColors || attributesToGroup)
89
+ stylesToAttributes = true;
90
+
91
+ if (stylesToAttributes) cleanUpStrokes = true;
81
92
 
82
93
  // replace namespaced refs
83
94
  if (fixHref) svgMarkup = svgMarkup.replaceAll("xlink:href=", "href=");
@@ -87,17 +98,39 @@ export function cleanUpSVG(svgMarkup, {
87
98
  .parseFromString(svgMarkup, "text/html")
88
99
  .querySelector("svg");
89
100
 
101
+
90
102
  let viewBox = getViewBox(svg)
91
103
  let { x, y, width, height } = viewBox;
104
+ let remove = []
92
105
 
93
106
 
94
- // get svg styles
107
+
108
+ // add viewBox
109
+ if (addViewBox) addSvgViewBox(svg, { x, y, width, height })
110
+ if (addDimensions) {
111
+ svg.setAttribute('width', width);
112
+ svg.setAttribute('height', height);
113
+ }
114
+
115
+ // remove unused defs or optimize order
116
+ if (cleanupDefs) cleanupSvgDefs(svg, { x, y, width, height, cleanupClip });
117
+
118
+
119
+ // remove off canvas
120
+ if (removeOffCanvas) removeOffCanvasEls(svg, { x, y, width, height });
121
+
122
+
123
+ /**
124
+ * collect svg styles
125
+ * and properties
126
+ */
95
127
  let propOptions = {
96
- width: width,
97
- height: height,
128
+ width,
129
+ height,
98
130
  normalizeTransforms,
99
131
  removeDefaults: false,
100
132
  cleanUpStrokes: false,
133
+ //cleanUpStrokes,
101
134
  allowMeta,
102
135
  allowDataAtts,
103
136
  allowAriaAtts,
@@ -105,18 +138,54 @@ export function cleanUpSVG(svgMarkup, {
105
138
  removeIds,
106
139
  removeClassNames,
107
140
  minifyRgbColors,
141
+ stylesheetProps: {},
142
+ exclude: []
108
143
  }
109
144
 
110
- // root svg properties
145
+ // root svg inline style properties
111
146
  let stylePropsSVG = parseStylesProperties(svg, propOptions)
112
147
  //console.log('stylePropsSVG', stylePropsSVG);
113
148
 
149
+ let styleEl = svg.querySelector('style')
150
+ let cssStylePropsSVG = {}
151
+
152
+ if (styleEl) {
153
+ cssStylePropsSVG = parseSvgCss(styleEl, { parent: svg })
154
+
155
+ //save stylesheet dependencies in node
156
+ for (let selector in cssStylePropsSVG) {
157
+ let els = svg.querySelectorAll(`${selector}`);
158
+ els.forEach(el => {
159
+ if (!el['cssRules']) el['cssRules'] = [];
160
+ el['cssRules'].push(selector)
161
+
162
+ // remove class names only used for styling
163
+ if (stylesToAttributes) {
164
+ let className = selector.substring(1)
165
+ el.classList.remove(className)
166
+ }
167
+ })
168
+ }
169
+
170
+ //console.log('cssStylePropsSVG', cssStylePropsSVG);
171
+
172
+ // remove style element from element
173
+ if (stylesToAttributes) {
174
+ styleEl.remove()
175
+ }
176
+ }
177
+ // remove style element from root SVG
178
+ if (stylesToAttributes) svg.removeAttribute('style')
179
+
180
+
181
+ // add stylesheet props
182
+ propOptions.stylesheetProps = cssStylePropsSVG;
183
+
114
184
 
115
185
  // add svg font size for scaling relative
116
186
  propOptions.fontSize = stylePropsSVG['font-size'] ? stylePropsSVG['font-size'][0] : 16;
117
187
 
118
188
 
119
-
120
189
  /**
121
190
  * get group styles
122
191
  * especially transformations to
@@ -126,7 +195,10 @@ export function cleanUpSVG(svgMarkup, {
126
195
  let groupProps = [];
127
196
 
128
197
  groups.forEach(g => {
198
+ //propOptions.exclude.push('class', 'id');
129
199
  let stylePropsG = parseStylesProperties(g, propOptions)
200
+ //console.log('stylePropsG', stylePropsG);
201
+
130
202
  groupProps.push(stylePropsG);
131
203
  let children = g.querySelectorAll(`${renderedEls.join(', ')}`)
132
204
 
@@ -141,85 +213,36 @@ export function cleanUpSVG(svgMarkup, {
141
213
 
142
214
 
143
215
 
144
- if (cleanupSVGAtts) {
145
- //console.log('cleanupSVGAtts');
146
- let allowed = new Set(['viewBox', 'xmlns', 'width', 'height']);
147
-
148
- if (!removeIds) allowed.add('id')
149
- if (!removeClassNames) allowed.add('class')
150
- if (removeDimensions) {
151
- allowed.delete('width')
152
- allowed.delete('height')
153
- }
154
-
155
- allowed = Array.from(allowed)
156
- if (!stylesToAttributes) {
157
- allowed.push('fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin', 'font-size', 'font-family', 'font-style', 'style');
158
- }
159
-
160
- //console.log(allowed);
161
- //if(allow)
162
- removeExcludedAttribues(svg, { allowed, allowMeta, allowAriaAtts, allowDataAtts })
163
- }
164
-
165
- // remove meta
166
- if (!allowMeta) {
167
- let metaEls = svg.querySelectorAll('meta, metadata, desc, title')
168
- metaEls.forEach(meta => {
169
- meta.remove()
170
- })
171
- }
172
-
173
- // add viewBox
174
- if (addViewBox) addSvgViewBox(svg, { x, y, width, height })
175
- if (addDimensions) {
176
- svg.setAttribute('width', width);
177
- svg.setAttribute('height', height);
178
- }
179
-
180
-
181
- // remove unused defs or optimize order
182
- if (cleanupDefs) cleanupSvgDefs(svg, { x, y, width, height, cleanupClip });
183
-
184
-
185
- // remove off canvas
186
- if (removeOffCanvas) removeOffCanvasEls(svg, { x, y, width, height });
187
-
188
-
189
- // always remove scripts
190
- //let removeEls = ['metadata', 'script', ...excludedEls]
191
- let removeEls = ['script', ...excludedEls]
192
-
193
- removeSVGEls(svg, { remove: removeEls, removeNameSpaced });
194
-
195
- // an array of all elements' properties
216
+ // collect all elements' properties
196
217
  let svgElProps = []
197
218
  let els = svg.querySelectorAll(`${renderedEls.join(', ')}`)
198
219
 
199
220
 
221
+ /**
222
+ * loop all geometry elements
223
+ */
200
224
  for (let i = 0; i < els.length; i++) {
201
225
  let el = els[i];
202
226
 
203
227
  let name = el.nodeName.toLowerCase();
204
228
  //console.log(name);
205
229
 
206
- // 1. remove hidden elements
207
- let style = el.getAttribute('style') || ''
208
- let isHiddenByStyle = style ? style.trim().includes('display:none') : false;
209
- let isHidden = (el.getAttribute('display') && el.getAttribute('display') === 'none') || isHiddenByStyle;
210
- if (name.includes(':') || removeEls.includes(name) || (removeHidden && isHidden)) {
211
- el.remove();
212
- continue;
213
- }
214
-
215
-
216
230
  /**
217
- * get all style properties
231
+ * get all element style properties
218
232
  * convert relative or physical units
219
233
  * to user units
220
234
  */
221
235
  let styleProps = parseStylesProperties(el, propOptions)
222
236
  let stylePropsFiltered = {}
237
+ //console.log('styleProps', name, styleProps);
238
+
239
+
240
+ // convert pathLength before transforming
241
+ if (convertPathLength) {
242
+ styleProps = convertPathLengthAtt(el, { styleProps });
243
+ remove = [...new Set([...remove, ...styleProps.remove])];
244
+ }
245
+
223
246
 
224
247
  // get parent styles
225
248
  let { parentStyleProps = [] } = el;
@@ -227,7 +250,10 @@ export function cleanUpSVG(svgMarkup, {
227
250
  let transFormInherited = []
228
251
 
229
252
 
230
- /** inherit transforms
253
+
254
+ /**
255
+ * consolidate all properties:
256
+ * merge with inherited transforms
231
257
  * and styles from group
232
258
  */
233
259
  parentStyleProps.forEach(props => {
@@ -243,178 +269,170 @@ export function cleanUpSVG(svgMarkup, {
243
269
  })
244
270
 
245
271
 
246
- //merge transforms
272
+ // merge all transforms
247
273
  transFormInherited = [...transFormInherited, ...styleProps.transformArr]
248
274
  styleProps.transformArr = transFormInherited
249
275
 
250
276
 
251
- // merge with svg props
277
+ // don't inherit class from SVG
278
+ if (stylePropsSVG['class']) delete stylePropsSVG['class']
279
+ if (stylePropsSVG['id']) delete stylePropsSVG['id']
252
280
 
253
- styleProps = {
281
+ // add svg props
282
+ inheritedProps = {
254
283
  ...stylePropsSVG,
255
284
  ...inheritedProps,
256
- ...styleProps
257
- }
285
+ };
258
286
 
287
+ //console.log('inheritedProps', inheritedProps);
259
288
 
260
- // dont inherit class
261
- if (stylePropsSVG['class'] === styleProps['class']) {
262
- delete styleProps['class']
289
+ // merge with svg props
290
+ styleProps = {
291
+ ...inheritedProps,
292
+ ...styleProps
263
293
  }
264
294
 
295
+
265
296
  // add combined transforms
266
297
  addTransFormProps(styleProps, transFormInherited);
267
298
  //console.log('transFormInherited', transFormInherited);
268
299
  //console.log('styleProps', styleProps);
300
+ remove = [...new Set([...remove, ...styleProps.remove])];
269
301
 
270
302
 
271
- let { remove, matrix, transComponents } = styleProps;
303
+ /**
304
+ * remove els and attributes
305
+ */
272
306
 
273
- // styles to atts
274
- if (unGroup || convertTransforms || minifyRgbColors) stylesToAttributes = true;
307
+ // remove meta
308
+ if (!allowMeta) removeElements.push('meta', 'metadata', 'desc', 'title')
275
309
 
276
- if (stylesToAttributes) {
310
+ if (removeClassNames) {
311
+ removeSVGAttributes.push('class');
312
+ removeElAttributes.push('class');
313
+ }
277
314
 
278
- /**
279
- * normalize transforms
280
- */
281
- if (normalizeTransforms && matrix) {
282
- let { rotate, scaleX, scaleY, skewX, translateX, translateY } = transComponents;
283
- //console.log(rotate, scaleX, scaleY, skewX, skewY, translateX, translateY);
315
+ if (removeIds) {
316
+ removeSVGAttributes.push('id')
317
+ removeElAttributes.push('id')
318
+ }
284
319
 
285
- // scale attributes instead of transform
286
- let hasRot = rotate !== 0 || skewX !== 0;
287
- let unProportional = scaleX !== scaleY;
288
- let scalableByAtt = ['circle', 'ellipse', 'rect']
289
- let needsTrans = convertTransforms || (name === 'g') || (hasRot) || unProportional
290
320
 
291
- if (!needsTrans && scalableByAtt.includes(name)) {
321
+ // remove hidden elements
322
+ removeHiddenSvgEls(svg)
292
323
 
293
- if (name === 'circle' || name === 'ellipse') {
294
- styleProps.cx[0] = [styleProps.cx[0] * scaleX + translateX]
295
- styleProps.cy[0] = [styleProps.cy[0] * scaleX + translateY]
324
+ // remove SVG elements
325
+ removeSvgEls(svg, { removeElements, removeNameSpaced });
296
326
 
297
- if (styleProps.r) styleProps.r[0] = [styleProps.r[0] * scaleX]
298
- if (styleProps.rx) styleProps.rx[0] = [styleProps.rx[0] * scaleX]
299
- if (styleProps.ry) styleProps.ry[0] = [styleProps.ry[0] * scaleX]
327
+ // remove SVG attributes
328
+ removeSvgAtts(svg, removeSVGAttributes);
300
329
 
301
- }
302
- else if (name === 'rect') {
303
- let x = styleProps.x ? styleProps.x[0] + translateX : translateX;
304
- let y = styleProps.y ? styleProps.y[0] + translateY : translateY;
330
+ // remove SVG child element attributes
331
+ removeSvgChildAtts(svg, removeElAttributes);
305
332
 
306
- let rx = styleProps.rx ? styleProps.rx[0] * scaleX : 0;
307
- let ry = styleProps.ry ? styleProps.ry[0] * scaleY : 0;
308
333
 
309
- styleProps.x = [x]
310
- styleProps.y = [y]
334
+ // general cleanup
335
+ if (cleanupSVGAtts) cleanupSVGAttributes(svg, { removeIds, removeClassNames, removeDimensions, stylesToAttributes, allowMeta, allowAriaAtts, allowDataAtts });
311
336
 
312
- styleProps.rx = [rx]
313
- styleProps.ry = [ry]
337
+ // all relative units to absolute
338
+ if (toAbsoluteUnits) {
339
+ normalizeTransforms = true;
340
+ //stylesToAttributes = true
341
+ //console.log(name, styleProps);
314
342
 
315
- styleProps.width = [styleProps.width[0] * scaleX]
316
- styleProps.height = [styleProps.height[0] * scaleX]
317
- }
343
+ /**
344
+ * apply consolidated
345
+ * element attributes
346
+ * remove non-supported element props
347
+ */
348
+ stylePropsFiltered = filterSvgElProps(name, styleProps,
349
+ { removeDefaults: true, cleanUpStrokes, allowMeta, allowAriaAtts, allowDataAtts, removeIds, inheritedProps });
318
350
 
319
- // remove now obsolete transform properties
320
- delete styleProps.matrix
321
- delete styleProps.transformArr
322
- delete styleProps.transComponents
323
351
 
324
- // mark transform attribute for removal
325
- remove.push('transform')
352
+ for (let prop in stylePropsFiltered.propsFiltered) {
353
+ let values = styleProps[prop]
354
+ let val = values.length ? values.join(' ') : values[0]
355
+ el.setAttribute(prop, val)
356
+ }
326
357
 
327
- // scale props like stroke width or dash-array
328
- styleProps = scaleProps(styleProps, { props: ['stroke-width', 'stroke-dasharray'], scale: scaleX })
358
+ //console.log('inheritedProps', inheritedProps);
359
+ //console.log('current props', stylePropsFiltered.propsFiltered);
329
360
 
330
- } else {
331
- el.setAttribute('transform', transComponents.matrixAtt)
361
+ let removeAttsEl = [...new Set([...remove, ...stylePropsFiltered.remove])];
332
362
 
363
+ // check if same value is in inherited
364
+ for (let prop in stylePropsFiltered.propsFiltered) {
365
+ let valInh = inheritedProps[prop] || [];
366
+ let val = stylePropsFiltered.propsFiltered[prop] || [];
367
+ if (valInh.join() === val.join()) {
368
+ removeAttsEl.push(prop)
333
369
  }
334
370
  }
335
371
 
372
+ // remove obsolete/inherited
373
+ removeAtts(el, removeAttsEl)
374
+
375
+ }
376
+
377
+
378
+
379
+ if (stylesToAttributes) {
380
+
381
+ /**
382
+ * normalize transforms
383
+ */
384
+ if (normalizeTransforms) {
385
+ styleProps = setNormalizedTransformsToEl(el, { styleProps });
386
+ //remove = styleProps.remove;
387
+ remove = [...new Set([...remove, ...styleProps.remove])];
388
+
389
+ }
336
390
 
337
391
  /**
338
392
  * apply consolidated
339
393
  * element attributes
394
+ * remove non-supported element props
340
395
  */
341
396
  stylePropsFiltered = filterSvgElProps(name, styleProps,
342
- { removeDefaults: true, cleanUpStrokes, allowMeta, allowAriaAtts, allowDataAtts, removeIds });
397
+ { removeDefaults: true, cleanUpStrokes, allowMeta, allowAriaAtts, allowDataAtts, removeIds, inheritedProps });
343
398
 
344
- remove = [...remove, ...stylePropsFiltered.remove];
399
+ remove = [...new Set([...remove, ...stylePropsFiltered.remove])];
345
400
 
346
401
  for (let prop in stylePropsFiltered.propsFiltered) {
347
402
  let values = styleProps[prop]
348
403
  let val = values.length ? values.join(' ') : values[0]
349
404
  el.setAttribute(prop, val)
350
-
351
405
  }
352
406
 
353
407
 
354
- // remove obsolete attributes
355
- for (let i = 0; i < remove.length; i++) {
356
- let att = remove[i];
357
- if (!stylesToAttributes && att === 'style') continue
358
- //console.log('remove att', att, name);
359
- el.removeAttribute(att)
360
- }
361
-
362
-
363
-
364
-
365
408
  /**
366
- * remove group styles
367
- * copied to children
368
- * or remove nesting
409
+ * remove obsolete
410
+ * attributes
369
411
  */
412
+ removeAtts(el, remove)
370
413
 
371
- if (unGroup) {
372
- groups.forEach((g, i) => {
373
- let children = [...g.children];
374
-
375
- children.forEach(child => {
376
- g.parentNode.insertBefore(child, g)
377
- })
378
- g.remove()
379
- })
380
- } else {
381
- groups.forEach((g, i) => {
382
- //let atts = [...Object.keys(groupProps[i]), 'style', 'transform', 'id'];
383
- let atts = Object.keys(getElementAtts(g));
384
-
385
- atts.forEach(att => {
386
- //console.log('remove', remove);
387
- let isData = !allowDataAtts && att.startsWith('data-')
388
- let isAria = !allowAriaAtts && att.startsWith('aria-')
389
- //console.log(isData, att);
390
- remove.push('transform', 'style')
391
-
392
- if (remove.includes(att) || isData || isAria) {
393
- g.removeAttribute(att)
394
- }
395
- })
396
- })
397
-
414
+ /*
415
+ for (let i = 0; i < remove.length; i++) {
416
+ let att = remove[i];
417
+ el.removeAttribute(att)
398
418
  }
419
+ */
399
420
 
400
421
 
401
422
  } // endof style processing
402
423
 
403
424
 
425
+
404
426
  /**
405
427
  * element conversions:
406
428
  * shapes to paths or
407
429
  * paths to shapes
408
430
  */
409
431
 
410
-
411
432
  // force shape conversion when transform conversion is enabled
412
433
  if (convertTransforms) {
413
434
  shapeConvert = 'toPaths';
414
- convert_rects = true;
415
- convert_ellipses = true;
416
- convert_poly = true;
417
- convert_lines = true;
435
+ convertShapes = ['path', 'rect', 'ellipse', 'circle', 'line', 'polygon', 'polyline'];
418
436
  }
419
437
 
420
438
  // convert shapes to paths
@@ -422,8 +440,8 @@ export function cleanUpSVG(svgMarkup, {
422
440
 
423
441
  let { matrix = null, transComponents = null } = styleProps;
424
442
 
443
+ // scale props like stroke width or dash-array before conversion
425
444
  if (matrix && transComponents) {
426
- // scale props like stroke width or dash-array before conversion
427
445
  ['stroke-width', 'stroke-dasharray'].forEach(att => {
428
446
  let attVal = el.getAttribute(att)
429
447
  let vals = attVal ? attVal.split(' ').filter(Boolean).map(Number).map(val => val * transComponents.scaleX) : []
@@ -433,10 +451,9 @@ export function cleanUpSVG(svgMarkup, {
433
451
 
434
452
  // convert paths only if a matrix transform is required
435
453
  if (matrix ? geometryEls.includes(name) : shapeEls.includes(name)) {
436
-
437
- let path = shapeElToPath(el, { width, height, convert_rects, convert_ellipses, convert_poly, convert_lines, matrix });
454
+ //console.log('detrans', name, el.id, matrix);
455
+ let path = shapeElToPath(el, { width, height, convertShapes, matrix });
438
456
  el.replaceWith(path)
439
-
440
457
  name = 'path'
441
458
  el = path; // required for node
442
459
 
@@ -444,15 +461,19 @@ export function cleanUpSVG(svgMarkup, {
444
461
 
445
462
  }
446
463
 
447
- // convert paths to shapes
464
+ /**
465
+ * Reverse conversion:
466
+ * paths to shapes
467
+ */
448
468
  else if (shapeConvert === 'toShapes') {
449
469
  let paths = svg.querySelectorAll('path')
450
470
  paths.forEach(path => {
451
- let shape = pathElToShape(path, { convert_rects, convert_ellipses, convert_poly, convert_lines })
452
- //path.replaceWith(shape)
471
+ let shape = pathElToShape(path, { convertShapes })
472
+ path.replaceWith(shape)
453
473
  path = shape;
474
+ //name = shape.nodeName.toLowerCase()
475
+ //console.log('shape', shape);
454
476
  })
455
-
456
477
  }
457
478
 
458
479
 
@@ -460,7 +481,6 @@ export function cleanUpSVG(svgMarkup, {
460
481
  * combine styles
461
482
  * store in node property
462
483
  */
463
-
464
484
  if (mergePaths || attributesToGroup) {
465
485
 
466
486
  let options = { allowMeta, allowAriaAtts, removeIds, removeClassNames, allowDataAtts }
@@ -470,7 +490,7 @@ export function cleanUpSVG(svgMarkup, {
470
490
  * adjacent path merging
471
491
  * e.g ignore classnames or ids
472
492
  */
473
- if(mergePaths){
493
+ if (mergePaths) {
474
494
  options.removeIds = true;
475
495
  options.removeClassNames = true;
476
496
  options.allowAriaAtts = false;
@@ -486,226 +506,253 @@ export function cleanUpSVG(svgMarkup, {
486
506
  let values = stylePropsFiltered[prop]
487
507
  let val = values.length ? values.join(' ') : values[0]
488
508
 
489
- let propShort = toShortStr(prop)
490
- let valShort = toShortStr(val)
491
- let propStr = `${propShort}-${valShort}`;
509
+ if (prop !== 'class' && prop !== 'id') {
510
+
511
+ let propShort = toShortStr(prop)
512
+ let valShort = toShortStr(val)
513
+ let propStr = `${propShort}-${valShort}`;
492
514
 
493
- // store in node property
494
- if (!el.styleSet) el.styleSet = new Set()
495
- el.styleSet.add(propStr)
515
+ // store in node property
516
+ if (!el.styleSet) el.styleSet = new Set()
517
+ if (propStr) el.styleSet.add(propStr)
518
+ }
496
519
  }
497
520
 
498
521
  }
499
522
 
500
-
501
523
  }//endof element loop
502
524
 
503
525
 
526
+ /**
527
+ * remove group styles
528
+ * copied to children
529
+ * or remove nesting
530
+ */
531
+
532
+ if (unGroup) {
533
+ ungroupElements(groups)
534
+ } else {
535
+
536
+ if (stylesToAttributes) {
537
+ groups.forEach(g => {
538
+ removeElAtts(g, ['style', 'transform']);
539
+ })
540
+ }
541
+ //removeGroupProps(groups, { remove, allowDataAtts, allowAriaAtts })
542
+ }
543
+
544
+
545
+ // styles to group
546
+ if (attributesToGroup) sharedAttributesToGroup(svg);
504
547
 
505
548
  /**
506
549
  * merge paths with same styles
507
550
  */
508
551
  if (mergePaths) {
509
- let paths = svg.querySelectorAll('path')
510
- let len = paths.length;
511
-
512
- if (len) {
513
- let path0 = paths[0]
514
- let d0 = path0.getAttribute('d')
515
- let stylePrev = path0.styleSet !== undefined ? [...path0.styleSet].join('_') : ''
516
-
517
- for (let i = 1; i < len; i++) {
518
- let path = paths[i];
519
- let style = path.styleSet !== undefined ? [...path.styleSet].join('_') : ''
520
- let isSibling = path.previousElementSibling === path0;
521
- let d = path.getAttribute('d');
522
- let isAbs = d.startsWith('M')
523
-
524
- if (isSibling && style === stylePrev) {
525
- let dAbs = isAbs ? d : parsePathDataString(d).pathData.map(com => `${com.type} ${com.values.join(' ')}`).join(' ')
526
- //console.log('same style', dAbs, isAbs);
527
- d0 += dAbs;
528
- path0.setAttribute('d', d0)
529
- path.remove();
530
-
531
- } else {
532
- path0 = path
533
- d0 = isAbs ? d : parsePathDataString(d).pathData.map(com => `${com.type} ${com.values.join(' ')}`).join(' ')
534
- }
552
+ mergePathsWithSameProps(svg)
553
+ }
535
554
 
536
- // update style
537
- stylePrev = style
538
- }
539
- }
555
+ //console.log('svg', svg);
540
556
 
557
+ // remove futile clip-paths
558
+ if (cleanupClip) removeFutileClipPaths(svg, { x, y, width, height })
541
559
 
542
- /**
543
- * shared styles to group
544
- */
560
+ // replace href attributes with namespace - required by many older applications
561
+ if (legacyHref) hrefToXlink(svg);
545
562
 
546
- if (attributesToGroup) {
547
- let els = svg.querySelectorAll(geometryEls.join(', '))
548
- let len = els.length;
549
563
 
550
- let el0 = els[0] || null
551
- let stylePrev = el0.styleSet !== undefined ? [...el0.styleSet].join('_') : ''
564
+ // remove empty class attributes
565
+ removeEmptyClassAtts(svg);
566
+ return { svg, svgElProps }
552
567
 
568
+ }
553
569
 
554
- // all props
555
- let allProps = {}
556
570
 
557
- // find attributes shared by all
558
- let globalAtts = []
559
571
 
560
- if (len) {
572
+ function removeEmptyClassAtts(svg) {
573
+ let emptyClassEls = svg.querySelectorAll('[class=""]');
574
+ emptyClassEls.forEach(el => {
575
+ el.removeAttribute('class')
576
+ })
577
+ }
561
578
 
562
- let groups = [[el0]];
563
- let idx = 0;
564
- let elPrev = el0
565
579
 
566
- for (let i = 0; i < len; i++) {
567
- let el = els[i];
568
- let atts = getElementAtts(el)
569
- for (let att in atts) {
570
- let att_str = `${att}_${atts[att]}`
580
+ /**
581
+ * shared styles to group
582
+ */
583
+ function sharedAttributesToGroup(svg) {
571
584
 
572
- if (!allProps[att_str]) {
573
- allProps[att_str] = []
574
- }
575
- allProps[att_str].push(el)
576
- //
577
- if (allProps[att_str].length === len) {
578
- globalAtts.push(att)
579
- }
580
- }
581
- }
585
+ let els = svg.querySelectorAll(renderedEls.join(', '))
586
+ let len = els.length;
587
+ if (len === 1) return;
582
588
 
583
- //console.log('allProps', allProps);
584
- //console.log('globalAtts', globalAtts);
585
-
586
- // apply global to parent SVG
587
- if (globalAtts.length) {
588
- let atts0 = getElementAtts(el0)
589
- for (let att in atts0) {
590
- // &&
591
- if (globalAtts.includes(att) && att !== 'transform') {
592
- svg.setAttribute(att, atts0[att])
593
- }
594
- }
595
- }
589
+ let el0 = els[0] || null
590
+ let stylePrev = el0.styleSet !== undefined ? [...el0.styleSet].join('_') : ''
596
591
 
597
- // detect groups
598
- for (let i = 1; i < len; i++) {
599
- let el = els[i];
600
- let styleArr = el.styleSet !== undefined ? [...el.styleSet] : [];
601
- let style = styleArr.length ? styleArr.join('_') : '';
602
- //console.log('style', style);
603
592
 
604
- // same style add to group
605
- if (style === stylePrev && elPrev.nextElementSibling === el) {
606
- groups[idx].push(el)
607
- }
608
- // start new group
609
- else {
610
- groups.push([el])
611
- idx++
612
- }
613
- // update style
614
- stylePrev = style
615
- elPrev = el
593
+ // all props
594
+ let allProps = {}
595
+
596
+ // find attributes shared by all
597
+ let globalAtts = []
616
598
 
617
- }// endof el loop
599
+ if (len) {
618
600
 
601
+ let groups = [[el0]];
602
+ let idx = 0;
603
+ let elPrev = el0
619
604
 
605
+ for (let i = 0; i < len; i++) {
606
+ let el = els[i];
607
+ let atts = getElementAtts(el)
608
+ for (let att in atts) {
609
+ let att_str = `${att}_${atts[att]}`
620
610
 
611
+ if (!allProps[att_str]) {
612
+ allProps[att_str] = []
613
+ }
614
+ allProps[att_str].push(el)
615
+ //
616
+ if (allProps[att_str].length === len) {
617
+ globalAtts.push(att)
618
+ }
619
+ }
620
+ }
621
621
 
622
+ //console.log('allProps', allProps);
623
+ //console.log('globalAtts', globalAtts);
622
624
 
625
+ // apply global to parent SVG
626
+ if (globalAtts.length) {
627
+ let atts0 = getElementAtts(el0)
628
+ for (let att in atts0) {
629
+ if (globalAtts.includes(att) && att !== 'transform') {
630
+ svg.setAttribute(att, atts0[att])
631
+ }
632
+ }
633
+ }
623
634
 
635
+ // detect groups
636
+ for (let i = 1; i < len; i++) {
637
+ let el = els[i];
638
+ let styleArr = el.styleSet !== undefined ? [...el.styleSet] : [];
639
+ let style = styleArr.length ? styleArr.join('_') : '';
640
+ //console.log('style', style);
641
+ //console.log('style === stylePrev', style, stylePrev);
642
+
643
+ // same style add to group
644
+ if (style === stylePrev && elPrev.nextElementSibling === el) {
645
+ groups[idx].push(el)
646
+ }
647
+ // start new group
648
+ else {
649
+ groups.push([el])
650
+ idx++
651
+ }
652
+ // update style
653
+ stylePrev = style
654
+ elPrev = el
624
655
 
625
- //console.log('groups', groups);
656
+ }// endof el loop
626
657
 
627
- // create groups
628
- for (let i = 0; i < groups.length; i++) {
629
- let children = groups[i];
630
- let child0 = children[0]
631
- let atts = getElementAtts(child0)
632
- let groupEl = child0.parentNode.closest('g')
658
+ //console.log('g', groups);
633
659
 
660
+ // create groups
661
+ for (let i = 0; i < groups.length; i++) {
662
+ let children = groups[i];
663
+ let child0 = children[0]
664
+ let atts = getElementAtts(child0)
665
+ let groupEl = child0.parentNode.closest('g')
634
666
 
635
- if (children.length === 1) {
636
- if (globalAtts.length) {
637
- let globalTransform = globalAtts.includes('transform')
638
- if (globalTransform) {
639
- //console.log(globalTransform, globalAtts);
640
- groupEl.setAttribute('transform', atts['transform'])
641
- }
667
+ // only 1 child - nothing to group
668
+ if (children.length === 1) continue
642
669
 
643
- for (let att in atts) {
644
- //&& att!=='transform'
645
- if (globalAtts.includes(att)) {
646
- child0.removeAttribute(att)
647
- }
648
- }
649
- }
650
- continue
651
- }
652
670
 
671
+ // create new group
672
+ if (!groupEl || groups.length > 1) {
673
+ //console.log('new group');
674
+ groupEl = document.createElementNS(svgNs, 'g')
675
+ child0.parentNode.insertBefore(groupEl, child0)
676
+ groupEl.append(...children)
677
+ }
653
678
 
654
- // create new group
655
- if (!groupEl) {
656
- //console.log('new group');
657
- groupEl = document.createElementNS(svgNs, 'g')
658
- child0.parentNode.insertBefore(groupEl, child0)
659
- groupEl.append(...children)
679
+ // move attributes to group
680
+ for (let att in atts) {
681
+ let val = atts[att];
682
+ //console.log('att', atts, val);
683
+
684
+ //|| att === 'transform'
685
+ let excludeAtts = ['id', 'class'];
686
+ if (!geometryProps.includes(att) && !excludeAtts.includes(att)) {
687
+ if (!globalAtts.includes(att) || att === 'transform') {
688
+ groupEl.setAttribute(att, val)
660
689
  }
690
+ children.forEach(child => {
691
+ child.removeAttribute(att)
692
+ })
693
+ }
694
+ }
661
695
 
662
696
 
697
+ } // endof groups
663
698
 
664
- // move attributes to group
665
- for (let att in atts) {
666
- let val = atts[att];
667
- //console.log('att', atts, val);
699
+ }
700
+ }
668
701
 
669
- //|| att === 'transform'
670
- if (!geometryProps.includes(att)) {
671
- if (!globalAtts.includes(att) || att === 'transform') {
672
- groupEl.setAttribute(att, val)
673
- }
674
- children.forEach(child => {
675
- child.removeAttribute(att)
676
- })
677
- }
678
- }
679
702
 
680
- } // endof groups
703
+ // merge adjacent paths
704
+ function mergePathsWithSameProps(svg) {
705
+ let paths = svg.querySelectorAll('path')
706
+ let len = paths.length;
681
707
 
682
- }
683
- }
708
+ if (len) {
709
+ let path0 = paths[0]
710
+ let d0 = path0.getAttribute('d')
711
+ let stylePrev = path0.styleSet !== undefined ? [...path0.styleSet].join(' ') : ''
712
+ //console.log('path0', path0);
684
713
 
685
- }
714
+ let remove = []
686
715
 
716
+ for (let i = 1; i < len; i++) {
717
+ let path = paths[i];
718
+ let style = path.styleSet !== undefined ? [...path.styleSet].join(' ') : ''
719
+ let isSibling = path.previousElementSibling === path0;
720
+ let d = path.getAttribute('d');
721
+ let isAbs = d.startsWith('M')
722
+ //console.log('path.previousElementSibling', path.previousElementSibling);
723
+ //console.log(isSibling, style, stylePrev, path.id);
724
+
725
+ if (isSibling && style === stylePrev) {
726
+ let dAbs = isAbs ? d : parsePathDataString(d).pathData.map(com => `${com.type} ${com.values.join(' ')}`).join(' ')
727
+ //console.log('same style', dAbs, isAbs);
728
+ d0 += dAbs;
729
+ path0.setAttribute('d', d0)
730
+ //console.log('remove', path);
731
+ remove.push(path)
732
+ //path.remove();
687
733
 
734
+ } else {
735
+ path0 = path
736
+ //console.log('path0', path0, path);
737
+ d0 = isAbs ? d : parsePathDataString(d).pathData.map(com => `${com.type} ${com.values.join(' ')}`).join(' ')
688
738
 
689
- // remove futile clip-paths
690
- if (cleanupClip) removeFutileClipPaths(svg, { x, y, width, height })
739
+ }
691
740
 
741
+ // update style
742
+ stylePrev = style
743
+ }
692
744
 
693
- // replace href attributes with namespace - required by many older applications
694
- if (legacyHref) {
695
- svg.setAttribute('xmlns:xlink', "http://www.w3.org/1999/xlink")
696
- let hrefs = svg.querySelectorAll('[href]')
697
- hrefs.forEach(el => {
698
- let href = el.getAttribute('href')
699
- el.setAttribute('xlink:href', href)
700
- el.removeAttribute('href')
745
+
746
+ //console.log('remove', remove);
747
+ remove.forEach(el => {
748
+ el.remove()
701
749
  })
750
+
702
751
  }
703
752
 
753
+ }
704
754
 
705
755
 
706
- return { svg, svgElProps }
707
-
708
- }
709
756
 
710
757
 
711
758
  function removeOffCanvasEls(svg, { x = 0, y = 0, width = 0, height = 0 } = {}) {
@@ -718,7 +765,9 @@ function removeOffCanvasEls(svg, { x = 0, y = 0, width = 0, height = 0 } = {}) {
718
765
  bb0.bottom = y + height
719
766
 
720
767
  els.forEach(el => {
768
+ //console.log(el);
721
769
  let bb = getElBBox(el)
770
+ //console.log('!!bb', bb);
722
771
  let outside = bb.right < bb0.x || bb.bottom < bb0.y || bb.x > bb0.right || bb.y > bb.bottom
723
772
  if (outside) el.remove();
724
773
  })
@@ -733,6 +782,13 @@ function addSvgViewBox(svg, { x = 0, y = 0, width = 0, height = 0 } = {}) {
733
782
  svg.setAttribute('viewBox', [x, y, width, height].join(' '))
734
783
  }
735
784
 
785
+ export function removeEmptySVGEls(svg) {
786
+ let els = svg.querySelectorAll('g, defs');
787
+ els.forEach(el => {
788
+ if (!el.children.length) el.remove()
789
+ })
790
+ }
791
+
736
792
 
737
793
  function cleanupSvgDefs(svg, { x = 0, y = 0, width = 0, height = 0, cleanupClip = true } = {}) {
738
794
  let defs = svg.querySelectorAll('defs')
@@ -769,9 +825,6 @@ function cleanupSvgDefs(svg, { x = 0, y = 0, width = 0, height = 0, cleanupClip
769
825
  }
770
826
  })
771
827
 
772
- // remove futile clip-paths
773
- //if (cleanupClip) removeFutileClipPaths(svg, {x, y, width, height})
774
-
775
828
  }
776
829
 
777
830
 
@@ -825,274 +878,23 @@ function removeFutileClipPaths(svg, { x = 0, y = 0, width = 0, height = 0 } = {}
825
878
  }
826
879
 
827
880
 
828
-
829
- function moveAttributesToGroup(svgElProps = [], mergePaths = true) {
830
-
831
- let combine = [[svgElProps[0]]]
832
- let idx = 0;
833
- let lastProps = '';
834
- let l = svgElProps.length;
835
- let itemsWithProps = svgElProps.filter(item => item.propstr)
836
- let path0;
837
-
838
-
839
- // merge paths without properties
840
- let dCombined = ''
841
- if (!itemsWithProps.length && mergePaths) {
842
- let path0 = null;
843
-
844
- for (let i = 0; i < l; i++) {
845
- let item = svgElProps[i]
846
- if (item.name !== 'path') continue;
847
- let remove = true;
848
-
849
-
850
- let path = item.el;
851
-
852
- // set 1st path
853
- if (!path0) {
854
- path0 = path;
855
- remove = false;
856
- }
857
-
858
- let d = item.propsFiltered.d
859
- let isAbs = d.startsWith('M')
860
- let dAbs = isAbs ? d : parsePathDataString(d).pathData.map(com => `${com.type} ${com.values.join(' ')}`).join(' ')
861
-
862
- dCombined += dAbs;
863
-
864
- // delete path el
865
- if (remove) path.remove();
866
- }
867
-
868
- //console.log('dCombined', dCombined);
869
- if (path0) path0.setAttribute('d', dCombined)
870
- return
871
- }
872
-
873
-
874
- // add to combine chunks
875
- for (let i = 0; i < l; i++) {
876
- let item = svgElProps[i];
877
- let props = item.propsFiltered;
878
- let propstr = [];
879
- for (let prop in props) {
880
- if (prop !== 'd' && prop !== 'id') {
881
- propstr.push(`${prop}:${props[prop]}`)
882
- }
883
- }
884
- propstr = propstr.join('_')
885
- item.propstr = propstr;
886
-
887
- if (l > 1 && propstr === lastProps) {
888
- combine[idx].push(item)
889
- } else {
890
- if (l > 1 && combine[idx].length) {
891
- combine.push([])
892
- idx++
893
- }
894
- }
895
- lastProps = propstr;
896
- }
897
-
898
-
899
- // add att groups
900
- for (let i = 0; i < combine.length; i++) {
901
- let group = combine[i]
902
-
903
- if (group.length > 1) {
904
- // 1st el
905
- let el0 = group[0].el;
906
- let props = group[0].propsFiltered;
907
- let g = el0.parentNode.closest('g') ? el0.parentNode.closest('g') : null;
908
-
909
- // wrap in group if not existent
910
- if (!g) {
911
- g = document.createElementNS(svgNs, 'g');
912
- el0.parentNode.insertBefore(g, el0)
913
- group.forEach(item => {
914
- g.append(item.el)
915
- })
916
- }
917
-
918
- let children = [...g.children]
919
- for (let prop in props) {
920
- if (prop !== 'd' && prop !== 'id') {
921
- let value = props[prop]
922
- // apply to parent group
923
- g.setAttribute(prop, value)
924
-
925
- // remove from children
926
- children.forEach(el => {
927
- if (el.getAttribute(prop) === value) {
928
- el.removeAttribute(prop)
929
- }
930
- })
931
- }
932
-
933
-
934
- if (mergePaths) {
935
- group = group.filter(Boolean)
936
- let l = group.length
937
- // nothing to merge
938
- if (l === 1) return group[0].el;
939
-
940
- path0 = group[0].el;
941
- let dCombined = group[0].propsFiltered.d;
942
-
943
- for (let i = 1; i < l; i++) {
944
- let item = group[i]
945
- let path = item.el;
946
- let d = item.propsFiltered.d
947
- let isAbs = d.startsWith('M')
948
-
949
- let dAbs = isAbs ? d : parsePathDataString(d).pathData.map(com => `${com.type} ${com.values.join(' ')}`).join(' ')
950
-
951
- console.log('dAbs', dAbs);
952
-
953
- //console.log(isAbs, dAbs);
954
- // concat pathdata string
955
- dCombined += dAbs;
956
-
957
- // delete path el
958
- path.remove();
959
- }
960
-
961
- path0.setAttribute('d', dCombined)
962
-
963
- }
964
-
965
- }
966
- }
967
- }
968
-
969
- }
970
-
971
-
972
- export function scaleProps(styleProps = {}, { props = [], scale = 1 } = {}) {
973
- if (scale === 1 || !props.length) return props;
974
-
975
- for (let i = 0; i < props.length; i++) {
976
- let prop = props[i];
977
-
978
- if (styleProps[prop] !== undefined) {
979
- styleProps[prop] = styleProps[prop].map(val => val * scale)
980
- }
981
- }
982
- return styleProps
983
- }
984
-
985
- export function removeSVGEls(svg, {
986
- remove = ['metadata', 'script'],
987
- removeNameSpaced = true,
988
- } = {}) {
989
-
990
- let els = svg.querySelectorAll('*')
991
-
992
- let allowMeta = !remove.includes('metadata');
993
-
994
- els.forEach(el => {
995
- let nodeName = el.nodeName;
996
- let isMeta = allowMeta && el.closest('metadata')
997
- if (
998
- !isMeta &&
999
- ((removeNameSpaced && nodeName.includes(':')) ||
1000
- remove.includes(nodeName))
1001
- ) {
1002
- el.remove()
1003
- }
881
+ function hrefToXlink(svg) {
882
+ svg.setAttribute('xmlns:xlink', "http://www.w3.org/1999/xlink")
883
+ let hrefs = svg.querySelectorAll('[href]')
884
+ hrefs.forEach(el => {
885
+ let href = el.getAttribute('href')
886
+ el.setAttribute('xlink:href', href)
887
+ //el.removeAttribute('href')
1004
888
  })
1005
889
  }
1006
890
 
1007
891
 
1008
- function cleanSvgPrologue(svgString) {
1009
- return (
1010
- svgString
1011
- // Remove XML prologues like <?xml ... ?>
1012
- .replace(/<\?xml[\s\S]*?\?>/gi, "")
1013
- // Remove DOCTYPE declarations
1014
- .replace(/<!DOCTYPE[\s\S]*?>/gi, "")
1015
- // Remove comments <!-- ... -->
1016
- .replace(/<!--[\s\S]*?-->/g, "")
1017
- // Trim extra whitespace
1018
- .trim()
1019
- );
1020
- }
1021
-
1022
- function removeExcludedAttribues(el, {
1023
- allowed = ['viewBox', 'xmlns', 'width', 'height', 'id', 'class'],
1024
- allowAriaAtts = true,
1025
- allowDataAtts = true,
1026
- allowMeta = false
1027
- } = {}) {
1028
- let atts = [...el.attributes].map((att) => att.name);
1029
- atts.forEach((att) => {
1030
-
1031
- let isMeta = allowMeta && (att === 'title')
1032
- let isAria = allowAriaAtts && att.startsWith('aria-')
1033
- let isData = allowDataAtts && att.startsWith('data-')
1034
- //console.log(att, isData, 'allowDataAtts', allowDataAtts);
1035
-
1036
- if (
1037
- !allowed.includes(att) &&
1038
- !isAria && !isData && !isMeta
1039
- ) {
1040
- el.removeAttribute(att);
1041
- }
1042
- });
1043
- }
1044
-
1045
-
1046
- function removeAtts(el, exclude = [], include = []) {
1047
- let atts = [...el.attributes].map((att) => att.name);
1048
- atts.forEach((att) => {
1049
- if (exclude.includes(att) && !include.includes(att)) {
1050
- el.removeAttribute(att);
1051
- }
1052
- });
1053
- }
1054
-
1055
-
1056
- function removeNameSpaceAtts(el, {
1057
- include = ['xlink:href']
1058
- } = {}) {
1059
- let atts = [...el.attributes].map((att) => att.name);
1060
- atts.forEach((att) => {
1061
- if (att.includes(":") && !include.includes(att)) {
1062
- el.removeAttribute(att);
1063
- }
1064
- });
1065
- }
1066
892
 
1067
- export function stringifySVG(svg, {
1068
- omitNamespace = false,
1069
- removeComments = true,
1070
- } = {}) {
1071
- let markup = new XMLSerializer().serializeToString(svg);
1072
893
 
1073
- if (omitNamespace) {
1074
- markup = markup.replaceAll('xmlns="http://www.w3.org/2000/svg"', '')
1075
- }
1076
894
 
1077
- if (removeComments) {
1078
- markup = markup
1079
- .replace(/(<!--.*?-->)|(<!--[\S\s]+?-->)|(<!--[\S\s]*?$)/g, '')
1080
- }
1081
895
 
1082
- markup = markup
1083
- .replace(/\t/g, "")
1084
- .replace(/[\n\r|]/g, "\n")
1085
- .replace(/\n\s*\n/g, '\n')
1086
- .replace(/ +/g, ' ')
1087
- //.replace(/ +/g, ' ')
1088
- .replace(/> </g, '><')
1089
- .trim()
1090
- // sanitize linebreaks within pathdata
1091
- .replaceAll('&#10;', '\n');
1092
896
 
1093
897
 
1094
898
 
1095
899
 
1096
900
 
1097
- return markup
1098
- }