const fastify = require('fastify')({ logger: true, bodyLimit: 100 * 1024 * 1024 // 設置為 100MB }); const { PDFDocument, PDFName, StandardFonts, PDFString, PDFPermissionFlag } = require('pdf-lib'); const cors = require('@fastify/cors'); // 更詳細的 CORS 設置 fastify.register(cors, { origin: ['*', 'null'], methods: ['GET', 'POST', 'OPTIONS'], allowedHeaders: ['Content-Type'] }); // 處理 PDF 生成請求 fastify.post('/generate-pdf', { bodyLimit: 100 * 1024 * 1024, // 100MB }, async (request, reply) => { try { const { frames, settings } = request.body; // 檢查 frames 是否為有效數組 if (!Array.isArray(frames) || frames.length === 0) { throw new Error('No frames provided or frames is not an array'); } // 創建 PDF 文件 const pdfDoc = await PDFDocument.create(); // 設置 PDF 元數據 if (settings.title) pdfDoc.setTitle(settings.title); if (settings.author) pdfDoc.setAuthor(settings.author); if (settings.subject) pdfDoc.setSubject(settings.subject); if (settings.keywords) pdfDoc.setKeywords(Array.isArray(settings.keywords) ? settings.keywords : []); if (settings.creator) pdfDoc.setCreator(settings.creator); if (settings.producer) pdfDoc.setProducer(settings.producer); if (settings.language) pdfDoc.setLanguage(settings.language); // 設置日期 pdfDoc.setCreationDate(settings.creationDate ? new Date(settings.creationDate) : new Date()); pdfDoc.setModificationDate(settings.modificationDate ? new Date(settings.modificationDate) : new Date()); // 根據品質設定調整圖片品質 const qualitySettings = { low: { scale: 1 }, medium: { scale: 2 }, high: { scale: 3 } }; const quality = qualitySettings[settings.quality || 'medium']; // 處理每個 Frame for (const frame of frames) { if (!frame.imageBase64) { fastify.log.warn(`No image data for frame: ${frame.name}`); continue; } try { // 解碼 base64 圖片數據 const imageBuffer = Buffer.from(frame.imageBase64, 'base64'); // 嵌入圖片到 PDF const image = await pdfDoc.embedPng(imageBuffer); // 獲取圖片尺寸 const { width, height } = image.scale(1); // 添加新頁面 const page = pdfDoc.addPage([width, height]); // 繪製圖片 page.drawImage(image, { x: 0, y: 0, width, height, }); } catch (error) { fastify.log.error(`Error processing frame ${frame.name}: ${error.message}`); continue; } } // 檢查是否有頁面被添加 if (pdfDoc.getPageCount() === 0) { throw new Error('No valid frames were processed'); } // 處理加密設置 if (settings.encryption?.enabled) { // 定義權限映射 const permissionsMap = { printing: PDFPermissionFlag.Print, modifying: PDFPermissionFlag.ModifyDocument, copying: PDFPermissionFlag.Extract, annotating: PDFPermissionFlag.Modify, }; // 收集啟用的權限 const permissions = []; if (settings.encryption.permissions?.printing) permissions.push(permissionsMap.printing); if (settings.encryption.permissions?.modifying) permissions.push(permissionsMap.modifying); if (settings.encryption.permissions?.copying) permissions.push(permissionsMap.copying); if (settings.encryption.permissions?.annotating) permissions.push(permissionsMap.annotating); // 應用加密設置 await pdfDoc.encrypt({ userPassword: settings.encryption.userPassword || undefined, ownerPassword: settings.encryption.ownerPassword || undefined, permissions: permissions }); } // 生成 PDF const pdfBytes = await pdfDoc.save(); // 設置響應頭 reply.header('Content-Type', 'application/pdf'); reply.header('Content-Disposition', `attachment; filename=${settings.filename || 'figma-export.pdf'}`); return reply.send(Buffer.from(pdfBytes)); } catch (error) { fastify.log.error(error); reply.code(500).send({ error: 'Failed to generate PDF', message: error.message }); } }); // 啟動服務器 const start = async () => { try { await fastify.listen({ port: 3000, host: '0.0.0.0' }); console.log('Server running at http://localhost:3000'); } catch (err) { fastify.log.error(err); process.exit(1); } }; start();