| /* |
| * 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 <thread.h> |
| #include <bio.h> |
| #include <cursor.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"); |
| } |
| |
| void |
| spawnreader(void *cp) |
| { |
| int n, fd, pfd[2]; |
| char buf[1024]; |
| |
| recv(cp, &fd); |
| |
| if(pipe(pfd)<0) |
| wexits("pipe failed"); |
| |
| send(cp, &pfd[1]); |
| |
| while((n=read(pfd[0], buf, sizeof buf)) > 0) { |
| write(1, buf, n); |
| write(fd, buf, n); |
| } |
| |
| close(pfd[0]); |
| threadexits(0); |
| } |
| |
| void |
| spawnmonitor(void *cp) |
| { |
| char buf[4096]; |
| char *xbuf; |
| int fd; |
| int n; |
| int out; |
| int first; |
| |
| recv(cp, &fd); |
| |
| out = open("/dev/tty", 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); |
| } |
| threadexits(0); |
| } |
| |
| int |
| spawngs(GSInfo *g, char *safer) |
| { |
| Channel *cp; |
| char *args[16]; |
| char tb[32], gb[32]; |
| int i, nargs; |
| int devnull; |
| int stdinp[2]; |
| int stdoutp[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(stdinp)<0 || pipe(stdoutp)<0 || pipe(dataout)<0 || pipe(errout)<0) |
| return -1; |
| |
| nargs = 0; |
| args[nargs++] = "gs"; |
| args[nargs++] = "-dNOPAUSE"; |
| args[nargs++] = "-dNOPROMPT"; |
| args[nargs++] = "-dDELAYSAFER"; |
| args[nargs++] = "-dQUIET"; |
| args[nargs++] = "-sDEVICE=bmp16m"; |
| args[nargs++] = "-sOutputFile=/dev/fd/3"; |
| 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] = nil; |
| |
| gspid = fork(); |
| if(gspid == 0) { |
| close(stdinp[1]); |
| close(stdoutp[0]); |
| close(dataout[0]); |
| close(errout[0]); |
| |
| /* |
| * 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) |
| ; |
| |
| stdinp[0] = dup(stdinp[0], -1); |
| stdoutp[1] = dup(stdoutp[1], -1); |
| errout[1] = dup(errout[1], -1); |
| dataout[1] = dup(dataout[1], -1); |
| |
| dup(stdinp[0], 0); |
| dup(errout[1], 1); |
| dup(devnull, 2); /* never anything useful */ |
| dup(dataout[1], 3); |
| dup(stdoutp[1], 4); |
| for(i=5; i<20; i++) |
| close(i); |
| execvp("gs", args); |
| wexits("exec"); |
| } |
| close(stdinp[0]); |
| close(stdoutp[1]); |
| close(errout[1]); |
| close(dataout[1]); |
| atexit(killgs); |
| |
| cp = chancreate(sizeof(int), 0); |
| if(teegs) { |
| proccreate(spawnreader, cp, mainstacksize); |
| send(cp, &stdoutp[0]); |
| recv(cp, &stdoutp[0]); |
| } |
| |
| gsfd = g->gsfd = stdinp[1]; |
| g->gspid = gspid; |
| g->g.fd = dataout[0]; |
| g->g.name = "gs pipe"; |
| g->g.type = Ibmp; |
| |
| proccreate(spawnmonitor, cp, mainstacksize); |
| send(cp, &errout[0]); |
| chanfree(cp); |
| |
| Binit(&g->gsrd, stdoutp[0], OREAD); |
| |
| gscmd(g, "/PAGEOUT (/dev/fd/4) (w) file def\n"); |
| if(!strcmp(safer, "-dSAFER")) |
| gscmd(g, ".setsafe\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; |
| default: |
| 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); |
| } |