CatPtain commited on
Commit
fce556e
·
verified ·
1 Parent(s): 227619b

Upload public.js

Browse files
Files changed (1) hide show
  1. backend/src/routes/public.js +125 -125
backend/src/routes/public.js CHANGED
@@ -6,6 +6,129 @@ import { generateSlideHTML, generateExportPage, exportPPTToJSON, generateHTMLPre
6
 
7
  const router = express.Router();
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  // Generate error page for frontend display
10
  function generateErrorPage(title, message) {
11
  return `
@@ -352,13 +475,13 @@ router.get('/screenshot/:userId/:pptId/:slideIndex?', async (req, res, next) =>
352
  }
353
  });
354
 
355
- // Image endpoint - 修改为直接返回图片,而不是截图工具页面
356
  router.get('/image/:userId/:pptId/:slideIndex?', async (req, res, next) => {
357
  try {
358
  const { userId, pptId, slideIndex = 0 } = req.params;
359
  const { format = 'svg', quality = 90, width: requestWidth, height: requestHeight } = req.query;
360
 
361
- console.log(`🖼️ Image request (direct): userId=${userId}, pptId=${pptId}, slideIndex=${slideIndex}, format=${format}`);
362
 
363
  // Get PPT data
364
  const fileName = `${pptId}.json`;
@@ -827,127 +950,4 @@ router.get('/direct-image/:userId/:pptId/:slideIndex?', async (req, res, next) =
827
  }
828
  });
829
 
830
- // 辅助函数:生成SVG - 改进版,支持更精确的PPT渲染
831
- function generateSlideSVG(slide, pptData, options = {}) {
832
- const { width = 1000, height = 562 } = options;
833
-
834
- // 处理背景
835
- let backgroundStyle = '#ffffff';
836
- if (slide.background) {
837
- if (slide.background.type === 'solid') {
838
- backgroundStyle = slide.background.color || '#ffffff';
839
- } else if (slide.background.type === 'gradient' && slide.background.gradient) {
840
- // SVG渐变处理
841
- const colors = slide.background.gradient.colors || [];
842
- if (colors.length > 0) {
843
- backgroundStyle = colors[0].color || '#ffffff';
844
- }
845
- }
846
- }
847
-
848
- // 生成渐变定义(如果需要)
849
- let gradientDefs = '';
850
- if (slide.background?.type === 'gradient' && slide.background.gradient?.colors) {
851
- const colors = slide.background.gradient.colors;
852
- const gradientId = 'bg-gradient';
853
- if (slide.background.gradient.type === 'linear') {
854
- gradientDefs = `
855
- <defs>
856
- <linearGradient id="${gradientId}" x1="0%" y1="0%" x2="100%" y2="0%">
857
- ${colors.map((color, index) =>
858
- `<stop offset="${(index / (colors.length - 1)) * 100}%" style="stop-color:${color.color};stop-opacity:1" />`
859
- ).join('')}
860
- </linearGradient>
861
- </defs>
862
- `;
863
- backgroundStyle = `url(#${gradientId})`;
864
- }
865
- }
866
-
867
- // 渲染元素
868
- const elementsHTML = slide.elements.map(element => {
869
- const x = element.left || 0;
870
- const y = element.top || 0;
871
- const w = element.width || 100;
872
- const h = element.height || 100;
873
- const rotation = element.rotate || 0;
874
-
875
- // 变换属性
876
- const transform = rotation !== 0 ? `transform="rotate(${rotation} ${x + w/2} ${y + h/2})"` : '';
877
-
878
- if (element.type === 'text') {
879
- const fontSize = element.fontSize || 16;
880
- const fontFamily = element.fontName || 'Arial, sans-serif';
881
- const color = element.defaultColor || element.color || '#000000';
882
- const fontWeight = element.bold ? 'bold' : 'normal';
883
- const fontStyle = element.italic ? 'italic' : 'normal';
884
- const textDecoration = element.underline ? 'underline' : 'none';
885
- const textAnchor = element.align === 'center' ? 'middle' : element.align === 'right' ? 'end' : 'start';
886
-
887
- // 计算文本位置
888
- let textX = x + 10;
889
- if (element.align === 'center') textX = x + w/2;
890
- else if (element.align === 'right') textX = x + w - 10;
891
-
892
- // 处理多行文本
893
- const content = (element.content || '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
894
- const lines = content.split('\n');
895
-
896
- return `
897
- <g ${transform}>
898
- ${lines.map((line, index) => `
899
- <text x="${textX}" y="${y + fontSize + (index * fontSize * 1.2)}"
900
- font-family="${fontFamily}"
901
- font-size="${fontSize}"
902
- fill="${color}"
903
- font-weight="${fontWeight}"
904
- font-style="${fontStyle}"
905
- text-decoration="${textDecoration}"
906
- text-anchor="${textAnchor}">${line}</text>
907
- `).join('')}
908
- </g>
909
- `;
910
- }
911
-
912
- if (element.type === 'shape') {
913
- const fill = element.fill || '#cccccc';
914
- const stroke = element.outline?.color || 'none';
915
- const strokeWidth = element.outline?.width || 0;
916
-
917
- if (element.shape === 'ellipse') {
918
- const cx = x + w/2;
919
- const cy = y + h/2;
920
- const rx = w/2;
921
- const ry = h/2;
922
- return `<ellipse cx="${cx}" cy="${cy}" rx="${rx}" ry="${ry}" fill="${fill}" stroke="${stroke}" stroke-width="${strokeWidth}" ${transform}/>`;
923
- }
924
-
925
- // 默认矩形
926
- const borderRadius = element.borderRadius || 0;
927
- if (borderRadius > 0) {
928
- return `<rect x="${x}" y="${y}" width="${w}" height="${h}" rx="${borderRadius}" ry="${borderRadius}" fill="${fill}" stroke="${stroke}" stroke-width="${strokeWidth}" ${transform}/>`;
929
- }
930
- return `<rect x="${x}" y="${y}" width="${w}" height="${h}" fill="${fill}" stroke="${stroke}" stroke-width="${strokeWidth}" ${transform}/>`;
931
- }
932
-
933
- if (element.type === 'image' && element.src) {
934
- // 检查是否是base64图片
935
- if (element.src.startsWith('data:image/')) {
936
- return `<image x="${x}" y="${y}" width="${w}" height="${h}" href="${element.src}" ${transform}/>`;
937
- }
938
- // 外部图片 - 在SVG中可能有跨域问题,显示占位符
939
- return `<rect x="${x}" y="${y}" width="${w}" height="${h}" fill="#f0f0f0" stroke="#ccc" stroke-width="1" ${transform}/>
940
- <text x="${x + w/2}" y="${y + h/2}" text-anchor="middle" font-size="12" fill="#666">图片</text>`;
941
- }
942
-
943
- return '';
944
- }).filter(Boolean).join('');
945
-
946
- return `<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
947
- ${gradientDefs}
948
- <rect width="100%" height="100%" fill="${backgroundStyle}"/>
949
- <g>${elementsHTML}</g>
950
- </svg>`;
951
- }
952
-
953
  export default router;
 
6
 
7
  const router = express.Router();
8
 
9
+ // 辅助函数:生成SVG - 改进版,支持更精确的PPT渲染
10
+ function generateSlideSVG(slide, pptData, options = {}) {
11
+ const { width = 1000, height = 562 } = options;
12
+
13
+ // 处理背景
14
+ let backgroundStyle = '#ffffff';
15
+ if (slide.background) {
16
+ if (slide.background.type === 'solid') {
17
+ backgroundStyle = slide.background.color || '#ffffff';
18
+ } else if (slide.background.type === 'gradient' && slide.background.gradient) {
19
+ // SVG渐变处理
20
+ const colors = slide.background.gradient.colors || [];
21
+ if (colors.length > 0) {
22
+ backgroundStyle = colors[0].color || '#ffffff';
23
+ }
24
+ }
25
+ }
26
+
27
+ // 生成渐变定义(如果需要)
28
+ let gradientDefs = '';
29
+ if (slide.background?.type === 'gradient' && slide.background.gradient?.colors) {
30
+ const colors = slide.background.gradient.colors;
31
+ const gradientId = 'bg-gradient';
32
+ if (slide.background.gradient.type === 'linear') {
33
+ gradientDefs = `
34
+ <defs>
35
+ <linearGradient id="${gradientId}" x1="0%" y1="0%" x2="100%" y2="0%">
36
+ ${colors.map((color, index) =>
37
+ `<stop offset="${(index / (colors.length - 1)) * 100}%" style="stop-color:${color.color};stop-opacity:1" />`
38
+ ).join('')}
39
+ </linearGradient>
40
+ </defs>
41
+ `;
42
+ backgroundStyle = `url(#${gradientId})`;
43
+ }
44
+ }
45
+
46
+ // 渲染元素
47
+ const elementsHTML = slide.elements.map(element => {
48
+ const x = element.left || 0;
49
+ const y = element.top || 0;
50
+ const w = element.width || 100;
51
+ const h = element.height || 100;
52
+ const rotation = element.rotate || 0;
53
+
54
+ // 变换属性
55
+ const transform = rotation !== 0 ? `transform="rotate(${rotation} ${x + w/2} ${y + h/2})"` : '';
56
+
57
+ if (element.type === 'text') {
58
+ const fontSize = element.fontSize || 16;
59
+ const fontFamily = element.fontName || 'Arial, sans-serif';
60
+ const color = element.defaultColor || element.color || '#000000';
61
+ const fontWeight = element.bold ? 'bold' : 'normal';
62
+ const fontStyle = element.italic ? 'italic' : 'normal';
63
+ const textDecoration = element.underline ? 'underline' : 'none';
64
+ const textAnchor = element.align === 'center' ? 'middle' : element.align === 'right' ? 'end' : 'start';
65
+
66
+ // 计算文本位置
67
+ let textX = x + 10;
68
+ if (element.align === 'center') textX = x + w/2;
69
+ else if (element.align === 'right') textX = x + w - 10;
70
+
71
+ // 处理多行文本
72
+ const content = (element.content || '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
73
+ const lines = content.split('\n');
74
+
75
+ return `
76
+ <g ${transform}>
77
+ ${lines.map((line, index) => `
78
+ <text x="${textX}" y="${y + fontSize + (index * fontSize * 1.2)}"
79
+ font-family="${fontFamily}"
80
+ font-size="${fontSize}"
81
+ fill="${color}"
82
+ font-weight="${fontWeight}"
83
+ font-style="${fontStyle}"
84
+ text-decoration="${textDecoration}"
85
+ text-anchor="${textAnchor}">${line}</text>
86
+ `).join('')}
87
+ </g>
88
+ `;
89
+ }
90
+
91
+ if (element.type === 'shape') {
92
+ const fill = element.fill || '#cccccc';
93
+ const stroke = element.outline?.color || 'none';
94
+ const strokeWidth = element.outline?.width || 0;
95
+
96
+ if (element.shape === 'ellipse') {
97
+ const cx = x + w/2;
98
+ const cy = y + h/2;
99
+ const rx = w/2;
100
+ const ry = h/2;
101
+ return `<ellipse cx="${cx}" cy="${cy}" rx="${rx}" ry="${ry}" fill="${fill}" stroke="${stroke}" stroke-width="${strokeWidth}" ${transform}/>`;
102
+ }
103
+
104
+ // 默认矩形
105
+ const borderRadius = element.borderRadius || 0;
106
+ if (borderRadius > 0) {
107
+ return `<rect x="${x}" y="${y}" width="${w}" height="${h}" rx="${borderRadius}" ry="${borderRadius}" fill="${fill}" stroke="${stroke}" stroke-width="${strokeWidth}" ${transform}/>`;
108
+ }
109
+ return `<rect x="${x}" y="${y}" width="${w}" height="${h}" fill="${fill}" stroke="${stroke}" stroke-width="${strokeWidth}" ${transform}/>`;
110
+ }
111
+
112
+ if (element.type === 'image' && element.src) {
113
+ // 检查是否是base64图片
114
+ if (element.src.startsWith('data:image/')) {
115
+ return `<image x="${x}" y="${y}" width="${w}" height="${h}" href="${element.src}" ${transform}/>`;
116
+ }
117
+ // 外部图片 - 在SVG中可能有跨域问题,显示占位符
118
+ return `<rect x="${x}" y="${y}" width="${w}" height="${h}" fill="#f0f0f0" stroke="#ccc" stroke-width="1" ${transform}/>
119
+ <text x="${x + w/2}" y="${y + h/2}" text-anchor="middle" font-size="12" fill="#666">图片</text>`;
120
+ }
121
+
122
+ return '';
123
+ }).filter(Boolean).join('');
124
+
125
+ return `<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
126
+ ${gradientDefs}
127
+ <rect width="100%" height="100%" fill="${backgroundStyle}"/>
128
+ <g>${elementsHTML}</g>
129
+ </svg>`;
130
+ }
131
+
132
  // Generate error page for frontend display
133
  function generateErrorPage(title, message) {
134
  return `
 
475
  }
476
  });
477
 
478
+ // 🔥 关键修改:image端点现在直接返回SVG图片,不再返回截图工具页面
479
  router.get('/image/:userId/:pptId/:slideIndex?', async (req, res, next) => {
480
  try {
481
  const { userId, pptId, slideIndex = 0 } = req.params;
482
  const { format = 'svg', quality = 90, width: requestWidth, height: requestHeight } = req.query;
483
 
484
+ console.log(`🖼️ Direct Image request: userId=${userId}, pptId=${pptId}, slideIndex=${slideIndex}, format=${format}`);
485
 
486
  // Get PPT data
487
  const fileName = `${pptId}.json`;
 
950
  }
951
  });
952
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
953
  export default router;