svg-path-simplify 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +6 -4
  3. package/dist/svg-path-simplify.esm.js +2450 -888
  4. package/dist/svg-path-simplify.esm.min.js +2 -2
  5. package/dist/svg-path-simplify.js +2450 -888
  6. package/dist/svg-path-simplify.min.js +2 -2
  7. package/dist/svg-path-simplify.pathdata.esm.js +167 -85
  8. package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
  9. package/docs/privacy-webapp.md +24 -0
  10. package/index.html +333 -132
  11. package/package.json +5 -2
  12. package/src/css_parse.js +317 -0
  13. package/src/detect_input.js +34 -4
  14. package/src/pathData_simplify_harmonize_cpts.js +77 -1
  15. package/src/pathSimplify-main.js +246 -262
  16. package/src/pathSimplify-presets.js +243 -0
  17. package/src/poly-fit-curve-schneider.js +14 -7
  18. package/src/simplify_poly_RC.js +102 -0
  19. package/src/simplify_poly_RDP.js +109 -1
  20. package/src/simplify_poly_radial_distance.js +3 -3
  21. package/src/string_helpers.js +144 -0
  22. package/src/svgii/convert_units.js +8 -2
  23. package/src/svgii/geometry.js +182 -3
  24. package/src/svgii/geometry_length.js +237 -0
  25. package/src/svgii/pathData_convert.js +43 -1
  26. package/src/svgii/pathData_fix_directions.js +6 -0
  27. package/src/svgii/pathData_fromPoly.js +3 -3
  28. package/src/svgii/pathData_getLength.js +86 -0
  29. package/src/svgii/pathData_parse.js +2 -0
  30. package/src/svgii/pathData_parse_els.js +189 -189
  31. package/src/svgii/pathData_split_to_groups.js +168 -0
  32. package/src/svgii/pathData_stringify.js +26 -64
  33. package/src/svgii/pathData_toPolygon.js +3 -4
  34. package/src/svgii/poly_analyze.js +61 -0
  35. package/src/svgii/poly_normalize.js +11 -2
  36. package/src/svgii/poly_to_pathdata.js +85 -24
  37. package/src/svgii/rounding.js +8 -7
  38. package/src/svgii/svg-styles-to-attributes-const.js +1 -0
  39. package/src/svgii/svg_cleanup.js +467 -421
  40. package/src/svgii/svg_cleanup_convertPathLength.js +32 -0
  41. package/src/svgii/svg_cleanup_general_svg_atts.js +97 -0
  42. package/src/svgii/svg_cleanup_normalize_transforms.js +83 -0
  43. package/src/svgii/svg_cleanup_remove_els_and_atts.js +72 -0
  44. package/src/svgii/svg_cleanup_ungroup.js +36 -0
  45. package/src/svgii/svg_el_parse_style_props.js +76 -28
  46. package/src/svgii/svg_getElementLength.js +67 -0
  47. package/tests/testSVG_shape.js +59 -0
  48. package/tests/testSVG_transform.js +61 -0
@@ -1,8 +1,8 @@
1
1
  import { detectInputType } from './detect_input';
2
2
  import { simplifyPathDataCubic } from './pathData_simplify_cubic';
3
- import { getDistManhattan, getDistance, getPathDataVertices, interpolate, pointAtT } from './svgii/geometry';
3
+ import { getDistManhattan, getDistance, getPathDataVertices, interpolate, pointAtT, reducePoints, svgArcToCenterParam, toParametricAngle } from './svgii/geometry';
4
4
  import { getPolyBBox } from './svgii/geometry_bbox';
5
- import { analyzePathData } from './svgii/pathData_analyze';
5
+ import { analyzePathData, getPathDataVerbose } from './svgii/pathData_analyze';
6
6
  import { normalizePathData, parsePathDataNormalized, convertPathData } from './svgii/pathData_convert';
7
7
  import { shapeElToPath } from './svgii/pathData_parse_els';
8
8
  import { pathDataRemoveColinear } from './svgii/pathData_remove_collinear';
@@ -12,9 +12,9 @@ import { optimizeClosePath, pathDataToTopLeft } from './svgii/pathData_reorder';
12
12
  import { reversePathData } from './svgii/pathData_reverse';
13
13
  import { addExtremePoints, splitSubpaths } from './svgii/pathData_split';
14
14
  import { pathDataToD } from './svgii/pathData_stringify';
15
- import { detectAccuracy, roundPathData } from './svgii/rounding';
15
+ import { detectAccuracy, roundPathData, roundTo } from './svgii/rounding';
16
16
  import { refineAdjacentExtremes } from './svgii/pathData_simplify_refineExtremes';
17
- import { cleanUpSVG, removeEmptySVGEls, stringifySVG } from './svgii/svg_cleanup';
17
+ import { cleanUpSVG, removeEmptySVGEls } from './svgii/svg_cleanup';
18
18
  import { refineRoundedCorners } from './svgii/pathData_simplify_refineCorners';
19
19
  import { refineRoundSegments } from './svgii/pathData_refine_round';
20
20
  import { refineClosingCommand } from './svgii/pathData_remove_short';
@@ -23,144 +23,51 @@ import { getViewBox } from './svg_getViewbox';
23
23
  import { pathDataRevertCubicToQuadratic } from './pathData_simplify_revertToquadratics';
24
24
  import { pathDataCubicsToArc } from './pathData_simplify_cubicsToArcs';
25
25
  import { harmonizeCubicCpts } from './pathData_simplify_harmonize_cpts';
26
- import { pathDataToPolygon } from './svgii/pathData_toPolygon';
26
+ import { pathDataToPolygonOpt } from './svgii/pathData_toPolygon';
27
27
  import { pathDataLineToCubic } from './svgii/pathData_line_to_cubic';
28
28
  import { fixPathDataDirections } from './svgii/pathData_fix_directions';
29
29
  import { simplifyPolyChunks, getCurvePathData, simplifyPolygonToPathData } from './svgii/poly_to_pathdata';
30
30
  import { pathDataFromPoly } from './svgii/pathData_fromPoly';
31
- import { normalizePoly } from './svgii/poly_normalize';
31
+ import { normalizePoly, polyPtsToArray } from './svgii/poly_normalize';
32
32
  import { simplifyPolyRD } from './simplify_poly_radial_distance';
33
- import { simplifyPolyRDP } from './simplify_poly_RDP';
33
+ import { simplifyPolyRDP, simplifyPolyRDP__, simplifyRDP_rel } from './simplify_poly_RDP';
34
+ import { getEllipseLengthLG, getLegendreGaussValues, getLength, waArr_global } from './svgii/geometry_length';
35
+ import { deg2rad } from './constants';
36
+ import { getPathDataLength } from './svgii/pathData_getLength';
37
+ import { stringifySVG } from './string_helpers';
38
+ import { presetSettings, settingsDefaults } from './pathSimplify-presets';
39
+ import { splitCompundGroups } from './svgii/pathData_split_to_groups';
34
40
  //import { getPolyChunks } from "./svgii/poly_analyze_get_chunks";
35
41
 
36
42
 
37
43
  //import { installDOMPolyfills } from './dom-polyfill';
38
44
 
39
- export function svgPathSimplify(input = '', {
45
+ export function svgPathSimplify(input = '', settings = {}) {
40
46
 
41
- // return svg markup or object
42
- getObject = false,
47
+ let preset = settings['preset'] !== undefined && settings['preset'] ? settings['preset'] : null;
48
+ let defaults = preset && presetSettings[preset] !== undefined ? presetSettings[preset] : presetSettings['default'];
43
49
 
44
- toAbsolute = false,
45
- toRelative = true,
46
- toShorthands = true,
47
- toLonghands = false,
48
50
 
49
- //optimize = 0,
50
-
51
- // not necessary unless you need cubics only
52
- quadraticToCubic = true,
53
-
54
- // mostly a fallback if arc calculations fail
55
- arcToCubic = false,
56
- cubicToArc = false,
57
-
58
-
59
- simplifyBezier = true,
60
- optimizeOrder = true,
61
- autoClose = false,
62
- removeZeroLength = true,
63
- refineClosing = true,
64
- removeColinear = true,
65
- flatBezierToLinetos = true,
66
- revertToQuadratics = true,
67
-
68
- refineExtremes = true,
69
- simplifyCorners = false,
70
- removeDimensions = false,
71
- removeIds = false,
72
- removeClassNames = false,
73
- omitNamespace = false,
74
-
75
- fixDirections = false,
76
-
77
- keepExtremes = true,
78
- keepCorners = true,
79
- extrapolateDominant = true,
80
- keepInflections = false,
81
- addExtremes = false,
82
- addSemiExtremes = false,
83
-
84
- toPolygon = false,
85
- smoothPoly = false,
86
- polyFormat = 'points',
87
- precisionPoly = 1,
88
-
89
- simplifyRD = 1,
90
- simplifyRDP = 1,
91
-
92
- harmonizeCpts = false,
93
-
94
- removeOrphanSubpaths = false,
95
- simplifyRound = false,
96
-
97
- //svg scaling
98
- scale = 1,
99
- scaleTo = 0,
100
- crop = false,
101
- alignToOrigin = false,
102
-
103
- // flatten transforms
104
- convertTransforms = false,
105
-
106
-
107
-
108
- //svg path optimizations
109
- decimals = 3,
110
- autoAccuracy = true,
111
-
112
- // experimental
113
- //roundSub = false,
114
-
115
- minifyD = 0,
116
- tolerance = 1,
117
- reversePath = false,
118
-
119
- //svg cleanup options
120
- minifyRgbColors = false,
121
- removePrologue = true,
122
- removeHidden = true,
123
- removeUnused = true,
124
- cleanupDefs = true,
125
- cleanupClip = true,
126
- cleanupSVGAtts = true,
127
-
128
- stylesToAttributes = false,
129
- fixHref = false,
130
- legacyHref = false,
131
- removeNameSpaced = true,
132
-
133
- //attributesToGroup = false,
134
- removeOffCanvas = false,
135
- unGroup = false,
136
- mergePaths = false,
137
-
138
- // shape conversions
139
- shapesToPaths = false,
140
-
141
-
142
- //toPaths || toShapes
143
- shapeConvert = 0,
144
- convert_rects = false,
145
- convert_ellipses = false,
146
- convert_poly = false,
147
- convert_lines = false,
148
-
149
-
150
-
151
- lineToCubic = false,
152
- cleanUpStrokes = true,
153
- addViewBox = false,
154
- addDimensions = false,
51
+ // merge settings
52
+ settings = {
53
+ ...defaults,
54
+ ...settings
55
+ }
155
56
 
156
- removeComments = true,
157
57
 
158
- } = {}) {
58
+ let { getObject = false, removeComments, removeOffCanvas, unGroup, mergePaths, removeElements, removeDimensions, removeIds, removeClassNames, omitNamespace, cleanUpStrokes, addViewBox, addDimensions, removePrologue, removeHidden, removeUnused, cleanupDefs, cleanupClip, cleanupSVGAtts, removeNameSpaced, removeNameSpacedAtts, attributesToGroup, minifyRgbColors, stylesToAttributes, fixHref, legacyHref, allowMeta, allowDataAtts, allowAriaAtts, convertPathLength, removeSVGAttributes, removeElAttributes, shapesToPaths, shapeConvert, convertShapes, simplifyBezier, optimizeOrder, autoClose, removeZeroLength, refineClosing, removeColinear, flatBezierToLinetos, revertToQuadratics, refineExtremes, simplifyCorners, fixDirections, keepExtremes, keepCorners, keepInflections, addExtremes, reversePath, toAbsolute, toRelative, toMixed, toShorthands, toLonghands, quadraticToCubic, arcToCubic, cubicToArc, lineToCubic, decimals, autoAccuracy, minifyD, tolerance, toPolygon, smoothPoly, polyFormat, precisionPoly, simplifyRD, simplifyRDP, harmonizeCpts, removeOrphanSubpaths, simplifyRound, scale, scaleTo, crop, alignToOrigin, convertTransforms, keepSmaller, splitCompound } = settings;
159
59
 
60
+ //toAbsolute = !toRelative;
160
61
 
161
62
  // clamp tolerance and scale
162
63
  tolerance = Math.max(0.1, tolerance);
163
64
  scale = Math.max(0.001, scale)
65
+ if(fixDirections) keepSmaller = false;
66
+ if (scale !== 1 || scaleTo || crop || alignToOrigin) {
67
+ convertTransforms = true;
68
+ settings.convertTransforms = true
69
+ }
70
+
164
71
 
165
72
  let inputType = detectInputType(input);
166
73
  let svg = '';
@@ -175,8 +82,11 @@ export function svgPathSimplify(input = '', {
175
82
  // pathdata superset array - containing additional data
176
83
  let pathDataPlusArr_global = []
177
84
  let paths = []
85
+ let isPoly = false;
178
86
  let polys = []
87
+ let poly = []
179
88
  let dStr = '';
89
+ let dOriginal = ''
180
90
 
181
91
  /**
182
92
  * normalize input
@@ -200,29 +110,66 @@ export function svgPathSimplify(input = '', {
200
110
  arcToCubic = toPolygon ? true : arcToCubic;
201
111
  autoClose = false;
202
112
  let accuracyArr = []
203
-
113
+ //console.log(inputType);
114
+
115
+ // validate point JSON
116
+ if (inputType === 'json') {
117
+ let pts = [];
118
+ let needsQuotes = /([{,]\s*)(x|y)(\s*:)/.test(input)
119
+ if (needsQuotes) input = input.replaceAll('x:', '"x":').replaceAll('y:', '"y":')
120
+
121
+ try {
122
+ pts = JSON.parse(input)
123
+ } catch {
124
+ console.warn('No valid JSON');
125
+ }
126
+ if (pts.length) {
127
+ inputType = 'polyArray'
128
+ input = normalizePoly(pts);
129
+ isPoly = true;
130
+ }
131
+ }
204
132
 
205
133
  //console.log('inputType', inputType);
206
134
 
135
+
207
136
  // single path or polys
208
- if (inputType !== 'svgMarkup') {
137
+ if (inputType !== 'svgMarkup' && inputType !== 'symbol') {
209
138
  if (inputType === 'pathDataString') {
210
139
  d = input
211
140
  } else if (inputType === 'polyString') {
212
- d = 'M' + input
141
+ splitCompound = false;
142
+ isPoly = true;
143
+ poly = normalizePoly(input)
144
+ d = pathDataFromPoly(poly, closed)
145
+ //console.log(poly);
146
+
213
147
  }
214
148
 
215
149
  else if (inputType === 'polyArray' || inputType === 'polyObjectArray' || inputType === 'polyComplexArray' || inputType === 'polyComplexObjectArray') {
150
+ splitCompound = false;
216
151
 
217
152
  // normalize poly input to object array
218
- let poly = normalizePoly(input)
153
+ poly = normalizePoly(input)
219
154
 
220
155
  // convert to pathdata
221
- d = pathDataFromPoly(poly)
156
+ let closed = true;
157
+
158
+ isPoly = true;
159
+ //polys.push(poly)
222
160
 
223
161
  // calculate size
162
+ d = pathDataFromPoly(poly, closed)
224
163
  dStr = d.map(com => { return `${com.type} ${com.values.join(' ')}` }).join(' ');
164
+ dOriginal = dStr;
225
165
  svgSize = dStr.length;
166
+
167
+ /*
168
+ d=''
169
+ dOriginal = '';
170
+ svgSize = input.length;
171
+ */
172
+
226
173
  }
227
174
 
228
175
  else if (inputType === 'pathData') {
@@ -231,6 +178,12 @@ export function svgPathSimplify(input = '', {
231
178
  // stringify to compare lengths
232
179
  dStr = Array.from(d).map(com => { return `${com.type} ${com.values.join(' ')}` }).join(' ');
233
180
  svgSize = dStr.length;
181
+ isPoly = false;
182
+
183
+ }
184
+ // not valid - set dummy path data
185
+ else {
186
+ d = 'M0 0 h0'
234
187
  }
235
188
 
236
189
  paths.push({ d, el: null })
@@ -239,37 +192,48 @@ export function svgPathSimplify(input = '', {
239
192
  // mode:1 – process complete svg DOM
240
193
  else {
241
194
 
195
+
196
+ // convert symbol temporarily to SVG
197
+ if (inputType === 'symbol') {
198
+ input = input.replaceAll('<symbol', '<svg').replaceAll('</symbol', '</svg')
199
+ // ids are mandatory for symbols
200
+ removeIds = false
201
+ removeDimensions = true
202
+ }
203
+
242
204
  // convert all shapes to paths
243
205
  if (shapesToPaths) {
244
- shapeConvert = true
206
+ shapeConvert = 'toPaths'
245
207
  convert_rects = true
246
208
  convert_ellipses = true
247
209
  convert_poly = true
248
210
  convert_lines = true
249
211
  }
250
212
 
251
- //sanitize
252
- let svgPropObject = cleanUpSVG(input, {
253
- removeIds, removeClassNames, removeDimensions, cleanupSVGAtts, cleanUpStrokes, removeHidden, removeUnused, removeNameSpaced, stylesToAttributes, removePrologue, fixHref, mergePaths, convertTransforms, legacyHref, cleanupDefs, cleanupClip, addViewBox, removeOffCanvas, addDimensions,
254
- shapeConvert, convert_rects, convert_ellipses, convert_poly, convert_lines, minifyRgbColors, unGroup, convertTransforms
255
- }
256
- );
213
+ //console.log('shapesToPaths', shapesToPaths, 'shapeConvert', shapeConvert, convert_rects, convert_ellipses, convert_poly);
214
+
215
+ // sanitize SVG - clone/decouple settings
216
+ let svgPropObject = cleanUpSVG(input, JSON.parse(JSON.stringify(settings)));
217
+
218
+ //console.log('settings', settings);
219
+ //console.log('svgPropObject', svgPropObject);
257
220
 
258
221
  let { svgElProps } = svgPropObject
259
222
  svg = svgPropObject.svg;
260
-
223
+ //console.log(svgPropObject);
261
224
 
262
225
 
263
226
  // collect paths
264
227
  let pathEls = svg.querySelectorAll('path')
265
228
  //let pathEls2 = svg.getElementsByTagName('path')
266
- //console.log(pathEls2);
229
+ //console.log(pathEls);
267
230
 
268
231
  pathEls.forEach((path, i) => {
269
- paths.push({ d: path.getAttribute('d'), el: path, idx: i })
232
+ let d = path.getAttribute('d');
233
+ //console.log(d, path.nodeName, path.id);
234
+ paths.push({ d, el: path, idx: i })
270
235
  })
271
236
 
272
-
273
237
  // get viewBox/dimensions
274
238
  viewBox = getViewBox(svg, decimals)
275
239
 
@@ -285,14 +249,11 @@ export function svgPathSimplify(input = '', {
285
249
  // SVG optimization options
286
250
  let pathOptions = {
287
251
  toRelative,
288
- toAbsolute,
289
- toLonghands,
252
+ toMixed,
290
253
  toShorthands,
291
254
  decimals,
292
255
  }
293
-
294
- // combinded path data for SVGs with mergePaths enabled
295
- let pathData_merged = [];
256
+ //console.log('pathOptions', pathOptions);
296
257
 
297
258
 
298
259
  for (let i = 0, l = paths.length; l && i < l; i++) {
@@ -301,10 +262,14 @@ export function svgPathSimplify(input = '', {
301
262
  let path = paths[i];
302
263
  let { d, el } = path;
303
264
  let dN = ''
265
+ let isPoly = false;
304
266
 
305
-
267
+ // if polygon we already heave absolute coordinates
268
+ //let isPolyPath = !mode && isPoly && Array.isArray(d)
269
+ //let pathData = !isPolyPath ? parsePathDataNormalized(d, { quadraticToCubic, arcToCubic }) : d;
306
270
  let pathData = parsePathDataNormalized(d, { quadraticToCubic, arcToCubic });
307
271
  //console.log('!!!pathData', pathData, arcToCubic);
272
+ //console.log(isPoly);
308
273
 
309
274
  // get polygon bbox
310
275
  let bb_poly = smoothPoly || toPolygon ? getPolyBBox(getPathDataVertices(pathData)) : null
@@ -343,7 +308,8 @@ export function svgPathSimplify(input = '', {
343
308
  // count commands for evaluation
344
309
  let comCount = pathData.length
345
310
 
346
- if (removeOrphanSubpaths) pathData = removeOrphanedM(pathData);
311
+ if (!isPoly && removeOrphanSubpaths) pathData = removeOrphanedM(pathData);
312
+
347
313
 
348
314
 
349
315
  /**
@@ -351,7 +317,6 @@ export function svgPathSimplify(input = '', {
351
317
  */
352
318
  let subPathArr = splitSubpaths(pathData);
353
319
  let lenSub = subPathArr.length;
354
-
355
320
  //console.log('subPathArr', subPathArr);
356
321
 
357
322
 
@@ -361,27 +326,29 @@ export function svgPathSimplify(input = '', {
361
326
  //let { pathData, bb } = subPathArr[i];
362
327
  let pathDataSub = subPathArr[i];
363
328
  let poly = []
364
-
365
329
  let coms = Array.from(new Set(pathDataSub.map(com => com.type))).join('')
366
- let isPoly = !(/[acqts]/gi).test(coms)
367
- let closed = (/[z]/gi).test(coms)
330
+ isPoly = !(/[acqts]/gi).test(coms)
331
+ let closed = isPoly ? true : false;
368
332
 
369
- if (isPoly) {
333
+ if (isPoly && !mode) {
370
334
 
371
335
  poly = getPathDataVertices(pathDataSub);
372
- //console.log(poly);
336
+ let bb = getPolyBBox(reducePoints(poly, 64))
337
+ //console.log(poly, bb);
373
338
 
374
339
  // simplify polygon
375
340
  if (simplifyRD > 0) {
376
- poly = simplifyPolyRD(poly, { quality: simplifyRD + 'px' })
341
+ poly = simplifyPolyRD(poly, { quality: simplifyRD, width: bb.width, height: bb.height })
377
342
  }
378
343
 
379
344
  if (simplifyRDP > 0) {
380
- poly = simplifyPolyRDP(poly, { quality: simplifyRDP + 'px' })
345
+ poly = simplifyPolyRDP(poly, { quality: simplifyRDP, width: bb.width, height: bb.height })
346
+ //poly = simplifyRDP_rel(poly, simplifyRDP, bb.width, bb.height)
381
347
  }
382
348
 
349
+ toPolygon = false;
383
350
  pathDataSub = pathDataFromPoly(poly, closed)
384
-
351
+ //pathDataSub[0].bb = bb
385
352
  }
386
353
 
387
354
 
@@ -394,33 +361,25 @@ export function svgPathSimplify(input = '', {
394
361
  smoothPoly = false;
395
362
  harmonizeCpts = false;
396
363
 
397
- //pathDataSub = normalizePathData(pathDataSub, {arcToCubic:true})
398
- //console.log(pathDataSub);
399
-
400
- let pathDataSubPlus = analyzePathData(pathDataSub)
401
- let { bb, pathData } = pathDataSubPlus;
402
- pathDataSub = pathData;
364
+ pathDataSub = getPathDataVerbose(pathDataSub);
403
365
 
404
-
405
- let polyData = pathDataToPolygon(pathDataSub, {
366
+ let polyData = pathDataToPolygonOpt(pathDataSub, {
406
367
  precisionPoly,
407
368
  autoAccuracy,
408
- polyFormat,
409
- decimals,
369
+ //polyFormat,
370
+ //decimals,
410
371
  simplifyRD,
411
372
  simplifyRDP
412
373
  })
413
374
 
414
-
415
-
416
- //poly.push(polyData.poly)
417
- polys.push(polyData.poly)
375
+ //console.log('toPolygon');
376
+ //polys.push(polyData.poly)
418
377
  pathDataSub = polyData.pathData
378
+ isPoly = true;
419
379
 
420
380
  }
421
381
 
422
382
 
423
-
424
383
  /**
425
384
  * poly to beziers via
426
385
  * Philip J. Schneider's
@@ -448,21 +407,22 @@ export function svgPathSimplify(input = '', {
448
407
  simplifyRD,
449
408
  simplifyRDP,
450
409
  }
410
+
411
+ //console.log('smooth');
451
412
  pathDataSub = simplifyPolygonToPathData(poly, optionsPoly)
413
+ // flag as non poly as we're smoothing to curves
414
+ //isPoly = false
452
415
  }
453
416
  }
454
417
 
455
418
 
456
-
457
419
  // harmonize cpts
458
420
  // if (harmonizeCpts) pathDataSub = harmonizeCubicCpts(pathDataSub)
459
421
 
460
-
461
422
  // remove zero length linetos
462
423
  if (removeColinear || removeZeroLength) pathDataSub = removeZeroLengthLinetos(pathDataSub)
463
424
 
464
425
 
465
-
466
426
  // sort to top left
467
427
  if (optimizeOrder) pathDataSub = pathDataToTopLeft(pathDataSub);
468
428
 
@@ -471,8 +431,8 @@ export function svgPathSimplify(input = '', {
471
431
  if (removeColinear) pathDataSub = pathDataRemoveColinear(pathDataSub, { tolerance, flatBezierToLinetos: false });
472
432
 
473
433
  let tMin = 0, tMax = 1;
474
- if (addExtremes || addSemiExtremes) pathDataSub = addExtremePoints(pathDataSub,
475
- { tMin, tMax, addExtremes, addSemiExtremes, angles: [30] })
434
+ if (addExtremes) pathDataSub = addExtremePoints(pathDataSub,
435
+ { tMin, tMax, addExtremes, angles: [30] })
476
436
 
477
437
 
478
438
 
@@ -481,23 +441,39 @@ export function svgPathSimplify(input = '', {
481
441
  pathDataSub = reversePathData(pathDataSub)
482
442
  }
483
443
 
444
+ // analyze pathdata to add info about significant properties such as extremes, corners
445
+ let pathDataPlus = { bb: {}, dimA: 0, pathData: [] }
446
+
447
+ if (!isPoly) {
448
+ pathDataPlus = analyzePathData(pathDataSub);
449
+ }
450
+ // we skip detailed analysis for native polygons
451
+ else {
452
+ if (!poly.length) {
453
+ let pathDataCubic = convertPathData(JSON.parse(JSON.stringify(pathDataSub)), { toLonghands: true, toAbsolute: true, arcToCubic: true, testTypes: true })
454
+ pathDataPlus.bb = getPolyBBox(getPathDataVertices(pathDataCubic))
455
+ }
456
+ pathDataPlus.dimA = pathDataPlus.bb.width + pathDataPlus.bb.height;
457
+ pathDataPlus.pathData = getPathDataVerbose(pathDataSub, {
458
+ addSquareLength: false,
459
+ addArea: false,
460
+ addAverageDim: false
461
+ })
462
+ }
463
+
484
464
 
485
- // analyze pathdata to add info about signicant properties such as extremes, corners
486
- let pathDataPlus = analyzePathData(pathDataSub, {
487
- detectSemiExtremes: addSemiExtremes,
488
- });
489
465
 
490
466
 
491
467
  // simplify beziers
492
468
  let { pathData, bb, dimA } = pathDataPlus;
469
+
493
470
  xArr.push(bb.x, bb.x + bb.width)
494
471
  yArr.push(bb.y, bb.y + bb.height)
495
472
 
496
473
 
497
474
  if (refineClosing) pathData = refineClosingCommand(pathData, { threshold: dimA * 0.001 })
498
475
 
499
-
500
- pathData = simplifyBezier ? simplifyPathDataCubic(pathData, { simplifyBezier, keepInflections, keepExtremes, keepCorners, extrapolateDominant, revertToQuadratics, tolerance }) : pathData;
476
+ pathData = simplifyBezier ? simplifyPathDataCubic(pathData, { simplifyBezier, keepInflections, keepExtremes, keepCorners, revertToQuadratics, tolerance }) : pathData;
501
477
 
502
478
 
503
479
  // refine extremes
@@ -544,10 +520,6 @@ export function svgPathSimplify(input = '', {
544
520
  //let decimalsSub = Math.max(2, detectAccuracy(pathData));
545
521
  let decimalsSub = detectAccuracy(pathData);
546
522
  accuracyArr.push(decimalsSub);
547
- //let decimalsSub = detectAccuracy(pathData);
548
-
549
- // pre round sub path
550
- //if(roundSub) pathData = roundPathData(pathData, decimalsSub);
551
523
 
552
524
  }
553
525
 
@@ -556,6 +528,7 @@ export function svgPathSimplify(input = '', {
556
528
 
557
529
  } // end sup paths
558
530
 
531
+
559
532
  // sort subpaths to top left
560
533
  let xMin = Math.min(...xArr)
561
534
  let yMin = Math.min(...yArr)
@@ -569,6 +542,11 @@ export function svgPathSimplify(input = '', {
569
542
  //console.log(i, pathDataPlusArr);
570
543
 
571
544
 
545
+ // fix path directions - before reordering
546
+ if (fixDirections) {
547
+ pathDataPlusArr = fixPathDataDirections(pathDataPlusArr);
548
+ }
549
+
572
550
 
573
551
  // prefer top to bottom priority for portrait aspect ratios
574
552
  if (optimizeOrder) {
@@ -576,16 +554,12 @@ export function svgPathSimplify(input = '', {
576
554
  }
577
555
 
578
556
 
579
- // fix path directions
580
- if (fixDirections) {
581
- pathDataPlusArr = fixPathDataDirections(pathDataPlusArr);
582
- }
583
-
584
557
 
585
558
 
586
559
  // flatten compound paths
587
560
  pathData = [];
588
561
 
562
+
589
563
  // add to global array - including multiple path elements
590
564
  pathDataPlusArr_global.push(pathDataPlusArr);
591
565
 
@@ -603,99 +577,88 @@ export function svgPathSimplify(input = '', {
603
577
  pathOptions.decimals = decimals
604
578
  }
605
579
 
580
+ // add simplified poly - if not populated by toPoly conversion
581
+ if (isPoly) {
582
+ //console.log('5. isPoly', isPoly);
583
+
584
+ pathDataPlusArr.forEach(sub => {
585
+ let poly = getPathDataVertices(sub.pathData, false, decimals)
586
+ if (polyFormat === 'array') {
587
+ poly = polyPtsToArray(poly)
588
+ }
589
+ polys.push(poly)
590
+ })
606
591
 
607
- // collect for merged svg paths
608
- mergePaths = false
609
- if (el && mergePaths) {
610
- pathData_merged.push(...pathData)
611
592
  }
612
- // single output
613
- else {
614
593
 
615
- // clone pathdata
616
- //pathData = pathData.map(com => ({ type: com.type, values: [...com.values] }));
617
- pathData = JSON.parse(JSON.stringify(pathData));
618
594
 
619
- // optimize path data
620
- pathData = convertPathData(pathData, pathOptions)
595
+ // split into sub paths - returns svg with multiple paths
596
+ if (splitCompound && !mode && pathDataPlusArr.length > 1) {
597
+ let pathDataSplit = splitCompundGroups(pathDataPlusArr, { toRelative, toShorthands, decimals, addDimensions });
598
+ svg = new DOMParser().parseFromString(pathDataSplit.svg, 'image/svg+xml').querySelector('svg');
599
+ // switch output type
600
+ mode = 1;
601
+ inputType = 'splitPath'
602
+ }
621
603
 
622
- // remove zero-length segments introduced by rounding
623
- if (removeZeroLength) pathData = removeZeroLengthLinetos(pathData);
624
604
 
625
- // realign path to zero origin
626
- if (alignToOrigin) {
605
+ // clone pathdata
606
+ pathData = JSON.parse(JSON.stringify(pathData));
627
607
 
628
- pathData[0].values[0] = (pathData[0].values[0] - bb_global.x).toFixed(decimals)
629
- pathData[0].values[1] = (pathData[0].values[1] - bb_global.y).toFixed(decimals)
608
+ // optimize path data
609
+ pathData = convertPathData(pathData, pathOptions)
630
610
 
631
- bb_global.x = 0
632
- bb_global.y = 0
633
- }
611
+ // remove zero-length segments introduced by rounding
612
+ if (removeZeroLength) pathData = removeZeroLengthLinetos(pathData);
634
613
 
614
+ // realign path to zero origin
615
+ if (alignToOrigin) {
635
616
 
636
- // compare command count
637
- let comCountS = pathData.length
617
+ pathData[0].values[0] = roundTo((pathData[0].values[0] - bb_global.x), decimals)
618
+ pathData[0].values[1] = roundTo((pathData[0].values[1] - bb_global.y), decimals)
638
619
 
639
- let dOpt = pathDataToD(pathData, minifyD)
640
- //svgSizeOpt = new Blob([dOpt]).size;
641
- svgSizeOpt = dOpt.length
620
+ bb_global.x = 0
621
+ bb_global.y = 0
622
+ }
642
623
 
643
- compression = +(100 / svgSize * (svgSizeOpt)).toFixed(2)
644
624
 
645
- path.d = dOpt
646
- path.report = {
647
- original: comCount,
648
- new: comCountS,
649
- saved: comCount - comCountS,
650
- compression,
651
- decimals,
652
- //success: comCountS < comCount
653
- }
625
+ // compare command count
626
+ let comCountS = pathData.length
654
627
 
655
- //console.log('el', el);
656
- // apply new path for svgs
657
- if (el) {
658
- el.setAttribute('d', dOpt)
659
- }
628
+ let dOpt = pathDataToD(pathData, minifyD)
629
+ //svgSizeOpt = new Blob([dOpt]).size;
630
+ svgSizeOpt = dOpt.length
631
+
632
+ compression = +(100 / svgSize * (svgSizeOpt)).toFixed(2)
633
+
634
+ path.d = dOpt
635
+ path.report = {
636
+ original: comCount,
637
+ new: comCountS,
638
+ saved: comCount - comCountS,
639
+ compression,
640
+ decimals,
641
+ //success: comCountS < comCount
660
642
  }
661
643
 
644
+ // apply new path for svgs
645
+ if (el) {
646
+ el.setAttribute('d', dOpt)
647
+ }
648
+
649
+
662
650
  } // end path array
663
651
 
664
652
  /**
665
653
  * stringify new SVG
666
654
  */
667
- if (mode) {
668
-
669
- //console.log(pathData_merged);
670
- if (pathData_merged.length) {
671
-
672
- // optimize path data
673
- let pathData = convertPathData(pathData_merged, pathOptions)
674
-
675
- // remove zero-length segments introduced by rounding
676
- pathData = removeZeroLengthLinetos(pathData);
677
-
678
- let dOpt = pathDataToD(pathData, minifyD)
679
-
680
- // apply new path for svgs
681
- paths[0].el.setAttribute('d', dOpt)
655
+ if (mode || inputType === 'symbol') {
682
656
 
683
- // remove other paths
684
- for (let i = 1; i < paths.length; i++) {
685
- let pathEl = paths[i].el
686
- if (pathEl) pathEl.remove()
687
- }
688
-
689
- // remove empty groups e.g groups
690
- removeEmptySVGEls(svg);
691
- }
657
+ //console.log('process', inputType);
692
658
 
693
659
  // adjust viewBox and width for scale
694
660
  if (scale) {
695
-
696
661
  let { x, y, width, height, w, h, hasViewBox, hasWidth, hasHeight, widthUnit, heightUnit } = viewBox;
697
- //console.log('bb_global', bb_global);
698
-
699
662
  if (crop) {
700
663
  x = bb_global.x
701
664
  y = bb_global.y
@@ -706,14 +669,14 @@ export function svgPathSimplify(input = '', {
706
669
  }
707
670
 
708
671
  if (hasViewBox) {
709
- svg.setAttribute('viewBox', [x, y, width, height].map(val => +(val * scale).toFixed(decimals)).join(' '))
672
+ svg.setAttribute('viewBox', [x, y, width, height].map(val => roundTo(val * scale, decimals)).join(' '))
710
673
  }
711
674
  if (hasWidth) {
712
- svg.setAttribute('width', +(w * scale).toFixed(decimals) + widthUnit)
675
+ svg.setAttribute('width', roundTo(w * scale, decimals) + widthUnit)
713
676
  }
714
677
 
715
678
  if (hasHeight) {
716
- svg.setAttribute('height', +(h * scale).toFixed(decimals) + heightUnit)
679
+ svg.setAttribute('height', roundTo(h * scale, decimals) + heightUnit)
717
680
  }
718
681
  }
719
682
 
@@ -726,9 +689,12 @@ export function svgPathSimplify(input = '', {
726
689
  })
727
690
  }
728
691
 
692
+ //console.log(svg);
693
+ if (removeSVGAttributes.includes('xmlns')) omitNamespace = true;
729
694
 
730
- svg = stringifySVG(svg, { omitNamespace, removeComments });
731
695
 
696
+ svg = stringifySVG(svg, { omitNamespace, removeComments, format: minifyD });
697
+ //console.log('!!!svg', svg);
732
698
 
733
699
  //svgSizeOpt = new Blob([svg]).size
734
700
  svgSizeOpt = svg.length;
@@ -738,13 +704,22 @@ export function svgPathSimplify(input = '', {
738
704
  svgSize = +(svgSize / 1024).toFixed(3)
739
705
  svgSizeOpt = +(svgSizeOpt / 1024).toFixed(3)
740
706
 
707
+
741
708
  report = {
742
709
  svgSize,
743
710
  svgSizeOpt,
744
711
  compression,
745
- decimals
712
+ decimals,
713
+ }
714
+
715
+ if (keepSmaller && svgSize < svgSizeOpt && !splitCompound) {
716
+ //console.log('Original is smaller!');
717
+ svg = input
718
+ report.node = 'Original is smaller!'
746
719
  }
747
720
 
721
+
722
+
748
723
  } else {
749
724
  ({ d, report } = paths[0]);
750
725
  }
@@ -753,7 +728,16 @@ export function svgPathSimplify(input = '', {
753
728
  polys = polys[0]
754
729
  }
755
730
 
756
- return !getObject ? (d ? d : svg) : { svg, d, polys, report, pathDataPlusArr: pathDataPlusArr_global, inputType };
731
+
732
+ //console.log('---simplify', input);
733
+ //console.log('5. svg', svg);
734
+
735
+ if (polyFormat === 'string' && polys.length) {
736
+ polys = polys.flat().map(pt => `${pt.x},${pt.y}`).join(' ')
737
+ }
738
+
739
+
740
+ return !getObject ? (d ? d : svg) : { svg, d, polys, report, pathDataPlusArr: pathDataPlusArr_global, inputType, dOriginal };
757
741
 
758
742
  }
759
743