/** * web-to-pixso JSON → 翻译引擎 Schema 转换器 * * 把 web-to-pixso Chrome 扩展导出的 JSON 转成 renderer/index.js 能吃的标准格式 */ const fs = require('fs'); function convert(inputPath, outputPath) { const raw = JSON.parse(fs.readFileSync(inputPath, 'utf8')); const nodes = raw.nodes; const canvas = raw.canvas || {}; // 画布尺寸 const canvasW = canvas.width || 1920; const canvasH = canvas.height || 1080; // 收集所有页面(node.children 包含多页时按页面分组) const pages = []; function extractPage(node, parentY) { if (!node || !node.rect) return null; const rect = node.rect; const styles = node.styles || {}; const layout = node.layout || {}; const children = node.children || []; // 跳过不可见节点 if (styles.display === 'none' || styles.visibility === 'hidden') return null; const elements = []; // 如果是文字节点 if (node.type === 'text' && node.src) { elements.push({ type: 'text', text: node.src, options: { x: rect.x / 96, y: (rect.y - parentY) / 96, w: rect.w / 96, h: rect.h / 96, fontSize: styles.fontSize || 14, fontFace: (styles.fontFamily || 'Arial').split(',')[0].replace(/['"]/g, '').trim(), color: (styles.color || '#000000').replace('#', ''), bold: styles.fontWeight >= 700 || styles.fontWeight === 'bold', italic: styles.fontStyle === 'italic', align: styles.textAlign || 'left' } }); } // 如果有背景色或背景图,生成 shape if (styles.backgroundColor && styles.backgroundColor !== 'rgba(0,0,0,0)' && styles.backgroundColor !== 'transparent') { elements.push({ type: 'shape', shapeName: 'rect', options: { x: rect.x / 96, y: (rect.y - parentY) / 96, w: rect.w / 96, h: rect.h / 96, fill: { color: styles.backgroundColor.replace('#', '') } } }); } // 处理子节点 for (const childId of children) { const childNode = nodes[childId]; if (childNode) { const childElements = extractPage(childNode, parentY || rect.y); if (childElements) elements.push(...childElements); } } return elements; } // 找顶层容器 const root = nodes.children ? findRoot(nodes) : null; if (root) { const pageElements = extractPage(root, 0); if (pageElements && pageElements.length > 0) { pages.push({ objects: pageElements }); } } // 如果没有分页,把整个 canvas 当一页 if (pages.length === 0) { const allElements = []; for (const [id, node] of Object.entries(nodes)) { if (node.rect && node.type) { const elems = extractPage(node, 0); if (elems) allElements.push(...elems); } } pages.push({ objects: allElements }); } // 输出 Schema const spec = { presentation: { layout: 'LAYOUT_16x9' }, slides: pages }; fs.writeFileSync(outputPath, JSON.stringify(spec, null, 2)); console.log(`Converted: ${inputPath} → ${outputPath}`); console.log(`Pages: ${pages.length}, Total objects: ${pages.reduce((s,p) => s + p.objects.length, 0)}`); } function findRoot(nodes) { // 找没有 parent 引用的节点作为根 const allIds = new Set(Object.keys(nodes)); const childIds = new Set(); for (const [id, node] of Object.entries(nodes)) { if (node.children) { for (const cid of node.children) childIds.add(cid); } } for (const id of allIds) { if (!childIds.has(id)) return nodes[id]; } return null; } // CLI const args = process.argv.slice(2); if (args.length < 1) { console.error('Usage: node convert-w2p.js [output.json]'); process.exit(1); } const inputPath = args[0]; const outputPath = args[1] || inputPath.replace('.json', '_schema.json'); convert(inputPath, outputPath);