svg-path-simplify 0.4.4 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +1 -1
- package/dist/svg-path-simplify.esm.js +74 -28
- package/dist/svg-path-simplify.esm.min.js +2 -2
- package/dist/svg-path-simplify.js +74 -28
- package/dist/svg-path-simplify.min.js +2 -2
- package/dist/svg-path-simplify.pathdata.esm.js +46 -10
- package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
- package/index.html +2 -3
- package/package.json +1 -1
- package/src/pathData_simplify_cubic.js +20 -2
- package/src/pathSimplify-main.js +11 -5
- package/src/pathSimplify-presets.js +0 -1
- package/src/svg-getAttributes.js +1 -1
- package/src/svgii/geometry_length.js +1 -1
- package/src/svgii/pathData_reorder.js +3 -2
- package/src/svgii/poly_normalize.js +9 -8
- package/src/svgii/rounding.js +34 -3
- package/src/svgii/svg_cleanup.js +8 -3
- package/src/svgii/svg_cleanup_convertPathLength.js +5 -0
- package/src/svgii/svg_cleanup_normalize_transforms.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.5] - 2026-04-02
|
|
4
|
+
### Fixed
|
|
5
|
+
- stroke-dash conversion must disable reordering of commands
|
|
6
|
+
- stroke-dashoffset missing in attribute scaling
|
|
7
|
+
- removeOffCanvas - fixed bbox calculation
|
|
8
|
+
- rounding bug in command reordering
|
|
9
|
+
### Added
|
|
10
|
+
- better auto accuracy approximation
|
|
11
|
+
|
|
12
|
+
|
|
3
13
|
## [0.4.4] - 2026-04-01
|
|
4
14
|
### Fixed
|
|
5
15
|
- shape attribute retrieval in node.js
|
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ While this library reduces SVG markup sizes significantly by removing commands i
|
|
|
15
15
|
Unlike most existing approaches (e.g in graphic applications), it checks where simplifications are suitable and stops simplification at the right »point« (literally).
|
|
16
16
|
|
|
17
17
|

|
|
18
|
-
*Fira Sans (based on truetype/glyph quadratic commands) converted to cubic Béziers.
|
|
18
|
+
*Fira Sans (based on truetype/glyph quadratic commands) converted to cubic Béziers. Left:Original; Right:optimized*
|
|
19
19
|
|
|
20
20
|
## Features
|
|
21
21
|
### Path simplification
|
|
@@ -1852,7 +1852,7 @@ function roundPathData(pathData, decimalsGlobal = -1) {
|
|
|
1852
1852
|
|
|
1853
1853
|
let len = pathData.length;
|
|
1854
1854
|
let decimals = decimalsGlobal;
|
|
1855
|
-
let decimalsArc = decimals < 3 ? decimals+2 : decimals;
|
|
1855
|
+
let decimalsArc = decimals < 3 ? decimals + 2 : decimals;
|
|
1856
1856
|
|
|
1857
1857
|
for (let c = 0; c < len; c++) {
|
|
1858
1858
|
let com = pathData[c];
|
|
@@ -1912,23 +1912,47 @@ function detectAccuracy(pathData) {
|
|
|
1912
1912
|
|
|
1913
1913
|
dimA = dimA ? dimA : getDistManhattan(p0, p);
|
|
1914
1914
|
|
|
1915
|
-
if (dimA) dims.push(dimA);
|
|
1915
|
+
if (dimA) dims.push(+dimA.toFixed(8));
|
|
1916
1916
|
|
|
1917
1917
|
}
|
|
1918
1918
|
|
|
1919
1919
|
}
|
|
1920
1920
|
|
|
1921
|
-
|
|
1921
|
+
dims = dims.sort();
|
|
1922
|
+
let len = dims.length;
|
|
1923
|
+
let dim_mid = dims[Math.floor(len*0.5)];
|
|
1924
|
+
|
|
1925
|
+
// smallest 25% of values
|
|
1926
|
+
let idx_q = Math.ceil(len*0.25);
|
|
1927
|
+
let dims_min = dims.slice(0, idx_q);
|
|
1928
|
+
|
|
1929
|
+
// average smallest values with mid value
|
|
1930
|
+
let dim_min = ((dims_min.reduce((a, b) => a + b, 0) / idx_q) + dim_mid) * 0.5;
|
|
1931
|
+
|
|
1932
|
+
let threshold = 75;
|
|
1933
|
+
let decimalsAuto = dim_min > threshold * 1.5 ? 0 : Math.floor(threshold / dim_min).toString().length;
|
|
1934
|
+
|
|
1935
|
+
// clamp
|
|
1936
|
+
return Math.min(Math.max(0, decimalsAuto), 8)
|
|
1937
|
+
|
|
1938
|
+
/*
|
|
1939
|
+
let dim_min = dims.sort()
|
|
1922
1940
|
|
|
1923
|
-
let
|
|
1941
|
+
let dim_mid = dim_min[Math.floor(dim_min.length*0.5)]
|
|
1942
|
+
|
|
1943
|
+
let sliceIdx = Math.ceil(dim_min.length / 4);
|
|
1924
1944
|
dim_min = dim_min.slice(0, sliceIdx);
|
|
1925
1945
|
let minVal = dim_min.reduce((a, b) => a + b, 0) / sliceIdx;
|
|
1926
1946
|
|
|
1927
|
-
|
|
1928
|
-
|
|
1947
|
+
// average with mid value
|
|
1948
|
+
minVal = (minVal+dim_mid)*0.5
|
|
1949
|
+
|
|
1950
|
+
let threshold = 75
|
|
1951
|
+
let decimalsAuto = minVal > threshold * 1.5 ? 0 : Math.floor(threshold / minVal).toString().length
|
|
1929
1952
|
|
|
1930
1953
|
// clamp
|
|
1931
1954
|
return Math.min(Math.max(0, decimalsAuto), 8)
|
|
1955
|
+
*/
|
|
1932
1956
|
|
|
1933
1957
|
}
|
|
1934
1958
|
|
|
@@ -2494,7 +2518,7 @@ function getElementAtts(el, {x=0, y=0, width=0, height=0}={}){
|
|
|
2494
2518
|
attributes.forEach(att=>{
|
|
2495
2519
|
|
|
2496
2520
|
let value = normalizeUnits(el.getAttribute(att), {x, y, width, height});
|
|
2497
|
-
atts[att
|
|
2521
|
+
atts[att] = value;
|
|
2498
2522
|
});
|
|
2499
2523
|
|
|
2500
2524
|
return atts
|
|
@@ -3239,7 +3263,7 @@ function simplifyPathDataCubic(pathData, {
|
|
|
3239
3263
|
error += com.error;
|
|
3240
3264
|
|
|
3241
3265
|
// find next candidates
|
|
3242
|
-
for (let n = i +
|
|
3266
|
+
for (let n = i + offset; error < tolerance && n < l; n++) {
|
|
3243
3267
|
let comN = pathData[n];
|
|
3244
3268
|
|
|
3245
3269
|
if (comN.type !== 'C' ||
|
|
@@ -3249,6 +3273,7 @@ function simplifyPathDataCubic(pathData, {
|
|
|
3249
3273
|
(keepExtremes && com.extreme)
|
|
3250
3274
|
)
|
|
3251
3275
|
) {
|
|
3276
|
+
|
|
3252
3277
|
break
|
|
3253
3278
|
}
|
|
3254
3279
|
|
|
@@ -3256,6 +3281,7 @@ function simplifyPathDataCubic(pathData, {
|
|
|
3256
3281
|
|
|
3257
3282
|
// failure - could not be combined - exit loop
|
|
3258
3283
|
if (combined.length > 1) {
|
|
3284
|
+
|
|
3259
3285
|
break
|
|
3260
3286
|
}
|
|
3261
3287
|
|
|
@@ -3269,6 +3295,7 @@ function simplifyPathDataCubic(pathData, {
|
|
|
3269
3295
|
|
|
3270
3296
|
// return combined
|
|
3271
3297
|
com = combined[0];
|
|
3298
|
+
|
|
3272
3299
|
}
|
|
3273
3300
|
|
|
3274
3301
|
pathDataN.push(com);
|
|
@@ -3382,11 +3409,19 @@ function combineCubicPairs(com1, com2, {
|
|
|
3382
3409
|
|
|
3383
3410
|
comS.dimA = getDistManhattan(comS.p0, comS.p);
|
|
3384
3411
|
comS.type = 'C';
|
|
3412
|
+
|
|
3385
3413
|
comS.extreme = com2.extreme;
|
|
3386
3414
|
comS.directionChange = com2.directionChange;
|
|
3387
|
-
|
|
3388
3415
|
comS.corner = com2.corner;
|
|
3389
3416
|
|
|
3417
|
+
if (comS.extreme || comS.corner) ;
|
|
3418
|
+
|
|
3419
|
+
/*
|
|
3420
|
+
comS.extreme = com1.extreme;
|
|
3421
|
+
comS.directionChange = com1.directionChange;
|
|
3422
|
+
comS.corner = com1.corner;
|
|
3423
|
+
*/
|
|
3424
|
+
|
|
3390
3425
|
comS.values = [comS.cp1.x, comS.cp1.y, comS.cp2.x, comS.cp2.y, comS.p.x, comS.p.y];
|
|
3391
3426
|
|
|
3392
3427
|
// relative error
|
|
@@ -6555,7 +6590,8 @@ function pathDataToTopLeft(pathData) {
|
|
|
6555
6590
|
let { type, values } = com;
|
|
6556
6591
|
let valsLen = values.length;
|
|
6557
6592
|
if (valsLen) {
|
|
6558
|
-
|
|
6593
|
+
// we need rounding otherwise sorting may crash due to e notation
|
|
6594
|
+
let p = { type: type, x: +values[valsLen - 2].toFixed(8), y: +values[valsLen - 1].toFixed(8), index: 0 };
|
|
6559
6595
|
p.index = i;
|
|
6560
6596
|
indices.push(p);
|
|
6561
6597
|
}
|
|
@@ -6563,7 +6599,7 @@ function pathDataToTopLeft(pathData) {
|
|
|
6563
6599
|
|
|
6564
6600
|
// reorder to top left most
|
|
6565
6601
|
|
|
6566
|
-
indices = indices.sort((a, b) =>
|
|
6602
|
+
indices = indices.sort((a, b) => a.y - b.y || a.x - b.x);
|
|
6567
6603
|
newIndex = indices[0].index;
|
|
6568
6604
|
|
|
6569
6605
|
return newIndex ? shiftSvgStartingPoint(pathData, newIndex) : pathData;
|
|
@@ -7099,7 +7135,7 @@ function normalizePoly(pts, {
|
|
|
7099
7135
|
} = {}) {
|
|
7100
7136
|
|
|
7101
7137
|
// is stringified flat point attribute
|
|
7102
|
-
if(typeof pts === 'string' && !isNaN(pts[0])){
|
|
7138
|
+
if (typeof pts === 'string' && !isNaN(pts[0])) {
|
|
7103
7139
|
pts = toPointArray(pts.split(/,| /).filter(Boolean).map(Number));
|
|
7104
7140
|
return pts
|
|
7105
7141
|
}
|
|
@@ -7109,8 +7145,9 @@ function normalizePoly(pts, {
|
|
|
7109
7145
|
return poly
|
|
7110
7146
|
}
|
|
7111
7147
|
|
|
7112
|
-
function polyArrayToObject(pts) {
|
|
7148
|
+
function polyArrayToObject(pts = []) {
|
|
7113
7149
|
|
|
7150
|
+
if (!pts.length) return [];
|
|
7114
7151
|
// is point object array
|
|
7115
7152
|
if (pts[0].x !== undefined && pts[0].y !== undefined) return pts
|
|
7116
7153
|
|
|
@@ -7128,7 +7165,7 @@ function polyArrayToObject(pts) {
|
|
|
7128
7165
|
return poly
|
|
7129
7166
|
}
|
|
7130
7167
|
|
|
7131
|
-
else if(pts.length>3){
|
|
7168
|
+
else if (pts.length > 3) {
|
|
7132
7169
|
pts = toPointArray(pts);
|
|
7133
7170
|
return pts
|
|
7134
7171
|
}
|
|
@@ -7157,13 +7194,13 @@ function polyPtsToArray(pts) {
|
|
|
7157
7194
|
function toPointArray(pts) {
|
|
7158
7195
|
let ptArr = [];
|
|
7159
7196
|
|
|
7160
|
-
if(pts[0].length===2){
|
|
7161
|
-
for (let i = 0, l = pts.length; i < l; i
|
|
7197
|
+
if (pts[0].length === 2) {
|
|
7198
|
+
for (let i = 0, l = pts.length; i < l; i++) {
|
|
7162
7199
|
let pt = pts[i];
|
|
7163
|
-
ptArr.push({ x: pt[0], y:pt[1] });
|
|
7200
|
+
ptArr.push({ x: pt[0], y: pt[1] });
|
|
7164
7201
|
}
|
|
7165
7202
|
|
|
7166
|
-
}else {
|
|
7203
|
+
} else {
|
|
7167
7204
|
for (let i = 1, l = pts.length; i < l; i += 2) {
|
|
7168
7205
|
ptArr.push({ x: pts[i - 1], y: pts[i] });
|
|
7169
7206
|
}
|
|
@@ -8005,7 +8042,6 @@ function getLength(pts, {
|
|
|
8005
8042
|
|
|
8006
8043
|
// LG weight/abscissae generator
|
|
8007
8044
|
function getLegendreGaussValues(n, x1 = -1, x2 = 1) {
|
|
8008
|
-
console.log('add new LG', n);
|
|
8009
8045
|
|
|
8010
8046
|
let waArr = [];
|
|
8011
8047
|
let z1, z, xm, xl, pp, p3, p2, p1;
|
|
@@ -8482,7 +8518,7 @@ function scaleProps(styleProps = {}, { props = [], scale = 1 } = {}, round = tru
|
|
|
8482
8518
|
let prop = props[i];
|
|
8483
8519
|
|
|
8484
8520
|
if (styleProps[prop] !== undefined) {
|
|
8485
|
-
styleProps[prop] = styleProps[prop].map(val => round ? roundTo(val * scale,
|
|
8521
|
+
styleProps[prop] = styleProps[prop].map(val => round ? roundTo(val * scale, 3) : val * scale);
|
|
8486
8522
|
}
|
|
8487
8523
|
}
|
|
8488
8524
|
return styleProps
|
|
@@ -8503,6 +8539,7 @@ function convertPathLengthAtt(el, {
|
|
|
8503
8539
|
});
|
|
8504
8540
|
|
|
8505
8541
|
let scale = elLength / pathLength;
|
|
8542
|
+
|
|
8506
8543
|
styleProps = scaleProps(styleProps, { props: ['stroke-dasharray', 'stroke-dashoffset'], scale });
|
|
8507
8544
|
|
|
8508
8545
|
// set absolute
|
|
@@ -9031,9 +9068,13 @@ function cleanUpSVG(svgMarkup, {
|
|
|
9031
9068
|
let stylePropsFiltered = {};
|
|
9032
9069
|
|
|
9033
9070
|
// convert pathLength before transforming
|
|
9034
|
-
if
|
|
9071
|
+
if(convertTransforms || attributesToGroup) convertPathLength=true;
|
|
9072
|
+
|
|
9073
|
+
if (convertPathLength ) {
|
|
9074
|
+
|
|
9035
9075
|
styleProps = convertPathLengthAtt(el, { styleProps });
|
|
9036
9076
|
remove = [...new Set([...remove, ...styleProps.remove])];
|
|
9077
|
+
|
|
9037
9078
|
}
|
|
9038
9079
|
|
|
9039
9080
|
// get parent styles
|
|
@@ -9211,7 +9252,7 @@ function cleanUpSVG(svgMarkup, {
|
|
|
9211
9252
|
|
|
9212
9253
|
// scale props like stroke width or dash-array before conversion
|
|
9213
9254
|
if (matrix && transComponents) {
|
|
9214
|
-
['stroke-width', 'stroke-dasharray'].forEach(att => {
|
|
9255
|
+
['stroke-width', 'stroke-dasharray', 'stroke-dashoffset'].forEach(att => {
|
|
9215
9256
|
let attVal = el.getAttribute(att);
|
|
9216
9257
|
let vals = attVal ? attVal.split(' ').filter(Boolean).map(Number).map(val => val * transComponents.scaleX) : [];
|
|
9217
9258
|
if (vals.length) el.setAttribute(att, vals.join(' '));
|
|
@@ -12342,7 +12383,6 @@ const presetSettings = {
|
|
|
12342
12383
|
addViewBox: true,
|
|
12343
12384
|
removeDimensions: true,
|
|
12344
12385
|
removeOffCanvas: true,
|
|
12345
|
-
|
|
12346
12386
|
/*
|
|
12347
12387
|
*/
|
|
12348
12388
|
}
|
|
@@ -12529,11 +12569,11 @@ function svgPathSimplify(input = '', settings = {}) {
|
|
|
12529
12569
|
original: 0,
|
|
12530
12570
|
new: 0,
|
|
12531
12571
|
saved: 0,
|
|
12532
|
-
svgSize:0,
|
|
12533
|
-
svgSizeOpt:0,
|
|
12534
|
-
compression:0,
|
|
12535
|
-
decimals:0,
|
|
12536
|
-
invalid:true
|
|
12572
|
+
svgSize: 0,
|
|
12573
|
+
svgSizeOpt: 0,
|
|
12574
|
+
compression: 0,
|
|
12575
|
+
decimals: 0,
|
|
12576
|
+
invalid: true
|
|
12537
12577
|
};
|
|
12538
12578
|
|
|
12539
12579
|
return { svg: dummySVG, d: '', polys: [], report, pathDataPlusArr: [], pathDataPlusArr_global: [], inputType: 'invalid', dOriginal: '' };
|
|
@@ -12703,6 +12743,12 @@ function svgPathSimplify(input = '', settings = {}) {
|
|
|
12703
12743
|
let { d, el } = path;
|
|
12704
12744
|
let isPoly = false;
|
|
12705
12745
|
|
|
12746
|
+
// disable reordering for elements with stroke dash-array
|
|
12747
|
+
if (el && (el.hasAttribute('stroke-dasharray') || el.hasAttribute('stroke-dashoffset'))) {
|
|
12748
|
+
optimizeOrder = false;
|
|
12749
|
+
|
|
12750
|
+
}
|
|
12751
|
+
|
|
12706
12752
|
// if polygon we already heave absolute coordinates
|
|
12707
12753
|
|
|
12708
12754
|
let pathData = parsePathDataNormalized(d, { quadraticToCubic, arcToCubic });
|