svg-path-simplify 0.3.6 → 0.4.1
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 +24 -0
- package/README.md +38 -85
- package/dist/svg-path-simplify.esm.js +1405 -528
- package/dist/svg-path-simplify.esm.min.js +2 -2
- package/dist/svg-path-simplify.js +1405 -528
- package/dist/svg-path-simplify.min.js +2 -2
- package/dist/svg-path-simplify.pathdata.esm.js +76 -15
- package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
- package/docs/api.md +127 -0
- package/index.html +216 -277
- package/package.json +1 -1
- package/src/constants.js +8 -1
- package/src/pathData_simplify_cubic.js +0 -8
- package/src/pathSimplify-main.js +25 -9
- package/src/svg_flatten_transforms.js +1 -1
- package/src/svgii/convert_colors.js +52 -5
- package/src/svgii/convert_units.js +25 -10
- package/src/svgii/pathData_analyze.js +33 -13
- package/src/svgii/pathData_convert.js +62 -6
- package/src/svgii/pathData_parse_els.js +21 -5
- package/src/svgii/pathData_toPolygon.js +3 -1
- package/src/svgii/pathData_transform.js +307 -0
- package/src/svgii/svg-styles-getTransforms.js +119 -8
- package/src/svgii/svg-styles-to-attributes-const.js +19 -4
- package/src/svgii/svg_cleanup.js +319 -97
- package/src/svgii/svg_el_parse_style_props.js +218 -76
- package/src/svgii/transform_qr_decompose.js +74 -0
- package/src/svgii/pathData_scale.js +0 -42
- package/src/svgii/svg-styles-to-attributes.js +0 -236
|
@@ -123,9 +123,16 @@
|
|
|
123
123
|
} = Math;
|
|
124
124
|
|
|
125
125
|
const rad2Deg = 180/Math.PI;
|
|
126
|
+
const deg2rad = Math.PI/180;
|
|
126
127
|
const root2 = 1.4142135623730951;
|
|
127
128
|
const svgNs = 'http://www.w3.org/2000/svg';
|
|
128
129
|
|
|
130
|
+
// 1/2.54
|
|
131
|
+
const inch2cm = 0.39370078;
|
|
132
|
+
|
|
133
|
+
// 1/72
|
|
134
|
+
const inch2pt = 0.01388889;
|
|
135
|
+
|
|
129
136
|
/*
|
|
130
137
|
import {abs, acos, asin, atan, atan2, ceil, cos, exp, floor,
|
|
131
138
|
log, max, min, pow, random, round, sin, sqrt, tan, PI} from '/.constants.js';
|
|
@@ -1472,6 +1479,118 @@
|
|
|
1472
1479
|
return segmentPoints;
|
|
1473
1480
|
}
|
|
1474
1481
|
|
|
1482
|
+
function parseColor(str) {
|
|
1483
|
+
let type = str.startsWith('#') ? 'rgbHex' : (str.includes('(') ? 'fn' : typeof str);
|
|
1484
|
+
let col = {};
|
|
1485
|
+
let mode = null;
|
|
1486
|
+
let colObj = { mode: null, values: [] };
|
|
1487
|
+
if (type === 'rgbHex') {
|
|
1488
|
+
col = hex2Rgb(str);
|
|
1489
|
+
mode = 'rgba';
|
|
1490
|
+
}
|
|
1491
|
+
else if (type === 'fn') {
|
|
1492
|
+
let colVals = str.split(/\(|\)/).filter(Boolean);
|
|
1493
|
+
if (colVals.length < 2) return str;
|
|
1494
|
+
|
|
1495
|
+
mode = colVals[0];
|
|
1496
|
+
let colorComponents = colVals[1].split(/,| /).filter(Boolean).map(Number);
|
|
1497
|
+
|
|
1498
|
+
let keys = mode.split('');
|
|
1499
|
+
keys.forEach((k, i) => {
|
|
1500
|
+
let val = colorComponents[i];
|
|
1501
|
+
if (mode === 'rgba' && k === 'a') {
|
|
1502
|
+
val = Math.floor(val * 255);
|
|
1503
|
+
}
|
|
1504
|
+
col[k] = val;
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
else if (type === 'string') {
|
|
1508
|
+
colObj.mode = 'keyword';
|
|
1509
|
+
colObj.values = [str];
|
|
1510
|
+
return colObj
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
if (mode === 'rgba' || mode === 'rgb') {
|
|
1514
|
+
col.a = !col.a ? 255 : col.a;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
colObj.mode = mode;
|
|
1518
|
+
colObj.values = Object.values(col);
|
|
1519
|
+
|
|
1520
|
+
return colObj;
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
function hex2Rgb(hex = '') {
|
|
1524
|
+
// Remove # if present
|
|
1525
|
+
if (hex.startsWith('#')) hex = hex.substring(1);
|
|
1526
|
+
|
|
1527
|
+
// normalize short notation (e.g., 'fff' or 'ffff')
|
|
1528
|
+
if (hex.length === 3) {
|
|
1529
|
+
hex = hex.split('').map(char => char + char).join('');
|
|
1530
|
+
} else if (hex.length === 4) {
|
|
1531
|
+
// Handle short notation with alpha (e.g., 'ffff')
|
|
1532
|
+
hex = hex.split('').map(char => char + char).join('');
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
let r = 0, g = 0, b = 0, a = 0;
|
|
1536
|
+
|
|
1537
|
+
// invalid
|
|
1538
|
+
if (hex.length < 6 || hex.length > 8) {
|
|
1539
|
+
console.warn('Invalid hex format');
|
|
1540
|
+
return { r, g, b, a };
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
let isRgba = hex.length === 8;
|
|
1544
|
+
|
|
1545
|
+
let numericValue = parseInt(hex, 16);
|
|
1546
|
+
r = isRgba ? parseInt(hex.substring(0, 2), 16) : numericValue >> 16 & 0xFF;
|
|
1547
|
+
g = isRgba ? parseInt(hex.substring(2, 4), 16) : numericValue >> 8 & 0xFF;
|
|
1548
|
+
b = isRgba ? parseInt(hex.substring(4, 6), 16) : numericValue & 0xFF;
|
|
1549
|
+
a = isRgba ? parseInt(hex.substring(6, 8), 16) : 255;
|
|
1550
|
+
|
|
1551
|
+
return { r, g, b, a };
|
|
1552
|
+
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
function rgba2Hex({ r = 0, g = 0, b = 0, a = 255, values = [] }) {
|
|
1556
|
+
// Helper function to convert number to 2-digit hex
|
|
1557
|
+
const toHex = (num) => {
|
|
1558
|
+
const hex = Math.min(255, Math.max(0, Math.round(num))).toString(16);
|
|
1559
|
+
return hex.length === 1 ? '0' + hex : hex;
|
|
1560
|
+
};
|
|
1561
|
+
|
|
1562
|
+
// convert from number array input
|
|
1563
|
+
if (!r && !g && !b && values.length) {
|
|
1564
|
+
[r, g, b, a = 255] = values;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
// Get hex values
|
|
1568
|
+
let rHex = toHex(r);
|
|
1569
|
+
let gHex = toHex(g);
|
|
1570
|
+
let bHex = toHex(b);
|
|
1571
|
+
let aHex = a < 255 ? toHex(a) : 0;
|
|
1572
|
+
|
|
1573
|
+
let allowsShort = rHex[0] === rHex[1] && gHex[0] === gHex[1] && bHex[0] === bHex[1];
|
|
1574
|
+
|
|
1575
|
+
// Check for 3-character RGB short notation (e.g., #fff)
|
|
1576
|
+
if (!aHex && allowsShort) {
|
|
1577
|
+
return `#${rHex[0]}${gHex[0]}${bHex[0]}`;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
// Check for 4-character RGBA short notation (e.g., #ffff)
|
|
1581
|
+
if (aHex && allowsShort) {
|
|
1582
|
+
return `#${rHex[0]}${gHex[0]}${bHex[0]}${aHex[0]}`;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
// Return 6-character RGB if no alpha
|
|
1586
|
+
if (!aHex) {
|
|
1587
|
+
return `#${rHex}${gHex}${bHex}`;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
// Return 8-character RGBA
|
|
1591
|
+
return `#${rHex}${gHex}${bHex}${aHex}`;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1475
1594
|
function detectAccuracyPoly(pts) {
|
|
1476
1595
|
let dims = [];
|
|
1477
1596
|
|
|
@@ -1606,12 +1725,23 @@
|
|
|
1606
1725
|
|
|
1607
1726
|
const horizontalProps = ['x', 'cx', 'rx', 'dx', 'width', 'translateX'];
|
|
1608
1727
|
const verticalProps = ['y', 'cy', 'ry', 'dy', 'height', 'translateY'];
|
|
1728
|
+
const transHorizontal = ['scaleX', 'translateX', 'skewX'];
|
|
1729
|
+
const transVertical = ['scaleY', 'translateY', 'skewY'];
|
|
1730
|
+
|
|
1731
|
+
const colorProps = ['fill', 'stroke', 'stop-color'];
|
|
1609
1732
|
|
|
1610
1733
|
const geometryEls = [
|
|
1611
1734
|
"path",
|
|
1612
1735
|
...shapeEls
|
|
1613
1736
|
];
|
|
1614
1737
|
|
|
1738
|
+
const renderedEls = [
|
|
1739
|
+
"text",
|
|
1740
|
+
"textPath",
|
|
1741
|
+
"tspan",
|
|
1742
|
+
...geometryEls
|
|
1743
|
+
];
|
|
1744
|
+
|
|
1615
1745
|
const textEls = [
|
|
1616
1746
|
"textPath",
|
|
1617
1747
|
"text",
|
|
@@ -1877,7 +2007,7 @@
|
|
|
1877
2007
|
|
|
1878
2008
|
defaults: {
|
|
1879
2009
|
|
|
1880
|
-
transform: ["none", "matrix(1, 0, 0, 1, 0, 0)"],
|
|
2010
|
+
transform: ["none", "matrix(1, 0, 0, 1, 0, 0)", "matrix(1 0 0 1 0 0)"],
|
|
1881
2011
|
"transform-origin": ["0px, 0px", "0 0"],
|
|
1882
2012
|
rx: ["0", "0px"],
|
|
1883
2013
|
ry: ["0", "0px"],
|
|
@@ -1888,9 +2018,9 @@
|
|
|
1888
2018
|
"color": ["black", "rgb(0, 0, 0)", "rgba(0, 0, 0, 0)", "#000", "#000000"],
|
|
1889
2019
|
|
|
1890
2020
|
stroke: ["none"],
|
|
1891
|
-
"stroke-width": ["1", "1px"],
|
|
1892
2021
|
opacity: ["1"],
|
|
1893
2022
|
"fill-opacity": ["1"],
|
|
2023
|
+
"stroke-width": ["1", "1px"],
|
|
1894
2024
|
"stroke-opacity": ["1"],
|
|
1895
2025
|
"stroke-linecap": ["butt"],
|
|
1896
2026
|
"stroke-miterlimit": ["4"],
|
|
@@ -1978,20 +2108,21 @@
|
|
|
1978
2108
|
// only required for circle r normalization when height!=width
|
|
1979
2109
|
normalizedDiagonal = width === height ? false : normalizedDiagonal;
|
|
1980
2110
|
|
|
2111
|
+
let type = typeof value;
|
|
1981
2112
|
if (!value) return value;
|
|
1982
2113
|
|
|
1983
2114
|
// check if value is string
|
|
1984
|
-
let
|
|
1985
|
-
let
|
|
2115
|
+
let isNum = type === 'number' ? true : isNumericValue(value);
|
|
2116
|
+
let isArray = type === 'string' ? value.split(/,| /).length > 1 : false;
|
|
2117
|
+
let isFunction = type === 'string' ? value.includes('(') : false;
|
|
1986
2118
|
|
|
1987
|
-
let isNum = isNumericValue(value);
|
|
1988
2119
|
if (!isNum || isArray || isFunction) return value
|
|
1989
2120
|
|
|
1990
2121
|
// check unit if not specified
|
|
1991
2122
|
unit = !unit ? getUnit(value) : unit;
|
|
1992
2123
|
|
|
1993
2124
|
let val = parseFloat(value);
|
|
1994
|
-
let scale=1;
|
|
2125
|
+
let scale = 1;
|
|
1995
2126
|
let scaleRoot = Math.sqrt(width * width + height * height) / root2;
|
|
1996
2127
|
|
|
1997
2128
|
// no unit - already pixes/user unit
|
|
@@ -2022,15 +2153,31 @@
|
|
|
2022
2153
|
case "in":
|
|
2023
2154
|
scale = dpi;
|
|
2024
2155
|
break;
|
|
2156
|
+
|
|
2025
2157
|
case "pt":
|
|
2026
|
-
|
|
2158
|
+
// 1/72
|
|
2159
|
+
scale = dpi * inch2pt;
|
|
2160
|
+
break;
|
|
2161
|
+
|
|
2162
|
+
case "pc":
|
|
2163
|
+
// 1/6
|
|
2164
|
+
scale = dpi * 0.16666667;
|
|
2027
2165
|
break;
|
|
2166
|
+
|
|
2028
2167
|
case "cm":
|
|
2029
|
-
|
|
2168
|
+
// 1/2.54
|
|
2169
|
+
scale = inch2cm * dpi;
|
|
2030
2170
|
break;
|
|
2031
2171
|
case "mm":
|
|
2032
|
-
|
|
2172
|
+
|
|
2173
|
+
scale = inch2cm * dpi * 0.1;
|
|
2174
|
+
break;
|
|
2175
|
+
|
|
2176
|
+
// has anyone ever used it?
|
|
2177
|
+
case "Q":
|
|
2178
|
+
scale = inch2cm * dpi * 0.025;
|
|
2033
2179
|
break;
|
|
2180
|
+
|
|
2034
2181
|
// just a default approximation
|
|
2035
2182
|
case "em":
|
|
2036
2183
|
case "rem":
|
|
@@ -3080,6 +3227,10 @@
|
|
|
3080
3227
|
let pathDataProps = [pathData[0]];
|
|
3081
3228
|
let len = pathData.length;
|
|
3082
3229
|
|
|
3230
|
+
// threshold for corner angles: 10 deg
|
|
3231
|
+
|
|
3232
|
+
// define angle threshold for semi extremes
|
|
3233
|
+
|
|
3083
3234
|
for (let c = 2; len && c <= len; c++) {
|
|
3084
3235
|
|
|
3085
3236
|
let com = pathData[c - 1];
|
|
@@ -3099,7 +3250,7 @@
|
|
|
3099
3250
|
(type === 'C' ? [p0, cp1, cp2, p] : [p0, cp1, p]) :
|
|
3100
3251
|
([p0, p]);
|
|
3101
3252
|
let thresholdLength = dimA * 0.1;
|
|
3102
|
-
let threshold = thresholdLength*0.01;
|
|
3253
|
+
let threshold = thresholdLength * 0.01;
|
|
3103
3254
|
|
|
3104
3255
|
// bezier types
|
|
3105
3256
|
let isBezier = type === 'Q' || type === 'C';
|
|
@@ -3116,14 +3267,22 @@
|
|
|
3116
3267
|
let dx = type === 'C' ? Math.abs(com.cp2.x - com.p.x) : Math.abs(com.cp1.x - com.p.x);
|
|
3117
3268
|
let dy = type === 'C' ? Math.abs(com.cp2.y - com.p.y) : Math.abs(com.cp1.y - com.p.y);
|
|
3118
3269
|
|
|
3119
|
-
let horizontal = (dy === 0 || dy
|
|
3120
|
-
let vertical = (dx === 0 || dx
|
|
3270
|
+
let horizontal = (dy === 0 || dy <= threshold) && dx > 0;
|
|
3271
|
+
let vertical = (dx === 0 || dx <= threshold) && dy > 0;
|
|
3121
3272
|
|
|
3122
3273
|
if (horizontal || vertical) {
|
|
3123
3274
|
hasExtremes = true;
|
|
3124
3275
|
}
|
|
3125
3276
|
|
|
3126
3277
|
// is extreme relative to bounding box
|
|
3278
|
+
|
|
3279
|
+
// (cp1.x===p0.x && cp1.y!==p0.y ) ||
|
|
3280
|
+
if ((cp1.x === p0.x && cp1.y !== p0.y) || (cp1.y === p0.y && cp1.x !== p0.x)) {
|
|
3281
|
+
|
|
3282
|
+
pathDataProps[pathDataProps.length - 1].extreme = true;
|
|
3283
|
+
|
|
3284
|
+
}
|
|
3285
|
+
|
|
3127
3286
|
if ((p.x === left || p.y === top || p.x === right || p.y === bottom)) {
|
|
3128
3287
|
hasExtremes = true;
|
|
3129
3288
|
}
|
|
@@ -3133,6 +3292,7 @@
|
|
|
3133
3292
|
let couldHaveExtremes = bezierhasExtreme(null, commandPts);
|
|
3134
3293
|
if (couldHaveExtremes) {
|
|
3135
3294
|
let tArr = getTatAngles(commandPts);
|
|
3295
|
+
|
|
3136
3296
|
if (tArr.length && (tArr[0] > 0.2)) {
|
|
3137
3297
|
hasExtremes = true;
|
|
3138
3298
|
}
|
|
@@ -3187,21 +3347,23 @@
|
|
|
3187
3347
|
|
|
3188
3348
|
let signChange2 = (areaCpt < 0 && com.cptArea > 0) || (areaCpt > 0 && com.cptArea < 0) ? true : false;
|
|
3189
3349
|
|
|
3190
|
-
let isCorner
|
|
3350
|
+
let isCorner = !isFlat && signChange2;
|
|
3191
3351
|
if (isCorner) com.corner = true;
|
|
3192
3352
|
}
|
|
3193
3353
|
}
|
|
3194
3354
|
|
|
3195
|
-
|
|
3355
|
+
pathDataProps.push(com);
|
|
3356
|
+
|
|
3357
|
+
}
|
|
3358
|
+
|
|
3359
|
+
|
|
3360
|
+
if (debug) {
|
|
3361
|
+
pathDataProps.forEach(com=>{
|
|
3196
3362
|
|
|
3197
3363
|
if (com.directionChange) renderPoint(markers, com.p, 'orange', '1.5%', '0.5');
|
|
3198
3364
|
if (com.corner) renderPoint(markers, com.p, 'magenta', '1.5%', '0.5');
|
|
3199
3365
|
if (com.extreme) renderPoint(markers, com.p, 'cyan', '1%', '0.5');
|
|
3200
|
-
|
|
3201
|
-
}
|
|
3202
|
-
|
|
3203
|
-
pathDataProps.push(com);
|
|
3204
|
-
|
|
3366
|
+
});
|
|
3205
3367
|
}
|
|
3206
3368
|
|
|
3207
3369
|
let dimA = (width + height) / 2;
|
|
@@ -3752,6 +3914,7 @@
|
|
|
3752
3914
|
hasShorthands = true,
|
|
3753
3915
|
hasQuadratics = true,
|
|
3754
3916
|
hasArcs = true,
|
|
3917
|
+
optimizeArcs = true,
|
|
3755
3918
|
testTypes = false
|
|
3756
3919
|
|
|
3757
3920
|
} = {}) {
|
|
@@ -3771,23 +3934,68 @@
|
|
|
3771
3934
|
toRelative = toAbsolute ? false : toRelative;
|
|
3772
3935
|
toShorthands = toLonghands ? false : toShorthands;
|
|
3773
3936
|
|
|
3774
|
-
if (
|
|
3775
|
-
|
|
3776
|
-
if (toShorthands) pathData = pathDataToShorthands(pathData);
|
|
3937
|
+
if (toAbsolute) pathData = pathDataToAbsolute(pathData);
|
|
3777
3938
|
if (hasShorthands && toLonghands) pathData = pathDataToLonghands(pathData);
|
|
3778
3939
|
|
|
3779
|
-
|
|
3940
|
+
// minify semicircle radii
|
|
3941
|
+
if (optimizeArcs) pathData = optimizeArcPathData(pathData);
|
|
3942
|
+
|
|
3943
|
+
if (toShorthands) pathData = pathDataToShorthands(pathData);
|
|
3780
3944
|
|
|
3781
3945
|
if (hasArcs && arcToCubic) pathData = pathDataArcsToCubics(pathData);
|
|
3782
3946
|
|
|
3947
|
+
if (hasQuadratics && quadraticToCubic) pathData = pathDataQuadraticToCubic(pathData);
|
|
3948
|
+
|
|
3783
3949
|
// pre round - before relative conversion to minimize distortions
|
|
3784
3950
|
if (decimals > -1 && toRelative) pathData = roundPathData(pathData, decimals);
|
|
3951
|
+
|
|
3785
3952
|
if (toRelative) pathData = pathDataToRelative(pathData);
|
|
3786
3953
|
if (decimals > -1) pathData = roundPathData(pathData, decimals);
|
|
3787
3954
|
|
|
3788
3955
|
return pathData
|
|
3789
3956
|
}
|
|
3790
3957
|
|
|
3958
|
+
/**
|
|
3959
|
+
*
|
|
3960
|
+
* @param {*} pathData
|
|
3961
|
+
* @returns
|
|
3962
|
+
*/
|
|
3963
|
+
|
|
3964
|
+
function optimizeArcPathData(pathData = []) {
|
|
3965
|
+
pathData.forEach((com, i) => {
|
|
3966
|
+
let { type, values } = com;
|
|
3967
|
+
if (type === 'A') {
|
|
3968
|
+
let [rx, ry, largeArc, x, y] = [values[0], values[1], values[3], values[5], values[6]];
|
|
3969
|
+
let comPrev = pathData[i - 1];
|
|
3970
|
+
let [x0, y0] = [comPrev.values[comPrev.values.length - 2], comPrev.values[comPrev.values.length - 1]];
|
|
3971
|
+
let M = { x: x0, y: y0 };
|
|
3972
|
+
let p = { x, y };
|
|
3973
|
+
|
|
3974
|
+
// rx and ry are large enough
|
|
3975
|
+
if (rx >= 1 && (x === x0 || y === y0)) {
|
|
3976
|
+
let diff = Math.abs(rx - ry) / rx;
|
|
3977
|
+
|
|
3978
|
+
// rx~==ry
|
|
3979
|
+
if (diff < 0.01) {
|
|
3980
|
+
|
|
3981
|
+
// test radius against mid point
|
|
3982
|
+
let pMid = interpolate(M, p, 0.5);
|
|
3983
|
+
let distM = getDistance(pMid, M);
|
|
3984
|
+
let rDiff = Math.abs(distM - rx) / rx;
|
|
3985
|
+
|
|
3986
|
+
// half distance between mid and start point should be ~ equal
|
|
3987
|
+
if(rDiff<0.01){
|
|
3988
|
+
pathData[i].values[0] = 1;
|
|
3989
|
+
pathData[i].values[1] = 1;
|
|
3990
|
+
pathData[i].values[2] = 0;
|
|
3991
|
+
}
|
|
3992
|
+
}
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
});
|
|
3996
|
+
return pathData;
|
|
3997
|
+
}
|
|
3998
|
+
|
|
3791
3999
|
/**
|
|
3792
4000
|
* parse normalized
|
|
3793
4001
|
*/
|
|
@@ -4224,7 +4432,7 @@
|
|
|
4224
4432
|
let dx1 = (p0.x - cpPrev.x);
|
|
4225
4433
|
let dy1 = (p0.y - cpPrev.y);
|
|
4226
4434
|
|
|
4227
|
-
maxDist = getDistManhattan(cpPrev, cpFirst) * 0.
|
|
4435
|
+
maxDist = getDistManhattan(cpPrev, cpFirst) * 0.01;
|
|
4228
4436
|
|
|
4229
4437
|
// reflected cp
|
|
4230
4438
|
let cpR = { x: cpPrev.x + dx1 * 2, y: cpPrev.y + dy1 * 2 };
|
|
@@ -5092,6 +5300,324 @@
|
|
|
5092
5300
|
return svg;
|
|
5093
5301
|
}
|
|
5094
5302
|
|
|
5303
|
+
/**
|
|
5304
|
+
* scale pathData
|
|
5305
|
+
*/
|
|
5306
|
+
function transformPathData(pathData, matrix) {
|
|
5307
|
+
|
|
5308
|
+
// new pathdata
|
|
5309
|
+
let pathDataTrans = [];
|
|
5310
|
+
|
|
5311
|
+
// transform point by 2d matrix
|
|
5312
|
+
const transformPoint = (pt, matrix) => {
|
|
5313
|
+
let { a, b, c, d, e, f } = matrix;
|
|
5314
|
+
let { x, y } = pt;
|
|
5315
|
+
return { x: a * x + c * y + e, y: b * x + d * y + f };
|
|
5316
|
+
};
|
|
5317
|
+
|
|
5318
|
+
const normalizeMatrix = (matrix) => {
|
|
5319
|
+
matrix =
|
|
5320
|
+
typeof matrix === "string"
|
|
5321
|
+
? (matrix = matrix
|
|
5322
|
+
.replace(/^matrix\(|\)$/g, "")
|
|
5323
|
+
.split(",")
|
|
5324
|
+
.map(Number))
|
|
5325
|
+
: matrix;
|
|
5326
|
+
matrix = !Array.isArray(matrix)
|
|
5327
|
+
? {
|
|
5328
|
+
a: matrix.a,
|
|
5329
|
+
b: matrix.b,
|
|
5330
|
+
c: matrix.c,
|
|
5331
|
+
d: matrix.d,
|
|
5332
|
+
e: matrix.e,
|
|
5333
|
+
f: matrix.f
|
|
5334
|
+
}
|
|
5335
|
+
: {
|
|
5336
|
+
a: matrix[0],
|
|
5337
|
+
b: matrix[1],
|
|
5338
|
+
c: matrix[2],
|
|
5339
|
+
d: matrix[3],
|
|
5340
|
+
e: matrix[4],
|
|
5341
|
+
f: matrix[5]
|
|
5342
|
+
};
|
|
5343
|
+
return matrix;
|
|
5344
|
+
};
|
|
5345
|
+
|
|
5346
|
+
const transformArc = (p0, values, matrix) => {
|
|
5347
|
+
let [rx, ry, angle, largeArc, sweep, x, y] = values;
|
|
5348
|
+
|
|
5349
|
+
/**
|
|
5350
|
+
* parametrize arc command
|
|
5351
|
+
* to get the actual arc params
|
|
5352
|
+
*/
|
|
5353
|
+
let arcData = svgArcToCenterParam(
|
|
5354
|
+
p0.x,
|
|
5355
|
+
p0.y,
|
|
5356
|
+
values[0],
|
|
5357
|
+
values[1],
|
|
5358
|
+
angle,
|
|
5359
|
+
largeArc,
|
|
5360
|
+
sweep,
|
|
5361
|
+
x,
|
|
5362
|
+
y
|
|
5363
|
+
);
|
|
5364
|
+
({ rx, ry } = arcData);
|
|
5365
|
+
let { a, b, c, d, e, f } = matrix;
|
|
5366
|
+
|
|
5367
|
+
let ellipsetr = transformEllipse(rx, ry, angle, matrix);
|
|
5368
|
+
let p = transformPoint({ x: x, y: y }, matrix);
|
|
5369
|
+
|
|
5370
|
+
// adjust sweep if flipped
|
|
5371
|
+
let denom = a * a + b * b;
|
|
5372
|
+
let scaleX = Math.sqrt(denom);
|
|
5373
|
+
let scaleY = (a * d - c * b) / scaleX;
|
|
5374
|
+
|
|
5375
|
+
let flipX = scaleX < 0 ? true : false;
|
|
5376
|
+
let flipY = scaleY < 0 ? true : false;
|
|
5377
|
+
|
|
5378
|
+
// adjust sweep
|
|
5379
|
+
if (flipX || flipY) {
|
|
5380
|
+
sweep = sweep === 0 ? 1 : 0;
|
|
5381
|
+
}
|
|
5382
|
+
|
|
5383
|
+
return {
|
|
5384
|
+
type: 'A',
|
|
5385
|
+
values: [
|
|
5386
|
+
ellipsetr.rx,
|
|
5387
|
+
ellipsetr.ry,
|
|
5388
|
+
ellipsetr.ax,
|
|
5389
|
+
largeArc,
|
|
5390
|
+
sweep,
|
|
5391
|
+
p.x,
|
|
5392
|
+
p.y]
|
|
5393
|
+
};
|
|
5394
|
+
};
|
|
5395
|
+
|
|
5396
|
+
// normalize matrix input
|
|
5397
|
+
matrix = normalizeMatrix(matrix);
|
|
5398
|
+
|
|
5399
|
+
let matrixStr = [matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f]
|
|
5400
|
+
.map((val) => {
|
|
5401
|
+
return +val.toFixed(1);
|
|
5402
|
+
})
|
|
5403
|
+
.join("");
|
|
5404
|
+
|
|
5405
|
+
// no transform: quit
|
|
5406
|
+
if (matrixStr === "100100") {
|
|
5407
|
+
|
|
5408
|
+
return pathData;
|
|
5409
|
+
}
|
|
5410
|
+
|
|
5411
|
+
pathData.forEach((com, i) => {
|
|
5412
|
+
let { type, values } = com;
|
|
5413
|
+
let typeRel = type.toLowerCase();
|
|
5414
|
+
let comPrev = i > 0 ? pathData[i - 1] : pathData[i];
|
|
5415
|
+
let comPrevValues = comPrev.values;
|
|
5416
|
+
let comPrevValuesL = comPrevValues.length;
|
|
5417
|
+
let p0 = {
|
|
5418
|
+
x: comPrevValues[comPrevValuesL - 2],
|
|
5419
|
+
y: comPrevValues[comPrevValuesL - 1]
|
|
5420
|
+
};
|
|
5421
|
+
({ x: values[values.length - 2], y: values[values.length - 1] });
|
|
5422
|
+
let comT = { type: type, values: [] };
|
|
5423
|
+
|
|
5424
|
+
switch (typeRel) {
|
|
5425
|
+
case "a":
|
|
5426
|
+
comT = transformArc(p0, values, matrix);
|
|
5427
|
+
break;
|
|
5428
|
+
|
|
5429
|
+
default:
|
|
5430
|
+
// all other point based commands
|
|
5431
|
+
if (values.length) {
|
|
5432
|
+
for (let i = 0; i < values.length; i += 2) {
|
|
5433
|
+
let ptTrans = transformPoint(
|
|
5434
|
+
{ x: com.values[i], y: com.values[i + 1] },
|
|
5435
|
+
matrix
|
|
5436
|
+
);
|
|
5437
|
+
|
|
5438
|
+
comT.values[i] = ptTrans.x;
|
|
5439
|
+
comT.values[i + 1] = ptTrans.y;
|
|
5440
|
+
}
|
|
5441
|
+
}
|
|
5442
|
+
}
|
|
5443
|
+
|
|
5444
|
+
pathDataTrans.push(comT);
|
|
5445
|
+
});
|
|
5446
|
+
|
|
5447
|
+
return pathDataTrans;
|
|
5448
|
+
}
|
|
5449
|
+
|
|
5450
|
+
/**
|
|
5451
|
+
* Based on: https://github.com/fontello/svgpath/blob/master/lib/ellipse.js
|
|
5452
|
+
* and fork: https://github.com/kpym/SVGPathy/blob/master/lib/ellipse.js
|
|
5453
|
+
*/
|
|
5454
|
+
|
|
5455
|
+
function transformEllipse(rx, ry, ax, matrix) {
|
|
5456
|
+
const torad = Math.PI / 180;
|
|
5457
|
+
const epsilon = 1e-7;
|
|
5458
|
+
|
|
5459
|
+
matrix = !Array.isArray(matrix)
|
|
5460
|
+
? matrix
|
|
5461
|
+
: {
|
|
5462
|
+
a: matrix[0],
|
|
5463
|
+
b: matrix[1],
|
|
5464
|
+
c: matrix[2],
|
|
5465
|
+
d: matrix[3],
|
|
5466
|
+
e: matrix[4],
|
|
5467
|
+
f: matrix[5]
|
|
5468
|
+
};
|
|
5469
|
+
|
|
5470
|
+
// We consider the current ellipse as image of the unit circle
|
|
5471
|
+
// by first scale(rx,ry) and then rotate(ax) ...
|
|
5472
|
+
// So we apply ma = m x rotate(ax) x scale(rx,ry) to the unit circle.
|
|
5473
|
+
let c = Math.cos(ax * torad),
|
|
5474
|
+
s = Math.sin(ax * torad);
|
|
5475
|
+
let ma = [
|
|
5476
|
+
rx * (matrix.a * c + matrix.c * s),
|
|
5477
|
+
rx * (matrix.b * c + matrix.d * s),
|
|
5478
|
+
ry * (-matrix.a * s + matrix.c * c),
|
|
5479
|
+
ry * (-matrix.b * s + matrix.d * c)
|
|
5480
|
+
];
|
|
5481
|
+
|
|
5482
|
+
// ma * transpose(ma) = [ J L ]
|
|
5483
|
+
// [ L K ]
|
|
5484
|
+
// L is calculated later (if the image is not a circle)
|
|
5485
|
+
let J = ma[0] * ma[0] + ma[2] * ma[2],
|
|
5486
|
+
K = ma[1] * ma[1] + ma[3] * ma[3];
|
|
5487
|
+
|
|
5488
|
+
// the sqrt of the discriminant of the characteristic polynomial of ma * transpose(ma)
|
|
5489
|
+
// this is also the geometric mean of the eigenvalues
|
|
5490
|
+
let D = Math.sqrt(
|
|
5491
|
+
((ma[0] - ma[3]) * (ma[0] - ma[3]) + (ma[2] + ma[1]) * (ma[2] + ma[1])) *
|
|
5492
|
+
((ma[0] + ma[3]) * (ma[0] + ma[3]) + (ma[2] - ma[1]) * (ma[2] - ma[1]))
|
|
5493
|
+
);
|
|
5494
|
+
|
|
5495
|
+
// the arithmetic mean of the eigenvalues
|
|
5496
|
+
let JK = (J + K) / 2;
|
|
5497
|
+
|
|
5498
|
+
// check if the image is (almost) a circle
|
|
5499
|
+
if (D <= epsilon) {
|
|
5500
|
+
rx = ry = Math.sqrt(JK);
|
|
5501
|
+
ax = 0;
|
|
5502
|
+
return { rx: rx, ry: ry, ax: ax };
|
|
5503
|
+
}
|
|
5504
|
+
|
|
5505
|
+
// check if ma * transpose(ma) is (almost) diagonal
|
|
5506
|
+
if (Math.abs(D - Math.abs(J - K)) <= epsilon) {
|
|
5507
|
+
rx = Math.sqrt(J);
|
|
5508
|
+
ry = Math.sqrt(K);
|
|
5509
|
+
ax = 0;
|
|
5510
|
+
return { rx: rx, ry: ry, ax: ax };
|
|
5511
|
+
}
|
|
5512
|
+
|
|
5513
|
+
// if it is not a circle, nor diagonal
|
|
5514
|
+
let L = ma[0] * ma[1] + ma[2] * ma[3];
|
|
5515
|
+
|
|
5516
|
+
// {l1,l2} = the two eigen values of ma * transpose(ma)
|
|
5517
|
+
let l1 = JK + D / 2,
|
|
5518
|
+
l2 = JK - D / 2;
|
|
5519
|
+
|
|
5520
|
+
// the x - axis - rotation angle is the argument of the l1 - eigenvector
|
|
5521
|
+
if (Math.abs(L) <= epsilon && Math.abs(l1 - K) <= epsilon) {
|
|
5522
|
+
// if (ax == 90) => ax = 0 and exchange axes
|
|
5523
|
+
ax = 0;
|
|
5524
|
+
rx = Math.sqrt(l2);
|
|
5525
|
+
ry = Math.sqrt(l1);
|
|
5526
|
+
return { rx: rx, ry: ry, ax: ax };
|
|
5527
|
+
}
|
|
5528
|
+
|
|
5529
|
+
ax =
|
|
5530
|
+
Math.atan(Math.abs(L) > Math.abs(l1 - K) ? (l1 - J) / L : L / (l1 - K)) /
|
|
5531
|
+
torad; // the angle in degree
|
|
5532
|
+
|
|
5533
|
+
// if ax > 0 => rx = sqrt(l1), ry = sqrt(l2), else exchange axes and ax += 90
|
|
5534
|
+
if (ax >= 0) {
|
|
5535
|
+
// if ax in [0,90]
|
|
5536
|
+
rx = Math.sqrt(l1);
|
|
5537
|
+
ry = Math.sqrt(l2);
|
|
5538
|
+
} else {
|
|
5539
|
+
// if ax in ]-90,0[ => exchange axes
|
|
5540
|
+
ax += 90;
|
|
5541
|
+
rx = Math.sqrt(l2);
|
|
5542
|
+
ry = Math.sqrt(l1);
|
|
5543
|
+
}
|
|
5544
|
+
|
|
5545
|
+
return { rx: rx, ry: ry, ax: ax };
|
|
5546
|
+
}
|
|
5547
|
+
|
|
5548
|
+
/**
|
|
5549
|
+
* Decompose matrix to readable transform properties
|
|
5550
|
+
* translate() rotate() scale() etc.
|
|
5551
|
+
* based on @AndreaBogazzi's answer
|
|
5552
|
+
* https://stackoverflow.com/questions/5107134/find-the-rotation-and-skew-of-a-matrix-transformation#32125700
|
|
5553
|
+
* return object with seperate transform properties
|
|
5554
|
+
* and ready to use css or svg attribute strings
|
|
5555
|
+
*/
|
|
5556
|
+
function qrDecomposeMatrix(matrix, precision = 4) {
|
|
5557
|
+
let { a, b, c, d, e, f } = matrix;
|
|
5558
|
+
// matrix is array
|
|
5559
|
+
if (Array.isArray(matrix)) {
|
|
5560
|
+
[a, b, c, d, e, f] = matrix;
|
|
5561
|
+
}
|
|
5562
|
+
let angle = Math.atan2(b, a),
|
|
5563
|
+
denom = Math.pow(a, 2) + Math.pow(b, 2),
|
|
5564
|
+
scaleX = Math.sqrt(denom),
|
|
5565
|
+
scaleY = (a * d - c * b) / scaleX,
|
|
5566
|
+
skewX = Math.atan2(a * c + b * d, denom) / (Math.PI / 180),
|
|
5567
|
+
translateX = e ? e : 0,
|
|
5568
|
+
translateY = f ? f : 0,
|
|
5569
|
+
rotate = angle ? angle / (Math.PI / 180) : 0;
|
|
5570
|
+
let transObj = {
|
|
5571
|
+
translateX: translateX,
|
|
5572
|
+
translateY: translateY,
|
|
5573
|
+
rotate: rotate,
|
|
5574
|
+
scaleX: scaleX,
|
|
5575
|
+
scaleY: scaleY,
|
|
5576
|
+
skewX: skewX,
|
|
5577
|
+
skewY: 0
|
|
5578
|
+
};
|
|
5579
|
+
let cssTransforms = [];
|
|
5580
|
+
let svgTransforms = [];
|
|
5581
|
+
for (let prop in transObj) {
|
|
5582
|
+
transObj[prop] = +parseFloat(transObj[prop]).toFixed(precision);
|
|
5583
|
+
let val = transObj[prop];
|
|
5584
|
+
let unit = "";
|
|
5585
|
+
if (prop == "rotate" || prop == "skewX") {
|
|
5586
|
+
unit = "deg";
|
|
5587
|
+
}
|
|
5588
|
+
if (prop.indexOf("translate") != -1) {
|
|
5589
|
+
unit = "px";
|
|
5590
|
+
}
|
|
5591
|
+
// combine these properties
|
|
5592
|
+
let convert = ["scaleX", "scaleY", "translateX", "translateY"];
|
|
5593
|
+
if (val !== 0) {
|
|
5594
|
+
cssTransforms.push(`${prop}(${val}${unit})`);
|
|
5595
|
+
}
|
|
5596
|
+
if (convert.indexOf(prop) == -1 && val !== 0) {
|
|
5597
|
+
svgTransforms.push(`${prop}(${val})`);
|
|
5598
|
+
} else if (prop == "scaleX") {
|
|
5599
|
+
svgTransforms.push(
|
|
5600
|
+
`scale(${+scaleX.toFixed(precision)} ${+scaleY.toFixed(precision)})`
|
|
5601
|
+
);
|
|
5602
|
+
} else if (prop == "translateX") {
|
|
5603
|
+
svgTransforms.push(
|
|
5604
|
+
`translate(${transObj.translateX} ${transObj.translateY})`
|
|
5605
|
+
);
|
|
5606
|
+
}
|
|
5607
|
+
|
|
5608
|
+
}
|
|
5609
|
+
// append css style string to object
|
|
5610
|
+
transObj.cssTransform = cssTransforms.join(" ");
|
|
5611
|
+
transObj.svgTransform = svgTransforms.join(" ");
|
|
5612
|
+
|
|
5613
|
+
transObj.matrix = [a, b, c, d, e, f ].map(val=>roundTo(val, precision));
|
|
5614
|
+
transObj.matrixAtt = `matrix(${transObj.matrix.join(' ')})`;
|
|
5615
|
+
|
|
5616
|
+
|
|
5617
|
+
|
|
5618
|
+
return transObj;
|
|
5619
|
+
}
|
|
5620
|
+
|
|
5095
5621
|
function pathElToShape(el, {
|
|
5096
5622
|
convert_rects = false,
|
|
5097
5623
|
convert_ellipses = false,
|
|
@@ -5224,14 +5750,16 @@
|
|
|
5224
5750
|
convert_rects = false,
|
|
5225
5751
|
convert_ellipses = false,
|
|
5226
5752
|
convert_poly = false,
|
|
5227
|
-
convert_lines = false
|
|
5753
|
+
convert_lines = false,
|
|
5754
|
+
|
|
5755
|
+
matrix=null
|
|
5228
5756
|
|
|
5229
5757
|
} = {}) {
|
|
5230
5758
|
|
|
5231
5759
|
let nodeName = el.nodeName.toLowerCase();
|
|
5232
5760
|
|
|
5233
5761
|
if (
|
|
5234
|
-
nodeName === 'path' ||
|
|
5762
|
+
nodeName === 'path' && !matrix ||
|
|
5235
5763
|
nodeName === 'rect' && !convert_rects ||
|
|
5236
5764
|
(nodeName === 'circle' || nodeName === 'ellipse') && !convert_ellipses ||
|
|
5237
5765
|
(nodeName === 'polygon' || nodeName === 'polyline') && !convert_poly ||
|
|
@@ -5239,17 +5767,25 @@
|
|
|
5239
5767
|
) return el;
|
|
5240
5768
|
|
|
5241
5769
|
let pathData = getPathDataFromEl(el, { width, height });
|
|
5770
|
+
|
|
5771
|
+
// shape attributes – obsolete for path els
|
|
5772
|
+
let exclude = ['d', 'x', 'y', 'x1', 'y1', 'x2', 'y2', 'cx', 'cy', 'dx', 'dy', 'r', 'rx', 'ry', 'width', 'height', 'points'];
|
|
5773
|
+
|
|
5774
|
+
// transform pathData
|
|
5775
|
+
if(matrix && Object.values(matrix).join('')!=='100100'){
|
|
5776
|
+
pathData = transformPathData(pathData, matrix);
|
|
5777
|
+
exclude.push('transform', 'transform-origin');
|
|
5778
|
+
}
|
|
5779
|
+
|
|
5242
5780
|
let d = pathData.map(com => { return `${com.type} ${com.values} ` }).join(' ');
|
|
5243
5781
|
let attributes = [...el.attributes].map(att => att.name);
|
|
5244
5782
|
|
|
5245
5783
|
let pathN = document.createElementNS(svgNs, 'path');
|
|
5246
5784
|
pathN.setAttribute('d', d);
|
|
5247
5785
|
|
|
5248
|
-
|
|
5249
|
-
|
|
5786
|
+
// copy attributes
|
|
5250
5787
|
attributes.forEach(att => {
|
|
5251
5788
|
if (!exclude.includes(att)) {
|
|
5252
|
-
|
|
5253
5789
|
let val = el.getAttribute(att);
|
|
5254
5790
|
pathN.setAttribute(att, val);
|
|
5255
5791
|
}
|
|
@@ -6034,117 +6570,28 @@
|
|
|
6034
6570
|
* transform property object
|
|
6035
6571
|
*/
|
|
6036
6572
|
|
|
6037
|
-
function
|
|
6038
|
-
|
|
6039
|
-
if (!transformString) return false;
|
|
6040
|
-
|
|
6041
|
-
let transformOptions = {
|
|
6042
|
-
transforms: [],
|
|
6043
|
-
transformOrigin,
|
|
6044
|
-
};
|
|
6045
|
-
|
|
6046
|
-
let regex = /(\w+)\(([^)]+)\)/g;
|
|
6047
|
-
let match;
|
|
6048
|
-
|
|
6049
|
-
function convertToDegrees(value) {
|
|
6050
|
-
if (typeof value === 'string') {
|
|
6051
|
-
if (value.includes('rad')) {
|
|
6052
|
-
return parseFloat(value) * (180 / Math.PI);
|
|
6053
|
-
} else if (value.includes('turn')) {
|
|
6054
|
-
return parseFloat(value) * 360;
|
|
6055
|
-
}
|
|
6056
|
-
}
|
|
6057
|
-
return parseFloat(value);
|
|
6058
|
-
}
|
|
6059
|
-
|
|
6060
|
-
while ((match = regex.exec(transformString)) !== null) {
|
|
6061
|
-
let name = match[1];
|
|
6062
|
-
let values = match[2].split(/,\s*/).map(v => convertToDegrees(v));
|
|
6063
|
-
|
|
6064
|
-
switch (name) {
|
|
6065
|
-
|
|
6066
|
-
case 'translate':
|
|
6067
|
-
transformOptions.transforms.push({ translate: [values[0] || 0, values[1] || 0] });
|
|
6068
|
-
break;
|
|
6069
|
-
case 'translateX':
|
|
6070
|
-
transformOptions.transforms.push({ translate: [values[0] || 0, 0, 0] });
|
|
6071
|
-
break;
|
|
6072
|
-
|
|
6073
|
-
case 'translateY':
|
|
6074
|
-
transformOptions.transforms.push({ translate: [0, values[0] || 0, 0] });
|
|
6075
|
-
break;
|
|
6076
|
-
case 'scale':
|
|
6077
|
-
transformOptions.transforms.push({ scale: [values[0] || 0, values[1] || 0] });
|
|
6078
|
-
break;
|
|
6079
|
-
case 'skew':
|
|
6080
|
-
transformOptions.transforms.push({ skew: [values[0] || 0, values[1] || 0] });
|
|
6081
|
-
break;
|
|
6082
|
-
|
|
6083
|
-
case 'skewX':
|
|
6084
|
-
transformOptions.transforms.push({ skew: [values[0] || 0, 0] });
|
|
6085
|
-
break;
|
|
6086
|
-
|
|
6087
|
-
case 'skewY':
|
|
6088
|
-
transformOptions.transforms.push({ skew: [0, values[0] || 0] });
|
|
6089
|
-
break;
|
|
6090
|
-
|
|
6091
|
-
case 'rotate':
|
|
6092
|
-
|
|
6093
|
-
console.log('rotate', values);
|
|
6094
|
-
|
|
6095
|
-
transformOptions.transforms.push({ rotate: [0, 0, values[0] || 0] });
|
|
6096
|
-
break;
|
|
6097
|
-
case 'matrix':
|
|
6098
|
-
transformOptions.transforms.push({ matrix: values });
|
|
6099
|
-
break;
|
|
6100
|
-
}
|
|
6101
|
-
}
|
|
6102
|
-
|
|
6103
|
-
// Extract transform-origin, perspective-origin, and perspective if included as separate properties
|
|
6104
|
-
let styleProperties = transformString.split(/;\s*/);
|
|
6105
|
-
styleProperties.forEach(prop => {
|
|
6106
|
-
let [key, value] = prop.split(':').map(s => s.trim());
|
|
6107
|
-
if (key === 'transform-origin' || key === 'perspective-origin') {
|
|
6108
|
-
let [x, y] = value.split(/\s+/).map(parseFloat);
|
|
6109
|
-
if (key === 'transform-origin') {
|
|
6110
|
-
transformOptions.transformOrigin = { x: x || 0, y: y || 0 };
|
|
6111
|
-
}
|
|
6112
|
-
}
|
|
6113
|
-
});
|
|
6114
|
-
|
|
6115
|
-
return transformOptions;
|
|
6116
|
-
}
|
|
6117
|
-
|
|
6118
|
-
/**
|
|
6119
|
-
* wrapper function to switch between
|
|
6120
|
-
* 2D or 3D matrix
|
|
6121
|
-
*/
|
|
6122
|
-
function getMatrix({
|
|
6123
|
-
transforms = [],
|
|
6124
|
-
transformOrigin = { x: 0, y: 0 },
|
|
6125
|
-
} = {}) {
|
|
6126
|
-
|
|
6127
|
-
let matrix = getMatrix2D(transforms, transformOrigin);
|
|
6573
|
+
function getMatrixFromTransform(transformations = []) {
|
|
6128
6574
|
|
|
6129
|
-
|
|
6130
|
-
}
|
|
6575
|
+
// Helper function to multiply two 2D matrices
|
|
6131
6576
|
|
|
6132
|
-
|
|
6577
|
+
const multiply = (m1, m2) => {
|
|
6578
|
+
let mtxN = {
|
|
6579
|
+
a: m1.a * m2.a + m1.c * m2.b,
|
|
6580
|
+
b: m1.b * m2.a + m1.d * m2.b,
|
|
6581
|
+
c: m1.a * m2.c + m1.c * m2.d,
|
|
6582
|
+
d: m1.b * m2.c + m1.d * m2.d,
|
|
6583
|
+
e: m1.a * m2.e + m1.c * m2.f + m1.e,
|
|
6584
|
+
f: m1.b * m2.e + m1.d * m2.f + m1.f
|
|
6585
|
+
};
|
|
6133
6586
|
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
a: m1.a * m2.a + m1.c * m2.b,
|
|
6137
|
-
b: m1.b * m2.a + m1.d * m2.b,
|
|
6138
|
-
c: m1.a * m2.c + m1.c * m2.d,
|
|
6139
|
-
d: m1.b * m2.c + m1.d * m2.d,
|
|
6140
|
-
e: m1.a * m2.e + m1.c * m2.f + m1.e,
|
|
6141
|
-
f: m1.b * m2.e + m1.d * m2.f + m1.f
|
|
6142
|
-
});
|
|
6587
|
+
return mtxN;
|
|
6588
|
+
};
|
|
6143
6589
|
|
|
6144
6590
|
// Helper function to create a translation matrix
|
|
6145
|
-
const translationMatrix = (x, y) =>
|
|
6146
|
-
a: 1, b: 0, c: 0, d: 1, e: x, f: y
|
|
6147
|
-
|
|
6591
|
+
const translationMatrix = (x, y) => {
|
|
6592
|
+
let mtx ={a: 1, b: 0, c: 0, d: 1, e: x, f: y};
|
|
6593
|
+
return mtx
|
|
6594
|
+
};
|
|
6148
6595
|
|
|
6149
6596
|
// Helper function to create a scaling matrix
|
|
6150
6597
|
const scalingMatrix = (x, y) => ({
|
|
@@ -6153,13 +6600,13 @@
|
|
|
6153
6600
|
|
|
6154
6601
|
// get skew or rotation axis matrix
|
|
6155
6602
|
const angleMatrix = (angles, type) => {
|
|
6156
|
-
|
|
6157
|
-
let [angleX, angleY] = angles.map(ang =>
|
|
6603
|
+
|
|
6604
|
+
let [angleX, angleY=0] = angles.map(ang => ang*deg2rad);
|
|
6158
6605
|
let m = {};
|
|
6159
6606
|
|
|
6160
6607
|
if (type === 'rot') {
|
|
6161
|
-
let
|
|
6162
|
-
m = { a:
|
|
6608
|
+
let cosX = Math.cos(angleX), sinX = Math.sin(angleX);
|
|
6609
|
+
m = { a: cosX, b: sinX, c: -sinX, d: cosX, e: 0, f: 0 };
|
|
6163
6610
|
} else if (type === 'skew') {
|
|
6164
6611
|
let tanX = Math.tan(angleX), tanY = Math.tan(angleY);
|
|
6165
6612
|
m = {
|
|
@@ -6172,201 +6619,48 @@
|
|
|
6172
6619
|
// Start with an identity matrix
|
|
6173
6620
|
let matrix = { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 };
|
|
6174
6621
|
|
|
6175
|
-
//
|
|
6176
|
-
|
|
6177
|
-
matrix = multiply(matrix, translationMatrix(origin.x, origin.y));
|
|
6178
|
-
}
|
|
6622
|
+
// Process transformations in the provided order (right-to-left)
|
|
6623
|
+
for (let i = 0; i < transformations.length; i++) {
|
|
6179
6624
|
|
|
6180
|
-
|
|
6181
|
-
const defaults = {
|
|
6182
|
-
translate: [0, 0],
|
|
6183
|
-
scale: [1, 1],
|
|
6184
|
-
skew: [0, 0],
|
|
6185
|
-
rotate: [0],
|
|
6186
|
-
matrix: [1, 0, 0, 1, 0, 0]
|
|
6187
|
-
};
|
|
6625
|
+
let transform = transformations[i];
|
|
6188
6626
|
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
let type = Object.keys(transform)[0]; // Get the transformation type (e.g., "translate")
|
|
6192
|
-
let values = transform[type] || defaults[type]; // Use default values if none provided
|
|
6627
|
+
// Get the transformation type (e.g., "translate")
|
|
6628
|
+
let type = Object.keys(transform)[0];
|
|
6193
6629
|
|
|
6194
|
-
|
|
6195
|
-
let [x, y = defaults[type][1]] = values;
|
|
6630
|
+
let values = transform[type];
|
|
6196
6631
|
|
|
6197
|
-
|
|
6198
|
-
if (type === 'rotate' && values.length === 3) {
|
|
6199
|
-
x = values[2];
|
|
6200
|
-
}
|
|
6632
|
+
let [x, y] = values;
|
|
6201
6633
|
|
|
6202
6634
|
switch (type) {
|
|
6203
6635
|
case "matrix":
|
|
6204
6636
|
let keys = ['a', 'b', 'c', 'd', 'e', 'f'];
|
|
6205
6637
|
let obj = Object.fromEntries(keys.map((key, i) => [key, values[i]]));
|
|
6638
|
+
|
|
6206
6639
|
matrix = multiply(matrix, obj);
|
|
6207
6640
|
break;
|
|
6208
6641
|
case "translate":
|
|
6209
|
-
|
|
6642
|
+
matrix = multiply(matrix, translationMatrix(x, y));
|
|
6210
6643
|
break;
|
|
6211
6644
|
case "skew":
|
|
6212
|
-
|
|
6645
|
+
matrix = multiply(matrix, angleMatrix([x, y], 'skew'));
|
|
6213
6646
|
break;
|
|
6214
6647
|
case "rotate":
|
|
6215
|
-
|
|
6648
|
+
matrix = multiply(matrix, angleMatrix([x], 'rot'));
|
|
6649
|
+
|
|
6216
6650
|
break;
|
|
6217
6651
|
case "scale":
|
|
6218
|
-
|
|
6652
|
+
matrix = multiply(matrix, scalingMatrix(x, y));
|
|
6219
6653
|
break;
|
|
6220
6654
|
|
|
6221
6655
|
default:
|
|
6222
6656
|
throw new Error(`Unknown transformation type: ${type}`);
|
|
6223
6657
|
}
|
|
6224
|
-
}
|
|
6225
6658
|
|
|
6226
|
-
// Revert transform origin
|
|
6227
|
-
if (origin.x !== 0 || origin.y !== 0) {
|
|
6228
|
-
matrix = multiply(matrix, translationMatrix(-origin.x, -origin.y));
|
|
6229
6659
|
}
|
|
6230
6660
|
|
|
6231
6661
|
return matrix;
|
|
6232
6662
|
}
|
|
6233
6663
|
|
|
6234
|
-
function svgStylesToAttributes(el, {
|
|
6235
|
-
removeNameSpaced = true,
|
|
6236
|
-
decimals = -1
|
|
6237
|
-
} = {}) {
|
|
6238
|
-
|
|
6239
|
-
let nodeName = el.nodeName.toLowerCase();
|
|
6240
|
-
let attProps = getElAttributes(el);
|
|
6241
|
-
let cssProps = getElStyleProps(el);
|
|
6242
|
-
|
|
6243
|
-
// normalize transform attributes
|
|
6244
|
-
/*
|
|
6245
|
-
if (attProps['transform']) {
|
|
6246
|
-
console.log(`attProps['transform']`, attProps['transform']);
|
|
6247
|
-
}
|
|
6248
|
-
*/
|
|
6249
|
-
|
|
6250
|
-
// merge properties
|
|
6251
|
-
let props = {
|
|
6252
|
-
...attProps,
|
|
6253
|
-
...cssProps
|
|
6254
|
-
};
|
|
6255
|
-
|
|
6256
|
-
// filter out obsolete properties
|
|
6257
|
-
let propsFiltered = {};
|
|
6258
|
-
|
|
6259
|
-
// parse CSS transforms
|
|
6260
|
-
let cssTrans = cssProps['transform'];
|
|
6261
|
-
|
|
6262
|
-
if (cssTrans) {
|
|
6263
|
-
let transStr = `${cssTrans}`;
|
|
6264
|
-
let transformObj = parseCSSTransform(transStr);
|
|
6265
|
-
let matrix = getMatrix(transformObj);
|
|
6266
|
-
|
|
6267
|
-
// apply as SVG matrix transform
|
|
6268
|
-
props['transform'] = `matrix(${Object.values(matrix).join(',')})`;
|
|
6269
|
-
}
|
|
6270
|
-
|
|
6271
|
-
// can't be replaced with attributes
|
|
6272
|
-
let cssOnlyProps = ['inline-size'];
|
|
6273
|
-
let styleProps = [];
|
|
6274
|
-
|
|
6275
|
-
for (let prop in props) {
|
|
6276
|
-
|
|
6277
|
-
let value = props[prop];
|
|
6278
|
-
|
|
6279
|
-
// CSS variable
|
|
6280
|
-
if (value && prop.startsWith('--') || cssOnlyProps.includes(prop) ||
|
|
6281
|
-
(!removeNameSpaced && prop.startsWith('-'))) {
|
|
6282
|
-
styleProps.push(`${prop}:${value}`);
|
|
6283
|
-
continue
|
|
6284
|
-
}
|
|
6285
|
-
|
|
6286
|
-
// check if property is valid
|
|
6287
|
-
if (value && attLookup.atts[prop] &&
|
|
6288
|
-
(attLookup.atts[prop] === '*' ||
|
|
6289
|
-
attLookup.atts[prop].includes(nodeName) ||
|
|
6290
|
-
!removeNameSpaced && (prop.includes(':'))
|
|
6291
|
-
)
|
|
6292
|
-
) {
|
|
6293
|
-
propsFiltered[prop] = value;
|
|
6294
|
-
}
|
|
6295
|
-
|
|
6296
|
-
// remove property
|
|
6297
|
-
el.removeAttribute(prop);
|
|
6298
|
-
|
|
6299
|
-
}
|
|
6300
|
-
|
|
6301
|
-
// apply filtered attributes
|
|
6302
|
-
for (let prop in propsFiltered) {
|
|
6303
|
-
let value = propsFiltered[prop];
|
|
6304
|
-
el.setAttribute(prop, value);
|
|
6305
|
-
}
|
|
6306
|
-
|
|
6307
|
-
if (styleProps.length) {
|
|
6308
|
-
el.setAttribute('style', styleProps.join(';'));
|
|
6309
|
-
}
|
|
6310
|
-
|
|
6311
|
-
return propsFiltered;
|
|
6312
|
-
|
|
6313
|
-
}
|
|
6314
|
-
|
|
6315
|
-
function parseInlineStyle(styleAtt = '') {
|
|
6316
|
-
|
|
6317
|
-
let props = {};
|
|
6318
|
-
if (!styleAtt) return props;
|
|
6319
|
-
|
|
6320
|
-
let styleArr = styleAtt.split(';').filter(Boolean).map(prop => prop.trim());
|
|
6321
|
-
let l = styleArr.length;
|
|
6322
|
-
if (!l) return props;
|
|
6323
|
-
|
|
6324
|
-
for (let i = 0; l && i < l; i++) {
|
|
6325
|
-
let style = styleArr[i];
|
|
6326
|
-
let [prop, value] = style.split(':').filter(Boolean);
|
|
6327
|
-
props[prop] = value;
|
|
6328
|
-
|
|
6329
|
-
}
|
|
6330
|
-
|
|
6331
|
-
return props
|
|
6332
|
-
}
|
|
6333
|
-
|
|
6334
|
-
function getElStyleProps(el) {
|
|
6335
|
-
let styleAtt = el.getAttribute('style');
|
|
6336
|
-
let props = styleAtt ? parseInlineStyle(styleAtt) : {};
|
|
6337
|
-
return props
|
|
6338
|
-
}
|
|
6339
|
-
|
|
6340
|
-
function getElAttributes(el) {
|
|
6341
|
-
let props = {};
|
|
6342
|
-
let atts = [...el.attributes].map((att) => att.name);
|
|
6343
|
-
let l = atts.length;
|
|
6344
|
-
if (!l) return props;
|
|
6345
|
-
|
|
6346
|
-
for (let i = 0; i < l; i++) {
|
|
6347
|
-
let att = atts[i];
|
|
6348
|
-
let value = el.getAttribute(att);
|
|
6349
|
-
props[att] = value;
|
|
6350
|
-
}
|
|
6351
|
-
|
|
6352
|
-
return props;
|
|
6353
|
-
}
|
|
6354
|
-
|
|
6355
|
-
/*
|
|
6356
|
-
function roundValue(value = '', decimals = -1) {
|
|
6357
|
-
if (decimals < 0) return value;
|
|
6358
|
-
value = value.replace(/["]/g, '').trim()
|
|
6359
|
-
let valueNum = parseFloat(value);
|
|
6360
|
-
let valueHasNumber = !isNaN(valueNum);
|
|
6361
|
-
if (!valueHasNumber) return value;
|
|
6362
|
-
|
|
6363
|
-
let unit = valueHasNumber ? getUnit(value) : '';
|
|
6364
|
-
if (valueHasNumber) value = `${valueNum.toFixed(decimals)}${unit}`;
|
|
6365
|
-
|
|
6366
|
-
return value;
|
|
6367
|
-
}
|
|
6368
|
-
*/
|
|
6369
|
-
|
|
6370
6664
|
function normalizePoly(pts, {
|
|
6371
6665
|
toObject = true,
|
|
6372
6666
|
toArray = false,
|
|
@@ -6477,6 +6771,522 @@
|
|
|
6477
6771
|
return bb;
|
|
6478
6772
|
}
|
|
6479
6773
|
|
|
6774
|
+
/**
|
|
6775
|
+
* parse svg presentational attributes
|
|
6776
|
+
* or CSS styles
|
|
6777
|
+
*/
|
|
6778
|
+
|
|
6779
|
+
function parseStylesProperties(el, {
|
|
6780
|
+
fontSize = 16,
|
|
6781
|
+
removeNameSpaced = true,
|
|
6782
|
+
autoRoundValues = false,
|
|
6783
|
+
minifyRgbColors = false,
|
|
6784
|
+
removeInvalid = true,
|
|
6785
|
+
removeDefaults = true,
|
|
6786
|
+
cleanUpStrokes = true,
|
|
6787
|
+
normalizeTransforms = true,
|
|
6788
|
+
exclude = [],
|
|
6789
|
+
width = 0,
|
|
6790
|
+
height = 0,
|
|
6791
|
+
} = {}) {
|
|
6792
|
+
|
|
6793
|
+
let nodeName = el.nodeName.toLowerCase();
|
|
6794
|
+
let attProps = getSvgPresentationAtts(el);
|
|
6795
|
+
let cssProps = getSvgCssProps(el);
|
|
6796
|
+
|
|
6797
|
+
/**
|
|
6798
|
+
* merge props
|
|
6799
|
+
* CSS has higher specificity
|
|
6800
|
+
*/
|
|
6801
|
+
let props = {
|
|
6802
|
+
...attProps,
|
|
6803
|
+
...cssProps,
|
|
6804
|
+
};
|
|
6805
|
+
|
|
6806
|
+
delete props['style'];
|
|
6807
|
+
exclude.push('style');
|
|
6808
|
+
|
|
6809
|
+
let remove = ['style'];
|
|
6810
|
+
let transformsStandalone = ['scale', 'translate', 'rotate'];
|
|
6811
|
+
|
|
6812
|
+
/**
|
|
6813
|
+
* remove invalid properties
|
|
6814
|
+
* e.g font-family for <path>
|
|
6815
|
+
*/
|
|
6816
|
+
|
|
6817
|
+
if (removeInvalid || removeDefaults || removeNameSpaced) {
|
|
6818
|
+
let propsFilteredObj = filterSvgElProps(nodeName, props, { removeDefaults, removeNameSpaced, exclude, cleanUpStrokes, include: transformsStandalone, cleanUpStrokes: false });
|
|
6819
|
+
props = propsFilteredObj.propsFiltered;
|
|
6820
|
+
remove.push(...propsFilteredObj.remove);
|
|
6821
|
+
}
|
|
6822
|
+
|
|
6823
|
+
// sanitized prop array
|
|
6824
|
+
let propArr = [];
|
|
6825
|
+
|
|
6826
|
+
for (let prop in props) {
|
|
6827
|
+
|
|
6828
|
+
let valueStr = props[prop];
|
|
6829
|
+
|
|
6830
|
+
// we parse the path data separately
|
|
6831
|
+
if (prop === 'd' || prop.startsWith('data-')) {
|
|
6832
|
+
continue;
|
|
6833
|
+
}
|
|
6834
|
+
|
|
6835
|
+
let item = { prop, values: [] };
|
|
6836
|
+
|
|
6837
|
+
// minify rgb values
|
|
6838
|
+
if (minifyRgbColors && colorProps.includes(prop)) {
|
|
6839
|
+
let color = parseColor(valueStr);
|
|
6840
|
+
if (color.mode === 'rgba' || color.mode === 'rgb') {
|
|
6841
|
+
let hex = rgba2Hex(color);
|
|
6842
|
+
valueStr = hex;
|
|
6843
|
+
}
|
|
6844
|
+
}
|
|
6845
|
+
|
|
6846
|
+
if (prop === 'transform') {
|
|
6847
|
+
let transArr = [];
|
|
6848
|
+
|
|
6849
|
+
let transFormFunctions = valueStr.split(/(\w+)\(([^)]+)\)/).map(val => val.trim()).filter(Boolean);
|
|
6850
|
+
|
|
6851
|
+
for (let i = 1; i < transFormFunctions.length; i += 2) {
|
|
6852
|
+
let fn = transFormFunctions[i - 1];
|
|
6853
|
+
let isHorizontal = transHorizontal.includes(fn);
|
|
6854
|
+
let isVertical = transVertical.includes(fn);
|
|
6855
|
+
if (isHorizontal) fn = fn.replace('X', '');
|
|
6856
|
+
if (isVertical) fn = fn.replace('Y', '');
|
|
6857
|
+
let values = transFormFunctions[i].split(/,| /).filter(Boolean);
|
|
6858
|
+
let transItem = { fn, values: [] };
|
|
6859
|
+
|
|
6860
|
+
for (let v = 0; v < values.length; v++) {
|
|
6861
|
+
let transValues = parseValue(values[v]);
|
|
6862
|
+
transItem.values.push(...transValues);
|
|
6863
|
+
}
|
|
6864
|
+
|
|
6865
|
+
let defaultX = fn.startsWith('scale') ? 1 : 0;
|
|
6866
|
+
let defaultY = fn.startsWith('scale') ? 1 : 0;
|
|
6867
|
+
|
|
6868
|
+
if (isHorizontal) transItem.values = [transItem.values[0], { value: defaultX, unit: '', numeric: true }];
|
|
6869
|
+
if (isVertical) transItem.values = [{ value: defaultY, unit: '', numeric: true }, transItem.values[0]];
|
|
6870
|
+
|
|
6871
|
+
transArr.push(transItem);
|
|
6872
|
+
}
|
|
6873
|
+
|
|
6874
|
+
if (transArr.length) {
|
|
6875
|
+
propArr.push({ prop: 'transforms', values: transArr });
|
|
6876
|
+
}
|
|
6877
|
+
}
|
|
6878
|
+
|
|
6879
|
+
// other props
|
|
6880
|
+
else {
|
|
6881
|
+
|
|
6882
|
+
item.values = parseValue(valueStr);
|
|
6883
|
+
|
|
6884
|
+
}
|
|
6885
|
+
|
|
6886
|
+
if (item.values.length) {
|
|
6887
|
+
propArr.push(item);
|
|
6888
|
+
}
|
|
6889
|
+
|
|
6890
|
+
}
|
|
6891
|
+
|
|
6892
|
+
/**
|
|
6893
|
+
* normalize values to
|
|
6894
|
+
* user units
|
|
6895
|
+
*/
|
|
6896
|
+
|
|
6897
|
+
let propsNorm = { transformArr: [], matrix: null, transComponents: null };
|
|
6898
|
+
let transFormOrigin = [];
|
|
6899
|
+
let normalizedDiagonal = false;
|
|
6900
|
+
|
|
6901
|
+
for (let i = 0; i < propArr.length; i++) {
|
|
6902
|
+
let item = propArr[i];
|
|
6903
|
+
let { prop, values } = item;
|
|
6904
|
+
let valsNew = [], valX = 0, valY = 0, unitX = '', unitY = '';
|
|
6905
|
+
|
|
6906
|
+
if (prop !== 'transforms') {
|
|
6907
|
+
|
|
6908
|
+
if (cleanUpStrokes && (prop === 'stroke-dasharray' || prop === 'stroke-dashoffset')) {
|
|
6909
|
+
normalizedDiagonal = true;
|
|
6910
|
+
for (let i = 0; i < values.length; i++) {
|
|
6911
|
+
let val = normalizeUnits(values[i].value, { unit: values[i].unit, width, height, normalizedDiagonal, fontSize });
|
|
6912
|
+
valsNew.push(val);
|
|
6913
|
+
}
|
|
6914
|
+
}
|
|
6915
|
+
|
|
6916
|
+
else if (prop === 'transform-origin') {
|
|
6917
|
+
|
|
6918
|
+
values.forEach((item, i) => {
|
|
6919
|
+
let val = item.value;
|
|
6920
|
+
if (val === 'left') values[i].value = 0;
|
|
6921
|
+
else if (val === 'right') values[i].value = width;
|
|
6922
|
+
else if (val === 'top') values[i].value = 0;
|
|
6923
|
+
else if (val === 'bottom') values[i].value = height;
|
|
6924
|
+
else if (val === 'center') values[i].value = '50%';
|
|
6925
|
+
});
|
|
6926
|
+
|
|
6927
|
+
valX = values[0].value;
|
|
6928
|
+
valY = values[1] ? values[1].value : valX;
|
|
6929
|
+
unitX = values[0].unit;
|
|
6930
|
+
unitY = values[1] ? values[1].unit : unitX;
|
|
6931
|
+
|
|
6932
|
+
// normalize units for matrix calculation
|
|
6933
|
+
valX = normalizeUnits(valX, { unit: unitX, width, height, isHorizontal: true, fontSize });
|
|
6934
|
+
valY = normalizeUnits(valY, { unit: unitY, width, height, isVertical: true, fontSize });
|
|
6935
|
+
transFormOrigin.push(valX, valY);
|
|
6936
|
+
|
|
6937
|
+
} else {
|
|
6938
|
+
|
|
6939
|
+
for (let v = 0; v < values.length; v++) {
|
|
6940
|
+
let val = values[v];
|
|
6941
|
+
|
|
6942
|
+
let unit = val.unit;
|
|
6943
|
+
let valAbs = val.value;
|
|
6944
|
+
let isNumeric = val.numeric;
|
|
6945
|
+
|
|
6946
|
+
let isHorizontal = horizontalProps.includes(prop);
|
|
6947
|
+
let isVertical = verticalProps.includes(prop);
|
|
6948
|
+
|
|
6949
|
+
if (unit) {
|
|
6950
|
+
if (prop === 'scale' && unit === '%') {
|
|
6951
|
+
valAbs = valAbs * 0.01;
|
|
6952
|
+
} else {
|
|
6953
|
+
if (prop === 'r') normalizedDiagonal = true;
|
|
6954
|
+
valAbs = normalizeUnits(val.value, { unit, width, height, isHorizontal, isVertical, normalizedDiagonal, fontSize });
|
|
6955
|
+
|
|
6956
|
+
if (autoRoundValues && isNumeric) {
|
|
6957
|
+
valAbs = autoRound(valAbs);
|
|
6958
|
+
}
|
|
6959
|
+
|
|
6960
|
+
}
|
|
6961
|
+
}
|
|
6962
|
+
valsNew.push(valAbs);
|
|
6963
|
+
}
|
|
6964
|
+
}
|
|
6965
|
+
|
|
6966
|
+
if (valsNew.length) propsNorm[prop] = valsNew;
|
|
6967
|
+
|
|
6968
|
+
}
|
|
6969
|
+
|
|
6970
|
+
// is transform properties and functions
|
|
6971
|
+
else {
|
|
6972
|
+
|
|
6973
|
+
let transforms = values || [];
|
|
6974
|
+
|
|
6975
|
+
let len = transforms.length;
|
|
6976
|
+
let transFormAllObj = [];
|
|
6977
|
+
|
|
6978
|
+
for (let t = 0; len && t < len; t++) {
|
|
6979
|
+
let { fn, values } = transforms[t];
|
|
6980
|
+
let valsN = [], unitX = '', unitY = '', transformFunctionArr = [];
|
|
6981
|
+
|
|
6982
|
+
// defaults
|
|
6983
|
+
let valX = 0;
|
|
6984
|
+
let valY = 0;
|
|
6985
|
+
let transObj = {};
|
|
6986
|
+
|
|
6987
|
+
// console.log('!!!values', values);
|
|
6988
|
+
if (fn === 'scale' || fn === 'translate') {
|
|
6989
|
+
valX = values[0].value;
|
|
6990
|
+
valY = values[1] ? values[1].value : valX;
|
|
6991
|
+
unitX = values[0].unit;
|
|
6992
|
+
unitY = values[1] ? values[1].unit : unitX;
|
|
6993
|
+
|
|
6994
|
+
if (fn === 'scale') {
|
|
6995
|
+
valX = unitX === '%' ? valX * 0.01 : valX;
|
|
6996
|
+
valY = unitY === '%' ? valY * 0.01 : valY;
|
|
6997
|
+
} else {
|
|
6998
|
+
valX = normalizeUnits(valX, { unit: unitX, width, height, isHorizontal: true, fontSize });
|
|
6999
|
+
valY = normalizeUnits(valY, { unit: unitY, width, height, isVertical: true, fontSize });
|
|
7000
|
+
|
|
7001
|
+
}
|
|
7002
|
+
valsN.push(valX, valY);
|
|
7003
|
+
|
|
7004
|
+
transObj[fn] = valsN;
|
|
7005
|
+
transformFunctionArr.push(transObj);
|
|
7006
|
+
|
|
7007
|
+
}
|
|
7008
|
+
|
|
7009
|
+
if (fn === 'matrix') {
|
|
7010
|
+
valsN = values.map(val => val.value);
|
|
7011
|
+
transObj[fn] = valsN;
|
|
7012
|
+
transformFunctionArr.push(transObj);
|
|
7013
|
+
}
|
|
7014
|
+
|
|
7015
|
+
if (fn === 'skew') {
|
|
7016
|
+
|
|
7017
|
+
valX = values[0].value;
|
|
7018
|
+
unitX = values[0].unit;
|
|
7019
|
+
valY = values[1].value;
|
|
7020
|
+
unitY = values[1].unit;
|
|
7021
|
+
|
|
7022
|
+
valX = normalizeUnits(valX, { unit: unitX, isHorizontal: true, fontSize });
|
|
7023
|
+
valY = normalizeUnits(valY, { unit: unitY, isVertical: true, fontSize });
|
|
7024
|
+
|
|
7025
|
+
// normalize large angles
|
|
7026
|
+
valX = valX > 360 ? (valX % 360) : valX;
|
|
7027
|
+
valY = valY > 360 ? (valY % 360) : valY;
|
|
7028
|
+
|
|
7029
|
+
valsN = [valX, valY];
|
|
7030
|
+
transObj[fn] = valsN;
|
|
7031
|
+
transformFunctionArr.push(transObj);
|
|
7032
|
+
|
|
7033
|
+
}
|
|
7034
|
+
|
|
7035
|
+
// SVG rotations may contain a transform origin
|
|
7036
|
+
if (fn === 'rotate') {
|
|
7037
|
+
|
|
7038
|
+
let angle = values[0].value;
|
|
7039
|
+
let unit = values[0].unit;
|
|
7040
|
+
angle = normalizeUnits(angle, { unit });
|
|
7041
|
+
|
|
7042
|
+
let hasPivot = values.length === 3;
|
|
7043
|
+
let transOrigin = [];
|
|
7044
|
+
|
|
7045
|
+
if (hasPivot) {
|
|
7046
|
+
|
|
7047
|
+
let cx = values[1].value;
|
|
7048
|
+
let cy = values[2].value;
|
|
7049
|
+
transOrigin.push({ translate: [cx, cy] }, { translate: [-cx, -cy] });
|
|
7050
|
+
|
|
7051
|
+
}
|
|
7052
|
+
|
|
7053
|
+
transObj[fn] = [angle];
|
|
7054
|
+
|
|
7055
|
+
if (transOrigin.length) {
|
|
7056
|
+
transformFunctionArr.push(transOrigin[0], transObj, transOrigin[1]);
|
|
7057
|
+
} else {
|
|
7058
|
+
transformFunctionArr.push(transObj);
|
|
7059
|
+
}
|
|
7060
|
+
}
|
|
7061
|
+
|
|
7062
|
+
transFormAllObj.push(...transformFunctionArr);
|
|
7063
|
+
|
|
7064
|
+
}
|
|
7065
|
+
|
|
7066
|
+
propsNorm['transformArr'] = transFormAllObj;
|
|
7067
|
+
|
|
7068
|
+
}
|
|
7069
|
+
|
|
7070
|
+
}
|
|
7071
|
+
|
|
7072
|
+
// prepend standalone transforms before standards
|
|
7073
|
+
let translate = propsNorm['translate'] !== undefined ? { translate: propsNorm['translate'] } : null;
|
|
7074
|
+
let scale = propsNorm['scale'] !== undefined ? { scale: propsNorm['scale'] } : null;
|
|
7075
|
+
let rotate = propsNorm['rotate'] !== undefined ? { rotate: propsNorm['rotate'] } : null;
|
|
7076
|
+
let standaloneTransforms = [translate, rotate, scale].filter(Boolean);
|
|
7077
|
+
|
|
7078
|
+
if (standaloneTransforms.length) {
|
|
7079
|
+
if (normalizeTransforms) remove.push('translate', 'scale', 'rotate');
|
|
7080
|
+
propsNorm['transformArr'] = [...standaloneTransforms, ...propsNorm['transformArr']];
|
|
7081
|
+
}
|
|
7082
|
+
|
|
7083
|
+
// replace transform-origin with translates
|
|
7084
|
+
|
|
7085
|
+
if (transFormOrigin.length && propsNorm['transformArr'] !== undefined) {
|
|
7086
|
+
propsNorm['transformArr'] = [
|
|
7087
|
+
{ translate: [transFormOrigin[0], transFormOrigin[1]] },
|
|
7088
|
+
...propsNorm['transformArr'],
|
|
7089
|
+
{ translate: [-transFormOrigin[0], -transFormOrigin[1]] },
|
|
7090
|
+
];
|
|
7091
|
+
if (normalizeTransforms) remove.push('transform-origin');
|
|
7092
|
+
}
|
|
7093
|
+
|
|
7094
|
+
/**
|
|
7095
|
+
* test run
|
|
7096
|
+
* apply parsed transforms
|
|
7097
|
+
*/
|
|
7098
|
+
let { transformArr = [] } = propsNorm;
|
|
7099
|
+
|
|
7100
|
+
let transAtt = [];
|
|
7101
|
+
let l = transformArr.length;
|
|
7102
|
+
if (l) {
|
|
7103
|
+
for (let i = 0; l && i < l; i++) {
|
|
7104
|
+
let prop = transformArr[i];
|
|
7105
|
+
let values = Object.values(prop).flat();
|
|
7106
|
+
let name = Object.keys(prop)[0];
|
|
7107
|
+
if (name === 'skew') {
|
|
7108
|
+
if (values[0]) transAtt.push(`skewX(${values[0]})`);
|
|
7109
|
+
if (values[1]) transAtt.push(`skewY(${values[1]})`);
|
|
7110
|
+
} else {
|
|
7111
|
+
transAtt.push(`${name}(${values.join(' ')})`);
|
|
7112
|
+
}
|
|
7113
|
+
}
|
|
7114
|
+
// consolidate transforms to matrix
|
|
7115
|
+
|
|
7116
|
+
}
|
|
7117
|
+
|
|
7118
|
+
propsNorm.remove = remove;
|
|
7119
|
+
propsNorm.type = nodeName;
|
|
7120
|
+
|
|
7121
|
+
return propsNorm
|
|
7122
|
+
|
|
7123
|
+
}
|
|
7124
|
+
|
|
7125
|
+
/**
|
|
7126
|
+
* consolidate transforms to matrix
|
|
7127
|
+
*/
|
|
7128
|
+
function addTransFormProps(propsObj = {}, transformArr = []) {
|
|
7129
|
+
if (propsObj.transformArr === undefined || !transformArr.length) return;
|
|
7130
|
+
|
|
7131
|
+
// take existing array or custom
|
|
7132
|
+
transformArr = transformArr.length ? transformArr : propsObj.transformArr;
|
|
7133
|
+
let matrix = getMatrixFromTransform(transformArr);
|
|
7134
|
+
propsObj['matrix'] = matrix;
|
|
7135
|
+
|
|
7136
|
+
let transComponents = qrDecomposeMatrix(matrix, 3);
|
|
7137
|
+
propsObj.transComponents = transComponents;
|
|
7138
|
+
|
|
7139
|
+
return propsObj
|
|
7140
|
+
}
|
|
7141
|
+
|
|
7142
|
+
/**
|
|
7143
|
+
* filter out nonsense
|
|
7144
|
+
* presentation attributes or
|
|
7145
|
+
* style properties not valid
|
|
7146
|
+
* for element type
|
|
7147
|
+
*/
|
|
7148
|
+
function filterSvgElProps(elNodename = '', props = {}, {
|
|
7149
|
+
removeInvalid = true,
|
|
7150
|
+
removeDefaults = true,
|
|
7151
|
+
allowDataAtts = true,
|
|
7152
|
+
cleanUpStrokes = true,
|
|
7153
|
+
include = ['id', 'class'],
|
|
7154
|
+
exclude = [],
|
|
7155
|
+
} = {}) {
|
|
7156
|
+
let propsFiltered = {};
|
|
7157
|
+
let remove = [];
|
|
7158
|
+
|
|
7159
|
+
// allow defaults for nested
|
|
7160
|
+
|
|
7161
|
+
let noStrokeColor = cleanUpStrokes ? (props['stroke'] === undefined) : false;
|
|
7162
|
+
|
|
7163
|
+
for (let prop in props) {
|
|
7164
|
+
let values = props[prop];
|
|
7165
|
+
let value = Array.isArray(values) ? values[0] : values;
|
|
7166
|
+
|
|
7167
|
+
// filter out useless
|
|
7168
|
+
let isValid = removeInvalid ?
|
|
7169
|
+
(attLookup.atts[prop] ? attLookup.atts[prop].includes(elNodename) : false) :
|
|
7170
|
+
false;
|
|
7171
|
+
|
|
7172
|
+
// remove null transforms
|
|
7173
|
+
if(prop==='transform' && value==='matrix(1 0 0 1 0 0)') isValid = false;
|
|
7174
|
+
|
|
7175
|
+
// allow data attributes
|
|
7176
|
+
let isDataAtt = allowDataAtts ? prop.startsWith('data-') : false;
|
|
7177
|
+
|
|
7178
|
+
// filter out defaults
|
|
7179
|
+
let isDefault = removeDefaults ?
|
|
7180
|
+
(attLookup.defaults[prop] ? attLookup.defaults[prop] !== undefined && attLookup.defaults[prop].includes(value) : false) :
|
|
7181
|
+
false;
|
|
7182
|
+
|
|
7183
|
+
if (isDataAtt || include.includes(prop)) isValid = true;
|
|
7184
|
+
if (isDefault) isValid = false;
|
|
7185
|
+
if (exclude.length && exclude.includes(prop)) isValid = false;
|
|
7186
|
+
if (noStrokeColor && strokeAtts.includes(prop)) isValid = false;
|
|
7187
|
+
|
|
7188
|
+
if (isValid) {
|
|
7189
|
+
propsFiltered[prop] = props[prop];
|
|
7190
|
+
}
|
|
7191
|
+
else {
|
|
7192
|
+
remove.push(prop);
|
|
7193
|
+
}
|
|
7194
|
+
}
|
|
7195
|
+
|
|
7196
|
+
/*
|
|
7197
|
+
// set explicit stroke width when disabled by stroke color
|
|
7198
|
+
if (propsFiltered['stroke'] && propsFiltered['stroke'][0] === 'none') {
|
|
7199
|
+
propsFiltered['stroke-width'] = [1]
|
|
7200
|
+
remove.push('stroke', 'stroke-width')
|
|
7201
|
+
console.log('remove', remove);
|
|
7202
|
+
}
|
|
7203
|
+
*/
|
|
7204
|
+
|
|
7205
|
+
return { propsFiltered, remove }
|
|
7206
|
+
}
|
|
7207
|
+
|
|
7208
|
+
function parseValue(valStr = '') {
|
|
7209
|
+
let valArr = valStr.split(/,| /);
|
|
7210
|
+
|
|
7211
|
+
for (let i = 0; i < valArr.length; i++) {
|
|
7212
|
+
|
|
7213
|
+
let valStr = valArr[i];
|
|
7214
|
+
let val = { value: null, unit: '', numeric: false };
|
|
7215
|
+
let isNumeric = isNumericValue(valStr);
|
|
7216
|
+
if (!isNumeric) {
|
|
7217
|
+
val.value = valStr;
|
|
7218
|
+
}
|
|
7219
|
+
else if (isNumeric) {
|
|
7220
|
+
let unit = getUnit(valStr);
|
|
7221
|
+
let valNum = parseFloat(valStr);
|
|
7222
|
+
val.value = valNum;
|
|
7223
|
+
val.unit = unit;
|
|
7224
|
+
val.numeric = true;
|
|
7225
|
+
}
|
|
7226
|
+
valArr[i] = val;
|
|
7227
|
+
}
|
|
7228
|
+
|
|
7229
|
+
return valArr;
|
|
7230
|
+
}
|
|
7231
|
+
|
|
7232
|
+
function getSvgCssProps(el) {
|
|
7233
|
+
let styleAtt = el.getAttribute('style');
|
|
7234
|
+
let props = styleAtt ? parseInlineCss(styleAtt) : {};
|
|
7235
|
+
return props
|
|
7236
|
+
}
|
|
7237
|
+
|
|
7238
|
+
function getSvgPresentationAtts(el) {
|
|
7239
|
+
let props = {};
|
|
7240
|
+
let atts = [...el.attributes].map((att) => att.name);
|
|
7241
|
+
let l = atts.length;
|
|
7242
|
+
if (!l) return props;
|
|
7243
|
+
|
|
7244
|
+
for (let i = 0; i < l; i++) {
|
|
7245
|
+
let att = atts[i];
|
|
7246
|
+
let value = el.getAttribute(att);
|
|
7247
|
+
|
|
7248
|
+
// test invalid transform functions
|
|
7249
|
+
if (att === 'transform') {
|
|
7250
|
+
let transformSan = [];
|
|
7251
|
+
let transFormFunctions = value.split(/(\w+)\(([^)]+)\)/).map(val => val.trim()).filter(Boolean);
|
|
7252
|
+
|
|
7253
|
+
for (let i = 1; i < transFormFunctions.length; i += 2) {
|
|
7254
|
+
let prop = transFormFunctions[i - 1];
|
|
7255
|
+
let val = transFormFunctions[i];
|
|
7256
|
+
let units = val.split(/,| /).map(val => getUnit(val.trim())).filter(Boolean);
|
|
7257
|
+
|
|
7258
|
+
// remove invalid transform function
|
|
7259
|
+
if (!units.length) {
|
|
7260
|
+
transformSan.push(`${prop}(${val})`);
|
|
7261
|
+
}
|
|
7262
|
+
}
|
|
7263
|
+
value = transformSan.join(' ');
|
|
7264
|
+
}
|
|
7265
|
+
|
|
7266
|
+
props[att] = value.trim();
|
|
7267
|
+
}
|
|
7268
|
+
|
|
7269
|
+
return props;
|
|
7270
|
+
}
|
|
7271
|
+
|
|
7272
|
+
function parseInlineCss(styleAtt = '') {
|
|
7273
|
+
|
|
7274
|
+
let props = {};
|
|
7275
|
+
if (!styleAtt) return props;
|
|
7276
|
+
|
|
7277
|
+
let styleArr = styleAtt.split(';').filter(Boolean).map(prop => prop.trim());
|
|
7278
|
+
let l = styleArr.length;
|
|
7279
|
+
if (!l) return props;
|
|
7280
|
+
|
|
7281
|
+
for (let i = 0; l && i < l; i++) {
|
|
7282
|
+
let style = styleArr[i];
|
|
7283
|
+
let [prop, value] = style.split(':').filter(Boolean);
|
|
7284
|
+
props[prop] = value;
|
|
7285
|
+
}
|
|
7286
|
+
|
|
7287
|
+
return props
|
|
7288
|
+
}
|
|
7289
|
+
|
|
6480
7290
|
function removeEmptySVGEls(svg) {
|
|
6481
7291
|
let els = svg.querySelectorAll('g, defs');
|
|
6482
7292
|
els.forEach(el => {
|
|
@@ -6498,6 +7308,12 @@
|
|
|
6498
7308
|
cleanupClip = true,
|
|
6499
7309
|
addViewBox = false,
|
|
6500
7310
|
addDimensions = false,
|
|
7311
|
+
minifyRgbColors = false,
|
|
7312
|
+
|
|
7313
|
+
normalizeTransforms = true,
|
|
7314
|
+
autoRoundValues = true,
|
|
7315
|
+
|
|
7316
|
+
unGroup = false,
|
|
6501
7317
|
|
|
6502
7318
|
mergePaths = false,
|
|
6503
7319
|
removeOffCanvas = true,
|
|
@@ -6509,16 +7325,15 @@
|
|
|
6509
7325
|
convert_rects = false,
|
|
6510
7326
|
convert_ellipses = false,
|
|
6511
7327
|
convert_poly = false,
|
|
6512
|
-
convert_lines=false,
|
|
7328
|
+
convert_lines = false,
|
|
6513
7329
|
|
|
6514
7330
|
convertTransforms = false,
|
|
7331
|
+
removeDefaults = true,
|
|
6515
7332
|
cleanUpStrokes = true,
|
|
6516
7333
|
decimals = -1,
|
|
6517
7334
|
excludedEls = [],
|
|
6518
7335
|
} = {}) {
|
|
6519
7336
|
|
|
6520
|
-
attributesToGroup = cleanupSVGAtts ? true : false;
|
|
6521
|
-
|
|
6522
7337
|
// replace namespaced refs
|
|
6523
7338
|
if (fixHref) svgMarkup = svgMarkup.replaceAll("xlink:href=", "href=");
|
|
6524
7339
|
|
|
@@ -6529,16 +7344,57 @@
|
|
|
6529
7344
|
let viewBox = getViewBox(svg);
|
|
6530
7345
|
let { x, y, width, height } = viewBox;
|
|
6531
7346
|
|
|
7347
|
+
// get svg styles
|
|
7348
|
+
let propOptions = {
|
|
7349
|
+
width: width,
|
|
7350
|
+
height: height,
|
|
7351
|
+
normalizeTransforms,
|
|
7352
|
+
removeDefaults: false,
|
|
7353
|
+
cleanUpStrokes: false,
|
|
7354
|
+
autoRoundValues,
|
|
7355
|
+
minifyRgbColors,
|
|
7356
|
+
};
|
|
7357
|
+
let stylePropsSVG = parseStylesProperties(svg, propOptions);
|
|
7358
|
+
|
|
7359
|
+
// add svg font size for scaling relative
|
|
7360
|
+
propOptions.fontSize = stylePropsSVG['font-size'] ? stylePropsSVG['font-size'][0] : 16;
|
|
7361
|
+
|
|
7362
|
+
/**
|
|
7363
|
+
* get group styles
|
|
7364
|
+
* especially transformations to
|
|
7365
|
+
* be inherited by children
|
|
7366
|
+
*/
|
|
7367
|
+
let groups = svg.querySelectorAll('g');
|
|
7368
|
+
let groupProps = [];
|
|
7369
|
+
|
|
7370
|
+
groups.forEach(g => {
|
|
7371
|
+
let stylePropsG = parseStylesProperties(g, propOptions);
|
|
7372
|
+
groupProps.push(stylePropsG);
|
|
7373
|
+
let children = g.querySelectorAll(`${renderedEls.join(', ')}`);
|
|
7374
|
+
|
|
7375
|
+
// store parent styles to child property
|
|
7376
|
+
children.forEach(child => {
|
|
7377
|
+
if (child.parentStyleProps === undefined) {
|
|
7378
|
+
child.parentStyleProps = [];
|
|
7379
|
+
}
|
|
7380
|
+
child.parentStyleProps.push(stylePropsG);
|
|
7381
|
+
});
|
|
7382
|
+
});
|
|
7383
|
+
|
|
6532
7384
|
if (cleanupSVGAtts) {
|
|
6533
7385
|
|
|
6534
|
-
let allowed = ['viewBox', 'xmlns', 'width', 'height', 'id', 'class'
|
|
7386
|
+
let allowed = ['viewBox', 'xmlns', 'width', 'height', 'id', 'class'];
|
|
7387
|
+
if (!stylesToAttributes) {
|
|
7388
|
+
allowed.push('fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin', 'font-size', 'font-family', 'font-style', 'style');
|
|
7389
|
+
}
|
|
7390
|
+
|
|
6535
7391
|
removeExcludedAttribues(svg, allowed);
|
|
6536
7392
|
}
|
|
6537
7393
|
|
|
6538
7394
|
// add viewBox
|
|
6539
7395
|
if (addViewBox) addSvgViewBox(svg, { x, y, width, height });
|
|
6540
7396
|
if (addDimensions) {
|
|
6541
|
-
svg.setAttribute('width', width);
|
|
7397
|
+
svg.setAttribute('width', width);
|
|
6542
7398
|
svg.setAttribute('height', height);
|
|
6543
7399
|
}
|
|
6544
7400
|
|
|
@@ -6551,40 +7407,18 @@
|
|
|
6551
7407
|
// always remove scripts
|
|
6552
7408
|
let removeEls = ['metadata', 'script', ...excludedEls];
|
|
6553
7409
|
|
|
6554
|
-
|
|
7410
|
+
removeSVGEls(svg, { removeEls, removeNameSpaced });
|
|
6555
7411
|
|
|
6556
7412
|
// an array of all elements' properties
|
|
6557
7413
|
let svgElProps = [];
|
|
6558
|
-
|
|
6559
|
-
let geometryElements = ['polygon', 'polyline', 'line', 'rect', 'circle', 'ellipse'];
|
|
6560
|
-
|
|
6561
|
-
/** convert paths to shapes */
|
|
6562
|
-
if(shapeConvert === 'toShapes'){
|
|
6563
|
-
let paths = svg.querySelectorAll('path');
|
|
6564
|
-
paths.forEach(path=>{
|
|
6565
|
-
let shape = pathElToShape(path, {convert_rects, convert_ellipses, convert_poly, convert_lines});
|
|
6566
|
-
path.replaceWith(shape);
|
|
6567
|
-
path = shape;
|
|
6568
|
-
|
|
6569
|
-
});
|
|
6570
|
-
|
|
6571
|
-
}
|
|
7414
|
+
let els = svg.querySelectorAll(`${renderedEls.join(', ')}`);
|
|
6572
7415
|
|
|
6573
7416
|
for (let i = 0; i < els.length; i++) {
|
|
6574
7417
|
let el = els[i];
|
|
6575
7418
|
|
|
6576
7419
|
let name = el.nodeName.toLowerCase();
|
|
6577
7420
|
|
|
6578
|
-
//
|
|
6579
|
-
if (shapeConvert === 'toPaths' && name !== 'path' && geometryElements.includes(name)) {
|
|
6580
|
-
let path = shapeElToPath(el, { width, height, convert_rects, convert_ellipses, convert_poly, convert_lines });
|
|
6581
|
-
el.replaceWith(path);
|
|
6582
|
-
name = 'path';
|
|
6583
|
-
el = path;
|
|
6584
|
-
|
|
6585
|
-
}
|
|
6586
|
-
|
|
6587
|
-
// remove hidden elements
|
|
7421
|
+
// 1. remove hidden elements
|
|
6588
7422
|
let style = el.getAttribute('style') || '';
|
|
6589
7423
|
let isHiddenByStyle = style ? style.trim().includes('display:none') : false;
|
|
6590
7424
|
let isHidden = (el.getAttribute('display') && el.getAttribute('display') === 'none') || isHiddenByStyle;
|
|
@@ -6598,85 +7432,214 @@
|
|
|
6598
7432
|
* convert relative or physical units
|
|
6599
7433
|
* to user units
|
|
6600
7434
|
*/
|
|
7435
|
+
let styleProps = parseStylesProperties(el, propOptions);
|
|
6601
7436
|
|
|
6602
|
-
|
|
6603
|
-
let
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
})
|
|
6607
|
-
*/
|
|
7437
|
+
// get parent styles
|
|
7438
|
+
let { parentStyleProps = [] } = el;
|
|
7439
|
+
let inheritedProps = {};
|
|
7440
|
+
let transFormInherited = [];
|
|
6608
7441
|
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
7442
|
+
/** inherit transforms
|
|
7443
|
+
* and styles from group
|
|
7444
|
+
*/
|
|
7445
|
+
parentStyleProps.forEach(props => {
|
|
7446
|
+
// transforms from groups are applied cumulatively
|
|
7447
|
+
let { transformArr = [] } = props;
|
|
7448
|
+
transFormInherited.push(...transformArr);
|
|
7449
|
+
|
|
7450
|
+
// merge
|
|
7451
|
+
inheritedProps = {
|
|
7452
|
+
...inheritedProps,
|
|
7453
|
+
...props
|
|
7454
|
+
};
|
|
7455
|
+
});
|
|
7456
|
+
|
|
7457
|
+
transFormInherited = [...transFormInherited, ...styleProps.transformArr];
|
|
7458
|
+
styleProps.transformArr = transFormInherited;
|
|
7459
|
+
|
|
7460
|
+
// merge with svg props
|
|
7461
|
+
styleProps = {
|
|
7462
|
+
...stylePropsSVG,
|
|
7463
|
+
...inheritedProps,
|
|
7464
|
+
...styleProps
|
|
7465
|
+
};
|
|
6613
7466
|
|
|
6614
|
-
//
|
|
6615
|
-
|
|
6616
|
-
let propsFiltered = svgStylesToAttributes(el, { removeNameSpaced, decimals });
|
|
7467
|
+
// add combined transforms
|
|
7468
|
+
addTransFormProps(styleProps, transFormInherited);
|
|
6617
7469
|
|
|
6618
|
-
|
|
7470
|
+
let { remove, matrix, transComponents } = styleProps;
|
|
7471
|
+
|
|
7472
|
+
// mark attributes for removal
|
|
7473
|
+
if (removeClassNames) styleProps.remove.push('class');
|
|
7474
|
+
if (removeIds) styleProps.remove.push('id');
|
|
7475
|
+
if (removeDimensions) {
|
|
7476
|
+
styleProps.remove.push('width');
|
|
7477
|
+
styleProps.remove.push('height');
|
|
6619
7478
|
}
|
|
6620
7479
|
|
|
6621
|
-
|
|
7480
|
+
// styles to atts
|
|
7481
|
+
if (unGroup || convertTransforms || minifyRgbColors ) stylesToAttributes = true;
|
|
6622
7482
|
|
|
6623
|
-
|
|
6624
|
-
if (cleanUpStrokes) {
|
|
7483
|
+
if (stylesToAttributes) {
|
|
6625
7484
|
|
|
6626
|
-
|
|
7485
|
+
/**
|
|
7486
|
+
* normalize transforms
|
|
7487
|
+
*/
|
|
7488
|
+
if (normalizeTransforms && matrix) {
|
|
7489
|
+
let { rotate, scaleX, scaleY, skewX, translateX, translateY } = transComponents;
|
|
6627
7490
|
|
|
6628
|
-
|
|
6629
|
-
|
|
7491
|
+
// scale attributes instead of transform
|
|
7492
|
+
let hasRot = rotate !== 0 || skewX !== 0;
|
|
7493
|
+
let unProportional = scaleX !== scaleY;
|
|
7494
|
+
let scalableByAtt = ['circle', 'ellipse', 'rect'];
|
|
7495
|
+
let needsTrans = convertTransforms || (name === 'g') || (hasRot) || unProportional;
|
|
6630
7496
|
|
|
6631
|
-
|
|
6632
|
-
strokeAtts.forEach(att => {
|
|
6633
|
-
el.removeAttribute(att);
|
|
7497
|
+
if (!needsTrans && scalableByAtt.includes(name)) {
|
|
6634
7498
|
|
|
6635
|
-
|
|
6636
|
-
|
|
7499
|
+
if (name === 'circle' || name === 'ellipse') {
|
|
7500
|
+
styleProps.cx[0] = [styleProps.cx[0] * scaleX + translateX];
|
|
7501
|
+
styleProps.cy[0] = [styleProps.cy[0] * scaleX + translateY];
|
|
7502
|
+
|
|
7503
|
+
if (styleProps.r) styleProps.r[0] = [styleProps.r[0] * scaleX];
|
|
7504
|
+
|
|
7505
|
+
if (styleProps.rx) styleProps.rx[0] = [styleProps.rx[0] * scaleX];
|
|
7506
|
+
if (styleProps.ry) styleProps.ry[0] = [styleProps.ry[0] * scaleX];
|
|
7507
|
+
|
|
7508
|
+
}
|
|
7509
|
+
else if (name === 'rect') {
|
|
7510
|
+
let x = styleProps.x ? styleProps.x[0] + translateX : translateX;
|
|
7511
|
+
let y = styleProps.y ? styleProps.y[0] + translateY : translateY;
|
|
7512
|
+
|
|
7513
|
+
let rx = styleProps.rx ? styleProps.rx[0] * scaleX : 0;
|
|
7514
|
+
let ry = styleProps.ry ? styleProps.ry[0] * scaleY : 0;
|
|
7515
|
+
|
|
7516
|
+
styleProps.x = [x];
|
|
7517
|
+
styleProps.y = [y];
|
|
7518
|
+
|
|
7519
|
+
styleProps.rx = [rx];
|
|
7520
|
+
styleProps.ry = [ry];
|
|
7521
|
+
|
|
7522
|
+
styleProps.width = [styleProps.width[0] * scaleX];
|
|
7523
|
+
styleProps.height = [styleProps.height[0] * scaleX];
|
|
7524
|
+
}
|
|
7525
|
+
|
|
7526
|
+
remove.push('transform');
|
|
7527
|
+
|
|
7528
|
+
// scale props like stroke width or dash-array
|
|
7529
|
+
styleProps = scaleProps(styleProps, { props: ['stroke-width', 'stroke-dasharray'], scale: scaleX });
|
|
7530
|
+
|
|
7531
|
+
} else {
|
|
7532
|
+
el.setAttribute('transform', transComponents.matrixAtt);
|
|
7533
|
+
|
|
7534
|
+
}
|
|
7535
|
+
}
|
|
7536
|
+
|
|
7537
|
+
/**
|
|
7538
|
+
* apply consolidated
|
|
7539
|
+
* element attributes
|
|
7540
|
+
*/
|
|
7541
|
+
|
|
7542
|
+
let stylePropsFiltered = filterSvgElProps(name, styleProps,
|
|
7543
|
+
{ removeDefaults: true, cleanUpStrokes });
|
|
7544
|
+
|
|
7545
|
+
remove = [...remove, ...stylePropsFiltered.remove];
|
|
7546
|
+
|
|
7547
|
+
for (let prop in stylePropsFiltered.propsFiltered) {
|
|
7548
|
+
let values = styleProps[prop];
|
|
7549
|
+
|
|
7550
|
+
let val = values.length ? values.join(' ') : values[0];
|
|
7551
|
+
el.setAttribute(prop, val);
|
|
7552
|
+
}
|
|
7553
|
+
|
|
7554
|
+
// remove obsolete attributes
|
|
7555
|
+
for (let i = 0; i < remove.length; i++) {
|
|
7556
|
+
let att = remove[i];
|
|
7557
|
+
if (!stylesToAttributes && att === 'style') continue
|
|
7558
|
+
|
|
7559
|
+
el.removeAttribute(att);
|
|
7560
|
+
}
|
|
7561
|
+
|
|
7562
|
+
/**
|
|
7563
|
+
* remove group styles
|
|
7564
|
+
* copied to children
|
|
7565
|
+
* or remove nesting
|
|
7566
|
+
*/
|
|
7567
|
+
|
|
7568
|
+
if (unGroup) {
|
|
7569
|
+
groups.forEach((g, i) => {
|
|
7570
|
+
let children = [...g.children];
|
|
7571
|
+
|
|
7572
|
+
children.forEach(child => {
|
|
7573
|
+
g.parentNode.insertBefore(child, g);
|
|
7574
|
+
});
|
|
7575
|
+
g.remove();
|
|
7576
|
+
});
|
|
7577
|
+
} else {
|
|
7578
|
+
groups.forEach((g, i) => {
|
|
7579
|
+
let atts = [...Object.keys(groupProps[i]), 'style', 'transform'];
|
|
7580
|
+
atts.forEach(att => {
|
|
7581
|
+
g.removeAttribute(att);
|
|
7582
|
+
});
|
|
6637
7583
|
});
|
|
7584
|
+
|
|
6638
7585
|
}
|
|
7586
|
+
|
|
7587
|
+
} // endof style processing
|
|
7588
|
+
|
|
7589
|
+
/**
|
|
7590
|
+
* element conversions:
|
|
7591
|
+
* shapes to paths or
|
|
7592
|
+
* paths to shapes
|
|
7593
|
+
*/
|
|
7594
|
+
|
|
7595
|
+
// force shape conversion when transform conversion is enabled
|
|
7596
|
+
if (convertTransforms) {
|
|
7597
|
+
shapeConvert = 'toPaths';
|
|
7598
|
+
convert_rects = true;
|
|
7599
|
+
convert_ellipses = true;
|
|
7600
|
+
convert_poly = true;
|
|
7601
|
+
convert_lines = true;
|
|
6639
7602
|
}
|
|
6640
|
-
}
|
|
6641
7603
|
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
moveAttributesToGroup(svgElProps, mergePaths);
|
|
6645
|
-
}
|
|
7604
|
+
// convert shapes to paths
|
|
7605
|
+
if (shapeConvert === 'toPaths') {
|
|
6646
7606
|
|
|
6647
|
-
|
|
6648
|
-
svg.removeAttribute('width');
|
|
6649
|
-
svg.removeAttribute('height');
|
|
6650
|
-
}
|
|
7607
|
+
let { matrix = null, transComponents = null } = styleProps;
|
|
6651
7608
|
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
}
|
|
7609
|
+
if (matrix && transComponents) {
|
|
7610
|
+
// scale props like stroke width or dash-array before conversion
|
|
7611
|
+
['stroke-width', 'stroke-dasharray'].forEach(att => {
|
|
7612
|
+
let attVal = el.getAttribute(att);
|
|
7613
|
+
let vals = attVal ? attVal.split(' ').filter(Boolean).map(Number).map(val => val * transComponents.scaleX) : [];
|
|
7614
|
+
if (vals.length) el.setAttribute(att, vals.join(' '));
|
|
7615
|
+
});
|
|
7616
|
+
}
|
|
6661
7617
|
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
7618
|
+
// convert paths only if a matrix transform is required
|
|
7619
|
+
if (matrix ? geometryEls.includes(name) : shapeEls.includes(name)) {
|
|
7620
|
+
|
|
7621
|
+
let path = shapeElToPath(el, { width, height, convert_rects, convert_ellipses, convert_poly, convert_lines, matrix });
|
|
7622
|
+
el.replaceWith(path);
|
|
6666
7623
|
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
let item = svgElProps[i];
|
|
6670
|
-
let {propsFiltered} = item;
|
|
7624
|
+
name = 'path';
|
|
7625
|
+
el = path;
|
|
6671
7626
|
|
|
6672
|
-
|
|
7627
|
+
}
|
|
7628
|
+
|
|
7629
|
+
}
|
|
6673
7630
|
|
|
6674
|
-
|
|
7631
|
+
// convert paths to shapes
|
|
7632
|
+
else if (shapeConvert === 'toShapes') {
|
|
7633
|
+
let paths = svg.querySelectorAll('path');
|
|
7634
|
+
paths.forEach(path => {
|
|
7635
|
+
let shape = pathElToShape(path, { convert_rects, convert_ellipses, convert_poly, convert_lines });
|
|
7636
|
+
path.replaceWith(shape);
|
|
7637
|
+
path = shape;
|
|
7638
|
+
});
|
|
6675
7639
|
|
|
6676
7640
|
}
|
|
6677
7641
|
|
|
6678
|
-
}
|
|
6679
|
-
*/
|
|
7642
|
+
}//endof element loop
|
|
6680
7643
|
|
|
6681
7644
|
// remove futile clip-paths
|
|
6682
7645
|
if (cleanupClip) removeFutileClipPaths(svg, { x, y, width, height });
|
|
@@ -6806,139 +7769,32 @@
|
|
|
6806
7769
|
|
|
6807
7770
|
}
|
|
6808
7771
|
|
|
6809
|
-
function
|
|
6810
|
-
|
|
6811
|
-
let combine = [[svgElProps[0]]];
|
|
6812
|
-
let idx = 0;
|
|
6813
|
-
let lastProps = '';
|
|
6814
|
-
let l = svgElProps.length;
|
|
6815
|
-
let itemsWithProps = svgElProps.filter(item => item.propstr);
|
|
6816
|
-
let path0;
|
|
6817
|
-
|
|
6818
|
-
// merge paths without properties
|
|
6819
|
-
let dCombined = '';
|
|
6820
|
-
if (!itemsWithProps.length && mergePaths) {
|
|
6821
|
-
let path0 = null;
|
|
6822
|
-
|
|
6823
|
-
for (let i = 0; i < l; i++) {
|
|
6824
|
-
let item = svgElProps[i];
|
|
6825
|
-
if (item.name !== 'path') continue;
|
|
6826
|
-
let remove = true;
|
|
6827
|
-
|
|
6828
|
-
let path = item.el;
|
|
6829
|
-
|
|
6830
|
-
// set 1st path
|
|
6831
|
-
if (!path0) {
|
|
6832
|
-
path0 = path;
|
|
6833
|
-
remove = false;
|
|
6834
|
-
}
|
|
7772
|
+
function scaleProps(styleProps = {}, { props = [], scale = 1 } = {}) {
|
|
7773
|
+
if (scale === 1 || !props.length) return props;
|
|
6835
7774
|
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
let dAbs = isAbs ? d : parsePathDataString(d).pathData.map(com => `${com.type} ${com.values.join(' ')}`).join(' ');
|
|
7775
|
+
for (let i = 0; i < props.length; i++) {
|
|
7776
|
+
let prop = props[i];
|
|
6839
7777
|
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
// delete path el
|
|
6843
|
-
if (remove) path.remove();
|
|
6844
|
-
}
|
|
6845
|
-
|
|
6846
|
-
if (path0) path0.setAttribute('d', dCombined);
|
|
6847
|
-
return
|
|
6848
|
-
}
|
|
6849
|
-
|
|
6850
|
-
// add to combine chunks
|
|
6851
|
-
for (let i = 0; i < l; i++) {
|
|
6852
|
-
let item = svgElProps[i];
|
|
6853
|
-
let props = item.propsFiltered;
|
|
6854
|
-
let propstr = [];
|
|
6855
|
-
for (let prop in props) {
|
|
6856
|
-
if (prop !== 'd' && prop !== 'id') {
|
|
6857
|
-
propstr.push(`${prop}:${props[prop]}`);
|
|
6858
|
-
}
|
|
6859
|
-
}
|
|
6860
|
-
propstr = propstr.join('_');
|
|
6861
|
-
item.propstr = propstr;
|
|
6862
|
-
|
|
6863
|
-
if (l > 1 && propstr === lastProps) {
|
|
6864
|
-
combine[idx].push(item);
|
|
6865
|
-
} else {
|
|
6866
|
-
if (l > 1 && combine[idx].length) {
|
|
6867
|
-
combine.push([]);
|
|
6868
|
-
idx++;
|
|
6869
|
-
}
|
|
7778
|
+
if (styleProps[prop] !== undefined) {
|
|
7779
|
+
styleProps[prop] = styleProps[prop].map(val => val * scale);
|
|
6870
7780
|
}
|
|
6871
|
-
lastProps = propstr;
|
|
6872
7781
|
}
|
|
7782
|
+
return styleProps
|
|
7783
|
+
}
|
|
6873
7784
|
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
if (!g) {
|
|
6886
|
-
g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
6887
|
-
el0.parentNode.insertBefore(g, el0);
|
|
6888
|
-
group.forEach(item => {
|
|
6889
|
-
g.append(item.el);
|
|
6890
|
-
});
|
|
6891
|
-
}
|
|
6892
|
-
|
|
6893
|
-
let children = [...g.children];
|
|
6894
|
-
for (let prop in props) {
|
|
6895
|
-
if (prop !== 'd' && prop !== 'id') {
|
|
6896
|
-
let value = props[prop];
|
|
6897
|
-
// apply to parent group
|
|
6898
|
-
g.setAttribute(prop, value);
|
|
6899
|
-
|
|
6900
|
-
// remove from children
|
|
6901
|
-
children.forEach(el => {
|
|
6902
|
-
if (el.getAttribute(prop) === value) {
|
|
6903
|
-
el.removeAttribute(prop);
|
|
6904
|
-
}
|
|
6905
|
-
});
|
|
6906
|
-
}
|
|
6907
|
-
|
|
6908
|
-
if (mergePaths) {
|
|
6909
|
-
group = group.filter(Boolean);
|
|
6910
|
-
let l = group.length;
|
|
6911
|
-
// nothing to merge
|
|
6912
|
-
if (l === 1) return group[0].el;
|
|
6913
|
-
|
|
6914
|
-
path0 = group[0].el;
|
|
6915
|
-
let dCombined = group[0].propsFiltered.d;
|
|
6916
|
-
|
|
6917
|
-
for (let i = 1; i < l; i++) {
|
|
6918
|
-
let item = group[i];
|
|
6919
|
-
let path = item.el;
|
|
6920
|
-
let d = item.propsFiltered.d;
|
|
6921
|
-
let isAbs = d.startsWith('M');
|
|
6922
|
-
|
|
6923
|
-
let dAbs = isAbs ? d : parsePathDataString(d).pathData.map(com => `${com.type} ${com.values.join(' ')}`).join(' ');
|
|
6924
|
-
|
|
6925
|
-
console.log('dAbs', dAbs);
|
|
6926
|
-
|
|
6927
|
-
// concat pathdata string
|
|
6928
|
-
dCombined += dAbs;
|
|
6929
|
-
|
|
6930
|
-
// delete path el
|
|
6931
|
-
path.remove();
|
|
6932
|
-
}
|
|
6933
|
-
|
|
6934
|
-
path0.setAttribute('d', dCombined);
|
|
6935
|
-
|
|
6936
|
-
}
|
|
6937
|
-
|
|
6938
|
-
}
|
|
7785
|
+
function removeSVGEls(svg, {
|
|
7786
|
+
remove = ['metadata', 'script'],
|
|
7787
|
+
removeNameSpaced = true,
|
|
7788
|
+
} = {}) {
|
|
7789
|
+
let els = svg.querySelectorAll('*');
|
|
7790
|
+
els.forEach(el => {
|
|
7791
|
+
let nodeName = el.nodeName;
|
|
7792
|
+
if ((removeNameSpaced && nodeName.includes(':')) ||
|
|
7793
|
+
remove.includes(nodeName)
|
|
7794
|
+
) {
|
|
7795
|
+
el.remove();
|
|
6939
7796
|
}
|
|
6940
|
-
}
|
|
6941
|
-
|
|
7797
|
+
});
|
|
6942
7798
|
}
|
|
6943
7799
|
|
|
6944
7800
|
function removeExcludedAttribues(el, allowed = ['viewBox', 'xmlns', 'width', 'height', 'id', 'class']) {
|
|
@@ -6950,13 +7806,21 @@
|
|
|
6950
7806
|
});
|
|
6951
7807
|
}
|
|
6952
7808
|
|
|
6953
|
-
function stringifySVG(svg,
|
|
7809
|
+
function stringifySVG(svg, {
|
|
7810
|
+
omitNamespace = false,
|
|
7811
|
+
removeComments = true,
|
|
7812
|
+
} = {}) {
|
|
6954
7813
|
let markup = new XMLSerializer().serializeToString(svg);
|
|
6955
7814
|
|
|
6956
7815
|
if (omitNamespace) {
|
|
6957
7816
|
markup = markup.replaceAll('xmlns="http://www.w3.org/2000/svg"', '');
|
|
6958
7817
|
}
|
|
6959
7818
|
|
|
7819
|
+
if (removeComments) {
|
|
7820
|
+
markup = markup
|
|
7821
|
+
.replace(/(<!--.*?-->)|(<!--[\S\s]+?-->)|(<!--[\S\s]*?$)/g, '');
|
|
7822
|
+
}
|
|
7823
|
+
|
|
6960
7824
|
markup = markup
|
|
6961
7825
|
.replace(/\t/g, "")
|
|
6962
7826
|
.replace(/[\n\r|]/g, "\n")
|
|
@@ -6965,7 +7829,7 @@
|
|
|
6965
7829
|
|
|
6966
7830
|
.replace(/> </g, '><')
|
|
6967
7831
|
.trim()
|
|
6968
|
-
|
|
7832
|
+
// sanitize linebreaks within pathdata
|
|
6969
7833
|
.replaceAll(' ', '\n');
|
|
6970
7834
|
|
|
6971
7835
|
return markup
|
|
@@ -8814,7 +9678,7 @@
|
|
|
8814
9678
|
poly = poly.map(pt => { return [pt.x, pt.y] });
|
|
8815
9679
|
}
|
|
8816
9680
|
else if(polyFormat==='string'){
|
|
8817
|
-
poly = poly.map(pt => { return [pt.x, pt.y].join(',') }).join(' ');
|
|
9681
|
+
poly = poly.map(pt => { return [pt.x, pt.y].join(',') }).flat().join(' ');
|
|
8818
9682
|
}
|
|
8819
9683
|
|
|
8820
9684
|
return { pathData, poly }
|
|
@@ -9031,6 +9895,8 @@
|
|
|
9031
9895
|
scaleTo = 0,
|
|
9032
9896
|
crop = false,
|
|
9033
9897
|
alignToOrigin = false,
|
|
9898
|
+
|
|
9899
|
+
// flatten transforms
|
|
9034
9900
|
convertTransforms = false,
|
|
9035
9901
|
|
|
9036
9902
|
decimals = 3,
|
|
@@ -9042,20 +9908,21 @@
|
|
|
9042
9908
|
tolerance = 1,
|
|
9043
9909
|
reversePath = false,
|
|
9044
9910
|
|
|
9911
|
+
minifyRgbColors = false,
|
|
9045
9912
|
removePrologue = true,
|
|
9046
9913
|
removeHidden = true,
|
|
9047
9914
|
removeUnused = true,
|
|
9048
9915
|
cleanupDefs = true,
|
|
9049
9916
|
cleanupClip = true,
|
|
9050
9917
|
cleanupSVGAtts = true,
|
|
9051
|
-
|
|
9918
|
+
|
|
9052
9919
|
stylesToAttributes = false,
|
|
9053
9920
|
fixHref = false,
|
|
9054
9921
|
legacyHref = false,
|
|
9055
9922
|
removeNameSpaced = true,
|
|
9056
|
-
attributesToGroup = false,
|
|
9057
|
-
removeOffCanvas = false,
|
|
9058
9923
|
|
|
9924
|
+
removeOffCanvas = false,
|
|
9925
|
+
unGroup = false,
|
|
9059
9926
|
mergePaths = false,
|
|
9060
9927
|
|
|
9061
9928
|
// shape conversions
|
|
@@ -9072,6 +9939,8 @@
|
|
|
9072
9939
|
addViewBox = false,
|
|
9073
9940
|
addDimensions = false,
|
|
9074
9941
|
|
|
9942
|
+
removeComments = true,
|
|
9943
|
+
|
|
9075
9944
|
} = {}) {
|
|
9076
9945
|
|
|
9077
9946
|
// clamp tolerance and scale
|
|
@@ -9150,10 +10019,18 @@
|
|
|
9150
10019
|
// mode:1 – process complete svg DOM
|
|
9151
10020
|
else {
|
|
9152
10021
|
|
|
10022
|
+
// convert all shapes to paths
|
|
10023
|
+
if (shapesToPaths) {
|
|
10024
|
+
shapeConvert = true;
|
|
10025
|
+
convert_rects = true;
|
|
10026
|
+
convert_ellipses = true;
|
|
10027
|
+
convert_poly = true;
|
|
10028
|
+
convert_lines = true;
|
|
10029
|
+
}
|
|
10030
|
+
|
|
9153
10031
|
let svgPropObject = cleanUpSVG(input, {
|
|
9154
10032
|
removeIds, removeClassNames, removeDimensions, cleanupSVGAtts, cleanUpStrokes, removeHidden, removeUnused, removeNameSpaced, stylesToAttributes, removePrologue, fixHref, mergePaths, convertTransforms, legacyHref, cleanupDefs, cleanupClip, addViewBox, removeOffCanvas, addDimensions,
|
|
9155
|
-
shapeConvert, convert_rects, convert_ellipses, convert_poly, convert_lines
|
|
9156
|
-
|
|
10033
|
+
shapeConvert, convert_rects, convert_ellipses, convert_poly, convert_lines, minifyRgbColors, unGroup, convertTransforms
|
|
9157
10034
|
}
|
|
9158
10035
|
);
|
|
9159
10036
|
svg = svgPropObject.svg;
|
|
@@ -9435,8 +10312,8 @@
|
|
|
9435
10312
|
|
|
9436
10313
|
if (autoAccuracy) {
|
|
9437
10314
|
accuracyArr = accuracyArr.sort().reverse();
|
|
9438
|
-
let decimalsMid = accuracyArr[Math.floor(accuracyArr.length*0.5)];
|
|
9439
|
-
decimals = Math.floor(
|
|
10315
|
+
let decimalsMid = accuracyArr[Math.floor(accuracyArr.length * 0.5)];
|
|
10316
|
+
decimals = Math.floor((accuracyArr[0] + decimalsMid) * 0.5);
|
|
9440
10317
|
|
|
9441
10318
|
pathOptions.decimals = decimals;
|
|
9442
10319
|
}
|
|
@@ -9559,7 +10436,7 @@
|
|
|
9559
10436
|
});
|
|
9560
10437
|
}
|
|
9561
10438
|
|
|
9562
|
-
svg = stringifySVG(svg, omitNamespace);
|
|
10439
|
+
svg = stringifySVG(svg, { omitNamespace, removeComments });
|
|
9563
10440
|
|
|
9564
10441
|
svgSizeOpt = svg.length;
|
|
9565
10442
|
|