svg-path-simplify 0.0.9 → 0.1.2

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.
@@ -0,0 +1,222 @@
1
+ import { checkLineIntersection, getAngle, getDistAv, getDistance, getPointOnEllipse, getSquareDistance, pointAtT, rotatePoint } from "./geometry";
2
+ import { getPolygonArea } from "./geometry_area";
3
+ import { getArcFromPoly } from "./geometry_deduceRadius";
4
+ import { arcToBezierResolved, revertCubicQuadratic } from "./pathData_convert";
5
+ import { pathDataToD } from "./pathData_stringify";
6
+ import { renderPath, renderPoint, renderPoly } from "./visualize";
7
+
8
+ export function refineRoundSegments(pathData, {
9
+ threshold = 0,
10
+ tolerance = 1,
11
+ // take arcs or cubic beziers
12
+ toCubic = false,
13
+ debug = false
14
+ } = {}) {
15
+
16
+
17
+ // min size threshold for corners
18
+ threshold *= tolerance;
19
+
20
+ let l = pathData.length;
21
+
22
+ // add fist command
23
+ let pathDataN = [pathData[0]]
24
+
25
+ // just for debugging
26
+ let pathDataTest = []
27
+
28
+ for (let i = 1; i < l; i++) {
29
+ let com = pathData[i];
30
+ let { type } = com;
31
+ let comP = pathData[i - 1];
32
+ let comN = pathData[i + 1] ? pathData[i + 1] : null;
33
+ let comN2 = pathData[i + 2] ? pathData[i + 2] : null;
34
+ let comN3 = pathData[i + 3] ? pathData[i + 3] : null;
35
+ let comBez = null;
36
+
37
+ if ((com.type === 'C' || com.type === 'Q')) comBez = com;
38
+ else if (comN && (comN.type === 'C' || comN.type === 'Q')) comBez = comN;
39
+
40
+
41
+ let cpts = comBez ? (comBez.type === 'C' ? [comBez.p0, comBez.cp1, comBez.cp2, comBez.p] : [comBez.p0, comBez.cp1, comBez.p]) : []
42
+
43
+ let areaBez = 0;
44
+ let areaLines = 0;
45
+ let signChange = false;
46
+ let L1, L2;
47
+ let combine = false
48
+
49
+ let p0_S, p_S;
50
+ let poly = []
51
+ let pMid;
52
+
53
+
54
+ // 2. line-line-bezier-line-line
55
+ if (
56
+ comP.type === 'L' &&
57
+ type === 'L' &&
58
+ comBez &&
59
+ comN2.type === 'L' &&
60
+ comN3 && (comN3.type === 'L' || comN3.type === 'Z')
61
+ ) {
62
+
63
+ L1 = [com.p0, com.p];
64
+ L2 = [comN2.p0, comN2.p];
65
+ p0_S = com.p0
66
+ p_S = comN2.p
67
+
68
+ // don't allow sign changes
69
+ areaBez = getPolygonArea(cpts, false)
70
+ areaLines = getPolygonArea([...L1, ...L2], false)
71
+ signChange = (areaBez < 0 && areaLines > 0) || (areaBez > 0 && areaLines < 0)
72
+
73
+ if (!signChange) {
74
+
75
+ // mid point of mid bezier
76
+ pMid = pointAtT(cpts, 0.5)
77
+
78
+ // add to poly
79
+ poly = [p0_S, pMid, p_S]
80
+
81
+ combine = true
82
+ }
83
+
84
+ }
85
+
86
+ // 1. line-bezier-bezier-line
87
+ else if ((type === 'C' || type === 'Q') && comP.type === 'L') {
88
+
89
+ // 1.2 next is cubic next is lineto
90
+ if ((comN.type === 'C' || comN.type === 'Q') && comN2.type === 'L') {
91
+
92
+ combine = true
93
+
94
+ L1 = [comP.p0, comP.p];
95
+ L2 = [comN2.p0, comN2.p];
96
+ p0_S = comP.p
97
+ p_S = comN2.p0
98
+
99
+ // mid point of mid bezier
100
+ pMid = comBez.p
101
+
102
+ // add to poly
103
+ poly = [p0_S, comBez.p, p_S]
104
+
105
+
106
+ }
107
+ }
108
+
109
+
110
+ /**
111
+ * calculate either combined
112
+ * cubic or arc commands
113
+ */
114
+ if (combine) {
115
+
116
+
117
+ // try to find center of arc
118
+ let arcProps = getArcFromPoly(poly)
119
+ if (arcProps) {
120
+
121
+ let { centroid, r, deltaAngle, startAngle, endAngle } = arcProps;
122
+
123
+ let xAxisRotation = 0;
124
+ let sweep = deltaAngle > 0 ? 1 : 0;
125
+ let largeArc = Math.abs(deltaAngle) > Math.PI ? 1 : 0;
126
+
127
+ let pCM = rotatePoint(p0_S, centroid.x, centroid.y, deltaAngle * 0.5)
128
+
129
+
130
+ let dist2 = getDistAv(pCM, pMid)
131
+ let thresh = getDistAv(p0_S, p_S) * 0.05
132
+ let bezierCommands;
133
+
134
+ // point is close enough
135
+ if (dist2 < thresh) {
136
+
137
+ //toCubic = false;
138
+
139
+ bezierCommands = arcToBezierResolved(
140
+ {
141
+ p0: p0_S,
142
+ p: p_S,
143
+ centroid,
144
+ rx: r,
145
+ ry: r,
146
+ xAxisRotation,
147
+ sweep,
148
+ largeArc,
149
+ deltaAngle,
150
+ startAngle,
151
+ endAngle
152
+ }
153
+ );
154
+
155
+ if(bezierCommands.length === 1){
156
+
157
+ // prefer more compact quadratic - otherwise arcs
158
+ let comBezier = revertCubicQuadratic(p0_S, bezierCommands[0].cp1, bezierCommands[0].cp2, p_S)
159
+
160
+ if (comBezier.type === 'Q') {
161
+ toCubic = true
162
+ }
163
+
164
+ com = comBezier
165
+ }
166
+
167
+
168
+ // prefer arcs if 2 cubics are required
169
+ if (bezierCommands.length > 1) toCubic = false;
170
+
171
+
172
+ //toCubic = false
173
+
174
+ // return elliptic arc commands
175
+ if (!toCubic) {
176
+ // rewrite simplified command
177
+ com.type = 'A'
178
+ com.values = [r, r, xAxisRotation, largeArc, sweep, p_S.x, p_S.y];
179
+ }
180
+
181
+ com.p0 = p0_S;
182
+ com.p = p_S;
183
+ com.extreme = false;
184
+ com.corner = false;
185
+
186
+ // test rendering
187
+ //debug=true
188
+
189
+ if (debug) {
190
+ // arcs
191
+ if (!toCubic) {
192
+ pathDataTest = [
193
+ { type: 'M', values: [p0_S.x, p0_S.y] },
194
+ { type: 'A', values: [r, r, xAxisRotation, largeArc, sweep, p_S.x, p_S.y] },
195
+ ]
196
+ }
197
+ // cubics
198
+ else {
199
+ pathDataTest = [
200
+ { type: 'M', values: [p0_S.x, p0_S.y] },
201
+ ...bezierCommands
202
+ ]
203
+ }
204
+
205
+ let d = pathDataToD(pathDataTest);
206
+ renderPath(markers, d, 'orange', '0.5%', '0.5')
207
+ }
208
+
209
+ pathDataN.push(com);
210
+ i++
211
+ continue
212
+
213
+ }
214
+ }
215
+ }
216
+
217
+ // pass through
218
+ pathDataN.push(com)
219
+ }
220
+
221
+ return pathDataN;
222
+ }
@@ -24,11 +24,14 @@ export function pathDataRemoveColinear(pathData, {
24
24
  //let p1 = comN.type.toLowerCase() === 'z' ? M : { x: comN.values[comN.values.length - 2], y: comN.values[comN.values.length - 1] }
25
25
  let p1 = comN.type.toLowerCase() === 'z' ? M : { x: comN.values[comN.values.length - 2], y: comN.values[comN.values.length - 1] }
26
26
 
27
+
27
28
  let { type, values } = com;
28
29
  let valsL = values.slice(-2)
29
30
  p = type !== 'Z' ? { x: valsL[0], y: valsL[1] } : M;
30
31
 
31
- let area = getPolygonArea([p0, p, p1], true)
32
+
33
+ let area = p1 ? getPolygonArea([p0, p, p1], true) : Infinity
34
+
32
35
 
33
36
  //let distSquare0 = getSquareDistance(p0, p)
34
37
  //let distSquare1 = getSquareDistance(p, p1)
@@ -56,14 +59,14 @@ export function pathDataRemoveColinear(pathData, {
56
59
  type = "L"
57
60
  com.type = "L"
58
61
  com.values = valsL
59
- //renderPoint(markers, p, 'cyan', '1%', '0.5')
62
+ //renderPoint(markers, p, 'cyan', '2%', '0.5')
60
63
  }
61
64
  }
62
65
 
63
66
 
64
67
  // colinear – exclude arcs (as always =) as semicircles won't have an area
65
68
  //&& comN.type==='L'
66
- if ( isFlat && c < l - 1 && (type === 'L' || (flatBezierToLinetos && isFlatBez)) ) {
69
+ if ( isFlat && c < l - 1 && comN.type!=='A' && (type === 'L' || (flatBezierToLinetos && isFlatBez)) ) {
67
70
 
68
71
  /*
69
72
  console.log(area, distMax );
@@ -78,6 +81,8 @@ export function pathDataRemoveColinear(pathData, {
78
81
  renderPoint(markers, p1, 'cyan', '0.5%', '1')
79
82
  */
80
83
 
84
+ //renderPoint(markers, p, 'blue', '1%', '1')
85
+
81
86
 
82
87
  continue;
83
88
  }
@@ -0,0 +1,66 @@
1
+ import { getDistAv } from "./geometry";
2
+ import { renderPoint } from "./visualize";
3
+
4
+ export function refineClosingCommand(pathData = [], {
5
+ threshold = 0,
6
+ } = {}) {
7
+
8
+ let l = pathData.length;
9
+ let comLast = pathData[l - 1]
10
+ let isClosed = comLast.type.toLowerCase() === 'z';
11
+ let idxPenultimate = isClosed ? l - 2 : l - 1
12
+ let comPenultimate = isClosed ? pathData[idxPenultimate] : pathData[idxPenultimate]
13
+ let valsPen = comPenultimate.values.slice(-2)
14
+
15
+
16
+ let M = { x: pathData[0].values[0], y: pathData[0].values[1] }
17
+ let pPen = { x: valsPen[0], y: valsPen[1] }
18
+ let dist = getDistAv(M, pPen)
19
+
20
+ // adjust last coordinates for better reordering
21
+ if (dist && dist < threshold) {
22
+ //console.log('dist', dist, 'threshold', threshold, comPenultimate);
23
+ //renderPoint(markers, pPen)
24
+
25
+ let valsLast = pathData[idxPenultimate].values
26
+ let valsLastLen = valsLast.length;
27
+ pathData[idxPenultimate].values[valsLastLen - 2] = M.x
28
+ pathData[idxPenultimate].values[valsLastLen - 1] = M.y
29
+
30
+ // adjust cpts
31
+ let comFirst = pathData[1]
32
+ //console.log(comFirst, comPenultimate);
33
+
34
+ if (comFirst.type === 'C' && comPenultimate.type === 'C') {
35
+ let dx1 = Math.abs(comFirst.values[0] - comPenultimate.values[2])
36
+ let dy1 = Math.abs(comFirst.values[1] - comPenultimate.values[3])
37
+
38
+ let dx2 = Math.abs(pathData[1].values[0] - comFirst.values[0])
39
+ let dy2 = Math.abs(pathData[1].values[1] - comFirst.values[1])
40
+
41
+ let dx3 = Math.abs(pathData[1].values[0] - comPenultimate.values[2])
42
+ let dy3 = Math.abs(pathData[1].values[1] - comPenultimate.values[3])
43
+
44
+ let ver = dx2 < threshold && dx3 < threshold && dy1;
45
+ let hor = (dy2 < threshold && dy3 < threshold) && dx1;
46
+
47
+ //console.log(dy1);
48
+ if (dx1 && dx1 < threshold && ver) {
49
+ //console.log('adjust v');
50
+ pathData[1].values[0] = M.x
51
+ pathData[idxPenultimate].values[2] = M.x
52
+ }
53
+
54
+ if (dy1 && dy1 < threshold && hor) {
55
+ //console.log('should be y extreme');
56
+ pathData[1].values[1] = M.y
57
+ pathData[idxPenultimate].values[3] = M.y
58
+ }
59
+
60
+ }
61
+ }
62
+
63
+ return pathData;
64
+
65
+
66
+ }
@@ -47,28 +47,44 @@ export function pathDataToTopLeft(pathData) {
47
47
 
48
48
 
49
49
 
50
- export function optimizeClosePath(pathData, removeFinalLineto = true, reorder = true) {
50
+ export function optimizeClosePath(pathData, {removeFinalLineto = true, autoClose = true}={}) {
51
51
 
52
52
  let pathDataNew = [];
53
- let len = pathData.length;
53
+ let l = pathData.length;
54
54
  let M = { x: +pathData[0].values[0].toFixed(8), y: +pathData[0].values[1].toFixed(8) }
55
- let isClosed = pathData[len - 1].type.toLowerCase() === 'z'
55
+ let isClosed = pathData[l - 1].type.toLowerCase() === 'z'
56
56
 
57
57
  let linetos = pathData.filter(com => com.type === 'L')
58
58
 
59
- //return pathData;
60
-
61
59
 
62
60
  // check if order is ideal
63
- let penultimateCom = pathData[len - 2];
61
+ let idxPenultimate = isClosed ? l-2 : l-1
62
+
63
+ let penultimateCom = pathData[idxPenultimate];
64
64
  let penultimateType = penultimateCom.type;
65
65
  let penultimateComCoords = penultimateCom.values.slice(-2).map(val => +val.toFixed(8))
66
66
 
67
67
  // last L command ends at M
68
68
  let isClosingCommand = penultimateComCoords[0] === M.x && penultimateComCoords[1] === M.y
69
69
 
70
+ // add closepath Z to enable order optimizations
71
+ if(!isClosed && autoClose && isClosingCommand){
72
+
73
+ /*
74
+ // adjust final coords
75
+ let valsLast = pathData[idxPenultimate].values
76
+ let valsLastLen = valsLast.length;
77
+ pathData[idxPenultimate].values[valsLastLen-2] = M.x
78
+ pathData[idxPenultimate].values[valsLastLen-1] = M.y
79
+ */
80
+
81
+ pathData.push({type:'Z', values:[]})
82
+ isClosed = true;
83
+ l++
84
+ }
85
+
70
86
  // if last segment is not closing or a lineto
71
- let skipReorder = pathData[1].type !== 'L' && (!isClosingCommand || penultimateType === 'L')
87
+ let skipReorder = pathData[1].type !== 'L' && (!isClosingCommand || penultimateCom.type === 'L')
72
88
  skipReorder = false
73
89
 
74
90
 
@@ -82,7 +98,7 @@ export function optimizeClosePath(pathData, removeFinalLineto = true, reorder =
82
98
  if (!skipReorder) {
83
99
  //get top most index
84
100
  let indices = [];
85
- for (let i = 0, len = pathData.length; i < len; i++) {
101
+ for (let i = 0; i < l; i++) {
86
102
  let com = pathData[i];
87
103
  let { type, values } = com;
88
104
  if (values.length) {
@@ -122,10 +138,10 @@ export function optimizeClosePath(pathData, removeFinalLineto = true, reorder =
122
138
 
123
139
  M = { x: +pathData[0].values[0].toFixed(8), y: +pathData[0].values[1].toFixed(8) }
124
140
 
125
- len = pathData.length
141
+ l = pathData.length
126
142
 
127
143
  // remove last lineto
128
- penultimateCom = pathData[len - 2];
144
+ penultimateCom = pathData[l - 2];
129
145
  penultimateType = penultimateCom.type;
130
146
  penultimateComCoords = penultimateCom.values.slice(-2).map(val=>+val.toFixed(8))
131
147
 
@@ -134,7 +150,7 @@ export function optimizeClosePath(pathData, removeFinalLineto = true, reorder =
134
150
  //console.log('penultimateCom', isClosingCommand, penultimateCom.values, M);
135
151
 
136
152
  if (removeFinalLineto && isClosingCommand) {
137
- pathData.splice(len - 2, 1)
153
+ pathData.splice(l - 2, 1)
138
154
  }
139
155
 
140
156
  pathDataNew.push(...pathData);
@@ -1,4 +1,4 @@
1
- import { checkLineIntersection, getDistAv, interpolate, pointAtT } from "./geometry";
1
+ import { checkLineIntersection, getDistAv, getSquareDistance, interpolate, pointAtT } from "./geometry";
2
2
  import { getPolygonArea } from "./geometry_area";
3
3
  import { commandIsFlat } from "./geometry_flatness";
4
4
  import { renderPoint } from "./visualize";
@@ -8,12 +8,20 @@ export function refineRoundedCorners(pathData, {
8
8
  tolerance = 1
9
9
  } = {}) {
10
10
 
11
+
12
+ // min size threshold for corners
13
+ threshold *= tolerance;
14
+
11
15
  let l = pathData.length;
12
16
 
13
17
  // add fist command
14
18
  let pathDataN = [pathData[0]]
15
19
 
16
20
  let isClosed = pathData[l - 1].type.toLowerCase() === 'z';
21
+ let zIsLineto = isClosed ?
22
+ (pathData[l-1].p.x === pathData[0].p0.x && pathData[l-1].p.y === pathData[0].p0.y)
23
+ : false ;
24
+
17
25
  let lastOff = isClosed ? 2 : 1;
18
26
 
19
27
  let comLast = pathData[l - lastOff];
@@ -24,7 +32,9 @@ export function refineRoundedCorners(pathData, {
24
32
 
25
33
  //console.log('lastIsLine', lastIsLine, 'firstIsLine', firstIsLine, 'lastIsBez', lastIsBez, 'firstIsBez', firstIsBez, 'isClosed', isClosed, 'comLast1', comLast1);
26
34
 
27
- let normalizeClose = isClosed && firstIsBez;
35
+ let normalizeClose = isClosed && firstIsBez && (lastIsLine || zIsLineto);
36
+ let adjustStart = false
37
+ //normalizeClose = false
28
38
  //console.log('normalizeClose', normalizeClose);
29
39
 
30
40
  // normalize closepath to lineto
@@ -42,9 +52,8 @@ export function refineRoundedCorners(pathData, {
42
52
  // search small cubic segments enclosed by linetos
43
53
  if ((type === 'L' && comN && comN.type === 'C') ||
44
54
  (type === 'C' && comN && comN.type === 'L')
45
-
46
55
  ) {
47
- let comL0 = com;
56
+ let comL0 = type==='L' ? com : null;
48
57
  let comL1 = null;
49
58
  let comBez = [];
50
59
  let offset = 0;
@@ -54,7 +63,12 @@ export function refineRoundedCorners(pathData, {
54
63
  comBez = [pathData[1]]
55
64
  comL0 = pathData[l - 1]
56
65
  comL1 = comN
57
- //renderPoint(markers, com.p, 'orange')
66
+ //renderPoint(markers, com.p, 'purple')
67
+ }
68
+
69
+ if(!comL0) {
70
+ pathDataN.push(com)
71
+ continue
58
72
  }
59
73
 
60
74
  // closing corner to start
@@ -93,49 +107,50 @@ export function refineRoundedCorners(pathData, {
93
107
  // check concaveness by area sign change
94
108
  let area1 = getPolygonArea([comL0.p0, comL0.p, comL1.p0, comL1.p], false)
95
109
  let area2 = getPolygonArea([comBez[0].p0, comBez[0].cp1, comBez[0].cp2, comBez[0].p], false)
110
+ //let isFlatBezier = area2 < getSquareDistance(comL0.p, comL1.p)*0.001
96
111
 
97
112
  let signChange = (area1 < 0 && area2 > 0) || (area1 > 0 && area2 < 0)
98
113
 
99
- if (comBez && !signChange && len3 < threshold && len1 > len3 && len2 > len3) {
114
+ // exclude mid bezier segments that are larger than surrounding linetos
115
+ let bezThresh = len3*0.5 * tolerance
116
+ let isSmall = bezThresh < len1 && bezThresh < len2 ;
100
117
 
101
- let ptQ = checkLineIntersection(comL0.p0, comL0.p, comL1.p0, comL1.p, false)
102
- if (ptQ) {
118
+ //len1 > len3 && len2 > len3
119
+ if (comBez.length && !signChange && isSmall ) {
103
120
 
104
- /*
105
- let dist1 = getDistAv(ptQ, comL0.p)
106
- let dist2 = getDistAv(ptQ, comL1.p0)
107
- let diff = Math.abs(dist1-dist2)
108
- let rat = diff/Math.max(dist1, dist2)
109
- console.log('rat', rat);
110
- */
121
+ let isFlatBezier = Math.abs(area2) <= getSquareDistance(comBez[0].p0, comBez[0].p)*0.005
122
+ let ptQ = !isFlatBezier ? checkLineIntersection(comL0.p0, comL0.p, comL1.p0, comL1.p, false) : null
111
123
 
112
- /*
113
- // adjust curve start and end to meet original
114
- let t = 1
124
+ if (!isFlatBezier && ptQ) {
115
125
 
116
- let p0_2 = pointAtT([ptQ, comL0.p], t)
117
- //renderPoint(markers, p0_2, 'cyan', '1%', '0.5')
118
- comL0.p = p0_2
119
- comL0.values = [p0_2.x, p0_2.y]
126
+ // final check: mid point proximity
127
+ let ptM = pointAtT([comL0.p, ptQ, comL1.p0], 0.5)
128
+ //renderPoint(markers, ptM, 'red', '0.5%', '0.5')
120
129
 
121
- let p_2 = pointAtT([ptQ, comL1.p0], t)
122
- //renderPoint(markers, p_2, 'orange', '1%', '0.5')
123
- comL1.p0 = p_2
130
+ let ptM_bez = comBez.length===1 ? pointAtT( [comBez[0].p0, comBez[0].cp1, comBez[0].cp2, comBez[0].p], 0.5 ) : comBez[0].p ;
124
131
 
125
- //renderPoint(markers, comL0.p, 'red', '1%', '0.5')
126
- //renderPoint(markers, ptQ, 'magenta')
127
- */
132
+ let dist1 = getDistAv(ptM, ptM_bez)
128
133
 
134
+ // not in tolerance – rturn original command
135
+ if(dist1>len3){
136
+ //renderPoint(markers, ptM_bez, 'cyan', '0.5%', '0.5')
137
+ //renderPoint(markers, ptQ, 'magenta', '0.5%', '0.5')
138
+ pathDataN.push(com);
139
+ } else{
129
140
 
130
- let comQ = { type: 'Q', values: [ptQ.x, ptQ.y, comL1.p0.x, comL1.p0.y] }
131
- comQ.p0 = comL0.p;
132
- comQ.cp1 = ptQ;
133
- comQ.p = comL1.p0;
141
+ //renderPoint(markers, ptQ, 'magenta', '0.5%', '0.5')
142
+
143
+ let comQ = { type: 'Q', values: [ptQ.x, ptQ.y, comL1.p0.x, comL1.p0.y] }
144
+ comQ.p0 = comL0.p;
145
+ comQ.cp1 = ptQ;
146
+ comQ.p = comL1.p0;
147
+
148
+ // add quadratic command
149
+ pathDataN.push(comL0, comQ);
150
+ i += offset;
151
+ continue;
152
+ }
134
153
 
135
- // add quadratic command
136
- pathDataN.push(comL0, comQ);
137
- i += offset;
138
- continue;
139
154
  }
140
155
  }
141
156
  }
@@ -150,11 +165,16 @@ export function refineRoundedCorners(pathData, {
150
165
 
151
166
  }
152
167
 
168
+
169
+
153
170
  // revert close path normalization
154
- if (normalizeClose) {
171
+ if (normalizeClose || (isClosed && pathDataN[pathDataN.length-1].type!=='Z') ) {
155
172
  pathDataN.push({ type: 'Z', values: [] })
156
173
  }
157
174
 
175
+
176
+ //console.log(pathDataN);
177
+
158
178
  return pathDataN;
159
179
 
160
180
  }
@@ -1,54 +1,53 @@
1
1
 
2
2
 
3
-
4
3
  export function removeEmptySVGEls(svg) {
5
4
  let els = svg.querySelectorAll('g, defs');
6
5
  els.forEach(el => {
7
- if (!el.children.length) el.remove()
6
+ if (!el.children.length) el.remove()
8
7
  })
9
8
  }
10
9
 
11
-
10
+ //const DOMParserPoly = globalThis.DOMParser;
12
11
 
13
12
  export function cleanUpSVG(svgMarkup, {
14
- returnDom=false,
15
- removeHidden=true,
16
- removeUnused=true,
17
- }={}) {
13
+ returnDom = false,
14
+ removeHidden = true,
15
+ removeUnused = true,
16
+ } = {}) {
18
17
  svgMarkup = cleanSvgPrologue(svgMarkup);
19
-
18
+
20
19
  // replace namespaced refs
21
20
  svgMarkup = svgMarkup.replaceAll("xlink:href=", "href=");
22
-
21
+
23
22
  let svg = new DOMParser()
24
- .parseFromString(svgMarkup, "text/html")
25
- .querySelector("svg");
26
-
27
-
28
- let allowed=['viewBox', 'xmlns', 'width', 'height', 'id', 'class', 'fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin'];
23
+ .parseFromString(svgMarkup, "text/html")
24
+ .querySelector("svg");
25
+
26
+
27
+ let allowed = ['viewBox', 'xmlns', 'width', 'height', 'id', 'class', 'fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin'];
29
28
  removeExcludedAttribues(svg, allowed)
30
-
29
+
31
30
  let removeEls = ['metadata', 'script']
32
-
31
+
33
32
  let els = svg.querySelectorAll('*')
34
- els.forEach(el=>{
35
- let name = el.nodeName;
33
+ els.forEach(el => {
34
+ let name = el.nodeName;
36
35
  // remove hidden elements
37
36
  let style = el.getAttribute('style') || ''
38
37
  let isHiddenByStyle = style ? style.trim().includes('display:none') : false;
39
38
  let isHidden = (el.getAttribute('display') && el.getAttribute('display') === 'none') || isHiddenByStyle;
40
- if(name.includes(':') || removeEls.includes(name) || (removeHidden && isHidden )) {
39
+ if (name.includes(':') || removeEls.includes(name) || (removeHidden && isHidden)) {
41
40
  el.remove();
42
- }else{
41
+ } else {
43
42
  // remove BS elements
44
43
  removeNameSpaceAtts(el)
45
44
  }
46
45
  })
47
46
 
48
- if(returnDom) return svg
47
+ if (returnDom) return svg
49
48
 
50
49
  let markup = stringifySVG(svg)
51
- console.log(markup);
50
+ //console.log(markup);
52
51
 
53
52
  return markup;
54
53
  }
@@ -67,7 +66,7 @@ function cleanSvgPrologue(svgString) {
67
66
  );
68
67
  }
69
68
 
70
- function removeExcludedAttribues(el, allowed=['viewBox', 'xmlns', 'width', 'height', 'id', 'class']){
69
+ function removeExcludedAttribues(el, allowed = ['viewBox', 'xmlns', 'width', 'height', 'id', 'class']) {
71
70
  let atts = [...el.attributes].map((att) => att.name);
72
71
  atts.forEach((att) => {
73
72
  if (!allowed.includes(att)) {
@@ -86,13 +85,13 @@ function removeNameSpaceAtts(el) {
86
85
  });
87
86
  }
88
87
 
89
- export function stringifySVG(svg){
90
- let markup = new XMLSerializer().serializeToString(svg);
88
+ export function stringifySVG(svg) {
89
+ let markup = new XMLSerializer().serializeToString(svg);
91
90
  markup = markup
92
- .replace(/\t/g, "")
93
- .replace(/[\n\r|]/g, "\n")
94
- .replace(/\n\s*\n/g, '\n')
95
- .replace(/ +/g, ' ')
91
+ .replace(/\t/g, "")
92
+ .replace(/[\n\r|]/g, "\n")
93
+ .replace(/\n\s*\n/g, '\n')
94
+ .replace(/ +/g, ' ')
96
95
 
97
96
  return markup
98
97
  }