UNPKG

tty-typesetter

Version:

terminal typesetting with real world measurements of character advance widths

3 lines (2 loc) 7.48 kB
#!/usr/bin/env -S node const r=new Map([["+",[8205]],["!",[65039]],["%",[9792,9794]],["~",[128102,128103]],["&",[128104,128105]],["*",s(127995,127999)],["$",s(127462,127487)]]),q=/([0-9a-zA-Z]{4}(?:\/[0-9a-zA-Z]{4})*|[^0-9a-zA-Z?])(\??)/g;function s(r,q){const s=[];for(let o=r;o<=q;++o)s.push(o);return s}const o=new Map;for(const[q,s]of r)for(const r of s)o.set(r,q);function t(s){const o=[];return n(function(s){return[...s.matchAll(q)].map((([,q,s])=>({codepoints:r.get(q)??q.split("/").map((r=>Number.parseInt(r,36))),optional:Boolean(s)})))}(s),0,o,[]),o}function n(r,q,s,o){if(q===r.length)return void s.push([...o]);const t=o.length,{codepoints:w,optional:e}=r[q];e&&n(r,q+1,s,o);for(const e of w)o[t]=e,n(r,q+1,s,o);o.length=t}const w=process.argv.slice(2);if(!process.stderr.isTTY||!process.stdin.isTTY)throw new Error("Must be connected to TTY to measure");const e=/\x1B\[(\d+);(\d+)R/g,c=function(){const r=new TextDecoder,q=[];let s="",o=!1;return process.stdin.on("data",(t=>{for(s+=r.decode(t),s.includes("\x03")&&(s="",o||(o=!0,process.stderr.write("\n\nKilled\n\n"),setTimeout((()=>process.exit(1)),50)));;){const r=q[0];if(!r)return;r.pattern.lastIndex=0;const o=r.pattern.exec(s);if(!o)return;q.shift();const t=r.pattern.lastIndex;r.resolve(o),s=t===s.length?"":s.substring(t)}})),process.stdin.setRawMode(!0),(r,s)=>new Promise((t=>{q.push({pattern:s,resolve:t}),r&&!o&&process.stderr.write(r)}))}(),i=w.indexOf("--batch"),p=-1===i?256:Math.max(Number.parseInt(w[i+1]),1);let u;process.stderr.write("\n");const a="\ud83d\udc68",l="\ud83c\udffd",x=await y(a),m=await y(l);if(await y(a+l)===x)u="full";else{process.stderr.write("\x1b[?2027h");if(await y(a+l)===x)u="mode-2027";else{process.stderr.write("\r\x1b[2K"),process.stderr.write(" ".repeat(5)+"V\n"),process.stderr.write(a+l+" ".repeat(5-x-m)+"A"+" ".repeat(m-1)+"B\n"),process.stderr.write(" ".repeat(5)+"^\n\n");const r=await c("Font calibration: which letter (A or B) is being pointed at above? ",/[abq]/i);process.stderr.write("\n\n"),"q"===r[0].toLowerCase()&&process.exit(1),u="b"===r[0].toLowerCase()?"font":"none"}}function f(){process.stderr.write("\r\x1b[2K")}process.stderr.write("Measuring...\n");const j=r=>q=>process.stderr.write(`\n${r}: ${(100*q).toFixed(1)}%\x1b[A`),v=performance.now(),h=[];for await(const r of b(0,1114112,p,(function(r){if(r<32||r>=127&&r<160)return 0===r?0:-1;return y(String.fromCodePoint(r))}),f,j("codepoints (1/2)")))h.push(r);const d=performance.now();if("full"===u){const r="$$ %! &* &*+07s4!+&* &*+07s4!+2qvf+&* &*+2qsw/2rrx/2s4v+&* &*?+07md/07me/07pk! &*?+2qm6/2qnn/2qnw/2qoj/2qp0/2qp4/2qqz/2qr1/2qwr/2qws/2qzr/2qzw/2r9c/2r9u/2rvz/2rw0/2rw1/2rw2/2rw3/2rwc/2rwd &*?+2rvz/2rwc/2rwd+07tt! &+&+~ &+&+~+~ &+07s4!+& &+07s4!+2qvf+& &+~ &+~+~ 000z/0016/001c/001d/001e/001f/001g/001h/001i/001j/001k/001l!06hv 004p/004u/06d8/06dl/06jm/06k9/06ms/06mt/06mu/06mv/06mw/06mx/06nd/06ne/06y0/072n/073h/073i/073j/073l/073m/073s/073t/073u/079e/07fu/07fv/07g6/07gg/07i3/07i4/07i8/07i9/07ia/07ib/07ic/07im/07ip/07iw/07j1/07j4/07j6/07j7/07ja/07je/07ji/07jj/07js/07jt/07ju/07kv/07kw/07kz/07l1/07l2/07l4/07ln/07lq/07ma/07mc/07md/07me/07mf/07mh/07mj/07mk/07mo/07mv/07n4/07n5/07ns/07nz/07o1/07o3/07op/07ow/07ox/07p0/07p3/07p4/07p5/07pe/07pk/07pl/07po/07pp/07pr/07pu/07pw/07py/07q5/07q9/07qr/07qs/07r8/07rb/07s3/07s4/07tt/0850/0851/08hx/08hy/08hz/09io/09j1/09zr/09zt/2q9c/2q9d/2q9q/2q9r/2qde/2qev/2qld/2qlg/2qlh/2qli/2qlj/2qlk/2qll/2qlm/2qln/2qlo/2qly/2qnx/2qom/2qon/2qop/2qoq/2qor/2qou/2qov/2qq3/2qq4/2qq5/2qq6/2qqc/2qqd/2qqe/2qqf/2qqg/2qqh/2qqi/2qqj/2qqk/2qql/2qqm/2qqn/2qr7/2qr9/2qrb/2qtb/2qtd/2qyl/2r0p/2r0q/2r1r/2r1s/2r1v/2r1w/2r1x/2r1y/2r1z/2r20/2r21/2r2f/2r2i/2r2j/2r2k/2r2l/2r2o/2r39/2r3c/2r3l/2r3m/2r3w/2r42/2r43/2r44/2r4h/2r4i/2r4j/2r4s/2r4t/2r4u/2r4x/2r4z/2r54/2r5b/2r5f/2r5m/2rbf/2rbh/2rbi/2rbj/2rc0/2rc1/2rc2/2rc3/2rc4/2rc5/2rc9/2rcg/2rcj! 07j1/07p5/07pm/07pn/07po/07pp/2qo5/2qpu/2qpv/2qpw/2qpz/2qq2/2qq3/2qq4/2qte/2qtf/2qti/2qtj/2qtk/2qtl/2qtm/2qtn/2qto/2qtp/2qtq/2qtr/2qts/2quj/2quk/2qul/2qum/2qun/2quo/2qup/2quq/2qur/2qus/2qut/2quu/2quv/2quw/2qv0/2qv5/2qv6/2qv7/2qv9/2qva/2qvb/2qvj/2qvl/2qwa/2r1w/2r1x/2r22/2r2o/2r2t/2r2u/2r7p/2r7q/2r7r/2r7v/2r7w/2r7x/2r7y/2r7z/2rab/2ras/2rat/2rau/2rb4/2rbg/2rrg/2rrj/2rrs/2rrt/2rru/2rrv/2rrw/2rrx/2rry/2rrz/2rs6/2rsg/2rsh/2rsi/2rsj/2rsk/2rsl/2rsm/2rsn/2rso/2rsp/2rss/2rst/2rsu/2ruf/2rw5/2rw6/2rw8/2rw9/2rwb/2rwt/2rwu/2rwv/2rwx/2rwy/2rwz/2rx0/2rx1/2rx2/2rx3/2rx4/2rx5/2rx6/2rx7/2rx8/2rx9/2s3n/2s3o/2s3p/2s4w/2s4x/2s4y/2s4z/2s50/2s51/2s52/2s53/2s54* 07o3!+2qw5 07p5/2qpv/2qpw/2qq2/2qq3/2qq4/2qum/2qun/2quo/2qup/2qur/2quv/2qv5/2qv6/2qva/2qvb/2r1x/2r7p/2r7q/2r7r/2r7v/2r7x/2r7y/2rab/2ras/2rat/2rau/2rs6/2rsl/2rsn/2rso/2rsp/2rss/2rst/2rsu/2rw8/2rw9/2rwt/2rwu/2rwv/2rx0/2rx2/2rx3/2rx4/2rx5/2rx6/2rx7/2rx8/2rx9*+%! 07p5/2qq3/2qq4/2r1x!+%! 07s4!+2qzp/2s1l 2qmc+2rjf 2qmj+2rjd 2qpv/2qpw/2qq2/2qum/2qun/2quo/2qup/2qur/2quv/2qv5/2qv6/2qva/2qvb/2r7p/2r7q/2r7r/2r7v/2r7x/2r7y/2rab/2ras/2rat/2rau/2rs6/2rsl/2rsn/2rso/2rsp/2rss/2rst/2rsu/2rw8/2rw9/2rwt/2rwu/2rwv/2rx0/2rx2/2rx3/2rx4/2rx5/2rx6/2rx7/2rx8/2rx9/2rxa/2rxb+%! 2qpv/2rau/2rwu*?+%!+07tt! 2qpv/2rau/2rwu*?+07tt! 2qr7!+07mv! 2qr7!+2qko 2qr8+07j4! 2qr8jo13jo0yjo11jo1ajo13jo1r 2qr8jo13jo0yjo1fjo0zjo1gjo1r 2qr8jo13jo0yjo1jjo18jo1fjo1r 2qrs/2qsm+08ij 2qs5+2rwa 2qsm+2qzp 2qt7+07r8! 2qtd!+2r54! 2r72+2qw8 2r79+2qwb 2r7a+2qln! 2r7m+06ms/06mt! 2rwx*+07s4!+2qvf+2rwx* 2rwx*+07s4!+2rwx* 2rwx*+2qm6/2qnn/2qnw/2qo4/2qoj/2qp0/2qp4/2qqz/2qr1/2qwr/2qws/2qzr/2qzw/2r9c/2r9u/2rvz/2rw0/2rw1/2rw2/2rw3/2rwc/2rwd/2s1c 2rwx*+2qsw/2rrx/2s4v+2rwx* 2rwx*?+07md/07me/07pk! 2rwx*?+2rvz/2rwc/2rwd+07tt! 2rwx+2qm6/2qnn/2qnw/2qo4/2qoj/2qp0/2qp4/2qqz/2qr1/2qwr/2qws/2qzr/2qzw/2r9c/2r9u/2rvz/2rw0/2rw1/2rw2/2rw3/2rwc/2rwd/2rwy/2s1c 2rwx+2rrx+2rwx 2rwx+2rwx+2rwy+2rwy 2rwx+2rwx/2rwy+2rwy 2s4x*+2s4y* ~*".split(" ").flatMap(t);for await(const{w:q,i:s}of b(0,r.length,p,(q=>{return y((s=r[q],s.map((r=>String.fromCodePoint(r))).join(""))).then((r=>({w:r,i:q})));var s}),f,j("sequences (2/2)"))){let o=0;for(const q of r[s])o+=h[q];h.push(q===o?-1:q)}}else h.push(-1);process.stderr.write("\n\x1b[2KDone.\n\n"),process.stdin.setRawMode(!1),"mode-2027"===u&&process.stderr.write("\x1b[?2027l");for(const r of["TERM","TERM_PROGRAM","TERM_PROGRAM_VERSION","TERMINAL_EMULATOR","PTYXIS_VERSION","VTE_VERSION","LANG"]){const q=process.env[r];void 0!==q&&process.stdout.write(`${r}=${q}\n`)}process.stdout.write(`platform=${process.platform}\n`),process.stdout.write(`sequences=${u}\n`),process.stdout.write(`time=${(.001*(d-v)).toFixed(3)}s (batch=${p})\n`);const z=new class{constructor(r){this.curW=null,this.pending=null,this.onEntry=r}add(r,q,s=null){this.pending?s!==q?(this.onEntry(this.pending.char,this.curW=this.pending.w),this.pending=null,q!==this.curW&&this.onEntry(r,this.curW=q)):q!==this.pending.w&&(this.onEntry(this.pending.char,this.curW=-2),this.pending=null):q!==this.curW&&(s!==q?this.onEntry(r,this.curW=q):-2!==this.curW&&(this.pending={w:q,char:r}))}close(){this.pending&&this.onEntry(this.pending.char,this.pending.w)}}((function(r,q){process.stdout.write(`${q+2}${r.toString(36).padStart(4,"0")}`)}));async function*b(r,q,s,o,t,n){const w=[];n(0);for(let e=r;e<q;++e)w.push(o(e)),w.length>=s&&(t(),yield*await Promise.all(w),n((e-r)/(q-1-r)),w.length=0);w.length>0&&(t(),yield*await Promise.all(w),n(1))}async function y(r){process.stderr.write("\ra"+r+"b");const q=await c("\x1b[6n",e);return Number.parseInt(q[2],10)-3}h.forEach(((r,q)=>z.add(q,r))),z.close(),process.stdout.write("\n"),process.exit(0);