diff options
Diffstat (limited to 'src/cmd/page/gs.c')
-rw-r--r-- | src/cmd/page/gs.c | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/src/cmd/page/gs.c b/src/cmd/page/gs.c new file mode 100644 index 00000000..524701e3 --- /dev/null +++ b/src/cmd/page/gs.c @@ -0,0 +1,342 @@ +/* + * gs interface for page. + * ps.c and pdf.c both use these routines. + * a caveat: if you run more than one gs, only the last + * one gets killed by killgs + */ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <event.h> +#include <bio.h> +#include "page.h" + +static int gspid; /* globals for atexit */ +static int gsfd; +static void killgs(void); + +static void +killgs(void) +{ + char tmpfile[100]; + + close(gsfd); + postnote(PNGROUP, getpid(), "die"); + + /* + * from ghostscript's use.txt: + * ``Ghostscript currently doesn't do a very good job of deleting temporary + * files when it exits; you may have to delete them manually from time to + * time.'' + */ + sprint(tmpfile, "/tmp/gs_%.5da", (gspid+300000)%100000); + if(chatty) fprint(2, "remove %s...\n", tmpfile); + remove(tmpfile); + sleep(100); + postnote(PNPROC, gspid, "die yankee pig dog"); +} + +int +spawnwriter(GSInfo *g, Biobuf *b) +{ + char buf[4096]; + int n; + int fd; + + switch(fork()){ + case -1: return -1; + case 0: break; + default: return 0; + } + + Bseek(b, 0, 0); + fd = g->gsfd; + while((n = Bread(b, buf, sizeof buf)) > 0) + write(fd, buf, n); + fprint(fd, "(/fd/3) (w) file dup (THIS IS NOT AN INFERNO BITMAP\\n) writestring flushfile\n"); + _exits(0); + return -1; +} + +int +spawnreader(int fd) +{ + int n, pfd[2]; + char buf[1024]; + + if(pipe(pfd)<0) + return -1; + switch(fork()){ + case -1: + return -1; + case 0: + break; + default: + close(pfd[0]); + return pfd[1]; + } + + close(pfd[1]); + switch(fork()){ + case -1: + wexits("fork failed"); + case 0: + while((n=read(fd, buf, sizeof buf)) > 0) { + write(1, buf, n); + write(pfd[0], buf, n); + } + break; + default: + while((n=read(pfd[0], buf, sizeof buf)) > 0) { + write(1, buf, n); + write(fd, buf, n); + } + break; + } + postnote(PNGROUP, getpid(), "i'm die-ing"); + _exits(0); + return -1; +} + +void +spawnmonitor(int fd) +{ + char buf[4096]; + char *xbuf; + int n; + int out; + int first; + + switch(rfork(RFFDG|RFNOTEG|RFPROC)){ + case -1: + default: + return; + + case 0: + break; + } + + out = open("/dev/cons", OWRITE); + if(out < 0) + out = 2; + + xbuf = buf; /* for ease of acid */ + first = 1; + while((n = read(fd, xbuf, sizeof buf)) > 0){ + if(first){ + first = 0; + fprint(2, "Ghostscript Error:\n"); + } + write(out, xbuf, n); + alarm(500); + } + _exits(0); +} + +int +spawngs(GSInfo *g) +{ + char *args[16]; + char tb[32], gb[32]; + int i, nargs; + int devnull; + int stdinout[2]; + int dataout[2]; + int errout[2]; + + /* + * spawn gs + * + * gs's standard input is fed from stdinout. + * gs output written to fd-2 (i.e. output we generate intentionally) is fed to stdinout. + * gs output written to fd 1 (i.e. ouptut gs generates on error) is fed to errout. + * gs data output is written to fd 3, which is dataout. + */ + if(pipe(stdinout) < 0 || pipe(dataout)<0 || pipe(errout)<0) + return -1; + + nargs = 0; + args[nargs++] = "gs"; + args[nargs++] = "-dNOPAUSE"; + args[nargs++] = "-dSAFER"; + args[nargs++] = "-sDEVICE=plan9"; + args[nargs++] = "-sOutputFile=/fd/3"; + args[nargs++] = "-dQUIET"; + args[nargs++] = "-r100"; + sprint(tb, "-dTextAlphaBits=%d", textbits); + sprint(gb, "-dGraphicsAlphaBits=%d", gfxbits); + if(textbits) + args[nargs++] = tb; + if(gfxbits) + args[nargs++] = gb; + args[nargs++] = "-"; + args[nargs] = nil; + + gspid = fork(); + if(gspid == 0) { + close(stdinout[1]); + close(dataout[1]); + close(errout[1]); + + /* + * Horrible problem: we want to dup fd's 0-4 below, + * but some of the source fd's might have those small numbers. + * So we need to reallocate those. In order to not step on + * anything else, we'll dup the fd's to higher ones using + * dup(x, -1), but we need to use up the lower ones first. + */ + while((devnull = open("/dev/null", ORDWR)) < 5) + ; + + stdinout[0] = dup(stdinout[0], -1); + errout[0] = dup(errout[0], -1); + dataout[0] = dup(dataout[0], -1); + + dup(stdinout[0], 0); + dup(errout[0], 1); + dup(devnull, 2); /* never anything useful */ + dup(dataout[0], 3); + dup(stdinout[0], 4); + for(i=5; i<20; i++) + close(i); + exec("/bin/gs", args); + wexits("exec"); + } + close(stdinout[0]); + close(errout[0]); + close(dataout[0]); + atexit(killgs); + + if(teegs) + stdinout[1] = spawnreader(stdinout[1]); + + gsfd = g->gsfd = stdinout[1]; + g->gsdfd = dataout[1]; + g->gspid = gspid; + + spawnmonitor(errout[1]); + Binit(&g->gsrd, g->gsfd, OREAD); + + gscmd(g, "/PAGEOUT (/fd/4) (w) file def\n"); + gscmd(g, "/PAGE== { PAGEOUT exch write==only PAGEOUT (\\n) writestring PAGEOUT flushfile } def\n"); + waitgs(g); + + return 0; +} + +int +gscmd(GSInfo *gs, char *fmt, ...) +{ + char buf[1024]; + int n; + + va_list v; + va_start(v, fmt); + n = vseprint(buf, buf+sizeof buf, fmt, v) - buf; + if(n <= 0) + return n; + + if(chatty) { + fprint(2, "cmd: "); + write(2, buf, n); + } + + if(write(gs->gsfd, buf, n) != 0) + return -1; + + return n; +} + +/* + * set the dimensions of the bitmap we expect to get back from GS. + */ +void +setdim(GSInfo *gs, Rectangle bbox, int ppi, int landscape) +{ + Rectangle pbox; + + if(chatty) + fprint(2, "setdim: bbox=%R\n", bbox); + + if(ppi) + gs->ppi = ppi; + + gscmd(gs, "mark\n"); + if(ppi) + gscmd(gs, "/HWResolution [%d %d]\n", ppi, ppi); + + if(!Dx(bbox)) + bbox = Rect(0, 0, 612, 792); /* 8½×11 */ + + switch(landscape){ + case 0: + pbox = bbox; + break; + case 1: + pbox = Rect(bbox.min.y, bbox.min.x, bbox.max.y, bbox.max.x); + break; + } + gscmd(gs, "/PageSize [%d %d]\n", Dx(pbox), Dy(pbox)); + gscmd(gs, "/Margins [%d %d]\n", -pbox.min.x, -pbox.min.y); + gscmd(gs, "currentdevice putdeviceprops pop\n"); + gscmd(gs, "/#copies 1 store\n"); + + if(!eqpt(bbox.min, ZP)) + gscmd(gs, "%d %d translate\n", -bbox.min.x, -bbox.min.y); + + switch(landscape){ + case 0: + break; + case 1: + gscmd(gs, "%d 0 translate\n", Dy(bbox)); + gscmd(gs, "90 rotate\n"); + break; + } + + waitgs(gs); +} + +void +waitgs(GSInfo *gs) +{ + /* we figure out that gs is done by telling it to + * print something and waiting until it does. + */ + char *p; + Biobuf *b = &gs->gsrd; + uchar buf[1024]; + int n; + +// gscmd(gs, "(\\n**bstack\\n) print flush\n"); +// gscmd(gs, "stack flush\n"); +// gscmd(gs, "(**estack\\n) print flush\n"); + gscmd(gs, "(\\n//GO.SYSIN DD\\n) PAGE==\n"); + + alarm(300*1000); + for(;;) { + p = Brdline(b, '\n'); + if(p == nil) { + n = Bbuffered(b); + if(n <= 0) + break; + if(n > sizeof buf) + n = sizeof buf; + Bread(b, buf, n); + continue; + } + p[Blinelen(b)-1] = 0; + if(chatty) fprint(2, "p: "); + if(chatty) write(2, p, Blinelen(b)-1); + if(chatty) fprint(2, "\n"); + if(strstr(p, "Error:")) { + alarm(0); + fprint(2, "ghostscript error: %s\n", p); + wexits("gs error"); + } + + if(strstr(p, "//GO.SYSIN DD")) { + break; + } + } + alarm(0); +} |