@emasoft/svg-matrix 1.0.30 → 1.0.31
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/bin/svg-matrix.js +310 -61
- package/bin/svglinter.cjs +102 -3
- package/bin/svgm.js +236 -27
- package/package.json +1 -1
- package/src/animation-optimization.js +137 -17
- package/src/animation-references.js +123 -6
- package/src/arc-length.js +213 -4
- package/src/bezier-analysis.js +217 -21
- package/src/bezier-intersections.js +275 -12
- package/src/browser-verify.js +237 -4
- package/src/clip-path-resolver.js +168 -0
- package/src/convert-path-data.js +479 -28
- package/src/css-specificity.js +73 -10
- package/src/douglas-peucker.js +219 -2
- package/src/flatten-pipeline.js +284 -26
- package/src/geometry-to-path.js +250 -25
- package/src/gjk-collision.js +236 -33
- package/src/index.js +261 -3
- package/src/inkscape-support.js +86 -28
- package/src/logger.js +48 -3
- package/src/marker-resolver.js +278 -74
- package/src/mask-resolver.js +265 -66
- package/src/matrix.js +44 -5
- package/src/mesh-gradient.js +352 -102
- package/src/off-canvas-detection.js +382 -13
- package/src/path-analysis.js +192 -18
- package/src/path-data-plugins.js +309 -5
- package/src/path-optimization.js +129 -5
- package/src/path-simplification.js +188 -32
- package/src/pattern-resolver.js +454 -106
- package/src/polygon-clip.js +324 -1
- package/src/svg-boolean-ops.js +226 -9
- package/src/svg-collections.js +7 -5
- package/src/svg-flatten.js +386 -62
- package/src/svg-parser.js +179 -8
- package/src/svg-rendering-context.js +235 -6
- package/src/svg-toolbox.js +45 -8
- package/src/svg2-polyfills.js +40 -10
- package/src/transform-decomposition.js +258 -32
- package/src/transform-optimization.js +259 -13
- package/src/transforms2d.js +82 -9
- package/src/transforms3d.js +62 -10
- package/src/use-symbol-resolver.js +286 -42
- package/src/vector.js +64 -8
- package/src/verification.js +392 -1
|
@@ -94,6 +94,10 @@ export function pathToPolygon(
|
|
|
94
94
|
pathData,
|
|
95
95
|
samplesPerCurve = DEFAULT_CURVE_SAMPLES,
|
|
96
96
|
) {
|
|
97
|
+
if (typeof pathData !== 'string') throw new Error('pathToPolygon: pathData must be a string');
|
|
98
|
+
if (typeof samplesPerCurve !== 'number' || samplesPerCurve <= 0 || !Number.isFinite(samplesPerCurve)) {
|
|
99
|
+
throw new Error(`pathToPolygon: samplesPerCurve must be a positive finite number, got ${samplesPerCurve}`);
|
|
100
|
+
}
|
|
97
101
|
const points = [];
|
|
98
102
|
let currentX = D(0),
|
|
99
103
|
currentY = D(0);
|
|
@@ -101,12 +105,19 @@ export function pathToPolygon(
|
|
|
101
105
|
startY = D(0);
|
|
102
106
|
|
|
103
107
|
const commands = parsePathCommands(pathData);
|
|
108
|
+
if (!Array.isArray(commands)) {
|
|
109
|
+
throw new Error('pathToPolygon: parsePathCommands must return an array');
|
|
110
|
+
}
|
|
104
111
|
|
|
105
112
|
for (const cmd of commands) {
|
|
106
113
|
const { type, args } = cmd;
|
|
114
|
+
if (!Array.isArray(args)) {
|
|
115
|
+
throw new Error(`pathToPolygon: command args must be an array for command ${type}`);
|
|
116
|
+
}
|
|
107
117
|
|
|
108
118
|
switch (type) {
|
|
109
119
|
case "M":
|
|
120
|
+
if (args.length < 2) throw new Error(`pathToPolygon: M command requires 2 arguments, got ${args.length}`);
|
|
110
121
|
currentX = D(args[0]);
|
|
111
122
|
currentY = D(args[1]);
|
|
112
123
|
startX = currentX;
|
|
@@ -114,6 +125,7 @@ export function pathToPolygon(
|
|
|
114
125
|
points.push(PolygonClip.point(currentX, currentY));
|
|
115
126
|
break;
|
|
116
127
|
case "m":
|
|
128
|
+
if (args.length < 2) throw new Error(`pathToPolygon: m command requires 2 arguments, got ${args.length}`);
|
|
117
129
|
currentX = currentX.plus(args[0]);
|
|
118
130
|
currentY = currentY.plus(args[1]);
|
|
119
131
|
startX = currentX;
|
|
@@ -121,32 +133,39 @@ export function pathToPolygon(
|
|
|
121
133
|
points.push(PolygonClip.point(currentX, currentY));
|
|
122
134
|
break;
|
|
123
135
|
case "L":
|
|
136
|
+
if (args.length < 2) throw new Error(`pathToPolygon: L command requires 2 arguments, got ${args.length}`);
|
|
124
137
|
currentX = D(args[0]);
|
|
125
138
|
currentY = D(args[1]);
|
|
126
139
|
points.push(PolygonClip.point(currentX, currentY));
|
|
127
140
|
break;
|
|
128
141
|
case "l":
|
|
142
|
+
if (args.length < 2) throw new Error(`pathToPolygon: l command requires 2 arguments, got ${args.length}`);
|
|
129
143
|
currentX = currentX.plus(args[0]);
|
|
130
144
|
currentY = currentY.plus(args[1]);
|
|
131
145
|
points.push(PolygonClip.point(currentX, currentY));
|
|
132
146
|
break;
|
|
133
147
|
case "H":
|
|
148
|
+
if (args.length < 1) throw new Error(`pathToPolygon: H command requires 1 argument, got ${args.length}`);
|
|
134
149
|
currentX = D(args[0]);
|
|
135
150
|
points.push(PolygonClip.point(currentX, currentY));
|
|
136
151
|
break;
|
|
137
152
|
case "h":
|
|
153
|
+
if (args.length < 1) throw new Error(`pathToPolygon: h command requires 1 argument, got ${args.length}`);
|
|
138
154
|
currentX = currentX.plus(args[0]);
|
|
139
155
|
points.push(PolygonClip.point(currentX, currentY));
|
|
140
156
|
break;
|
|
141
157
|
case "V":
|
|
158
|
+
if (args.length < 1) throw new Error(`pathToPolygon: V command requires 1 argument, got ${args.length}`);
|
|
142
159
|
currentY = D(args[0]);
|
|
143
160
|
points.push(PolygonClip.point(currentX, currentY));
|
|
144
161
|
break;
|
|
145
162
|
case "v":
|
|
163
|
+
if (args.length < 1) throw new Error(`pathToPolygon: v command requires 1 argument, got ${args.length}`);
|
|
146
164
|
currentY = currentY.plus(args[0]);
|
|
147
165
|
points.push(PolygonClip.point(currentX, currentY));
|
|
148
166
|
break;
|
|
149
167
|
case "C":
|
|
168
|
+
if (args.length < 6) throw new Error(`pathToPolygon: C command requires 6 arguments, got ${args.length}`);
|
|
150
169
|
sampleCubicBezier(
|
|
151
170
|
points,
|
|
152
171
|
currentX,
|
|
@@ -163,6 +182,7 @@ export function pathToPolygon(
|
|
|
163
182
|
currentY = D(args[5]);
|
|
164
183
|
break;
|
|
165
184
|
case "c":
|
|
185
|
+
if (args.length < 6) throw new Error(`pathToPolygon: c command requires 6 arguments, got ${args.length}`);
|
|
166
186
|
sampleCubicBezier(
|
|
167
187
|
points,
|
|
168
188
|
currentX,
|
|
@@ -179,6 +199,7 @@ export function pathToPolygon(
|
|
|
179
199
|
currentY = currentY.plus(args[5]);
|
|
180
200
|
break;
|
|
181
201
|
case "Q":
|
|
202
|
+
if (args.length < 4) throw new Error(`pathToPolygon: Q command requires 4 arguments, got ${args.length}`);
|
|
182
203
|
sampleQuadraticBezier(
|
|
183
204
|
points,
|
|
184
205
|
currentX,
|
|
@@ -193,6 +214,7 @@ export function pathToPolygon(
|
|
|
193
214
|
currentY = D(args[3]);
|
|
194
215
|
break;
|
|
195
216
|
case "q":
|
|
217
|
+
if (args.length < 4) throw new Error(`pathToPolygon: q command requires 4 arguments, got ${args.length}`);
|
|
196
218
|
sampleQuadraticBezier(
|
|
197
219
|
points,
|
|
198
220
|
currentX,
|
|
@@ -207,6 +229,7 @@ export function pathToPolygon(
|
|
|
207
229
|
currentY = currentY.plus(args[3]);
|
|
208
230
|
break;
|
|
209
231
|
case "A":
|
|
232
|
+
if (args.length < 7) throw new Error(`pathToPolygon: A command requires 7 arguments, got ${args.length}`);
|
|
210
233
|
sampleArc(
|
|
211
234
|
points,
|
|
212
235
|
currentX,
|
|
@@ -224,6 +247,7 @@ export function pathToPolygon(
|
|
|
224
247
|
currentY = D(args[6]);
|
|
225
248
|
break;
|
|
226
249
|
case "a":
|
|
250
|
+
if (args.length < 7) throw new Error(`pathToPolygon: a command requires 7 arguments, got ${args.length}`);
|
|
227
251
|
sampleArc(
|
|
228
252
|
points,
|
|
229
253
|
currentX,
|
|
@@ -245,6 +269,8 @@ export function pathToPolygon(
|
|
|
245
269
|
currentX = startX;
|
|
246
270
|
currentY = startY;
|
|
247
271
|
break;
|
|
272
|
+
default:
|
|
273
|
+
break;
|
|
248
274
|
}
|
|
249
275
|
}
|
|
250
276
|
|
|
@@ -267,6 +293,9 @@ export function pathToPolygon(
|
|
|
267
293
|
* // Returns: [{type: 'M', args: [10, 20]}, {type: 'L', args: [30, 40]}]
|
|
268
294
|
*/
|
|
269
295
|
function parsePathCommands(pathData) {
|
|
296
|
+
if (typeof pathData !== 'string') {
|
|
297
|
+
throw new Error(`parsePathCommands: pathData must be a string, got ${typeof pathData}`);
|
|
298
|
+
}
|
|
270
299
|
const commands = [];
|
|
271
300
|
const regex = /([MmLlHhVvCcSsQqTtAaZz])([^MmLlHhVvCcSsQqTtAaZz]*)/g;
|
|
272
301
|
let match;
|
|
@@ -310,6 +339,16 @@ function parsePathCommands(pathData) {
|
|
|
310
339
|
* // points now contains 20 sampled points along the cubic Bezier curve
|
|
311
340
|
*/
|
|
312
341
|
function sampleCubicBezier(points, x0, y0, x1, y1, x2, y2, x3, y3, samples) {
|
|
342
|
+
if (!Array.isArray(points)) {
|
|
343
|
+
throw new Error('sampleCubicBezier: points must be an array');
|
|
344
|
+
}
|
|
345
|
+
if (typeof samples !== 'number' || samples <= 0 || !Number.isFinite(samples)) {
|
|
346
|
+
throw new Error(`sampleCubicBezier: samples must be a positive finite number, got ${samples}`);
|
|
347
|
+
}
|
|
348
|
+
if (!(x0 instanceof Decimal) || !(y0 instanceof Decimal) || !(x1 instanceof Decimal) || !(y1 instanceof Decimal) ||
|
|
349
|
+
!(x2 instanceof Decimal) || !(y2 instanceof Decimal) || !(x3 instanceof Decimal) || !(y3 instanceof Decimal)) {
|
|
350
|
+
throw new Error('sampleCubicBezier: all coordinate parameters must be Decimal instances');
|
|
351
|
+
}
|
|
313
352
|
for (let i = 1; i <= samples; i++) {
|
|
314
353
|
const t = D(i).div(samples);
|
|
315
354
|
const mt = D(1).minus(t);
|
|
@@ -353,6 +392,16 @@ function sampleCubicBezier(points, x0, y0, x1, y1, x2, y2, x3, y3, samples) {
|
|
|
353
392
|
* // points now contains 20 sampled points along the quadratic Bezier curve
|
|
354
393
|
*/
|
|
355
394
|
function sampleQuadraticBezier(points, x0, y0, x1, y1, x2, y2, samples) {
|
|
395
|
+
if (!Array.isArray(points)) {
|
|
396
|
+
throw new Error('sampleQuadraticBezier: points must be an array');
|
|
397
|
+
}
|
|
398
|
+
if (typeof samples !== 'number' || samples <= 0 || !Number.isFinite(samples)) {
|
|
399
|
+
throw new Error(`sampleQuadraticBezier: samples must be a positive finite number, got ${samples}`);
|
|
400
|
+
}
|
|
401
|
+
if (!(x0 instanceof Decimal) || !(y0 instanceof Decimal) || !(x1 instanceof Decimal) ||
|
|
402
|
+
!(y1 instanceof Decimal) || !(x2 instanceof Decimal) || !(y2 instanceof Decimal)) {
|
|
403
|
+
throw new Error('sampleQuadraticBezier: all coordinate parameters must be Decimal instances');
|
|
404
|
+
}
|
|
356
405
|
for (let i = 1; i <= samples; i++) {
|
|
357
406
|
const t = D(i).div(samples);
|
|
358
407
|
const mt = D(1).minus(t);
|
|
@@ -401,6 +450,22 @@ function sampleArc(
|
|
|
401
450
|
y1,
|
|
402
451
|
samples,
|
|
403
452
|
) {
|
|
453
|
+
if (!Array.isArray(points)) {
|
|
454
|
+
throw new Error('sampleArc: points must be an array');
|
|
455
|
+
}
|
|
456
|
+
if (typeof samples !== 'number' || samples <= 0 || !Number.isFinite(samples)) {
|
|
457
|
+
throw new Error(`sampleArc: samples must be a positive finite number, got ${samples}`);
|
|
458
|
+
}
|
|
459
|
+
if (!(x0 instanceof Decimal) || !(y0 instanceof Decimal) || !(rx instanceof Decimal) || !(ry instanceof Decimal) ||
|
|
460
|
+
!(xAxisRotation instanceof Decimal) || !(x1 instanceof Decimal) || !(y1 instanceof Decimal)) {
|
|
461
|
+
throw new Error('sampleArc: all coordinate and angle parameters must be Decimal instances');
|
|
462
|
+
}
|
|
463
|
+
if (typeof largeArc !== 'number' || (largeArc !== 0 && largeArc !== 1)) {
|
|
464
|
+
throw new Error(`sampleArc: largeArc must be 0 or 1, got ${largeArc}`);
|
|
465
|
+
}
|
|
466
|
+
if (typeof sweep !== 'number' || (sweep !== 0 && sweep !== 1)) {
|
|
467
|
+
throw new Error(`sampleArc: sweep must be 0 or 1, got ${sweep}`);
|
|
468
|
+
}
|
|
404
469
|
if (rx.eq(0) || ry.eq(0)) {
|
|
405
470
|
points.push(PolygonClip.point(x1, y1));
|
|
406
471
|
return;
|
|
@@ -447,10 +512,20 @@ function sampleArc(
|
|
|
447
512
|
const vx = x1p.neg().minus(cxp).div(rx),
|
|
448
513
|
vy = y1p.neg().minus(cyp).div(ry);
|
|
449
514
|
const n1 = ux.mul(ux).plus(uy.mul(uy)).sqrt();
|
|
515
|
+
if (n1.eq(0)) {
|
|
516
|
+
// Degenerate arc: start and end points are the same after transformation
|
|
517
|
+
points.push(PolygonClip.point(x1, y1));
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
450
520
|
let theta1 = Decimal.acos(ux.div(n1));
|
|
451
521
|
if (uy.lt(0)) theta1 = theta1.neg();
|
|
452
522
|
|
|
453
523
|
const n2 = n1.mul(vx.mul(vx).plus(vy.mul(vy)).sqrt());
|
|
524
|
+
if (n2.eq(0)) {
|
|
525
|
+
// Degenerate arc: endpoints coincide
|
|
526
|
+
points.push(PolygonClip.point(x1, y1));
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
454
529
|
let dtheta = Decimal.acos(ux.mul(vx).plus(uy.mul(vy)).div(n2));
|
|
455
530
|
if (ux.mul(vy).minus(uy.mul(vx)).lt(0)) dtheta = dtheta.neg();
|
|
456
531
|
|
|
@@ -493,6 +568,9 @@ function sampleArc(
|
|
|
493
568
|
* // Returns: [{x: D(0), y: D(0)}, {x: D(1), y: D(1)}]
|
|
494
569
|
*/
|
|
495
570
|
function removeDuplicateConsecutive(points) {
|
|
571
|
+
if (!Array.isArray(points)) {
|
|
572
|
+
throw new Error('removeDuplicateConsecutive: points must be an array');
|
|
573
|
+
}
|
|
496
574
|
if (points.length < 2) return points;
|
|
497
575
|
const result = [points[0]];
|
|
498
576
|
for (let i = 1; i < points.length; i++) {
|
|
@@ -545,6 +623,21 @@ export function shapeToPolygon(
|
|
|
545
623
|
samples = DEFAULT_CURVE_SAMPLES,
|
|
546
624
|
bezierArcs = 4,
|
|
547
625
|
) {
|
|
626
|
+
if (!element || typeof element !== 'object') {
|
|
627
|
+
throw new Error('shapeToPolygon: element must be an object');
|
|
628
|
+
}
|
|
629
|
+
if (!element.type || typeof element.type !== 'string') {
|
|
630
|
+
throw new Error('shapeToPolygon: element.type must be a string');
|
|
631
|
+
}
|
|
632
|
+
if (typeof samples !== 'number' || samples <= 0 || !Number.isFinite(samples)) {
|
|
633
|
+
throw new Error(`shapeToPolygon: samples must be a positive finite number, got ${samples}`);
|
|
634
|
+
}
|
|
635
|
+
if (typeof bezierArcs !== 'number' || bezierArcs <= 0 || !Number.isFinite(bezierArcs)) {
|
|
636
|
+
throw new Error(`shapeToPolygon: bezierArcs must be a positive finite number, got ${bezierArcs}`);
|
|
637
|
+
}
|
|
638
|
+
if (ctm !== null && !(ctm instanceof Matrix)) {
|
|
639
|
+
throw new Error('shapeToPolygon: ctm must be null or a Matrix instance');
|
|
640
|
+
}
|
|
548
641
|
let pathData;
|
|
549
642
|
switch (element.type) {
|
|
550
643
|
case "circle":
|
|
@@ -687,7 +780,19 @@ export function resolveClipPath(
|
|
|
687
780
|
ctm = null,
|
|
688
781
|
options = {},
|
|
689
782
|
) {
|
|
783
|
+
if (!clipPathDef || typeof clipPathDef !== 'object') {
|
|
784
|
+
throw new Error('resolveClipPath: clipPathDef must be an object');
|
|
785
|
+
}
|
|
786
|
+
if (ctm !== null && !(ctm instanceof Matrix)) {
|
|
787
|
+
throw new Error('resolveClipPath: ctm must be null or a Matrix instance');
|
|
788
|
+
}
|
|
789
|
+
if (typeof options !== 'object' || options === null) {
|
|
790
|
+
throw new Error('resolveClipPath: options must be an object');
|
|
791
|
+
}
|
|
690
792
|
const { samples = DEFAULT_CURVE_SAMPLES } = options;
|
|
793
|
+
if (typeof samples !== 'number' || samples <= 0 || !Number.isFinite(samples)) {
|
|
794
|
+
throw new Error(`resolveClipPath: samples must be a positive finite number, got ${samples}`);
|
|
795
|
+
}
|
|
691
796
|
const clipPathUnits = clipPathDef.clipPathUnits || "userSpaceOnUse";
|
|
692
797
|
let clipTransform = ctm ? ctm.clone() : Matrix.identity(3);
|
|
693
798
|
|
|
@@ -746,6 +851,18 @@ export function resolveClipPath(
|
|
|
746
851
|
* @returns {Array} Clipped polygon(s), or array of polygon arrays for multi-region results
|
|
747
852
|
*/
|
|
748
853
|
function clipPolygonWithRule(elementPolygon, clipPolygon, clipRule) {
|
|
854
|
+
if (!Array.isArray(elementPolygon)) {
|
|
855
|
+
throw new Error('clipPolygonWithRule: elementPolygon must be an array');
|
|
856
|
+
}
|
|
857
|
+
if (!Array.isArray(clipPolygon)) {
|
|
858
|
+
throw new Error('clipPolygonWithRule: clipPolygon must be an array');
|
|
859
|
+
}
|
|
860
|
+
if (clipRule !== 'nonzero' && clipRule !== 'evenodd') {
|
|
861
|
+
throw new Error(`clipPolygonWithRule: clipRule must be 'nonzero' or 'evenodd', got ${clipRule}`);
|
|
862
|
+
}
|
|
863
|
+
if (elementPolygon.length < 3 || clipPolygon.length < 3) {
|
|
864
|
+
return [];
|
|
865
|
+
}
|
|
749
866
|
// For nonzero rule, standard intersection works correctly
|
|
750
867
|
// because polygonIntersection uses the winding number test internally
|
|
751
868
|
if (clipRule === "nonzero") {
|
|
@@ -789,6 +906,12 @@ function clipPolygonWithRule(elementPolygon, clipPolygon, clipRule) {
|
|
|
789
906
|
* @private
|
|
790
907
|
*/
|
|
791
908
|
function computeCentroid(polygon) {
|
|
909
|
+
if (!Array.isArray(polygon)) {
|
|
910
|
+
throw new Error('computeCentroid: polygon must be an array');
|
|
911
|
+
}
|
|
912
|
+
if (polygon.length === 0) {
|
|
913
|
+
throw new Error('computeCentroid: polygon must not be empty');
|
|
914
|
+
}
|
|
792
915
|
let cx = new Decimal(0);
|
|
793
916
|
let cy = new Decimal(0);
|
|
794
917
|
let area = new Decimal(0);
|
|
@@ -874,7 +997,25 @@ function computeCentroid(polygon) {
|
|
|
874
997
|
* const clipped = applyClipPath(ellipse, clipDef, null, { samples: 50 });
|
|
875
998
|
*/
|
|
876
999
|
export function applyClipPath(element, clipPathDef, ctm = null, options = {}) {
|
|
1000
|
+
if (!element || typeof element !== 'object') {
|
|
1001
|
+
throw new Error('applyClipPath: element must be an object');
|
|
1002
|
+
}
|
|
1003
|
+
if (!clipPathDef || typeof clipPathDef !== 'object') {
|
|
1004
|
+
throw new Error('applyClipPath: clipPathDef must be an object');
|
|
1005
|
+
}
|
|
1006
|
+
if (ctm !== null && !(ctm instanceof Matrix)) {
|
|
1007
|
+
throw new Error('applyClipPath: ctm must be null or a Matrix instance');
|
|
1008
|
+
}
|
|
1009
|
+
if (typeof options !== 'object' || options === null) {
|
|
1010
|
+
throw new Error('applyClipPath: options must be an object');
|
|
1011
|
+
}
|
|
877
1012
|
const { samples = DEFAULT_CURVE_SAMPLES, clipRule = "nonzero" } = options;
|
|
1013
|
+
if (typeof samples !== 'number' || samples <= 0 || !Number.isFinite(samples)) {
|
|
1014
|
+
throw new Error(`applyClipPath: samples must be a positive finite number, got ${samples}`);
|
|
1015
|
+
}
|
|
1016
|
+
if (clipRule !== 'nonzero' && clipRule !== 'evenodd') {
|
|
1017
|
+
throw new Error(`applyClipPath: clipRule must be 'nonzero' or 'evenodd', got ${clipRule}`);
|
|
1018
|
+
}
|
|
878
1019
|
const clipPolygon = resolveClipPath(clipPathDef, element, ctm, options);
|
|
879
1020
|
if (clipPolygon.length < 3) return [];
|
|
880
1021
|
|
|
@@ -910,6 +1051,12 @@ export function applyClipPath(element, clipPathDef, ctm = null, options = {}) {
|
|
|
910
1051
|
* // Returns: {x: Decimal(25), y: Decimal(25), width: Decimal(50), height: Decimal(50)}
|
|
911
1052
|
*/
|
|
912
1053
|
function getElementBoundingBox(element) {
|
|
1054
|
+
if (!element || typeof element !== 'object') {
|
|
1055
|
+
throw new Error('getElementBoundingBox: element must be an object');
|
|
1056
|
+
}
|
|
1057
|
+
if (!element.type || typeof element.type !== 'string') {
|
|
1058
|
+
throw new Error('getElementBoundingBox: element.type must be a string');
|
|
1059
|
+
}
|
|
913
1060
|
switch (element.type) {
|
|
914
1061
|
case "rect":
|
|
915
1062
|
return {
|
|
@@ -988,6 +1135,12 @@ function getElementBoundingBox(element) {
|
|
|
988
1135
|
* // Returns: "M 0.12345679 0.98765432 Z"
|
|
989
1136
|
*/
|
|
990
1137
|
export function polygonToPathData(polygon, precision = 6) {
|
|
1138
|
+
if (!Array.isArray(polygon)) {
|
|
1139
|
+
throw new Error('polygonToPathData: polygon must be an array');
|
|
1140
|
+
}
|
|
1141
|
+
if (typeof precision !== 'number' || precision < 0 || !Number.isFinite(precision)) {
|
|
1142
|
+
throw new Error(`polygonToPathData: precision must be a non-negative finite number, got ${precision}`);
|
|
1143
|
+
}
|
|
991
1144
|
if (polygon.length < 2) return "";
|
|
992
1145
|
const fmt = (n) =>
|
|
993
1146
|
(n instanceof Decimal ? n : D(n)).toFixed(precision).replace(/\.?0+$/, "");
|
|
@@ -1054,6 +1207,21 @@ export function resolveNestedClipPath(
|
|
|
1054
1207
|
visited = new Set(),
|
|
1055
1208
|
options = {},
|
|
1056
1209
|
) {
|
|
1210
|
+
if (!clipPathDef || typeof clipPathDef !== 'object') {
|
|
1211
|
+
throw new Error('resolveNestedClipPath: clipPathDef must be an object');
|
|
1212
|
+
}
|
|
1213
|
+
if (!(defsMap instanceof Map)) {
|
|
1214
|
+
throw new Error('resolveNestedClipPath: defsMap must be a Map');
|
|
1215
|
+
}
|
|
1216
|
+
if (ctm !== null && !(ctm instanceof Matrix)) {
|
|
1217
|
+
throw new Error('resolveNestedClipPath: ctm must be null or a Matrix instance');
|
|
1218
|
+
}
|
|
1219
|
+
if (!(visited instanceof Set)) {
|
|
1220
|
+
throw new Error('resolveNestedClipPath: visited must be a Set');
|
|
1221
|
+
}
|
|
1222
|
+
if (typeof options !== 'object' || options === null) {
|
|
1223
|
+
throw new Error('resolveNestedClipPath: options must be an object');
|
|
1224
|
+
}
|
|
1057
1225
|
const clipId = clipPathDef.id;
|
|
1058
1226
|
if (clipId && visited.has(clipId)) {
|
|
1059
1227
|
Logger.warn(`Circular clipPath reference detected: ${clipId}`);
|