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