CatPtain commited on
Commit
ad7128b
·
verified ·
1 Parent(s): 3065c1d

Upload 3 files

Browse files
debug_vector_export.html ADDED
@@ -0,0 +1,319 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>矢量元素导出调试</title>
7
+ <style>
8
+ body {
9
+ font-family: Arial, sans-serif;
10
+ margin: 20px;
11
+ background-color: #f5f5f5;
12
+ }
13
+ .container {
14
+ max-width: 1200px;
15
+ margin: 0 auto;
16
+ background: white;
17
+ padding: 20px;
18
+ border-radius: 8px;
19
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
20
+ }
21
+ .test-section {
22
+ margin-bottom: 30px;
23
+ padding: 20px;
24
+ border: 1px solid #ddd;
25
+ border-radius: 5px;
26
+ }
27
+ .test-title {
28
+ font-size: 18px;
29
+ font-weight: bold;
30
+ margin-bottom: 15px;
31
+ color: #333;
32
+ }
33
+ .svg-container {
34
+ display: inline-block;
35
+ margin: 10px;
36
+ border: 1px solid #ccc;
37
+ padding: 10px;
38
+ background: white;
39
+ }
40
+ .debug-info {
41
+ background: #f8f9fa;
42
+ padding: 10px;
43
+ margin-top: 10px;
44
+ border-radius: 4px;
45
+ font-family: monospace;
46
+ font-size: 12px;
47
+ }
48
+ button {
49
+ background: #007bff;
50
+ color: white;
51
+ border: none;
52
+ padding: 8px 16px;
53
+ border-radius: 4px;
54
+ cursor: pointer;
55
+ margin: 5px;
56
+ }
57
+ button:hover {
58
+ background: #0056b3;
59
+ }
60
+ .error {
61
+ color: #dc3545;
62
+ background: #f8d7da;
63
+ padding: 10px;
64
+ border-radius: 4px;
65
+ margin: 10px 0;
66
+ }
67
+ .success {
68
+ color: #155724;
69
+ background: #d4edda;
70
+ padding: 10px;
71
+ border-radius: 4px;
72
+ margin: 10px 0;
73
+ }
74
+ </style>
75
+ </head>
76
+ <body>
77
+ <div class="container">
78
+ <h1>PPT矢量元素导出问题诊断</h1>
79
+
80
+ <div class="test-section">
81
+ <div class="test-title">1. 基础形状元素测试</div>
82
+ <div class="svg-container">
83
+ <svg width="100" height="100" id="basic-shape">
84
+ <path d="M10,10 L90,10 L90,90 L10,90 Z"
85
+ fill="#ff6b6b"
86
+ stroke="#333"
87
+ stroke-width="2"
88
+ vector-effect="non-scaling-stroke"/>
89
+ </svg>
90
+ <div class="debug-info">基础矩形 - 100x100px</div>
91
+ </div>
92
+
93
+ <div class="svg-container">
94
+ <svg width="100" height="100" id="circle-shape">
95
+ <circle cx="50" cy="50" r="40"
96
+ fill="#4ecdc4"
97
+ stroke="#333"
98
+ stroke-width="2"
99
+ vector-effect="non-scaling-stroke"/>
100
+ </svg>
101
+ <div class="debug-info">圆形 - 半径40px</div>
102
+ </div>
103
+ </div>
104
+
105
+ <div class="test-section">
106
+ <div class="test-title">2. 复杂路径元素测试</div>
107
+ <div class="svg-container">
108
+ <svg width="120" height="100" id="complex-path">
109
+ <path d="M20,80 C20,80 20,20 50,20 S80,20 80,50 S80,80 50,80 S20,80 20,80"
110
+ fill="#45b7d1"
111
+ stroke="#333"
112
+ stroke-width="2"
113
+ vector-effect="non-scaling-stroke"/>
114
+ </svg>
115
+ <div class="debug-info">复杂曲线路径</div>
116
+ </div>
117
+
118
+ <div class="svg-container">
119
+ <svg width="100" height="100" id="star-shape">
120
+ <path d="M50,5 L61,35 L95,35 L68,57 L79,91 L50,70 L21,91 L32,57 L5,35 L39,35 Z"
121
+ fill="#f7b731"
122
+ stroke="#333"
123
+ stroke-width="2"
124
+ vector-effect="non-scaling-stroke"/>
125
+ </svg>
126
+ <div class="debug-info">星形路径</div>
127
+ </div>
128
+ </div>
129
+
130
+ <div class="test-section">
131
+ <div class="test-title">3. 线条元素测试</div>
132
+ <div class="svg-container">
133
+ <svg width="120" height="80" id="line-element">
134
+ <path d="M10,40 L110,40"
135
+ stroke="#e74c3c"
136
+ stroke-width="3"
137
+ vector-effect="non-scaling-stroke"
138
+ marker-end="url(#arrowhead)"/>
139
+ <defs>
140
+ <marker id="arrowhead" markerWidth="10" markerHeight="7"
141
+ refX="9" refY="3.5" orient="auto">
142
+ <polygon points="0 0, 10 3.5, 0 7" fill="#e74c3c"/>
143
+ </marker>
144
+ </defs>
145
+ </svg>
146
+ <div class="debug-info">带箭头的直线</div>
147
+ </div>
148
+
149
+ <div class="svg-container">
150
+ <svg width="120" height="80" id="curved-line">
151
+ <path d="M10,60 Q60,10 110,60"
152
+ stroke="#9b59b6"
153
+ stroke-width="3"
154
+ fill="none"
155
+ vector-effect="non-scaling-stroke"/>
156
+ </svg>
157
+ <div class="debug-info">二次贝塞尔曲线</div>
158
+ </div>
159
+ </div>
160
+
161
+ <div class="test-section">
162
+ <div class="test-title">4. 导出测试功能</div>
163
+ <button onclick="testSVGSerialization()">测试SVG序列化</button>
164
+ <button onclick="testBase64Conversion()">测试Base64转换</button>
165
+ <button onclick="testElementDimensions()">测试元素尺寸</button>
166
+ <button onclick="runAllTests()">运行所有测试</button>
167
+
168
+ <div id="test-results"></div>
169
+ </div>
170
+
171
+ <div class="test-section">
172
+ <div class="test-title">5. 问题诊断结果</div>
173
+ <div id="diagnosis-results">
174
+ <p>点击上方按钮开始诊断...</p>
175
+ </div>
176
+ </div>
177
+ </div>
178
+
179
+ <script>
180
+ // SVG序列化测试
181
+ function testSVGSerialization() {
182
+ const results = document.getElementById('test-results');
183
+ results.innerHTML = '<h4>SVG序列化测试结果:</h4>';
184
+
185
+ const svgElements = ['basic-shape', 'circle-shape', 'complex-path', 'star-shape', 'line-element', 'curved-line'];
186
+
187
+ svgElements.forEach(id => {
188
+ const svg = document.getElementById(id);
189
+ if (svg) {
190
+ try {
191
+ const serializer = new XMLSerializer();
192
+ const svgString = serializer.serializeToString(svg);
193
+ const isValid = svgString.length > 0 && svgString.includes('<svg');
194
+
195
+ results.innerHTML += `
196
+ <div class="${isValid ? 'success' : 'error'}">
197
+ <strong>${id}:</strong> ${isValid ? '✓ 序列化成功' : '✗ 序列化失败'}<br>
198
+ <small>长度: ${svgString.length} 字符</small>
199
+ </div>
200
+ `;
201
+ } catch (error) {
202
+ results.innerHTML += `
203
+ <div class="error">
204
+ <strong>${id}:</strong> ✗ 序列化异常: ${error.message}
205
+ </div>
206
+ `;
207
+ }
208
+ }
209
+ });
210
+ }
211
+
212
+ // Base64转换测试
213
+ function testBase64Conversion() {
214
+ const results = document.getElementById('test-results');
215
+ results.innerHTML = '<h4>Base64转换测试结果:</h4>';
216
+
217
+ const svg = document.getElementById('basic-shape');
218
+ if (svg) {
219
+ try {
220
+ const serializer = new XMLSerializer();
221
+ const svgString = serializer.serializeToString(svg);
222
+ const base64 = btoa(unescape(encodeURIComponent(svgString)));
223
+ const dataUrl = `data:image/svg+xml;base64,${base64}`;
224
+
225
+ results.innerHTML += `
226
+ <div class="success">
227
+ <strong>Base64转换:</strong> ✓ 成功<br>
228
+ <small>Data URL长度: ${dataUrl.length} 字符</small><br>
229
+ <img src="${dataUrl}" style="max-width: 100px; border: 1px solid #ccc; margin-top: 5px;">
230
+ </div>
231
+ `;
232
+ } catch (error) {
233
+ results.innerHTML += `
234
+ <div class="error">
235
+ <strong>Base64转换:</strong> ✗ 失败: ${error.message}
236
+ </div>
237
+ `;
238
+ }
239
+ }
240
+ }
241
+
242
+ // 元素尺寸测试
243
+ function testElementDimensions() {
244
+ const results = document.getElementById('test-results');
245
+ results.innerHTML = '<h4>元素尺寸测试结果:</h4>';
246
+
247
+ const svgElements = ['basic-shape', 'circle-shape', 'complex-path'];
248
+
249
+ svgElements.forEach(id => {
250
+ const svg = document.getElementById(id);
251
+ if (svg) {
252
+ const clientWidth = svg.clientWidth;
253
+ const clientHeight = svg.clientHeight;
254
+ const boundingRect = svg.getBoundingClientRect();
255
+
256
+ const isValidSize = clientWidth > 0 && clientHeight > 0;
257
+
258
+ results.innerHTML += `
259
+ <div class="${isValidSize ? 'success' : 'error'}">
260
+ <strong>${id}:</strong><br>
261
+ clientWidth: ${clientWidth}px<br>
262
+ clientHeight: ${clientHeight}px<br>
263
+ boundingRect: ${boundingRect.width}x${boundingRect.height}px<br>
264
+ ${isValidSize ? '✓ 尺寸正常' : '✗ 尺寸异常 (可能导致导出失败)'}
265
+ </div>
266
+ `;
267
+ }
268
+ });
269
+ }
270
+
271
+ // 运行所有测试
272
+ function runAllTests() {
273
+ testSVGSerialization();
274
+ setTimeout(() => {
275
+ testBase64Conversion();
276
+ setTimeout(() => {
277
+ testElementDimensions();
278
+ setTimeout(generateDiagnosis, 500);
279
+ }, 500);
280
+ }, 500);
281
+ }
282
+
283
+ // 生成诊断报告
284
+ function generateDiagnosis() {
285
+ const diagnosisDiv = document.getElementById('diagnosis-results');
286
+
287
+ diagnosisDiv.innerHTML = `
288
+ <h4>诊断报告:</h4>
289
+ <div class="debug-info">
290
+ <strong>可能的问题原因:</strong><br>
291
+ 1. SVG元素尺寸为0或负数 (clientWidth < 1 || clientHeight < 1)<br>
292
+ 2. SVG序列化失败或返回空字符串<br>
293
+ 3. Base64编码过程中出现错误<br>
294
+ 4. 特殊形状元素(special=true)的DOM查询失败<br>
295
+ 5. vector-effect="non-scaling-stroke"属性导致的兼容性问题<br><br>
296
+
297
+ <strong>建议的解决方案:</strong><br>
298
+ 1. 在导出前检查SVG元素的实际渲染尺寸<br>
299
+ 2. 添加SVG序列化的错误处理和重试机制<br>
300
+ 3. 确保DOM元素在导出时已完全渲染<br>
301
+ 4. 考虑使用setTimeout延迟导出以确保渲染完成<br>
302
+ 5. 添加更详细的错误日志记录<br>
303
+ </div>
304
+ `;
305
+ }
306
+
307
+ // 页面加载完成后自动运行基础检查
308
+ window.addEventListener('load', () => {
309
+ console.log('矢量元素导出调试页面已加载');
310
+ console.log('可用的测试函数:', {
311
+ testSVGSerialization,
312
+ testBase64Conversion,
313
+ testElementDimensions,
314
+ runAllTests
315
+ });
316
+ });
317
+ </script>
318
+ </body>
319
+ </html>
test_vector_export.js ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // 矢量元素导出测试脚本
2
+ // 在浏览器控制台中运行此脚本来测试修复效果
3
+
4
+ (function() {
5
+ console.log('=== PPT矢量元素导出测试开始 ===');
6
+
7
+ // 测试SVG元素检测
8
+ function testSVGElementDetection() {
9
+ console.log('\n1. 测试SVG元素检测...');
10
+
11
+ const svgElements = document.querySelectorAll('svg');
12
+ console.log(`找到 ${svgElements.length} 个SVG元素`);
13
+
14
+ svgElements.forEach((svg, index) => {
15
+ const clientWidth = svg.clientWidth;
16
+ const clientHeight = svg.clientHeight;
17
+ const boundingRect = svg.getBoundingClientRect();
18
+ const computedStyle = window.getComputedStyle(svg);
19
+
20
+ const hasValidSize = (
21
+ (clientWidth > 0 && clientHeight > 0) ||
22
+ (boundingRect.width > 0 && boundingRect.height > 0) ||
23
+ (parseFloat(computedStyle.width) > 0 && parseFloat(computedStyle.height) > 0)
24
+ );
25
+
26
+ console.log(`SVG ${index + 1}:`, {
27
+ element: svg,
28
+ clientSize: `${clientWidth}x${clientHeight}`,
29
+ boundingRect: `${boundingRect.width}x${boundingRect.height}`,
30
+ computedSize: `${computedStyle.width}x${computedStyle.height}`,
31
+ hasValidSize,
32
+ classes: svg.className.baseVal || svg.className
33
+ });
34
+ });
35
+ }
36
+
37
+ // 测试SVG序列化
38
+ function testSVGSerialization() {
39
+ console.log('\n2. 测试SVG序列化...');
40
+
41
+ const svgElements = document.querySelectorAll('svg');
42
+ let successCount = 0;
43
+ let failCount = 0;
44
+
45
+ svgElements.forEach((svg, index) => {
46
+ try {
47
+ const serializer = new XMLSerializer();
48
+ const svgString = serializer.serializeToString(svg);
49
+
50
+ if (svgString && svgString.length > 0) {
51
+ successCount++;
52
+ console.log(`✓ SVG ${index + 1} 序列化成功 (${svgString.length} 字符)`);
53
+ } else {
54
+ failCount++;
55
+ console.log(`✗ SVG ${index + 1} 序列化返回空字符串`);
56
+ }
57
+ } catch (error) {
58
+ failCount++;
59
+ console.log(`✗ SVG ${index + 1} 序列化失败:`, error.message);
60
+ }
61
+ });
62
+
63
+ console.log(`序列化结果: ${successCount} 成功, ${failCount} 失败`);
64
+ }
65
+
66
+ // 测试Base64转换
67
+ function testBase64Conversion() {
68
+ console.log('\n3. 测试Base64转换...');
69
+
70
+ const svgElements = document.querySelectorAll('svg');
71
+ let successCount = 0;
72
+ let failCount = 0;
73
+
74
+ svgElements.forEach((svg, index) => {
75
+ try {
76
+ // 模拟改进后的svg2Base64函数
77
+ const clonedElement = svg.cloneNode(true);
78
+
79
+ if (clonedElement.tagName.toLowerCase() === 'svg') {
80
+ clonedElement.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
81
+ clonedElement.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
82
+ }
83
+
84
+ const serializer = new XMLSerializer();
85
+ const svgString = serializer.serializeToString(clonedElement);
86
+
87
+ if (!svgString || svgString.length === 0) {
88
+ throw new Error('SVG序列化返回空字符串');
89
+ }
90
+
91
+ const base64 = btoa(unescape(encodeURIComponent(svgString)));
92
+ const dataUrl = `data:image/svg+xml;base64,${base64}`;
93
+
94
+ if (base64 && base64.length > 0) {
95
+ successCount++;
96
+ console.log(`✓ SVG ${index + 1} Base64转换成功 (${dataUrl.length} 字符)`);
97
+ } else {
98
+ failCount++;
99
+ console.log(`✗ SVG ${index + 1} Base64转换返回空字符串`);
100
+ }
101
+ } catch (error) {
102
+ failCount++;
103
+ console.log(`✗ SVG ${index + 1} Base64转换失败:`, error.message);
104
+ }
105
+ });
106
+
107
+ console.log(`Base64转换结果: ${successCount} 成功, ${failCount} 失败`);
108
+ }
109
+
110
+ // 测试形状元素检测
111
+ function testShapeElementDetection() {
112
+ console.log('\n4. 测试形状元素检测...');
113
+
114
+ // 查找可能的形状元素
115
+ const shapeElements = document.querySelectorAll('[class*="base-element-"]');
116
+ console.log(`找到 ${shapeElements.length} 个可能的形状元素`);
117
+
118
+ shapeElements.forEach((element, index) => {
119
+ const svg = element.querySelector('svg');
120
+ if (svg) {
121
+ const elementId = element.className.match(/base-element-([^\s]+)/);
122
+ console.log(`形状元素 ${index + 1}:`, {
123
+ elementId: elementId ? elementId[1] : 'unknown',
124
+ element: element,
125
+ svg: svg,
126
+ svgSize: `${svg.clientWidth}x${svg.clientHeight}`,
127
+ hasValidSize: svg.clientWidth > 0 && svg.clientHeight > 0
128
+ });
129
+ }
130
+ });
131
+ }
132
+
133
+ // 测试渲染等待机制
134
+ function testRenderWait() {
135
+ console.log('\n5. 测试渲染等待机制...');
136
+
137
+ return new Promise((resolve) => {
138
+ const startTime = performance.now();
139
+
140
+ // 强制重绘
141
+ document.body.offsetHeight;
142
+
143
+ // 等待下一个动画帧
144
+ requestAnimationFrame(() => {
145
+ requestAnimationFrame(() => {
146
+ const endTime = performance.now();
147
+ console.log(`✓ 渲染等待完成,耗时: ${(endTime - startTime).toFixed(2)}ms`);
148
+ resolve();
149
+ });
150
+ });
151
+ });
152
+ }
153
+
154
+ // 运行所有测试
155
+ async function runAllTests() {
156
+ try {
157
+ testSVGElementDetection();
158
+ testSVGSerialization();
159
+ testBase64Conversion();
160
+ testShapeElementDetection();
161
+ await testRenderWait();
162
+
163
+ console.log('\n=== 测试完成 ===');
164
+ console.log('如果发现问题,请检查:');
165
+ console.log('1. SVG元素是否正确渲染');
166
+ console.log('2. 元素尺寸是否有效');
167
+ console.log('3. SVG序列化是否成功');
168
+ console.log('4. Base64转换是否正常');
169
+
170
+ } catch (error) {
171
+ console.error('测试过程中发生错误:', error);
172
+ }
173
+ }
174
+
175
+ // 导出测试函数到全局作用域
176
+ window.vectorExportTest = {
177
+ runAllTests,
178
+ testSVGElementDetection,
179
+ testSVGSerialization,
180
+ testBase64Conversion,
181
+ testShapeElementDetection,
182
+ testRenderWait
183
+ };
184
+
185
+ console.log('测试函数已加载,使用 vectorExportTest.runAllTests() 运行所有测试');
186
+
187
+ // 自动运行测试
188
+ runAllTests();
189
+ })();
vector_export_fix.md ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # PPT矢量元素导出问题修复方案
2
+
3
+ ## 问题分析
4
+
5
+ 通过代码分析,发现PPT内置矢量元素无法正常导出的主要原因:
6
+
7
+ ### 1. 核心问题
8
+ 在 `useExport.ts` 第977行存在一个临时处理逻辑:
9
+ ```typescript
10
+ if (svgRef.clientWidth < 1 || svgRef.clientHeight < 1) continue // 临时处理(导入PPTX文件带来的异常数据)
11
+ ```
12
+
13
+ 这个检查会跳过所有尺寸小于1像素的SVG元素,但某些矢量元素在特定情况下可能确实会出现这种情况。
14
+
15
+ ### 2. 相关问题
16
+ - SVG元素在DOM中可能未完全渲染
17
+ - `vector-effect="non-scaling-stroke"` 属性可能影响尺寸计算
18
+ - 特殊形状元素(special=true)的DOM查询可能失败
19
+ - SVG序列化过程缺乏错误处理
20
+
21
+ ## 修复方案
22
+
23
+ ### 方案1: 改进尺寸检查逻辑
24
+
25
+ ```typescript
26
+ // 替换原有的简单尺寸检查
27
+ if (el.special) {
28
+ const svgRef = document.querySelector(`.thumbnail-list .base-element-${el.id} svg`) as HTMLElement
29
+
30
+ // 改进的尺寸检查
31
+ if (!svgRef) {
32
+ console.warn(`SVG element not found for shape ${el.id}`);
33
+ continue;
34
+ }
35
+
36
+ // 获取多种尺寸信息进行判断
37
+ const clientWidth = svgRef.clientWidth;
38
+ const clientHeight = svgRef.clientHeight;
39
+ const boundingRect = svgRef.getBoundingClientRect();
40
+ const computedStyle = window.getComputedStyle(svgRef);
41
+
42
+ // 更智能的尺寸判断
43
+ const hasValidSize = (
44
+ (clientWidth > 0 && clientHeight > 0) ||
45
+ (boundingRect.width > 0 && boundingRect.height > 0) ||
46
+ (parseFloat(computedStyle.width) > 0 && parseFloat(computedStyle.height) > 0)
47
+ );
48
+
49
+ if (!hasValidSize) {
50
+ console.warn(`Invalid SVG dimensions for shape ${el.id}:`, {
51
+ clientWidth,
52
+ clientHeight,
53
+ boundingRect: { width: boundingRect.width, height: boundingRect.height },
54
+ computedStyle: { width: computedStyle.width, height: computedStyle.height }
55
+ });
56
+ continue;
57
+ }
58
+
59
+ // SVG序列化with错误处理
60
+ let base64SVG;
61
+ try {
62
+ base64SVG = svg2Base64(svgRef);
63
+ if (!base64SVG || base64SVG === 'data:image/svg+xml;base64,') {
64
+ throw new Error('SVG serialization returned empty result');
65
+ }
66
+ } catch (error) {
67
+ console.error(`SVG serialization failed for shape ${el.id}:`, error);
68
+ continue;
69
+ }
70
+
71
+ // 其余导出逻辑...
72
+ }
73
+ ```
74
+
75
+ ### 方案2: 添加渲染等待机制
76
+
77
+ ```typescript
78
+ // 在导出开始前添加渲染等待
79
+ const ensureElementsRendered = async () => {
80
+ return new Promise<void>((resolve) => {
81
+ // 强制重绘
82
+ document.body.offsetHeight;
83
+
84
+ // 等待下一个动画帧
85
+ requestAnimationFrame(() => {
86
+ requestAnimationFrame(() => {
87
+ resolve();
88
+ });
89
+ });
90
+ });
91
+ };
92
+
93
+ // 在exportPPTX函数开始处调用
94
+ export const exportPPTX = async (slides: Slide[], title: string, ignoreMedia = false) => {
95
+ exporting.value = true;
96
+
97
+ // 确保所有元素已渲染
98
+ await ensureElementsRendered();
99
+
100
+ // 其余导出逻辑...
101
+ }
102
+ ```
103
+
104
+ ### 方案3: 改进SVG处理逻辑
105
+
106
+ ```typescript
107
+ // 改进svg2Base64函数
108
+ export const svg2Base64 = (element: Element) => {
109
+ try {
110
+ // 克隆元素以避免修改原始DOM
111
+ const clonedElement = element.cloneNode(true) as Element;
112
+
113
+ // 确保SVG有正确的命名空间
114
+ if (clonedElement.tagName.toLowerCase() === 'svg') {
115
+ clonedElement.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
116
+ clonedElement.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
117
+ }
118
+
119
+ const XMLS = new XMLSerializer();
120
+ const svg = XMLS.serializeToString(clonedElement);
121
+
122
+ if (!svg || svg.length === 0) {
123
+ throw new Error('SVG serialization returned empty string');
124
+ }
125
+
126
+ const encoded = encode(svg);
127
+ if (!encoded) {
128
+ throw new Error('Base64 encoding failed');
129
+ }
130
+
131
+ return PREFIX + encoded;
132
+ } catch (error) {
133
+ console.error('svg2Base64 failed:', error);
134
+ throw error;
135
+ }
136
+ };
137
+ ```
138
+
139
+ ### 方案4: 添加降级处理
140
+
141
+ ```typescript
142
+ // 为特殊形状添加降级处理
143
+ else if (el.type === 'shape') {
144
+ if (el.special) {
145
+ let base64SVG;
146
+
147
+ try {
148
+ // 尝试从DOM获取SVG
149
+ const svgRef = document.querySelector(`.thumbnail-list .base-element-${el.id} svg`) as HTMLElement;
150
+ if (svgRef && svgRef.clientWidth > 0 && svgRef.clientHeight > 0) {
151
+ base64SVG = svg2Base64(svgRef);
152
+ } else {
153
+ throw new Error('SVG element not found or has invalid dimensions');
154
+ }
155
+ } catch (error) {
156
+ console.warn(`Failed to export special shape ${el.id} as SVG, falling back to path-based export:`, error);
157
+
158
+ // 降级到普通形状处理
159
+ const scale = {
160
+ x: el.width / el.viewBox[0],
161
+ y: el.height / el.viewBox[1],
162
+ };
163
+ const points = formatPoints(toPoints(el.path), scale);
164
+
165
+ let fillColor = formatColor(el.fill);
166
+ if (el.gradient) {
167
+ const colors = el.gradient.colors;
168
+ const color1 = colors[0].color;
169
+ const color2 = colors[colors.length - 1].color;
170
+ const color = tinycolor.mix(color1, color2).toHexString();
171
+ fillColor = formatColor(color);
172
+ }
173
+
174
+ const opacity = el.opacity === undefined ? 1 : el.opacity;
175
+
176
+ const options: pptxgen.ShapeProps = {
177
+ x: el.left / ratioPx2Inch.value,
178
+ y: el.top / ratioPx2Inch.value,
179
+ w: el.width / ratioPx2Inch.value,
180
+ h: el.height / ratioPx2Inch.value,
181
+ fill: { color: fillColor.color, transparency: (1 - fillColor.alpha * opacity) * 100 },
182
+ points,
183
+ };
184
+
185
+ if (el.flipH) options.flipH = el.flipH;
186
+ if (el.flipV) options.flipV = el.flipV;
187
+ if (el.shadow) options.shadow = getShadowOption(el.shadow);
188
+ if (el.outline?.width) options.line = getOutlineOption(el.outline);
189
+ if (el.rotate) options.rotate = el.rotate;
190
+
191
+ pptxSlide.addShape('custGeom' as pptxgen.ShapeType, options);
192
+ continue;
193
+ }
194
+
195
+ // 成功获取SVG的情况
196
+ if (base64SVG) {
197
+ const options: pptxgen.ImageProps = {
198
+ data: base64SVG,
199
+ x: el.left / ratioPx2Inch.value,
200
+ y: el.top / ratioPx2Inch.value,
201
+ w: el.width / ratioPx2Inch.value,
202
+ h: el.height / ratioPx2Inch.value,
203
+ };
204
+
205
+ if (el.rotate) options.rotate = el.rotate;
206
+ if (el.flipH) options.flipH = el.flipH;
207
+ if (el.flipV) options.flipV = el.flipV;
208
+ if (el.link) {
209
+ const linkOption = getLinkOption(el.link);
210
+ if (linkOption) options.hyperlink = linkOption;
211
+ }
212
+
213
+ pptxSlide.addImage(options);
214
+ }
215
+ }
216
+ // 普通形状处理逻辑保持不变
217
+ else {
218
+ // 现有的普通形状处理代码...
219
+ }
220
+ }
221
+ ```
222
+
223
+ ## 实施步骤
224
+
225
+ 1. **立即修复**: 实施方案1,改进尺寸检查逻辑
226
+ 2. **增强稳定性**: 实施方案2,添加渲染等待机制
227
+ 3. **提升兼容性**: 实施方案3,改进SVG处理
228
+ 4. **添加容错**: 实施方案4,添加降级处理
229
+
230
+ ## 测试验证
231
+
232
+ 使用提供的 `debug_vector_export.html` 文件进行测试:
233
+ 1. 打开调试页面
234
+ 2. 运行所有测试
235
+ 3. 检查SVG序列化和Base64转换是否正常
236
+ 4. 验证元素尺寸检测逻辑
237
+
238
+ ## 预期效果
239
+
240
+ 修复后应该能够:
241
+ - 正确导出所有类型的矢量元素
242
+ - 提供详细的错误日志用于问题排查
243
+ - 在特殊情况下提供降级处理方案
244
+ - 提高导出成功率和稳定性