code wiki / _hdl_build / nx_cover_gen.nx

nx_cover_gen.nx source

↩ module page · 158 lines · 11018 B

1// nx_cover_gen.nx -- SOVEREIGN GENERATED book covers (operator: "keep going especially on the book covers"). Many 2// liberated books (MOBI/KF8) + native-authored books have NO cover, so the shelf falls back to a title-text box. 3// This GENERATES a designed SVG cover from the book's title + author: a deterministic palette (title hash -> 1 of 6 4// designed color schemes), a wrapped serif title, the author, a Nishi mark. SVG = sovereign (we emit every byte), 5// vector (crisp at any size), tiny, deterministic (same title -> same cover), and renders in <img> in the shelf + 6// reader. Writes reader/<slug>/cover.svg + sets book.json "cover":"cover.svg" IF the book is coverless (a real 7// extracted cover wins). Usage: nx_cover_gen <slug>. expect_exit: 0 license_tier: ORIGINAL 8import "nx_syscalls.nx" 9const K_MAGIC_1024: i64 = 1024 10const K_MAGIC_5381: i64 = 5381 11const K_MAGIC_16384: i64 = 16384 12 13func cg_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 14func cg_p(s: *u8) -> i64 { sys_write(1, s, cg_slen(s)); return 0 } 15func ensure_dir(path: *u8) -> i64 { __syscall(258, 0-100, path, 0x1ed, 0, 0, 0); return 0 } 16func cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){d[o+i]=s[i];i=i+1} return o+i } 17func writef(path: *u8, buf: *u8, n: i64) -> i64 { let fd: i64=sys_openat_wr(path, 0x1a4); if fd<0 {return 0-1} sys_write(fd, buf, n); sys_close(fd); return 0 } 18// extract "key":"value" from JSON into out; ret len. 19func jval(hay: *u8, hl: i64, key: *u8, out: *u8, cap: i64) -> i64 { 20 let kl: i64 = cg_slen(key); var i: i64 = 0 21 while i + kl <= hl { var k: i64=0; var hit: i64=1; while k<kl { if hay[i+k]!=key[k]{hit=0;k=kl}else{k=k+1} } if hit==1 { 22 var j: i64 = i + kl; var o: i64 = 0 23 while j < hl { let c: i64 = hay[j] as i64; if c==0x22 { j=hl } else { if c==0x5c { if j+1<hl { if o<cap-1{out[o]=hay[j+1];o=o+1} j=j+2 } else { j=j+1 } } else { if o<cap-1{out[o]=hay[j] as u8;o=o+1} j=j+1 } } } 24 out[o]=0 as u8; return o 25 } i=i+1 } 26 out[0]=0 as u8; return 0-1 27} 28// XML-escape into the buffer 29func axe(o: *u8, p: *i64, s: *u8) -> i64 { 30 var i: i64=0 31 while s[i]!=(0 as u8) { let c: i64 = s[i] as i64 32 if c==0x3c { i=i; o[p[0]]=0x26 as u8; p[0]=p[0]+1; o[p[0]]=0x6c as u8; p[0]=p[0]+1; o[p[0]]=0x74 as u8; p[0]=p[0]+1; o[p[0]]=0x3b as u8; p[0]=p[0]+1 } 33 else { if c==0x3e { o[p[0]]=0x26 as u8;p[0]=p[0]+1;o[p[0]]=0x67 as u8;p[0]=p[0]+1;o[p[0]]=0x74 as u8;p[0]=p[0]+1;o[p[0]]=0x3b as u8;p[0]=p[0]+1 } 34 else { if c==0x26 { o[p[0]]=0x26 as u8;p[0]=p[0]+1;o[p[0]]=0x61 as u8;p[0]=p[0]+1;o[p[0]]=0x6d as u8;p[0]=p[0]+1;o[p[0]]=0x70 as u8;p[0]=p[0]+1;o[p[0]]=0x3b as u8;p[0]=p[0]+1 } 35 else { o[p[0]]=s[i]; p[0]=p[0]+1 } } } 36 i=i+1 } 37 return 0 38} 39func aw(o: *u8, p: *i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){ o[p[0]]=s[i]; p[0]=p[0]+1; i=i+1 } return 0 } 40func awn(o: *u8, p: *i64, v0: i64) -> i64 { var v: i64=v0; if v<0 { o[p[0]]=45 as u8; p[0]=p[0]+1; v=0-v } let b: *u8=sys_mmap(24); var k: i64=0; if v==0{b[0]=48 as u8;k=1} while v>0 {b[k]=(48+(v%10)) as u8;v=v/10;k=k+1} var j: i64=k-1; while j>=0 { o[p[0]]=b[j]; p[0]=p[0]+1; j=j-1 } return 0 } 41 42func main(argc: i64, argv: *i64) -> i64 { 43 var slug: *u8 = "real_huffdic" as *u8 44 if argc >= 2 { slug = argv[1] as *u8 } 45 let dir: *u8 = sys_mmap(512); var dl: i64 = cat(dir, 0, "knowledge/staging/media/reader/" as *u8); dl = cat(dir, dl, slug); dir[dl]=0 as u8 46 let jp: *u8 = sys_mmap(K_MAGIC_1024); var jl: i64 = cat(jp, 0, dir as *u8); jl = cat(jp, jl, "/book.json" as *u8); jp[jl]=0 as u8 47 let lp: *i64 = sys_mmap(8) as *i64; lp[0]=0 48 let bj: *u8 = sys_read_file(jp, lp) 49 if (bj as i64)==0 { cg_p("COVER-GEN-FAIL no book.json " as *u8); cg_p(jp); cg_p("\n" as *u8); sys_exit(1); return 1 } 50 let bn: i64 = lp[0] 51 52 let title: *u8 = sys_mmap(K_MAGIC_1024); jval(bj, bn, "\"title\":\"" as *u8, title, K_MAGIC_1024) 53 let author: *u8 = sys_mmap(K_MAGIC_1024); author[0]=0 as u8; jval(bj, bn, "\"author\":\"" as *u8, author, K_MAGIC_1024) 54 let curcov: *u8 = sys_mmap(512); curcov[0]=0 as u8; jval(bj, bn, "\"cover\":\"" as *u8, curcov, 512) 55 if curcov[0] != (0 as u8) { cg_p("COVER-GEN skip (already has cover=" as *u8); cg_p(curcov); cg_p(")\n" as *u8); sys_exit(0); return 0 } 56 if title[0] == (0 as u8) { cat(title, 0, slug); } 57 58 // deterministic palette from the title hash (djb2 % 6). 6 designed schemes: bg1,bg2,accent,ink,ink2. 59 var h: i64 = K_MAGIC_5381; var ti: i64 = 0; while title[ti]!=(0 as u8){ h = ((h*33) + (title[ti] as i64)) & 0x7fffffff; ti=ti+1 } 60 let pal: i64 = h % 6 61 var bg1: *u8="#1a2238" as *u8; var bg2: *u8="#0d1220" as *u8; var ac: *u8="#c9a227" as *u8; var ink: *u8="#f4efe1" as *u8; var ink2: *u8="#bcae8a" as *u8 62 if pal==1 { bg1="#1e3a2f" as *u8; bg2="#0f1f18" as *u8; ac="#e0c068" as *u8; ink="#f1ead6" as *u8; ink2="#b8c9b0" as *u8 } 63 if pal==2 { bg1="#4a1f24" as *u8; bg2="#2a0f12" as *u8; ac="#d8a36b" as *u8; ink="#f5e9da" as *u8; ink2="#d0a59a" as *u8 } 64 if pal==3 { bg1="#22343a" as *u8; bg2="#0f1c20" as *u8; ac="#5fb3a1" as *u8; ink="#eaf2f0" as *u8; ink2="#9fc3bb" as *u8 } 65 if pal==4 { bg1="#3a2440" as *u8; bg2="#1e1224" as *u8; ac="#d98aa8" as *u8; ink="#f3e9f0" as *u8; ink2="#c3a3bd" as *u8 } 66 if pal==5 { bg1="#2b2723" as *u8; bg2="#15120f" as *u8; ac="#e08a3c" as *u8; ink="#f2ebe0" as *u8; ink2="#c2b09a" as *u8 } 67 68 // word-wrap the title into lines of <= 14 chars (store as (off,len) into title) 69 let loff: *i64 = sys_mmap(8*16) as *i64; let llen: *i64 = sys_mmap(8*16) as *i64; var nlines: i64 = 0 70 let tn: i64 = cg_slen(title) 71 var ls: i64 = 0; var le: i64 = 0 72 while le <= tn { 73 let atend: i64 = le; var brk: i64 = 0 74 if le==tn { brk=1 } else { if title[le]==(0x20 as u8) { brk=1 } } 75 if brk==1 { 76 if le - ls > 14 { if le > ls { } 77 // current word makes the line too long -> flush previous line if any 78 } 79 // decide: if the line [ls..le) <= 14 keep going; else flush at last space. 80 le = le 81 } 82 le = le + 1 83 } 84 // simpler greedy wrap: scan words, accumulate 85 nlines = 0; var cs: i64 = 0; var ce: i64 = 0; var i2: i64 = 0 86 while i2 <= tn { 87 let isspace: i64 = (i2==tn) | 0 88 var sp: i64 = 0; if i2==tn { sp=1 } else { if title[i2]==(0x20 as u8) { sp=1 } } 89 if sp==1 { 90 // word is [ce.. i2) following the current line start cs (ce marks last word-start within line) 91 if (i2 - cs) > 14 { if ce > cs { 92 if nlines < 15 { loff[nlines]=cs; llen[nlines]=ce-1-cs; nlines=nlines+1 } 93 cs = ce 94 } } 95 ce = i2 + 1 96 } 97 i2 = i2 + 1 98 } 99 if cs < tn { if nlines < 15 { loff[nlines]=cs; llen[nlines]=tn-cs; nlines=nlines+1 } } 100 if nlines == 0 { loff[0]=0; llen[0]=tn; nlines=1 } 101 102 // ----- emit the SVG ----- 103 let H: *u8 = sys_mmap(K_MAGIC_16384); let hp: *i64 = sys_mmap(8) as *i64; hp[0]=0 104 aw(H, hp, "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 400 600\" width=\"400\" height=\"600\" role=\"img\" aria-label=\"Cover: " as *u8) 105 let tmp: *u8 = sys_mmap(K_MAGIC_1024); var tc: i64=0; while title[tc]!=(0 as u8){tmp[tc]=title[tc];tc=tc+1} tmp[tc]=0 as u8 106 axe(H, hp, tmp) 107 aw(H, hp, "\"><defs><linearGradient id=\"g\" x1=\"0\" y1=\"0\" x2=\"0.4\" y2=\"1\"><stop offset=\"0\" stop-color=\"" as *u8); aw(H, hp, bg1) 108 aw(H, hp, "\"/><stop offset=\"1\" stop-color=\"" as *u8); aw(H, hp, bg2); aw(H, hp, "\"/></linearGradient></defs>" as *u8) 109 aw(H, hp, "<rect width=\"400\" height=\"600\" fill=\"url(#g)\"/>" as *u8) 110 aw(H, hp, "<rect x=\"0\" y=\"0\" width=\"400\" height=\"9\" fill=\"" as *u8); aw(H, hp, ac); aw(H, hp, "\"/>" as *u8) 111 aw(H, hp, "<rect x=\"0\" y=\"591\" width=\"400\" height=\"9\" fill=\"" as *u8); aw(H, hp, ac); aw(H, hp, "\"/>" as *u8) 112 aw(H, hp, "<rect x=\"40\" y=\"150\" width=\"54\" height=\"6\" fill=\"" as *u8); aw(H, hp, ac); aw(H, hp, "\"/>" as *u8) 113 // title block, vertically centered around y=270 114 var y0: i64 = 270 - (nlines-1) * 26 115 aw(H, hp, "<text x=\"40\" y=\"" as *u8); awn(H, hp, y0) 116 aw(H, hp, "\" font-family=\"Georgia,'Times New Roman',serif\" font-size=\"44\" font-weight=\"700\" fill=\"" as *u8); aw(H, hp, ink); aw(H, hp, "\">" as *u8) 117 var li: i64 = 0 118 while li < nlines { 119 aw(H, hp, "<tspan x=\"40\" dy=\"" as *u8); if li==0 { awn(H, hp, 0) } else { awn(H, hp, 52) }; aw(H, hp, "\">" as *u8) 120 var c2: i64=0; while c2 < llen[li] { tmp[c2]=title[loff[li]+c2]; c2=c2+1 } tmp[llen[li]]=0 as u8 121 axe(H, hp, tmp) 122 aw(H, hp, "</tspan>" as *u8) 123 li = li + 1 124 } 125 aw(H, hp, "</text>" as *u8) 126 // author 127 if author[0]!=(0 as u8) { 128 aw(H, hp, "<line x1=\"40\" x2=\"160\" y1=\"500\" y2=\"500\" stroke=\"" as *u8); aw(H, hp, ac); aw(H, hp, "\" stroke-width=\"2\"/>" as *u8) 129 aw(H, hp, "<text x=\"40\" y=\"530\" font-family=\"system-ui,-apple-system,Segoe UI,sans-serif\" font-size=\"19\" letter-spacing=\"1\" fill=\"" as *u8); aw(H, hp, ink2); aw(H, hp, "\">" as *u8) 130 var ac2: i64=0; while author[ac2]!=(0 as u8){tmp[ac2]=author[ac2];ac2=ac2+1} tmp[ac2]=0 as u8; axe(H, hp, tmp) 131 aw(H, hp, "</text>" as *u8) 132 } 133 aw(H, hp, "<text x=\"40\" y=\"572\" font-family=\"system-ui,sans-serif\" font-size=\"11\" letter-spacing=\"4\" fill=\"" as *u8); aw(H, hp, ac); aw(H, hp, "\">NISHI</text>" as *u8) 134 aw(H, hp, "</svg>" as *u8) 135 let hn: i64 = hp[0] 136 137 ensure_dir(dir) 138 let cp: *u8 = sys_mmap(K_MAGIC_1024); var cl: i64 = cat(cp, 0, dir as *u8); cl = cat(cp, cl, "/cover.svg" as *u8); cp[cl]=0 as u8 139 if writef(cp, H, hn) != 0 { cg_p("COVER-GEN-FAIL write svg\n" as *u8); sys_exit(1); return 1 } 140 141 // update book.json: "cover":"" -> "cover":"cover.svg" 142 let nb: *u8 = sys_mmap(bn + 64); let np: *i64 = sys_mmap(8) as *i64; np[0]=0 143 let needle: *u8 = "\"cover\":\"\"" as *u8; let ndl: i64 = cg_slen(needle) 144 var done: i64 = 0; var k2: i64 = 0 145 while k2 < bn { 146 var hit: i64 = 0 147 if done==0 { if k2+ndl<=bn { hit=1; var z: i64=0; while z<ndl { if bj[k2+z]!=needle[z]{hit=0;z=ndl}else{z=z+1} } } } 148 if hit==1 { aw(nb, np, "\"cover\":\"cover.svg\"" as *u8); k2=k2+ndl; done=1 } 149 else { nb[np[0]]=bj[k2]; np[0]=np[0]+1; k2=k2+1 } 150 } 151 writef(jp, nb, np[0]) 152 153 cg_p("COVER-GEN slug=" as *u8); cg_p(slug); cg_p(" pal=" as *u8); let pb: *u8=sys_mmap(4); pb[0]=(48+pal) as u8; sys_write(1,pb,1) 154 cg_p(" lines=" as *u8); let lb: *u8=sys_mmap(4); lb[0]=(48+nlines) as u8; sys_write(1,lb,1) 155 cg_p(" svg=" as *u8); let szb: *u8=sys_mmap(8); var sv: i64=hn; var sk: i64=0; if sv==0{szb[0]=48 as u8;sk=1} while sv>0{szb[sk]=(48+(sv%10)) as u8;sv=sv/10;sk=sk+1} var sj: i64=sk-1; while sj>=0{sys_write(1,(szb as i64+sj) as *u8,1);sj=sj-1} 156 cg_p("B -> " as *u8); cg_p(cp); cg_p(" (book.json cover set)\n" as *u8) 157 cg_p("COVER-GEN-OK\n" as *u8); sys_exit(0); return 0 158}