61 lines
3.2 KiB
JavaScript
61 lines
3.2 KiB
JavaScript
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));
|
||
|
|
})();
|