svg-path-simplify 0.4.2 → 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 +10 -0
- package/README.md +6 -4
- package/dist/svg-path-simplify.esm.js +2139 -940
- package/dist/svg-path-simplify.esm.min.js +2 -2
- package/dist/svg-path-simplify.js +2139 -940
- package/dist/svg-path-simplify.min.js +2 -2
- package/dist/svg-path-simplify.pathdata.esm.js +127 -85
- package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
- package/docs/privacy-webapp.md +24 -0
- package/index.html +290 -152
- package/package.json +1 -1
- 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 +242 -269
- 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 +130 -4
- package/src/svgii/geometry.js +182 -3
- package/src/svgii/geometry_length.js +237 -0
- package/src/svgii/pathData_convert.js +5 -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 +66 -68
- 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_cleanup.js +374 -620
- 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 +65 -43
- package/src/svgii/svg_getElementLength.js +67 -0
package/src/svgii/svg_cleanup.js
CHANGED
|
@@ -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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
//const DOMParserPoly = globalThis.DOMParser;
|
|
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";
|
|
29
26
|
|
|
30
27
|
|
|
31
28
|
export function cleanUpSVG(svgMarkup, {
|
|
@@ -56,6 +53,8 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
56
53
|
|
|
57
54
|
cleanupSVGAtts = true,
|
|
58
55
|
removeNameSpaced = true,
|
|
56
|
+
removeNameSpacedAtts = true,
|
|
57
|
+
convertPathLength = false,
|
|
59
58
|
|
|
60
59
|
// meta
|
|
61
60
|
allowMeta = false,
|
|
@@ -64,10 +63,14 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
64
63
|
|
|
65
64
|
//shapesToPaths = false,
|
|
66
65
|
shapeConvert = false,
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
convertShapes = [],
|
|
67
|
+
|
|
68
|
+
// remove elements
|
|
69
|
+
removeElements = [],
|
|
70
|
+
|
|
71
|
+
// remove attributes
|
|
72
|
+
removeSVGAttributes = [],
|
|
73
|
+
removeElAttributes = [],
|
|
71
74
|
|
|
72
75
|
convertTransforms = false,
|
|
73
76
|
removeDefaults = true,
|
|
@@ -76,8 +79,13 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
76
79
|
excludedEls = [],
|
|
77
80
|
} = {}) {
|
|
78
81
|
|
|
79
|
-
|
|
80
|
-
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
// resolve dependencies
|
|
85
|
+
if (unGroup || convertTransforms || minifyRgbColors || attributesToGroup)
|
|
86
|
+
stylesToAttributes = true;
|
|
87
|
+
|
|
88
|
+
if(stylesToAttributes) cleanUpStrokes = true;
|
|
81
89
|
|
|
82
90
|
// replace namespaced refs
|
|
83
91
|
if (fixHref) svgMarkup = svgMarkup.replaceAll("xlink:href=", "href=");
|
|
@@ -87,17 +95,39 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
87
95
|
.parseFromString(svgMarkup, "text/html")
|
|
88
96
|
.querySelector("svg");
|
|
89
97
|
|
|
98
|
+
|
|
90
99
|
let viewBox = getViewBox(svg)
|
|
91
100
|
let { x, y, width, height } = viewBox;
|
|
101
|
+
let remove = []
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
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 });
|
|
92
114
|
|
|
93
115
|
|
|
94
|
-
//
|
|
116
|
+
// remove off canvas
|
|
117
|
+
if (removeOffCanvas) removeOffCanvasEls(svg, { x, y, width, height });
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* collect svg styles
|
|
122
|
+
* and properties
|
|
123
|
+
*/
|
|
95
124
|
let propOptions = {
|
|
96
|
-
width
|
|
97
|
-
height
|
|
125
|
+
width,
|
|
126
|
+
height,
|
|
98
127
|
normalizeTransforms,
|
|
99
128
|
removeDefaults: false,
|
|
100
129
|
cleanUpStrokes: false,
|
|
130
|
+
//cleanUpStrokes,
|
|
101
131
|
allowMeta,
|
|
102
132
|
allowDataAtts,
|
|
103
133
|
allowAriaAtts,
|
|
@@ -105,18 +135,54 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
105
135
|
removeIds,
|
|
106
136
|
removeClassNames,
|
|
107
137
|
minifyRgbColors,
|
|
138
|
+
stylesheetProps: {},
|
|
139
|
+
exclude:[]
|
|
108
140
|
}
|
|
109
141
|
|
|
110
|
-
// root svg properties
|
|
142
|
+
// root svg inline style properties
|
|
111
143
|
let stylePropsSVG = parseStylesProperties(svg, propOptions)
|
|
112
144
|
//console.log('stylePropsSVG', stylePropsSVG);
|
|
113
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
|
+
|
|
114
181
|
|
|
115
182
|
// add svg font size for scaling relative
|
|
116
183
|
propOptions.fontSize = stylePropsSVG['font-size'] ? stylePropsSVG['font-size'][0] : 16;
|
|
117
184
|
|
|
118
185
|
|
|
119
|
-
|
|
120
186
|
/**
|
|
121
187
|
* get group styles
|
|
122
188
|
* especially transformations to
|
|
@@ -126,7 +192,10 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
126
192
|
let groupProps = [];
|
|
127
193
|
|
|
128
194
|
groups.forEach(g => {
|
|
195
|
+
//propOptions.exclude.push('class', 'id');
|
|
129
196
|
let stylePropsG = parseStylesProperties(g, propOptions)
|
|
197
|
+
//console.log('stylePropsG', stylePropsG);
|
|
198
|
+
|
|
130
199
|
groupProps.push(stylePropsG);
|
|
131
200
|
let children = g.querySelectorAll(`${renderedEls.join(', ')}`)
|
|
132
201
|
|
|
@@ -141,85 +210,36 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
141
210
|
|
|
142
211
|
|
|
143
212
|
|
|
144
|
-
|
|
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
|
|
213
|
+
// collect all elements' properties
|
|
196
214
|
let svgElProps = []
|
|
197
215
|
let els = svg.querySelectorAll(`${renderedEls.join(', ')}`)
|
|
198
216
|
|
|
199
217
|
|
|
218
|
+
/**
|
|
219
|
+
* loop all geometry elements
|
|
220
|
+
*/
|
|
200
221
|
for (let i = 0; i < els.length; i++) {
|
|
201
222
|
let el = els[i];
|
|
202
223
|
|
|
203
224
|
let name = el.nodeName.toLowerCase();
|
|
204
225
|
//console.log(name);
|
|
205
226
|
|
|
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
227
|
/**
|
|
217
|
-
* get all style properties
|
|
228
|
+
* get all element style properties
|
|
218
229
|
* convert relative or physical units
|
|
219
230
|
* to user units
|
|
220
231
|
*/
|
|
221
232
|
let styleProps = parseStylesProperties(el, propOptions)
|
|
222
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
|
+
|
|
223
243
|
|
|
224
244
|
// get parent styles
|
|
225
245
|
let { parentStyleProps = [] } = el;
|
|
@@ -227,7 +247,10 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
227
247
|
let transFormInherited = []
|
|
228
248
|
|
|
229
249
|
|
|
230
|
-
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* consolidate all properties:
|
|
253
|
+
* merge with inherited transforms
|
|
231
254
|
* and styles from group
|
|
232
255
|
*/
|
|
233
256
|
parentStyleProps.forEach(props => {
|
|
@@ -243,13 +266,16 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
243
266
|
})
|
|
244
267
|
|
|
245
268
|
|
|
246
|
-
//merge transforms
|
|
269
|
+
// merge all transforms
|
|
247
270
|
transFormInherited = [...transFormInherited, ...styleProps.transformArr]
|
|
248
271
|
styleProps.transformArr = transFormInherited
|
|
249
272
|
|
|
250
273
|
|
|
251
|
-
//
|
|
274
|
+
// don't inherit class from SVG
|
|
275
|
+
if (stylePropsSVG['class']) delete stylePropsSVG['class']
|
|
276
|
+
if (stylePropsSVG['id']) delete stylePropsSVG['id']
|
|
252
277
|
|
|
278
|
+
// merge with svg props
|
|
253
279
|
styleProps = {
|
|
254
280
|
...stylePropsSVG,
|
|
255
281
|
...inheritedProps,
|
|
@@ -257,164 +283,110 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
257
283
|
}
|
|
258
284
|
|
|
259
285
|
|
|
260
|
-
// dont inherit class
|
|
261
|
-
if (stylePropsSVG['class'] === styleProps['class']) {
|
|
262
|
-
delete styleProps['class']
|
|
263
|
-
}
|
|
264
|
-
|
|
265
286
|
// add combined transforms
|
|
266
287
|
addTransFormProps(styleProps, transFormInherited);
|
|
267
288
|
//console.log('transFormInherited', transFormInherited);
|
|
268
289
|
//console.log('styleProps', styleProps);
|
|
269
290
|
|
|
291
|
+
remove = [...new Set([...remove, ...styleProps.remove])];
|
|
270
292
|
|
|
271
|
-
let { remove, matrix, transComponents } = styleProps;
|
|
272
293
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if (stylesToAttributes) {
|
|
294
|
+
/**
|
|
295
|
+
* remove els and attributes
|
|
296
|
+
*/
|
|
277
297
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
*/
|
|
281
|
-
if (normalizeTransforms && matrix) {
|
|
282
|
-
let { rotate, scaleX, scaleY, skewX, translateX, translateY } = transComponents;
|
|
283
|
-
//console.log(rotate, scaleX, scaleY, skewX, skewY, translateX, translateY);
|
|
298
|
+
// remove meta
|
|
299
|
+
if (!allowMeta) removeElements.push('meta', 'metadata', 'desc', 'title')
|
|
284
300
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
let needsTrans = convertTransforms || (name === 'g') || (hasRot) || unProportional
|
|
301
|
+
if (removeClassNames) {
|
|
302
|
+
removeSVGAttributes.push('class');
|
|
303
|
+
removeElAttributes.push('class');
|
|
304
|
+
}
|
|
290
305
|
|
|
291
|
-
|
|
306
|
+
if (removeIds) {
|
|
307
|
+
removeSVGAttributes.push('id')
|
|
308
|
+
removeElAttributes.push('id')
|
|
309
|
+
}
|
|
292
310
|
|
|
293
|
-
if (name === 'circle' || name === 'ellipse') {
|
|
294
|
-
styleProps.cx[0] = [styleProps.cx[0] * scaleX + translateX]
|
|
295
|
-
styleProps.cy[0] = [styleProps.cy[0] * scaleX + translateY]
|
|
296
311
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if (styleProps.ry) styleProps.ry[0] = [styleProps.ry[0] * scaleX]
|
|
312
|
+
// remove hidden elements
|
|
313
|
+
removeHiddenSvgEls(svg)
|
|
300
314
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
let x = styleProps.x ? styleProps.x[0] + translateX : translateX;
|
|
304
|
-
let y = styleProps.y ? styleProps.y[0] + translateY : translateY;
|
|
315
|
+
// remove SVG elements
|
|
316
|
+
removeSvgEls(svg, { removeElements, removeNameSpaced });
|
|
305
317
|
|
|
306
|
-
|
|
307
|
-
|
|
318
|
+
// remove SVG attributes
|
|
319
|
+
removeSvgAtts(svg, removeSVGAttributes);
|
|
308
320
|
|
|
309
|
-
|
|
310
|
-
|
|
321
|
+
// remove SVG child element attributes
|
|
322
|
+
removeSvgChildAtts(svg, removeElAttributes);
|
|
311
323
|
|
|
312
|
-
styleProps.rx = [rx]
|
|
313
|
-
styleProps.ry = [ry]
|
|
314
324
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
325
|
+
// general cleanup
|
|
326
|
+
if (cleanupSVGAtts) cleanupSVGAttributes(svg, { removeIds, removeClassNames, removeDimensions, stylesToAttributes, allowMeta, allowAriaAtts, allowDataAtts });
|
|
318
327
|
|
|
319
|
-
// remove now obsolete transform properties
|
|
320
|
-
delete styleProps.matrix
|
|
321
|
-
delete styleProps.transformArr
|
|
322
|
-
delete styleProps.transComponents
|
|
323
328
|
|
|
324
|
-
// mark transform attribute for removal
|
|
325
|
-
remove.push('transform')
|
|
326
329
|
|
|
327
|
-
|
|
328
|
-
styleProps = scaleProps(styleProps, { props: ['stroke-width', 'stroke-dasharray'], scale: scaleX })
|
|
330
|
+
if (stylesToAttributes) {
|
|
329
331
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
+
/**
|
|
333
|
+
* normalize transforms
|
|
334
|
+
*/
|
|
335
|
+
if (normalizeTransforms) {
|
|
336
|
+
styleProps = setNormalizedTransformsToEl(el, { styleProps });
|
|
337
|
+
//remove = styleProps.remove;
|
|
338
|
+
remove = [...new Set([...remove, ...styleProps.remove])];
|
|
332
339
|
|
|
333
|
-
}
|
|
334
340
|
}
|
|
335
341
|
|
|
336
|
-
|
|
337
342
|
/**
|
|
338
343
|
* apply consolidated
|
|
339
344
|
* element attributes
|
|
345
|
+
* remove non-supported element props
|
|
340
346
|
*/
|
|
341
347
|
stylePropsFiltered = filterSvgElProps(name, styleProps,
|
|
342
348
|
{ removeDefaults: true, cleanUpStrokes, allowMeta, allowAriaAtts, allowDataAtts, removeIds });
|
|
343
349
|
|
|
344
|
-
remove = [...remove, ...stylePropsFiltered.remove];
|
|
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);
|
|
345
354
|
|
|
346
355
|
for (let prop in stylePropsFiltered.propsFiltered) {
|
|
347
356
|
let values = styleProps[prop]
|
|
348
357
|
let val = values.length ? values.join(' ') : values[0]
|
|
349
358
|
el.setAttribute(prop, val)
|
|
350
|
-
|
|
351
359
|
}
|
|
352
360
|
|
|
353
361
|
|
|
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
362
|
/**
|
|
366
|
-
* remove
|
|
367
|
-
*
|
|
368
|
-
* or remove nesting
|
|
363
|
+
* remove obsolete
|
|
364
|
+
* attributes
|
|
369
365
|
*/
|
|
366
|
+
//removeSvgChildAtts(svg, remove)
|
|
370
367
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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
|
-
|
|
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)
|
|
398
373
|
}
|
|
399
374
|
|
|
400
375
|
|
|
401
376
|
} // endof style processing
|
|
402
377
|
|
|
403
378
|
|
|
379
|
+
|
|
404
380
|
/**
|
|
405
381
|
* element conversions:
|
|
406
382
|
* shapes to paths or
|
|
407
383
|
* paths to shapes
|
|
408
384
|
*/
|
|
409
385
|
|
|
410
|
-
|
|
411
386
|
// force shape conversion when transform conversion is enabled
|
|
412
387
|
if (convertTransforms) {
|
|
413
388
|
shapeConvert = 'toPaths';
|
|
414
|
-
|
|
415
|
-
convert_ellipses = true;
|
|
416
|
-
convert_poly = true;
|
|
417
|
-
convert_lines = true;
|
|
389
|
+
convertShapes = ['path', 'rect', 'ellipse', 'circle', 'line', 'polygon', 'polyline'];
|
|
418
390
|
}
|
|
419
391
|
|
|
420
392
|
// convert shapes to paths
|
|
@@ -422,8 +394,8 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
422
394
|
|
|
423
395
|
let { matrix = null, transComponents = null } = styleProps;
|
|
424
396
|
|
|
397
|
+
// scale props like stroke width or dash-array before conversion
|
|
425
398
|
if (matrix && transComponents) {
|
|
426
|
-
// scale props like stroke width or dash-array before conversion
|
|
427
399
|
['stroke-width', 'stroke-dasharray'].forEach(att => {
|
|
428
400
|
let attVal = el.getAttribute(att)
|
|
429
401
|
let vals = attVal ? attVal.split(' ').filter(Boolean).map(Number).map(val => val * transComponents.scaleX) : []
|
|
@@ -433,10 +405,9 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
433
405
|
|
|
434
406
|
// convert paths only if a matrix transform is required
|
|
435
407
|
if (matrix ? geometryEls.includes(name) : shapeEls.includes(name)) {
|
|
436
|
-
|
|
437
|
-
let path = shapeElToPath(el, { width, height,
|
|
408
|
+
//console.log('detrans', name, el.id, matrix);
|
|
409
|
+
let path = shapeElToPath(el, { width, height, convertShapes, matrix });
|
|
438
410
|
el.replaceWith(path)
|
|
439
|
-
|
|
440
411
|
name = 'path'
|
|
441
412
|
el = path; // required for node
|
|
442
413
|
|
|
@@ -444,15 +415,19 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
444
415
|
|
|
445
416
|
}
|
|
446
417
|
|
|
447
|
-
|
|
418
|
+
/**
|
|
419
|
+
* Reverse conversion:
|
|
420
|
+
* paths to shapes
|
|
421
|
+
*/
|
|
448
422
|
else if (shapeConvert === 'toShapes') {
|
|
449
423
|
let paths = svg.querySelectorAll('path')
|
|
450
424
|
paths.forEach(path => {
|
|
451
|
-
let shape = pathElToShape(path, {
|
|
452
|
-
|
|
425
|
+
let shape = pathElToShape(path, { convertShapes })
|
|
426
|
+
path.replaceWith(shape)
|
|
453
427
|
path = shape;
|
|
428
|
+
//name = shape.nodeName.toLowerCase()
|
|
429
|
+
//console.log('shape', shape);
|
|
454
430
|
})
|
|
455
|
-
|
|
456
431
|
}
|
|
457
432
|
|
|
458
433
|
|
|
@@ -460,7 +435,6 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
460
435
|
* combine styles
|
|
461
436
|
* store in node property
|
|
462
437
|
*/
|
|
463
|
-
|
|
464
438
|
if (mergePaths || attributesToGroup) {
|
|
465
439
|
|
|
466
440
|
let options = { allowMeta, allowAriaAtts, removeIds, removeClassNames, allowDataAtts }
|
|
@@ -470,7 +444,7 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
470
444
|
* adjacent path merging
|
|
471
445
|
* e.g ignore classnames or ids
|
|
472
446
|
*/
|
|
473
|
-
if(mergePaths){
|
|
447
|
+
if (mergePaths) {
|
|
474
448
|
options.removeIds = true;
|
|
475
449
|
options.removeClassNames = true;
|
|
476
450
|
options.allowAriaAtts = false;
|
|
@@ -486,226 +460,253 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
486
460
|
let values = stylePropsFiltered[prop]
|
|
487
461
|
let val = values.length ? values.join(' ') : values[0]
|
|
488
462
|
|
|
489
|
-
|
|
490
|
-
let valShort = toShortStr(val)
|
|
491
|
-
let propStr = `${propShort}-${valShort}`;
|
|
463
|
+
if(prop!=='class' && prop!=='id'){
|
|
492
464
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
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
|
+
}
|
|
496
473
|
}
|
|
497
474
|
|
|
498
475
|
}
|
|
499
476
|
|
|
500
|
-
|
|
501
477
|
}//endof element loop
|
|
502
478
|
|
|
503
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
|
+
}
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
// styles to group
|
|
500
|
+
if (attributesToGroup) sharedAttributesToGroup(svg);
|
|
504
501
|
|
|
505
502
|
/**
|
|
506
503
|
* merge paths with same styles
|
|
507
504
|
*/
|
|
508
505
|
if (mergePaths) {
|
|
509
|
-
|
|
510
|
-
|
|
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
|
-
}
|
|
506
|
+
mergePathsWithSameProps(svg)
|
|
507
|
+
}
|
|
535
508
|
|
|
536
|
-
|
|
537
|
-
stylePrev = style
|
|
538
|
-
}
|
|
539
|
-
}
|
|
509
|
+
//console.log('svg', svg);
|
|
540
510
|
|
|
511
|
+
// remove futile clip-paths
|
|
512
|
+
if (cleanupClip) removeFutileClipPaths(svg, { x, y, width, height })
|
|
541
513
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
*/
|
|
514
|
+
// replace href attributes with namespace - required by many older applications
|
|
515
|
+
if (legacyHref) hrefToXlink(svg);
|
|
545
516
|
|
|
546
|
-
if (attributesToGroup) {
|
|
547
|
-
let els = svg.querySelectorAll(geometryEls.join(', '))
|
|
548
|
-
let len = els.length;
|
|
549
517
|
|
|
550
|
-
|
|
551
|
-
|
|
518
|
+
// remove empty class attributes
|
|
519
|
+
removeEmptyClassAtts(svg);
|
|
520
|
+
return { svg, svgElProps }
|
|
552
521
|
|
|
522
|
+
}
|
|
553
523
|
|
|
554
|
-
// all props
|
|
555
|
-
let allProps = {}
|
|
556
524
|
|
|
557
|
-
// find attributes shared by all
|
|
558
|
-
let globalAtts = []
|
|
559
525
|
|
|
560
|
-
|
|
526
|
+
function removeEmptyClassAtts(svg) {
|
|
527
|
+
let emptyClassEls = svg.querySelectorAll('[class=""');
|
|
528
|
+
emptyClassEls.forEach(el => {
|
|
529
|
+
el.removeAttribute('class')
|
|
530
|
+
})
|
|
531
|
+
}
|
|
561
532
|
|
|
562
|
-
let groups = [[el0]];
|
|
563
|
-
let idx = 0;
|
|
564
|
-
let elPrev = el0
|
|
565
533
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
let att_str = `${att}_${atts[att]}`
|
|
534
|
+
/**
|
|
535
|
+
* shared styles to group
|
|
536
|
+
*/
|
|
537
|
+
function sharedAttributesToGroup(svg) {
|
|
571
538
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
allProps[att_str].push(el)
|
|
576
|
-
//
|
|
577
|
-
if (allProps[att_str].length === len) {
|
|
578
|
-
globalAtts.push(att)
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
}
|
|
539
|
+
let els = svg.querySelectorAll(renderedEls.join(', '))
|
|
540
|
+
let len = els.length;
|
|
541
|
+
if(len===1) return;
|
|
582
542
|
|
|
583
|
-
|
|
584
|
-
|
|
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
|
-
}
|
|
543
|
+
let el0 = els[0] || null
|
|
544
|
+
let stylePrev = el0.styleSet !== undefined ? [...el0.styleSet].join('_') : ''
|
|
596
545
|
|
|
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
546
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
idx++
|
|
612
|
-
}
|
|
613
|
-
// update style
|
|
614
|
-
stylePrev = style
|
|
615
|
-
elPrev = el
|
|
547
|
+
// all props
|
|
548
|
+
let allProps = {}
|
|
549
|
+
|
|
550
|
+
// find attributes shared by all
|
|
551
|
+
let globalAtts = []
|
|
552
|
+
|
|
553
|
+
if (len) {
|
|
616
554
|
|
|
617
|
-
|
|
555
|
+
let groups = [[el0]];
|
|
556
|
+
let idx = 0;
|
|
557
|
+
let elPrev = el0
|
|
618
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]}`
|
|
619
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
|
+
}
|
|
620
575
|
|
|
576
|
+
//console.log('allProps', allProps);
|
|
577
|
+
//console.log('globalAtts', globalAtts);
|
|
621
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
|
+
}
|
|
622
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
|
|
623
609
|
|
|
610
|
+
}// endof el loop
|
|
624
611
|
|
|
625
|
-
|
|
612
|
+
//console.log('g', groups);
|
|
626
613
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
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')
|
|
633
620
|
|
|
621
|
+
// only 1 child - nothing to group
|
|
622
|
+
if (children.length === 1) continue
|
|
634
623
|
|
|
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
|
-
}
|
|
642
624
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
continue
|
|
651
|
-
}
|
|
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
|
+
}
|
|
652
632
|
|
|
633
|
+
// move attributes to group
|
|
634
|
+
for (let att in atts) {
|
|
635
|
+
let val = atts[att];
|
|
636
|
+
//console.log('att', atts, val);
|
|
653
637
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
groupEl.append(...children)
|
|
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)
|
|
660
643
|
}
|
|
644
|
+
children.forEach(child => {
|
|
645
|
+
child.removeAttribute(att)
|
|
646
|
+
})
|
|
647
|
+
}
|
|
648
|
+
}
|
|
661
649
|
|
|
662
650
|
|
|
651
|
+
} // endof groups
|
|
663
652
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
let val = atts[att];
|
|
667
|
-
//console.log('att', atts, val);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
668
655
|
|
|
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
656
|
|
|
680
|
-
|
|
657
|
+
// merge adjacent paths
|
|
658
|
+
function mergePathsWithSameProps(svg) {
|
|
659
|
+
let paths = svg.querySelectorAll('path')
|
|
660
|
+
let len = paths.length;
|
|
681
661
|
|
|
682
|
-
|
|
683
|
-
|
|
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);
|
|
684
667
|
|
|
685
|
-
|
|
668
|
+
let remove = []
|
|
686
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
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(' ')
|
|
688
692
|
|
|
689
|
-
|
|
690
|
-
if (cleanupClip) removeFutileClipPaths(svg, { x, y, width, height })
|
|
693
|
+
}
|
|
691
694
|
|
|
695
|
+
// update style
|
|
696
|
+
stylePrev = style
|
|
697
|
+
}
|
|
692
698
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
hrefs.forEach(el => {
|
|
698
|
-
let href = el.getAttribute('href')
|
|
699
|
-
el.setAttribute('xlink:href', href)
|
|
700
|
-
el.removeAttribute('href')
|
|
699
|
+
|
|
700
|
+
//console.log('remove', remove);
|
|
701
|
+
remove.forEach(el => {
|
|
702
|
+
el.remove()
|
|
701
703
|
})
|
|
704
|
+
|
|
702
705
|
}
|
|
703
706
|
|
|
707
|
+
}
|
|
704
708
|
|
|
705
709
|
|
|
706
|
-
return { svg, svgElProps }
|
|
707
|
-
|
|
708
|
-
}
|
|
709
710
|
|
|
710
711
|
|
|
711
712
|
function removeOffCanvasEls(svg, { x = 0, y = 0, width = 0, height = 0 } = {}) {
|
|
@@ -733,6 +734,13 @@ function addSvgViewBox(svg, { x = 0, y = 0, width = 0, height = 0 } = {}) {
|
|
|
733
734
|
svg.setAttribute('viewBox', [x, y, width, height].join(' '))
|
|
734
735
|
}
|
|
735
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
|
+
|
|
736
744
|
|
|
737
745
|
function cleanupSvgDefs(svg, { x = 0, y = 0, width = 0, height = 0, cleanupClip = true } = {}) {
|
|
738
746
|
let defs = svg.querySelectorAll('defs')
|
|
@@ -769,9 +777,6 @@ function cleanupSvgDefs(svg, { x = 0, y = 0, width = 0, height = 0, cleanupClip
|
|
|
769
777
|
}
|
|
770
778
|
})
|
|
771
779
|
|
|
772
|
-
// remove futile clip-paths
|
|
773
|
-
//if (cleanupClip) removeFutileClipPaths(svg, {x, y, width, height})
|
|
774
|
-
|
|
775
780
|
}
|
|
776
781
|
|
|
777
782
|
|
|
@@ -825,274 +830,23 @@ function removeFutileClipPaths(svg, { x = 0, y = 0, width = 0, height = 0 } = {}
|
|
|
825
830
|
}
|
|
826
831
|
|
|
827
832
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
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
|
-
}
|
|
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')
|
|
1004
840
|
})
|
|
1005
841
|
}
|
|
1006
842
|
|
|
1007
843
|
|
|
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
844
|
|
|
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
845
|
|
|
1055
846
|
|
|
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
|
-
|
|
1067
|
-
export function stringifySVG(svg, {
|
|
1068
|
-
omitNamespace = false,
|
|
1069
|
-
removeComments = true,
|
|
1070
|
-
} = {}) {
|
|
1071
|
-
let markup = new XMLSerializer().serializeToString(svg);
|
|
1072
|
-
|
|
1073
|
-
if (omitNamespace) {
|
|
1074
|
-
markup = markup.replaceAll('xmlns="http://www.w3.org/2000/svg"', '')
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
if (removeComments) {
|
|
1078
|
-
markup = markup
|
|
1079
|
-
.replace(/(<!--.*?-->)|(<!--[\S\s]+?-->)|(<!--[\S\s]*?$)/g, '')
|
|
1080
|
-
}
|
|
1081
847
|
|
|
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(' ', '\n');
|
|
1092
848
|
|
|
1093
849
|
|
|
1094
850
|
|
|
1095
851
|
|
|
1096
852
|
|
|
1097
|
-
return markup
|
|
1098
|
-
}
|