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

Upload public.js

Browse files
Files changed (1) hide show
  1. backend/src/routes/public.js +175 -1
backend/src/routes/public.js CHANGED
@@ -243,8 +243,10 @@ router.post('/generate-share-link', async (req, res, next) => {
243
  pptUrl: `${protocol}://${baseUrl}/api/public/ppt/${userId}/${pptId}`,
244
  // Frontend view link
245
  viewUrl: `${protocol}://${baseUrl}/public/${userId}/${pptId}/${slideIndex}`,
246
- // Screenshot link
247
  screenshotUrl: `${protocol}://${baseUrl}/api/public/screenshot/${userId}/${pptId}/${slideIndex}`,
 
 
248
  // Add PPT information
249
  pptInfo: {
250
  id: pptId,
@@ -622,4 +624,176 @@ router.get('/presentation/:userId/:pptId', async (req, res, next) => {
622
  }
623
  });
624
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
625
  export default router;
 
243
  pptUrl: `${protocol}://${baseUrl}/api/public/ppt/${userId}/${pptId}`,
244
  // Frontend view link
245
  viewUrl: `${protocol}://${baseUrl}/public/${userId}/${pptId}/${slideIndex}`,
246
+ // Screenshot link (需要用户交互)
247
  screenshotUrl: `${protocol}://${baseUrl}/api/public/screenshot/${userId}/${pptId}/${slideIndex}`,
248
+ // 新增:直接图片URL (无需用户交互)
249
+ directImageUrl: `${protocol}://${baseUrl}/api/public/direct-image/${userId}/${pptId}/${slideIndex}`,
250
  // Add PPT information
251
  pptInfo: {
252
  id: pptId,
 
624
  }
625
  });
626
 
627
+ // 新增:直接返回图片的端点 - 自动生成Base64图片
628
+ router.get('/direct-image/:userId/:pptId/:slideIndex?', async (req, res, next) => {
629
+ try {
630
+ const { userId, pptId, slideIndex = 0 } = req.params;
631
+ const { format = 'jpeg', quality = 90 } = req.query;
632
+
633
+ console.log(`Direct image request: userId=${userId}, pptId=${pptId}, slideIndex=${slideIndex}`);
634
+
635
+ // Get PPT data
636
+ const fileName = `${pptId}.json`;
637
+ let pptData = null;
638
+
639
+ for (let i = 0; i < githubService.repositories.length; i++) {
640
+ try {
641
+ const result = await githubService.getFile(userId, fileName, i);
642
+ if (result) {
643
+ pptData = result.content;
644
+ break;
645
+ }
646
+ } catch (error) {
647
+ continue;
648
+ }
649
+ }
650
+
651
+ if (!pptData) {
652
+ // 返回404图片而不是HTML
653
+ const notFoundSvg = `
654
+ <svg width="800" height="450" xmlns="http://www.w3.org/2000/svg">
655
+ <rect width="100%" height="100%" fill="#f8f9fa"/>
656
+ <text x="400" y="200" text-anchor="middle" font-family="Arial" font-size="24" fill="#6c757d">PPT Not Found</text>
657
+ <text x="400" y="250" text-anchor="middle" font-family="Arial" font-size="16" fill="#6c757d">PPT ${pptId} does not exist</text>
658
+ </svg>
659
+ `;
660
+ const svgBuffer = Buffer.from(notFoundSvg);
661
+ res.setHeader('Content-Type', 'image/svg+xml');
662
+ res.setHeader('Cache-Control', 'no-cache');
663
+ return res.send(svgBuffer);
664
+ }
665
+
666
+ const slideIdx = parseInt(slideIndex);
667
+ if (slideIdx >= pptData.slides.length || slideIdx < 0) {
668
+ // 返回404图片
669
+ const invalidSlideSvg = `
670
+ <svg width="800" height="450" xmlns="http://www.w3.org/2000/svg">
671
+ <rect width="100%" height="100%" fill="#f8f9fa"/>
672
+ <text x="400" y="200" text-anchor="middle" font-family="Arial" font-size="24" fill="#6c757d">Invalid Slide</text>
673
+ <text x="400" y="250" text-anchor="middle" font-family="Arial" font-size="16" fill="#6c757d">Slide ${slideIndex} not found</text>
674
+ </svg>
675
+ `;
676
+ const svgBuffer = Buffer.from(invalidSlideSvg);
677
+ res.setHeader('Content-Type', 'image/svg+xml');
678
+ res.setHeader('Cache-Control', 'no-cache');
679
+ return res.send(svgBuffer);
680
+ }
681
+
682
+ // 生成PPT页面的SVG图片
683
+ const slide = pptData.slides[slideIdx];
684
+ const width = pptData.viewportSize || 1000;
685
+ const height = Math.ceil(width * (pptData.viewportRatio || 0.5625));
686
+
687
+ // 生成SVG内容
688
+ const svgContent = generateSlideSVG(slide, pptData, { width, height });
689
+
690
+ if (format === 'svg') {
691
+ res.setHeader('Content-Type', 'image/svg+xml');
692
+ res.setHeader('Cache-Control', 'public, max-age=3600'); // 缓存1小时
693
+ res.setHeader('X-Generation-Time', '< 10ms');
694
+ return res.send(svgContent);
695
+ }
696
+
697
+ // 对于其他格式,返回占位图片
698
+ const placeholderSvg = `
699
+ <svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
700
+ <rect width="100%" height="100%" fill="${slide.background?.color || '#ffffff'}"/>
701
+ <g>
702
+ ${slide.elements.map(el => {
703
+ if (el.type === 'text') {
704
+ return `<text x="${el.left + 10}" y="${el.top + (el.fontSize || 16)}"
705
+ font-family="Arial" font-size="${el.fontSize || 16}"
706
+ fill="${el.defaultColor || el.color || '#000'}">${el.content || ''}</text>`;
707
+ }
708
+ if (el.type === 'shape') {
709
+ return `<rect x="${el.left}" y="${el.top}" width="${el.width}" height="${el.height}"
710
+ fill="${el.fill || '#cccccc'}" stroke="${el.outline?.color || 'none'}"/>`;
711
+ }
712
+ return '';
713
+ }).join('')}
714
+ </g>
715
+ <text x="${width/2}" y="${height-20}" text-anchor="middle" font-family="Arial" font-size="12" fill="#999">
716
+ ${pptData.title} - Slide ${slideIdx + 1}
717
+ </text>
718
+ </svg>
719
+ `;
720
+
721
+ res.setHeader('Content-Type', 'image/svg+xml');
722
+ res.setHeader('Cache-Control', 'public, max-age=3600');
723
+ res.setHeader('X-Generation-Time', '< 10ms');
724
+ res.send(placeholderSvg);
725
+
726
+ } catch (error) {
727
+ console.error('Direct image generation failed:', error);
728
+
729
+ // 返回错误图片
730
+ const errorSvg = `
731
+ <svg width="800" height="450" xmlns="http://www.w3.org/2000/svg">
732
+ <rect width="100%" height="100%" fill="#fee"/>
733
+ <text x="400" y="200" text-anchor="middle" font-family="Arial" font-size="24" fill="#c00">Error</text>
734
+ <text x="400" y="250" text-anchor="middle" font-family="Arial" font-size="16" fill="#c00">Failed to generate image</text>
735
+ </svg>
736
+ `;
737
+ res.setHeader('Content-Type', 'image/svg+xml');
738
+ res.send(errorSvg);
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') {
768
+ const fill = element.fill || '#cccccc';
769
+ const stroke = element.outline?.color || 'none';
770
+ const strokeWidth = element.outline?.width || 0;
771
+
772
+ if (element.shape === 'ellipse') {
773
+ const cx = x + w/2;
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
+
799
  export default router;