first commit

This commit is contained in:
liamzi
2025-01-21 10:57:59 +08:00
commit 83096f7c9e
22 changed files with 8799 additions and 0 deletions

144
server.js Normal file
View File

@@ -0,0 +1,144 @@
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();