// 前后端共享的PPT导出工具 // 可以在浏览器和Node.js环境中运行 export const generateSlideHTML = (pptData, slideIndex, options = {}) => { const { width = 1000, height = 562, includeInteractive = false, format = 'view' // 'view' | 'export' | 'screenshot' } = options; const slide = pptData.slides[slideIndex]; if (!slide) { throw new Error(`Slide ${slideIndex} not found`); } const theme = pptData.theme || {}; // 计算实际尺寸 const actualWidth = pptData.viewportSize || width; const actualHeight = Math.ceil(actualWidth * (pptData.viewportRatio || 0.5625)); // 渲染元素 const renderElements = (elements) => { if (!elements || elements.length === 0) return ''; return elements.map(element => { const baseStyle = ` position: absolute; left: ${element.left || 0}px; top: ${element.top || 0}px; width: ${element.width || 0}px; height: ${element.height || 0}px; z-index: ${element.zIndex || 1}; transform: rotate(${element.rotate || 0}deg); `; let content = ''; let specificStyles = ''; switch (element.type) { case 'text': const fontFamily = element.fontName || 'Microsoft YaHei'; const fontFallback = `${fontFamily}, 'Noto Sans SC', 'PingFang SC', 'Hiragino Sans GB', 'SimHei', 'SimSun', Arial, Helvetica, sans-serif`; specificStyles = ` font-family: ${fontFallback}; font-size: ${element.fontSize || 14}px; color: ${element.defaultColor || element.color || '#000'}; font-weight: ${element.bold ? 'bold' : 'normal'}; font-style: ${element.italic ? 'italic' : 'normal'}; text-decoration: ${element.underline ? 'underline' : 'none'}; text-align: ${element.align || 'left'}; line-height: ${element.lineHeight || 1.2}; padding: 10px; word-wrap: break-word; overflow: hidden; box-sizing: border-box; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility; `; content = element.content || ''; break; case 'image': specificStyles = ` background-image: url('${element.src}'); background-size: ${element.objectFit || 'cover'}; background-position: center; background-repeat: no-repeat; `; break; case 'shape': specificStyles = ` background-color: ${element.fill || 'transparent'}; border: ${element.outline?.width || 0}px ${element.outline?.style || 'solid'} ${element.outline?.color || 'transparent'}; border-radius: ${element.shape === 'ellipse' ? '50%' : '0px'}; `; break; } return `
${content}
`; }).join(''); }; // 生成样式 const generateStyles = () => { const baseStyles = ` * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: 'Microsoft YaHei', 'Noto Sans SC', 'PingFang SC', 'Hiragino Sans GB', 'SimHei', 'SimSun', Arial, Helvetica, sans-serif; margin: 0; padding: 0; } .slide-container { position: relative; width: ${actualWidth}px; height: ${actualHeight}px; background: ${slide.background?.color || theme.backgroundColor || '#ffffff'}; margin: 0 auto; overflow: hidden; } `; if (format === 'view') { return baseStyles + ` html, body { width: 100vw; height: 100vh; background: #000; display: flex; align-items: center; justify-content: center; overflow: hidden; } .slide-container { transform-origin: center center; } `; } return baseStyles; }; // 生成JavaScript(仅用于view模式) const generateScript = () => { if (format !== 'view') return ''; return ` `; }; return ` ${pptData.title || 'PPT'} - Page ${slideIndex + 1}
${renderElements(slide.elements)}
${generateScript()} `; }; export const generateExportPage = (pptData, slideIndex, options = {}) => { const { format = 'jpeg', quality = 90, autoDownload = false } = options; const slideHTML = generateSlideHTML(pptData, slideIndex, { ...options, format: 'export' }); // 提取slide-container内容 const slideContent = slideHTML.match(/
(.*?)<\/div>/s)?.[1] || ''; return ` PPT Export - ${pptData.title}

🚀 PPT Export

${pptData.title} - Slide ${slideIndex + 1}

${slideContent}
Ready to generate screenshot
`; }; // 新增:前端导出功能的服务器端实现 export const exportPPTToJSON = (pptData) => { return { title: pptData.title, width: pptData.viewportSize || 1000, height: Math.ceil((pptData.viewportSize || 1000) * (pptData.viewportRatio || 0.5625)), theme: pptData.theme, slides: pptData.slides, exportedAt: new Date().toISOString(), format: 'json', version: '1.0' }; }; // 新增:生成完整的HTML演示文稿 export const generateHTMLPresentation = (pptData, options = {}) => { const { includeInteractivity = true, standalone = true, includeCSS = true } = options; const viewportSize = pptData.viewportSize || 1000; const viewportRatio = pptData.viewportRatio || 0.5625; const slideHeight = Math.round(viewportSize * viewportRatio); const theme = pptData.theme || {}; // CSS样式 const css = includeCSS ? ` ` : ''; // JavaScript交互功能 const javascript = includeInteractivity ? ` ` : ''; // 格式化幻灯片背景 const formatSlideBackground = (background) => { if (!background) return 'background: #ffffff;'; if (background.type === 'solid') { return `background: ${background.color || '#ffffff'};`; } if (background.type === 'gradient') { const { gradientType, colors } = background; if (gradientType === 'linear') { return `background: linear-gradient(${background.gradientRotate || 0}deg, ${colors.map(c => c.color).join(', ')});`; } return `background: radial-gradient(${colors.map(c => c.color).join(', ')});`; } if (background.type === 'image' && background.image) { return `background-image: url(${background.image.src}); background-size: cover; background-position: center;`; } return 'background: #ffffff;'; }; // 格式化元素 const formatElement = (element) => { const baseStyle = ` left: ${element.left || 0}px; top: ${element.top || 0}px; width: ${element.width || 100}px; height: ${element.height || 100}px; transform: rotate(${element.rotate || 0}deg); `; if (element.type === 'text') { const fontFamily = element.fontName || 'Microsoft YaHei'; const fontFallback = `${fontFamily}, 'Noto Sans SC', 'PingFang SC', 'Hiragino Sans GB', 'SimHei', 'SimSun', Arial, Helvetica, sans-serif`; const textStyle = ` font-size: ${element.fontSize || 16}px; font-family: ${fontFallback}; color: ${element.defaultColor || element.color || '#000000'}; font-weight: ${element.bold ? 'bold' : 'normal'}; font-style: ${element.italic ? 'italic' : 'normal'}; text-decoration: ${element.underline ? 'underline' : 'none'}; text-align: ${element.align || 'left'}; line-height: ${element.lineHeight || 1.5}; padding: 10px; word-wrap: break-word; overflow: hidden; box-sizing: border-box; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility; `; return `
${element.content || ''}
`; } if (element.type === 'image' && element.src) { return `
图片
`; } if (element.type === 'shape') { const shapeStyle = ` background: ${element.fill || '#ffffff'}; border: ${element.outline?.width || 0}px solid ${element.outline?.color || '#000000'}; border-radius: ${element.borderRadius || 0}px; `; return `
`; } // 其他元素类型的基本处理 return `
`; }; // 生成幻灯片HTML const slidesHTML = pptData.slides.map((slide, index) => { const slideBackground = formatSlideBackground(slide.background); const elementsHTML = slide.elements.map(element => formatElement(element)).join(''); return `
${elementsHTML}
`; }).join(''); // 导航控件HTML const navigationHTML = includeInteractivity ? `
1 / ${pptData.slides.length}
` : ''; // 组装完整HTML return ` ${pptData.title} ${css}
${slidesHTML}
${navigationHTML} ${javascript} `; };