code wiki / _hdl_build / nx_pdf.nx

nx_pdf.nx source

↩ module page · 137 lines · 8001 B

1// nx_pdf.nx -- sovereign PDF -> HTML preview (v1 = TEXT-LAYER preview). Composes the proven extractor 2// nx_pdf_text (objects -> FlateDecode -> per-font /ToUnicode CMap -> UTF-8; rule 15 DRY, fork-exec'd) and 3// adds the viewer half: a TEXT-QUALITY LINE FILTER (real PDFs leak non-text streams + glyph soup from 4// subset fonts without usable ToUnicode -- a line is kept only if it looks like language: a >=3-letter word, 5// >=40% ASCII-alpha of non-space bytes, no control bytes) then wraps the survivors as the standard 0-JS 6// preview page. HONEST v1: text layer only -- figures/graphics/exact typography are NOT painted, and the 7// page footer says so. Same `html <in> <out.html> [dlurl]` iface as nx_docx/nx_md/nx_csv, so the 8// nx_doc_view registry routes .pdf with zero special-casing. license_tier: ORIGINAL 9import "nx_syscalls.nx" 10import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 11const K_MAGIC_6000: i64 = 6000 12const K_MAGIC_33554432: i64 = 33554432 13 14func pf_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8) { n=n+1 } return n } 15func pf_puts(s: *u8) -> i64 { sys_write(1, s, pf_slen(s)); return 0 } 16// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 17// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 18// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 19// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 20func pf_num(v: i64) -> i64 { nxi_out(v); return 0 } 21func pf_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 } 22 23func pf_exists(path: *u8) -> i64 { let fd: i64=sys_openat_rd(path); if fd<0 { return 0 } sys_close(fd); return 1 } 24 25// fork the blessed extractor: _offc/nx_pdf_text.elf <in.pdf> <out.txt>; return child exit code. 26func pf_extract(inp: *u8, txtout: *u8) -> i64 { 27 let pid: i64=sys_fork() 28 if pid==0 { 29 let lfd: i64=sys_openat_wr("/tmp/nx_pdf_extract.log" as *u8, 0x1a4) 30 if lfd>=0 { sys_dup3(lfd,1,0); sys_dup3(lfd,2,0) } 31 let argv: *i64=sys_mmap(48) as *i64 32 argv[0]="_offc/nx_pdf_text.elf" as *u8 as i64 33 argv[1]=inp as i64 34 argv[2]=txtout as i64 35 argv[3]=0 36 let envp: *i64=sys_mmap(16) as *i64; envp[0]=0 37 sys_execve("_offc/nx_pdf_text.elf" as *u8, argv, envp) 38 sys_exit(127) 39 } 40 let st: *i64=sys_mmap(16) as *i64; sys_wait4(pid, st, 0) 41 let sig: i64=st[0]&0x7f; if sig!=0 { return 128+sig } 42 return (st[0]>>8)&0xff 43} 44 45// does src[s,e) look like LANGUAGE? (>=3-letter run, alpha>=40% of non-space, no control bytes) 46func pf_is_text(src: *u8, s: i64, e: i64) -> i64 { 47 var alpha: i64=0 48 var nonspace: i64=0 49 var run: i64=0 50 var bestrun: i64=0 51 var i: i64=s 52 while i<e { 53 let c: i64=src[i]&0xff 54 if c<32 { if c!=9 { return 0 } } // control byte -> not text 55 if c==127 { return 0 } 56 var isal: i64=0 57 if c>=65 { if c<=90 { isal=1 } } 58 if c>=97 { if c<=122 { isal=1 } } 59 if isal==1 { alpha=alpha+1; run=run+1; if run>bestrun { bestrun=run } } else { run=0 } 60 if c!=32 { if c!=9 { nonspace=nonspace+1 } } 61 i=i+1 62 } 63 if bestrun<3 { return 0 } 64 if nonspace<4 { return 0 } 65 if alpha*100 < nonspace*40 { return 0 } 66 return 1 67} 68 69// escape src[s,e) into dst as HTML text (& < >); 0xC2 0xA0 (nbsp) -> plain space; return new offset 70func pf_esc(dst: *u8, off: i64, src: *u8, s: i64, e: i64) -> i64 { 71 var o: i64=off; var i: i64=s 72 while i<e { 73 let c: i64=src[i]&0xff 74 if c==38 { o=pf_cat(dst,o,"&amp;" as *u8); i=i+1 } else { 75 if c==60 { o=pf_cat(dst,o,"&lt;" as *u8); i=i+1 } else { 76 if c==62 { o=pf_cat(dst,o,"&gt;" as *u8); i=i+1 } else { 77 if c==194 { if i+1<e { if (src[i+1]&0xff)==160 { dst[o]=32 as u8; o=o+1; i=i+2 } else { dst[o]=src[i]; o=o+1; i=i+1 } } else { i=i+1 } } else { 78 dst[o]=src[i]; o=o+1; i=i+1 79 } } } } 80 } 81 return o 82} 83 84func pf_to_html(txt: *u8, tlen: i64, out: *u8, dlurl: *u8) -> i64 { 85 var o: i64=0 86 o=pf_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>Document Preview - Nishi Office</title><style>\nbody{font:16px/1.65 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.doc{max-width:840px;margin:22px auto;background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:34px 46px;box-shadow:0 1px 4px rgba(15,23,42,.08)}\n.doc p{margin:9px 0}\n.note{max-width:840px;margin:0 auto 8px;background:#fef9c3;border:1px solid #fde047;border-radius:8px;padding:8px 14px;font-size:13px;color:#713f12}\n.bar{max-width:840px;margin:0 auto 6px;text-align:right}.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:840px;margin:14px auto 46px;color:#64748b;font-size:13px;text-align:center}\n</style></head>\n<body>\n<header><h1>&#128196; Document Preview &mdash; Nishi Office</h1></header>\n<div class=note>Text-layer preview (v1): the document&#39;s TEXT, filtered and readable. Figures, graphics and exact typography are not painted yet.</div>\n<div class=doc>\n" as *u8) 87 var kept: i64=0 88 var i: i64=0 89 while i<tlen { 90 var e: i64=i 91 while e<tlen { if txt[e]==(10 as u8) { break } e=e+1 } 92 var end: i64=e 93 if end>i { if txt[end-1]==(13 as u8) { end=end-1 } } 94 if end>i { 95 if kept<K_MAGIC_6000 { 96 let ok: i64=pf_is_text(txt, i, end) 97 if ok==1 { 98 o=pf_cat(out,o,"<p>" as *u8) 99 o=pf_esc(out,o,txt,i,end) 100 o=pf_cat(out,o,"</p>\n" as *u8) 101 kept=kept+1 102 } 103 } 104 } 105 i=e+1 106 } 107 if kept==0 { o=pf_cat(out,o,"<p><i>No readable text layer found in this PDF (it may be scanned images or use fonts without a text map).</i></p>\n" as *u8) } 108 o=pf_cat(out,o,"</div>\n" as *u8) 109 if (dlurl as i64)!=0 { o=pf_cat(out,o,"<div class=bar><a class=dl href=\"" as *u8); o=pf_cat(out,o,dlurl); o=pf_cat(out,o,"\">&#11015; Download .pdf</a></div>\n" as *u8) } 110 o=pf_cat(out,o,"<footer>Rendered natively by <b>nx_pdf</b> (sovereign extractor nx_pdf_text: FlateDecode + per-font ToUnicode) &middot; viewable inline in the Nishi browser</footer>\n</body></html>\n" as *u8) 111 return o 112} 113 114func main(argc: i64, argv: *i64) -> i64 { 115 if argc<4 { pf_puts("usage: nx_pdf html <in.pdf> <out.html> [download_url]\n" as *u8); sys_exit(2); return 2 } 116 let cmd: *u8=argv[1] as *u8 117 if cmd[0]!=(104 as u8) { pf_puts("nx_pdf: unknown command (html)\n" as *u8); sys_exit(2); return 2 } 118 let inp: *u8=argv[2] as *u8 119 let outp: *u8=argv[3] as *u8 120 var dlu: *u8=0 as *u8 121 if argc>4 { dlu=argv[4] as *u8 } 122 if pf_exists(inp)==0 { pf_puts("PDF-HTML-FAIL cannot-read\n" as *u8); sys_exit(1); return 1 } 123 let txtp: *u8="/tmp/nx_pdf_extract.txt" as *u8 124 let xrc: i64=pf_extract(inp, txtp) 125 if xrc!=0 { pf_puts("PDF-HTML-FAIL extractor rc=" as *u8); pf_num(xrc); pf_puts("\n" as *u8); sys_exit(1); return 1 } 126 let lp: *i64=sys_mmap(16) as *i64 127 let txt: *u8=sys_read_file(txtp, lp) 128 let tlen: i64=lp[0] 129 if tlen<=0 { pf_puts("PDF-HTML-FAIL empty-extract\n" as *u8); sys_exit(1); return 1 } 130 let out: *u8=sys_mmap(K_MAGIC_33554432) 131 let n: i64=pf_to_html(txt, tlen, out, dlu) 132 let fd: i64=sys_openat_wr(outp, 0x1a4) 133 if fd<0 { pf_puts("PDF-HTML-FAIL cannot-write\n" as *u8); sys_exit(1); return 1 } 134 sys_write(fd, out, n); sys_close(fd) 135 pf_puts("PDF-HTML-OK path=" as *u8); pf_puts(outp); pf_puts(" bytes=" as *u8); pf_num(n); pf_puts("\n" as *u8) 136 sys_exit(0); return 0 137}