code wiki / _hdl_build / nx_pptx.nx

nx_pptx.nx source

↩ module page · 299 lines · 17473 B

1// nx_pptx.nx -- SOVEREIGN presentations (.pptx / PresentationML) for the Nishi Office suite: the census's 2// biggest ABSENT category (OF-P1 author + OF-P2 render). A .pptx is an OPC package (shared nx_opc, STORED 3// ZIP like .docx/.xlsx) with ppt/presentation.xml + ppt/slides/slideN.xml parts. 4// fromspec : spec text -> deck. "S <title>" starts a slide, "B <bullet>" adds a bullet (XML-escaped). 5// read : deck -> per-slide title + bullets (walks slide parts, extracts <a:t> runs). 6// html : deck -> 0-JS inline SLIDE-DECK preview (16:9 cards) the Nishi browser (and the nx_doc_view 7// registry) renders -- same `html <in> <out> [dlurl]` iface as nx_docx/nx_md/nx_csv/nx_pdf/nx_xlsx. 8// HONEST SCOPE (v1, named): title + bullet slides only -- no slide masters/layouts/themes/images/animations. 9// The package is valid OPC + PresentationML that OUR tools round-trip; "opens in PowerPoint" is NOT claimed 10// until verified against real PowerPoint (masters/layouts may be required there) -- that check is a named 11// follow-up, exactly like the docx harness's third-party validation was. 12// nx_pptx fromspec <spec.txt> <out.pptx> | read <in.pptx> | html <in.pptx> <out.html> [dlurl] 13// license_tier: ORIGINAL 14import "nx_syscalls.nx" 15import "nx_opc.nx" 16const PP_MAGIC_1048592: i64 = 1048592 17const PP_MAGIC_1048576: i64 = 1048576 18const PP_MAGIC_16384: i64 = 16384 19const PP_MAGIC_262144: i64 = 262144 20const PP_MAGIC_65536: i64 = 65536 21const PP_MAGIC_8388608: i64 = 8388608 22 23const PP_MAXSLIDES: i64 = 64 24 25func pp_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8) { n=n+1 } return n } 26func pp_puts(s: *u8) -> i64 { sys_write(1, s, pp_slen(s)); return 0 } 27func pp_num(v: i64) -> i64 { 28 let b: *u8=sys_mmap(28); var m: i64=v; if m<0 { m=0-m; sys_write(1,"-" as *u8,1) } 29 let t: *u8=sys_mmap(28); var k: i64=0; if m==0 { t[0]=48 as u8; k=1 } 30 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } 31 var i: i64=0; while i<k { b[i]=t[k-1-i]; i=i+1 } sys_write(1,b,k); return 0 32} 33func pp_streq(a: *u8, b: *u8) -> i64 { var i: i64=0; while a[i]!=(0 as u8) { if a[i]!=b[i] { return 0 } i=i+1 } if b[i]!=(0 as u8) { return 0 } return 1 } 34func pp_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8) { dst[off+i]=s[i]; i=i+1 } return off+i } 35func pp_uint(dst: *u8, off: i64, v: i64) -> i64 { 36 var m: i64=v; let t: *u8=sys_mmap(28); var k: i64=0; if m==0 { t[0]=48 as u8; k=1 } 37 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } 38 var o: i64=off; var i: i64=0; while i<k { dst[o]=t[k-1-i]; o=o+1; i=i+1 } return o 39} 40func pp_esc(out: *u8, o: i64, src: *u8, soff: i64, slen: i64) -> i64 { 41 var oo: i64=o; var k: i64=0 42 while k<slen { 43 let c: i64=src[soff+k] as i64 44 if c==38 { oo=pp_cat(out,oo,"&amp;" as *u8) } else { if c==60 { oo=pp_cat(out,oo,"&lt;" as *u8) } else { if c==62 { oo=pp_cat(out,oo,"&gt;" as *u8) } else { out[oo]=src[soff+k]; oo=oo+1 } } } 45 k=k+1 46 } 47 return oo 48} 49func pp_readfile(path: *u8, buf: *u8, cap: i64) -> i64 { 50 let fd: i64=sys_openat_rd(path); if fd<0 { return 0-1 } 51 var tot: i64=0; while tot<cap { let r: i64=sys_read(fd,(buf as i64+tot) as *u8,cap-tot); if r<=0 { break } tot=tot+r } 52 sys_close(fd); return tot 53} 54func pp_match(buf: *u8, pos: i64, n: i64, pat: *u8) -> i64 { 55 var i: i64=0 56 while pat[i]!=(0 as u8) { if pos+i>=n { return 0 } if buf[pos+i]!=pat[i] { return 0 } i=i+1 } 57 return 1 58} 59 60// one slide's XML: title shape + one body shape with a bullet paragraph per bullet 61func pp_build_slide(out: *u8, spec: *u8, tso: *i64, tsl: *i64, nb: i64) -> i64 { 62 // tso/tsl: [0]=title span, [1..nb]=bullet spans 63 var o: i64=0 64 o=pp_cat(out,o,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<p:sld xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" xmlns:p=\"http://schemas.openxmlformats.org/presentationml/2006/main\"><p:cSld><p:spTree><p:nvGrpSpPr><p:cNvPr id=\"1\" name=\"\"/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr><p:grpSpPr/>" as *u8) 65 // title shape 66 o=pp_cat(out,o,"<p:sp><p:nvSpPr><p:cNvPr id=\"2\" name=\"Title\"/><p:cNvSpPr/><p:nvPr/></p:nvSpPr><p:spPr><a:xfrm><a:off x=\"838200\" y=\"365125\"/><a:ext cx=\"10515600\" cy=\"1325563\"/></a:xfrm></p:spPr><p:txBody><a:bodyPr/><a:p><a:r><a:rPr lang=\"en-US\" sz=\"3600\" b=\"1\"/><a:t>" as *u8) 67 o=pp_esc(out,o,spec,tso[0],tsl[0]) 68 o=pp_cat(out,o,"</a:t></a:r></a:p></p:txBody></p:sp>" as *u8) 69 // body shape with bullets 70 if nb>0 { 71 o=pp_cat(out,o,"<p:sp><p:nvSpPr><p:cNvPr id=\"3\" name=\"Content\"/><p:cNvSpPr/><p:nvPr/></p:nvSpPr><p:spPr><a:xfrm><a:off x=\"838200\" y=\"1825625\"/><a:ext cx=\"10515600\" cy=\"4351338\"/></a:xfrm></p:spPr><p:txBody><a:bodyPr/>" as *u8) 72 var bi: i64=1 73 while bi<=nb { 74 o=pp_cat(out,o,"<a:p><a:r><a:rPr lang=\"en-US\" sz=\"2000\"/><a:t>" as *u8) 75 o=pp_esc(out,o,spec,tso[bi],tsl[bi]) 76 o=pp_cat(out,o,"</a:t></a:r></a:p>" as *u8) 77 bi=bi+1 78 } 79 o=pp_cat(out,o,"</p:txBody></p:sp>" as *u8) 80 } 81 o=pp_cat(out,o,"</p:spTree></p:cSld></p:sld>" as *u8) 82 return o 83} 84 85// spec -> deck: parse "S <title>" / "B <bullet>" lines, build one slide part per S. 86func pptx_write_spec(specpath: *u8, outpath: *u8) -> i64 { 87 let spec: *u8=sys_mmap(PP_MAGIC_1048592) 88 let sn: i64=pp_readfile(specpath, spec, PP_MAGIC_1048576) 89 if sn<=0 { return 0-1 } 90 // collect per-slide spans 91 let so: *i64=sys_mmap(8*PP_MAXSLIDES*33) as *i64 // per slide: [0]=title, [1..32]=bullets 92 let sl: *i64=sys_mmap(8*PP_MAXSLIDES*33) as *i64 93 let nbul: *i64=sys_mmap(8*PP_MAXSLIDES) as *i64 94 var ns: i64=0 95 var i: i64=0 96 while i<sn { 97 var e: i64=i 98 while e<sn { if spec[e]==(10 as u8) { break } e=e+1 } 99 var end: i64=e 100 if end>i { if spec[end-1]==(13 as u8) { end=end-1 } } 101 if end>=i+2 { if spec[i+1]==(32 as u8) { 102 let c0: i64=spec[i] as i64 103 if c0==83 { if ns<PP_MAXSLIDES { so[ns*33]=i+2; sl[ns*33]=end-(i+2); nbul[ns]=0; ns=ns+1 } } 104 if c0==66 { if ns>0 { let b: i64=nbul[ns-1]; if b<32 { so[(ns-1)*33+1+b]=i+2; sl[(ns-1)*33+1+b]=end-(i+2); nbul[ns-1]=b+1 } } } 105 } } 106 i=e+1 107 } 108 if ns==0 { return 0-2 } 109 // OPC parts: content types + root rels + presentation + its rels + N slides 110 let np: i64=4+ns 111 let names: *i64=sys_mmap(8*(np+2)) as *i64 112 let datap: *i64=sys_mmap(8*(np+2)) as *i64 113 let lens: *i64=sys_mmap(8*(np+2)) as *i64 114 // [Content_Types].xml (Override per slide) 115 let ct: *u8=sys_mmap(PP_MAGIC_16384) 116 var cto: i64=0 117 cto=pp_cat(ct,cto,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\"><Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/><Default Extension=\"xml\" ContentType=\"application/xml\"/><Override PartName=\"/ppt/presentation.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml\"/>" as *u8) 118 var si: i64=1 119 while si<=ns { 120 cto=pp_cat(ct,cto,"<Override PartName=\"/ppt/slides/slide" as *u8); cto=pp_uint(ct,cto,si) 121 cto=pp_cat(ct,cto,".xml\" ContentType=\"application/vnd.openxmlformats-officedocument.presentationml.slide+xml\"/>" as *u8) 122 si=si+1 123 } 124 cto=pp_cat(ct,cto,"</Types>" as *u8) 125 // root rels 126 let rr: *u8="<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\" Target=\"ppt/presentation.xml\"/></Relationships>" as *u8 127 // presentation.xml (sldIdLst + 16:9 size) 128 let pres: *u8=sys_mmap(PP_MAGIC_16384) 129 var po: i64=0 130 po=pp_cat(pres,po,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<p:presentation xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" xmlns:p=\"http://schemas.openxmlformats.org/presentationml/2006/main\"><p:sldIdLst>" as *u8) 131 si=1 132 while si<=ns { 133 po=pp_cat(pres,po,"<p:sldId id=\"" as *u8); po=pp_uint(pres,po,255+si) 134 po=pp_cat(pres,po,"\" r:id=\"rId" as *u8); po=pp_uint(pres,po,si); po=pp_cat(pres,po,"\"/>" as *u8) 135 si=si+1 136 } 137 po=pp_cat(pres,po,"</p:sldIdLst><p:sldSz cx=\"12192000\" cy=\"6858000\"/></p:presentation>" as *u8) 138 // presentation rels 139 let prels: *u8=sys_mmap(PP_MAGIC_16384) 140 var pro: i64=0 141 pro=pp_cat(prels,pro,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">" as *u8) 142 si=1 143 while si<=ns { 144 pro=pp_cat(prels,pro,"<Relationship Id=\"rId" as *u8); pro=pp_uint(prels,pro,si) 145 pro=pp_cat(prels,pro,"\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide\" Target=\"slides/slide" as *u8) 146 pro=pp_uint(prels,pro,si); pro=pp_cat(prels,pro,".xml\"/>" as *u8) 147 si=si+1 148 } 149 pro=pp_cat(prels,pro,"</Relationships>" as *u8) 150 names[0]="[Content_Types].xml" as *u8 as i64; datap[0]=ct as i64; lens[0]=cto 151 names[1]="_rels/.rels" as *u8 as i64; datap[1]=rr as i64; lens[1]=pp_slen(rr) 152 names[2]="ppt/presentation.xml" as *u8 as i64; datap[2]=pres as i64; lens[2]=po 153 names[3]="ppt/_rels/presentation.xml.rels" as *u8 as i64; datap[3]=prels as i64; lens[3]=pro 154 // slides (part NAMES need distinct buffers) 155 let nmbuf: *u8=sys_mmap(PP_MAXSLIDES*40) 156 var nmo: i64=0 157 si=0 158 while si<ns { 159 let sxml: *u8=sys_mmap(PP_MAGIC_262144) 160 let tso: *i64=((so as i64)+si*33*8) as *i64 161 let tsl: *i64=((sl as i64)+si*33*8) as *i64 162 let slen2: i64=pp_build_slide(sxml, spec, tso, tsl, nbul[si]) 163 let nm: *u8=((nmbuf as i64)+nmo) as *u8 164 var q: i64=pp_cat(nm,0,"ppt/slides/slide" as *u8) 165 q=pp_uint(nm,q,si+1) 166 q=pp_cat(nm,q,".xml" as *u8) 167 nm[q]=0 as u8 168 nmo=nmo+q+1 169 names[4+si]=nm as i64 170 datap[4+si]=sxml as i64 171 lens[4+si]=slen2 172 si=si+1 173 } 174 return opc_write(outpath, names, datap, lens, np) 175} 176 177// extract slide si's <a:t> runs: first run -> title (tb), rest -> bullets appended to out with "- " prefix. 178// Returns 1 if the slide part exists, else 0. 179func pp_read_slide(path: *u8, si: i64, fd: i64) -> i64 { 180 let nm: *u8=sys_mmap(64) 181 var q: i64=pp_cat(nm,0,"ppt/slides/slide" as *u8) 182 q=pp_uint(nm,q,si) 183 q=pp_cat(nm,q,".xml" as *u8) 184 nm[q]=0 as u8 185 let sx: *u8=sys_mmap(PP_MAGIC_1048592) 186 let n: i64=opc_read_part(path, nm, sx, PP_MAGIC_1048576) 187 if n<0 { return 0 } 188 let line: *u8=sys_mmap(PP_MAGIC_65536) 189 var o: i64=0 190 o=pp_cat(line,o,"SLIDE " as *u8); o=pp_uint(line,o,si); o=pp_cat(line,o,": " as *u8) 191 var first: i64=1 192 var i: i64=0 193 while i<n { 194 if pp_match(sx,i,n,"<a:t>" as *u8)==1 { 195 var k: i64=i+5 196 if first==0 { o=pp_cat(line,o,"\n- " as *u8) } 197 while k<n { 198 if pp_match(sx,k,n,"</a:t>" as *u8)==1 { break } 199 if sx[k]==(38 as u8) { 200 if pp_match(sx,k,n,"&amp;" as *u8)==1 { line[o]=38 as u8; o=o+1; k=k+5 } else { 201 if pp_match(sx,k,n,"&lt;" as *u8)==1 { line[o]=60 as u8; o=o+1; k=k+4 } else { 202 if pp_match(sx,k,n,"&gt;" as *u8)==1 { line[o]=62 as u8; o=o+1; k=k+4 } else { 203 line[o]=sx[k]; o=o+1; k=k+1 } } } 204 } else { line[o]=sx[k]; o=o+1; k=k+1 } 205 } 206 first=0 207 i=k+6 208 } else { i=i+1 } 209 } 210 line[o]=10 as u8 211 o=o+1 212 sys_write(fd, line, o) 213 return 1 214} 215 216// deck -> 0-JS slide-card preview (16:9), one card per slide 217func pptx_to_html(path: *u8, out: *u8, dlurl: *u8) -> i64 { 218 var o: i64=0 219 o=pp_cat(out,o,"<!DOCTYPE html>\n<html lang=en><head><meta charset=utf-8><meta name=viewport content=\"width=device-width,initial-scale=1\"><title>Slide Deck Preview - Nishi Office</title><style>\nbody{font:16px/1.6 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;margin:0;background:#eef1f5;color:#1a1d21}\nheader{background:#0f172a;color:#fff;padding:16px 24px}header h1{margin:0;font-size:17px;font-weight:600}\n.deck{max-width:900px;margin:22px auto;padding:0 12px}\n.slide{background:#fff;border:1px solid #e2e8f0;border-radius:12px;box-shadow:0 1px 4px rgba(15,23,42,.08);margin:0 0 22px;padding:34px 44px;aspect-ratio:16/9;overflow:hidden;position:relative}\n.slide h2{font-size:28px;margin:0 0 18px;color:#0f172a;border-bottom:2px solid #2563eb;padding-bottom:10px}\n.slide ul{margin:0;padding-left:26px}.slide li{margin:9px 0;font-size:19px}\n.sn{position:absolute;right:14px;bottom:8px;color:#94a3b8;font-size:12px}\n.bar{max-width:900px;margin:0 auto 6px;text-align:right;padding:0 12px}.dl{display:inline-block;padding:9px 18px;background:#2563eb;color:#fff;border-radius:8px;text-decoration:none;font-size:14px;font-weight:500}\nfooter{max-width:900px;margin:14px auto 46px;color:#64748b;font-size:13px;text-align:center}\n</style></head>\n<body>\n<header><h1>&#127916; Slide Deck Preview &mdash; Nishi Office</h1></header>\n<div class=deck>\n" as *u8) 220 let sx: *u8=sys_mmap(PP_MAGIC_1048592) 221 var si: i64=1 222 var shown: i64=0 223 while si<=PP_MAXSLIDES { 224 let nm: *u8=sys_mmap(64) 225 var q: i64=pp_cat(nm,0,"ppt/slides/slide" as *u8) 226 q=pp_uint(nm,q,si) 227 q=pp_cat(nm,q,".xml" as *u8) 228 nm[q]=0 as u8 229 let n: i64=opc_read_part(path, nm, sx, PP_MAGIC_1048576) 230 if n<0 { si=PP_MAXSLIDES+1 } else { 231 o=pp_cat(out,o,"<div class=slide>" as *u8) 232 var first: i64=1 233 var inlist: i64=0 234 var i: i64=0 235 while i<n { 236 if pp_match(sx,i,n,"<a:t>" as *u8)==1 { 237 var k: i64=i+5 238 if first==1 { o=pp_cat(out,o,"<h2>" as *u8) } else { 239 if inlist==0 { o=pp_cat(out,o,"<ul>" as *u8); inlist=1 } 240 o=pp_cat(out,o,"<li>" as *u8) 241 } 242 while k<n { 243 if pp_match(sx,k,n,"</a:t>" as *u8)==1 { break } 244 out[o]=sx[k]; o=o+1; k=k+1 245 } 246 if first==1 { o=pp_cat(out,o,"</h2>" as *u8); first=0 } else { o=pp_cat(out,o,"</li>" as *u8) } 247 i=k+6 248 } else { i=i+1 } 249 } 250 if inlist==1 { o=pp_cat(out,o,"</ul>" as *u8) } 251 o=pp_cat(out,o,"<div class=sn>" as *u8); o=pp_uint(out,o,si); o=pp_cat(out,o,"</div></div>\n" as *u8) 252 shown=shown+1 253 si=si+1 254 } 255 } 256 if shown==0 { return 0-1 } 257 o=pp_cat(out,o,"</div>\n" as *u8) 258 if (dlurl as i64)!=0 { o=pp_cat(out,o,"<div class=bar><a class=dl href=\"" as *u8); o=pp_cat(out,o,dlurl); o=pp_cat(out,o,"\">&#11015; Download .pptx</a></div>\n" as *u8) } 259 o=pp_cat(out,o,"<footer>Rendered natively from PresentationML by <b>nx_pptx</b> &middot; " as *u8) 260 o=pp_uint(out,o,shown) 261 o=pp_cat(out,o," slides &middot; viewable inline in the Nishi browser</footer>\n</body></html>\n" as *u8) 262 return o 263} 264 265func main(argc: i64, argv: *i64) -> i64 { 266 if argc<3 { pp_puts("usage: nx_pptx fromspec <spec.txt> <out.pptx> | read <in.pptx> | html <in.pptx> <out.html> [download_url]\n" as *u8); sys_exit(2); return 2 } 267 let cmd: *u8=argv[1] as *u8 268 if pp_streq(cmd,"fromspec" as *u8)==1 { 269 if argc<4 { pp_puts("usage: nx_pptx fromspec <spec.txt> <out.pptx>\n" as *u8); sys_exit(2); return 2 } 270 let sz: i64=pptx_write_spec(argv[2] as *u8, argv[3] as *u8) 271 if sz<0 { pp_puts("PPTX-WRITE-FAIL code=" as *u8); pp_num(sz); pp_puts("\n" as *u8); sys_exit(1); return 1 } 272 pp_puts("PPTX-WRITE-OK path=" as *u8); pp_puts(argv[3] as *u8); pp_puts(" bytes=" as *u8); pp_num(sz); pp_puts("\n" as *u8) 273 sys_exit(0); return 0 274 } 275 if pp_streq(cmd,"read" as *u8)==1 { 276 pp_puts("PPTX-DECK-BEGIN\n" as *u8) 277 var si: i64=1 278 var n: i64=0 279 while si<=PP_MAXSLIDES { if pp_read_slide(argv[2] as *u8, si, 1)==1 { n=n+1; si=si+1 } else { si=PP_MAXSLIDES+1 } } 280 pp_puts("PPTX-DECK-END slides=" as *u8); pp_num(n); pp_puts("\n" as *u8) 281 if n==0 { sys_exit(1); return 1 } 282 sys_exit(0); return 0 283 } 284 if pp_streq(cmd,"html" as *u8)==1 { 285 if argc<4 { pp_puts("usage: nx_pptx html <in.pptx> <out.html> [download_url]\n" as *u8); sys_exit(2); return 2 } 286 var dlu: *u8=0 as *u8 287 if argc>4 { dlu=argv[4] as *u8 } 288 let out: *u8=sys_mmap(PP_MAGIC_8388608) 289 let hn: i64=pptx_to_html(argv[2] as *u8, out, dlu) 290 if hn<0 { pp_puts("PPTX-HTML-FAIL cannot-read-deck\n" as *u8); sys_exit(1); return 1 } 291 let fd: i64=sys_openat_wr(argv[3] as *u8, 0x1a4) 292 if fd<0 { pp_puts("PPTX-HTML-FAIL cannot-write\n" as *u8); sys_exit(1); return 1 } 293 sys_write(fd, out, hn); sys_close(fd) 294 pp_puts("PPTX-HTML-OK path=" as *u8); pp_puts(argv[3] as *u8); pp_puts(" bytes=" as *u8); pp_num(hn); pp_puts("\n" as *u8) 295 sys_exit(0); return 0 296 } 297 pp_puts("nx_pptx: unknown command (fromspec|read|html)\n" as *u8) 298 sys_exit(2); return 2 299}