CatPtain commited on
Commit
77be7be
·
verified ·
1 Parent(s): af7cc7f

Upload svg2Base64.ts

Browse files
Files changed (1) hide show
  1. frontend/src/utils/svg2Base64.ts +154 -19
frontend/src/utils/svg2Base64.ts CHANGED
@@ -60,47 +60,182 @@ export const svg2Base64 = (element: Element) => {
60
  const clonedElement = element.cloneNode(true) as Element;
61
  console.log('svg2Base64: Element cloned successfully');
62
 
63
- // 确保SVG有正确的命名空间
64
  if (clonedElement.tagName.toLowerCase() === 'svg') {
65
- clonedElement.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
66
- clonedElement.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
67
- console.log('svg2Base64: Added SVG namespaces');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  }
69
 
70
- // 检查是否有内联样式需要处理
71
- const computedStyles = window.getComputedStyle(element);
72
- console.log('svg2Base64: Element computed styles:', {
73
- width: computedStyles.width,
74
- height: computedStyles.height,
75
- display: computedStyles.display,
76
- visibility: computedStyles.visibility
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  });
78
 
 
 
 
 
79
  const XMLS = new XMLSerializer();
80
- const svg = XMLS.serializeToString(clonedElement);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
  console.log('svg2Base64: Serialization result:', {
83
  length: svg.length,
84
  preview: svg.substring(0, 200) + '...',
85
  containsSvgTag: svg.includes('<svg'),
86
- containsContent: svg.length > 50
 
87
  });
88
 
89
  if (!svg || svg.length === 0) {
90
  throw new Error('SVG serialization returned empty string');
91
  }
92
 
 
 
 
 
93
  const encoded = encode(svg);
94
  if (!encoded) {
95
  throw new Error('Base64 encoding failed');
96
  }
97
 
98
- console.log('svg2Base64: Encoding successful, result length:', (PREFIX + encoded).length);
99
- return PREFIX + encoded;
 
 
 
 
 
 
 
100
  } catch (error) {
101
- console.error('svg2Base64 failed:', error);
102
- console.error('svg2Base64: Element that caused error:', element);
103
- console.error('svg2Base64: Element outerHTML:', element.outerHTML);
104
- throw error;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  }
106
  }
 
60
  const clonedElement = element.cloneNode(true) as Element;
61
  console.log('svg2Base64: Element cloned successfully');
62
 
63
+ // 确保SVG有正确的命名空间和属性
64
  if (clonedElement.tagName.toLowerCase() === 'svg') {
65
+ const svgElement = clonedElement as SVGElement;
66
+ svgElement.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
67
+ svgElement.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
68
+
69
+ // 确保SVG有明确的尺寸
70
+ const rect = element.getBoundingClientRect();
71
+ if (!svgElement.getAttribute('width') && rect.width > 0) {
72
+ svgElement.setAttribute('width', rect.width.toString());
73
+ }
74
+ if (!svgElement.getAttribute('height') && rect.height > 0) {
75
+ svgElement.setAttribute('height', rect.height.toString());
76
+ }
77
+
78
+ // 确保viewBox存在
79
+ if (!svgElement.getAttribute('viewBox')) {
80
+ const width = parseFloat(svgElement.getAttribute('width') || '0');
81
+ const height = parseFloat(svgElement.getAttribute('height') || '0');
82
+ if (width > 0 && height > 0) {
83
+ svgElement.setAttribute('viewBox', `0 0 ${width} ${height}`);
84
+ }
85
+ }
86
+
87
+ console.log('svg2Base64: Added SVG namespaces and dimensions');
88
  }
89
 
90
+ // 处理内联样式 - 将计算样式应用到元素
91
+ const applyComputedStyles = (elem: Element, originalElem: Element) => {
92
+ if (elem.nodeType === Node.ELEMENT_NODE) {
93
+ const computedStyles = window.getComputedStyle(originalElem as HTMLElement);
94
+ const styleProps = ['fill', 'stroke', 'stroke-width', 'opacity', 'font-family', 'font-size', 'font-weight'];
95
+
96
+ styleProps.forEach(prop => {
97
+ const value = computedStyles.getPropertyValue(prop);
98
+ if (value && value !== 'none' && !elem.getAttribute(prop)) {
99
+ elem.setAttribute(prop, value);
100
+ }
101
+ });
102
+
103
+ // 递归处理子元素
104
+ for (let i = 0; i < elem.children.length; i++) {
105
+ const child = elem.children[i];
106
+ const originalChild = (originalElem as HTMLElement).children[i];
107
+ if (originalChild) {
108
+ applyComputedStyles(child, originalChild);
109
+ }
110
+ }
111
+ }
112
+ };
113
+
114
+ applyComputedStyles(clonedElement, element);
115
+
116
+ // 检查元素尺寸 - 使用更宽松的检查逻辑
117
+ const rect = element.getBoundingClientRect();
118
+ const computedStyle = window.getComputedStyle(element as HTMLElement);
119
+ const hasValidDimensions = (
120
+ rect.width > 0 || rect.height > 0 ||
121
+ parseFloat(computedStyle.width) > 0 || parseFloat(computedStyle.height) > 0 ||
122
+ (element as HTMLElement).offsetWidth > 0 || (element as HTMLElement).offsetHeight > 0
123
+ );
124
+
125
+ console.log('svg2Base64: Element dimensions:', {
126
+ boundingRect: { width: rect.width, height: rect.height },
127
+ computedStyle: { width: computedStyle.width, height: computedStyle.height },
128
+ offset: { width: (element as HTMLElement).offsetWidth, height: (element as HTMLElement).offsetHeight },
129
+ hasValidDimensions
130
  });
131
 
132
+ if (!hasValidDimensions) {
133
+ console.warn('svg2Base64: Element has no valid dimensions, but continuing with serialization');
134
+ }
135
+
136
  const XMLS = new XMLSerializer();
137
+ let svg = XMLS.serializeToString(clonedElement);
138
+
139
+ // 清理和优化SVG字符串
140
+ svg = svg.replace(/vector-effect="[^"]*"/g, ''); // 移除vector-effect属性
141
+ svg = svg.replace(/xmlns="[^"]*"/g, ''); // 移除重复的xmlns
142
+
143
+ // 确保SVG标签包含正确的命名空间
144
+ if (svg.includes('<svg') && !svg.includes('xmlns="http://www.w3.org/2000/svg"')) {
145
+ svg = svg.replace('<svg', '<svg xmlns="http://www.w3.org/2000/svg"');
146
+ }
147
+
148
+ // 添加xmlns:xlink命名空间(如果需要)
149
+ if (svg.includes('xlink:') && !svg.includes('xmlns:xlink')) {
150
+ svg = svg.replace('<svg', '<svg xmlns:xlink="http://www.w3.org/1999/xlink"');
151
+ }
152
+
153
+ // 确保SVG有基本的尺寸信息
154
+ if (svg.includes('<svg') && !svg.includes('viewBox=') && !svg.includes('width=')) {
155
+ const rect = element.getBoundingClientRect();
156
+ if (rect.width > 0 && rect.height > 0) {
157
+ svg = svg.replace('<svg', `<svg width="${rect.width}" height="${rect.height}" viewBox="0 0 ${rect.width} ${rect.height}"`);
158
+ }
159
+ }
160
 
161
  console.log('svg2Base64: Serialization result:', {
162
  length: svg.length,
163
  preview: svg.substring(0, 200) + '...',
164
  containsSvgTag: svg.includes('<svg'),
165
+ containsContent: svg.length > 50,
166
+ hasNamespace: svg.includes('xmlns="http://www.w3.org/2000/svg"')
167
  });
168
 
169
  if (!svg || svg.length === 0) {
170
  throw new Error('SVG serialization returned empty string');
171
  }
172
 
173
+ if (svg.length < 20) {
174
+ throw new Error('SVG serialization returned suspiciously short string');
175
+ }
176
+
177
  const encoded = encode(svg);
178
  if (!encoded) {
179
  throw new Error('Base64 encoding failed');
180
  }
181
 
182
+ const result = PREFIX + encoded;
183
+ console.log('svg2Base64: Encoding successful, result length:', result.length);
184
+
185
+ // 验证结果
186
+ if (result.length < 100) {
187
+ throw new Error('Base64 result is suspiciously short');
188
+ }
189
+
190
+ return result;
191
  } catch (error) {
192
+ console.error('svg2Base64: Conversion failed:', error);
193
+
194
+ // 尝试多种备选方案
195
+ const fallbackStrategies = [
196
+ // 策略1: 简化的序列化
197
+ () => {
198
+ console.log('svg2Base64: Attempting simplified serialization');
199
+ const rect = element.getBoundingClientRect();
200
+ const width = rect.width || 100;
201
+ const height = rect.height || 100;
202
+ const simplifiedSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">${element.innerHTML}</svg>`;
203
+ const base64 = encode(utf8Encode(simplifiedSvg));
204
+ return `data:image/svg+xml;base64,${base64}`;
205
+ },
206
+
207
+ // 策略2: 使用outerHTML
208
+ () => {
209
+ console.log('svg2Base64: Attempting outerHTML serialization');
210
+ let svgString = (element as HTMLElement).outerHTML;
211
+ if (!svgString.includes('xmlns')) {
212
+ svgString = svgString.replace('<svg', '<svg xmlns="http://www.w3.org/2000/svg"');
213
+ }
214
+ const base64 = encode(utf8Encode(svgString));
215
+ return `data:image/svg+xml;base64,${base64}`;
216
+ },
217
+
218
+ // 策略3: 最小化SVG
219
+ () => {
220
+ console.log('svg2Base64: Attempting minimal SVG creation');
221
+ const rect = element.getBoundingClientRect();
222
+ const minimalSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="${rect.width || 100}" height="${rect.height || 100}"><rect width="100%" height="100%" fill="#cccccc"/></svg>`;
223
+ const base64 = encode(utf8Encode(minimalSvg));
224
+ return `data:image/svg+xml;base64,${base64}`;
225
+ }
226
+ ];
227
+
228
+ for (let i = 0; i < fallbackStrategies.length; i++) {
229
+ try {
230
+ const result = fallbackStrategies[i]();
231
+ console.log(`svg2Base64: Fallback strategy ${i + 1} succeeded`);
232
+ return result;
233
+ } catch (fallbackError) {
234
+ console.warn(`svg2Base64: Fallback strategy ${i + 1} failed:`, fallbackError);
235
+ }
236
+ }
237
+
238
+ console.error('svg2Base64: All fallback strategies failed');
239
+ throw new Error(`SVG to Base64 conversion failed: ${error}`);
240
  }
241
  }