svg-toolbox 1.1.10 → 1.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +146 -125
- package/es/analyze/__tests__/colors.test.d.ts +1 -0
- package/es/analyze/__tests__/colors.test.js +44 -0
- package/es/analyze/__tests__/colors.test.js.map +1 -0
- package/es/analyze/__tests__/paths.test.d.ts +1 -0
- package/es/analyze/__tests__/paths.test.js +71 -0
- package/es/analyze/__tests__/paths.test.js.map +1 -0
- package/es/analyze/colors.d.ts +8 -0
- package/es/analyze/colors.js +55 -0
- package/es/analyze/colors.js.map +1 -0
- package/es/analyze/paths.d.ts +20 -0
- package/es/analyze/paths.js +78 -0
- package/es/analyze/paths.js.map +1 -0
- package/es/compare/diff.d.ts +18 -0
- package/es/compare/diff.js +64 -0
- package/es/compare/diff.js.map +1 -0
- package/es/convert/__tests__/base64.test.d.ts +1 -0
- package/es/convert/__tests__/base64.test.js +49 -0
- package/es/convert/__tests__/base64.test.js.map +1 -0
- package/es/convert/base64.d.ts +11 -0
- package/es/convert/base64.js +38 -0
- package/es/convert/base64.js.map +1 -0
- package/es/convert/image.d.ts +18 -0
- package/es/convert/image.js +58 -0
- package/es/convert/image.js.map +1 -0
- package/es/core/__tests__/dimensions.test.d.ts +1 -0
- package/es/core/__tests__/dimensions.test.js +40 -0
- package/es/core/__tests__/dimensions.test.js.map +1 -0
- package/es/core/__tests__/element.test.d.ts +1 -0
- package/es/core/__tests__/element.test.js +79 -0
- package/es/core/__tests__/element.test.js.map +1 -0
- package/es/core/dimensions.d.ts +8 -0
- package/es/core/dimensions.js +34 -0
- package/es/core/dimensions.js.map +1 -0
- package/es/core/element.d.ts +24 -0
- package/es/core/element.js +52 -0
- package/es/core/element.js.map +1 -0
- package/es/index.d.ts +15 -10
- package/es/index.js +21 -10
- package/es/index.js.map +1 -1
- package/es/optimize/__tests__/cleanup.test.d.ts +1 -0
- package/es/optimize/__tests__/cleanup.test.js +66 -0
- package/es/optimize/__tests__/cleanup.test.js.map +1 -0
- package/es/optimize/__tests__/path.test.d.ts +1 -0
- package/es/optimize/__tests__/path.test.js +41 -0
- package/es/optimize/__tests__/path.test.js.map +1 -0
- package/es/optimize/cleanup.d.ts +19 -0
- package/es/optimize/cleanup.js +63 -0
- package/es/optimize/cleanup.js.map +1 -0
- package/es/optimize/path.d.ts +7 -0
- package/es/optimize/path.js +58 -0
- package/es/optimize/path.js.map +1 -0
- package/es/types/index.d.ts +31 -0
- package/es/types/index.js +5 -0
- package/es/types/index.js.map +1 -0
- package/es/utils/__tests__/validation.test.d.ts +1 -0
- package/es/utils/__tests__/validation.test.js +42 -0
- package/es/utils/__tests__/validation.test.js.map +1 -0
- package/es/utils/dom.d.ts +23 -0
- package/es/utils/dom.js +25 -0
- package/es/utils/dom.js.map +1 -0
- package/es/utils/validation.d.ts +11 -0
- package/es/utils/validation.js +30 -0
- package/es/utils/validation.js.map +1 -0
- package/lib/analyze/__tests__/colors.test.d.ts +1 -0
- package/lib/analyze/__tests__/colors.test.js +46 -0
- package/lib/analyze/__tests__/colors.test.js.map +1 -0
- package/lib/analyze/__tests__/paths.test.d.ts +1 -0
- package/lib/analyze/__tests__/paths.test.js +73 -0
- package/lib/analyze/__tests__/paths.test.js.map +1 -0
- package/lib/analyze/colors.d.ts +8 -0
- package/lib/analyze/colors.js +58 -0
- package/lib/analyze/colors.js.map +1 -0
- package/lib/analyze/paths.d.ts +20 -0
- package/lib/analyze/paths.js +83 -0
- package/lib/analyze/paths.js.map +1 -0
- package/lib/compare/diff.d.ts +18 -0
- package/lib/{extra-apply/applyDiffSvg.js → compare/diff.js} +72 -58
- package/lib/compare/diff.js.map +1 -0
- package/lib/convert/__tests__/base64.test.d.ts +1 -0
- package/lib/convert/__tests__/base64.test.js +51 -0
- package/lib/convert/__tests__/base64.test.js.map +1 -0
- package/lib/convert/base64.d.ts +11 -0
- package/lib/convert/base64.js +42 -0
- package/lib/convert/base64.js.map +1 -0
- package/lib/convert/image.d.ts +18 -0
- package/lib/{extra-apply/applySvg2Png.js → convert/image.js} +59 -53
- package/lib/convert/image.js.map +1 -0
- package/lib/core/__tests__/dimensions.test.d.ts +1 -0
- package/lib/core/__tests__/dimensions.test.js +42 -0
- package/lib/core/__tests__/dimensions.test.js.map +1 -0
- package/lib/core/__tests__/element.test.d.ts +1 -0
- package/lib/core/__tests__/element.test.js +81 -0
- package/lib/core/__tests__/element.test.js.map +1 -0
- package/lib/core/dimensions.d.ts +8 -0
- package/lib/core/dimensions.js +37 -0
- package/lib/core/dimensions.js.map +1 -0
- package/lib/core/element.d.ts +24 -0
- package/lib/core/element.js +58 -0
- package/lib/core/element.js.map +1 -0
- package/lib/index.d.ts +15 -10
- package/lib/index.js +48 -20
- package/lib/index.js.map +1 -1
- package/lib/optimize/__tests__/cleanup.test.d.ts +1 -0
- package/lib/optimize/__tests__/cleanup.test.js +68 -0
- package/lib/optimize/__tests__/cleanup.test.js.map +1 -0
- package/lib/optimize/__tests__/path.test.d.ts +1 -0
- package/lib/optimize/__tests__/path.test.js +43 -0
- package/lib/optimize/__tests__/path.test.js.map +1 -0
- package/lib/optimize/cleanup.d.ts +19 -0
- package/lib/optimize/cleanup.js +69 -0
- package/lib/optimize/cleanup.js.map +1 -0
- package/lib/optimize/path.d.ts +7 -0
- package/lib/optimize/path.js +63 -0
- package/lib/optimize/path.js.map +1 -0
- package/lib/types/index.d.ts +31 -0
- package/lib/types/index.js +6 -0
- package/lib/types/index.js.map +1 -0
- package/lib/utils/__tests__/validation.test.d.ts +1 -0
- package/lib/utils/__tests__/validation.test.js +44 -0
- package/lib/utils/__tests__/validation.test.js.map +1 -0
- package/lib/utils/dom.d.ts +23 -0
- package/lib/utils/dom.js +31 -0
- package/lib/utils/dom.js.map +1 -0
- package/lib/utils/validation.d.ts +11 -0
- package/lib/utils/validation.js +35 -0
- package/lib/utils/validation.js.map +1 -0
- package/package.json +8 -2
- package/es/common.d.ts +0 -68
- package/es/common.js +0 -110
- package/es/common.js.map +0 -1
- package/es/extra-apply/applyDiffSvg.d.ts +0 -11
- package/es/extra-apply/applyDiffSvg.js +0 -63
- package/es/extra-apply/applyDiffSvg.js.map +0 -1
- package/es/extra-apply/applyRemoveNanCoordinates.d.ts +0 -6
- package/es/extra-apply/applyRemoveNanCoordinates.js +0 -75
- package/es/extra-apply/applyRemoveNanCoordinates.js.map +0 -1
- package/es/extra-apply/applySvg2Png.d.ts +0 -16
- package/es/extra-apply/applySvg2Png.js +0 -64
- package/es/extra-apply/applySvg2Png.js.map +0 -1
- package/es/utils/pixelLevelDiffPng.d.ts +0 -4
- package/es/utils/pixelLevelDiffPng.js +0 -29
- package/es/utils/pixelLevelDiffPng.js.map +0 -1
- package/es/validate.d.ts +0 -14
- package/es/validate.js +0 -44
- package/es/validate.js.map +0 -1
- package/lib/common.d.ts +0 -68
- package/lib/common.js +0 -117
- package/lib/common.js.map +0 -1
- package/lib/extra-apply/applyDiffSvg.d.ts +0 -11
- package/lib/extra-apply/applyDiffSvg.js.map +0 -1
- package/lib/extra-apply/applyRemoveNanCoordinates.d.ts +0 -6
- package/lib/extra-apply/applyRemoveNanCoordinates.js +0 -78
- package/lib/extra-apply/applyRemoveNanCoordinates.js.map +0 -1
- package/lib/extra-apply/applySvg2Png.d.ts +0 -16
- package/lib/extra-apply/applySvg2Png.js.map +0 -1
- package/lib/utils/pixelLevelDiffPng.d.ts +0 -4
- package/lib/utils/pixelLevelDiffPng.js +0 -36
- package/lib/utils/pixelLevelDiffPng.js.map +0 -1
- package/lib/validate.d.ts +0 -14
- package/lib/validate.js +0 -48
- package/lib/validate.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,175 +1,196 @@
|
|
|
1
|
-
|
|
2
|
-
<br/>
|
|
3
|
-
<img width="200" alt="image" src="https://github.com/user-attachments/assets/5fb4fbd0-10b4-4abf-93fd-d98b17845f34" />
|
|
4
|
-
<br/>
|
|
5
|
-
</h1>
|
|
1
|
+
# SVG Toolbox
|
|
6
2
|
|
|
3
|
+
一个功能全面的 SVG 操作和分析工具库,提供 SVG 元素的创建、转换、优化、比较和分析等功能。
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/svg-toolbox)
|
|
6
|
+
[](https://www.npmjs.com/package/svg-toolbox)
|
|
7
|
+
[](https://www.npmjs.com/package/svg-toolbox)
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
[](https://www.npmjs.com/package/svg-toolbox)
|
|
9
|
+
## 安装
|
|
13
10
|
|
|
14
|
-
## Installation
|
|
15
11
|
```bash
|
|
16
12
|
npm install svg-toolbox
|
|
17
13
|
```
|
|
18
14
|
|
|
19
|
-
##
|
|
15
|
+
## 适用场景
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
- [Installation](#installation)
|
|
23
|
-
- [Table of Contents](#table-of-contents)
|
|
24
|
-
- [Usage](#usage)
|
|
25
|
-
- [createSVGElement](#createsvgelement)
|
|
26
|
-
- [cloneSVGElement](#clonesvgelement)
|
|
27
|
-
- [mergeSVGElements](#mergesvgelements)
|
|
28
|
-
- [convertSVGToBase64](#convertsvgtobase64)
|
|
29
|
-
- [convertBase64ToSVG](#convertbase64tosvg)
|
|
30
|
-
- [diffSvg](#diffsvg)
|
|
31
|
-
- [svg2png](#svg2png)
|
|
32
|
-
- [removeNanCoordinates](#removenancoordinates)
|
|
33
|
-
- [License](#license)
|
|
17
|
+
### 1. SVG 元素操作场景
|
|
34
18
|
|
|
35
|
-
|
|
19
|
+
当你需要在 Node.js 环境中创建、克隆或合并 SVG 元素时,可以使用核心元素操作功能:
|
|
36
20
|
|
|
37
|
-
|
|
21
|
+
- **动态生成 SVG 图形**:根据数据动态创建 SVG 元素
|
|
22
|
+
- **SVG 模板复用**:克隆现有 SVG 元素作为模板
|
|
23
|
+
- **组合多个 SVG**:将多个独立的 SVG 图形合并为一个
|
|
38
24
|
|
|
39
|
-
|
|
25
|
+
### 2. 格式转换场景
|
|
40
26
|
|
|
41
|
-
|
|
42
|
-
const svgElement = createSVGElement(`<svg><path d="M10 20L30 40Z" /></svg>`);
|
|
43
|
-
console.log(svgElement);
|
|
44
|
-
```
|
|
45
|
-
### cloneSVGElement
|
|
27
|
+
当你需要将 SVG 转换为其他格式或进行编码转换时:
|
|
46
28
|
|
|
47
|
-
|
|
29
|
+
- **Web 应用嵌入**:将 SVG 转换为 Base64 数据 URI,方便嵌入 HTML/CSS
|
|
30
|
+
- **图片导出**:将 SVG 转换为 PNG、JPG 或 WebP 格式用于下载或分享
|
|
31
|
+
- **跨平台兼容**:在不同系统间传输 SVG 数据时使用 Base64 编码
|
|
48
32
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
33
|
+
### 3. 视觉回归测试场景
|
|
34
|
+
|
|
35
|
+
当你需要比较 SVG 渲染结果以确保视觉一致性时:
|
|
36
|
+
|
|
37
|
+
- **自动化测试**:在 CI/CD 流程中检测 SVG 渲染变化
|
|
38
|
+
- **版本对比**:比较不同版本的 SVG 文件差异
|
|
39
|
+
- **质量保证**:确保 SVG 修改不会引入意外的视觉变化
|
|
40
|
+
|
|
41
|
+
### 4. SVG 优化场景
|
|
42
|
+
|
|
43
|
+
当你需要清理和优化 SVG 代码时:
|
|
44
|
+
|
|
45
|
+
- **性能优化**:移除无效坐标和空属性,减小文件大小
|
|
46
|
+
- **代码清理**:移除注释和多余空白,提高可读性
|
|
47
|
+
- **数据修复**:修复包含 NaN 或无效值的路径数据
|
|
48
|
+
|
|
49
|
+
### 5. SVG 分析场景
|
|
50
|
+
|
|
51
|
+
当你需要深入了解 SVG 内容时:
|
|
52
|
+
|
|
53
|
+
- **设计审查**:提取 SVG 中使用的所有颜色
|
|
54
|
+
- **路径分析**:分析路径命令的结构和复杂度
|
|
55
|
+
- **尺寸检测**:自动提取 SVG 的尺寸信息
|
|
56
|
+
|
|
57
|
+
## 功能模块
|
|
58
|
+
|
|
59
|
+
### 核心功能 (Core)
|
|
60
|
+
|
|
61
|
+
提供基础的 SVG 元素操作能力:
|
|
62
|
+
|
|
63
|
+
- `createSVGElement` - 从字符串创建 SVG 元素
|
|
64
|
+
- `cloneSVGElement` - 深度克隆 SVG 元素
|
|
65
|
+
- `mergeSVGElements` - 合并多个 SVG 元素
|
|
66
|
+
- `getSVGDimensions` - 获取 SVG 尺寸信息
|
|
67
|
+
|
|
68
|
+
### 转换功能 (Convert)
|
|
69
|
+
|
|
70
|
+
提供格式转换和编码功能:
|
|
71
|
+
|
|
72
|
+
- `convertSVGToBase64` - SVG 转 Base64 编码
|
|
73
|
+
- `convertBase64ToSVG` - Base64 解码为 SVG
|
|
74
|
+
- `svgToImage` - SVG 转图片格式(PNG/JPG/WebP)
|
|
75
|
+
- `svg2Png` - SVG 转 PNG(兼容旧版 API)
|
|
76
|
+
|
|
77
|
+
### 比较功能 (Compare)
|
|
78
|
+
|
|
79
|
+
提供图像对比和差异检测:
|
|
67
80
|
|
|
68
|
-
|
|
69
|
-
|
|
81
|
+
- `diffImages` - 比较两个图像文件并生成差异图
|
|
82
|
+
- `pixelLevelDiff` - 像素级别的图像差异比较
|
|
83
|
+
- `diffSvg` - SVG 差异比较(兼容旧版 API)
|
|
84
|
+
|
|
85
|
+
### 优化功能 (Optimize)
|
|
86
|
+
|
|
87
|
+
提供 SVG 代码优化和清理:
|
|
88
|
+
|
|
89
|
+
- `removeNanCoordinates` - 移除路径中的 NaN 坐标
|
|
90
|
+
- `removeEmptyAttributes` - 移除空属性
|
|
91
|
+
- `removeComments` - 移除注释
|
|
92
|
+
- `normalizeWhitespace` - 规范化空白字符
|
|
93
|
+
- `optimizeSVG` - 综合优化 SVG 代码
|
|
94
|
+
|
|
95
|
+
### 分析功能 (Analyze)
|
|
96
|
+
|
|
97
|
+
提供 SVG 内容分析:
|
|
98
|
+
|
|
99
|
+
- `extractColors` - 提取 SVG 中使用的颜色
|
|
100
|
+
- `parsePathData` - 解析路径数据
|
|
101
|
+
- `analyzePaths` - 分析所有路径元素
|
|
102
|
+
- `getPathStatistics` - 获取路径统计信息
|
|
103
|
+
|
|
104
|
+
## 使用示例
|
|
105
|
+
|
|
106
|
+
### 基础操作
|
|
70
107
|
|
|
71
108
|
```typescript
|
|
72
|
-
import { mergeSVGElements } from 'svg-toolbox';
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
svgElement1.setAttribute('stroke', 'black');
|
|
83
|
-
svgElement1.setAttribute('stroke-width', '3');
|
|
84
|
-
svgElement1.setAttribute('fill', 'red');
|
|
85
|
-
|
|
86
|
-
const svgElement2 = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
87
|
-
svgElement2.setAttribute('x', '10');
|
|
88
|
-
svgElement2.setAttribute('y', '10');
|
|
89
|
-
svgElement2.setAttribute('width', '100');
|
|
90
|
-
svgElement2.setAttribute('height', '100');
|
|
91
|
-
svgElement2.setAttribute('fill', 'blue');
|
|
92
|
-
|
|
93
|
-
const mergedElement = mergeSVGElements([svgElement1, svgElement2]);
|
|
94
|
-
console.log(mergedElement);
|
|
109
|
+
import { createSVGElement, cloneSVGElement, mergeSVGElements } from 'svg-toolbox';
|
|
110
|
+
|
|
111
|
+
// 创建 SVG 元素
|
|
112
|
+
const svg = createSVGElement('<svg><circle cx="50" cy="50" r="40" /></svg>');
|
|
113
|
+
|
|
114
|
+
// 克隆元素
|
|
115
|
+
const cloned = cloneSVGElement(svg);
|
|
116
|
+
|
|
117
|
+
// 合并多个元素
|
|
118
|
+
const merged = mergeSVGElements([svg, cloned]);
|
|
95
119
|
```
|
|
96
120
|
|
|
97
|
-
###
|
|
98
|
-
Converts an SVG element or SVG string to a Base64-encoded string.
|
|
121
|
+
### 格式转换
|
|
99
122
|
|
|
100
123
|
```typescript
|
|
101
|
-
import {
|
|
102
|
-
|
|
103
|
-
const svgElement = createSVGElement(`<svg><path d="M10 20L30 40Z" /></svg>`);
|
|
124
|
+
import { convertSVGToBase64, svgToImage } from 'svg-toolbox';
|
|
104
125
|
|
|
105
|
-
|
|
106
|
-
|
|
126
|
+
// 转换为 Base64
|
|
127
|
+
const base64 = convertSVGToBase64('<svg>...</svg>');
|
|
107
128
|
|
|
108
|
-
|
|
109
|
-
|
|
129
|
+
// 转换为 PNG
|
|
130
|
+
const pngBuffer = await svgToImage('input.svg', { scale: 2, format: 'png' });
|
|
110
131
|
|
|
111
|
-
|
|
112
|
-
|
|
132
|
+
// 转换为 WebP
|
|
133
|
+
const webpBuffer = await svgToImage('input.svg', { format: 'webp', quality: 90 });
|
|
113
134
|
```
|
|
114
135
|
|
|
115
|
-
###
|
|
116
|
-
Converts a Base64-encoded string back to an SVG string.
|
|
136
|
+
### 图像比较
|
|
117
137
|
|
|
118
138
|
```typescript
|
|
119
|
-
import {
|
|
139
|
+
import { diffImages } from 'svg-toolbox';
|
|
120
140
|
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
console.log(
|
|
141
|
+
// 比较两个图像并生成差异图
|
|
142
|
+
const result = await diffImages('image1.svg', 'image2.svg', 'diff.png');
|
|
143
|
+
console.log(`差异像素数: ${result.numDiffPixels}`);
|
|
124
144
|
```
|
|
125
145
|
|
|
126
|
-
###
|
|
127
|
-
Compares two PNG images and generates a diff image.
|
|
146
|
+
### SVG 优化
|
|
128
147
|
|
|
129
148
|
```typescript
|
|
130
|
-
import {
|
|
149
|
+
import { optimizeSVG, removeNanCoordinates } from 'svg-toolbox';
|
|
131
150
|
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
const diffFilePath = 'path/to/save/diff/image.png';
|
|
151
|
+
// 综合优化
|
|
152
|
+
const optimized = optimizeSVG('<svg><!-- comment --><path d="M 10,20 nan L 30,40" /></svg>');
|
|
135
153
|
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
const { diffPngBuffer, numDiffPixels } = await diffSvg(pathA, pathB, diffFilePath)
|
|
139
|
-
const diffPngBase64 = `data:image/png;base64,${diffPngBuffer.toString('base64')}`;
|
|
140
|
-
console.log(`Number of different pixels: ${numDiffPixels}`);
|
|
154
|
+
// 移除 NaN 坐标
|
|
155
|
+
const cleaned = removeNanCoordinates('<svg><path d="M 10,20 nan L 30,40" /></svg>');
|
|
141
156
|
```
|
|
142
157
|
|
|
143
|
-
###
|
|
144
|
-
Converts an SVG file to PNG format.
|
|
158
|
+
### 内容分析
|
|
145
159
|
|
|
146
160
|
```typescript
|
|
147
|
-
|
|
148
|
-
import { svg2Png } from 'svg-toolbox';
|
|
149
|
-
|
|
150
|
-
const svgPath = 'path/to/input/image.svg';
|
|
151
|
-
const pngPath = 'path/to/output/image.png';
|
|
152
|
-
const scale = 2; // Scaling factor
|
|
161
|
+
import { extractColors, getPathStatistics } from 'svg-toolbox';
|
|
153
162
|
|
|
154
|
-
|
|
163
|
+
// 提取颜色
|
|
164
|
+
const colors = extractColors('<svg><circle fill="red" stroke="blue" /></svg>');
|
|
155
165
|
|
|
156
|
-
//
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
console.log(pngBase64);
|
|
166
|
+
// 获取路径统计
|
|
167
|
+
const stats = getPathStatistics('<svg><path d="M 10,20 L 30,40 Z" /></svg>');
|
|
168
|
+
console.log(`路径数: ${stats.totalPaths}, 命令数: ${stats.totalCommands}`);
|
|
160
169
|
```
|
|
161
170
|
|
|
162
|
-
|
|
163
|
-
Parses and normalizes the d attribute of all path elements in an SVG content.
|
|
171
|
+
## API 文档
|
|
164
172
|
|
|
165
|
-
|
|
166
|
-
|
|
173
|
+
详细的 API 文档请参考 [TypeScript 类型定义](./src/types/index.ts) 和源代码注释。
|
|
174
|
+
|
|
175
|
+
## 开发
|
|
167
176
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
177
|
+
```bash
|
|
178
|
+
# 安装依赖
|
|
179
|
+
npm install
|
|
180
|
+
|
|
181
|
+
# 运行测试
|
|
182
|
+
npm test
|
|
183
|
+
|
|
184
|
+
# 构建项目
|
|
185
|
+
npm run build
|
|
171
186
|
|
|
187
|
+
# 监听模式测试
|
|
188
|
+
npm run test:watch
|
|
189
|
+
|
|
190
|
+
# 生成覆盖率报告
|
|
191
|
+
npm run test:coverage
|
|
172
192
|
```
|
|
173
193
|
|
|
174
|
-
##
|
|
194
|
+
## 许可证
|
|
195
|
+
|
|
175
196
|
MIT License
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { extractColors } from '../colors';
|
|
2
|
+
import { createSVGElement } from '../../core/element';
|
|
3
|
+
describe('Color Analysis', () => {
|
|
4
|
+
describe('extractColors', () => {
|
|
5
|
+
it('should extract fill colors', () => {
|
|
6
|
+
const svgContent = '<svg><circle fill="red" /><rect fill="blue" /></svg>';
|
|
7
|
+
const colors = extractColors(svgContent);
|
|
8
|
+
expect(colors.length).toBeGreaterThan(0);
|
|
9
|
+
expect(colors.some(c => c.fill === 'red')).toBe(true);
|
|
10
|
+
expect(colors.some(c => c.fill === 'blue')).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
it('should extract stroke colors', () => {
|
|
13
|
+
const svgContent = '<svg><path stroke="green" stroke-width="2" /></svg>';
|
|
14
|
+
const colors = extractColors(svgContent);
|
|
15
|
+
expect(colors.some(c => c.stroke === 'green')).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
it('should extract opacity values', () => {
|
|
18
|
+
const svgContent = '<svg><circle fill="red" opacity="0.5" /></svg>';
|
|
19
|
+
const colors = extractColors(svgContent);
|
|
20
|
+
const redColor = colors.find(c => c.fill === 'red');
|
|
21
|
+
expect(redColor?.opacity).toBe(0.5);
|
|
22
|
+
});
|
|
23
|
+
it('should ignore transparent and none values', () => {
|
|
24
|
+
const svgContent = '<svg><circle fill="none" stroke="transparent" /><rect fill="red" /></svg>';
|
|
25
|
+
const colors = extractColors(svgContent);
|
|
26
|
+
expect(colors.some(c => c.fill === 'none')).toBe(false);
|
|
27
|
+
expect(colors.some(c => c.stroke === 'transparent')).toBe(false);
|
|
28
|
+
expect(colors.some(c => c.fill === 'red')).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
it('should work with SVG element input', () => {
|
|
31
|
+
const svgContent = '<svg><circle fill="purple" /></svg>';
|
|
32
|
+
const element = createSVGElement(svgContent);
|
|
33
|
+
const colors = extractColors(element);
|
|
34
|
+
expect(colors.some(c => c.fill === 'purple')).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
it('should handle nested elements', () => {
|
|
37
|
+
const svgContent = '<svg><g><circle fill="red" /><rect fill="blue" /></g></svg>';
|
|
38
|
+
const colors = extractColors(svgContent);
|
|
39
|
+
expect(colors.some(c => c.fill === 'red')).toBe(true);
|
|
40
|
+
expect(colors.some(c => c.fill === 'blue')).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
//# sourceMappingURL=colors.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"colors.test.js","sourceRoot":"","sources":["../../../src/analyze/__tests__/colors.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { parsePathData, analyzePaths, getPathStatistics } from '../paths';
|
|
2
|
+
import { createSVGElement } from '../../core/element';
|
|
3
|
+
describe('Path Analysis', () => {
|
|
4
|
+
describe('parsePathData', () => {
|
|
5
|
+
it('should parse simple path commands', () => {
|
|
6
|
+
const pathData = 'M 10,20 L 30,40 Z';
|
|
7
|
+
const commands = parsePathData(pathData);
|
|
8
|
+
expect(commands.length).toBe(3);
|
|
9
|
+
expect(commands[0].type).toBe('M');
|
|
10
|
+
expect(commands[0].params).toEqual([10, 20]);
|
|
11
|
+
expect(commands[1].type).toBe('L');
|
|
12
|
+
expect(commands[2].type).toBe('Z');
|
|
13
|
+
});
|
|
14
|
+
it('should handle lowercase commands', () => {
|
|
15
|
+
const pathData = 'm 10,20 l 30,40 z';
|
|
16
|
+
const commands = parsePathData(pathData);
|
|
17
|
+
expect(commands.length).toBe(3);
|
|
18
|
+
expect(commands[0].type).toBe('m');
|
|
19
|
+
});
|
|
20
|
+
it('should handle multiple parameters', () => {
|
|
21
|
+
const pathData = 'M 10,20 L 30,40 50,60';
|
|
22
|
+
const commands = parsePathData(pathData);
|
|
23
|
+
expect(commands[1].params.length).toBe(4);
|
|
24
|
+
});
|
|
25
|
+
it('should handle cubic bezier commands', () => {
|
|
26
|
+
const pathData = 'M 10,20 C 20,30 40,50 60,70';
|
|
27
|
+
const commands = parsePathData(pathData);
|
|
28
|
+
expect(commands.length).toBe(2);
|
|
29
|
+
expect(commands[1].type).toBe('C');
|
|
30
|
+
expect(commands[1].params.length).toBe(6);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('analyzePaths', () => {
|
|
34
|
+
it('should analyze all paths in SVG', () => {
|
|
35
|
+
const svgContent = '<svg><path id="path1" d="M 10,20 L 30,40" /><path id="path2" d="M 50,60 L 70,80" /></svg>';
|
|
36
|
+
const analysis = analyzePaths(svgContent);
|
|
37
|
+
expect(analysis.size).toBe(2);
|
|
38
|
+
expect(analysis.has('path1')).toBe(true);
|
|
39
|
+
expect(analysis.has('path2')).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
it('should use index as id if path has no id', () => {
|
|
42
|
+
const svgContent = '<svg><path d="M 10,20" /><path d="M 30,40" /></svg>';
|
|
43
|
+
const analysis = analyzePaths(svgContent);
|
|
44
|
+
expect(analysis.has('path-0')).toBe(true);
|
|
45
|
+
expect(analysis.has('path-1')).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
it('should work with SVG element input', () => {
|
|
48
|
+
const svgContent = '<svg><path d="M 10,20 L 30,40" /></svg>';
|
|
49
|
+
const element = createSVGElement(svgContent);
|
|
50
|
+
const analysis = analyzePaths(element);
|
|
51
|
+
expect(analysis.size).toBe(1);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe('getPathStatistics', () => {
|
|
55
|
+
it('should return correct statistics', () => {
|
|
56
|
+
const svgContent = '<svg><path d="M 10,20 L 30,40 Z" /><path d="M 50,60 L 70,80" /></svg>';
|
|
57
|
+
const stats = getPathStatistics(svgContent);
|
|
58
|
+
expect(stats.totalPaths).toBe(2);
|
|
59
|
+
expect(stats.totalCommands).toBeGreaterThan(0);
|
|
60
|
+
expect(stats.commandTypes['M']).toBeGreaterThan(0);
|
|
61
|
+
expect(stats.commandTypes['L']).toBeGreaterThan(0);
|
|
62
|
+
});
|
|
63
|
+
it('should count command types correctly', () => {
|
|
64
|
+
const svgContent = '<svg><path d="M 10,20 M 30,40 L 50,60 L 70,80" /></svg>';
|
|
65
|
+
const stats = getPathStatistics(svgContent);
|
|
66
|
+
expect(stats.commandTypes['M']).toBe(2);
|
|
67
|
+
expect(stats.commandTypes['L']).toBe(2);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
//# sourceMappingURL=paths.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.test.js","sourceRoot":"","sources":["../../../src/analyze/__tests__/paths.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SVG color analysis utilities
|
|
3
|
+
*/
|
|
4
|
+
import { JSDOM } from 'jsdom';
|
|
5
|
+
import { serializeSVG } from '../core/element';
|
|
6
|
+
import { isValidSvgString } from '../utils/validation';
|
|
7
|
+
/**
|
|
8
|
+
* Extracts all colors used in an SVG
|
|
9
|
+
*/
|
|
10
|
+
export function extractColors(svgContent) {
|
|
11
|
+
const svgString = isValidSvgString(svgContent)
|
|
12
|
+
? svgContent
|
|
13
|
+
: serializeSVG(svgContent);
|
|
14
|
+
const dom = new JSDOM(svgString, {
|
|
15
|
+
contentType: 'image/svg+xml'
|
|
16
|
+
});
|
|
17
|
+
const document = dom.window.document;
|
|
18
|
+
const svgElement = document.querySelector('svg');
|
|
19
|
+
if (!svgElement) {
|
|
20
|
+
throw new Error('No SVG element found in the provided content.');
|
|
21
|
+
}
|
|
22
|
+
const colors = [];
|
|
23
|
+
const colorSet = new Set();
|
|
24
|
+
function extractFromElement(element) {
|
|
25
|
+
const fill = element.getAttribute('fill');
|
|
26
|
+
const stroke = element.getAttribute('stroke');
|
|
27
|
+
const opacity = element.getAttribute('opacity');
|
|
28
|
+
if (fill && fill !== 'none' && fill !== 'transparent') {
|
|
29
|
+
const colorKey = `${fill}-${opacity || '1'}`;
|
|
30
|
+
if (!colorSet.has(colorKey)) {
|
|
31
|
+
colorSet.add(colorKey);
|
|
32
|
+
colors.push({
|
|
33
|
+
fill,
|
|
34
|
+
opacity: opacity ? parseFloat(opacity) : undefined
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (stroke && stroke !== 'none' && stroke !== 'transparent') {
|
|
39
|
+
const colorKey = `stroke-${stroke}-${opacity || '1'}`;
|
|
40
|
+
if (!colorSet.has(colorKey)) {
|
|
41
|
+
colorSet.add(colorKey);
|
|
42
|
+
colors.push({
|
|
43
|
+
stroke,
|
|
44
|
+
opacity: opacity ? parseFloat(opacity) : undefined
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
Array.from(element.children).forEach(child => {
|
|
49
|
+
extractFromElement(child);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
extractFromElement(svgElement);
|
|
53
|
+
return colors;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=colors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"colors.js","sourceRoot":"","sources":["../../src/analyze/colors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAqB,MAAM,qBAAqB,CAAC;AAG1E;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,UAA4B;IACxD,MAAM,SAAS,GAAG,gBAAgB,CAAC,UAAU,CAAC;QAC5C,CAAC,CAAC,UAAU;QACZ,CAAC,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;IAE7B,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,SAAS,EAAE;QAC/B,WAAW,EAAE,eAAe;KAC7B,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC;IACrC,MAAM,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAEjD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,MAAM,GAAe,EAAE,CAAC;IAC9B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IAEnC,SAAS,kBAAkB,CAAC,OAAgB;QAC1C,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;QAEhD,IAAI,IAAI,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,aAAa,EAAE,CAAC;YACtD,MAAM,QAAQ,GAAG,GAAG,IAAI,IAAI,OAAO,IAAI,GAAG,EAAE,CAAC;YAC7C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5B,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBACvB,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI;oBACJ,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS;iBACnD,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,IAAI,MAAM,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,aAAa,EAAE,CAAC;YAC5D,MAAM,QAAQ,GAAG,UAAU,MAAM,IAAI,OAAO,IAAI,GAAG,EAAE,CAAC;YACtD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5B,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBACvB,MAAM,CAAC,IAAI,CAAC;oBACV,MAAM;oBACN,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS;iBACnD,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;YAC3C,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAE/B,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SVG path analysis utilities
|
|
3
|
+
*/
|
|
4
|
+
import { PathCommand } from '../types';
|
|
5
|
+
/**
|
|
6
|
+
* Parses the 'd' attribute of a path element into command objects
|
|
7
|
+
*/
|
|
8
|
+
export declare function parsePathData(pathData: string): PathCommand[];
|
|
9
|
+
/**
|
|
10
|
+
* Analyzes all paths in an SVG and returns their command structures
|
|
11
|
+
*/
|
|
12
|
+
export declare function analyzePaths(svgContent: Element | string): Map<string, PathCommand[]>;
|
|
13
|
+
/**
|
|
14
|
+
* Gets statistics about paths in an SVG
|
|
15
|
+
*/
|
|
16
|
+
export declare function getPathStatistics(svgContent: Element | string): {
|
|
17
|
+
totalPaths: number;
|
|
18
|
+
totalCommands: number;
|
|
19
|
+
commandTypes: Record<string, number>;
|
|
20
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
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
|
+
* Parses the 'd' attribute of a path element into command objects
|
|
9
|
+
*/
|
|
10
|
+
export function parsePathData(pathData) {
|
|
11
|
+
const commands = [];
|
|
12
|
+
// Split by path commands (M, L, H, V, C, S, Q, T, A, Z)
|
|
13
|
+
const commandRegex = /([MmLlHhVvCcSsQqTtAaZz])((?:\s*-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\s*,?\s*)*)/g;
|
|
14
|
+
let match;
|
|
15
|
+
while ((match = commandRegex.exec(pathData)) !== null) {
|
|
16
|
+
const type = match[1];
|
|
17
|
+
const paramsStr = match[2].trim();
|
|
18
|
+
if (type.toLowerCase() === 'z') {
|
|
19
|
+
commands.push({ type, params: [] });
|
|
20
|
+
}
|
|
21
|
+
else if (paramsStr) {
|
|
22
|
+
const params = paramsStr
|
|
23
|
+
.split(/[\s,]+/)
|
|
24
|
+
.filter(p => p.trim() !== '')
|
|
25
|
+
.map(Number)
|
|
26
|
+
.filter(n => !isNaN(n));
|
|
27
|
+
commands.push({ type, params });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return commands;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Analyzes all paths in an SVG and returns their command structures
|
|
34
|
+
*/
|
|
35
|
+
export function analyzePaths(svgContent) {
|
|
36
|
+
const svgString = isValidSvgString(svgContent)
|
|
37
|
+
? svgContent
|
|
38
|
+
: serializeSVG(svgContent);
|
|
39
|
+
const dom = new JSDOM(svgString, {
|
|
40
|
+
contentType: 'image/svg+xml'
|
|
41
|
+
});
|
|
42
|
+
const document = dom.window.document;
|
|
43
|
+
const svgElement = document.querySelector('svg');
|
|
44
|
+
if (!svgElement) {
|
|
45
|
+
throw new Error('No SVG element found in the provided content.');
|
|
46
|
+
}
|
|
47
|
+
const paths = svgElement.querySelectorAll('path');
|
|
48
|
+
const pathAnalysis = new Map();
|
|
49
|
+
Array.from(paths).forEach((path, index) => {
|
|
50
|
+
const d = path.getAttribute('d');
|
|
51
|
+
if (d) {
|
|
52
|
+
const id = path.getAttribute('id') || `path-${index}`;
|
|
53
|
+
pathAnalysis.set(id, parsePathData(d));
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return pathAnalysis;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Gets statistics about paths in an SVG
|
|
60
|
+
*/
|
|
61
|
+
export function getPathStatistics(svgContent) {
|
|
62
|
+
const pathAnalysis = analyzePaths(svgContent);
|
|
63
|
+
let totalCommands = 0;
|
|
64
|
+
const commandTypes = {};
|
|
65
|
+
pathAnalysis.forEach(commands => {
|
|
66
|
+
totalCommands += commands.length;
|
|
67
|
+
commands.forEach(cmd => {
|
|
68
|
+
const type = cmd.type.toUpperCase();
|
|
69
|
+
commandTypes[type] = (commandTypes[type] || 0) + 1;
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
return {
|
|
73
|
+
totalPaths: pathAnalysis.size,
|
|
74
|
+
totalCommands,
|
|
75
|
+
commandTypes
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# 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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"}
|