svg-toolbox 1.1.11 → 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 (181) hide show
  1. package/README.md +156 -118
  2. package/es/analyze/__tests__/colors.test.d.ts +1 -0
  3. package/es/analyze/__tests__/colors.test.js +44 -0
  4. package/es/analyze/__tests__/colors.test.js.map +1 -0
  5. package/es/analyze/__tests__/paths.test.d.ts +1 -0
  6. package/es/analyze/__tests__/paths.test.js +71 -0
  7. package/es/analyze/__tests__/paths.test.js.map +1 -0
  8. package/es/analyze/colors.d.ts +8 -0
  9. package/es/analyze/colors.js +55 -0
  10. package/es/analyze/colors.js.map +1 -0
  11. package/es/analyze/paths.d.ts +20 -0
  12. package/es/analyze/paths.js +92 -0
  13. package/es/analyze/paths.js.map +1 -0
  14. package/es/compare/__tests__/diff.test.d.ts +1 -0
  15. package/es/compare/__tests__/diff.test.js +64 -0
  16. package/es/compare/__tests__/diff.test.js.map +1 -0
  17. package/es/compare/diff.d.ts +18 -0
  18. package/es/compare/diff.js +70 -0
  19. package/es/compare/diff.js.map +1 -0
  20. package/es/convert/__tests__/base64.test.d.ts +1 -0
  21. package/es/convert/__tests__/base64.test.js +49 -0
  22. package/es/convert/__tests__/base64.test.js.map +1 -0
  23. package/es/convert/base64.d.ts +11 -0
  24. package/es/convert/base64.js +38 -0
  25. package/es/convert/base64.js.map +1 -0
  26. package/es/convert/image.d.ts +18 -0
  27. package/es/convert/image.js +63 -0
  28. package/es/convert/image.js.map +1 -0
  29. package/es/core/__tests__/dimensions.test.d.ts +1 -0
  30. package/es/core/__tests__/dimensions.test.js +40 -0
  31. package/es/core/__tests__/dimensions.test.js.map +1 -0
  32. package/es/core/__tests__/element.test.d.ts +1 -0
  33. package/es/core/__tests__/element.test.js +79 -0
  34. package/es/core/__tests__/element.test.js.map +1 -0
  35. package/es/core/dimensions.d.ts +8 -0
  36. package/es/core/dimensions.js +34 -0
  37. package/es/core/dimensions.js.map +1 -0
  38. package/es/core/element.d.ts +24 -0
  39. package/es/core/element.js +52 -0
  40. package/es/core/element.js.map +1 -0
  41. package/es/index.d.ts +15 -10
  42. package/es/index.js +21 -10
  43. package/es/index.js.map +1 -1
  44. package/es/optimize/__tests__/cleanup.test.d.ts +1 -0
  45. package/es/optimize/__tests__/cleanup.test.js +95 -0
  46. package/es/optimize/__tests__/cleanup.test.js.map +1 -0
  47. package/es/optimize/__tests__/path.test.d.ts +1 -0
  48. package/es/optimize/__tests__/path.test.js +41 -0
  49. package/es/optimize/__tests__/path.test.js.map +1 -0
  50. package/es/optimize/cleanup.d.ts +27 -0
  51. package/es/optimize/cleanup.js +100 -0
  52. package/es/optimize/cleanup.js.map +1 -0
  53. package/es/optimize/path.d.ts +7 -0
  54. package/es/optimize/path.js +58 -0
  55. package/es/optimize/path.js.map +1 -0
  56. package/es/types/index.d.ts +31 -0
  57. package/es/types/index.js +5 -0
  58. package/es/types/index.js.map +1 -0
  59. package/es/utils/__tests__/path-validation.test.d.ts +1 -0
  60. package/es/utils/__tests__/path-validation.test.js +64 -0
  61. package/es/utils/__tests__/path-validation.test.js.map +1 -0
  62. package/es/utils/__tests__/validation.test.d.ts +1 -0
  63. package/es/utils/__tests__/validation.test.js +42 -0
  64. package/es/utils/__tests__/validation.test.js.map +1 -0
  65. package/es/utils/dom.d.ts +23 -0
  66. package/es/utils/dom.js +25 -0
  67. package/es/utils/dom.js.map +1 -0
  68. package/es/utils/path-validation.d.ts +29 -0
  69. package/es/utils/path-validation.js +101 -0
  70. package/es/utils/path-validation.js.map +1 -0
  71. package/es/utils/validation.d.ts +11 -0
  72. package/es/utils/validation.js +30 -0
  73. package/es/utils/validation.js.map +1 -0
  74. package/lib/analyze/__tests__/colors.test.d.ts +1 -0
  75. package/lib/analyze/__tests__/colors.test.js +46 -0
  76. package/lib/analyze/__tests__/colors.test.js.map +1 -0
  77. package/lib/analyze/__tests__/paths.test.d.ts +1 -0
  78. package/lib/analyze/__tests__/paths.test.js +73 -0
  79. package/lib/analyze/__tests__/paths.test.js.map +1 -0
  80. package/lib/analyze/colors.d.ts +8 -0
  81. package/lib/analyze/colors.js +58 -0
  82. package/lib/analyze/colors.js.map +1 -0
  83. package/lib/analyze/paths.d.ts +20 -0
  84. package/lib/analyze/paths.js +97 -0
  85. package/lib/analyze/paths.js.map +1 -0
  86. package/lib/compare/__tests__/diff.test.d.ts +1 -0
  87. package/lib/compare/__tests__/diff.test.js +128 -0
  88. package/lib/compare/__tests__/diff.test.js.map +1 -0
  89. package/lib/compare/diff.d.ts +18 -0
  90. package/lib/compare/diff.js +134 -0
  91. package/lib/compare/diff.js.map +1 -0
  92. package/lib/convert/__tests__/base64.test.d.ts +1 -0
  93. package/lib/convert/__tests__/base64.test.js +51 -0
  94. package/lib/convert/__tests__/base64.test.js.map +1 -0
  95. package/lib/convert/base64.d.ts +11 -0
  96. package/lib/convert/base64.js +42 -0
  97. package/lib/convert/base64.js.map +1 -0
  98. package/lib/convert/image.d.ts +18 -0
  99. package/lib/{extra-apply/applySvg2Png.js → convert/image.js} +62 -53
  100. package/lib/convert/image.js.map +1 -0
  101. package/lib/core/__tests__/dimensions.test.d.ts +1 -0
  102. package/lib/core/__tests__/dimensions.test.js +42 -0
  103. package/lib/core/__tests__/dimensions.test.js.map +1 -0
  104. package/lib/core/__tests__/element.test.d.ts +1 -0
  105. package/lib/core/__tests__/element.test.js +81 -0
  106. package/lib/core/__tests__/element.test.js.map +1 -0
  107. package/lib/core/dimensions.d.ts +8 -0
  108. package/lib/core/dimensions.js +37 -0
  109. package/lib/core/dimensions.js.map +1 -0
  110. package/lib/core/element.d.ts +24 -0
  111. package/lib/core/element.js +58 -0
  112. package/lib/core/element.js.map +1 -0
  113. package/lib/index.d.ts +15 -10
  114. package/lib/index.js +48 -20
  115. package/lib/index.js.map +1 -1
  116. package/lib/optimize/__tests__/cleanup.test.d.ts +1 -0
  117. package/lib/optimize/__tests__/cleanup.test.js +97 -0
  118. package/lib/optimize/__tests__/cleanup.test.js.map +1 -0
  119. package/lib/optimize/__tests__/path.test.d.ts +1 -0
  120. package/lib/optimize/__tests__/path.test.js +43 -0
  121. package/lib/optimize/__tests__/path.test.js.map +1 -0
  122. package/lib/optimize/cleanup.d.ts +27 -0
  123. package/lib/optimize/cleanup.js +106 -0
  124. package/lib/optimize/cleanup.js.map +1 -0
  125. package/lib/optimize/path.d.ts +7 -0
  126. package/lib/optimize/path.js +63 -0
  127. package/lib/optimize/path.js.map +1 -0
  128. package/lib/types/index.d.ts +31 -0
  129. package/lib/types/index.js +6 -0
  130. package/lib/types/index.js.map +1 -0
  131. package/lib/utils/__tests__/path-validation.test.d.ts +1 -0
  132. package/lib/utils/__tests__/path-validation.test.js +69 -0
  133. package/lib/utils/__tests__/path-validation.test.js.map +1 -0
  134. package/lib/utils/__tests__/validation.test.d.ts +1 -0
  135. package/lib/utils/__tests__/validation.test.js +44 -0
  136. package/lib/utils/__tests__/validation.test.js.map +1 -0
  137. package/lib/utils/dom.d.ts +23 -0
  138. package/lib/utils/dom.js +31 -0
  139. package/lib/utils/dom.js.map +1 -0
  140. package/lib/utils/path-validation.d.ts +29 -0
  141. package/lib/utils/path-validation.js +109 -0
  142. package/lib/utils/path-validation.js.map +1 -0
  143. package/lib/utils/validation.d.ts +11 -0
  144. package/lib/utils/validation.js +35 -0
  145. package/lib/utils/validation.js.map +1 -0
  146. package/package.json +17 -4
  147. package/es/common.d.ts +0 -68
  148. package/es/common.js +0 -110
  149. package/es/common.js.map +0 -1
  150. package/es/extra-apply/applyDiffSvg.d.ts +0 -11
  151. package/es/extra-apply/applyDiffSvg.js +0 -63
  152. package/es/extra-apply/applyDiffSvg.js.map +0 -1
  153. package/es/extra-apply/applyRemoveNanCoordinates.d.ts +0 -6
  154. package/es/extra-apply/applyRemoveNanCoordinates.js +0 -75
  155. package/es/extra-apply/applyRemoveNanCoordinates.js.map +0 -1
  156. package/es/extra-apply/applySvg2Png.d.ts +0 -16
  157. package/es/extra-apply/applySvg2Png.js +0 -64
  158. package/es/extra-apply/applySvg2Png.js.map +0 -1
  159. package/es/utils/pixelLevelDiffPng.d.ts +0 -4
  160. package/es/utils/pixelLevelDiffPng.js +0 -29
  161. package/es/utils/pixelLevelDiffPng.js.map +0 -1
  162. package/es/validate.d.ts +0 -14
  163. package/es/validate.js +0 -44
  164. package/es/validate.js.map +0 -1
  165. package/lib/common.d.ts +0 -68
  166. package/lib/common.js +0 -117
  167. package/lib/common.js.map +0 -1
  168. package/lib/extra-apply/applyDiffSvg.d.ts +0 -11
  169. package/lib/extra-apply/applyDiffSvg.js +0 -116
  170. package/lib/extra-apply/applyDiffSvg.js.map +0 -1
  171. package/lib/extra-apply/applyRemoveNanCoordinates.d.ts +0 -6
  172. package/lib/extra-apply/applyRemoveNanCoordinates.js +0 -78
  173. package/lib/extra-apply/applyRemoveNanCoordinates.js.map +0 -1
  174. package/lib/extra-apply/applySvg2Png.d.ts +0 -16
  175. package/lib/extra-apply/applySvg2Png.js.map +0 -1
  176. package/lib/utils/pixelLevelDiffPng.d.ts +0 -4
  177. package/lib/utils/pixelLevelDiffPng.js +0 -36
  178. package/lib/utils/pixelLevelDiffPng.js.map +0 -1
  179. package/lib/validate.d.ts +0 -14
  180. package/lib/validate.js +0 -48
  181. package/lib/validate.js.map +0 -1
@@ -0,0 +1,92 @@
1
+ /**
2
+ * SVG path analysis utilities
3
+ */
4
+ import { JSDOM } from 'jsdom';
5
+ import { serializeSVG } from '../core/element';
6
+ import { isValidSvgString } from '../utils/validation';
7
+ /**
8
+ * Maximum allowed path data length to prevent ReDoS attacks
9
+ */
10
+ const MAX_PATH_DATA_LENGTH = 100000; // 100KB should be sufficient for most SVG paths
11
+ /**
12
+ * Parses the 'd' attribute of a path element into command objects
13
+ */
14
+ export function parsePathData(pathData) {
15
+ // Validate input length to prevent ReDoS attacks
16
+ if (pathData.length > MAX_PATH_DATA_LENGTH) {
17
+ throw new Error(`Path data length exceeds maximum allowed length of ${MAX_PATH_DATA_LENGTH} characters`);
18
+ }
19
+ const commands = [];
20
+ // Split by path commands (M, L, H, V, C, S, Q, T, A, Z)
21
+ const commandRegex = /([MmLlHhVvCcSsQqTtAaZz])((?:\s*-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\s*,?\s*)*)/g;
22
+ let match;
23
+ let iterations = 0;
24
+ const MAX_ITERATIONS = 10000; // Prevent infinite loops
25
+ while ((match = commandRegex.exec(pathData)) !== null) {
26
+ iterations++;
27
+ if (iterations > MAX_ITERATIONS) {
28
+ throw new Error('Path parsing exceeded maximum iterations. Path data may be malformed.');
29
+ }
30
+ const type = match[1];
31
+ const paramsStr = match[2].trim();
32
+ if (type.toLowerCase() === 'z') {
33
+ commands.push({ type, params: [] });
34
+ }
35
+ else if (paramsStr) {
36
+ const params = paramsStr
37
+ .split(/[\s,]+/)
38
+ .filter(p => p.trim() !== '')
39
+ .map(Number)
40
+ .filter(n => !isNaN(n));
41
+ commands.push({ type, params });
42
+ }
43
+ }
44
+ return commands;
45
+ }
46
+ /**
47
+ * Analyzes all paths in an SVG and returns their command structures
48
+ */
49
+ export function analyzePaths(svgContent) {
50
+ const svgString = isValidSvgString(svgContent)
51
+ ? svgContent
52
+ : serializeSVG(svgContent);
53
+ const dom = new JSDOM(svgString, {
54
+ contentType: 'image/svg+xml'
55
+ });
56
+ const document = dom.window.document;
57
+ const svgElement = document.querySelector('svg');
58
+ if (!svgElement) {
59
+ throw new Error('No SVG element found in the provided content.');
60
+ }
61
+ const paths = svgElement.querySelectorAll('path');
62
+ const pathAnalysis = new Map();
63
+ Array.from(paths).forEach((path, index) => {
64
+ const d = path.getAttribute('d');
65
+ if (d) {
66
+ const id = path.getAttribute('id') || `path-${index}`;
67
+ pathAnalysis.set(id, parsePathData(d));
68
+ }
69
+ });
70
+ return pathAnalysis;
71
+ }
72
+ /**
73
+ * Gets statistics about paths in an SVG
74
+ */
75
+ export function getPathStatistics(svgContent) {
76
+ const pathAnalysis = analyzePaths(svgContent);
77
+ let totalCommands = 0;
78
+ const commandTypes = {};
79
+ pathAnalysis.forEach(commands => {
80
+ totalCommands += commands.length;
81
+ commands.forEach(cmd => {
82
+ const type = cmd.type.toUpperCase();
83
+ commandTypes[type] = (commandTypes[type] || 0) + 1;
84
+ });
85
+ });
86
+ return {
87
+ totalPaths: pathAnalysis.size,
88
+ totalCommands,
89
+ commandTypes
90
+ };
91
+ }
92
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/analyze/paths.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,64 @@
1
+ import { diffImages } from '../diff';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ describe('Image Diff', () => {
5
+ const fixturesDir = path.join(__dirname, '../../../test-fixtures');
6
+ const test1Path = path.join(fixturesDir, 'test1.svg');
7
+ const test2Path = path.join(fixturesDir, 'test2.svg');
8
+ const outputDir = path.join(fixturesDir, 'diff-output');
9
+ const diffImagePath = path.join(outputDir, 'diff-test1-test2.png');
10
+ beforeAll(() => {
11
+ // Ensure output directory exists
12
+ if (!fs.existsSync(outputDir)) {
13
+ fs.mkdirSync(outputDir, { recursive: true });
14
+ }
15
+ });
16
+ afterAll(() => {
17
+ // Clean up: optionally remove the diff image after tests
18
+ // Uncomment the following line if you want to clean up after tests
19
+ // if (fs.existsSync(diffImagePath)) {
20
+ // fs.unlinkSync(diffImagePath);
21
+ // }
22
+ });
23
+ it('should generate diff image between two similar but different SVGs', async () => {
24
+ // Verify test fixtures exist
25
+ expect(fs.existsSync(test1Path)).toBe(true);
26
+ expect(fs.existsSync(test2Path)).toBe(true);
27
+ // Generate diff image
28
+ const result = await diffImages(test1Path, test2Path, diffImagePath, 0.1);
29
+ // Verify diff image was created
30
+ expect(fs.existsSync(diffImagePath)).toBe(true);
31
+ // Verify result contains expected properties
32
+ expect(result).toHaveProperty('diffPngBuffer');
33
+ expect(result).toHaveProperty('numDiffPixels');
34
+ expect(result.diffPngBuffer).toBeInstanceOf(Buffer);
35
+ expect(typeof result.numDiffPixels).toBe('number');
36
+ // Since the SVGs are different, there should be some differences
37
+ expect(result.numDiffPixels).toBeGreaterThan(0);
38
+ // Verify the saved file matches the buffer
39
+ const savedFile = fs.readFileSync(diffImagePath);
40
+ expect(savedFile.equals(result.diffPngBuffer)).toBe(true);
41
+ // Verify file size is reasonable (not empty)
42
+ expect(savedFile.length).toBeGreaterThan(0);
43
+ console.log(`Diff image saved to: ${diffImagePath}`);
44
+ console.log(`Number of different pixels: ${result.numDiffPixels}`);
45
+ });
46
+ it('should handle identical SVGs (no differences)', async () => {
47
+ const identicalDiffPath = path.join(outputDir, 'diff-identical.png');
48
+ // Compare test1 with itself
49
+ const result = await diffImages(test1Path, test1Path, identicalDiffPath, 0.1);
50
+ // Should have no differences
51
+ expect(result.numDiffPixels).toBe(0);
52
+ expect(fs.existsSync(identicalDiffPath)).toBe(true);
53
+ console.log(`Identical comparison diff saved to: ${identicalDiffPath}`);
54
+ console.log(`Number of different pixels: ${result.numDiffPixels}`);
55
+ });
56
+ it('should generate diff without saving to file', async () => {
57
+ const result = await diffImages(test1Path, test2Path, undefined, 0.1);
58
+ // Should still return result even without file path
59
+ expect(result).toHaveProperty('diffPngBuffer');
60
+ expect(result).toHaveProperty('numDiffPixels');
61
+ expect(result.numDiffPixels).toBeGreaterThan(0);
62
+ });
63
+ });
64
+ //# sourceMappingURL=diff.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.test.js","sourceRoot":"","sources":["../../../src/compare/__tests__/diff.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Image comparison and diff utilities
3
+ */
4
+ import { DiffResult } from '../types';
5
+ /**
6
+ * Compares two PNG images at the pixel level and generates a diff image
7
+ */
8
+ export declare function pixelLevelDiff(pngA: Buffer, pngB: Buffer, threshold?: number): DiffResult;
9
+ /**
10
+ * Compares two image files (SVG or PNG) and generates a diff image
11
+ */
12
+ export declare function diffImages(pathA: string, pathB: string, diffFilePath?: string, threshold?: number): Promise<DiffResult>;
13
+ /**
14
+ * Legacy function name for diffImages
15
+ *
16
+ * @deprecated Use diffImages instead
17
+ */
18
+ export declare function diffSvg(pathA: string, pathB: string, diffFilePath: string): Promise<DiffResult | void>;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Image comparison and diff utilities
3
+ */
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+ import sharp from 'sharp';
7
+ import { PNG } from 'pngjs';
8
+ import pixelmatch from 'pixelmatch';
9
+ import { validateReadPath, validateWritePath } from '../utils/path-validation';
10
+ /**
11
+ * Compares two PNG images at the pixel level and generates a diff image
12
+ */
13
+ export function pixelLevelDiff(pngA, pngB, threshold = 0.1) {
14
+ const img1 = PNG.sync.read(pngA);
15
+ const img2 = PNG.sync.read(pngB);
16
+ const { width, height } = img1;
17
+ if (img2.width !== width || img2.height !== height) {
18
+ throw new Error('Images must have the same dimensions');
19
+ }
20
+ const diff = new PNG({ width, height });
21
+ const numDiffPixels = pixelmatch(img1.data, img2.data, diff.data, width, height, { threshold });
22
+ const diffPngBuffer = PNG.sync.write(diff);
23
+ return {
24
+ diffPngBuffer,
25
+ numDiffPixels
26
+ };
27
+ }
28
+ /**
29
+ * Compares two image files (SVG or PNG) and generates a diff image
30
+ */
31
+ export async function diffImages(pathA, pathB, diffFilePath, threshold = 0.1) {
32
+ // Validate and normalize file paths to prevent path traversal
33
+ const validatedPathA = validateReadPath(pathA, ['.svg', '.png', '.jpg', '.jpeg', '.webp']);
34
+ const validatedPathB = validateReadPath(pathB, ['.svg', '.png', '.jpg', '.jpeg', '.webp']);
35
+ if (!fs.existsSync(validatedPathA)) {
36
+ throw new Error(`File not found: ${validatedPathA}`);
37
+ }
38
+ if (!fs.existsSync(validatedPathB)) {
39
+ throw new Error(`File not found: ${validatedPathB}`);
40
+ }
41
+ // Convert both images to PNG buffers
42
+ const pngA = await sharp(validatedPathA).png().toBuffer();
43
+ const pngB = await sharp(validatedPathB).png().toBuffer();
44
+ const result = pixelLevelDiff(pngA, pngB, threshold);
45
+ if (diffFilePath) {
46
+ const ext = path.extname(diffFilePath);
47
+ if (!ext) {
48
+ throw new Error('Diff file path must have a file extension');
49
+ }
50
+ // Validate write path to prevent path traversal
51
+ const validatedWritePath = validateWritePath(diffFilePath, ['.png']);
52
+ fs.writeFileSync(validatedWritePath, result.diffPngBuffer);
53
+ }
54
+ return result;
55
+ }
56
+ /**
57
+ * Legacy function name for diffImages
58
+ *
59
+ * @deprecated Use diffImages instead
60
+ */
61
+ export async function diffSvg(pathA, pathB, diffFilePath) {
62
+ try {
63
+ return await diffImages(pathA, pathB, diffFilePath);
64
+ }
65
+ catch (error) {
66
+ console.error(`Error comparing images: ${error instanceof Error ? error.message : String(error)}`);
67
+ return;
68
+ }
69
+ }
70
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/compare/diff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,49 @@
1
+ import { convertSVGToBase64, convertBase64ToSVG } from '../base64';
2
+ import { createSVGElement } from '../../core/element';
3
+ describe('Base64 Conversion', () => {
4
+ const testSvg = '<svg viewBox="0 0 100 100"><circle cx="50" cy="50" r="40" /></svg>';
5
+ describe('convertSVGToBase64', () => {
6
+ it('should convert SVG string to base64 data URI', () => {
7
+ const base64 = convertSVGToBase64(testSvg);
8
+ expect(base64).toMatch(/^data:image\/svg\+xml;base64,/);
9
+ expect(base64.length).toBeGreaterThan(50);
10
+ });
11
+ it('should convert SVG element to base64 data URI', () => {
12
+ const element = createSVGElement(testSvg);
13
+ const base64 = convertSVGToBase64(element);
14
+ expect(base64).toMatch(/^data:image\/svg\+xml;base64,/);
15
+ });
16
+ it('should produce consistent results for same input', () => {
17
+ const base64_1 = convertSVGToBase64(testSvg);
18
+ const base64_2 = convertSVGToBase64(testSvg);
19
+ expect(base64_1).toBe(base64_2);
20
+ });
21
+ });
22
+ describe('convertBase64ToSVG', () => {
23
+ it('should convert base64 data URI back to SVG string', () => {
24
+ const base64 = convertSVGToBase64(testSvg);
25
+ const svgString = convertBase64ToSVG(base64);
26
+ expect(svgString).toContain('<svg');
27
+ expect(svgString).toContain('circle');
28
+ });
29
+ it('should handle base64 string without data URI prefix', () => {
30
+ const base64 = convertSVGToBase64(testSvg);
31
+ const base64Only = base64.split(',')[1];
32
+ const svgString = convertBase64ToSVG(base64Only);
33
+ expect(svgString).toContain('<svg');
34
+ });
35
+ it('should throw error for invalid base64 string', () => {
36
+ expect(() => convertBase64ToSVG('invalid-base64!!!')).toThrow('Invalid Base64 string');
37
+ });
38
+ it('should round-trip correctly', () => {
39
+ const original = testSvg;
40
+ const base64 = convertSVGToBase64(original);
41
+ const converted = convertBase64ToSVG(base64);
42
+ // Remove whitespace differences for comparison
43
+ const normalize = (s) => s.replace(/\s+/g, ' ').trim();
44
+ expect(normalize(converted)).toContain('<svg');
45
+ expect(normalize(converted)).toContain('circle');
46
+ });
47
+ });
48
+ });
49
+ //# sourceMappingURL=base64.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64.test.js","sourceRoot":"","sources":["../../../src/convert/__tests__/base64.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Base64 encoding/decoding utilities for SVG
3
+ */
4
+ /**
5
+ * Converts an SVG element or SVG string to a Base64-encoded data URI
6
+ */
7
+ export declare function convertSVGToBase64(svgContent: Element | string): string;
8
+ /**
9
+ * Converts a Base64-encoded data URI back to an SVG string
10
+ */
11
+ export declare function convertBase64ToSVG(base64String: string): string;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Base64 encoding/decoding utilities for SVG
3
+ */
4
+ import { serializeSVG } from '../core/element';
5
+ /**
6
+ * Converts an SVG element or SVG string to a Base64-encoded data URI
7
+ */
8
+ export function convertSVGToBase64(svgContent) {
9
+ const svgString = serializeSVG(svgContent);
10
+ return `data:image/svg+xml;base64,${Buffer.from(svgString).toString('base64')}`;
11
+ }
12
+ /**
13
+ * Converts a Base64-encoded data URI back to an SVG string
14
+ */
15
+ export function convertBase64ToSVG(base64String) {
16
+ try {
17
+ const base64Data = base64String.includes(',')
18
+ ? base64String.split(',')[1]
19
+ : base64String;
20
+ // Validate base64 string format
21
+ if (!/^[A-Za-z0-9+/]*={0,2}$/.test(base64Data)) {
22
+ throw new Error('Invalid Base64 string.');
23
+ }
24
+ const svgString = Buffer.from(base64Data, 'base64').toString('utf-8');
25
+ // Basic validation that result looks like SVG
26
+ if (!svgString.includes('<svg') && !svgString.includes('<SVG')) {
27
+ throw new Error('Invalid Base64 string.');
28
+ }
29
+ return svgString;
30
+ }
31
+ catch (error) {
32
+ if (error instanceof Error && error.message === 'Invalid Base64 string.') {
33
+ throw error;
34
+ }
35
+ throw new Error('Invalid Base64 string.');
36
+ }
37
+ }
38
+ //# sourceMappingURL=base64.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64.js","sourceRoot":"","sources":["../../src/convert/base64.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,UAA4B;IAC7D,MAAM,SAAS,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;IAC3C,OAAO,6BAA6B,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;AAClF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,YAAoB;IACrD,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC;YAC3C,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAC5B,CAAC,CAAC,YAAY,CAAC;QAEjB,gCAAgC;QAChC,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAC/C,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC5C,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAEtE,8CAA8C;QAC9C,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC/D,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC5C,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,KAAK,wBAAwB,EAAE,CAAC;YACzE,MAAM,KAAK,CAAC;QACd,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * SVG to image format conversion utilities
3
+ */
4
+ import { ConversionOptions } from '../types';
5
+ /**
6
+ * Converts an SVG file to PNG format
7
+ *
8
+ * @param svgPath - Path to the SVG file
9
+ * @param options - Conversion options including scale, format, and quality
10
+ * @returns Promise resolving to a Buffer containing the converted image
11
+ */
12
+ export declare function svgToImage(svgPath: string, options?: ConversionOptions): Promise<Buffer>;
13
+ /**
14
+ * Converts an SVG file to PNG format (legacy function name)
15
+ *
16
+ * @deprecated Use svgToImage instead
17
+ */
18
+ export declare function svg2Png(svgPath: string, pngPath?: string, scale?: number): Promise<void | Buffer>;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * SVG to image format conversion utilities
3
+ */
4
+ import fs from 'fs';
5
+ import sharp from 'sharp';
6
+ import { getSVGDimensions } from '../core/dimensions';
7
+ import { validateReadPath, validateWritePath } from '../utils/path-validation';
8
+ /**
9
+ * Converts an SVG file to PNG format
10
+ *
11
+ * @param svgPath - Path to the SVG file
12
+ * @param options - Conversion options including scale, format, and quality
13
+ * @returns Promise resolving to a Buffer containing the converted image
14
+ */
15
+ export async function svgToImage(svgPath, options = {}) {
16
+ const { scale = 2, format = 'png', quality = 90 } = options;
17
+ // Validate and normalize the file path to prevent path traversal
18
+ const validatedPath = validateReadPath(svgPath, ['.svg']);
19
+ if (!fs.existsSync(validatedPath)) {
20
+ throw new Error(`SVG file not found: ${validatedPath}`);
21
+ }
22
+ const svgContent = fs.readFileSync(validatedPath, 'utf8');
23
+ const dimensions = getSVGDimensions(svgContent);
24
+ if (!dimensions.width || !dimensions.height) {
25
+ throw new Error('SVG must have width and height or viewBox');
26
+ }
27
+ const width = scale * dimensions.width;
28
+ const height = scale * dimensions.height;
29
+ let sharpInstance = sharp(validatedPath).resize(width, height);
30
+ switch (format) {
31
+ case 'jpg':
32
+ case 'jpeg':
33
+ sharpInstance = sharpInstance.jpeg({ quality });
34
+ break;
35
+ case 'webp':
36
+ sharpInstance = sharpInstance.webp({ quality });
37
+ break;
38
+ case 'png':
39
+ default:
40
+ sharpInstance = sharpInstance.png();
41
+ break;
42
+ }
43
+ return sharpInstance.toBuffer();
44
+ }
45
+ /**
46
+ * Converts an SVG file to PNG format (legacy function name)
47
+ *
48
+ * @deprecated Use svgToImage instead
49
+ */
50
+ export async function svg2Png(svgPath, pngPath, scale) {
51
+ if (!svgPath) {
52
+ throw new Error('SVG file path is required');
53
+ }
54
+ const buffer = await svgToImage(svgPath, { scale: scale ?? 2, format: 'png' });
55
+ if (pngPath) {
56
+ // Validate write path to prevent path traversal
57
+ const validatedWritePath = validateWritePath(pngPath, ['.png']);
58
+ fs.writeFileSync(validatedWritePath, buffer);
59
+ return;
60
+ }
61
+ return buffer;
62
+ }
63
+ //# sourceMappingURL=image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.js","sourceRoot":"","sources":["../../src/convert/image.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE/E;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAe,EACf,UAA6B,EAAE;IAE/B,MAAM,EAAE,KAAK,GAAG,CAAC,EAAE,MAAM,GAAG,KAAK,EAAE,OAAO,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC;IAE5D,iEAAiE;IACjE,MAAM,aAAa,GAAG,gBAAgB,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAE1D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,uBAAuB,aAAa,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,UAAU,GAAG,EAAE,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAEhD,IAAI,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC/D,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC;IACvC,MAAM,MAAM,GAAG,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC;IAEzC,IAAI,aAAa,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAE/D,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,KAAK,CAAC;QACX,KAAK,MAAM;YACT,aAAa,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;YAChD,MAAM;QACR,KAAK,MAAM;YACT,aAAa,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;YAChD,MAAM;QACR,KAAK,KAAK,CAAC;QACX;YACE,aAAa,GAAG,aAAa,CAAC,GAAG,EAAE,CAAC;YACpC,MAAM;IACV,CAAC;IAED,OAAO,aAAa,CAAC,QAAQ,EAAE,CAAC;AAClC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,OAAe,EACf,OAAgB,EAChB,KAAc;IAEd,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAE/E,IAAI,OAAO,EAAE,CAAC;QACZ,gDAAgD;QAChD,MAAM,kBAAkB,GAAG,iBAAiB,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;QAChE,EAAE,CAAC,aAAa,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;QAC7C,OAAO;IACT,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,40 @@
1
+ import { getSVGDimensions } from '../dimensions';
2
+ import { createSVGElement } from '../element';
3
+ describe('SVG Dimensions', () => {
4
+ describe('getSVGDimensions', () => {
5
+ it('should extract dimensions from viewBox', () => {
6
+ const svgString = '<svg viewBox="0 0 100 200"></svg>';
7
+ const dimensions = getSVGDimensions(svgString);
8
+ expect(dimensions.width).toBe(100);
9
+ expect(dimensions.height).toBe(200);
10
+ expect(dimensions.viewBox).toEqual({ x: 0, y: 0, width: 100, height: 200 });
11
+ });
12
+ it('should extract dimensions from width and height attributes', () => {
13
+ const svgString = '<svg width="150" height="250"></svg>';
14
+ const dimensions = getSVGDimensions(svgString);
15
+ expect(dimensions.width).toBe(150);
16
+ expect(dimensions.height).toBe(250);
17
+ });
18
+ it('should prioritize viewBox over width/height', () => {
19
+ const svgString = '<svg viewBox="0 0 100 200" width="150" height="250"></svg>';
20
+ const dimensions = getSVGDimensions(svgString);
21
+ expect(dimensions.width).toBe(100);
22
+ expect(dimensions.height).toBe(200);
23
+ expect(dimensions.viewBox).toEqual({ x: 0, y: 0, width: 100, height: 200 });
24
+ });
25
+ it('should work with SVG element input', () => {
26
+ const svgString = '<svg viewBox="0 0 50 75"></svg>';
27
+ const element = createSVGElement(svgString);
28
+ const dimensions = getSVGDimensions(element);
29
+ expect(dimensions.width).toBe(50);
30
+ expect(dimensions.height).toBe(75);
31
+ });
32
+ it('should handle decimal values', () => {
33
+ const svgString = '<svg viewBox="0 0 100.5 200.75"></svg>';
34
+ const dimensions = getSVGDimensions(svgString);
35
+ expect(dimensions.width).toBe(100.5);
36
+ expect(dimensions.height).toBe(200.75);
37
+ });
38
+ });
39
+ });
40
+ //# sourceMappingURL=dimensions.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dimensions.test.js","sourceRoot":"","sources":["../../../src/core/__tests__/dimensions.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,SAAS,GAAG,mCAAmC,CAAC;YACtD,MAAM,UAAU,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;YAE/C,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACnC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACpC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAC9E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;YACpE,MAAM,SAAS,GAAG,sCAAsC,CAAC;YACzD,MAAM,UAAU,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;YAE/C,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACnC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;YACrD,MAAM,SAAS,GAAG,4DAA4D,CAAC;YAC/E,MAAM,UAAU,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;YAE/C,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACnC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACpC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAC9E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;YAC5C,MAAM,SAAS,GAAG,iCAAiC,CAAC;YACpD,MAAM,OAAO,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;YAC5C,MAAM,UAAU,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;YAE7C,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAClC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;YACtC,MAAM,SAAS,GAAG,wCAAwC,CAAC;YAC3D,MAAM,UAAU,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;YAE/C,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACrC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,79 @@
1
+ import { createSVGElement, cloneSVGElement, mergeSVGElements, serializeSVG } from '../element';
2
+ import { JSDOM } from 'jsdom';
3
+ describe('Element Manipulation', () => {
4
+ const dom = new JSDOM(`<!DOCTYPE html><html><body></body></html>`);
5
+ const { document } = dom.window;
6
+ describe('createSVGElement', () => {
7
+ it('should create an SVG element from a string', () => {
8
+ const svgString = '<svg><circle cx="50" cy="50" r="40" /></svg>';
9
+ const element = createSVGElement(svgString);
10
+ expect(element.tagName.toLowerCase()).toBe('svg');
11
+ expect(element.querySelector('circle')).toBeTruthy();
12
+ });
13
+ it('should handle complex SVG content', () => {
14
+ const svgString = '<svg viewBox="0 0 100 100"><path d="M10 10 L90 90" /></svg>';
15
+ const element = createSVGElement(svgString);
16
+ expect(element.getAttribute('viewBox')).toBe('0 0 100 100');
17
+ expect(element.querySelector('path')).toBeTruthy();
18
+ });
19
+ });
20
+ describe('cloneSVGElement', () => {
21
+ it('should create a deep clone of an SVG element', () => {
22
+ const svgString = '<svg><circle cx="50" cy="50" r="40" /></svg>';
23
+ const original = createSVGElement(svgString);
24
+ const cloned = cloneSVGElement(original);
25
+ expect(cloned.tagName.toLowerCase()).toBe('svg');
26
+ expect(cloned.querySelector('circle')).toBeTruthy();
27
+ expect(cloned).not.toBe(original);
28
+ });
29
+ it('should preserve all attributes when cloning', () => {
30
+ const svgString = '<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><rect x="10" y="10" width="80" height="80" /></svg>';
31
+ const original = createSVGElement(svgString);
32
+ const cloned = cloneSVGElement(original);
33
+ expect(cloned.getAttribute('viewBox')).toBe('0 0 100 100');
34
+ expect(cloned.querySelector('rect')).toBeTruthy();
35
+ });
36
+ });
37
+ describe('mergeSVGElements', () => {
38
+ it('should merge multiple SVG elements into one', () => {
39
+ const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
40
+ circle.setAttribute('cx', '50');
41
+ circle.setAttribute('cy', '50');
42
+ circle.setAttribute('r', '40');
43
+ const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
44
+ rect.setAttribute('x', '10');
45
+ rect.setAttribute('y', '10');
46
+ rect.setAttribute('width', '80');
47
+ rect.setAttribute('height', '80');
48
+ const merged = mergeSVGElements([circle, rect]);
49
+ expect(merged.tagName.toLowerCase()).toBe('svg');
50
+ expect(merged.querySelector('circle')).toBeTruthy();
51
+ expect(merged.querySelector('rect')).toBeTruthy();
52
+ });
53
+ it('should handle empty array', () => {
54
+ const merged = mergeSVGElements([]);
55
+ expect(merged.tagName.toLowerCase()).toBe('svg');
56
+ expect(merged.children.length).toBe(0);
57
+ });
58
+ });
59
+ describe('serializeSVG', () => {
60
+ it('should serialize an SVG element to string', () => {
61
+ const svgString = '<svg><circle cx="50" cy="50" r="40" /></svg>';
62
+ const element = createSVGElement(svgString);
63
+ const serialized = serializeSVG(element);
64
+ expect(typeof serialized).toBe('string');
65
+ expect(serialized).toContain('svg');
66
+ expect(serialized).toContain('circle');
67
+ });
68
+ it('should return string as-is if input is already a string', () => {
69
+ const svgString = '<svg><rect /></svg>';
70
+ const serialized = serializeSVG(svgString);
71
+ expect(serialized).toBe(svgString);
72
+ });
73
+ it('should throw error for invalid input', () => {
74
+ expect(() => serializeSVG(null)).toThrow();
75
+ expect(() => serializeSVG(123)).toThrow();
76
+ });
77
+ });
78
+ });
79
+ //# sourceMappingURL=element.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element.test.js","sourceRoot":"","sources":["../../../src/core/__tests__/element.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/F,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAE9B,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IACnE,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;IAEhC,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;YACpD,MAAM,SAAS,GAAG,8CAA8C,CAAC;YACjE,MAAM,OAAO,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;YAE5C,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAClD,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;QACvD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;YAC3C,MAAM,SAAS,GAAG,6DAA6D,CAAC;YAChF,MAAM,OAAO,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;YAE5C,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAC5D,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;QACrD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;QAC/B,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACtD,MAAM,SAAS,GAAG,8CAA8C,CAAC;YACjE,MAAM,QAAQ,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;YAC7C,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;YAEzC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACjD,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;YACpD,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;YACrD,MAAM,SAAS,GAAG,mHAAmH,CAAC;YACtI,MAAM,QAAQ,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;YAC7C,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;YAEzC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAC3D,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;QACpD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;YACrD,MAAM,MAAM,GAAG,QAAQ,CAAC,eAAe,CAAC,4BAA4B,EAAE,QAAQ,CAAC,CAAC;YAChF,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAChC,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAChC,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YAE/B,MAAM,IAAI,GAAG,QAAQ,CAAC,eAAe,CAAC,4BAA4B,EAAE,MAAM,CAAC,CAAC;YAC5E,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YAC7B,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YAC7B,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YACjC,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YAElC,MAAM,MAAM,GAAG,gBAAgB,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;YAEhD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACjD,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;YACpD,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;QACpD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;YACnC,MAAM,MAAM,GAAG,gBAAgB,CAAC,EAAE,CAAC,CAAC;YACpC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACjD,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;YACnD,MAAM,SAAS,GAAG,8CAA8C,CAAC;YACjE,MAAM,OAAO,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;YAC5C,MAAM,UAAU,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;YAEzC,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACzC,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACpC,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;YACjE,MAAM,SAAS,GAAG,qBAAqB,CAAC;YACxC,MAAM,UAAU,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;YAE3C,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;YAC9C,MAAM,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,IAAW,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;YAClD,MAAM,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,GAAU,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QACnD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * SVG dimensions and viewBox utilities
3
+ */
4
+ import { SVGDimensions } from '../types';
5
+ /**
6
+ * Extracts dimensions from an SVG element or string
7
+ */
8
+ export declare function getSVGDimensions(svgContent: Element | string): SVGDimensions;