137 lines
3.9 KiB
Plaintext
137 lines
3.9 KiB
Plaintext
/**
|
|
* 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 <input.json> [output.json]');
|
|
process.exit(1);
|
|
}
|
|
const inputPath = args[0];
|
|
const outputPath = args[1] || inputPath.replace('.json', '_schema.json');
|
|
convert(inputPath, outputPath);
|