CatPtain commited on
Commit
bdc636e
·
verified ·
1 Parent(s): 18c248a

Upload public.js

Browse files
Files changed (1) hide show
  1. backend/src/routes/public.js +82 -16
backend/src/routes/public.js CHANGED
@@ -739,29 +739,86 @@ router.get('/direct-image/:userId/:pptId/:slideIndex?', async (req, res, next) =
739
  }
740
  });
741
 
742
- // 辅助函数:生成SVG
743
  function generateSlideSVG(slide, pptData, options = {}) {
744
  const { width = 1000, height = 562 } = options;
745
 
746
- const backgroundStyle = slide.background?.color || '#ffffff';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
747
 
 
748
  const elementsHTML = slide.elements.map(element => {
749
  const x = element.left || 0;
750
  const y = element.top || 0;
751
  const w = element.width || 100;
752
  const h = element.height || 100;
 
 
 
 
753
 
754
  if (element.type === 'text') {
755
  const fontSize = element.fontSize || 16;
756
- const fontFamily = element.fontName || 'Arial';
757
  const color = element.defaultColor || element.color || '#000000';
 
 
 
 
 
 
 
 
 
 
 
758
  const content = (element.content || '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
 
759
 
760
- return `<text x="${x + 10}" y="${y + fontSize}"
761
- font-family="${fontFamily}" font-size="${fontSize}"
762
- fill="${color}" font-weight="${element.bold ? 'bold' : 'normal'}"
763
- font-style="${element.italic ? 'italic' : 'normal'}"
764
- text-decoration="${element.underline ? 'underline' : 'none'}">${content}</text>`;
 
 
 
 
 
 
 
 
 
765
  }
766
 
767
  if (element.type === 'shape') {
@@ -774,25 +831,34 @@ function generateSlideSVG(slide, pptData, options = {}) {
774
  const cy = y + h/2;
775
  const rx = w/2;
776
  const ry = h/2;
777
- return `<ellipse cx="${cx}" cy="${cy}" rx="${rx}" ry="${ry}" fill="${fill}" stroke="${stroke}" stroke-width="${strokeWidth}"/>`;
778
  }
779
 
780
- return `<rect x="${x}" y="${y}" width="${w}" height="${h}" fill="${fill}" stroke="${stroke}" stroke-width="${strokeWidth}"/>`;
 
 
 
 
 
781
  }
782
 
783
  if (element.type === 'image' && element.src) {
784
- return `<image x="${x}" y="${y}" width="${w}" height="${h}" href="${element.src}"/>`;
 
 
 
 
 
 
785
  }
786
 
787
  return '';
788
- }).join('');
789
 
790
- return `<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
 
791
  <rect width="100%" height="100%" fill="${backgroundStyle}"/>
792
  <g>${elementsHTML}</g>
793
- <text x="${width-10}" y="${height-10}" text-anchor="end" font-family="Arial" font-size="10" fill="#999" opacity="0.7">
794
- ${pptData.title || 'PPTist'} - Generated at ${new Date().toISOString()}
795
- </text>
796
  </svg>`;
797
  }
798
 
 
739
  }
740
  });
741
 
742
+ // 辅助函数:生成SVG - 改进版,支持更精确的PPT渲染
743
  function generateSlideSVG(slide, pptData, options = {}) {
744
  const { width = 1000, height = 562 } = options;
745
 
746
+ // 处理背景
747
+ let backgroundStyle = '#ffffff';
748
+ if (slide.background) {
749
+ if (slide.background.type === 'solid') {
750
+ backgroundStyle = slide.background.color || '#ffffff';
751
+ } else if (slide.background.type === 'gradient' && slide.background.gradient) {
752
+ // SVG渐变处理
753
+ const colors = slide.background.gradient.colors || [];
754
+ if (colors.length > 0) {
755
+ backgroundStyle = colors[0].color || '#ffffff';
756
+ }
757
+ }
758
+ }
759
+
760
+ // 生成渐变定义(如果需要)
761
+ let gradientDefs = '';
762
+ if (slide.background?.type === 'gradient' && slide.background.gradient?.colors) {
763
+ const colors = slide.background.gradient.colors;
764
+ const gradientId = 'bg-gradient';
765
+ if (slide.background.gradient.type === 'linear') {
766
+ gradientDefs = `
767
+ <defs>
768
+ <linearGradient id="${gradientId}" x1="0%" y1="0%" x2="100%" y2="0%">
769
+ ${colors.map((color, index) =>
770
+ `<stop offset="${(index / (colors.length - 1)) * 100}%" style="stop-color:${color.color};stop-opacity:1" />`
771
+ ).join('')}
772
+ </linearGradient>
773
+ </defs>
774
+ `;
775
+ backgroundStyle = `url(#${gradientId})`;
776
+ }
777
+ }
778
 
779
+ // 渲染元素
780
  const elementsHTML = slide.elements.map(element => {
781
  const x = element.left || 0;
782
  const y = element.top || 0;
783
  const w = element.width || 100;
784
  const h = element.height || 100;
785
+ const rotation = element.rotate || 0;
786
+
787
+ // 变换属性
788
+ const transform = rotation !== 0 ? `transform="rotate(${rotation} ${x + w/2} ${y + h/2})"` : '';
789
 
790
  if (element.type === 'text') {
791
  const fontSize = element.fontSize || 16;
792
+ const fontFamily = element.fontName || 'Arial, sans-serif';
793
  const color = element.defaultColor || element.color || '#000000';
794
+ const fontWeight = element.bold ? 'bold' : 'normal';
795
+ const fontStyle = element.italic ? 'italic' : 'normal';
796
+ const textDecoration = element.underline ? 'underline' : 'none';
797
+ const textAnchor = element.align === 'center' ? 'middle' : element.align === 'right' ? 'end' : 'start';
798
+
799
+ // 计算文本位置
800
+ let textX = x + 10;
801
+ if (element.align === 'center') textX = x + w/2;
802
+ else if (element.align === 'right') textX = x + w - 10;
803
+
804
+ // 处理多行文本
805
  const content = (element.content || '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
806
+ const lines = content.split('\n');
807
 
808
+ return `
809
+ <g ${transform}>
810
+ ${lines.map((line, index) => `
811
+ <text x="${textX}" y="${y + fontSize + (index * fontSize * 1.2)}"
812
+ font-family="${fontFamily}"
813
+ font-size="${fontSize}"
814
+ fill="${color}"
815
+ font-weight="${fontWeight}"
816
+ font-style="${fontStyle}"
817
+ text-decoration="${textDecoration}"
818
+ text-anchor="${textAnchor}">${line}</text>
819
+ `).join('')}
820
+ </g>
821
+ `;
822
  }
823
 
824
  if (element.type === 'shape') {
 
831
  const cy = y + h/2;
832
  const rx = w/2;
833
  const ry = h/2;
834
+ return `<ellipse cx="${cx}" cy="${cy}" rx="${rx}" ry="${ry}" fill="${fill}" stroke="${stroke}" stroke-width="${strokeWidth}" ${transform}/>`;
835
  }
836
 
837
+ // 默认矩形
838
+ const borderRadius = element.borderRadius || 0;
839
+ if (borderRadius > 0) {
840
+ return `<rect x="${x}" y="${y}" width="${w}" height="${h}" rx="${borderRadius}" ry="${borderRadius}" fill="${fill}" stroke="${stroke}" stroke-width="${strokeWidth}" ${transform}/>`;
841
+ }
842
+ return `<rect x="${x}" y="${y}" width="${w}" height="${h}" fill="${fill}" stroke="${stroke}" stroke-width="${strokeWidth}" ${transform}/>`;
843
  }
844
 
845
  if (element.type === 'image' && element.src) {
846
+ // 检查是否是base64图片
847
+ if (element.src.startsWith('data:image/')) {
848
+ return `<image x="${x}" y="${y}" width="${w}" height="${h}" href="${element.src}" ${transform}/>`;
849
+ }
850
+ // 外部图片 - 在SVG中可能有跨域问题,显示占位符
851
+ return `<rect x="${x}" y="${y}" width="${w}" height="${h}" fill="#f0f0f0" stroke="#ccc" stroke-width="1" ${transform}/>
852
+ <text x="${x + w/2}" y="${y + h/2}" text-anchor="middle" font-size="12" fill="#666">图片</text>`;
853
  }
854
 
855
  return '';
856
+ }).filter(Boolean).join('');
857
 
858
+ return `<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
859
+ ${gradientDefs}
860
  <rect width="100%" height="100%" fill="${backgroundStyle}"/>
861
  <g>${elementsHTML}</g>
 
 
 
862
  </svg>`;
863
  }
864