svg-path-simplify 0.3.5 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +3 -76
  3. package/dist/svg-path-simplify.esm.js +5505 -4030
  4. package/dist/svg-path-simplify.esm.min.js +2 -8
  5. package/dist/svg-path-simplify.js +5506 -4029
  6. package/dist/svg-path-simplify.min.js +2 -8
  7. package/dist/svg-path-simplify.pathdata.esm.js +1154 -1042
  8. package/dist/svg-path-simplify.pathdata.esm.min.js +2 -8
  9. package/docs/api.md +127 -0
  10. package/index.html +279 -257
  11. package/package.json +1 -1
  12. package/src/constants.js +10 -1
  13. package/src/index.js +7 -1
  14. package/src/pathData_simplify_cubic.js +1 -18
  15. package/src/pathSimplify-main.js +87 -30
  16. package/src/pathSimplify-only-pathdata.js +2 -2
  17. package/src/svg-getAttributes.js +13 -0
  18. package/src/svg_flatten_transforms.js +1 -1
  19. package/src/svg_getViewbox.js +23 -11
  20. package/src/svg_rootSVG.js +9 -0
  21. package/src/svgii/convert_colors.js +145 -0
  22. package/src/svgii/convert_units.js +159 -0
  23. package/src/svgii/geometry.js +24 -4
  24. package/src/svgii/geometry_bbox.js +2 -1
  25. package/src/svgii/geometry_bbox_element.js +46 -0
  26. package/src/svgii/pathData_analyze.js +34 -14
  27. package/src/svgii/pathData_convert.js +204 -34
  28. package/src/svgii/pathData_parse.js +2 -99
  29. package/src/svgii/pathData_parse_els.js +217 -128
  30. package/src/svgii/pathData_simplify_refineCorners.js +72 -43
  31. package/src/svgii/pathData_stringify.js +6 -5
  32. package/src/svgii/pathData_toPolygon.js +3 -1
  33. package/src/svgii/pathData_transform.js +307 -0
  34. package/src/svgii/poly_normalize.js +21 -1
  35. package/src/svgii/rounding.js +36 -5
  36. package/src/svgii/svg-styles-getTransforms.js +119 -8
  37. package/src/svgii/svg-styles-to-attributes-const.js +26 -6
  38. package/src/svgii/svg_cleanup.js +540 -74
  39. package/src/svgii/svg_el_parse_style_props.js +561 -0
  40. package/src/svgii/transform_qr_decompose.js +74 -0
  41. package/src/svgii/pathData_scale.js +0 -42
  42. package/src/svgii/stringify.js +0 -103
  43. package/src/svgii/svg-styles-to-attributes.js +0 -217
@@ -0,0 +1,307 @@
1
+ import { svgArcToCenterParam } from "./geometry";
2
+
3
+ /**
4
+ * scale pathData
5
+ */
6
+ export function transformPathData(pathData, matrix) {
7
+
8
+ // new pathdata
9
+ let pathDataTrans = [];
10
+
11
+ // transform point by 2d matrix
12
+ const transformPoint = (pt, matrix) => {
13
+ let { a, b, c, d, e, f } = matrix;
14
+ let { x, y } = pt;
15
+ return { x: a * x + c * y + e, y: b * x + d * y + f };
16
+ }
17
+
18
+ //normalize matrix notations object, array or css matrix string
19
+ const normalizeMatrix = (matrix) => {
20
+ matrix =
21
+ typeof matrix === "string"
22
+ ? (matrix = matrix
23
+ .replace(/^matrix\(|\)$/g, "")
24
+ .split(",")
25
+ .map(Number))
26
+ : matrix;
27
+ matrix = !Array.isArray(matrix)
28
+ ? {
29
+ a: matrix.a,
30
+ b: matrix.b,
31
+ c: matrix.c,
32
+ d: matrix.d,
33
+ e: matrix.e,
34
+ f: matrix.f
35
+ }
36
+ : {
37
+ a: matrix[0],
38
+ b: matrix[1],
39
+ c: matrix[2],
40
+ d: matrix[3],
41
+ e: matrix[4],
42
+ f: matrix[5]
43
+ };
44
+ return matrix;
45
+ }
46
+
47
+
48
+ const transformArc = (p0, values, matrix) => {
49
+ let [rx, ry, angle, largeArc, sweep, x, y] = values;
50
+
51
+ /**
52
+ * parametrize arc command
53
+ * to get the actual arc params
54
+ */
55
+ let arcData = svgArcToCenterParam(
56
+ p0.x,
57
+ p0.y,
58
+ values[0],
59
+ values[1],
60
+ angle,
61
+ largeArc,
62
+ sweep,
63
+ x,
64
+ y
65
+ );
66
+ ({ rx, ry } = arcData);
67
+ let { a, b, c, d, e, f } = matrix;
68
+
69
+ let ellipsetr = transformEllipse(rx, ry, angle, matrix);
70
+ let p = transformPoint({ x: x, y: y }, matrix);
71
+
72
+
73
+ // adjust sweep if flipped
74
+ let denom = a * a + b * b;
75
+ let scaleX = Math.sqrt(denom)
76
+ let scaleY = (a * d - c * b) / scaleX
77
+
78
+ let flipX = scaleX < 0 ? true : false;
79
+ let flipY = scaleY < 0 ? true : false;
80
+
81
+
82
+ // adjust sweep
83
+ if (flipX || flipY) {
84
+ sweep = sweep === 0 ? 1 : 0;
85
+ }
86
+
87
+ return {
88
+ type: 'A',
89
+ values: [
90
+ ellipsetr.rx,
91
+ ellipsetr.ry,
92
+ ellipsetr.ax,
93
+ largeArc,
94
+ sweep,
95
+ p.x,
96
+ p.y]
97
+ };
98
+ }
99
+
100
+ // normalize matrix input
101
+ matrix = normalizeMatrix(matrix);
102
+
103
+ let matrixStr = [matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f]
104
+ .map((val) => {
105
+ return +val.toFixed(1);
106
+ })
107
+ .join("");
108
+
109
+ // no transform: quit
110
+ if (matrixStr === "100100") {
111
+ //console.log("no transform");
112
+ return pathData;
113
+ }
114
+
115
+
116
+ pathData.forEach((com, i) => {
117
+ let { type, values } = com;
118
+ let typeRel = type.toLowerCase();
119
+ let comPrev = i > 0 ? pathData[i - 1] : pathData[i];
120
+ let comPrevValues = comPrev.values;
121
+ let comPrevValuesL = comPrevValues.length;
122
+ let p0 = {
123
+ x: comPrevValues[comPrevValuesL - 2],
124
+ y: comPrevValues[comPrevValuesL - 1]
125
+ };
126
+ let p = { x: values[values.length - 2], y: values[values.length - 1] };
127
+ let comT = { type: type, values: [] };
128
+
129
+ switch (typeRel) {
130
+ case "a":
131
+ comT = transformArc(p0, values, matrix)
132
+ break;
133
+
134
+ default:
135
+ // all other point based commands
136
+ if (values.length) {
137
+ for (let i = 0; i < values.length; i += 2) {
138
+ let ptTrans = transformPoint(
139
+ { x: com.values[i], y: com.values[i + 1] },
140
+ matrix
141
+ );
142
+
143
+ comT.values[i] = ptTrans.x;
144
+ comT.values[i + 1] = ptTrans.y;
145
+ }
146
+ }
147
+ }
148
+
149
+ pathDataTrans.push(comT);
150
+ });
151
+
152
+
153
+ //console.log('pathDataTrans', pathDataTrans);
154
+
155
+ return pathDataTrans;
156
+ }
157
+
158
+
159
+
160
+
161
+
162
+
163
+ /**
164
+ * Based on: https://github.com/fontello/svgpath/blob/master/lib/ellipse.js
165
+ * and fork: https://github.com/kpym/SVGPathy/blob/master/lib/ellipse.js
166
+ */
167
+
168
+ function transformEllipse(rx, ry, ax, matrix) {
169
+ const torad = Math.PI / 180;
170
+ const epsilon = 1e-7;
171
+
172
+ //normalize matrix object or array notations
173
+ matrix = !Array.isArray(matrix)
174
+ ? matrix
175
+ : {
176
+ a: matrix[0],
177
+ b: matrix[1],
178
+ c: matrix[2],
179
+ d: matrix[3],
180
+ e: matrix[4],
181
+ f: matrix[5]
182
+ };
183
+
184
+ // We consider the current ellipse as image of the unit circle
185
+ // by first scale(rx,ry) and then rotate(ax) ...
186
+ // So we apply ma = m x rotate(ax) x scale(rx,ry) to the unit circle.
187
+ let c = Math.cos(ax * torad),
188
+ s = Math.sin(ax * torad);
189
+ let ma = [
190
+ rx * (matrix.a * c + matrix.c * s),
191
+ rx * (matrix.b * c + matrix.d * s),
192
+ ry * (-matrix.a * s + matrix.c * c),
193
+ ry * (-matrix.b * s + matrix.d * c)
194
+ ];
195
+
196
+ // ma * transpose(ma) = [ J L ]
197
+ // [ L K ]
198
+ // L is calculated later (if the image is not a circle)
199
+ let J = ma[0] * ma[0] + ma[2] * ma[2],
200
+ K = ma[1] * ma[1] + ma[3] * ma[3];
201
+
202
+ // the sqrt of the discriminant of the characteristic polynomial of ma * transpose(ma)
203
+ // this is also the geometric mean of the eigenvalues
204
+ let D = Math.sqrt(
205
+ ((ma[0] - ma[3]) * (ma[0] - ma[3]) + (ma[2] + ma[1]) * (ma[2] + ma[1])) *
206
+ ((ma[0] + ma[3]) * (ma[0] + ma[3]) + (ma[2] - ma[1]) * (ma[2] - ma[1]))
207
+ );
208
+
209
+ // the arithmetic mean of the eigenvalues
210
+ let JK = (J + K) / 2;
211
+
212
+ // check if the image is (almost) a circle
213
+ if (D <= epsilon) {
214
+ rx = ry = Math.sqrt(JK);
215
+ ax = 0;
216
+ return { rx: rx, ry: ry, ax: ax };
217
+ }
218
+
219
+ // check if ma * transpose(ma) is (almost) diagonal
220
+ if (Math.abs(D - Math.abs(J - K)) <= epsilon) {
221
+ rx = Math.sqrt(J);
222
+ ry = Math.sqrt(K);
223
+ ax = 0;
224
+ return { rx: rx, ry: ry, ax: ax };
225
+ }
226
+
227
+ // if it is not a circle, nor diagonal
228
+ let L = ma[0] * ma[1] + ma[2] * ma[3];
229
+
230
+ // {l1,l2} = the two eigen values of ma * transpose(ma)
231
+ let l1 = JK + D / 2,
232
+ l2 = JK - D / 2;
233
+
234
+ // the x - axis - rotation angle is the argument of the l1 - eigenvector
235
+ if (Math.abs(L) <= epsilon && Math.abs(l1 - K) <= epsilon) {
236
+ // if (ax == 90) => ax = 0 and exchange axes
237
+ ax = 0;
238
+ rx = Math.sqrt(l2);
239
+ ry = Math.sqrt(l1);
240
+ return { rx: rx, ry: ry, ax: ax };
241
+ }
242
+
243
+ ax =
244
+ Math.atan(Math.abs(L) > Math.abs(l1 - K) ? (l1 - J) / L : L / (l1 - K)) /
245
+ torad; // the angle in degree
246
+
247
+ // if ax > 0 => rx = sqrt(l1), ry = sqrt(l2), else exchange axes and ax += 90
248
+ if (ax >= 0) {
249
+ // if ax in [0,90]
250
+ rx = Math.sqrt(l1);
251
+ ry = Math.sqrt(l2);
252
+ } else {
253
+ // if ax in ]-90,0[ => exchange axes
254
+ ax += 90;
255
+ rx = Math.sqrt(l2);
256
+ ry = Math.sqrt(l1);
257
+ }
258
+
259
+ return { rx: rx, ry: ry, ax: ax };
260
+ }
261
+
262
+
263
+
264
+
265
+
266
+ /**
267
+ * scale pathData
268
+ */
269
+
270
+ export function scalePathData(pathData, scaleX, scaleY) {
271
+ pathData.forEach((com, i) => {
272
+ let { type, values } = com;
273
+ let typeRel = type.toLowerCase();
274
+
275
+ switch (typeRel) {
276
+ case "a":
277
+ com.values = [
278
+ values[0] * scaleX,
279
+ values[1] * scaleY,
280
+ values[2],
281
+ values[3],
282
+ values[4],
283
+ values[5] * scaleX,
284
+ values[6] * scaleY
285
+ ];
286
+ break;
287
+
288
+ case "h":
289
+ com.values = [values[0] * scaleX];
290
+ break;
291
+
292
+ case "v":
293
+ com.values = [values[0] * scaleY];
294
+ break;
295
+
296
+ default:
297
+ if (values.length) {
298
+ for (let i = 0; i < values.length; i += 2) {
299
+ com.values[i] *= scaleX;
300
+ com.values[i + 1] *= scaleY;
301
+ }
302
+ }
303
+ }
304
+
305
+ });
306
+ return pathData;
307
+ }
@@ -4,6 +4,12 @@ export function normalizePoly(pts, {
4
4
  flatten = false
5
5
  } = {}) {
6
6
 
7
+ // is stringified flat point attribute
8
+ if(typeof pts === 'string' && !isNaN(pts[0])){
9
+ pts = toPointArray(pts.split(/,| /).filter(Boolean).map(Number));
10
+ return pts
11
+ }
12
+
7
13
  if (flatten) pts = pts.flat(2);
8
14
  let poly = toArray ? polyPtsToArray(pts) : polyArrayToObject(pts)
9
15
  return poly
@@ -29,6 +35,11 @@ export function polyArrayToObject(pts) {
29
35
  return poly
30
36
  }
31
37
 
38
+ else if(pts.length>3){
39
+ pts = toPointArray(pts)
40
+ return pts
41
+ }
42
+
32
43
  return pts.map(pt => { return { x: pt[0], y: pt[1] } })
33
44
  }
34
45
 
@@ -48,4 +59,13 @@ export function polyPtsToArray(pts) {
48
59
 
49
60
  poly = Array.from(pts).map(pt => [pt.x, pt.y])
50
61
  return poly
51
- }
62
+ }
63
+
64
+ // convert flat point value array to point object array
65
+ export function toPointArray(pts) {
66
+ let ptArr = [];
67
+ for (let i = 1, l = pts.length; i < l; i += 2) {
68
+ ptArr.push({ x: pts[i - 1], y: pts[i] });
69
+ }
70
+ return ptArr;
71
+ };
@@ -85,32 +85,63 @@ export function detectAccuracy(pathData) {
85
85
  }
86
86
 
87
87
 
88
+
89
+
88
90
  export function roundTo(num = 0, decimals = 3) {
89
91
  if (!decimals) return Math.round(num);
90
92
  let factor = 10 ** decimals;
91
93
  return Math.round(num * factor) / factor;
92
94
  }
93
95
 
96
+ /**
97
+ * round to reasonable
98
+ * floating point accuracy
99
+ * based on numeric value
100
+ */
101
+ export function autoRound(val, integerThresh = 10){
102
+ let decimals=8;
103
+
104
+ //console.log('val', val);
105
+
106
+ if(val>integerThresh){
107
+ decimals=0
108
+ }
109
+ else if(val>5){
110
+ decimals=1
111
+ }else{
112
+ decimals=Math.ceil(integerThresh/val).toString().length
113
+ }
114
+
115
+ let factor = 10 ** decimals;
116
+ return Math.round(val * factor) / factor;
117
+ }
118
+
119
+
120
+
94
121
 
95
122
  /**
96
123
  * round path data
97
124
  * either by explicit decimal value or
98
125
  * based on suggested accuracy in path data
99
126
  */
100
- export function roundPathData(pathData, decimals = -1) {
127
+ export function roundPathData(pathData, decimalsGlobal = -1) {
101
128
 
102
- if (decimals < 0) return pathData;
129
+ if (decimalsGlobal < 0) return pathData;
103
130
 
104
131
  let len = pathData.length;
132
+ //let decimals = pathData[0].decimals ? pathData[0].decimals+1 : decimalsGlobal
133
+ let decimals = decimalsGlobal
134
+ //decimals = decimalsGlobal;
135
+ //console.log('decimals subpath', decimals, pathData[0].decimals, 'decimalsGlobal', decimalsGlobal);
105
136
 
106
137
  for (let c = 0; c < len; c++) {
107
- //let com = pathData[c];
108
- let values = pathData[c].values
138
+ let com = pathData[c];
139
+ let {values} = com
140
+ //let values = pathData[c].values
109
141
  let valLen = values.length;
110
142
  if (!valLen) continue
111
143
 
112
144
  for (let v = 0; v < valLen; v++) {
113
- //pathData[c].values[v] = +values[v].toFixed(decimals);
114
145
  pathData[c].values[v] = roundTo(values[v], decimals);
115
146
  }
116
147
  };
@@ -3,6 +3,8 @@
3
3
  * transform property object
4
4
  */
5
5
 
6
+ import { deg2rad } from "../constants";
7
+
6
8
  export function parseTransform(transformString, transformOrigin = { x: 0, y: 0 }) {
7
9
 
8
10
  //let regex = /(\w+)\(([^)]+)\)/g;
@@ -18,12 +20,12 @@ export function parseTransform(transformString, transformOrigin = { x: 0, y: 0 }
18
20
  console.log('trans', prop, vals);
19
21
 
20
22
  // rotate has origin
21
- if(prop==='rotate' && vals.length===3){
22
- transforms.push({prop:'translate', values:[vals[1], vals[2]]})
23
- transforms.push({prop:'rotate', values:[vals[0]]})
24
- transforms.push({prop:'translate', values:[-vals[1], -vals[2]]})
25
- }else{
26
- transforms.push({prop, values:[vals[0]]})
23
+ if (prop === 'rotate' && vals.length === 3) {
24
+ transforms.push({ prop: 'translate', values: [vals[1], vals[2]] })
25
+ transforms.push({ prop: 'rotate', values: [vals[0]] })
26
+ transforms.push({ prop: 'translate', values: [-vals[1], -vals[2]] })
27
+ } else {
28
+ transforms.push({ prop, values: [vals[0]] })
27
29
  }
28
30
  })
29
31
 
@@ -89,8 +91,7 @@ export function parseCSSTransform(transformString, transformOrigin = { x: 0, y:
89
91
  break;
90
92
 
91
93
  case 'rotate':
92
-
93
- console.log('rotate', values);
94
+ //console.log('rotate', values);
94
95
 
95
96
  transformOptions.transforms.push({ rotate: [0, 0, values[0] || 0] });
96
97
  break;
@@ -139,6 +140,116 @@ export function getMatrix({
139
140
 
140
141
 
141
142
 
143
+
144
+ export function getMatrixFromTransform(transformations = []) {
145
+
146
+ //console.log('getMatrix2D', transformations, origin);
147
+ // Helper function to multiply two 2D matrices
148
+
149
+ const multiply = (m1, m2) => {
150
+ let mtxN = {
151
+ a: m1.a * m2.a + m1.c * m2.b,
152
+ b: m1.b * m2.a + m1.d * m2.b,
153
+ c: m1.a * m2.c + m1.c * m2.d,
154
+ d: m1.b * m2.c + m1.d * m2.d,
155
+ e: m1.a * m2.e + m1.c * m2.f + m1.e,
156
+ f: m1.b * m2.e + m1.d * m2.f + m1.f
157
+ }
158
+
159
+ //console.log('m1', m1, 'm2', m2, 'mtxN', mtxN);
160
+ return mtxN;
161
+ };
162
+
163
+
164
+ // Helper function to create a translation matrix
165
+ const translationMatrix = (x, y) => {
166
+ let mtx ={a: 1, b: 0, c: 0, d: 1, e: x, f: y}
167
+ return mtx
168
+ };
169
+
170
+ // Helper function to create a scaling matrix
171
+ const scalingMatrix = (x, y) => ({
172
+ a: x, b: 0, c: 0, d: y, e: 0, f: 0
173
+ });
174
+
175
+
176
+ // get skew or rotation axis matrix
177
+ const angleMatrix = (angles, type) => {
178
+ //const toRad = (angle) => angle * Math.PI / 180;
179
+ let [angleX, angleY=0] = angles.map(ang => ang*deg2rad)
180
+ let m = {}
181
+ //console.log('angles', angles);
182
+
183
+ if (type === 'rot') {
184
+ let cosX = Math.cos(angleX), sinX = Math.sin(angleX);
185
+ m = { a: cosX, b: sinX, c: -sinX, d: cosX, e: 0, f: 0 }
186
+ } else if (type === 'skew') {
187
+ let tanX = Math.tan(angleX), tanY = Math.tan(angleY);
188
+ m = {
189
+ a: 1, b: tanY, c: tanX, d: 1, e: 0, f: 0
190
+ };
191
+ }
192
+ return m
193
+ };
194
+
195
+
196
+ // Start with an identity matrix
197
+ let matrix = { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 };
198
+
199
+
200
+ // Process transformations in the provided order (right-to-left)
201
+ for (let i = 0; i < transformations.length; i++) {
202
+
203
+ let transform = transformations[i];
204
+
205
+ // Get the transformation type (e.g., "translate")
206
+ let type = Object.keys(transform)[0];
207
+
208
+ let values = transform[type];
209
+ //console.log('transform', transform, type, values);
210
+
211
+ let [x, y] = values
212
+
213
+
214
+ switch (type) {
215
+ case "matrix":
216
+ let keys = ['a', 'b', 'c', 'd', 'e', 'f'];
217
+ let obj = Object.fromEntries(keys.map((key, i) => [key, values[i]]));
218
+ //console.log('mtx',obj );
219
+ matrix = multiply(matrix, obj);
220
+ break;
221
+ case "translate":
222
+ matrix = multiply(matrix, translationMatrix(x, y));
223
+ break;
224
+ case "skew":
225
+ matrix = multiply(matrix, angleMatrix([x, y], 'skew'));
226
+ break;
227
+ case "rotate":
228
+ matrix = multiply(matrix, angleMatrix([x], 'rot'));
229
+ //console.log('rot', matrix);
230
+ break;
231
+ case "scale":
232
+ matrix = multiply(matrix, scalingMatrix(x, y));
233
+ break;
234
+
235
+ default:
236
+ throw new Error(`Unknown transformation type: ${type}`);
237
+ }
238
+
239
+
240
+ //let mtxTmp = JSON.parse(JSON.stringify(matrix))
241
+ //console.log('??? mtxTmp', type, mtxTmp, 'transform', transform);
242
+ }
243
+
244
+
245
+ //console.log('matrix2D', matrix);
246
+ return matrix;
247
+ }
248
+
249
+
250
+
251
+
252
+
142
253
  export function getMatrix2D(transformations = [], origin = { x: 0, y: 0 }) {
143
254
 
144
255
  //console.log('getMatrix2D', transformations, origin);
@@ -4,7 +4,7 @@
4
4
  * used to remove unnecessary attribution
5
5
  */
6
6
 
7
- const shapeEls = [
7
+ export const shapeEls = [
8
8
  "polygon",
9
9
  "polyline",
10
10
  "line",
@@ -13,16 +13,36 @@ const shapeEls = [
13
13
  "ellipse",
14
14
  ]
15
15
 
16
- const geometryEls = [
16
+ export const horizontalProps = ['x', 'cx', 'rx', 'dx', 'width', 'translateX'];
17
+ export const verticalProps = ['y', 'cy', 'ry', 'dy', 'height', 'translateY'];
18
+ export const transHorizontal = ['scaleX', 'translateX', 'skewX'];
19
+ export const transVertical = ['scaleY', 'translateY', 'skewY'];
20
+
21
+ export const colorProps = ['fill', 'stroke', 'stop-color'];
22
+
23
+
24
+ export const geometryEls = [
17
25
  "path",
18
26
  ...shapeEls
19
- ]
27
+ ];
28
+
29
+ export const renderedEls = [
30
+ "text",
31
+ "textPath",
32
+ "tspan",
33
+ ...geometryEls
34
+ ];
20
35
 
21
- const textEls = [
36
+ export const textEls = [
22
37
  "textPath",
23
38
  "text",
24
39
  "tspan",
25
- ]
40
+ ];
41
+
42
+
43
+
44
+ export const strokeAtts = ['stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin','stroke-linecap', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-miterlimit', 'stroke-opacity' ];
45
+
26
46
 
27
47
  export const attLookup = {
28
48
 
@@ -301,9 +321,9 @@ export const attLookup = {
301
321
  "color": ["black", "rgb(0, 0, 0)", "rgba(0, 0, 0, 0)", "#000", "#000000"],
302
322
 
303
323
  stroke: ["none"],
304
- "stroke-width": ["1", "1px"],
305
324
  opacity: ["1"],
306
325
  "fill-opacity": ["1"],
326
+ "stroke-width": ["1", "1px"],
307
327
  "stroke-opacity": ["1"],
308
328
  "stroke-linecap": ["butt"],
309
329
  "stroke-miterlimit": ["4"],