const fs=require("fs"); const JSZip=require("jszip"); const {XMLParser}=require("fast-xml-parser"); const arrify=(v)=>Array.isArray(v)?v:(v==null?[]:[v]); const safeText=(v)=>typeof v==='string'&&v.trim()?v.trim():(v&&typeof v==='object'&&v.PlainText?String(v.PlainText).trim():"未命名节点"); const pickUid=(obj)=>Object.entries(obj||{}).find(([k,v])=>['OId','Guid','ID','Id','uid'].includes(k)&&typeof v==='string'&&v.trim())?.[1]||`node_${Math.random().toString(16).slice(2)}`; const parseUri=(input)=>{if(typeof input==='string') return input.trim(); if(input && typeof input==='object' && '#text' in input && typeof input['#text']==='string') return input['#text'].trim(); return null;}; async function parse(buf){ const zip=await JSZip.loadAsync(buf); const xml=await zip.file('Document.xml').async('text'); const parser=new XMLParser({ignoreAttributes:false,removeNSPrefix:true,attributeNamePrefix:''}); const parsed=parser.parse(xml); const rootTopic=(parsed.Map?.OneTopic?.Topic)||parsed.OneTopic?.Topic||parsed.Topic; const root=Array.isArray(rootTopic)?rootTopic[0]:rootTopic; const uriSet=new Set(); (function collect(node){ if(!node||typeof node!=='object') return; const images=[...arrify(node.OneImage), ...arrify(node.Image)]; images.forEach(img=>{ const uri=parseUri(img?.Image?.ImageData?.Uri ?? img?.ImageData?.Uri ?? img?.Uri); if(uri) uriSet.add(uri); }); const children=(arrify(node.SubTopics?.Topic)||arrify(node.Topics?.Topic)||arrify(node.Topic)); children.forEach(collect); })(root); const uriData=new Map(); const detectMime=(b)=> b.slice(0,8).equals(Buffer.from([0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a]))?'image/png':b.slice(0,3).equals(Buffer.from([0xff,0xd8,0xff]))?'image/jpeg':b.slice(0,2).equals(Buffer.from([0x42,0x4d]))?'image/bmp':'application/octet-stream'; for(const uri of uriSet){ const path=uri.startsWith('mmarch://')?uri.replace('mmarch://',''):uri; const file=zip.file(path); if(!file) continue; const bin=await file.async('nodebuffer'); const mime=detectMime(bin); uriData.set(uri,{url:`data:${mime};base64,${bin.toString('base64')}`}); } const conv=(node)=>{ const data={text:safeText(node?.Text??node?.text)}; const images=[...arrify(node?.OneImage), ...arrify(node?.Image)]; if(images.length){ const first=images[0]; const uri=parseUri(first?.Image?.ImageData?.Uri ?? first?.ImageData?.Uri ?? first?.Uri); const size=first?.Image?.ImageSize||first?.ImageSize; if(uri && uriData.has(uri)){ data.image=uriData.get(uri).url; if(size?.Width||size?.Height){ data.imageSize={width:Number(size.Width)||0,height:Number(size.Height)||0,custom:true}; } } } const kids=(arrify(node?.SubTopics?.Topic)||arrify(node?.Topics?.Topic)||arrify(node?.Topic)).map(conv); return {uid:pickUid(node||{}), data, children:kids}; }; return conv(root); } (async()=>{ const root=await parse(fs.readFileSync('..\\design\\导图1.mmap')); const imgs=[];const walk=(n)=>{if(n.data.image) imgs.push(n.data.image.slice(0,40));(n.children||[]).forEach(walk)};walk(root); console.log('image count', imgs.length); console.log('sample', imgs[0]?.slice(0,80)); })();