Upload useExport.ts
Browse files- frontend/src/hooks/useExport.ts +105 -39
frontend/src/hooks/useExport.ts
CHANGED
|
@@ -1156,12 +1156,20 @@ export default () => {
|
|
| 1156 |
continue;
|
| 1157 |
}
|
| 1158 |
|
| 1159 |
-
// 检查元素尺寸
|
| 1160 |
const dimensions = getElementDimensions(targetElement);
|
| 1161 |
console.log(`Target element dimensions for ${el.id}:`, dimensions);
|
| 1162 |
|
| 1163 |
-
|
| 1164 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1165 |
console.log('Falling back to path-based export');
|
| 1166 |
// 降级处理代码...
|
| 1167 |
const scale = {
|
|
@@ -1204,52 +1212,111 @@ export default () => {
|
|
| 1204 |
continue;
|
| 1205 |
}
|
| 1206 |
|
| 1207 |
-
//
|
| 1208 |
let base64Image;
|
|
|
|
|
|
|
|
|
|
| 1209 |
try {
|
| 1210 |
-
console.log(`
|
| 1211 |
-
|
| 1212 |
-
// Canvas渲染选项
|
| 1213 |
-
const renderOptions = {
|
| 1214 |
-
scale: 2, // 高分辨率渲染
|
| 1215 |
-
backgroundColor: null, // 透明背景
|
| 1216 |
-
useCORS: true,
|
| 1217 |
-
timeout: 10000, // 10秒超时
|
| 1218 |
-
format: 'png' as const,
|
| 1219 |
-
quality: 0.95
|
| 1220 |
-
};
|
| 1221 |
|
| 1222 |
-
|
| 1223 |
-
|
| 1224 |
-
|
| 1225 |
-
|
| 1226 |
-
|
| 1227 |
-
|
| 1228 |
-
|
| 1229 |
-
|
| 1230 |
-
|
| 1231 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1232 |
}
|
| 1233 |
-
} catch (
|
| 1234 |
-
console.
|
| 1235 |
-
|
| 1236 |
-
|
| 1237 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1238 |
try {
|
|
|
|
|
|
|
| 1239 |
const svgElement = targetElement.tagName.toLowerCase() === 'svg'
|
| 1240 |
? targetElement
|
| 1241 |
: targetElement.querySelector('svg');
|
| 1242 |
|
| 1243 |
if (svgElement) {
|
| 1244 |
-
|
| 1245 |
-
|
| 1246 |
-
|
| 1247 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1248 |
}
|
| 1249 |
-
} catch (
|
| 1250 |
-
console.error(`SVG
|
| 1251 |
-
|
| 1252 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1253 |
const scale = {
|
| 1254 |
x: el.width / el.viewBox[0],
|
| 1255 |
y: el.height / el.viewBox[1],
|
|
@@ -1288,7 +1355,6 @@ export default () => {
|
|
| 1288 |
|
| 1289 |
pptxSlide.addShape('custGeom' as pptxgen.ShapeType, fallbackOptions);
|
| 1290 |
continue;
|
| 1291 |
-
}
|
| 1292 |
}
|
| 1293 |
|
| 1294 |
// 添加渲染后的图像到幻灯片
|
|
|
|
| 1156 |
continue;
|
| 1157 |
}
|
| 1158 |
|
| 1159 |
+
// 检查元素尺寸 - 使用更智能的检查逻辑
|
| 1160 |
const dimensions = getElementDimensions(targetElement);
|
| 1161 |
console.log(`Target element dimensions for ${el.id}:`, dimensions);
|
| 1162 |
|
| 1163 |
+
// 更宽松的尺寸检查:只要有任何一种尺寸测量方法返回有效值就继续
|
| 1164 |
+
const hasValidDimensions = (
|
| 1165 |
+
dimensions.width > 0 || dimensions.height > 0 ||
|
| 1166 |
+
dimensions.clientWidth > 0 || dimensions.clientHeight > 0 ||
|
| 1167 |
+
dimensions.offsetWidth > 0 || dimensions.offsetHeight > 0 ||
|
| 1168 |
+
dimensions.boundingRect.width > 0 || dimensions.boundingRect.height > 0
|
| 1169 |
+
);
|
| 1170 |
+
|
| 1171 |
+
if (!hasValidDimensions) {
|
| 1172 |
+
console.warn(`No valid dimensions found for shape ${el.id}:`, dimensions);
|
| 1173 |
console.log('Falling back to path-based export');
|
| 1174 |
// 降级处理代码...
|
| 1175 |
const scale = {
|
|
|
|
| 1212 |
continue;
|
| 1213 |
}
|
| 1214 |
|
| 1215 |
+
// 使用多重策略渲染矢量图形
|
| 1216 |
let base64Image;
|
| 1217 |
+
let renderSuccess = false;
|
| 1218 |
+
|
| 1219 |
+
// 策略1: 优先使用SVG序列化(对矢量图形更可靠)
|
| 1220 |
try {
|
| 1221 |
+
console.log(`Attempting SVG serialization for ${el.id}`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1222 |
|
| 1223 |
+
const svgElement = targetElement.tagName.toLowerCase() === 'svg'
|
| 1224 |
+
? targetElement
|
| 1225 |
+
: targetElement.querySelector('svg');
|
| 1226 |
+
|
| 1227 |
+
if (svgElement) {
|
| 1228 |
+
// 确保SVG元素已完全渲染
|
| 1229 |
+
await new Promise(resolve => {
|
| 1230 |
+
requestAnimationFrame(() => {
|
| 1231 |
+
requestAnimationFrame(resolve);
|
| 1232 |
+
});
|
| 1233 |
+
});
|
| 1234 |
+
|
| 1235 |
+
base64Image = svg2Base64(svgElement);
|
| 1236 |
+
|
| 1237 |
+
console.log(`SVG serialization result for ${el.id}:`, {
|
| 1238 |
+
success: !!base64Image,
|
| 1239 |
+
length: base64Image ? base64Image.length : 0,
|
| 1240 |
+
preview: base64Image ? base64Image.substring(0, 100) + '...' : 'null'
|
| 1241 |
+
});
|
| 1242 |
+
|
| 1243 |
+
if (base64Image && base64Image.length > 100) {
|
| 1244 |
+
renderSuccess = true;
|
| 1245 |
+
console.log(`SVG serialization successful for ${el.id}`);
|
| 1246 |
+
}
|
| 1247 |
}
|
| 1248 |
+
} catch (svgError) {
|
| 1249 |
+
console.warn(`SVG serialization failed for ${el.id}:`, svgError);
|
| 1250 |
+
}
|
| 1251 |
+
|
| 1252 |
+
// 策略2: 如果SVG序列化失败,使用Canvas渲染
|
| 1253 |
+
if (!renderSuccess) {
|
| 1254 |
+
try {
|
| 1255 |
+
console.log(`Attempting Canvas rendering for ${el.id}`);
|
| 1256 |
+
|
| 1257 |
+
// Canvas渲染选项
|
| 1258 |
+
const renderOptions = {
|
| 1259 |
+
scale: 2, // 高分辨率渲染
|
| 1260 |
+
backgroundColor: null, // 透明背景
|
| 1261 |
+
useCORS: true,
|
| 1262 |
+
timeout: 15000, // 增加超时时间
|
| 1263 |
+
format: 'png' as const,
|
| 1264 |
+
quality: 0.95
|
| 1265 |
+
};
|
| 1266 |
+
|
| 1267 |
+
base64Image = await renderElementToBase64(targetElement, renderOptions);
|
| 1268 |
+
|
| 1269 |
+
console.log(`Canvas rendering result for ${el.id}:`, {
|
| 1270 |
+
success: !!base64Image,
|
| 1271 |
+
length: base64Image ? base64Image.length : 0,
|
| 1272 |
+
preview: base64Image ? base64Image.substring(0, 100) + '...' : 'null'
|
| 1273 |
+
});
|
| 1274 |
+
|
| 1275 |
+
if (base64Image && base64Image.length > 100) {
|
| 1276 |
+
renderSuccess = true;
|
| 1277 |
+
console.log(`Canvas rendering successful for ${el.id}`);
|
| 1278 |
+
}
|
| 1279 |
+
} catch (canvasError) {
|
| 1280 |
+
console.error(`Canvas rendering failed for ${el.id}:`, canvasError);
|
| 1281 |
+
}
|
| 1282 |
+
}
|
| 1283 |
+
|
| 1284 |
+
// 策略3: 如果前两种方法都失败,尝试简化的SVG渲染
|
| 1285 |
+
if (!renderSuccess) {
|
| 1286 |
try {
|
| 1287 |
+
console.log(`Attempting simplified SVG rendering for ${el.id}`);
|
| 1288 |
+
|
| 1289 |
const svgElement = targetElement.tagName.toLowerCase() === 'svg'
|
| 1290 |
? targetElement
|
| 1291 |
: targetElement.querySelector('svg');
|
| 1292 |
|
| 1293 |
if (svgElement) {
|
| 1294 |
+
// 创建简化的SVG副本
|
| 1295 |
+
const simplifiedSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
| 1296 |
+
const rect = svgElement.getBoundingClientRect();
|
| 1297 |
+
|
| 1298 |
+
simplifiedSVG.setAttribute('width', (rect.width || 100).toString());
|
| 1299 |
+
simplifiedSVG.setAttribute('height', (rect.height || 100).toString());
|
| 1300 |
+
simplifiedSVG.setAttribute('viewBox', `0 0 ${rect.width || 100} ${rect.height || 100}`);
|
| 1301 |
+
simplifiedSVG.innerHTML = svgElement.innerHTML;
|
| 1302 |
+
|
| 1303 |
+
base64Image = svg2Base64(simplifiedSVG);
|
| 1304 |
+
|
| 1305 |
+
if (base64Image && base64Image.length > 100) {
|
| 1306 |
+
renderSuccess = true;
|
| 1307 |
+
console.log(`Simplified SVG rendering successful for ${el.id}`);
|
| 1308 |
+
}
|
| 1309 |
}
|
| 1310 |
+
} catch (simplifiedError) {
|
| 1311 |
+
console.error(`Simplified SVG rendering failed for ${el.id}:`, simplifiedError);
|
| 1312 |
+
}
|
| 1313 |
+
}
|
| 1314 |
+
|
| 1315 |
+
// 如果所有渲染策略都失败,使用路径导出作为最后备选
|
| 1316 |
+
if (!renderSuccess) {
|
| 1317 |
+
console.warn(`All rendering strategies failed for ${el.id}, falling back to path export`);
|
| 1318 |
+
// 最终降级到路径导出
|
| 1319 |
+
console.log(`Final fallback to path-based export for ${el.id}`);
|
| 1320 |
const scale = {
|
| 1321 |
x: el.width / el.viewBox[0],
|
| 1322 |
y: el.height / el.viewBox[1],
|
|
|
|
| 1355 |
|
| 1356 |
pptxSlide.addShape('custGeom' as pptxgen.ShapeType, fallbackOptions);
|
| 1357 |
continue;
|
|
|
|
| 1358 |
}
|
| 1359 |
|
| 1360 |
// 添加渲染后的图像到幻灯片
|