CatPtain commited on
Commit
cccb22d
·
verified ·
1 Parent(s): f11a4fe

Upload useExport.ts

Browse files
Files changed (1) hide show
  1. frontend/src/hooks/useExport.ts +117 -4
frontend/src/hooks/useExport.ts CHANGED
@@ -14,6 +14,8 @@ import { encrypt } from '@/utils/crypto'
14
  import { svg2Base64 } from '@/utils/svg2Base64'
15
  import { renderElementToBase64, isCanvasRenderSupported, getElementDimensions } from '@/utils/canvasRenderer'
16
  import { renderWithHuggingfaceFix, isHuggingfaceEnvironment } from '@/utils/huggingfaceRenderer'
 
 
17
  import message from '@/utils/message'
18
 
19
  interface ExportImageConfig {
@@ -37,19 +39,68 @@ export default () => {
37
 
38
  const exporting = ref(false)
39
 
40
- // 导出图片
41
  const exportImage = (domRef: HTMLElement, format: string, quality: number, ignoreWebfont = true) => {
42
  exporting.value = true
43
 
44
- // 检查是否在Huggingface环境
45
- const isHF = isHuggingfaceEnvironment()
46
- console.log('exportImage: Environment check:', { isHuggingface: isHF, format })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
  const foreignObjectSpans = domRef.querySelectorAll('foreignObject [xmlns]')
49
  foreignObjectSpans.forEach(spanRef => spanRef.removeAttribute('xmlns'))
50
 
51
  setTimeout(async () => {
52
  try {
 
 
 
53
  let dataUrl: string
54
 
55
  if (isHF) {
@@ -509,6 +560,46 @@ export default () => {
509
  }
510
 
511
  // 格式化元素
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
512
  const formatElement = (element: any) => {
513
  const baseStyle = `
514
  left: ${element.left || 0}px;
@@ -538,6 +629,28 @@ export default () => {
538
  }
539
 
540
  if (element.type === 'shape') {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
541
  const shapeStyle = `
542
  background: ${element.fill || '#ffffff'};
543
  border: ${element.outline?.width || 0}px solid ${element.outline?.color || '#000000'};
 
14
  import { svg2Base64 } from '@/utils/svg2Base64'
15
  import { renderElementToBase64, isCanvasRenderSupported, getElementDimensions } from '@/utils/canvasRenderer'
16
  import { renderWithHuggingfaceFix, isHuggingfaceEnvironment } from '@/utils/huggingfaceRenderer'
17
+ import { vectorRenderManager, RenderStrategy } from '@/utils/VectorRenderManager'
18
+ import { VECTOR_EXPORT_CONFIG } from '@/config/vectorExportConfig'
19
  import message from '@/utils/message'
20
 
21
  interface ExportImageConfig {
 
39
 
40
  const exporting = ref(false)
41
 
42
+ // 导出图片(生产环境优化版本)
43
  const exportImage = (domRef: HTMLElement, format: string, quality: number, ignoreWebfont = true) => {
44
  exporting.value = true
45
 
46
+ // 环境检测
47
+ const isHF = VECTOR_EXPORT_CONFIG.ENVIRONMENT.isHuggingface()
48
+ const isProd = VECTOR_EXPORT_CONFIG.ENVIRONMENT.isProduction()
49
+
50
+ if (!isProd) {
51
+ console.log('exportImage: Environment check:', { isHuggingface: isHF, format, production: isProd })
52
+ }
53
+
54
+ // 预处理矢量图形元素
55
+ const preprocessVectorElements = async () => {
56
+ const svgElements = domRef.querySelectorAll('svg');
57
+ const vectorShapes = domRef.querySelectorAll('.vector-shape');
58
+
59
+ // 处理SVG元素
60
+ for (const svg of Array.from(svgElements)) {
61
+ try {
62
+ // 移除problematic属性
63
+ svg.removeAttribute('vector-effect');
64
+ const vectorEffectElements = svg.querySelectorAll('[vector-effect]');
65
+ vectorEffectElements.forEach(el => el.removeAttribute('vector-effect'));
66
+
67
+ // 确保命名空间
68
+ if (!svg.hasAttribute('xmlns')) {
69
+ svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
70
+ }
71
+ } catch (error) {
72
+ if (!isProd) {
73
+ console.warn('exportImage: SVG preprocessing failed:', error);
74
+ }
75
+ }
76
+ }
77
+
78
+ // 处理矢量形状
79
+ for (const shape of Array.from(vectorShapes)) {
80
+ try {
81
+ const svgChild = shape.querySelector('svg');
82
+ if (svgChild) {
83
+ svgChild.removeAttribute('vector-effect');
84
+ if (!svgChild.hasAttribute('xmlns')) {
85
+ svgChild.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
86
+ }
87
+ }
88
+ } catch (error) {
89
+ if (!isProd) {
90
+ console.warn('exportImage: Vector shape preprocessing failed:', error);
91
+ }
92
+ }
93
+ }
94
+ };
95
 
96
  const foreignObjectSpans = domRef.querySelectorAll('foreignObject [xmlns]')
97
  foreignObjectSpans.forEach(spanRef => spanRef.removeAttribute('xmlns'))
98
 
99
  setTimeout(async () => {
100
  try {
101
+ // 预处理矢量元素
102
+ await preprocessVectorElements();
103
+
104
  let dataUrl: string
105
 
106
  if (isHF) {
 
560
  }
561
 
562
  // 格式化元素
563
+ // 生产环境级别的矢量图形SVG生成函数
564
+ const generateSVGFromShape = (element: any): string => {
565
+ try {
566
+ const { width, height, path, fill, outline, viewBox, opacity = 1 } = element;
567
+ const [vbX, vbY, vbWidth, vbHeight] = viewBox || [0, 0, width, height];
568
+
569
+ // 安全的颜色处理
570
+ const fillColor = fill || '#000000';
571
+ const strokeColor = outline?.color || 'none';
572
+ const strokeWidth = outline?.width || 0;
573
+
574
+ // 构建SVG字符串,确保所有属性都被正确转义
575
+ const svgAttributes = [
576
+ `width="${width}"`,
577
+ `height="${height}"`,
578
+ `viewBox="${vbX} ${vbY} ${vbWidth} ${vbHeight}"`,
579
+ 'xmlns="http://www.w3.org/2000/svg"',
580
+ 'style="display: block; width: 100%; height: 100%;"'
581
+ ].join(' ');
582
+
583
+ const pathAttributes = [
584
+ `d="${path}"`,
585
+ `fill="${fillColor}"`,
586
+ strokeWidth > 0 ? `stroke="${strokeColor}"` : '',
587
+ strokeWidth > 0 ? `stroke-width="${strokeWidth}"` : '',
588
+ opacity < 1 ? `opacity="${opacity}"` : '',
589
+ 'vector-effect="non-scaling-stroke"'
590
+ ].filter(Boolean).join(' ');
591
+
592
+ return `<svg ${svgAttributes}><path ${pathAttributes} /></svg>`;
593
+ } catch (error) {
594
+ console.error('generateSVGFromShape error:', error);
595
+ // 生产环境降级处理:返回简单的占位符
596
+ return `<svg width="${element.width || 100}" height="${element.height || 100}" xmlns="http://www.w3.org/2000/svg">
597
+ <rect width="100%" height="100%" fill="#f5f5f5" stroke="#ddd" stroke-width="1"/>
598
+ <text x="50%" y="50%" text-anchor="middle" dy="0.3em" font-size="12" fill="#999">Vector</text>
599
+ </svg>`;
600
+ }
601
+ };
602
+
603
  const formatElement = (element: any) => {
604
  const baseStyle = `
605
  left: ${element.left || 0}px;
 
629
  }
630
 
631
  if (element.type === 'shape') {
632
+ // 处理特殊矢量图形(生产环境优化)
633
+ if (element.special && element.path) {
634
+ try {
635
+ const svgContent = generateSVGFromShape(element);
636
+ return `<div class="element shape-element vector-shape" style="${baseStyle}">${svgContent}</div>`;
637
+ } catch (error) {
638
+ // 生产环境降级处理
639
+ if (VECTOR_EXPORT_CONFIG.ERROR_CONFIG.LOGGING.VERBOSE) {
640
+ console.warn('formatElement: Vector shape generation failed, using fallback:', error);
641
+ }
642
+
643
+ // 使用简化的矢量图形表示
644
+ const fallbackSvg = `<svg width="${element.width}" height="${element.height}" xmlns="http://www.w3.org/2000/svg">
645
+ <rect width="100%" height="100%" fill="${element.fill || '#f5f5f5'}" stroke="${element.outline?.color || '#ddd'}" stroke-width="${element.outline?.width || 1}"/>
646
+ <text x="50%" y="50%" text-anchor="middle" dy="0.3em" font-size="12" fill="#999">Vector</text>
647
+ </svg>`;
648
+
649
+ return `<div class="element shape-element vector-shape-fallback" style="${baseStyle}">${fallbackSvg}</div>`;
650
+ }
651
+ }
652
+
653
+ // 处理普通形状
654
  const shapeStyle = `
655
  background: ${element.fill || '#ffffff'};
656
  border: ${element.outline?.width || 0}px solid ${element.outline?.color || '#000000'};