@emasoft/svg-matrix 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/bin/svg-matrix.js +7 -6
  2. package/bin/svgm.js +109 -40
  3. package/dist/svg-matrix.min.js +7 -7
  4. package/dist/svg-toolbox.min.js +148 -228
  5. package/dist/svgm.min.js +152 -232
  6. package/dist/version.json +5 -5
  7. package/package.json +1 -1
  8. package/scripts/postinstall.js +72 -41
  9. package/scripts/test-postinstall.js +18 -16
  10. package/scripts/version-sync.js +78 -60
  11. package/src/animation-optimization.js +190 -98
  12. package/src/animation-references.js +11 -3
  13. package/src/arc-length.js +23 -20
  14. package/src/bezier-analysis.js +9 -13
  15. package/src/bezier-intersections.js +18 -4
  16. package/src/browser-verify.js +35 -8
  17. package/src/clip-path-resolver.js +285 -114
  18. package/src/convert-path-data.js +20 -8
  19. package/src/css-specificity.js +33 -9
  20. package/src/douglas-peucker.js +272 -141
  21. package/src/geometry-to-path.js +79 -22
  22. package/src/gjk-collision.js +287 -126
  23. package/src/index.js +56 -21
  24. package/src/inkscape-support.js +122 -101
  25. package/src/logger.js +43 -27
  26. package/src/marker-resolver.js +201 -121
  27. package/src/mask-resolver.js +231 -98
  28. package/src/matrix.js +9 -5
  29. package/src/mesh-gradient.js +22 -14
  30. package/src/off-canvas-detection.js +53 -17
  31. package/src/path-optimization.js +356 -171
  32. package/src/path-simplification.js +671 -256
  33. package/src/pattern-resolver.js +1 -3
  34. package/src/polygon-clip.js +396 -78
  35. package/src/svg-boolean-ops.js +90 -23
  36. package/src/svg-collections.js +1546 -667
  37. package/src/svg-flatten.js +152 -38
  38. package/src/svg-matrix-lib.js +2 -2
  39. package/src/svg-parser.js +5 -1
  40. package/src/svg-rendering-context.js +3 -1
  41. package/src/svg-toolbox-lib.js +2 -2
  42. package/src/svg-toolbox.js +99 -457
  43. package/src/svg-validation-data.js +513 -345
  44. package/src/svg2-polyfills.js +156 -93
  45. package/src/svgm-lib.js +8 -4
  46. package/src/transform-optimization.js +168 -51
  47. package/src/transforms2d.js +73 -40
  48. package/src/transforms3d.js +34 -27
  49. package/src/use-symbol-resolver.js +175 -76
  50. package/src/vector.js +80 -44
  51. package/src/vendor/inkscape-hatch-polyfill.js +143 -108
  52. package/src/vendor/inkscape-hatch-polyfill.min.js +291 -1
  53. package/src/vendor/inkscape-mesh-polyfill.js +953 -766
  54. package/src/vendor/inkscape-mesh-polyfill.min.js +896 -1
  55. package/src/verification.js +3 -4
@@ -30,7 +30,11 @@ function validateNumeric(value, name) {
30
30
  if (value === undefined || value === null) {
31
31
  throw new Error(`${name} is required`);
32
32
  }
33
- if (typeof value !== 'number' && typeof value !== 'string' && !(value instanceof Decimal)) {
33
+ if (
34
+ typeof value !== "number" &&
35
+ typeof value !== "string" &&
36
+ !(value instanceof Decimal)
37
+ ) {
34
38
  throw new Error(`${name} must be a number, string, or Decimal`);
35
39
  }
36
40
  }
@@ -52,7 +56,8 @@ function normalizeAngle(theta) {
52
56
  }
53
57
 
54
58
  // For small angles, no normalization needed - preserve full precision
55
- if (Math.abs(tNum) <= 6.283185307179586) { // 2π
59
+ if (Math.abs(tNum) <= 6.283185307179586) {
60
+ // 2π
56
61
  return tNum;
57
62
  }
58
63
 
@@ -148,9 +153,9 @@ function normalizeAngle(theta) {
148
153
  * // First translates by (10,0,0), then rotates around Z
149
154
  */
150
155
  export function translation(tx, ty, tz) {
151
- validateNumeric(tx, 'tx');
152
- validateNumeric(ty, 'ty');
153
- validateNumeric(tz, 'tz');
156
+ validateNumeric(tx, "tx");
157
+ validateNumeric(ty, "ty");
158
+ validateNumeric(tz, "tz");
154
159
  return Matrix.from([
155
160
  [new Decimal(1), new Decimal(0), new Decimal(0), D(tx)],
156
161
  [new Decimal(0), new Decimal(1), new Decimal(0), D(ty)],
@@ -209,12 +214,12 @@ export function translation(tx, ty, tz) {
209
214
  * // Cannot invert this matrix - information about z coordinate is lost
210
215
  */
211
216
  export function scale(sx, sy = null, sz = null) {
212
- validateNumeric(sx, 'sx');
217
+ validateNumeric(sx, "sx");
213
218
  if (sy !== null) {
214
- validateNumeric(sy, 'sy');
219
+ validateNumeric(sy, "sy");
215
220
  }
216
221
  if (sz !== null) {
217
- validateNumeric(sz, 'sz');
222
+ validateNumeric(sz, "sz");
218
223
  }
219
224
  const syValue = sy === null ? sx : sy;
220
225
  const szValue = sz === null ? sx : sz;
@@ -270,7 +275,7 @@ export function scale(sx, sy = null, sz = null) {
270
275
  * const pitch = rotateX(-0.1); // Slight downward tilt
271
276
  */
272
277
  export function rotateX(theta) {
273
- validateNumeric(theta, 'theta');
278
+ validateNumeric(theta, "theta");
274
279
  const t = D(theta);
275
280
  // Normalize angle to reduce precision loss for very large angles
276
281
  const tNum = normalizeAngle(t);
@@ -317,7 +322,7 @@ export function rotateX(theta) {
317
322
  * const yaw = rotateY(0.5); // Turn right by ~28.6°
318
323
  */
319
324
  export function rotateY(theta) {
320
- validateNumeric(theta, 'theta');
325
+ validateNumeric(theta, "theta");
321
326
  const t = D(theta);
322
327
  // Normalize angle to reduce precision loss for very large angles
323
328
  const tNum = normalizeAngle(t);
@@ -365,7 +370,7 @@ export function rotateY(theta) {
365
370
  * const roll = rotateZ(0.2); // Slight clockwise tilt from viewer perspective
366
371
  */
367
372
  export function rotateZ(theta) {
368
- validateNumeric(theta, 'theta');
373
+ validateNumeric(theta, "theta");
369
374
  const t = D(theta);
370
375
  // Normalize angle to reduce precision loss for very large angles
371
376
  const tNum = normalizeAngle(t);
@@ -434,10 +439,10 @@ export function rotateZ(theta) {
434
439
  * // Equivalent to rotateX(Math.PI / 2)
435
440
  */
436
441
  export function rotateAroundAxis(ux, uy, uz, theta) {
437
- validateNumeric(ux, 'ux');
438
- validateNumeric(uy, 'uy');
439
- validateNumeric(uz, 'uz');
440
- validateNumeric(theta, 'theta');
442
+ validateNumeric(ux, "ux");
443
+ validateNumeric(uy, "uy");
444
+ validateNumeric(uz, "uz");
445
+ validateNumeric(theta, "theta");
441
446
  const u = [D(ux), D(uy), D(uz)];
442
447
  const norm = u[0].mul(u[0]).plus(u[1].mul(u[1])).plus(u[2].mul(u[2])).sqrt();
443
448
  if (norm.isZero()) {
@@ -520,13 +525,13 @@ export function rotateAroundAxis(ux, uy, uz, theta) {
520
525
  * // Complex rotation around axis (1,1,1) passing through (10,20,30)
521
526
  */
522
527
  export function rotateAroundPoint(ux, uy, uz, theta, px, py, pz) {
523
- validateNumeric(ux, 'ux');
524
- validateNumeric(uy, 'uy');
525
- validateNumeric(uz, 'uz');
526
- validateNumeric(theta, 'theta');
527
- validateNumeric(px, 'px');
528
- validateNumeric(py, 'py');
529
- validateNumeric(pz, 'pz');
528
+ validateNumeric(ux, "ux");
529
+ validateNumeric(uy, "uy");
530
+ validateNumeric(uz, "uz");
531
+ validateNumeric(theta, "theta");
532
+ validateNumeric(px, "px");
533
+ validateNumeric(py, "py");
534
+ validateNumeric(pz, "pz");
530
535
  const pxD = D(px),
531
536
  pyD = D(py),
532
537
  pzD = D(pz);
@@ -582,14 +587,14 @@ export function rotateAroundPoint(ux, uy, uz, theta, px, py, pz) {
582
587
  */
583
588
  export function applyTransform(M, x, y, z) {
584
589
  if (!(M instanceof Matrix)) {
585
- throw new Error('M must be a Matrix instance');
590
+ throw new Error("M must be a Matrix instance");
586
591
  }
587
592
  if (M.rows !== 4 || M.cols !== 4) {
588
593
  throw new Error(`M must be a 4x4 matrix, got ${M.rows}x${M.cols}`);
589
594
  }
590
- validateNumeric(x, 'x');
591
- validateNumeric(y, 'y');
592
- validateNumeric(z, 'z');
595
+ validateNumeric(x, "x");
596
+ validateNumeric(y, "y");
597
+ validateNumeric(z, "z");
593
598
 
594
599
  const P = Matrix.from([[D(x)], [D(y)], [D(z)], [new Decimal(1)]]);
595
600
  const R = M.mul(P);
@@ -599,7 +604,9 @@ export function applyTransform(M, x, y, z) {
599
604
  rw = R.data[3][0];
600
605
 
601
606
  if (rw.isZero()) {
602
- throw new Error('Perspective division by zero: transformation results in point at infinity');
607
+ throw new Error(
608
+ "Perspective division by zero: transformation results in point at infinity",
609
+ );
603
610
  }
604
611
 
605
612
  return [rx.div(rw), ry.div(rw), rz.div(rw)];
@@ -36,16 +36,18 @@ Decimal.set({ precision: 80 });
36
36
  */
37
37
  function hasCircularReference(startId, getNextId, maxDepth = 100) {
38
38
  // Parameter validation: startId must be a non-empty string
39
- if (!startId || typeof startId !== 'string') {
40
- throw new Error('hasCircularReference: startId must be a non-empty string');
39
+ if (!startId || typeof startId !== "string") {
40
+ throw new Error("hasCircularReference: startId must be a non-empty string");
41
41
  }
42
42
  // Parameter validation: getNextId must be a function
43
- if (typeof getNextId !== 'function') {
44
- throw new Error('hasCircularReference: getNextId must be a function');
43
+ if (typeof getNextId !== "function") {
44
+ throw new Error("hasCircularReference: getNextId must be a function");
45
45
  }
46
46
  // Parameter validation: maxDepth must be a positive finite number
47
- if (typeof maxDepth !== 'number' || maxDepth <= 0 || !isFinite(maxDepth)) {
48
- throw new Error('hasCircularReference: maxDepth must be a positive finite number');
47
+ if (typeof maxDepth !== "number" || maxDepth <= 0 || !isFinite(maxDepth)) {
48
+ throw new Error(
49
+ "hasCircularReference: maxDepth must be a positive finite number",
50
+ );
49
51
  }
50
52
 
51
53
  const visited = new Set();
@@ -100,7 +102,7 @@ function hasCircularReference(startId, getNextId, maxDepth = 100) {
100
102
  */
101
103
  export function parseUseElement(useElement) {
102
104
  // Parameter validation: useElement must be defined
103
- if (!useElement) throw new Error('parseUseElement: useElement is required');
105
+ if (!useElement) throw new Error("parseUseElement: useElement is required");
104
106
 
105
107
  const href =
106
108
  useElement.getAttribute("href") ||
@@ -111,7 +113,9 @@ export function parseUseElement(useElement) {
111
113
 
112
114
  // Validate href is not empty after parsing
113
115
  if (!parsedHref) {
114
- throw new Error('parseUseElement: href attribute must reference a valid element id (found empty reference)');
116
+ throw new Error(
117
+ "parseUseElement: href attribute must reference a valid element id (found empty reference)",
118
+ );
115
119
  }
116
120
 
117
121
  // Parse numeric attributes and validate for NaN and finiteness
@@ -120,7 +124,9 @@ export function parseUseElement(useElement) {
120
124
 
121
125
  // Validate that x and y are valid finite numbers
122
126
  if (isNaN(x) || isNaN(y) || !isFinite(x) || !isFinite(y)) {
123
- throw new Error('parseUseElement: x and y attributes must be valid finite numbers');
127
+ throw new Error(
128
+ "parseUseElement: x and y attributes must be valid finite numbers",
129
+ );
124
130
  }
125
131
 
126
132
  // Parse width and height if present, validate for NaN and finiteness and positivity
@@ -130,14 +136,18 @@ export function parseUseElement(useElement) {
130
136
  if (useElement.getAttribute("width")) {
131
137
  width = parseFloat(useElement.getAttribute("width"));
132
138
  if (isNaN(width) || !isFinite(width) || width <= 0) {
133
- throw new Error('parseUseElement: width attribute must be a valid positive finite number');
139
+ throw new Error(
140
+ "parseUseElement: width attribute must be a valid positive finite number",
141
+ );
134
142
  }
135
143
  }
136
144
 
137
145
  if (useElement.getAttribute("height")) {
138
146
  height = parseFloat(useElement.getAttribute("height"));
139
147
  if (isNaN(height) || !isFinite(height) || height <= 0) {
140
- throw new Error('parseUseElement: height attribute must be a valid positive finite number');
148
+ throw new Error(
149
+ "parseUseElement: height attribute must be a valid positive finite number",
150
+ );
141
151
  }
142
152
  }
143
153
 
@@ -193,14 +203,17 @@ export function parseUseElement(useElement) {
193
203
  */
194
204
  export function parseSymbolElement(symbolElement) {
195
205
  // Parameter validation: symbolElement must be defined
196
- if (!symbolElement) throw new Error('parseSymbolElement: symbolElement is required');
206
+ if (!symbolElement)
207
+ throw new Error("parseSymbolElement: symbolElement is required");
197
208
 
198
209
  // Parse refX and refY with NaN and finiteness validation
199
210
  const refX = parseFloat(symbolElement.getAttribute("refX") || "0");
200
211
  const refY = parseFloat(symbolElement.getAttribute("refY") || "0");
201
212
 
202
213
  if (isNaN(refX) || isNaN(refY) || !isFinite(refX) || !isFinite(refY)) {
203
- throw new Error('parseSymbolElement: refX and refY must be valid finite numbers');
214
+ throw new Error(
215
+ "parseSymbolElement: refX and refY must be valid finite numbers",
216
+ );
204
217
  }
205
218
 
206
219
  const data = {
@@ -302,7 +315,7 @@ export function parseSymbolElement(symbolElement) {
302
315
  export function parseChildElement(element) {
303
316
  // Parameter validation: element must be defined and have a tagName
304
317
  if (!element || !element.tagName) {
305
- throw new Error('parseChildElement: element with tagName is required');
318
+ throw new Error("parseChildElement: element with tagName is required");
306
319
  }
307
320
 
308
321
  const tagName = element.tagName.toLowerCase();
@@ -318,7 +331,9 @@ export function parseChildElement(element) {
318
331
  const safeParseFloat = (attrName, defaultValue = "0") => {
319
332
  const value = parseFloat(element.getAttribute(attrName) || defaultValue);
320
333
  if (isNaN(value) || !isFinite(value)) {
321
- throw new Error(`parseChildElement: ${attrName} must be a valid finite number in ${tagName} element`);
334
+ throw new Error(
335
+ `parseChildElement: ${attrName} must be a valid finite number in ${tagName} element`,
336
+ );
322
337
  }
323
338
  return value;
324
339
  };
@@ -428,7 +443,7 @@ export function parseChildElement(element) {
428
443
  export function extractStyleAttributes(element) {
429
444
  // Parameter validation: element must be defined
430
445
  if (!element) {
431
- throw new Error('extractStyleAttributes: element is required');
446
+ throw new Error("extractStyleAttributes: element is required");
432
447
  }
433
448
 
434
449
  return {
@@ -502,7 +517,12 @@ export function calculateViewBoxTransform(
502
517
  }
503
518
 
504
519
  // Validate targetWidth and targetHeight are finite positive numbers
505
- if (!isFinite(targetWidth) || !isFinite(targetHeight) || targetWidth <= 0 || targetHeight <= 0) {
520
+ if (
521
+ !isFinite(targetWidth) ||
522
+ !isFinite(targetHeight) ||
523
+ targetWidth <= 0 ||
524
+ targetHeight <= 0
525
+ ) {
506
526
  return Matrix.identity(3);
507
527
  }
508
528
 
@@ -512,7 +532,14 @@ export function calculateViewBoxTransform(
512
532
  const vbY = viewBox.y;
513
533
 
514
534
  // Validate viewBox dimensions are finite and positive
515
- if (!isFinite(vbW) || !isFinite(vbH) || !isFinite(vbX) || !isFinite(vbY) || vbW <= 0 || vbH <= 0) {
535
+ if (
536
+ !isFinite(vbW) ||
537
+ !isFinite(vbH) ||
538
+ !isFinite(vbX) ||
539
+ !isFinite(vbY) ||
540
+ vbW <= 0 ||
541
+ vbH <= 0
542
+ ) {
516
543
  return Matrix.identity(3);
517
544
  }
518
545
 
@@ -639,63 +666,99 @@ export function calculateViewBoxTransform(
639
666
  export function resolveUse(useData, defs, options = {}) {
640
667
  // Parameter validation: useData must be defined with href property
641
668
  if (!useData || !useData.href) {
642
- throw new Error('resolveUse: useData with href property is required');
669
+ throw new Error("resolveUse: useData with href property is required");
643
670
  }
644
671
 
645
672
  // Parameter validation: defs must be defined
646
673
  if (!defs) {
647
- throw new Error('resolveUse: defs map is required');
674
+ throw new Error("resolveUse: defs map is required");
648
675
  }
649
676
 
650
677
  // Validate useData.x and useData.y are valid numbers
651
- if (typeof useData.x !== 'number' || isNaN(useData.x) || !isFinite(useData.x)) {
652
- throw new Error('resolveUse: useData.x must be a valid finite number');
678
+ if (
679
+ typeof useData.x !== "number" ||
680
+ isNaN(useData.x) ||
681
+ !isFinite(useData.x)
682
+ ) {
683
+ throw new Error("resolveUse: useData.x must be a valid finite number");
653
684
  }
654
- if (typeof useData.y !== 'number' || isNaN(useData.y) || !isFinite(useData.y)) {
655
- throw new Error('resolveUse: useData.y must be a valid finite number');
685
+ if (
686
+ typeof useData.y !== "number" ||
687
+ isNaN(useData.y) ||
688
+ !isFinite(useData.y)
689
+ ) {
690
+ throw new Error("resolveUse: useData.y must be a valid finite number");
656
691
  }
657
692
 
658
693
  // Validate useData.width and useData.height are null or valid numbers
659
- if (useData.width !== null && (typeof useData.width !== 'number' || isNaN(useData.width) || !isFinite(useData.width) || useData.width <= 0)) {
660
- throw new Error('resolveUse: useData.width must be null or a positive finite number');
694
+ if (
695
+ useData.width !== null &&
696
+ (typeof useData.width !== "number" ||
697
+ isNaN(useData.width) ||
698
+ !isFinite(useData.width) ||
699
+ useData.width <= 0)
700
+ ) {
701
+ throw new Error(
702
+ "resolveUse: useData.width must be null or a positive finite number",
703
+ );
661
704
  }
662
- if (useData.height !== null && (typeof useData.height !== 'number' || isNaN(useData.height) || !isFinite(useData.height) || useData.height <= 0)) {
663
- throw new Error('resolveUse: useData.height must be null or a positive finite number');
705
+ if (
706
+ useData.height !== null &&
707
+ (typeof useData.height !== "number" ||
708
+ isNaN(useData.height) ||
709
+ !isFinite(useData.height) ||
710
+ useData.height <= 0)
711
+ ) {
712
+ throw new Error(
713
+ "resolveUse: useData.height must be null or a positive finite number",
714
+ );
664
715
  }
665
716
 
666
717
  // Validate useData.style is an object or null/undefined
667
718
  if (useData.style !== null && useData.style !== undefined) {
668
- if (typeof useData.style !== 'object' || Array.isArray(useData.style)) {
669
- throw new Error('resolveUse: useData.style must be null, undefined, or a valid non-array object');
719
+ if (typeof useData.style !== "object" || Array.isArray(useData.style)) {
720
+ throw new Error(
721
+ "resolveUse: useData.style must be null, undefined, or a valid non-array object",
722
+ );
670
723
  }
671
724
  }
672
725
 
673
726
  // Validate useData.transform is a string or null/undefined
674
- if (useData.transform !== null && useData.transform !== undefined && typeof useData.transform !== 'string') {
675
- throw new Error('resolveUse: useData.transform must be null, undefined, or a string');
727
+ if (
728
+ useData.transform !== null &&
729
+ useData.transform !== undefined &&
730
+ typeof useData.transform !== "string"
731
+ ) {
732
+ throw new Error(
733
+ "resolveUse: useData.transform must be null, undefined, or a string",
734
+ );
676
735
  }
677
736
 
678
737
  // Validate options parameter
679
- if (options && typeof options !== 'object') {
680
- throw new Error('resolveUse: options must be an object or undefined');
738
+ if (options && typeof options !== "object") {
739
+ throw new Error("resolveUse: options must be an object or undefined");
681
740
  }
682
741
 
683
742
  const { maxDepth = 10, _visited = new Set() } = options;
684
743
 
685
744
  // Validate maxDepth is a positive finite number
686
- if (typeof maxDepth !== 'number' || maxDepth <= 0 || !isFinite(maxDepth)) {
687
- throw new Error('resolveUse: maxDepth must be a positive finite number');
745
+ if (typeof maxDepth !== "number" || maxDepth <= 0 || !isFinite(maxDepth)) {
746
+ throw new Error("resolveUse: maxDepth must be a positive finite number");
688
747
  }
689
748
 
690
749
  // Detect circular references by tracking visited IDs
691
750
  if (_visited.has(useData.href)) {
692
- console.warn(`resolveUse: circular reference detected for '#${useData.href}', skipping to prevent infinite loop`);
751
+ console.warn(
752
+ `resolveUse: circular reference detected for '#${useData.href}', skipping to prevent infinite loop`,
753
+ );
693
754
  return null;
694
755
  }
695
756
 
696
757
  const target = defs[useData.href];
697
758
  if (!target) {
698
- console.warn(`resolveUse: target element '#${useData.href}' not found in defs map`);
759
+ console.warn(
760
+ `resolveUse: target element '#${useData.href}' not found in defs map`,
761
+ );
699
762
  return null; // Target element not found
700
763
  }
701
764
 
@@ -726,17 +789,28 @@ export function resolveUse(useData, defs, options = {}) {
726
789
  if (target.type === "symbol" && target.viewBoxParsed) {
727
790
  // Validate viewBoxParsed has all required properties with finite values (x, y, width, height)
728
791
  // and width/height must be positive
729
- if (typeof target.viewBoxParsed.x !== 'number' || typeof target.viewBoxParsed.y !== 'number' ||
730
- typeof target.viewBoxParsed.width !== 'number' || typeof target.viewBoxParsed.height !== 'number' ||
731
- !isFinite(target.viewBoxParsed.x) || !isFinite(target.viewBoxParsed.y) ||
732
- !isFinite(target.viewBoxParsed.width) || !isFinite(target.viewBoxParsed.height) ||
733
- target.viewBoxParsed.width <= 0 || target.viewBoxParsed.height <= 0) {
734
- throw new Error('resolveUse: target.viewBoxParsed must have finite x, y and positive finite width, height');
792
+ if (
793
+ typeof target.viewBoxParsed.x !== "number" ||
794
+ typeof target.viewBoxParsed.y !== "number" ||
795
+ typeof target.viewBoxParsed.width !== "number" ||
796
+ typeof target.viewBoxParsed.height !== "number" ||
797
+ !isFinite(target.viewBoxParsed.x) ||
798
+ !isFinite(target.viewBoxParsed.y) ||
799
+ !isFinite(target.viewBoxParsed.width) ||
800
+ !isFinite(target.viewBoxParsed.height) ||
801
+ target.viewBoxParsed.width <= 0 ||
802
+ target.viewBoxParsed.height <= 0
803
+ ) {
804
+ throw new Error(
805
+ "resolveUse: target.viewBoxParsed must have finite x, y and positive finite width, height",
806
+ );
735
807
  }
736
808
 
737
809
  // Use explicit null check to allow width/height of 0 (though unusual)
738
- const width = useData.width !== null ? useData.width : target.viewBoxParsed.width;
739
- const height = useData.height !== null ? useData.height : target.viewBoxParsed.height;
810
+ const width =
811
+ useData.width !== null ? useData.width : target.viewBoxParsed.width;
812
+ const height =
813
+ useData.height !== null ? useData.height : target.viewBoxParsed.height;
740
814
 
741
815
  const viewBoxTransform = calculateViewBoxTransform(
742
816
  target.viewBoxParsed,
@@ -753,19 +827,27 @@ export function resolveUse(useData, defs, options = {}) {
753
827
  const resolvedChildren = [];
754
828
  // For symbols and groups, use children array; for leaf elements, wrap as single child
755
829
  // Check for children property existence and if it's an array, not just truthiness
756
- const children = (target.children && Array.isArray(target.children) && target.children.length > 0)
757
- ? target.children
758
- : [target];
830
+ const children =
831
+ target.children &&
832
+ Array.isArray(target.children) &&
833
+ target.children.length > 0
834
+ ? target.children
835
+ : [target];
759
836
 
760
837
  for (const child of children) {
761
838
  if (child.type === "use") {
762
839
  // Check maxDepth before recursing to prevent infinite recursion
763
840
  if (maxDepth <= 1) {
764
- console.warn(`resolveUse: maximum nesting depth reached for use element '#${child.href}', skipping nested resolution`);
841
+ console.warn(
842
+ `resolveUse: maximum nesting depth reached for use element '#${child.href}', skipping nested resolution`,
843
+ );
765
844
  continue;
766
845
  }
767
846
  // Recursive resolution with passed _visited set to track circular references across the chain
768
- const resolved = resolveUse(child, defs, { maxDepth: maxDepth - 1, _visited });
847
+ const resolved = resolveUse(child, defs, {
848
+ maxDepth: maxDepth - 1,
849
+ _visited,
850
+ });
769
851
  if (resolved) {
770
852
  resolvedChildren.push(resolved);
771
853
  }
@@ -848,8 +930,10 @@ export function flattenResolvedUse(resolved, samples = 20) {
848
930
  if (!resolved) return results;
849
931
 
850
932
  // Parameter validation: samples must be a positive number
851
- if (typeof samples !== 'number' || samples <= 0 || !isFinite(samples)) {
852
- throw new Error('flattenResolvedUse: samples must be a positive finite number');
933
+ if (typeof samples !== "number" || samples <= 0 || !isFinite(samples)) {
934
+ throw new Error(
935
+ "flattenResolvedUse: samples must be a positive finite number",
936
+ );
853
937
  }
854
938
 
855
939
  // Validate required properties exist
@@ -932,12 +1016,14 @@ export function flattenResolvedUse(resolved, samples = 20) {
932
1016
  export function elementToPolygon(element, transform, samples = 20) {
933
1017
  // Parameter validation: element must be defined
934
1018
  if (!element) {
935
- throw new Error('elementToPolygon: element is required');
1019
+ throw new Error("elementToPolygon: element is required");
936
1020
  }
937
1021
 
938
1022
  // Parameter validation: samples must be a positive finite number
939
- if (typeof samples !== 'number' || samples <= 0 || !isFinite(samples)) {
940
- throw new Error('elementToPolygon: samples must be a positive finite number');
1023
+ if (typeof samples !== "number" || samples <= 0 || !isFinite(samples)) {
1024
+ throw new Error(
1025
+ "elementToPolygon: samples must be a positive finite number",
1026
+ );
941
1027
  }
942
1028
 
943
1029
  // Use ClipPathResolver's shapeToPolygon
@@ -1000,8 +1086,8 @@ export function elementToPolygon(element, transform, samples = 20) {
1000
1086
  */
1001
1087
  export function mergeStyles(inherited, element) {
1002
1088
  // Parameter validation: element must be defined (inherited can be null)
1003
- if (!element || typeof element !== 'object' || Array.isArray(element)) {
1004
- throw new Error('mergeStyles: element must be a valid non-array object');
1089
+ if (!element || typeof element !== "object" || Array.isArray(element)) {
1090
+ throw new Error("mergeStyles: element must be a valid non-array object");
1005
1091
  }
1006
1092
 
1007
1093
  const result = { ...element };
@@ -1009,8 +1095,10 @@ export function mergeStyles(inherited, element) {
1009
1095
  // Inherited can be null/undefined, handle gracefully
1010
1096
  // Also validate inherited is an object if not null/undefined
1011
1097
  if (inherited !== null && inherited !== undefined) {
1012
- if (typeof inherited !== 'object' || Array.isArray(inherited)) {
1013
- throw new Error('mergeStyles: inherited must be null, undefined, or a valid non-array object');
1098
+ if (typeof inherited !== "object" || Array.isArray(inherited)) {
1099
+ throw new Error(
1100
+ "mergeStyles: inherited must be null, undefined, or a valid non-array object",
1101
+ );
1014
1102
  }
1015
1103
  }
1016
1104
 
@@ -1069,8 +1157,10 @@ export function mergeStyles(inherited, element) {
1069
1157
  */
1070
1158
  export function getResolvedBBox(resolved, samples = 20) {
1071
1159
  // Parameter validation: samples must be a positive finite number
1072
- if (typeof samples !== 'number' || samples <= 0 || !isFinite(samples)) {
1073
- throw new Error('getResolvedBBox: samples must be a positive finite number');
1160
+ if (typeof samples !== "number" || samples <= 0 || !isFinite(samples)) {
1161
+ throw new Error(
1162
+ "getResolvedBBox: samples must be a positive finite number",
1163
+ );
1074
1164
  }
1075
1165
 
1076
1166
  const polygons = flattenResolvedUse(resolved, samples);
@@ -1160,17 +1250,21 @@ export function getResolvedBBox(resolved, samples = 20) {
1160
1250
  export function clipResolvedUse(resolved, clipPolygon, samples = 20) {
1161
1251
  // Parameter validation: clipPolygon must be defined and be an array
1162
1252
  if (!clipPolygon || !Array.isArray(clipPolygon)) {
1163
- throw new Error('clipResolvedUse: clipPolygon must be a valid array');
1253
+ throw new Error("clipResolvedUse: clipPolygon must be a valid array");
1164
1254
  }
1165
1255
 
1166
1256
  // Validate clipPolygon has at least 3 points
1167
1257
  if (clipPolygon.length < 3) {
1168
- throw new Error('clipResolvedUse: clipPolygon must have at least 3 vertices');
1258
+ throw new Error(
1259
+ "clipResolvedUse: clipPolygon must have at least 3 vertices",
1260
+ );
1169
1261
  }
1170
1262
 
1171
1263
  // Parameter validation: samples must be a positive finite number
1172
- if (typeof samples !== 'number' || samples <= 0 || !isFinite(samples)) {
1173
- throw new Error('clipResolvedUse: samples must be a positive finite number');
1264
+ if (typeof samples !== "number" || samples <= 0 || !isFinite(samples)) {
1265
+ throw new Error(
1266
+ "clipResolvedUse: samples must be a positive finite number",
1267
+ );
1174
1268
  }
1175
1269
 
1176
1270
  const polygons = flattenResolvedUse(resolved, samples);
@@ -1243,8 +1337,10 @@ export function clipResolvedUse(resolved, clipPolygon, samples = 20) {
1243
1337
  */
1244
1338
  export function resolvedUseToPathData(resolved, samples = 20) {
1245
1339
  // Parameter validation: samples must be a positive finite number
1246
- if (typeof samples !== 'number' || samples <= 0 || !isFinite(samples)) {
1247
- throw new Error('resolvedUseToPathData: samples must be a positive finite number');
1340
+ if (typeof samples !== "number" || samples <= 0 || !isFinite(samples)) {
1341
+ throw new Error(
1342
+ "resolvedUseToPathData: samples must be a positive finite number",
1343
+ );
1248
1344
  }
1249
1345
 
1250
1346
  const polygons = flattenResolvedUse(resolved, samples);
@@ -1263,7 +1359,10 @@ export function resolvedUseToPathData(resolved, samples = 20) {
1263
1359
  continue;
1264
1360
  }
1265
1361
 
1266
- d += i === 0 ? `M ${x.toFixed(6)} ${y.toFixed(6)}` : ` L ${x.toFixed(6)} ${y.toFixed(6)}`;
1362
+ d +=
1363
+ i === 0
1364
+ ? `M ${x.toFixed(6)} ${y.toFixed(6)}`
1365
+ : ` L ${x.toFixed(6)} ${y.toFixed(6)}`;
1267
1366
  }
1268
1367
  d += " Z";
1269
1368
  paths.push(d);
@@ -1326,8 +1425,8 @@ export function resolvedUseToPathData(resolved, samples = 20) {
1326
1425
  */
1327
1426
  export function buildDefsMap(svgRoot) {
1328
1427
  // Parameter validation: svgRoot must be defined and have querySelectorAll method
1329
- if (!svgRoot || typeof svgRoot.querySelectorAll !== 'function') {
1330
- throw new Error('buildDefsMap: svgRoot must be a valid DOM element');
1428
+ if (!svgRoot || typeof svgRoot.querySelectorAll !== "function") {
1429
+ throw new Error("buildDefsMap: svgRoot must be a valid DOM element");
1331
1430
  }
1332
1431
 
1333
1432
  const defs = {};
@@ -1427,13 +1526,13 @@ export function buildDefsMap(svgRoot) {
1427
1526
  */
1428
1527
  export function resolveAllUses(svgRoot, options = {}) {
1429
1528
  // Parameter validation: svgRoot must be defined and have querySelectorAll method
1430
- if (!svgRoot || typeof svgRoot.querySelectorAll !== 'function') {
1431
- throw new Error('resolveAllUses: svgRoot must be a valid DOM element');
1529
+ if (!svgRoot || typeof svgRoot.querySelectorAll !== "function") {
1530
+ throw new Error("resolveAllUses: svgRoot must be a valid DOM element");
1432
1531
  }
1433
1532
 
1434
1533
  // Validate options parameter
1435
- if (options && typeof options !== 'object') {
1436
- throw new Error('resolveAllUses: options must be an object or undefined');
1534
+ if (options && typeof options !== "object") {
1535
+ throw new Error("resolveAllUses: options must be an object or undefined");
1437
1536
  }
1438
1537
 
1439
1538
  const defs = buildDefsMap(svgRoot);
@@ -1443,7 +1542,7 @@ export function resolveAllUses(svgRoot, options = {}) {
1443
1542
  // Helper to get the next use reference from a definition
1444
1543
  const getUseRef = (id) => {
1445
1544
  // Validate id parameter
1446
- if (!id || typeof id !== 'string') {
1545
+ if (!id || typeof id !== "string") {
1447
1546
  return null;
1448
1547
  }
1449
1548