svg-path-simplify 0.4.1 → 0.4.3
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/CHANGELOG.md +19 -0
- package/README.md +6 -4
- package/dist/svg-path-simplify.esm.js +2450 -888
- package/dist/svg-path-simplify.esm.min.js +2 -2
- package/dist/svg-path-simplify.js +2450 -888
- package/dist/svg-path-simplify.min.js +2 -2
- package/dist/svg-path-simplify.pathdata.esm.js +167 -85
- package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
- package/docs/privacy-webapp.md +24 -0
- package/index.html +333 -132
- package/package.json +5 -2
- package/src/css_parse.js +317 -0
- package/src/detect_input.js +34 -4
- package/src/pathData_simplify_harmonize_cpts.js +77 -1
- package/src/pathSimplify-main.js +246 -262
- package/src/pathSimplify-presets.js +243 -0
- package/src/poly-fit-curve-schneider.js +14 -7
- package/src/simplify_poly_RC.js +102 -0
- package/src/simplify_poly_RDP.js +109 -1
- package/src/simplify_poly_radial_distance.js +3 -3
- package/src/string_helpers.js +144 -0
- package/src/svgii/convert_units.js +8 -2
- package/src/svgii/geometry.js +182 -3
- package/src/svgii/geometry_length.js +237 -0
- package/src/svgii/pathData_convert.js +43 -1
- package/src/svgii/pathData_fix_directions.js +6 -0
- package/src/svgii/pathData_fromPoly.js +3 -3
- package/src/svgii/pathData_getLength.js +86 -0
- package/src/svgii/pathData_parse.js +2 -0
- package/src/svgii/pathData_parse_els.js +189 -189
- package/src/svgii/pathData_split_to_groups.js +168 -0
- package/src/svgii/pathData_stringify.js +26 -64
- package/src/svgii/pathData_toPolygon.js +3 -4
- package/src/svgii/poly_analyze.js +61 -0
- package/src/svgii/poly_normalize.js +11 -2
- package/src/svgii/poly_to_pathdata.js +85 -24
- package/src/svgii/rounding.js +8 -7
- package/src/svgii/svg-styles-to-attributes-const.js +1 -0
- package/src/svgii/svg_cleanup.js +467 -421
- package/src/svgii/svg_cleanup_convertPathLength.js +32 -0
- package/src/svgii/svg_cleanup_general_svg_atts.js +97 -0
- package/src/svgii/svg_cleanup_normalize_transforms.js +83 -0
- package/src/svgii/svg_cleanup_remove_els_and_atts.js +72 -0
- package/src/svgii/svg_cleanup_ungroup.js +36 -0
- package/src/svgii/svg_el_parse_style_props.js +76 -28
- package/src/svgii/svg_getElementLength.js +67 -0
- package/tests/testSVG_shape.js +59 -0
- package/tests/testSVG_transform.js +61 -0
package/src/svgii/svg_cleanup.js
CHANGED
|
@@ -9,28 +9,28 @@ import { parsePathDataString } from "./pathData_parse";
|
|
|
9
9
|
import { parsePathDataNormalized } from "./pathData_convert";
|
|
10
10
|
import { pathElToShape, shapeElToPath } from "./pathData_parse_els";
|
|
11
11
|
//import { scaleProps } from "./svg-styles-to-attributes";
|
|
12
|
-
import { geometryEls, renderedEls, shapeEls, strokeAtts } from "./svg-styles-to-attributes-const";
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
//const DOMParserPoly = globalThis.DOMParser;
|
|
18
|
+
import { toCamelCase, toShortStr } from "../string_helpers";
|
|
19
|
+
import { getElementLength } from "./svg_getElementLength";
|
|
20
|
+
import { 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";
|
|
28
26
|
|
|
29
27
|
|
|
30
28
|
export function cleanUpSVG(svgMarkup, {
|
|
31
29
|
removeHidden = true,
|
|
32
30
|
//removeUnused = true,
|
|
33
31
|
stylesToAttributes = true,
|
|
32
|
+
attributesToGroup = false,
|
|
33
|
+
|
|
34
34
|
removePrologue = true,
|
|
35
35
|
removeIds = false,
|
|
36
36
|
removeClassNames = false,
|
|
@@ -50,15 +50,27 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
50
50
|
|
|
51
51
|
mergePaths = false,
|
|
52
52
|
removeOffCanvas = true,
|
|
53
|
+
|
|
53
54
|
cleanupSVGAtts = true,
|
|
54
55
|
removeNameSpaced = true,
|
|
55
|
-
|
|
56
|
+
removeNameSpacedAtts = true,
|
|
57
|
+
convertPathLength = false,
|
|
58
|
+
|
|
59
|
+
// meta
|
|
60
|
+
allowMeta = false,
|
|
61
|
+
allowDataAtts = true,
|
|
62
|
+
allowAriaAtts = true,
|
|
63
|
+
|
|
56
64
|
//shapesToPaths = false,
|
|
57
65
|
shapeConvert = false,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
66
|
+
convertShapes = [],
|
|
67
|
+
|
|
68
|
+
// remove elements
|
|
69
|
+
removeElements = [],
|
|
70
|
+
|
|
71
|
+
// remove attributes
|
|
72
|
+
removeSVGAttributes = [],
|
|
73
|
+
removeElAttributes = [],
|
|
62
74
|
|
|
63
75
|
convertTransforms = false,
|
|
64
76
|
removeDefaults = true,
|
|
@@ -67,9 +79,14 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
67
79
|
excludedEls = [],
|
|
68
80
|
} = {}) {
|
|
69
81
|
|
|
70
|
-
//attributesToGroup = cleanupSVGAtts ? true : false;
|
|
71
82
|
|
|
72
83
|
|
|
84
|
+
// resolve dependencies
|
|
85
|
+
if (unGroup || convertTransforms || minifyRgbColors || attributesToGroup)
|
|
86
|
+
stylesToAttributes = true;
|
|
87
|
+
|
|
88
|
+
if(stylesToAttributes) cleanUpStrokes = true;
|
|
89
|
+
|
|
73
90
|
// replace namespaced refs
|
|
74
91
|
if (fixHref) svgMarkup = svgMarkup.replaceAll("xlink:href=", "href=");
|
|
75
92
|
|
|
@@ -78,21 +95,89 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
78
95
|
.parseFromString(svgMarkup, "text/html")
|
|
79
96
|
.querySelector("svg");
|
|
80
97
|
|
|
98
|
+
|
|
81
99
|
let viewBox = getViewBox(svg)
|
|
82
100
|
let { x, y, width, height } = viewBox;
|
|
101
|
+
let remove = []
|
|
102
|
+
|
|
83
103
|
|
|
84
104
|
|
|
85
|
-
//
|
|
105
|
+
// add viewBox
|
|
106
|
+
if (addViewBox) addSvgViewBox(svg, { x, y, width, height })
|
|
107
|
+
if (addDimensions) {
|
|
108
|
+
svg.setAttribute('width', width);
|
|
109
|
+
svg.setAttribute('height', height);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// remove unused defs or optimize order
|
|
113
|
+
if (cleanupDefs) cleanupSvgDefs(svg, { x, y, width, height, cleanupClip });
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
// remove off canvas
|
|
117
|
+
if (removeOffCanvas) removeOffCanvasEls(svg, { x, y, width, height });
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* collect svg styles
|
|
122
|
+
* and properties
|
|
123
|
+
*/
|
|
86
124
|
let propOptions = {
|
|
87
|
-
width
|
|
88
|
-
height
|
|
125
|
+
width,
|
|
126
|
+
height,
|
|
89
127
|
normalizeTransforms,
|
|
90
128
|
removeDefaults: false,
|
|
91
129
|
cleanUpStrokes: false,
|
|
130
|
+
//cleanUpStrokes,
|
|
131
|
+
allowMeta,
|
|
132
|
+
allowDataAtts,
|
|
133
|
+
allowAriaAtts,
|
|
92
134
|
autoRoundValues,
|
|
135
|
+
removeIds,
|
|
136
|
+
removeClassNames,
|
|
93
137
|
minifyRgbColors,
|
|
138
|
+
stylesheetProps: {},
|
|
139
|
+
exclude:[]
|
|
94
140
|
}
|
|
141
|
+
|
|
142
|
+
// root svg inline style properties
|
|
95
143
|
let stylePropsSVG = parseStylesProperties(svg, propOptions)
|
|
144
|
+
//console.log('stylePropsSVG', stylePropsSVG);
|
|
145
|
+
|
|
146
|
+
let styleEl = svg.querySelector('style')
|
|
147
|
+
let cssStylePropsSVG = {}
|
|
148
|
+
|
|
149
|
+
if (styleEl) {
|
|
150
|
+
cssStylePropsSVG = parseSvgCss(styleEl, { parent: svg })
|
|
151
|
+
|
|
152
|
+
//save stylesheet dependencies in node
|
|
153
|
+
for (let selector in cssStylePropsSVG) {
|
|
154
|
+
let els = svg.querySelectorAll(`${selector}`);
|
|
155
|
+
els.forEach(el => {
|
|
156
|
+
if (!el['cssRules']) el['cssRules'] = [];
|
|
157
|
+
el['cssRules'].push(selector)
|
|
158
|
+
|
|
159
|
+
// remove class names only used for styling
|
|
160
|
+
if (stylesToAttributes) {
|
|
161
|
+
let className = selector.substring(1)
|
|
162
|
+
el.classList.remove(className)
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
//console.log('cssStylePropsSVG', cssStylePropsSVG);
|
|
168
|
+
|
|
169
|
+
// remove style element from element
|
|
170
|
+
if (stylesToAttributes) {
|
|
171
|
+
styleEl.remove()
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// remove style element from root SVG
|
|
175
|
+
if (stylesToAttributes) svg.removeAttribute('style')
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
// add stylesheet props
|
|
179
|
+
propOptions.stylesheetProps = cssStylePropsSVG;
|
|
180
|
+
|
|
96
181
|
|
|
97
182
|
// add svg font size for scaling relative
|
|
98
183
|
propOptions.fontSize = stylePropsSVG['font-size'] ? stylePropsSVG['font-size'][0] : 16;
|
|
@@ -107,7 +192,10 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
107
192
|
let groupProps = [];
|
|
108
193
|
|
|
109
194
|
groups.forEach(g => {
|
|
195
|
+
//propOptions.exclude.push('class', 'id');
|
|
110
196
|
let stylePropsG = parseStylesProperties(g, propOptions)
|
|
197
|
+
//console.log('stylePropsG', stylePropsG);
|
|
198
|
+
|
|
111
199
|
groupProps.push(stylePropsG);
|
|
112
200
|
let children = g.querySelectorAll(`${renderedEls.join(', ')}`)
|
|
113
201
|
|
|
@@ -122,64 +210,36 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
122
210
|
|
|
123
211
|
|
|
124
212
|
|
|
125
|
-
|
|
126
|
-
//console.log('cleanupSVGAtts');
|
|
127
|
-
let allowed = ['viewBox', 'xmlns', 'width', 'height', 'id', 'class'];
|
|
128
|
-
if (!stylesToAttributes) {
|
|
129
|
-
allowed.push('fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin', 'font-size', 'font-family', 'font-style', 'style');
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
removeExcludedAttribues(svg, allowed)
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// add viewBox
|
|
136
|
-
if (addViewBox) addSvgViewBox(svg, { x, y, width, height })
|
|
137
|
-
if (addDimensions) {
|
|
138
|
-
svg.setAttribute('width', width);
|
|
139
|
-
svg.setAttribute('height', height);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
// remove unused defs or optimize order
|
|
144
|
-
if (cleanupDefs) cleanupSvgDefs(svg, { x, y, width, height, cleanupClip });
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
// remove off canvas
|
|
148
|
-
if (removeOffCanvas) removeOffCanvasEls(svg, { x, y, width, height });
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
// always remove scripts
|
|
152
|
-
let removeEls = ['metadata', 'script', ...excludedEls]
|
|
153
|
-
|
|
154
|
-
removeSVGEls(svg, { removeEls, removeNameSpaced });
|
|
155
|
-
|
|
156
|
-
// an array of all elements' properties
|
|
213
|
+
// collect all elements' properties
|
|
157
214
|
let svgElProps = []
|
|
158
215
|
let els = svg.querySelectorAll(`${renderedEls.join(', ')}`)
|
|
159
216
|
|
|
160
217
|
|
|
218
|
+
/**
|
|
219
|
+
* loop all geometry elements
|
|
220
|
+
*/
|
|
161
221
|
for (let i = 0; i < els.length; i++) {
|
|
162
222
|
let el = els[i];
|
|
163
223
|
|
|
164
224
|
let name = el.nodeName.toLowerCase();
|
|
165
225
|
//console.log(name);
|
|
166
226
|
|
|
167
|
-
// 1. remove hidden elements
|
|
168
|
-
let style = el.getAttribute('style') || ''
|
|
169
|
-
let isHiddenByStyle = style ? style.trim().includes('display:none') : false;
|
|
170
|
-
let isHidden = (el.getAttribute('display') && el.getAttribute('display') === 'none') || isHiddenByStyle;
|
|
171
|
-
if (name.includes(':') || removeEls.includes(name) || (removeHidden && isHidden)) {
|
|
172
|
-
el.remove();
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
|
|
177
227
|
/**
|
|
178
|
-
* get all style properties
|
|
228
|
+
* get all element style properties
|
|
179
229
|
* convert relative or physical units
|
|
180
230
|
* to user units
|
|
181
231
|
*/
|
|
182
232
|
let styleProps = parseStylesProperties(el, propOptions)
|
|
233
|
+
let stylePropsFiltered = {}
|
|
234
|
+
//console.log('styleProps', name, styleProps);
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
// convert pathLength before transforming
|
|
238
|
+
if (convertPathLength) {
|
|
239
|
+
styleProps = convertPathLengthAtt(el, { styleProps });
|
|
240
|
+
remove = [...new Set([...remove, ...styleProps.remove])];
|
|
241
|
+
}
|
|
242
|
+
|
|
183
243
|
|
|
184
244
|
// get parent styles
|
|
185
245
|
let { parentStyleProps = [] } = el;
|
|
@@ -187,7 +247,10 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
187
247
|
let transFormInherited = []
|
|
188
248
|
|
|
189
249
|
|
|
190
|
-
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* consolidate all properties:
|
|
253
|
+
* merge with inherited transforms
|
|
191
254
|
* and styles from group
|
|
192
255
|
*/
|
|
193
256
|
parentStyleProps.forEach(props => {
|
|
@@ -203,169 +266,127 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
203
266
|
})
|
|
204
267
|
|
|
205
268
|
|
|
206
|
-
//merge transforms
|
|
269
|
+
// merge all transforms
|
|
207
270
|
transFormInherited = [...transFormInherited, ...styleProps.transformArr]
|
|
208
271
|
styleProps.transformArr = transFormInherited
|
|
209
272
|
|
|
210
273
|
|
|
274
|
+
// don't inherit class from SVG
|
|
275
|
+
if (stylePropsSVG['class']) delete stylePropsSVG['class']
|
|
276
|
+
if (stylePropsSVG['id']) delete stylePropsSVG['id']
|
|
277
|
+
|
|
211
278
|
// merge with svg props
|
|
212
279
|
styleProps = {
|
|
213
280
|
...stylePropsSVG,
|
|
214
281
|
...inheritedProps,
|
|
215
282
|
...styleProps
|
|
216
283
|
}
|
|
217
|
-
|
|
284
|
+
|
|
218
285
|
|
|
219
286
|
// add combined transforms
|
|
220
287
|
addTransFormProps(styleProps, transFormInherited);
|
|
221
|
-
|
|
222
288
|
//console.log('transFormInherited', transFormInherited);
|
|
223
289
|
//console.log('styleProps', styleProps);
|
|
224
290
|
|
|
225
|
-
|
|
226
|
-
let { remove, matrix, transComponents } = styleProps;
|
|
227
|
-
|
|
228
|
-
// mark attributes for removal
|
|
229
|
-
if (removeClassNames) styleProps.remove.push('class')
|
|
230
|
-
if (removeIds) styleProps.remove.push('id')
|
|
231
|
-
if (removeDimensions) {
|
|
232
|
-
styleProps.remove.push('width')
|
|
233
|
-
styleProps.remove.push('height')
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
// styles to atts
|
|
238
|
-
if (unGroup || convertTransforms || minifyRgbColors ) stylesToAttributes = true;
|
|
291
|
+
remove = [...new Set([...remove, ...styleProps.remove])];
|
|
239
292
|
|
|
240
293
|
|
|
241
|
-
|
|
294
|
+
/**
|
|
295
|
+
* remove els and attributes
|
|
296
|
+
*/
|
|
242
297
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
*/
|
|
246
|
-
if (normalizeTransforms && matrix) {
|
|
247
|
-
let { rotate, scaleX, scaleY, skewX, translateX, translateY } = transComponents;
|
|
248
|
-
//console.log(rotate, scaleX, scaleY, skewX, skewY, translateX, translateY);
|
|
298
|
+
// remove meta
|
|
299
|
+
if (!allowMeta) removeElements.push('meta', 'metadata', 'desc', 'title')
|
|
249
300
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
let needsTrans = convertTransforms || (name === 'g') || (hasRot) || unProportional
|
|
255
|
-
//needsTrans = true
|
|
301
|
+
if (removeClassNames) {
|
|
302
|
+
removeSVGAttributes.push('class');
|
|
303
|
+
removeElAttributes.push('class');
|
|
304
|
+
}
|
|
256
305
|
|
|
257
|
-
|
|
306
|
+
if (removeIds) {
|
|
307
|
+
removeSVGAttributes.push('id')
|
|
308
|
+
removeElAttributes.push('id')
|
|
309
|
+
}
|
|
258
310
|
|
|
259
|
-
if (name === 'circle' || name === 'ellipse') {
|
|
260
|
-
styleProps.cx[0] = [styleProps.cx[0] * scaleX + translateX]
|
|
261
|
-
styleProps.cy[0] = [styleProps.cy[0] * scaleX + translateY]
|
|
262
311
|
|
|
263
|
-
|
|
312
|
+
// remove hidden elements
|
|
313
|
+
removeHiddenSvgEls(svg)
|
|
264
314
|
|
|
265
|
-
|
|
266
|
-
|
|
315
|
+
// remove SVG elements
|
|
316
|
+
removeSvgEls(svg, { removeElements, removeNameSpaced });
|
|
267
317
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
let x = styleProps.x ? styleProps.x[0] + translateX : translateX;
|
|
271
|
-
let y = styleProps.y ? styleProps.y[0] + translateY : translateY;
|
|
318
|
+
// remove SVG attributes
|
|
319
|
+
removeSvgAtts(svg, removeSVGAttributes);
|
|
272
320
|
|
|
273
|
-
|
|
274
|
-
|
|
321
|
+
// remove SVG child element attributes
|
|
322
|
+
removeSvgChildAtts(svg, removeElAttributes);
|
|
275
323
|
|
|
276
|
-
styleProps.x = [x]
|
|
277
|
-
styleProps.y = [y]
|
|
278
324
|
|
|
279
|
-
|
|
280
|
-
|
|
325
|
+
// general cleanup
|
|
326
|
+
if (cleanupSVGAtts) cleanupSVGAttributes(svg, { removeIds, removeClassNames, removeDimensions, stylesToAttributes, allowMeta, allowAriaAtts, allowDataAtts });
|
|
281
327
|
|
|
282
|
-
styleProps.width = [styleProps.width[0] * scaleX]
|
|
283
|
-
styleProps.height = [styleProps.height[0] * scaleX]
|
|
284
|
-
}
|
|
285
328
|
|
|
286
|
-
remove.push('transform')
|
|
287
329
|
|
|
288
|
-
|
|
289
|
-
styleProps = scaleProps(styleProps, { props: ['stroke-width', 'stroke-dasharray'], scale: scaleX })
|
|
330
|
+
if (stylesToAttributes) {
|
|
290
331
|
|
|
291
|
-
|
|
292
|
-
|
|
332
|
+
/**
|
|
333
|
+
* normalize transforms
|
|
334
|
+
*/
|
|
335
|
+
if (normalizeTransforms) {
|
|
336
|
+
styleProps = setNormalizedTransformsToEl(el, { styleProps });
|
|
337
|
+
//remove = styleProps.remove;
|
|
338
|
+
remove = [...new Set([...remove, ...styleProps.remove])];
|
|
293
339
|
|
|
294
|
-
}
|
|
295
340
|
}
|
|
296
341
|
|
|
297
|
-
|
|
298
342
|
/**
|
|
299
343
|
* apply consolidated
|
|
300
344
|
* element attributes
|
|
345
|
+
* remove non-supported element props
|
|
301
346
|
*/
|
|
347
|
+
stylePropsFiltered = filterSvgElProps(name, styleProps,
|
|
348
|
+
{ removeDefaults: true, cleanUpStrokes, allowMeta, allowAriaAtts, allowDataAtts, removeIds });
|
|
302
349
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
350
|
+
//remove = [...remove, ...stylePropsFiltered.remove];
|
|
351
|
+
remove = [...new Set([...remove, ...stylePropsFiltered.remove])];
|
|
352
|
+
//console.log('el remove', name, remove);
|
|
353
|
+
//console.log('!!stylePropsFiltered', name, stylePropsFiltered);
|
|
307
354
|
|
|
308
355
|
for (let prop in stylePropsFiltered.propsFiltered) {
|
|
309
356
|
let values = styleProps[prop]
|
|
310
|
-
//console.log('add', prop);
|
|
311
357
|
let val = values.length ? values.join(' ') : values[0]
|
|
312
358
|
el.setAttribute(prop, val)
|
|
313
359
|
}
|
|
314
360
|
|
|
315
|
-
// remove obsolete attributes
|
|
316
|
-
for (let i = 0; i < remove.length; i++) {
|
|
317
|
-
let att = remove[i];
|
|
318
|
-
if (!stylesToAttributes && att === 'style') continue
|
|
319
|
-
|
|
320
|
-
//console.log('remove att', att, name);
|
|
321
|
-
el.removeAttribute(att)
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
|
|
325
361
|
|
|
326
362
|
/**
|
|
327
|
-
* remove
|
|
328
|
-
*
|
|
329
|
-
* or remove nesting
|
|
363
|
+
* remove obsolete
|
|
364
|
+
* attributes
|
|
330
365
|
*/
|
|
366
|
+
//removeSvgChildAtts(svg, remove)
|
|
331
367
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
g.parentNode.insertBefore(child, g)
|
|
338
|
-
})
|
|
339
|
-
g.remove()
|
|
340
|
-
})
|
|
341
|
-
} else {
|
|
342
|
-
groups.forEach((g, i) => {
|
|
343
|
-
let atts = [...Object.keys(groupProps[i]), 'style', 'transform'];
|
|
344
|
-
atts.forEach(att => {
|
|
345
|
-
g.removeAttribute(att)
|
|
346
|
-
})
|
|
347
|
-
})
|
|
348
|
-
|
|
368
|
+
for (let i = 0; i < remove.length; i++) {
|
|
369
|
+
let att = remove[i];
|
|
370
|
+
//if (att === 'style') continue
|
|
371
|
+
//console.log('--remove att', remove, att, name);
|
|
372
|
+
el.removeAttribute(att)
|
|
349
373
|
}
|
|
350
374
|
|
|
351
375
|
|
|
352
376
|
} // endof style processing
|
|
353
377
|
|
|
354
378
|
|
|
379
|
+
|
|
355
380
|
/**
|
|
356
381
|
* element conversions:
|
|
357
382
|
* shapes to paths or
|
|
358
383
|
* paths to shapes
|
|
359
384
|
*/
|
|
360
385
|
|
|
361
|
-
|
|
362
386
|
// force shape conversion when transform conversion is enabled
|
|
363
387
|
if (convertTransforms) {
|
|
364
388
|
shapeConvert = 'toPaths';
|
|
365
|
-
|
|
366
|
-
convert_ellipses = true;
|
|
367
|
-
convert_poly = true;
|
|
368
|
-
convert_lines = true;
|
|
389
|
+
convertShapes = ['path', 'rect', 'ellipse', 'circle', 'line', 'polygon', 'polyline'];
|
|
369
390
|
}
|
|
370
391
|
|
|
371
392
|
// convert shapes to paths
|
|
@@ -373,8 +394,8 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
373
394
|
|
|
374
395
|
let { matrix = null, transComponents = null } = styleProps;
|
|
375
396
|
|
|
397
|
+
// scale props like stroke width or dash-array before conversion
|
|
376
398
|
if (matrix && transComponents) {
|
|
377
|
-
// scale props like stroke width or dash-array before conversion
|
|
378
399
|
['stroke-width', 'stroke-dasharray'].forEach(att => {
|
|
379
400
|
let attVal = el.getAttribute(att)
|
|
380
401
|
let vals = attVal ? attVal.split(' ').filter(Boolean).map(Number).map(val => val * transComponents.scaleX) : []
|
|
@@ -384,59 +405,310 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
384
405
|
|
|
385
406
|
// convert paths only if a matrix transform is required
|
|
386
407
|
if (matrix ? geometryEls.includes(name) : shapeEls.includes(name)) {
|
|
387
|
-
|
|
388
|
-
let path = shapeElToPath(el, { width, height,
|
|
408
|
+
//console.log('detrans', name, el.id, matrix);
|
|
409
|
+
let path = shapeElToPath(el, { width, height, convertShapes, matrix });
|
|
389
410
|
el.replaceWith(path)
|
|
390
|
-
|
|
391
411
|
name = 'path'
|
|
392
|
-
el = path;
|
|
393
|
-
|
|
412
|
+
el = path; // required for node
|
|
394
413
|
|
|
395
414
|
}
|
|
396
415
|
|
|
397
416
|
}
|
|
398
417
|
|
|
399
|
-
|
|
418
|
+
/**
|
|
419
|
+
* Reverse conversion:
|
|
420
|
+
* paths to shapes
|
|
421
|
+
*/
|
|
400
422
|
else if (shapeConvert === 'toShapes') {
|
|
401
423
|
let paths = svg.querySelectorAll('path')
|
|
402
424
|
paths.forEach(path => {
|
|
403
|
-
let shape = pathElToShape(path, {
|
|
425
|
+
let shape = pathElToShape(path, { convertShapes })
|
|
404
426
|
path.replaceWith(shape)
|
|
405
427
|
path = shape;
|
|
428
|
+
//name = shape.nodeName.toLowerCase()
|
|
429
|
+
//console.log('shape', shape);
|
|
406
430
|
})
|
|
407
|
-
|
|
408
431
|
}
|
|
409
432
|
|
|
410
433
|
|
|
434
|
+
/**
|
|
435
|
+
* combine styles
|
|
436
|
+
* store in node property
|
|
437
|
+
*/
|
|
438
|
+
if (mergePaths || attributesToGroup) {
|
|
439
|
+
|
|
440
|
+
let options = { allowMeta, allowAriaAtts, removeIds, removeClassNames, allowDataAtts }
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* exclude properties for
|
|
444
|
+
* adjacent path merging
|
|
445
|
+
* e.g ignore classnames or ids
|
|
446
|
+
*/
|
|
447
|
+
if (mergePaths) {
|
|
448
|
+
options.removeIds = true;
|
|
449
|
+
options.removeClassNames = true;
|
|
450
|
+
options.allowAriaAtts = false;
|
|
451
|
+
options.allowMeta = false;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
stylePropsFiltered = filterSvgElProps(name, styleProps, options).propsFiltered
|
|
455
|
+
|
|
456
|
+
for (let prop in stylePropsFiltered) {
|
|
457
|
+
|
|
458
|
+
if (geometryProps.includes(prop)) continue;
|
|
459
|
+
|
|
460
|
+
let values = stylePropsFiltered[prop]
|
|
461
|
+
let val = values.length ? values.join(' ') : values[0]
|
|
462
|
+
|
|
463
|
+
if(prop!=='class' && prop!=='id'){
|
|
464
|
+
|
|
465
|
+
let propShort = toShortStr(prop)
|
|
466
|
+
let valShort = toShortStr(val)
|
|
467
|
+
let propStr = `${propShort}-${valShort}`;
|
|
468
|
+
|
|
469
|
+
// store in node property
|
|
470
|
+
if (!el.styleSet) el.styleSet = new Set()
|
|
471
|
+
if(propStr) el.styleSet.add(propStr)
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
}
|
|
411
476
|
|
|
412
477
|
}//endof element loop
|
|
413
478
|
|
|
414
479
|
|
|
480
|
+
/**
|
|
481
|
+
* remove group styles
|
|
482
|
+
* copied to children
|
|
483
|
+
* or remove nesting
|
|
484
|
+
*/
|
|
485
|
+
|
|
486
|
+
if (unGroup) {
|
|
487
|
+
ungroupElements(groups)
|
|
488
|
+
} else {
|
|
489
|
+
|
|
490
|
+
if (stylesToAttributes) {
|
|
491
|
+
groups.forEach(g => {
|
|
492
|
+
removeElAtts(g, ['style', 'transform']);
|
|
493
|
+
})
|
|
494
|
+
}
|
|
495
|
+
//removeGroupProps(groups, { remove, allowDataAtts, allowAriaAtts })
|
|
496
|
+
}
|
|
415
497
|
|
|
416
498
|
|
|
499
|
+
// styles to group
|
|
500
|
+
if (attributesToGroup) sharedAttributesToGroup(svg);
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* merge paths with same styles
|
|
504
|
+
*/
|
|
505
|
+
if (mergePaths) {
|
|
506
|
+
mergePathsWithSameProps(svg)
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
//console.log('svg', svg);
|
|
417
510
|
|
|
418
511
|
// remove futile clip-paths
|
|
419
512
|
if (cleanupClip) removeFutileClipPaths(svg, { x, y, width, height })
|
|
420
513
|
|
|
421
|
-
|
|
422
514
|
// replace href attributes with namespace - required by many older applications
|
|
423
|
-
if (legacyHref)
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
515
|
+
if (legacyHref) hrefToXlink(svg);
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
// remove empty class attributes
|
|
519
|
+
removeEmptyClassAtts(svg);
|
|
520
|
+
return { svg, svgElProps }
|
|
521
|
+
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
function removeEmptyClassAtts(svg) {
|
|
527
|
+
let emptyClassEls = svg.querySelectorAll('[class=""');
|
|
528
|
+
emptyClassEls.forEach(el => {
|
|
529
|
+
el.removeAttribute('class')
|
|
530
|
+
})
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* shared styles to group
|
|
536
|
+
*/
|
|
537
|
+
function sharedAttributesToGroup(svg) {
|
|
538
|
+
|
|
539
|
+
let els = svg.querySelectorAll(renderedEls.join(', '))
|
|
540
|
+
let len = els.length;
|
|
541
|
+
if(len===1) return;
|
|
542
|
+
|
|
543
|
+
let el0 = els[0] || null
|
|
544
|
+
let stylePrev = el0.styleSet !== undefined ? [...el0.styleSet].join('_') : ''
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
// all props
|
|
548
|
+
let allProps = {}
|
|
549
|
+
|
|
550
|
+
// find attributes shared by all
|
|
551
|
+
let globalAtts = []
|
|
552
|
+
|
|
553
|
+
if (len) {
|
|
554
|
+
|
|
555
|
+
let groups = [[el0]];
|
|
556
|
+
let idx = 0;
|
|
557
|
+
let elPrev = el0
|
|
558
|
+
|
|
559
|
+
for (let i = 0; i < len; i++) {
|
|
560
|
+
let el = els[i];
|
|
561
|
+
let atts = getElementAtts(el)
|
|
562
|
+
for (let att in atts) {
|
|
563
|
+
let att_str = `${att}_${atts[att]}`
|
|
564
|
+
|
|
565
|
+
if (!allProps[att_str]) {
|
|
566
|
+
allProps[att_str] = []
|
|
567
|
+
}
|
|
568
|
+
allProps[att_str].push(el)
|
|
569
|
+
//
|
|
570
|
+
if (allProps[att_str].length === len) {
|
|
571
|
+
globalAtts.push(att)
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
//console.log('allProps', allProps);
|
|
577
|
+
//console.log('globalAtts', globalAtts);
|
|
578
|
+
|
|
579
|
+
// apply global to parent SVG
|
|
580
|
+
if (globalAtts.length) {
|
|
581
|
+
let atts0 = getElementAtts(el0)
|
|
582
|
+
for (let att in atts0) {
|
|
583
|
+
if (globalAtts.includes(att) && att !== 'transform') {
|
|
584
|
+
svg.setAttribute(att, atts0[att])
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// detect groups
|
|
590
|
+
for (let i = 1; i < len; i++) {
|
|
591
|
+
let el = els[i];
|
|
592
|
+
let styleArr = el.styleSet !== undefined ? [...el.styleSet] : [];
|
|
593
|
+
let style = styleArr.length ? styleArr.join('_') : '';
|
|
594
|
+
//console.log('style', style);
|
|
595
|
+
//console.log('style === stylePrev', style, stylePrev);
|
|
596
|
+
|
|
597
|
+
// same style add to group
|
|
598
|
+
if (style === stylePrev && elPrev.nextElementSibling === el) {
|
|
599
|
+
groups[idx].push(el)
|
|
600
|
+
}
|
|
601
|
+
// start new group
|
|
602
|
+
else {
|
|
603
|
+
groups.push([el])
|
|
604
|
+
idx++
|
|
605
|
+
}
|
|
606
|
+
// update style
|
|
607
|
+
stylePrev = style
|
|
608
|
+
elPrev = el
|
|
609
|
+
|
|
610
|
+
}// endof el loop
|
|
611
|
+
|
|
612
|
+
//console.log('g', groups);
|
|
613
|
+
|
|
614
|
+
// create groups
|
|
615
|
+
for (let i = 0; i < groups.length; i++) {
|
|
616
|
+
let children = groups[i];
|
|
617
|
+
let child0 = children[0]
|
|
618
|
+
let atts = getElementAtts(child0)
|
|
619
|
+
let groupEl = child0.parentNode.closest('g')
|
|
620
|
+
|
|
621
|
+
// only 1 child - nothing to group
|
|
622
|
+
if (children.length === 1) continue
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
// create new group
|
|
626
|
+
if (!groupEl || groups.length>1) {
|
|
627
|
+
//console.log('new group');
|
|
628
|
+
groupEl = document.createElementNS(svgNs, 'g')
|
|
629
|
+
child0.parentNode.insertBefore(groupEl, child0)
|
|
630
|
+
groupEl.append(...children)
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// move attributes to group
|
|
634
|
+
for (let att in atts) {
|
|
635
|
+
let val = atts[att];
|
|
636
|
+
//console.log('att', atts, val);
|
|
637
|
+
|
|
638
|
+
//|| att === 'transform'
|
|
639
|
+
let excludeAtts = ['id', 'class'];
|
|
640
|
+
if (!geometryProps.includes(att) && !excludeAtts.includes(att)) {
|
|
641
|
+
if (!globalAtts.includes(att) || att === 'transform') {
|
|
642
|
+
groupEl.setAttribute(att, val)
|
|
643
|
+
}
|
|
644
|
+
children.forEach(child => {
|
|
645
|
+
child.removeAttribute(att)
|
|
646
|
+
})
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
} // endof groups
|
|
652
|
+
|
|
431
653
|
}
|
|
654
|
+
}
|
|
432
655
|
|
|
433
656
|
|
|
657
|
+
// merge adjacent paths
|
|
658
|
+
function mergePathsWithSameProps(svg) {
|
|
659
|
+
let paths = svg.querySelectorAll('path')
|
|
660
|
+
let len = paths.length;
|
|
434
661
|
|
|
435
|
-
|
|
662
|
+
if (len) {
|
|
663
|
+
let path0 = paths[0]
|
|
664
|
+
let d0 = path0.getAttribute('d')
|
|
665
|
+
let stylePrev = path0.styleSet !== undefined ? [...path0.styleSet].join(' ') : ''
|
|
666
|
+
//console.log('path0', path0);
|
|
667
|
+
|
|
668
|
+
let remove = []
|
|
669
|
+
|
|
670
|
+
for (let i = 1; i < len; i++) {
|
|
671
|
+
let path = paths[i];
|
|
672
|
+
let style = path.styleSet !== undefined ? [...path.styleSet].join(' ') : ''
|
|
673
|
+
let isSibling = path.previousElementSibling === path0;
|
|
674
|
+
let d = path.getAttribute('d');
|
|
675
|
+
let isAbs = d.startsWith('M')
|
|
676
|
+
//console.log('path.previousElementSibling', path.previousElementSibling);
|
|
677
|
+
//console.log(isSibling, style, stylePrev, path.id);
|
|
678
|
+
|
|
679
|
+
if (isSibling && style === stylePrev) {
|
|
680
|
+
let dAbs = isAbs ? d : parsePathDataString(d).pathData.map(com => `${com.type} ${com.values.join(' ')}`).join(' ')
|
|
681
|
+
//console.log('same style', dAbs, isAbs);
|
|
682
|
+
d0 += dAbs;
|
|
683
|
+
path0.setAttribute('d', d0)
|
|
684
|
+
//console.log('remove', path);
|
|
685
|
+
remove.push(path)
|
|
686
|
+
//path.remove();
|
|
687
|
+
|
|
688
|
+
} else {
|
|
689
|
+
path0 = path
|
|
690
|
+
//console.log('path0', path0, path);
|
|
691
|
+
d0 = isAbs ? d : parsePathDataString(d).pathData.map(com => `${com.type} ${com.values.join(' ')}`).join(' ')
|
|
692
|
+
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// update style
|
|
696
|
+
stylePrev = style
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
//console.log('remove', remove);
|
|
701
|
+
remove.forEach(el => {
|
|
702
|
+
el.remove()
|
|
703
|
+
})
|
|
704
|
+
|
|
705
|
+
}
|
|
436
706
|
|
|
437
707
|
}
|
|
438
708
|
|
|
439
709
|
|
|
710
|
+
|
|
711
|
+
|
|
440
712
|
function removeOffCanvasEls(svg, { x = 0, y = 0, width = 0, height = 0 } = {}) {
|
|
441
713
|
let els = [...svg.querySelectorAll('path, polygon, polyline, line, rect, circle, ellipse, text')];
|
|
442
714
|
els = els.filter(el => !el.parentNode.closest('defs') && !el.parentNode.closest('symbol') && !el.parentNode.closest('clipPath') && !el.parentNode.closest('mask') && !el.parentNode.closest('pattern'))
|
|
@@ -462,6 +734,13 @@ function addSvgViewBox(svg, { x = 0, y = 0, width = 0, height = 0 } = {}) {
|
|
|
462
734
|
svg.setAttribute('viewBox', [x, y, width, height].join(' '))
|
|
463
735
|
}
|
|
464
736
|
|
|
737
|
+
export function removeEmptySVGEls(svg) {
|
|
738
|
+
let els = svg.querySelectorAll('g, defs');
|
|
739
|
+
els.forEach(el => {
|
|
740
|
+
if (!el.children.length) el.remove()
|
|
741
|
+
})
|
|
742
|
+
}
|
|
743
|
+
|
|
465
744
|
|
|
466
745
|
function cleanupSvgDefs(svg, { x = 0, y = 0, width = 0, height = 0, cleanupClip = true } = {}) {
|
|
467
746
|
let defs = svg.querySelectorAll('defs')
|
|
@@ -498,9 +777,6 @@ function cleanupSvgDefs(svg, { x = 0, y = 0, width = 0, height = 0, cleanupClip
|
|
|
498
777
|
}
|
|
499
778
|
})
|
|
500
779
|
|
|
501
|
-
// remove futile clip-paths
|
|
502
|
-
//if (cleanupClip) removeFutileClipPaths(svg, {x, y, width, height})
|
|
503
|
-
|
|
504
780
|
}
|
|
505
781
|
|
|
506
782
|
|
|
@@ -554,253 +830,23 @@ function removeFutileClipPaths(svg, { x = 0, y = 0, width = 0, height = 0 } = {}
|
|
|
554
830
|
}
|
|
555
831
|
|
|
556
832
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
let itemsWithProps = svgElProps.filter(item => item.propstr)
|
|
565
|
-
let path0;
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
// merge paths without properties
|
|
569
|
-
let dCombined = ''
|
|
570
|
-
if (!itemsWithProps.length && mergePaths) {
|
|
571
|
-
let path0 = null;
|
|
572
|
-
|
|
573
|
-
for (let i = 0; i < l; i++) {
|
|
574
|
-
let item = svgElProps[i]
|
|
575
|
-
if (item.name !== 'path') continue;
|
|
576
|
-
let remove = true;
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
let path = item.el;
|
|
580
|
-
|
|
581
|
-
// set 1st path
|
|
582
|
-
if (!path0) {
|
|
583
|
-
path0 = path;
|
|
584
|
-
remove = false;
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
let d = item.propsFiltered.d
|
|
588
|
-
let isAbs = d.startsWith('M')
|
|
589
|
-
let dAbs = isAbs ? d : parsePathDataString(d).pathData.map(com => `${com.type} ${com.values.join(' ')}`).join(' ')
|
|
590
|
-
|
|
591
|
-
dCombined += dAbs;
|
|
592
|
-
|
|
593
|
-
// delete path el
|
|
594
|
-
if (remove) path.remove();
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
//console.log('dCombined', dCombined);
|
|
598
|
-
if (path0) path0.setAttribute('d', dCombined)
|
|
599
|
-
return
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
// add to combine chunks
|
|
604
|
-
for (let i = 0; i < l; i++) {
|
|
605
|
-
let item = svgElProps[i];
|
|
606
|
-
let props = item.propsFiltered;
|
|
607
|
-
let propstr = [];
|
|
608
|
-
for (let prop in props) {
|
|
609
|
-
if (prop !== 'd' && prop !== 'id') {
|
|
610
|
-
propstr.push(`${prop}:${props[prop]}`)
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
propstr = propstr.join('_')
|
|
614
|
-
item.propstr = propstr;
|
|
615
|
-
|
|
616
|
-
if (l > 1 && propstr === lastProps) {
|
|
617
|
-
combine[idx].push(item)
|
|
618
|
-
} else {
|
|
619
|
-
if (l > 1 && combine[idx].length) {
|
|
620
|
-
combine.push([])
|
|
621
|
-
idx++
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
lastProps = propstr;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
// add att groups
|
|
629
|
-
for (let i = 0; i < combine.length; i++) {
|
|
630
|
-
let group = combine[i]
|
|
631
|
-
|
|
632
|
-
if (group.length > 1) {
|
|
633
|
-
// 1st el
|
|
634
|
-
let el0 = group[0].el;
|
|
635
|
-
let props = group[0].propsFiltered;
|
|
636
|
-
let g = el0.parentNode.closest('g') ? el0.parentNode.closest('g') : null;
|
|
637
|
-
|
|
638
|
-
// wrap in group if not existent
|
|
639
|
-
if (!g) {
|
|
640
|
-
g = document.createElementNS(svgNs, 'g');
|
|
641
|
-
el0.parentNode.insertBefore(g, el0)
|
|
642
|
-
group.forEach(item => {
|
|
643
|
-
g.append(item.el)
|
|
644
|
-
})
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
let children = [...g.children]
|
|
648
|
-
for (let prop in props) {
|
|
649
|
-
if (prop !== 'd' && prop !== 'id') {
|
|
650
|
-
let value = props[prop]
|
|
651
|
-
// apply to parent group
|
|
652
|
-
g.setAttribute(prop, value)
|
|
653
|
-
|
|
654
|
-
// remove from children
|
|
655
|
-
children.forEach(el => {
|
|
656
|
-
if (el.getAttribute(prop) === value) {
|
|
657
|
-
el.removeAttribute(prop)
|
|
658
|
-
}
|
|
659
|
-
})
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
if (mergePaths) {
|
|
664
|
-
group = group.filter(Boolean)
|
|
665
|
-
let l = group.length
|
|
666
|
-
// nothing to merge
|
|
667
|
-
if (l === 1) return group[0].el;
|
|
668
|
-
|
|
669
|
-
path0 = group[0].el;
|
|
670
|
-
let dCombined = group[0].propsFiltered.d;
|
|
671
|
-
|
|
672
|
-
for (let i = 1; i < l; i++) {
|
|
673
|
-
let item = group[i]
|
|
674
|
-
let path = item.el;
|
|
675
|
-
let d = item.propsFiltered.d
|
|
676
|
-
let isAbs = d.startsWith('M')
|
|
677
|
-
|
|
678
|
-
let dAbs = isAbs ? d : parsePathDataString(d).pathData.map(com => `${com.type} ${com.values.join(' ')}`).join(' ')
|
|
679
|
-
|
|
680
|
-
console.log('dAbs', dAbs);
|
|
681
|
-
|
|
682
|
-
//console.log(isAbs, dAbs);
|
|
683
|
-
// concat pathdata string
|
|
684
|
-
dCombined += dAbs;
|
|
685
|
-
|
|
686
|
-
// delete path el
|
|
687
|
-
path.remove();
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
path0.setAttribute('d', dCombined)
|
|
691
|
-
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
export function scaleProps(styleProps = {}, { props = [], scale = 1 } = {}) {
|
|
702
|
-
if (scale === 1 || !props.length) return props;
|
|
703
|
-
|
|
704
|
-
for (let i = 0; i < props.length; i++) {
|
|
705
|
-
let prop = props[i];
|
|
706
|
-
|
|
707
|
-
if (styleProps[prop] !== undefined) {
|
|
708
|
-
styleProps[prop] = styleProps[prop].map(val => val * scale)
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
return styleProps
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
export function removeSVGEls(svg, {
|
|
715
|
-
remove = ['metadata', 'script'],
|
|
716
|
-
removeNameSpaced = true,
|
|
717
|
-
} = {}) {
|
|
718
|
-
let els = svg.querySelectorAll('*')
|
|
719
|
-
els.forEach(el => {
|
|
720
|
-
let nodeName = el.nodeName;
|
|
721
|
-
if ((removeNameSpaced && nodeName.includes(':')) ||
|
|
722
|
-
remove.includes(nodeName)
|
|
723
|
-
) {
|
|
724
|
-
el.remove()
|
|
725
|
-
}
|
|
833
|
+
function hrefToXlink(svg) {
|
|
834
|
+
svg.setAttribute('xmlns:xlink', "http://www.w3.org/1999/xlink")
|
|
835
|
+
let hrefs = svg.querySelectorAll('[href]')
|
|
836
|
+
hrefs.forEach(el => {
|
|
837
|
+
let href = el.getAttribute('href')
|
|
838
|
+
el.setAttribute('xlink:href', href)
|
|
839
|
+
//el.removeAttribute('href')
|
|
726
840
|
})
|
|
727
841
|
}
|
|
728
842
|
|
|
729
843
|
|
|
730
|
-
function cleanSvgPrologue(svgString) {
|
|
731
|
-
return (
|
|
732
|
-
svgString
|
|
733
|
-
// Remove XML prologues like <?xml ... ?>
|
|
734
|
-
.replace(/<\?xml[\s\S]*?\?>/gi, "")
|
|
735
|
-
// Remove DOCTYPE declarations
|
|
736
|
-
.replace(/<!DOCTYPE[\s\S]*?>/gi, "")
|
|
737
|
-
// Remove comments <!-- ... -->
|
|
738
|
-
.replace(/<!--[\s\S]*?-->/g, "")
|
|
739
|
-
// Trim extra whitespace
|
|
740
|
-
.trim()
|
|
741
|
-
);
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
function removeExcludedAttribues(el, allowed = ['viewBox', 'xmlns', 'width', 'height', 'id', 'class']) {
|
|
745
|
-
let atts = [...el.attributes].map((att) => att.name);
|
|
746
|
-
atts.forEach((att) => {
|
|
747
|
-
if (!allowed.includes(att)) {
|
|
748
|
-
el.removeAttribute(att);
|
|
749
|
-
}
|
|
750
|
-
});
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
function removeAtts(el, exclude = [], include = []) {
|
|
755
|
-
let atts = [...el.attributes].map((att) => att.name);
|
|
756
|
-
atts.forEach((att) => {
|
|
757
|
-
if (exclude.includes(att) && !include.includes(att)) {
|
|
758
|
-
el.removeAttribute(att);
|
|
759
|
-
}
|
|
760
|
-
});
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
function removeNameSpaceAtts(el, {
|
|
765
|
-
include = ['xlink:href']
|
|
766
|
-
} = {}) {
|
|
767
|
-
let atts = [...el.attributes].map((att) => att.name);
|
|
768
|
-
atts.forEach((att) => {
|
|
769
|
-
if (att.includes(":") && !include.includes(att)) {
|
|
770
|
-
el.removeAttribute(att);
|
|
771
|
-
}
|
|
772
|
-
});
|
|
773
|
-
}
|
|
774
844
|
|
|
775
|
-
export function stringifySVG(svg, {
|
|
776
|
-
omitNamespace = false,
|
|
777
|
-
removeComments = true,
|
|
778
|
-
} = {}) {
|
|
779
|
-
let markup = new XMLSerializer().serializeToString(svg);
|
|
780
845
|
|
|
781
|
-
if (omitNamespace) {
|
|
782
|
-
markup = markup.replaceAll('xmlns="http://www.w3.org/2000/svg"', '')
|
|
783
|
-
}
|
|
784
846
|
|
|
785
|
-
if (removeComments) {
|
|
786
|
-
markup = markup
|
|
787
|
-
.replace(/(<!--.*?-->)|(<!--[\S\s]+?-->)|(<!--[\S\s]*?$)/g, '')
|
|
788
|
-
}
|
|
789
847
|
|
|
790
|
-
markup = markup
|
|
791
|
-
.replace(/\t/g, "")
|
|
792
|
-
.replace(/[\n\r|]/g, "\n")
|
|
793
|
-
.replace(/\n\s*\n/g, '\n')
|
|
794
|
-
.replace(/ +/g, ' ')
|
|
795
|
-
//.replace(/ +/g, ' ')
|
|
796
|
-
.replace(/> </g, '><')
|
|
797
|
-
.trim()
|
|
798
|
-
// sanitize linebreaks within pathdata
|
|
799
|
-
.replaceAll(' ', '\n');
|
|
800
848
|
|
|
801
849
|
|
|
802
850
|
|
|
803
851
|
|
|
804
852
|
|
|
805
|
-
return markup
|
|
806
|
-
}
|