@emasoft/svg-matrix 1.0.28 → 1.0.30

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 (46) hide show
  1. package/README.md +325 -0
  2. package/bin/svg-matrix.js +985 -378
  3. package/bin/svglinter.cjs +4172 -433
  4. package/bin/svgm.js +723 -180
  5. package/package.json +16 -4
  6. package/src/animation-references.js +71 -52
  7. package/src/arc-length.js +160 -96
  8. package/src/bezier-analysis.js +257 -117
  9. package/src/bezier-intersections.js +411 -148
  10. package/src/browser-verify.js +240 -100
  11. package/src/clip-path-resolver.js +350 -142
  12. package/src/convert-path-data.js +279 -134
  13. package/src/css-specificity.js +78 -70
  14. package/src/flatten-pipeline.js +751 -263
  15. package/src/geometry-to-path.js +511 -182
  16. package/src/index.js +191 -46
  17. package/src/inkscape-support.js +18 -7
  18. package/src/marker-resolver.js +278 -164
  19. package/src/mask-resolver.js +209 -98
  20. package/src/matrix.js +147 -67
  21. package/src/mesh-gradient.js +187 -96
  22. package/src/off-canvas-detection.js +201 -104
  23. package/src/path-analysis.js +187 -107
  24. package/src/path-data-plugins.js +628 -167
  25. package/src/path-simplification.js +0 -1
  26. package/src/pattern-resolver.js +125 -88
  27. package/src/polygon-clip.js +111 -66
  28. package/src/svg-boolean-ops.js +194 -118
  29. package/src/svg-collections.js +22 -18
  30. package/src/svg-flatten.js +282 -164
  31. package/src/svg-parser.js +427 -200
  32. package/src/svg-rendering-context.js +147 -104
  33. package/src/svg-toolbox.js +16381 -3370
  34. package/src/svg2-polyfills.js +93 -224
  35. package/src/transform-decomposition.js +46 -41
  36. package/src/transform-optimization.js +89 -68
  37. package/src/transforms2d.js +49 -16
  38. package/src/transforms3d.js +58 -22
  39. package/src/use-symbol-resolver.js +150 -110
  40. package/src/vector.js +67 -15
  41. package/src/vendor/README.md +110 -0
  42. package/src/vendor/inkscape-hatch-polyfill.js +401 -0
  43. package/src/vendor/inkscape-hatch-polyfill.min.js +8 -0
  44. package/src/vendor/inkscape-mesh-polyfill.js +843 -0
  45. package/src/vendor/inkscape-mesh-polyfill.min.js +8 -0
  46. package/src/verification.js +288 -124
package/src/index.js CHANGED
@@ -5,13 +5,19 @@
5
5
  * SVG path conversion, and 2D/3D affine transformations using Decimal.js.
6
6
  *
7
7
  * @module @emasoft/svg-matrix
8
- * @version 1.0.28
8
+ * @version 1.0.30
9
9
  * @license MIT
10
10
  *
11
11
  * @example
12
- * // ES Module import
12
+ * // ES Module import - Core functionality
13
13
  * import { Decimal, Matrix, Vector, Transforms2D, GeometryToPath } from '@emasoft/svg-matrix';
14
14
  *
15
+ * // SVG2 Polyfills - Detect and inject polyfills for mesh gradients and hatches
16
+ * import { detectSVG2Features, injectPolyfills } from '@emasoft/svg-matrix';
17
+ *
18
+ * // Inkscape Support - Extract layers and preserve Inkscape metadata
19
+ * import { findLayers, extractLayer, extractAllLayers } from '@emasoft/svg-matrix';
20
+ *
15
21
  * // Precision is already set to 80 by default (max is 1e9)
16
22
  * // You can increase it further if needed:
17
23
  * // Decimal.set({ precision: 200 });
@@ -28,55 +34,97 @@
28
34
  * const path = GeometryToPath.circleToPathData(100, 100, 50, 15);
29
35
  */
30
36
 
31
- import Decimal from 'decimal.js';
32
- import { Matrix } from './matrix.js';
33
- import { Vector } from './vector.js';
34
- import * as Transforms2D from './transforms2d.js';
35
- import * as Transforms3D from './transforms3d.js';
36
- import * as GeometryToPath from './geometry-to-path.js';
37
- import * as PolygonClip from './polygon-clip.js';
38
- import * as SVGFlatten from './svg-flatten.js';
39
- import * as BrowserVerify from './browser-verify.js';
40
- import * as ClipPathResolver from './clip-path-resolver.js';
41
- import * as MaskResolver from './mask-resolver.js';
42
- import * as PatternResolver from './pattern-resolver.js';
43
- import * as UseSymbolResolver from './use-symbol-resolver.js';
44
- import * as MarkerResolver from './marker-resolver.js';
45
- import * as MeshGradient from './mesh-gradient.js';
46
- import * as SVGParser from './svg-parser.js';
47
- import * as FlattenPipeline from './flatten-pipeline.js';
48
- import * as Verification from './verification.js';
49
- import * as InkscapeSupport from './inkscape-support.js';
50
- import * as SVG2Polyfills from './svg2-polyfills.js';
51
- import { Logger, LogLevel, setLogLevel, getLogLevel as getLoggerLevel, enableFileLogging, disableFileLogging } from './logger.js';
37
+ import Decimal from "decimal.js";
38
+ import { Matrix } from "./matrix.js";
39
+ import { Vector } from "./vector.js";
40
+ import * as Transforms2D from "./transforms2d.js";
41
+ import * as Transforms3D from "./transforms3d.js";
42
+ import * as GeometryToPath from "./geometry-to-path.js";
43
+ import * as PolygonClip from "./polygon-clip.js";
44
+ import * as SVGFlatten from "./svg-flatten.js";
45
+ import * as BrowserVerify from "./browser-verify.js";
46
+ import * as ClipPathResolver from "./clip-path-resolver.js";
47
+ import * as MaskResolver from "./mask-resolver.js";
48
+ import * as PatternResolver from "./pattern-resolver.js";
49
+ import * as UseSymbolResolver from "./use-symbol-resolver.js";
50
+ import * as MarkerResolver from "./marker-resolver.js";
51
+ import * as MeshGradient from "./mesh-gradient.js";
52
+ import * as SVGParser from "./svg-parser.js";
53
+ import * as FlattenPipeline from "./flatten-pipeline.js";
54
+ import * as Verification from "./verification.js";
55
+ import * as InkscapeSupport from "./inkscape-support.js";
56
+ import {
57
+ INKSCAPE_NS,
58
+ SODIPODI_NS,
59
+ INKSCAPE_PREFIXES,
60
+ isInkscapeLayer,
61
+ getLayerLabel,
62
+ findLayers,
63
+ getNamedViewSettings,
64
+ findGuides,
65
+ getArcParameters,
66
+ getNodeTypes,
67
+ getExportSettings,
68
+ isTiledClone,
69
+ getTiledCloneSource,
70
+ hasInkscapeNamespaces,
71
+ ensureInkscapeNamespaces,
72
+ findReferencedIds,
73
+ buildDefsMapFromDefs,
74
+ resolveDefsDependencies,
75
+ cloneElement,
76
+ extractLayer,
77
+ extractAllLayers,
78
+ analyzeLayerDependencies,
79
+ } from "./inkscape-support.js";
80
+
81
+ import * as SVG2Polyfills from "./svg2-polyfills.js";
82
+ import {
83
+ setPolyfillMinification,
84
+ SVG2_FEATURES,
85
+ detectSVG2Features,
86
+ needsPolyfills,
87
+ generatePolyfillScript,
88
+ injectPolyfills,
89
+ removePolyfills,
90
+ } from "./svg2-polyfills.js";
91
+
92
+ import {
93
+ Logger,
94
+ LogLevel,
95
+ setLogLevel,
96
+ getLogLevel as _getLoggerLevel,
97
+ enableFileLogging,
98
+ disableFileLogging,
99
+ } from "./logger.js";
52
100
 
53
101
  // SVGO-inspired precision modules
54
- import * as PathSimplification from './path-simplification.js';
55
- import * as TransformDecomposition from './transform-decomposition.js';
56
- import * as GJKCollision from './gjk-collision.js';
57
- import * as PathOptimization from './path-optimization.js';
58
- import * as TransformOptimization from './transform-optimization.js';
59
- import * as OffCanvasDetection from './off-canvas-detection.js';
60
- import * as CSSSpecificity from './css-specificity.js';
102
+ import * as PathSimplification from "./path-simplification.js";
103
+ import * as TransformDecomposition from "./transform-decomposition.js";
104
+ import * as GJKCollision from "./gjk-collision.js";
105
+ import * as PathOptimization from "./path-optimization.js";
106
+ import * as TransformOptimization from "./transform-optimization.js";
107
+ import * as OffCanvasDetection from "./off-canvas-detection.js";
108
+ import * as CSSSpecificity from "./css-specificity.js";
61
109
 
62
110
  // Animation-aware reference tracking (FIXES SVGO's animation destruction bug)
63
- import * as AnimationReferences from './animation-references.js';
111
+ import * as AnimationReferences from "./animation-references.js";
64
112
 
65
113
  // SVG Toolbox - SVGO-equivalent functions with simple API
66
- import * as SVGToolbox from './svg-toolbox.js';
114
+ import * as SVGToolbox from "./svg-toolbox.js";
67
115
 
68
116
  // Bezier Curve Analysis - svgpathtools-equivalent with 80-digit arbitrary precision
69
117
  // These modules provide superior precision compared to Python's float64 svgpathtools
70
- import * as BezierAnalysis from './bezier-analysis.js';
71
- import * as ArcLength from './arc-length.js';
72
- import * as PathAnalysis from './path-analysis.js';
73
- import * as BezierIntersections from './bezier-intersections.js';
118
+ import * as BezierAnalysis from "./bezier-analysis.js";
119
+ import * as ArcLength from "./arc-length.js";
120
+ import * as PathAnalysis from "./path-analysis.js";
121
+ import * as BezierIntersections from "./bezier-intersections.js";
74
122
 
75
123
  // SVG Boolean Operations - fill-rule and stroke-aware geometric operations
76
- import * as SVGBooleanOps from './svg-boolean-ops.js';
124
+ import * as SVGBooleanOps from "./svg-boolean-ops.js";
77
125
 
78
126
  // SVG Rendering Context - tracks ALL SVG properties affecting rendered geometry
79
- import * as SVGRenderingContext from './svg-rendering-context.js';
127
+ import * as SVGRenderingContext from "./svg-rendering-context.js";
80
128
 
81
129
  // Set high-precision default (80 significant digits) on module load
82
130
  // This is the same precision used internally by all svg-matrix modules
@@ -87,7 +135,7 @@ Decimal.set({ precision: 80 });
87
135
  * Library version
88
136
  * @constant {string}
89
137
  */
90
- export const VERSION = '1.0.28';
138
+ export const VERSION = '1.0.30';
91
139
 
92
140
  /**
93
141
  * Default precision for path output (decimal places)
@@ -223,8 +271,8 @@ export {
223
271
  removeAttrs,
224
272
  removeElementsByAttr,
225
273
  // Presets
226
- preset_default,
227
- preset_none,
274
+ presetDefault,
275
+ presetNone,
228
276
  applyPreset,
229
277
  optimize,
230
278
  createConfig,
@@ -238,14 +286,55 @@ export {
238
286
  imageToPath,
239
287
  detectCollisions,
240
288
  measureDistance,
289
+ validateXML,
241
290
  validateSVG,
242
- validateSvg,
243
- fixInvalidSvg,
291
+ fixInvalidSVG,
244
292
  ValidationSeverity,
245
293
  flattenAll,
246
294
  simplifyPath,
295
+ optimizeAnimationTiming,
296
+ optimizePaths,
297
+ simplifyPaths,
247
298
  decomposeTransform,
248
- } from './svg-toolbox.js';
299
+ embedExternalDependencies,
300
+ } from "./svg-toolbox.js";
301
+
302
+ // Re-export all svg2-polyfills functions for direct access
303
+ export {
304
+ setPolyfillMinification,
305
+ SVG2_FEATURES,
306
+ detectSVG2Features,
307
+ needsPolyfills,
308
+ generatePolyfillScript,
309
+ injectPolyfills,
310
+ removePolyfills,
311
+ } from "./svg2-polyfills.js";
312
+
313
+ // Re-export all inkscape-support functions for direct access
314
+ export {
315
+ INKSCAPE_NS,
316
+ SODIPODI_NS,
317
+ INKSCAPE_PREFIXES,
318
+ isInkscapeLayer,
319
+ getLayerLabel,
320
+ findLayers,
321
+ getNamedViewSettings,
322
+ findGuides,
323
+ getArcParameters,
324
+ getNodeTypes,
325
+ getExportSettings,
326
+ isTiledClone,
327
+ getTiledCloneSource,
328
+ hasInkscapeNamespaces,
329
+ ensureInkscapeNamespaces,
330
+ findReferencedIds,
331
+ buildDefsMapFromDefs,
332
+ resolveDefsDependencies,
333
+ cloneElement,
334
+ extractLayer,
335
+ extractAllLayers,
336
+ analyzeLayerDependencies,
337
+ } from "./inkscape-support.js";
249
338
 
250
339
  // ============================================================================
251
340
  // LOGGING: Configurable logging control
@@ -375,8 +464,25 @@ export function ellipseToPath(cx, cy, rx, ry, precision = DEFAULT_PRECISION) {
375
464
  * @param {number} [precision=6] - Number of decimal places in output
376
465
  * @returns {string} SVG path data string
377
466
  */
378
- export function rectToPath(x, y, width, height, rx = 0, ry = null, precision = DEFAULT_PRECISION) {
379
- return GeometryToPath.rectToPathData(x, y, width, height, rx, ry, false, precision);
467
+ export function rectToPath(
468
+ x,
469
+ y,
470
+ width,
471
+ height,
472
+ rx = 0,
473
+ ry = null,
474
+ precision = DEFAULT_PRECISION,
475
+ ) {
476
+ return GeometryToPath.rectToPathData(
477
+ x,
478
+ y,
479
+ width,
480
+ height,
481
+ rx,
482
+ ry,
483
+ false,
484
+ precision,
485
+ );
380
486
  }
381
487
 
382
488
  /**
@@ -583,6 +689,39 @@ export default {
583
689
  InkscapeSupport,
584
690
  SVG2Polyfills,
585
691
 
692
+ // SVG2 Polyfills - individual exports
693
+ setPolyfillMinification,
694
+ SVG2_FEATURES,
695
+ detectSVG2Features,
696
+ needsPolyfills,
697
+ generatePolyfillScript,
698
+ injectPolyfills,
699
+ removePolyfills,
700
+
701
+ // Inkscape Support - individual exports
702
+ INKSCAPE_NS,
703
+ SODIPODI_NS,
704
+ INKSCAPE_PREFIXES,
705
+ isInkscapeLayer,
706
+ getLayerLabel,
707
+ findLayers,
708
+ getNamedViewSettings,
709
+ findGuides,
710
+ getArcParameters,
711
+ getNodeTypes,
712
+ getExportSettings,
713
+ isTiledClone,
714
+ getTiledCloneSource,
715
+ hasInkscapeNamespaces,
716
+ ensureInkscapeNamespaces,
717
+ findReferencedIds,
718
+ buildDefsMapFromDefs,
719
+ resolveDefsDependencies,
720
+ cloneElement,
721
+ extractLayer,
722
+ extractAllLayers,
723
+ analyzeLayerDependencies,
724
+
586
725
  // Logging
587
726
  Logger,
588
727
  LogLevel,
@@ -611,6 +750,12 @@ export default {
611
750
  PathAnalysis,
612
751
  BezierIntersections,
613
752
 
753
+ // SVG Boolean Operations (fill-rule and stroke-aware)
754
+ SVGBooleanOps,
755
+
756
+ // SVG Rendering Context (tracks all SVG properties affecting geometry)
757
+ SVGRenderingContext,
758
+
614
759
  // Convenience functions
615
760
  translate2D,
616
761
  rotate2D,
@@ -25,6 +25,8 @@ export const INKSCAPE_PREFIXES = ['inkscape', 'sodipodi'];
25
25
  */
26
26
  export function isInkscapeLayer(element) {
27
27
  if (!element || element.tagName !== 'g') return false;
28
+ // Safety check: ensure getAttribute method exists before calling it
29
+ if (typeof element.getAttribute !== 'function') return false;
28
30
  return element.getAttribute('inkscape:groupmode') === 'layer';
29
31
  }
30
32
 
@@ -223,7 +225,9 @@ export function getTiledCloneSource(element) {
223
225
  * @returns {boolean} True if Inkscape namespaces are present
224
226
  */
225
227
  export function hasInkscapeNamespaces(doc) {
228
+ if (!doc) return false;
226
229
  const svg = doc.documentElement || doc;
230
+ if (!svg || typeof svg.getAttribute !== 'function') return false;
227
231
  const hasInkscape = svg.getAttribute('xmlns:inkscape') === INKSCAPE_NS;
228
232
  const hasSodipodi = svg.getAttribute('xmlns:sodipodi') === SODIPODI_NS;
229
233
  return hasInkscape || hasSodipodi;
@@ -239,6 +243,11 @@ export function hasInkscapeNamespaces(doc) {
239
243
  export function ensureInkscapeNamespaces(doc) {
240
244
  const svg = doc.documentElement || doc;
241
245
 
246
+ // Safety check: ensure getAttribute and setAttribute methods exist
247
+ if (typeof svg.getAttribute !== 'function' || typeof svg.setAttribute !== 'function') {
248
+ return doc;
249
+ }
250
+
242
251
  if (!svg.getAttribute('xmlns:inkscape')) {
243
252
  svg.setAttribute('xmlns:inkscape', INKSCAPE_NS);
244
253
  }
@@ -332,7 +341,7 @@ export function findReferencedIds(element) {
332
341
  * @param {Object} doc - Parsed SVG document
333
342
  * @returns {Map<string, Object>} Map of ID to element
334
343
  */
335
- export function buildDefsMap(doc) {
344
+ export function buildDefsMapFromDefs(doc) {
336
345
  const defsMap = new Map();
337
346
 
338
347
  const walk = (el) => {
@@ -462,8 +471,8 @@ export function extractLayer(doc, layerOrId, options = {}) {
462
471
  if (typeof layerOrId === 'string') {
463
472
  const layers = findLayers(doc);
464
473
  const found = layers.find(l => l.id === layerOrId || l.label === layerOrId);
465
- if (!found) {
466
- throw new Error(`Layer not found: ${layerOrId}`);
474
+ if (!found || !found.element) {
475
+ throw new Error(`Layer not found or invalid: ${layerOrId}`);
467
476
  }
468
477
  layer = found.element;
469
478
  } else {
@@ -478,7 +487,7 @@ export function extractLayer(doc, layerOrId, options = {}) {
478
487
  const svgRoot = doc.documentElement || doc;
479
488
 
480
489
  // Build defs map from source document
481
- const defsMap = buildDefsMap(doc);
490
+ const defsMap = buildDefsMapFromDefs(doc);
482
491
 
483
492
  // Find all IDs referenced by this layer
484
493
  const referencedIds = findReferencedIds(layer);
@@ -508,8 +517,10 @@ export function extractLayer(doc, layerOrId, options = {}) {
508
517
  defsChildren.push(cloneElement(defElement));
509
518
  }
510
519
  }
511
- const newDefs = new SVGElement('defs', {}, defsChildren, null);
512
- svgChildren.push(newDefs);
520
+ if (defsChildren.length > 0) {
521
+ const newDefs = new SVGElement('defs', {}, defsChildren, null);
522
+ svgChildren.push(newDefs);
523
+ }
513
524
  }
514
525
 
515
526
  // Clone the layer
@@ -586,7 +597,7 @@ export function extractAllLayers(doc, options = {}) {
586
597
  */
587
598
  export function analyzeLayerDependencies(doc) {
588
599
  const layers = findLayers(doc);
589
- const defsMap = buildDefsMap(doc);
600
+ const defsMap = buildDefsMapFromDefs(doc);
590
601
  const layerRefs = new Map(); // layer ID -> Set of referenced def IDs
591
602
  const defUsage = new Map(); // def ID -> Set of layer IDs that use it
592
603