svg-path-simplify 0.2.4 → 0.2.7

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.
@@ -162,7 +162,7 @@
162
162
  * http://jsfiddle.net/justin_c_rounds/Gd2S2/light/
163
163
  */
164
164
 
165
- function checkLineIntersection(p1 = null, p2 = null, p3 = null, p4 = null, exact = true, debug = false) {
165
+ function checkLineIntersection(p1 = null, p2 = null, p3 = null, p4 = null, exact = true, respectDirection = false, debug = false) {
166
166
  // if the lines intersect, the result contains the x and y of the intersection (treating the lines as infinite) and booleans for whether line segment 1 or line segment 2 contain the point
167
167
  let denominator, a, b, numerator1, numerator2;
168
168
  let intersectionPoint = {};
@@ -174,7 +174,9 @@
174
174
 
175
175
  try {
176
176
  denominator = ((p4.y - p3.y) * (p2.x - p1.x)) - ((p4.x - p3.x) * (p2.y - p1.y));
177
- if (denominator == 0) {
177
+
178
+ // parallel or colinear
179
+ if (denominator === 0) {
178
180
  return false;
179
181
  }
180
182
  } catch {
@@ -203,8 +205,13 @@
203
205
 
204
206
  }
205
207
 
206
- if (exact && !intersection) {
208
+ // direction
209
+ if (!exact && respectDirection && ((a > 0 && b < 0) || (a < 0 && b > 0))) {
210
+ intersection = false;
211
+ return false
212
+ }
207
213
 
214
+ if (exact && !intersection) {
208
215
  return false;
209
216
  }
210
217
 
@@ -213,6 +220,44 @@
213
220
  return intersectionPoint;
214
221
  }
215
222
 
223
+ /** Get relationship between a point and a polygon using ray-casting algorithm
224
+ * based on timepp's answer
225
+ * https://stackoverflow.com/questions/217578/how-can-i-determine-whether-a-2d-point-is-within-a-polygon#63436180
226
+ */
227
+ function isPointInPolygon(pt, polygon, bb, skipBB = false) {
228
+ const between = (p, a, b) => (p >= a && p <= b) || (p <= a && p >= b);
229
+ let inside = false;
230
+
231
+ // not in bbox - quit || no bbox defined
232
+ if (!skipBB || !bb.bottom) {
233
+ if (bb.left > pt.x || bb.top > pt.y || bb.bottom < pt.y || bb.right < pt.x) {
234
+ return false;
235
+ }
236
+ }
237
+
238
+ let l=polygon.length;
239
+ for (let i = l - 1, j = 0; j < l; i = j, j++) {
240
+ const A = polygon[i];
241
+ const B = polygon[j];
242
+ // corner cases
243
+ if ((pt.x == A.x && pt.y == A.y) || (pt.x == B.x && pt.y == B.y))
244
+ return true;
245
+ if (A.y == B.y && pt.y == A.y && between(pt.x, A.x, B.x)) return true;
246
+ if (between(pt.y, A.y, B.y)) {
247
+ /**
248
+ * if pt inside the vertical range filter out "ray pass vertex" problem
249
+ * by treating the line a little lower
250
+ */
251
+ if ((pt.y == A.y && B.y >= A.y) || (pt.y == B.y && A.y >= B.y)) continue;
252
+ // calc cross product `ptA X ptB`, pt lays on left side of AB if c > 0
253
+ const c = (A.x - pt.x) * (B.y - pt.y) - (B.x - pt.x) * (A.y - pt.y);
254
+ if (c == 0) return true;
255
+ if (A.y < B.y == c > 0) inside = !inside;
256
+ }
257
+ }
258
+ return inside ? true : false;
259
+ }
260
+
216
261
  /**
217
262
  * Linear interpolation (LERP) helper
218
263
  */
@@ -2372,6 +2417,7 @@
2372
2417
  }
2373
2418
 
2374
2419
  let dim_min = dims.sort();
2420
+
2375
2421
  let sliceIdx = Math.ceil(dim_min.length / 8);
2376
2422
  dim_min = dim_min.slice(0, sliceIdx);
2377
2423
  let minVal = dim_min.reduce((a, b) => a + b, 0) / sliceIdx;
@@ -2475,6 +2521,7 @@
2475
2521
  (type === 'C' ? [p0, cp1, cp2, p] : [p0, cp1, p]) :
2476
2522
  ([p0, p]);
2477
2523
  let thresholdLength = dimA * 0.1;
2524
+ let threshold = thresholdLength*0.01;
2478
2525
 
2479
2526
  // bezier types
2480
2527
  let isBezier = type === 'Q' || type === 'C';
@@ -2491,10 +2538,12 @@
2491
2538
  let dx = type === 'C' ? Math.abs(com.cp2.x - com.p.x) : Math.abs(com.cp1.x - com.p.x);
2492
2539
  let dy = type === 'C' ? Math.abs(com.cp2.y - com.p.y) : Math.abs(com.cp1.y - com.p.y);
2493
2540
 
2494
- let horizontal = dy === 0 && dx > 0;
2495
- let vertical = dx === 0 && dy > 0;
2541
+ let horizontal = (dy === 0 || dy<threshold ) && dx > 0;
2542
+ let vertical = (dx === 0 || dx<threshold ) && dy > 0;
2496
2543
 
2497
- if (horizontal || vertical) hasExtremes = true;
2544
+ if (horizontal || vertical) {
2545
+ hasExtremes = true;
2546
+ }
2498
2547
 
2499
2548
  // is extreme relative to bounding box
2500
2549
  if ((p.x === left || p.y === top || p.x === right || p.y === bottom)) {
@@ -2506,7 +2555,7 @@
2506
2555
  let couldHaveExtremes = bezierhasExtreme(null, commandPts);
2507
2556
  if (couldHaveExtremes) {
2508
2557
  let tArr = getTatAngles(commandPts);
2509
- if (tArr.length && (tArr[0] > 0.15)) {
2558
+ if (tArr.length && (tArr[0] > 0.2)) {
2510
2559
  hasExtremes = true;
2511
2560
  }
2512
2561
  }
@@ -3110,7 +3159,7 @@
3110
3159
  let dx1 = (p0.x - cpPrev.x);
3111
3160
  let dy1 = (p0.y - cpPrev.y);
3112
3161
 
3113
- maxDist = getDistManhattan(cpPrev, cpFirst) * 0.05;
3162
+ maxDist = getDistManhattan(cpPrev, cpFirst) * 0.025;
3114
3163
 
3115
3164
  // reflected cp
3116
3165
  let cpR = { x: cpPrev.x + dx1 * 2, y: cpPrev.y + dy1 * 2 };
@@ -3898,12 +3947,14 @@
3898
3947
  let attributes = [...el.attributes].map(att => att.name);
3899
3948
 
3900
3949
  let pathN = document.createElementNS('http://www.w3.org/2000/svg', 'path');
3950
+
3901
3951
  pathN.setAttribute('d', d);
3902
3952
 
3903
- let exclude = ['x', 'y', 'cx', 'cy', 'dx', 'dy', 'r', 'rx', 'ry', 'width', 'height', 'points'];
3953
+ let exclude = ['x', 'y', 'x1', 'y1', 'x2', 'y2', 'cx', 'cy', 'dx', 'dy', 'r', 'rx', 'ry', 'width', 'height', 'points'];
3904
3954
 
3905
3955
  attributes.forEach(att => {
3906
3956
  if (!exclude.includes(att)) {
3957
+
3907
3958
  let val = el.getAttribute(att);
3908
3959
  pathN.setAttribute(att, val);
3909
3960
  }
@@ -3917,7 +3968,7 @@
3917
3968
  function getPathDataFromEl(el, stringify = false) {
3918
3969
 
3919
3970
  let pathData = [];
3920
- let type = el.nodeName;
3971
+ let type = el.nodeName.toLowerCase();
3921
3972
  let atts, attNames, d, x, y, width, height, r, rx, ry, cx, cy, x1, x2, y1, y2;
3922
3973
 
3923
3974
  // convert relative or absolute units
@@ -4540,6 +4591,125 @@
4540
4591
  return pathData;
4541
4592
  }
4542
4593
 
4594
+ /**
4595
+ * reverse pathdata
4596
+ * make sure all command coordinates are absolute and
4597
+ * shorthands are converted to long notation
4598
+ */
4599
+ function reversePathData(pathData, {
4600
+ arcToCubic = false,
4601
+ quadraticToCubic = false,
4602
+ toClockwise = false,
4603
+ returnD = false
4604
+ } = {}) {
4605
+
4606
+ /**
4607
+ * Add closing lineto:
4608
+ * needed for path reversing or adding points
4609
+ */
4610
+ const addClosePathLineto = (pathData) => {
4611
+ let closed = pathData[pathData.length - 1].type.toLowerCase() === "z";
4612
+ let M = pathData[0];
4613
+ let [x0, y0] = [M.values[0], M.values[1]];
4614
+ let lastCom = closed ? pathData[pathData.length - 2] : pathData[pathData.length - 1];
4615
+ let [xE, yE] = [lastCom.values[lastCom.values.length - 2], lastCom.values[lastCom.values.length - 1]];
4616
+
4617
+ if (closed && (x0 != xE || y0 != yE)) {
4618
+
4619
+ pathData.pop();
4620
+ pathData.push(
4621
+ {
4622
+ type: "L",
4623
+ values: [x0, y0]
4624
+ },
4625
+ {
4626
+ type: "Z",
4627
+ values: []
4628
+ }
4629
+ );
4630
+ }
4631
+ return pathData;
4632
+ };
4633
+
4634
+ // helper to rearrange control points for all command types
4635
+ const reverseControlPoints = (type, values) => {
4636
+ let controlPoints = [];
4637
+ let endPoints = [];
4638
+ if (type !== "A") {
4639
+ for (let p = 0; p < values.length; p += 2) {
4640
+ controlPoints.push([values[p], values[p + 1]]);
4641
+ }
4642
+ endPoints = controlPoints.pop();
4643
+ controlPoints.reverse();
4644
+ }
4645
+ // is arc
4646
+ else {
4647
+
4648
+ let sweep = values[4] == 0 ? 1 : 0;
4649
+ controlPoints = [values[0], values[1], values[2], values[3], sweep];
4650
+ endPoints = [values[5], values[6]];
4651
+ }
4652
+ return { controlPoints, endPoints };
4653
+ };
4654
+
4655
+ // start compiling new path data
4656
+ let pathDataNew = [];
4657
+
4658
+ let closed =
4659
+ pathData[pathData.length - 1].type.toLowerCase() === "z" ? true : false;
4660
+ if (closed) {
4661
+ // add lineto closing space between Z and M
4662
+ pathData = addClosePathLineto(pathData);
4663
+ // remove Z closepath
4664
+ pathData.pop();
4665
+ }
4666
+
4667
+ // define last point as new M if path isn't closed
4668
+ let valuesLast = pathData[pathData.length - 1].values;
4669
+ let valuesLastL = valuesLast.length;
4670
+ let M = closed
4671
+ ? pathData[0]
4672
+ : {
4673
+ type: "M",
4674
+ values: [valuesLast[valuesLastL - 2], valuesLast[valuesLastL - 1]]
4675
+ };
4676
+ // starting M stays the same – unless the path is not closed
4677
+ pathDataNew.push(M);
4678
+
4679
+ // reverse path data command order for processing
4680
+ pathData.reverse();
4681
+ for (let i = 1; i < pathData.length; i++) {
4682
+ let com = pathData[i];
4683
+ let type = com.type;
4684
+ let values = com.values;
4685
+ let comPrev = pathData[i - 1];
4686
+ let typePrev = comPrev.type;
4687
+ let valuesPrev = comPrev.values;
4688
+
4689
+ // get reversed control points and new end coordinates
4690
+ let controlPointsPrev = reverseControlPoints(typePrev, valuesPrev).controlPoints;
4691
+ let endPoints = reverseControlPoints(type, values).endPoints;
4692
+
4693
+ // create new path data
4694
+ let newValues = [];
4695
+ newValues = [controlPointsPrev, endPoints].flat();
4696
+ pathDataNew.push({
4697
+ type: typePrev,
4698
+ values: newValues.flat()
4699
+ });
4700
+ }
4701
+
4702
+ // add previously removed Z close path
4703
+ if (closed) {
4704
+ pathDataNew.push({
4705
+ type: "z",
4706
+ values: []
4707
+ });
4708
+ }
4709
+
4710
+ return pathDataNew;
4711
+ }
4712
+
4543
4713
  function simplifyRDP(pts, {
4544
4714
  quality = 0.9,
4545
4715
  width = 0,
@@ -6290,6 +6460,8 @@
6290
6460
  ...textEls,
6291
6461
  ],
6292
6462
 
6463
+ "fill-rule": ["svg", "g", "path", "polygon", "text", "textPath"],
6464
+
6293
6465
  opacity: [
6294
6466
  "svg",
6295
6467
  "g",
@@ -6654,26 +6826,30 @@
6654
6826
  removeUnused = true,
6655
6827
  stylesToAttributes = true,
6656
6828
  removePrologue = true,
6829
+ removeIds = false,
6830
+ removeClassNames = false,
6831
+ removeDimensions = false,
6657
6832
  fixHref = true,
6658
6833
  mergePaths = false,
6659
6834
  cleanupSVGAtts = true,
6660
6835
  removeNameSpaced = true,
6661
6836
  attributesToGroup = true,
6837
+ shapesToPaths = false,
6662
6838
  decimals = -1,
6663
6839
  excludedEls = [],
6664
6840
  } = {}) {
6665
6841
 
6666
- svgMarkup = cleanSvgPrologue(svgMarkup);
6842
+ attributesToGroup = cleanupSVGAtts ? true : false;
6667
6843
 
6668
6844
  // replace namespaced refs
6669
6845
  if (fixHref) svgMarkup = svgMarkup.replaceAll("xlink:href=", "href=");
6670
6846
 
6671
6847
  let svg = new DOMParser()
6672
-
6673
6848
  .parseFromString(svgMarkup, "text/html")
6674
6849
  .querySelector("svg");
6675
6850
 
6676
6851
  if (cleanupSVGAtts) {
6852
+
6677
6853
  let allowed = ['viewBox', 'xmlns', 'width', 'height', 'id', 'class', 'fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin'];
6678
6854
  removeExcludedAttribues(svg, allowed);
6679
6855
 
@@ -6685,11 +6861,22 @@
6685
6861
  let els = svg.querySelectorAll('*');
6686
6862
  let elProps = [];
6687
6863
 
6864
+ let geometryElements = ['polygon', 'polyline', 'line', 'rect', 'circle', 'ellipse'];
6865
+
6688
6866
  for (let i = 0; i < els.length; i++) {
6689
6867
  let el = els[i];
6690
6868
 
6691
6869
  let name = el.nodeName.toLowerCase();
6692
6870
 
6871
+ // convert shapes
6872
+ if (shapesToPaths && name !== 'path' && geometryElements.includes(name)) {
6873
+ let path = shapeElToPath(el);
6874
+ el.replaceWith(path);
6875
+ name = 'path';
6876
+ el = path;
6877
+
6878
+ }
6879
+
6693
6880
  // remove hidden elements
6694
6881
  let style = el.getAttribute('style') || '';
6695
6882
  let isHiddenByStyle = style ? style.trim().includes('display:none') : false;
@@ -6709,11 +6896,25 @@
6709
6896
  }
6710
6897
 
6711
6898
  // group styles
6712
-
6713
6899
  if (attributesToGroup || mergePaths) {
6714
6900
  moveAttributesToGroup(elProps, mergePaths);
6715
6901
  }
6716
6902
 
6903
+ if (removeDimensions) {
6904
+ svg.removeAttribute('width');
6905
+ svg.removeAttribute('height');
6906
+ }
6907
+
6908
+ if (removeClassNames || removeIds) {
6909
+ let att = removeClassNames ? 'class' : 'id';
6910
+ let selector = `[${att}]`;
6911
+ let els = svg.querySelectorAll(selector);
6912
+ svg.removeAttribute(att);
6913
+ els.forEach(el => {
6914
+ el.removeAttribute(att);
6915
+ });
6916
+ }
6917
+
6717
6918
  if (returnDom) return svg
6718
6919
  let markup = stringifySVG(svg);
6719
6920
 
@@ -6725,11 +6926,39 @@
6725
6926
  let combine = [[elProps[0]]];
6726
6927
  let idx = 0;
6727
6928
  let lastProps = '';
6728
- for (let i = 0; i < elProps.length; i++) {
6929
+ let l = elProps.length;
6930
+ let itemsWithProps = elProps.filter(item => item.propstr);
6931
+ let path0;
6932
+
6933
+ // merge paths without properties
6934
+ if (!itemsWithProps.length && mergePaths) {
6935
+ let item0 = elProps[0];
6936
+ path0 = item0.el;
6937
+ let dCombined = item0.propsFiltered.d;
6938
+
6939
+ for (let i = 1; i < l; i++) {
6940
+ let item = elProps[i];
6941
+ let path = item.el;
6942
+
6943
+ let d = item.propsFiltered.d;
6944
+ let isAbs = d.startsWith('M');
6945
+ let dAbs = isAbs ? d : parsePathDataString(d).pathData.map(com => `${com.type} ${com.values.join(' ')}`).join(' ');
6946
+
6947
+ dCombined += dAbs;
6948
+
6949
+ // delete path el
6950
+ path.remove();
6951
+ }
6952
+
6953
+ path0.setAttribute('d', dCombined);
6954
+ return
6955
+ }
6956
+
6957
+ // add to combine chunks
6958
+ for (let i = 0; i < l; i++) {
6729
6959
  let item = elProps[i];
6730
6960
  let props = item.propsFiltered;
6731
6961
  let propstr = [];
6732
-
6733
6962
  for (let prop in props) {
6734
6963
  if (prop !== 'd' && prop !== 'id') {
6735
6964
  propstr.push(`${prop}:${props[prop]}`);
@@ -6738,16 +6967,15 @@
6738
6967
  propstr = propstr.join('_');
6739
6968
  item.propstr = propstr;
6740
6969
 
6741
- if (propstr === lastProps) {
6970
+ if (l > 1 && propstr === lastProps) {
6742
6971
  combine[idx].push(item);
6743
6972
  } else {
6744
- if (combine[idx].length) {
6973
+ if (l > 1 && combine[idx].length) {
6745
6974
  combine.push([]);
6746
6975
  idx++;
6747
6976
  }
6748
6977
  }
6749
6978
  lastProps = propstr;
6750
-
6751
6979
  }
6752
6980
 
6753
6981
  // add att groups
@@ -6785,10 +7013,15 @@
6785
7013
  }
6786
7014
 
6787
7015
  if (mergePaths) {
6788
- let path0 = group[0].el;
7016
+ group = group.filter(Boolean);
7017
+ let l = group.length;
7018
+ // nothing to merge
7019
+ if (l === 1) return group[0].el;
7020
+
7021
+ path0 = group[0].el;
6789
7022
  let dCombined = group[0].propsFiltered.d;
6790
7023
 
6791
- for (let i = 1; i < group.length; i++) {
7024
+ for (let i = 1; i < l; i++) {
6792
7025
  let item = group[i];
6793
7026
  let path = item.el;
6794
7027
  let d = item.propsFiltered.d;
@@ -6813,20 +7046,6 @@
6813
7046
 
6814
7047
  }
6815
7048
 
6816
- function cleanSvgPrologue(svgString) {
6817
- return (
6818
- svgString
6819
- // Remove XML prologues like <?xml ... ?>
6820
- .replace(/<\?xml[\s\S]*?\?>/gi, "")
6821
- // Remove DOCTYPE declarations
6822
- .replace(/<!DOCTYPE[\s\S]*?>/gi, "")
6823
- // Remove comments <!-- ... -->
6824
- .replace(/<!--[\s\S]*?-->/g, "")
6825
- // Trim extra whitespace
6826
- .trim()
6827
- );
6828
- }
6829
-
6830
7049
  function removeExcludedAttribues(el, allowed = ['viewBox', 'xmlns', 'width', 'height', 'id', 'class']) {
6831
7050
  let atts = [...el.attributes].map((att) => att.name);
6832
7051
  atts.forEach((att) => {
@@ -6836,13 +7055,22 @@
6836
7055
  });
6837
7056
  }
6838
7057
 
6839
- function stringifySVG(svg) {
7058
+ function stringifySVG(svg, omitNamespace = false) {
6840
7059
  let markup = new XMLSerializer().serializeToString(svg);
7060
+
7061
+ if (omitNamespace) {
7062
+ markup = markup.replaceAll('xmlns="http://www.w3.org/2000/svg"', '');
7063
+ }
7064
+
6841
7065
  markup = markup
6842
7066
  .replace(/\t/g, "")
6843
7067
  .replace(/[\n\r|]/g, "\n")
6844
7068
  .replace(/\n\s*\n/g, '\n')
6845
- .replace(/ +/g, ' ');
7069
+ .replace(/ +/g, ' ')
7070
+
7071
+ .replace(/> </g, '><')
7072
+ .trim();
7073
+
6846
7074
 
6847
7075
  return markup
6848
7076
  }
@@ -7409,296 +7637,14 @@
7409
7637
  return viewBox
7410
7638
  }
7411
7639
 
7412
- function redrawPathData(pathData, {
7413
- tolerance = 1
7414
-
7415
- } = {}) {
7416
- let chunks = [];
7417
- let chunk = [];
7418
-
7419
- let l = pathData.length;
7420
-
7421
- for (let i = 1; i < l; i++) {
7422
- let com = pathData[i];
7423
- let { type, values, p0, cp1 = null, cp2 = null, p, extreme = null, semiExtreme = null, corner = null, directionChange } = com;
7424
-
7425
- let comN = pathData[i + 1] || null;
7426
-
7427
- /*
7428
- if (extreme || corner || semiExtreme || directionChange) {
7429
-
7430
- if (extreme) renderPoint(markers, com.p, 'cyan', '1%', '0.5')
7431
-
7432
- if (semiExtreme) renderPoint(markers, com.p, 'orange', '1%', '0.5')
7433
- if (corner) renderPoint(markers, com.p, 'magenta', '1.75%', '0.5')
7434
- }
7435
- */
7436
-
7437
- if (extreme || corner || (comN && comN.type !== type)) {
7438
- chunk.push(com);
7439
- chunks.push(chunk);
7440
- chunk = [];
7441
- continue
7442
- }
7443
-
7444
- chunk.push(com);
7445
-
7446
- }
7447
-
7448
- console.log('!!!chunks', chunks);
7449
-
7450
- renderChunks(chunks);
7451
-
7452
- // cleanup chunks
7453
-
7454
- let chunksLen = chunks.length;
7455
-
7456
-
7457
-
7458
- for (let c = 0; c < chunksLen; c++) {
7459
- let chunk = chunks[c];
7460
- let chunkN = chunks[c + 1] || null;
7461
-
7462
- let chunkLen = chunk.length;
7463
-
7464
- if (chunkLen === 1 && chunkN && chunkN[0].type === 'C') ;
7465
-
7466
- }
7467
-
7468
- chunks = chunks.filter(Boolean);
7469
-
7470
- // test render
7471
-
7472
- let pathDataC = [pathData[0]];
7473
-
7474
- /**
7475
- * combine chunk based
7476
- */
7477
- for (let c = 0; c < chunks.length; c++) {
7478
- let chunk = chunks[c];
7479
- let chunkLen = chunk.length;
7480
- let comChunk0 = chunk[0];
7481
- let comChunk1 = chunk[chunkLen - 1];
7482
- let thresh = getDistManhattan(comChunk0.p0, comChunk1.p) * 0.05;
7483
-
7484
- // commands in chunk
7485
- for (let i = 0, l = chunkLen; i < l; i++) {
7486
- let com = chunk[i];
7487
- chunk[i + 1];
7488
- chunk[l - 1];
7489
-
7490
- let isBezier = comChunk0.type === 'C' && comChunk1.type === 'C';
7491
-
7492
- let { type, values, p0, cp1 = null, cp2 = null, p = null, extreme, semiExtreme = null, corner = null } = com;
7493
-
7494
- let pI1 = null, pI2 = null;
7495
- let cp1_S = null, cp2_S = null;
7496
- let cp2_M = null;
7497
- let cp1_M = null;
7498
- let pathDataS = [];
7499
- let tSplit = 0.666;
7500
- let comMid = null;
7501
-
7502
- // 0. adjust Extreme cpts
7503
- if (isBezier) {
7504
- let dx1 = Math.abs(comChunk0.p0.x - comChunk0.cp1.x);
7505
- let dy1 = Math.abs(comChunk0.p0.y - comChunk0.cp1.y);
7506
- let dx2 = Math.abs(comChunk1.p.x - comChunk1.cp2.x);
7507
- let dy2 = Math.abs(comChunk1.p.y - comChunk1.cp2.y);
7508
-
7509
- let vertical1 = dx1 < thresh && dx1 < dy1;
7510
- let horizontal1 = dy1 < thresh && dx1 > dy1;
7511
-
7512
- let vertical2 = dx2 < thresh && dx2 < dy2;
7513
- let horizontal2 = dy2 < thresh && dx2 > dy2;
7514
-
7515
- if (horizontal1) comChunk0.cp1.y = comChunk0.p0.y;
7516
- if (horizontal2) comChunk1.cp2.y = comChunk1.p.y;
7517
- if (vertical1) comChunk0.cp1.x = comChunk0.p0.x;
7518
- if (vertical2) comChunk1.cp2.x = comChunk1.p.x;
7519
- }
7520
-
7521
- // test render - original pathdata
7522
- let pathDataChunk = [
7523
- { type: 'M', values: [com.p0.x, com.p0.y] },
7524
- { type, values },
7525
- ];
7526
-
7527
- pathDataToD(pathDataChunk);
7528
- // renderPath(markers, d, stroke, '1%', '0.5')
7529
- // continue
7530
- /*
7531
-
7532
- */
7533
-
7534
- // 1. only one command in chunk - nothing to simplify
7535
- if (chunkLen === 1 || type !== 'C') {
7536
- pathDataC.push(com);
7537
- }
7538
-
7539
- // 2. could be simplified
7540
- else {
7541
- // 2.1 has semi extreme - extrapolate
7542
- // 2.2 has sdirection change
7543
- let semiExtremes = chunk.filter(ch => ch.semiExtreme);
7544
- let comsDirectionChange = chunk.filter(ch => ch.directionChange);
7545
-
7546
- if (semiExtremes.length || comsDirectionChange.length) {
7547
-
7548
- // semiExtreme command
7549
- comMid = semiExtremes.length ? semiExtremes[0] : comsDirectionChange[0];
7550
-
7551
- // zero length cpt vectors
7552
- if (comChunk0.p0.x === comChunk0.cp1.x && comChunk0.p0.y === comChunk0.cp1.y) {
7553
- comChunk0.cp1 = pointAtT([comChunk0.p0, comChunk0.cp1, comChunk0.cp2, comChunk0.p], 0.5);
7554
- }
7555
- else if (comChunk1.p.x === comChunk1.cp2.x && comChunk1.p.y === comChunk1.cp2.y) {
7556
- comChunk1.cp2 = pointAtT([comChunk1.p0, comChunk1.cp1, comChunk1.cp2, comChunk1.p], 0.5);
7557
- }
7558
-
7559
- pI1 = checkLineIntersection(comMid.p, comMid.cp2, comChunk0.p0, comChunk0.cp1, false);
7560
- pI2 = checkLineIntersection(comMid.p, comMid.cp2, comChunk1.p, comChunk1.cp2, false);
7561
-
7562
- // intersections try to extrapolate cpts
7563
- if (pI1 && pI2) {
7564
-
7565
- cp1_S = pointAtT([comChunk0.p0, pI1], tSplit);
7566
- cp2_S = pointAtT([comChunk1.p, pI2], tSplit);
7567
-
7568
- cp2_M = pointAtT([comMid.p, pI1], tSplit);
7569
- cp1_M = pointAtT([comMid.p, pI2], tSplit);
7570
-
7571
- /*
7572
- renderPoint(markers, cp1_S, 'magenta', '1%', '1' )
7573
- */
7574
-
7575
- pathDataS = [
7576
- { type: 'M', values: [comChunk0.p0.x, comChunk0.p0.y] },
7577
- {
7578
- type: 'C', values: [
7579
- cp1_S.x, cp1_S.y,
7580
- cp2_M.x, cp2_M.y,
7581
- comMid.p.x,
7582
- comMid.p.y
7583
- ]
7584
- },
7585
- {
7586
- type: 'C', values: [
7587
- cp1_M.x, cp1_M.y,
7588
- cp2_S.x, cp2_S.y,
7589
- comChunk1.p.x,
7590
- comChunk1.p.y,
7591
- ]
7592
- },
7593
- ];
7594
- pathDataToD(pathDataS);
7595
-
7596
- pathDataC.push(
7597
- {
7598
- type: 'C', values: [
7599
- cp1_S.x, cp1_S.y,
7600
- cp2_M.x, cp2_M.y,
7601
- comMid.p.x,
7602
- comMid.p.y
7603
- ],
7604
- p0: comChunk0.p0,
7605
- cp1: cp1_S,
7606
- cp2: cp2_M,
7607
- p: comMid.p,
7608
- dimA: getDistManhattan(comChunk0.p0, comMid.p)
7609
- },
7610
-
7611
- {
7612
- type: 'C', values: [
7613
- cp1_M.x, cp1_M.y,
7614
- cp2_S.x, cp2_S.y,
7615
- comChunk1.p.x,
7616
- comChunk1.p.y,
7617
- ],
7618
- p0: comMid.p,
7619
- cp1: cp1_M,
7620
- cp2: cp2_S,
7621
- p: comChunk1.p,
7622
- extreme: true,
7623
- dimA: getDistManhattan(comMid.p, comChunk1.p)
7624
-
7625
- }
7626
- );
7627
- break
7628
-
7629
- }
7630
- } else {
7631
- pathDataC.push(com);
7632
- }
7633
-
7634
- }
7635
-
7636
- }
7637
-
7638
- }
7639
-
7640
- /*
7641
- // render
7642
- let d = pathDataToD(pathDataC)
7643
- console.log(d);
7644
- renderPath(markers, d, 'red', '1%', '0.5')
7645
- */
7646
-
7647
- return pathDataC
7648
-
7649
- }
7650
-
7651
- function renderChunks(chunks) {
7652
-
7653
- console.log('chunks', chunks);
7654
-
7655
- let stroke = 'green';
7656
-
7657
- /**
7658
- * combine chunk based
7659
- */
7660
- for (let c = 0; c < chunks.length; c++) {
7661
- let chunk = chunks[c];
7662
- let chunkLen = chunk.length;
7663
-
7664
- stroke = c % 2 === 0 ? 'orange' : 'green';
7665
- let comChunk0 = chunk[0];
7666
- let comChunk1 = chunk[chunkLen - 1];
7667
-
7668
- let pathDataChunk = [
7669
- { type: 'M', values: [comChunk0.p0.x, comChunk0.p0.y] }
7670
- ];
7671
-
7672
- // commands in chunk
7673
- for (let i = 0, l = chunkLen; i < l; i++) {
7674
- let com = chunk[i];
7675
- chunk[i + 1];
7676
- chunk[l - 1];
7677
- comChunk0.type === 'C' && comChunk1.type === 'C';
7678
-
7679
- let { type, values, p0, cp1 = null, cp2 = null, p = null, extreme, semiExtreme = null, corner = null } = com;
7680
-
7681
- // test render - original pathdata
7682
- pathDataChunk.push(
7683
- { type, values },
7684
- );
7685
-
7686
- }
7687
-
7688
- let d = pathDataToD(pathDataChunk);
7689
- renderPath(markers, d, stroke, '1%', '0.5');
7690
-
7691
- }
7692
- }
7693
-
7694
- function pathDataRevertCubicToQuadratic(pathData) {
7640
+ function pathDataRevertCubicToQuadratic(pathData, tolerance=1) {
7695
7641
 
7696
7642
  for (let c = 1, l = pathData.length; c < l; c++) {
7697
7643
  let com = pathData[c];
7698
7644
  let { type, values, p0, cp1 = null, cp2 = null, p = null } = com;
7699
7645
  if (type === 'C') {
7700
7646
 
7701
- let comQ = revertCubicQuadratic(p0, cp1, cp2, p);
7647
+ let comQ = revertCubicQuadratic(p0, cp1, cp2, p, tolerance);
7702
7648
  if (comQ.type === 'Q') {
7703
7649
  comQ.extreme = com.extreme;
7704
7650
  comQ.corner = com.corner;
@@ -8213,12 +8159,59 @@
8213
8159
  }
8214
8160
  */
8215
8161
 
8162
+ function getPathDataPolyPrecise(pathData = []) {
8163
+
8164
+ let poly = [];
8165
+ for (let i = 0; i < pathData.length; i++) {
8166
+ let com = pathData[i];
8167
+ let prev = i > 0 ? pathData[i - 1] : pathData[i];
8168
+ let { type, values } = com;
8169
+ let p0 = { x: prev.values[prev.values.length - 2], y: prev.values[prev.values.length - 1] };
8170
+ let p = values.length ? { x: values[values.length - 2], y: values[values.length - 1] } : '';
8171
+ let cp1 = values.length ? { x: values[0], y: values[1] } : '';
8172
+
8173
+ switch (type) {
8174
+
8175
+ // convert to cubic to get polygon
8176
+ case 'A':
8177
+ if (typeof arcToBezier$1 !== 'function') {
8178
+
8179
+ break;
8180
+ }
8181
+ let cubic = arcToBezier$1(p0, values);
8182
+ cubic.forEach(com => {
8183
+ let vals = com.values;
8184
+ let cp1 = { x: vals[0], y: vals[1] };
8185
+ let cp2 = { x: vals[2], y: vals[3] };
8186
+ let p = { x: vals[4], y: vals[5] };
8187
+ poly.push(cp1, cp2, p);
8188
+ });
8189
+ break;
8190
+
8191
+ case 'C':
8192
+ let cp2 = { x: values[2], y: values[3] };
8193
+ poly.push(cp1, cp2);
8194
+ break;
8195
+ case 'Q':
8196
+ poly.push(cp1);
8197
+ break;
8198
+ }
8199
+
8200
+ // M and L commands
8201
+ if (type.toLowerCase() !== 'z') {
8202
+ poly.push(p);
8203
+ }
8204
+ }
8205
+
8206
+ return poly;
8207
+ }
8208
+
8216
8209
  function pathDataToPolygon(pathData, {
8217
8210
  angles = [],
8218
8211
  split = 0,
8219
8212
  getPathData = true,
8220
- width=0,
8221
- height=0
8213
+ width = 0,
8214
+ height = 0
8222
8215
  } = {}) {
8223
8216
 
8224
8217
  let l = pathData.length;
@@ -8234,9 +8227,9 @@
8234
8227
 
8235
8228
  split = !split ? 1 : split;
8236
8229
 
8237
- if(width && height){
8238
- minLength = (width+height) * 0.025 / split;
8239
- }else {
8230
+ if (width && height) {
8231
+ minLength = (width + height) * 0.025 / split;
8232
+ } else {
8240
8233
 
8241
8234
  let lengths = pathData.map(com => com.dimA || 0).filter(Boolean).sort();
8242
8235
  minLength = lengths[0];
@@ -8262,8 +8255,8 @@
8262
8255
  split = Math.ceil(length / minLength);
8263
8256
 
8264
8257
  let tArr = [];
8265
- for(let i=1; i<split; i++){
8266
- tArr.push(1/split*i);
8258
+ for (let i = 1; i < split; i++) {
8259
+ tArr.push(1 / split * i);
8267
8260
  }
8268
8261
 
8269
8262
  tArr.forEach(t => {
@@ -8277,10 +8270,10 @@
8277
8270
  M = p;
8278
8271
  }
8279
8272
 
8280
- p.area = com.cptArea|| 0;
8281
- p.isExtreme = com.extreme|| false;
8282
- p.isCorner = com.corner|| false;
8283
- p.isDirChange = com.directionChange || false ;
8273
+ p.area = com.cptArea || 0;
8274
+ p.isExtreme = com.extreme || false;
8275
+ p.isCorner = com.corner || false;
8276
+ p.isDirChange = com.directionChange || false;
8284
8277
 
8285
8278
  // segment end point
8286
8279
  pts.push(p);
@@ -8294,7 +8287,7 @@
8294
8287
 
8295
8288
  // reduce poly vertices
8296
8289
 
8297
- pts = simplifyRD(pts, {quality:0.5, width, height});
8290
+ pts = simplifyRD(pts, { quality: 0.5, width, height });
8298
8291
 
8299
8292
  /*
8300
8293
  pts.forEach(pt => {
@@ -8307,6 +8300,103 @@
8307
8300
  return getPathData ? pathDataPoly : pts;
8308
8301
  }
8309
8302
 
8303
+ function pathDataLineToCubic(pathData) {
8304
+
8305
+ for (let c = 1, l = pathData.length; c < l; c++) {
8306
+ let com = pathData[c];
8307
+ let { type, values, p0, cp1 = null, cp2 = null, p = null } = com;
8308
+ if (type === 'L') {
8309
+
8310
+ let cp1 = interpolate(p0, p, 0.333);
8311
+ let cp2 = interpolate(p, p0, 0.333);
8312
+
8313
+ pathData[c].type = 'C';
8314
+ pathData[c].values = [cp1.x, cp1.y, cp2.x, cp2.y, p.x, p.y];
8315
+ pathData[c].cp1 = cp1;
8316
+ pathData[c].cp2 = cp2;
8317
+
8318
+ }
8319
+ }
8320
+ return pathData
8321
+ }
8322
+
8323
+ /**
8324
+ * fix sub path directions
8325
+ * pathdata must be be normalized to
8326
+ * absolute and longhand commands
8327
+ * toClockwise = force default direction
8328
+ */
8329
+
8330
+ function fixPathDataDirections(pathDataArr = [], toClockwise = false) {
8331
+
8332
+ let polys = [];
8333
+
8334
+ pathDataArr.forEach((sub, i) => {
8335
+ let pathData = sub.pathData;
8336
+
8337
+ let vertices = getPathDataPolyPrecise(pathData);
8338
+ let area = getPolygonArea(vertices);
8339
+ let isClockwise = area >= 0;
8340
+ polys.push({ pts: vertices, bb: getPolyBBox(vertices), cw: isClockwise, index: i, inter: 0, includes: [], includedIn: [] });
8341
+ });
8342
+
8343
+ // check poly intersections
8344
+ let l = polys.length;
8345
+ for (let i = 0; i < l; i++) {
8346
+ let prev = polys[i];
8347
+ let bb0 = prev.bb;
8348
+
8349
+ for (let j = 0; j < l; j++) {
8350
+
8351
+ let poly = polys[j];
8352
+ let bb = poly.bb;
8353
+
8354
+ // skip if the same poly or parent
8355
+ if (i === j || poly.includes.includes(i)) continue
8356
+
8357
+ // if mid point is in previous polygon
8358
+ let ptMid = { x: bb.left + bb.width / 2, y: bb.top + bb.height / 2 };
8359
+ let inPoly = isPointInPolygon(ptMid, prev.pts, bb0);
8360
+
8361
+ if (inPoly) {
8362
+ polys[j].inter += 1;
8363
+ poly.includedIn.push(i);
8364
+ prev.includes.push(j);
8365
+ }
8366
+ }
8367
+ }
8368
+
8369
+ // reverse paths
8370
+ for (let i = 0; i < l; i++) {
8371
+
8372
+ let poly = polys[i];
8373
+ let { cw, includedIn, includes } = poly;
8374
+
8375
+ // outer path direction to counter clockwise
8376
+ if (!includedIn.length && cw && !toClockwise
8377
+ || !includedIn.length && !cw && toClockwise
8378
+ ) {
8379
+ pathDataArr[i].pathData = reversePathData(pathDataArr[i].pathData);
8380
+ polys[i].cw = polys[i].cw ? false : true;
8381
+ cw = polys[i].cw;
8382
+ }
8383
+
8384
+ // reverse inner sub paths
8385
+ for (let j = 0; j < includes.length; j++) {
8386
+ let ind = includes[j];
8387
+ let child = polys[ind];
8388
+
8389
+ if (child.cw === cw) {
8390
+ pathDataArr[ind].pathData = reversePathData(pathDataArr[ind].pathData);
8391
+ polys[ind].cw = polys[ind].cw ? false : true;
8392
+ }
8393
+ }
8394
+ }
8395
+
8396
+ return pathDataArr
8397
+
8398
+ }
8399
+
8310
8400
  function svgPathSimplify(input = '', {
8311
8401
 
8312
8402
  // return svg markup or object
@@ -8334,6 +8424,12 @@
8334
8424
 
8335
8425
  refineExtremes = true,
8336
8426
  simplifyCorners = false,
8427
+ removeDimensions = false,
8428
+ removeIds = false,
8429
+ removeClassNames = false,
8430
+ omitNamespace = false,
8431
+
8432
+ fixDirections = false,
8337
8433
 
8338
8434
  keepExtremes = true,
8339
8435
  keepCorners = true,
@@ -8347,7 +8443,6 @@
8347
8443
  toPolygon = false,
8348
8444
 
8349
8445
  removeOrphanSubpaths = false,
8350
-
8351
8446
  simplifyRound = false,
8352
8447
 
8353
8448
  scale = 1,
@@ -8355,32 +8450,28 @@
8355
8450
  crop = false,
8356
8451
  alignToOrigin = false,
8357
8452
 
8358
- // svg path optimizations
8359
8453
  decimals = 3,
8360
8454
  autoAccuracy = true,
8361
8455
 
8362
8456
  minifyD = 0,
8363
8457
  tolerance = 1,
8364
- reverse = false,
8458
+ reversePath = false,
8365
8459
 
8366
- // svg cleanup options
8367
- cleanupSVGAtts=true,
8460
+ cleanupSVGAtts = true,
8368
8461
  removePrologue = true,
8369
8462
  stylesToAttributes = true,
8370
8463
  fixHref = true,
8371
- removeNameSpaced=true,
8372
- attributesToGroup=false,
8464
+ removeNameSpaced = true,
8465
+ attributesToGroup = false,
8373
8466
  mergePaths = false,
8374
8467
  removeHidden = true,
8375
8468
  removeUnused = true,
8376
- shapesToPaths = true,
8469
+ shapesToPaths = false,
8470
+ lineToCubic = false,
8377
8471
 
8378
8472
  tMin = 0,
8379
8473
  tMax = 1,
8380
8474
 
8381
- // redraw - for messed up paths
8382
- redraw = false,
8383
-
8384
8475
  } = {}) {
8385
8476
 
8386
8477
  // clamp tolerance and scale
@@ -8438,14 +8529,16 @@
8438
8529
  else {
8439
8530
 
8440
8531
  let returnDom = true;
8441
- svg = cleanUpSVG(input, { cleanupSVGAtts, returnDom, removeHidden, removeUnused, removeNameSpaced, attributesToGroup, stylesToAttributes, removePrologue, fixHref ,mergePaths }
8532
+ svg = cleanUpSVG(input, { removeIds, removeClassNames, removeDimensions, cleanupSVGAtts, returnDom, removeHidden, removeUnused, removeNameSpaced, stylesToAttributes, removePrologue, fixHref, mergePaths, shapesToPaths }
8442
8533
  );
8443
8534
 
8444
8535
  if (shapesToPaths) {
8445
8536
  let shapes = svg.querySelectorAll('polygon, polyline, line, rect, circle, ellipse');
8446
8537
  shapes.forEach(shape => {
8447
8538
  let path = shapeElToPath(shape);
8539
+
8448
8540
  shape.replaceWith(path);
8541
+
8449
8542
  });
8450
8543
  }
8451
8544
 
@@ -8598,23 +8691,6 @@
8598
8691
  // remove zero length linetos
8599
8692
  if (removeColinear || removeZeroLength) pathDataSub = removeZeroLengthLinetos(pathDataSub);
8600
8693
 
8601
- /**
8602
- * try to redraw messed up paths
8603
- * based on significant points suchas
8604
- * extremes, semi-extremes and corners
8605
- */
8606
- if (redraw) {
8607
- addExtremes = true;
8608
- addSemiExtremes = true;
8609
- simplifyCorners = false;
8610
- keepCorners = true;
8611
- keepExtremes = true;
8612
- optimizeOrder = true;
8613
-
8614
- tMin = 0;
8615
- tMax = 0;
8616
- }
8617
-
8618
8694
  // sort to top left
8619
8695
  if (optimizeOrder) pathDataSub = pathDataToTopLeft(pathDataSub);
8620
8696
 
@@ -8624,6 +8700,11 @@
8624
8700
  if (addExtremes || addSemiExtremes) pathDataSub = addExtremePoints(pathDataSub,
8625
8701
  { tMin, tMax, addExtremes, addSemiExtremes, angles: [30] });
8626
8702
 
8703
+ // reverse
8704
+ if(reversePath) {
8705
+ pathDataSub = reversePathData(pathDataSub);
8706
+ }
8707
+
8627
8708
  // analyze pathdata to add info about signicant properties such as extremes, corners
8628
8709
  let pathDataPlus = analyzePathData(pathDataSub, {
8629
8710
  detectSemiExtremes: addSemiExtremes,
@@ -8636,7 +8717,7 @@
8636
8717
 
8637
8718
  if (refineClosing) pathData = refineClosingCommand(pathData, { threshold: dimA * 0.001 });
8638
8719
 
8639
- pathData = simplifyBezier ? simplifyPathDataCubic(pathData, { simplifyBezier, keepInflections, keepExtremes, keepCorners, extrapolateDominant, revertToQuadratics, tolerance, reverse }) : pathData;
8720
+ pathData = simplifyBezier ? simplifyPathDataCubic(pathData, { simplifyBezier, keepInflections, keepExtremes, keepCorners, extrapolateDominant, revertToQuadratics, tolerance }) : pathData;
8640
8721
 
8641
8722
  // refine extremes
8642
8723
  if (refineExtremes) {
@@ -8645,29 +8726,6 @@
8645
8726
  pathData = refineAdjacentExtremes(pathData, { threshold: thresholdEx, tolerance });
8646
8727
  }
8647
8728
 
8648
- /**
8649
- * try redrawing
8650
- */
8651
-
8652
- if (redraw) {
8653
-
8654
- /*
8655
- pathData = addExtremePoints(pathData,
8656
- { tMin: 0, tMax: 1, addExtremes: true, addSemiExtremes: true })
8657
-
8658
- pathData = analyzePathData(pathDataSub, {
8659
- detectSemiExtremes: true,
8660
- detectExtremes: true,
8661
- }).pathData;
8662
-
8663
- */
8664
-
8665
- (bb.width + bb.height) * 0.1;
8666
-
8667
- pathData = redrawPathData(pathData, { tolerance, threshold: dimA * 0.001 });
8668
-
8669
- }
8670
-
8671
8729
  // cubic to arcs
8672
8730
  if (cubicToArc) pathData = pathDataCubicsToArc(pathData, { areaThreshold: 2.5 });
8673
8731
 
@@ -8687,13 +8745,14 @@
8687
8745
  if (simplifyRound) pathData = refineRoundSegments(pathData);
8688
8746
 
8689
8747
  // simplify to quadratics
8690
- if (revertToQuadratics) pathData = pathDataRevertCubicToQuadratic(pathData);
8748
+ if (revertToQuadratics) pathData = pathDataRevertCubicToQuadratic(pathData, tolerance);
8749
+
8750
+ if (lineToCubic) pathData = pathDataLineToCubic(pathData);
8691
8751
 
8692
8752
  // optimize close path
8693
8753
  if (optimizeOrder) pathData = optimizeClosePath(pathData, { autoClose });
8694
8754
 
8695
8755
  // update
8696
-
8697
8756
  pathDataPlusArr.push({ pathData, bb });
8698
8757
 
8699
8758
  } // end sup paths
@@ -8712,6 +8771,11 @@
8712
8771
  pathDataPlusArr = isPortrait ? pathDataPlusArr.sort((a, b) => a.bb.y - b.bb.y || a.bb.x - b.bb.x) : pathDataPlusArr.sort((a, b) => a.bb.x - b.bb.x || a.bb.y - b.bb.y);
8713
8772
  }
8714
8773
 
8774
+ // fix path directions
8775
+ if (fixDirections) {
8776
+ pathDataPlusArr = fixPathDataDirections(pathDataPlusArr);
8777
+ }
8778
+
8715
8779
  // flatten compound paths
8716
8780
  pathData = [];
8717
8781
  pathDataPlusArr.forEach(sub => {
@@ -8721,11 +8785,10 @@
8721
8785
  if (autoAccuracy) {
8722
8786
  decimals = detectAccuracy(pathData);
8723
8787
  pathOptions.decimals = decimals;
8724
-
8725
8788
  }
8726
8789
 
8727
8790
  // collect for merged svg paths
8728
- mergePaths= false;
8791
+ mergePaths = false;
8729
8792
  if (el && mergePaths) {
8730
8793
  pathData_merged.push(...pathData);
8731
8794
  }
@@ -8828,7 +8891,16 @@
8828
8891
  }
8829
8892
  }
8830
8893
 
8831
- svg = stringifySVG(svg);
8894
+ // remove fill rules
8895
+ if (fixDirections) {
8896
+ let elsFill = svg.querySelectorAll('path[fill-rule], path[clip-rule]');
8897
+ elsFill.forEach(el => {
8898
+ el.removeAttribute('fill-rule');
8899
+ el.removeAttribute('clip-rule');
8900
+ });
8901
+ }
8902
+
8903
+ svg = stringifySVG(svg, omitNamespace);
8832
8904
 
8833
8905
  svgSizeOpt = svg.length;
8834
8906
 
@@ -8872,6 +8944,9 @@
8872
8944
 
8873
8945
  import {parsePathDataString_plus} from './svgii/pathData_parse2';
8874
8946
  export {parsePathDataString_plus as parsePathDataString_plus}
8947
+
8948
+ import {getPathDataFromEl} from './svgii/pathData_parse_els';
8949
+ export{getPathDataFromEl as getPathDataFromEl};
8875
8950
  */
8876
8951
 
8877
8952
  // IIFE