Skip to content

Commit f8b6a3f

Browse files
committed
🐛 fix: fix svg parsing
1 parent bfcb52b commit f8b6a3f

File tree

5 files changed

+107
-115
lines changed

5 files changed

+107
-115
lines changed

src/parser/svg.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@ import { nodeToRawSVG, StrToRenderSVG, urlToRawSVG } from '../utils/svg';
99
export const parseToSvg = async (node: SVGElement) => {
1010
const { height, width, left, top } = node.getBoundingClientRect();
1111

12-
const svgString = await StrToRenderSVG(nodeToRawSVG(node), {
13-
width,
14-
height,
15-
});
12+
const svgString = await StrToRenderSVG(
13+
nodeToRawSVG(node),
14+
{
15+
width,
16+
height,
17+
},
18+
node,
19+
);
1620

1721
const svg = new Svg({
1822
x: left,

src/parser/svgson.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,18 @@ export class Svgson {
131131
height?: number;
132132
},
133133
) {
134+
if (!svgString) return;
134135
// --------- 处理 Svg String 变成 Svg Shape ---------- //
135136

136137
let result = svgson.parseSync(svgString, { camelcase: true });
138+
137139
// 排除一下 svg 的循环嵌套的问题
138140
while (result.children.length === 1 && result.children[0].name === 'svg') {
139141
[result] = result.children;
140142
}
141143

142144
const { children, attributes } = result;
145+
143146
const { viewBox } = attributes;
144147

145148
// 解析获得 viewBox 值
@@ -158,6 +161,8 @@ export class Svgson {
158161
x: 0,
159162
y: 0,
160163
});
164+
if (!children) return;
165+
161166
const background = new Rectangle(this.viewBox.toJSON());
162167
background.name = '容器';
163168
background.hasClippingMask = true;
@@ -178,12 +183,12 @@ export class Svgson {
178183
/**
179184
* 缩放比例
180185
*/
181-
aspectRatio: number;
186+
aspectRatio: number = 1;
182187

183188
/**
184189
* svg 的 ViewBox
185190
*/
186-
viewBox: Frame;
191+
viewBox = new Frame();
187192

188193
/**
189194
* Svg 包含的图层对象
@@ -199,6 +204,10 @@ export class Svgson {
199204

200205
shapes: { path: string; style?: string }[] = [];
201206

207+
static init(): Svgson {
208+
return new Svgson('', {});
209+
}
210+
202211
/**
203212
* 将图层
204213
*/

src/utils/svg.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,12 @@ const getUseReplacement = (node: SVGUseElement) => {
247247
* @param svgString
248248
* @param width
249249
* @param height
250+
* @param node 需要挂载的节点位置
250251
*/
251252
export const StrToRenderSVG = async (
252253
svgString: string,
253254
{ width, height }: { width: number; height: number },
255+
node?: Element,
254256
) => {
255257
const divNode = document.createElement('div');
256258
divNode.innerHTML = svgString;
@@ -259,7 +261,11 @@ export const StrToRenderSVG = async (
259261
svgNode.style.width = `${width}px`;
260262
svgNode.style.height = `${height}px`;
261263

262-
document.body.append(divNode);
264+
if (node) {
265+
node.append(divNode);
266+
} else {
267+
document.body.append(divNode);
268+
}
263269

264270
const queue = Array.from(svgNode.children);
265271

tests/__tests__/parser/shape.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Bitmap, Group, parseToShape, Rectangle } from 'html2sketch';
2+
import { setupTestNode } from '@test-utils';
23

34
describe('parseToShape', () => {
45
beforeEach(() => {
@@ -18,9 +19,8 @@ describe('parseToShape', () => {
1819
1920
</style>
2021
`;
21-
const node = document.createElement('div');
2222

23-
node.innerHTML = `
23+
const innerHTML = `
2424
<div
2525
id="radial-gradient"
2626
class="radial-gradient"
@@ -74,7 +74,7 @@ describe('parseToShape', () => {
7474
/>
7575
`;
7676

77-
document.body.append(node);
77+
setupTestNode(innerHTML);
7878
});
7979
it('shape 正常解析', async () => {
8080
const node = document.getElementById('shape') as HTMLDivElement;

tests/__tests__/parser/svgson.spec.ts

Lines changed: 78 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { SketchFormat } from 'html2sketch';
1+
import { SketchFormat, Svgson } from 'html2sketch';
22
import {
33
calcFrameScale,
44
normalizeWindingRule,
@@ -127,108 +127,81 @@ describe('normalizeWindingRule', () => {
127127
});
128128

129129
describe('Svgson 解析器', () => {
130-
// describe('parseGNodeToGroup', () => {
131-
// it('不是 g 则返回空', () => {
132-
// const gNode = {
133-
// name: 'a',
134-
// type: 'element',
135-
// value: '',
136-
// attributes: {},
137-
// children: [],
138-
// };
139-
//
140-
// expect(Svgson.parseGNodeToGroup(gNode)).toBeUndefined();
141-
// });
142-
// it('没有 children 正常解析', () => {
143-
// const gNode = {
144-
// name: 'g',
145-
// type: 'element',
146-
// value: '',
147-
// attributes: {
148-
// id: 'Page-1',
149-
// stroke: 'none',
150-
// strokeWidth: '1',
151-
// fill: 'none',
152-
// fillRule: 'evenodd',
153-
// style: '',
154-
// },
155-
// children: [],
156-
// };
157-
//
158-
// expect(Svg.parseGNodeToGroup(gNode)).toStrictEqual({
159-
// type: 'group',
160-
// layers: [],
161-
// path: '',
162-
// style: { fills: [], strokes: [], style: '' },
163-
// windingRule: SketchFormat.WindingRule.EvenOdd,
164-
// });
165-
// });
166-
// it('有 children 解析正常', () => {
167-
// const gNode = {
168-
// name: 'g',
169-
// type: 'element',
170-
// value: '',
171-
// attributes: {
172-
// id: 'Page-1',
173-
// stroke: 'none',
174-
// strokeWidth: '1',
175-
// fill: 'none',
176-
// fillRule: 'evenodd',
177-
// style: '',
178-
// },
179-
// children: [
180-
// {
181-
// name: 'ellipse',
182-
// type: 'element',
183-
// value: '',
184-
// attributes: {
185-
// id: 'Combined-Shape',
186-
// fill: 'url(#linearGradient-4)',
187-
// cx: '100.519339',
188-
// cy: '100.436681',
189-
// rx: '23.6001926',
190-
// ry: '23.580786',
191-
// style:
192-
// 'cx: 100.519px; cy: 100.437px; rx: 23.6002px; ry: 23.5808px; fill: url(&quot;#linearGradient-4&quot;); color: rgba(0, 0, 0, 0.85); fill-rule: evenodd; font-size: 14px; font-variant: tabular-nums; text-decoration: none solid rgba(0, 0, 0, 0.85);',
193-
// },
194-
// children: [],
195-
// },
196-
// ],
197-
// };
198-
//
199-
// expect(Svg.parseGNodeToGroup(gNode)).toStrictEqual({
200-
// type: 'group',
201-
// layers: [],
202-
// path: '',
203-
// style: { fills: [], strokes: [], style: '' },
204-
// windingRule: SketchFormat.WindingRule.EvenOdd,
205-
// });
206-
// });
207-
// });
208-
// describe('parseNodeToEllipse', () => {
209-
// it('没有填充 没有描边', () => {
210-
// const node = {
211-
// name: 'ellipse',
212-
// type: 'element',
213-
// value: '',
214-
// attributes: {
215-
// id: 'Combined-Shape',
216-
// fill: 'url(#linearGradient-4)',
217-
// cx: '100.519339',
218-
// cy: '100.436681',
219-
// rx: '23.6001926',
220-
// ry: '23.580786',
221-
// style:
222-
// 'cx: 100.519px; cy: 100.437px; rx: 23.6002px; ry: 23.5808px; fill: url(&quot;#linearGradient-4&quot;); color: rgba(0, 0, 0, 0.85); fill-rule: evenodd; font-size: 14px; font-variant: tabular-nums; text-decoration: none solid rgba(0, 0, 0, 0.85);',
223-
// },
224-
// children: [],
225-
// };
226-
// expect(parseNodeToEllipse(node)).toStrictEqual({
227-
// type: 'ellipse',
228-
// fills: [],
229-
// strokes: [],
230-
// style: '',
231-
// });
232-
// });
233-
// });
130+
const svgson = Svgson.init();
131+
describe('parseGNodeToGroup', () => {
132+
it('没有 children 正常解析', () => {
133+
const gNode = {
134+
name: 'g',
135+
type: 'element',
136+
value: '',
137+
attributes: {
138+
id: 'Page-1',
139+
stroke: 'none',
140+
strokeWidth: '1',
141+
fill: 'none',
142+
fillRule: 'evenodd',
143+
style: '',
144+
},
145+
children: [],
146+
};
147+
148+
const group = svgson.parseNodeToGroup(gNode);
149+
expect(group.layers).toHaveLength(0);
150+
});
151+
it('有 children 解析正常', () => {
152+
const gNode = {
153+
name: 'g',
154+
type: 'element',
155+
value: '',
156+
attributes: {
157+
id: 'Page-1',
158+
stroke: 'none',
159+
strokeWidth: '1',
160+
fill: 'none',
161+
fillRule: 'evenodd',
162+
style: '',
163+
},
164+
children: [
165+
{
166+
name: 'ellipse',
167+
type: 'element',
168+
value: '',
169+
attributes: {
170+
id: 'Combined-Shape',
171+
fill: '#f1232f',
172+
cx: '100.519339',
173+
cy: '100.436681',
174+
rx: '23.6001926',
175+
ry: '23.580786',
176+
},
177+
children: [],
178+
},
179+
],
180+
};
181+
const group = svgson.parseNodeToGroup(gNode);
182+
expect(group.layers).toHaveLength(1);
183+
});
184+
});
185+
describe('parseNodeToEllipse', () => {
186+
it('没有填充 没有描边', () => {
187+
const node = {
188+
name: 'ellipse',
189+
type: 'element',
190+
value: '',
191+
attributes: {
192+
id: 'Combined-Shape',
193+
fill: '#f1232f',
194+
cx: '100',
195+
cy: '50',
196+
rx: '25',
197+
ry: '20',
198+
},
199+
children: [],
200+
};
201+
const ellipse = svgson.parseNodeToEllipse(node);
202+
203+
expect(ellipse?.cx).toBe(100);
204+
expect(ellipse?.cy).toBe(50);
205+
});
206+
});
234207
});

0 commit comments

Comments
 (0)