@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
@@ -7,14 +7,14 @@
7
7
  * @module inkscape-support
8
8
  */
9
9
 
10
- import { SVGElement } from './svg-parser.js';
10
+ import { SVGElement } from "./svg-parser.js";
11
11
 
12
12
  // Inkscape namespace URIs
13
- export const INKSCAPE_NS = 'http://www.inkscape.org/namespaces/inkscape';
14
- export const SODIPODI_NS = 'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd';
13
+ export const INKSCAPE_NS = "http://www.inkscape.org/namespaces/inkscape";
14
+ export const SODIPODI_NS = "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
15
15
 
16
16
  // Inkscape-specific element and attribute prefixes
17
- export const INKSCAPE_PREFIXES = ['inkscape', 'sodipodi'];
17
+ export const INKSCAPE_PREFIXES = ["inkscape", "sodipodi"];
18
18
 
19
19
  /**
20
20
  * Check if an element is an Inkscape layer.
@@ -24,10 +24,10 @@ export const INKSCAPE_PREFIXES = ['inkscape', 'sodipodi'];
24
24
  * @returns {boolean} True if the element is an Inkscape layer
25
25
  */
26
26
  export function isInkscapeLayer(element) {
27
- if (!element || element.tagName !== 'g') return false;
27
+ if (!element || element.tagName !== "g") return false;
28
28
  // Safety check: ensure getAttribute method exists before calling it
29
- if (typeof element.getAttribute !== 'function') return false;
30
- return element.getAttribute('inkscape:groupmode') === 'layer';
29
+ if (typeof element.getAttribute !== "function") return false;
30
+ return element.getAttribute("inkscape:groupmode") === "layer";
31
31
  }
32
32
 
33
33
  /**
@@ -37,8 +37,8 @@ export function isInkscapeLayer(element) {
37
37
  * @returns {string|null} Layer label or null if not set
38
38
  */
39
39
  export function getLayerLabel(element) {
40
- if (!element || typeof element.getAttribute !== 'function') return null;
41
- return element.getAttribute('inkscape:label') || null;
40
+ if (!element || typeof element.getAttribute !== "function") return null;
41
+ return element.getAttribute("inkscape:label") || null;
42
42
  }
43
43
 
44
44
  /**
@@ -56,7 +56,8 @@ export function findLayers(doc) {
56
56
  layers.push({
57
57
  element: el,
58
58
  label: getLayerLabel(el),
59
- id: (typeof el.getAttribute === 'function' ? el.getAttribute('id') : null)
59
+ id:
60
+ typeof el.getAttribute === "function" ? el.getAttribute("id") : null,
60
61
  });
61
62
  }
62
63
  // Safety check: ensure children is an array before iteration
@@ -87,7 +88,7 @@ export function getNamedViewSettings(doc) {
87
88
 
88
89
  const findNamedview = (el) => {
89
90
  if (!el) return;
90
- if (el.tagName === 'sodipodi:namedview') {
91
+ if (el.tagName === "sodipodi:namedview") {
91
92
  namedview = el;
92
93
  return;
93
94
  }
@@ -100,21 +101,21 @@ export function getNamedViewSettings(doc) {
100
101
  };
101
102
 
102
103
  findNamedview(doc);
103
- if (!namedview || typeof namedview.getAttribute !== 'function') return null;
104
+ if (!namedview || typeof namedview.getAttribute !== "function") return null;
104
105
 
105
106
  return {
106
- pagecolor: namedview.getAttribute('pagecolor'),
107
- bordercolor: namedview.getAttribute('bordercolor'),
108
- borderopacity: namedview.getAttribute('borderopacity'),
109
- showgrid: namedview.getAttribute('showgrid'),
110
- showguides: namedview.getAttribute('showguides'),
111
- guidetolerance: namedview.getAttribute('guidetolerance'),
112
- inkscapeZoom: namedview.getAttribute('inkscape:zoom'),
113
- inkscapeCx: namedview.getAttribute('inkscape:cx'),
114
- inkscapeCy: namedview.getAttribute('inkscape:cy'),
115
- inkscapeWindowWidth: namedview.getAttribute('inkscape:window-width'),
116
- inkscapeWindowHeight: namedview.getAttribute('inkscape:window-height'),
117
- inkscapeCurrentLayer: namedview.getAttribute('inkscape:current-layer')
107
+ pagecolor: namedview.getAttribute("pagecolor"),
108
+ bordercolor: namedview.getAttribute("bordercolor"),
109
+ borderopacity: namedview.getAttribute("borderopacity"),
110
+ showgrid: namedview.getAttribute("showgrid"),
111
+ showguides: namedview.getAttribute("showguides"),
112
+ guidetolerance: namedview.getAttribute("guidetolerance"),
113
+ inkscapeZoom: namedview.getAttribute("inkscape:zoom"),
114
+ inkscapeCx: namedview.getAttribute("inkscape:cx"),
115
+ inkscapeCy: namedview.getAttribute("inkscape:cy"),
116
+ inkscapeWindowWidth: namedview.getAttribute("inkscape:window-width"),
117
+ inkscapeWindowHeight: namedview.getAttribute("inkscape:window-height"),
118
+ inkscapeCurrentLayer: namedview.getAttribute("inkscape:current-layer"),
118
119
  };
119
120
  }
120
121
 
@@ -132,9 +133,9 @@ export function findGuides(doc) {
132
133
 
133
134
  const walk = (el) => {
134
135
  if (!el) return;
135
- if (el.tagName === 'sodipodi:guide') {
136
- const position = el.getAttribute?.('position') || null;
137
- const orientation = el.getAttribute?.('orientation') || null;
136
+ if (el.tagName === "sodipodi:guide") {
137
+ const position = el.getAttribute?.("position") || null;
138
+ const orientation = el.getAttribute?.("orientation") || null;
138
139
 
139
140
  // Validate guide has required attributes (position and orientation)
140
141
  if (!position || !orientation) {
@@ -150,9 +151,9 @@ export function findGuides(doc) {
150
151
  guides.push({
151
152
  position,
152
153
  orientation,
153
- id: el.getAttribute?.('id') || null,
154
- inkscapeColor: el.getAttribute?.('inkscape:color') || null,
155
- inkscapeLabel: el.getAttribute?.('inkscape:label') || null
154
+ id: el.getAttribute?.("id") || null,
155
+ inkscapeColor: el.getAttribute?.("inkscape:color") || null,
156
+ inkscapeLabel: el.getAttribute?.("inkscape:label") || null,
156
157
  });
157
158
  }
158
159
  // Safety check: ensure children is an array before iteration
@@ -175,29 +176,29 @@ export function findGuides(doc) {
175
176
  * @returns {Object|null} Arc parameters or null if not an arc
176
177
  */
177
178
  export function getArcParameters(element) {
178
- if (!element || typeof element.getAttribute !== 'function') return null;
179
+ if (!element || typeof element.getAttribute !== "function") return null;
179
180
 
180
- const type = element.getAttribute('sodipodi:type');
181
- if (type !== 'arc') return null;
181
+ const type = element.getAttribute("sodipodi:type");
182
+ if (type !== "arc") return null;
182
183
 
183
184
  // Validate that required arc parameters exist
184
- const cx = element.getAttribute('sodipodi:cx');
185
- const cy = element.getAttribute('sodipodi:cy');
186
- const rx = element.getAttribute('sodipodi:rx');
187
- const ry = element.getAttribute('sodipodi:ry');
185
+ const cx = element.getAttribute("sodipodi:cx");
186
+ const cy = element.getAttribute("sodipodi:cy");
187
+ const rx = element.getAttribute("sodipodi:rx");
188
+ const ry = element.getAttribute("sodipodi:ry");
188
189
 
189
190
  // Arc must have center and radii
190
191
  if (!cx || !cy || !rx || !ry) return null;
191
192
 
192
193
  return {
193
- type: 'arc',
194
+ type: "arc",
194
195
  cx,
195
196
  cy,
196
197
  rx,
197
198
  ry,
198
- start: element.getAttribute('sodipodi:start'),
199
- end: element.getAttribute('sodipodi:end'),
200
- open: element.getAttribute('sodipodi:open')
199
+ start: element.getAttribute("sodipodi:start"),
200
+ end: element.getAttribute("sodipodi:end"),
201
+ open: element.getAttribute("sodipodi:open"),
201
202
  };
202
203
  }
203
204
 
@@ -209,9 +210,9 @@ export function getArcParameters(element) {
209
210
  * @returns {string|null} Node types string (c=corner, s=smooth, z=symmetric, a=auto)
210
211
  */
211
212
  export function getNodeTypes(element) {
212
- if (!element || typeof element.getAttribute !== 'function') return null;
213
+ if (!element || typeof element.getAttribute !== "function") return null;
213
214
 
214
- const nodeTypes = element.getAttribute('sodipodi:nodetypes');
215
+ const nodeTypes = element.getAttribute("sodipodi:nodetypes");
215
216
  if (!nodeTypes) return null;
216
217
 
217
218
  // Validate format: should only contain c, s, z, a characters
@@ -227,11 +228,11 @@ export function getNodeTypes(element) {
227
228
  * @returns {Object|null} Export settings or null if not set
228
229
  */
229
230
  export function getExportSettings(element) {
230
- if (!element || typeof element.getAttribute !== 'function') return null;
231
+ if (!element || typeof element.getAttribute !== "function") return null;
231
232
 
232
- const filename = element.getAttribute('inkscape:export-filename');
233
- const xdpi = element.getAttribute('inkscape:export-xdpi');
234
- const ydpi = element.getAttribute('inkscape:export-ydpi');
233
+ const filename = element.getAttribute("inkscape:export-filename");
234
+ const xdpi = element.getAttribute("inkscape:export-xdpi");
235
+ const ydpi = element.getAttribute("inkscape:export-ydpi");
235
236
 
236
237
  if (!filename && !xdpi && !ydpi) return null;
237
238
 
@@ -241,8 +242,8 @@ export function getExportSettings(element) {
241
242
 
242
243
  return {
243
244
  filename,
244
- xdpi: (parsedXdpi !== null && !isNaN(parsedXdpi)) ? parsedXdpi : null,
245
- ydpi: (parsedYdpi !== null && !isNaN(parsedYdpi)) ? parsedYdpi : null
245
+ xdpi: parsedXdpi !== null && !isNaN(parsedXdpi) ? parsedXdpi : null,
246
+ ydpi: parsedYdpi !== null && !isNaN(parsedYdpi) ? parsedYdpi : null,
246
247
  };
247
248
  }
248
249
 
@@ -253,8 +254,8 @@ export function getExportSettings(element) {
253
254
  * @returns {boolean} True if element is a tiled clone
254
255
  */
255
256
  export function isTiledClone(element) {
256
- if (!element || typeof element.hasAttribute !== 'function') return false;
257
- return element.hasAttribute('inkscape:tiled-clone-of');
257
+ if (!element || typeof element.hasAttribute !== "function") return false;
258
+ return element.hasAttribute("inkscape:tiled-clone-of");
258
259
  }
259
260
 
260
261
  /**
@@ -264,8 +265,8 @@ export function isTiledClone(element) {
264
265
  * @returns {string|null} Source element ID or null
265
266
  */
266
267
  export function getTiledCloneSource(element) {
267
- if (!element || typeof element.getAttribute !== 'function') return null;
268
- return element.getAttribute('inkscape:tiled-clone-of') || null;
268
+ if (!element || typeof element.getAttribute !== "function") return null;
269
+ return element.getAttribute("inkscape:tiled-clone-of") || null;
269
270
  }
270
271
 
271
272
  /**
@@ -279,11 +280,11 @@ export function hasInkscapeNamespaces(doc) {
279
280
 
280
281
  // Try documentElement first, fall back to doc itself
281
282
  const svg = doc.documentElement || doc;
282
- if (!svg || typeof svg.getAttribute !== 'function') return false;
283
+ if (!svg || typeof svg.getAttribute !== "function") return false;
283
284
 
284
285
  // Check for exact namespace URI matches
285
- const inkscapeNs = svg.getAttribute('xmlns:inkscape');
286
- const sodipodiNs = svg.getAttribute('xmlns:sodipodi');
286
+ const inkscapeNs = svg.getAttribute("xmlns:inkscape");
287
+ const sodipodiNs = svg.getAttribute("xmlns:sodipodi");
287
288
 
288
289
  const hasInkscape = inkscapeNs === INKSCAPE_NS;
289
290
  const hasSodipodi = sodipodiNs === SODIPODI_NS;
@@ -305,15 +306,18 @@ export function ensureInkscapeNamespaces(doc) {
305
306
  const svg = doc.documentElement || doc;
306
307
 
307
308
  // Safety check: ensure getAttribute and setAttribute methods exist
308
- if (typeof svg.getAttribute !== 'function' || typeof svg.setAttribute !== 'function') {
309
+ if (
310
+ typeof svg.getAttribute !== "function" ||
311
+ typeof svg.setAttribute !== "function"
312
+ ) {
309
313
  return doc;
310
314
  }
311
315
 
312
- if (!svg.getAttribute('xmlns:inkscape')) {
313
- svg.setAttribute('xmlns:inkscape', INKSCAPE_NS);
316
+ if (!svg.getAttribute("xmlns:inkscape")) {
317
+ svg.setAttribute("xmlns:inkscape", INKSCAPE_NS);
314
318
  }
315
- if (!svg.getAttribute('xmlns:sodipodi')) {
316
- svg.setAttribute('xmlns:sodipodi', SODIPODI_NS);
319
+ if (!svg.getAttribute("xmlns:sodipodi")) {
320
+ svg.setAttribute("xmlns:sodipodi", SODIPODI_NS);
317
321
  }
318
322
 
319
323
  return doc;
@@ -339,24 +343,30 @@ export function findReferencedIds(element) {
339
343
 
340
344
  // Attributes that can contain url(#id) references
341
345
  const urlRefAttrs = [
342
- 'fill', 'stroke', 'clip-path', 'mask', 'filter',
343
- 'marker-start', 'marker-mid', 'marker-end'
346
+ "fill",
347
+ "stroke",
348
+ "clip-path",
349
+ "mask",
350
+ "filter",
351
+ "marker-start",
352
+ "marker-mid",
353
+ "marker-end",
344
354
  ];
345
355
 
346
356
  // Attributes that can contain #id or url(#id) references
347
- const hrefAttrs = ['href', 'xlink:href'];
357
+ const hrefAttrs = ["href", "xlink:href"];
348
358
 
349
359
  const extractUrlId = (value) => {
350
- if (!value || typeof value !== 'string') return null;
360
+ if (!value || typeof value !== "string") return null;
351
361
  // Match url(#id) or url("#id")
352
362
  const match = value.match(/url\(["']?#([^"')]+)["']?\)/);
353
363
  return match ? match[1] : null;
354
364
  };
355
365
 
356
366
  const extractHrefId = (value) => {
357
- if (!value || typeof value !== 'string') return null;
367
+ if (!value || typeof value !== "string") return null;
358
368
  // Match #id references
359
- if (value.startsWith('#')) {
369
+ if (value.startsWith("#")) {
360
370
  return value.slice(1);
361
371
  }
362
372
  return null;
@@ -378,7 +388,7 @@ export function findReferencedIds(element) {
378
388
  }
379
389
 
380
390
  // Check style attribute for url() references
381
- const style = el.getAttribute?.('style');
391
+ const style = el.getAttribute?.("style");
382
392
  if (style) {
383
393
  const urlMatches = style.matchAll(/url\(["']?#([^"')]+)["']?\)/g);
384
394
  for (const match of urlMatches) {
@@ -414,7 +424,7 @@ export function buildDefsMapFromDefs(doc) {
414
424
  if (!el) return;
415
425
 
416
426
  // If element has an ID, add to map
417
- const id = el.getAttribute?.('id');
427
+ const id = el.getAttribute?.("id");
418
428
  if (id) {
419
429
  defsMap.set(id, el);
420
430
  }
@@ -430,7 +440,7 @@ export function buildDefsMapFromDefs(doc) {
430
440
  // Only scan defs elements for efficiency
431
441
  const findDefs = (el) => {
432
442
  if (!el) return;
433
- if (el.tagName === 'defs') {
443
+ if (el.tagName === "defs") {
434
444
  walk(el);
435
445
  }
436
446
  if (el.children && Array.isArray(el.children)) {
@@ -455,10 +465,12 @@ export function buildDefsMapFromDefs(doc) {
455
465
  export function resolveDefsDependencies(initialIds, defsMap) {
456
466
  // Validate parameters
457
467
  if (!initialIds || !(initialIds instanceof Set)) {
458
- throw new Error('resolveDefsDependencies: initialIds parameter must be a Set');
468
+ throw new Error(
469
+ "resolveDefsDependencies: initialIds parameter must be a Set",
470
+ );
459
471
  }
460
472
  if (!defsMap || !(defsMap instanceof Map)) {
461
- throw new Error('resolveDefsDependencies: defsMap parameter must be a Map');
473
+ throw new Error("resolveDefsDependencies: defsMap parameter must be a Map");
462
474
  }
463
475
 
464
476
  const resolved = new Set();
@@ -498,7 +510,7 @@ export function cloneElement(element) {
498
510
  const attrs = {};
499
511
  if (element._attributes) {
500
512
  Object.assign(attrs, element._attributes);
501
- } else if (typeof element.getAttributeNames === 'function') {
513
+ } else if (typeof element.getAttributeNames === "function") {
502
514
  for (const name of element.getAttributeNames()) {
503
515
  attrs[name] = element.getAttribute(name);
504
516
  }
@@ -520,7 +532,7 @@ export function cloneElement(element) {
520
532
  element.tagName,
521
533
  attrs,
522
534
  clonedChildren,
523
- element.textContent || null
535
+ element.textContent || null,
524
536
  );
525
537
 
526
538
  return clone;
@@ -540,16 +552,18 @@ export function cloneElement(element) {
540
552
  export function extractLayer(doc, layerOrId, options = {}) {
541
553
  // Validate doc parameter
542
554
  if (!doc) {
543
- throw new Error('doc parameter is required');
555
+ throw new Error("doc parameter is required");
544
556
  }
545
557
 
546
558
  const { preserveTransform = true } = options;
547
559
 
548
560
  // Find the layer element
549
561
  let layer;
550
- if (typeof layerOrId === 'string') {
562
+ if (typeof layerOrId === "string") {
551
563
  const layers = findLayers(doc);
552
- const found = layers.find(l => l.id === layerOrId || l.label === layerOrId);
564
+ const found = layers.find(
565
+ (l) => l.id === layerOrId || l.label === layerOrId,
566
+ );
553
567
  if (!found) {
554
568
  throw new Error(`Layer not found: ${layerOrId}`);
555
569
  }
@@ -559,13 +573,13 @@ export function extractLayer(doc, layerOrId, options = {}) {
559
573
  layer = found.element;
560
574
  } else {
561
575
  if (!layerOrId) {
562
- throw new Error('layerOrId parameter is required');
576
+ throw new Error("layerOrId parameter is required");
563
577
  }
564
578
  layer = layerOrId;
565
579
  }
566
580
 
567
581
  if (!isInkscapeLayer(layer)) {
568
- throw new Error('Element is not an Inkscape layer');
582
+ throw new Error("Element is not an Inkscape layer");
569
583
  }
570
584
 
571
585
  // Get SVG root element
@@ -584,7 +598,7 @@ export function extractLayer(doc, layerOrId, options = {}) {
584
598
  const svgAttrs = {};
585
599
  if (svgRoot._attributes) {
586
600
  Object.assign(svgAttrs, svgRoot._attributes);
587
- } else if (typeof svgRoot.getAttributeNames === 'function') {
601
+ } else if (typeof svgRoot.getAttributeNames === "function") {
588
602
  for (const name of svgRoot.getAttributeNames()) {
589
603
  svgAttrs[name] = svgRoot.getAttribute(name);
590
604
  }
@@ -603,7 +617,7 @@ export function extractLayer(doc, layerOrId, options = {}) {
603
617
  }
604
618
  }
605
619
  if (defsChildren.length > 0) {
606
- const newDefs = new SVGElement('defs', {}, defsChildren, null);
620
+ const newDefs = new SVGElement("defs", {}, defsChildren, null);
607
621
  svgChildren.push(newDefs);
608
622
  }
609
623
  }
@@ -619,12 +633,15 @@ export function extractLayer(doc, layerOrId, options = {}) {
619
633
  svgChildren.push(clonedLayer);
620
634
 
621
635
  // Create new SVG document using SVGElement
622
- const newSvg = new SVGElement('svg', svgAttrs, svgChildren, null);
636
+ const newSvg = new SVGElement("svg", svgAttrs, svgChildren, null);
623
637
 
624
638
  // Get layer info
625
639
  const layerInfo = {
626
- id: (typeof layer.getAttribute === 'function' ? layer.getAttribute('id') : null),
627
- label: getLayerLabel(layer)
640
+ id:
641
+ typeof layer.getAttribute === "function"
642
+ ? layer.getAttribute("id")
643
+ : null,
644
+ label: getLayerLabel(layer),
628
645
  };
629
646
 
630
647
  return { svg: newSvg, layerInfo };
@@ -642,7 +659,7 @@ export function extractLayer(doc, layerOrId, options = {}) {
642
659
  export function extractAllLayers(doc, options = {}) {
643
660
  // Validate doc parameter
644
661
  if (!doc) {
645
- throw new Error('doc parameter is required');
662
+ throw new Error("doc parameter is required");
646
663
  }
647
664
 
648
665
  const { includeHidden = false } = options;
@@ -655,20 +672,22 @@ export function extractAllLayers(doc, options = {}) {
655
672
  // Skip hidden layers unless requested
656
673
  if (!includeHidden) {
657
674
  // Validate getAttribute method exists
658
- if (typeof layer.getAttribute !== 'function') continue;
675
+ if (typeof layer.getAttribute !== "function") continue;
659
676
 
660
- const style = layer.getAttribute('style') || '';
661
- const display = layer.getAttribute('display');
662
- const visibility = layer.getAttribute('visibility');
677
+ const style = layer.getAttribute("style") || "";
678
+ const display = layer.getAttribute("display");
679
+ const visibility = layer.getAttribute("visibility");
663
680
 
664
681
  // Use regex to avoid partial matches in style attribute
665
682
  const hasDisplayNone = /display\s*:\s*none/i.test(style);
666
683
  const hasVisibilityHidden = /visibility\s*:\s*hidden/i.test(style);
667
684
 
668
- if (display === 'none' ||
669
- visibility === 'hidden' ||
670
- hasDisplayNone ||
671
- hasVisibilityHidden) {
685
+ if (
686
+ display === "none" ||
687
+ visibility === "hidden" ||
688
+ hasDisplayNone ||
689
+ hasVisibilityHidden
690
+ ) {
672
691
  continue;
673
692
  }
674
693
  }
@@ -678,7 +697,9 @@ export function extractAllLayers(doc, options = {}) {
678
697
  results.push(extracted);
679
698
  } catch (e) {
680
699
  // Skip layers that fail to extract
681
- console.warn(`Failed to extract layer ${layerData.id || layerData.label}: ${e.message}`);
700
+ console.warn(
701
+ `Failed to extract layer ${layerData.id || layerData.label}: ${e.message}`,
702
+ );
682
703
  }
683
704
  }
684
705
 
@@ -695,17 +716,17 @@ export function extractAllLayers(doc, options = {}) {
695
716
  export function analyzeLayerDependencies(doc) {
696
717
  // Validate doc parameter
697
718
  if (!doc) {
698
- throw new Error('doc parameter is required');
719
+ throw new Error("doc parameter is required");
699
720
  }
700
721
 
701
722
  const layers = findLayers(doc);
702
723
  const defsMap = buildDefsMapFromDefs(doc);
703
724
  const layerRefs = new Map(); // layer ID -> Set of referenced def IDs
704
- const defUsage = new Map(); // def ID -> Set of layer IDs that use it
725
+ const defUsage = new Map(); // def ID -> Set of layer IDs that use it
705
726
 
706
727
  for (const layerData of layers) {
707
728
  const layer = layerData.element;
708
- const layerId = layerData.id || layerData.label || 'unnamed';
729
+ const layerId = layerData.id || layerData.label || "unnamed";
709
730
 
710
731
  // Find refs for this layer
711
732
  const refs = findReferencedIds(layer);
@@ -730,7 +751,7 @@ export function analyzeLayerDependencies(doc) {
730
751
  if (layerSet.size > 1) {
731
752
  sharedDefs.push({
732
753
  id: defId,
733
- usedBy: [...layerSet]
754
+ usedBy: [...layerSet],
734
755
  });
735
756
  } else {
736
757
  const layerId = [...layerSet][0];
@@ -742,13 +763,13 @@ export function analyzeLayerDependencies(doc) {
742
763
  }
743
764
 
744
765
  return {
745
- layers: layers.map(l => ({
766
+ layers: layers.map((l) => ({
746
767
  id: l.id,
747
768
  label: l.label,
748
- referencedDefs: [...(layerRefs.get(l.id || l.label || 'unnamed') || [])]
769
+ referencedDefs: [...(layerRefs.get(l.id || l.label || "unnamed") || [])],
749
770
  })),
750
771
  sharedDefs,
751
772
  exclusiveDefs: Object.fromEntries(exclusiveDefs),
752
- totalDefs: defsMap.size
773
+ totalDefs: defsMap.size,
753
774
  };
754
775
  }