plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

gs.c (6449B)


      1 /*
      2  * gs interface for page.
      3  * ps.c and pdf.c both use these routines.
      4  * a caveat: if you run more than one gs, only the last
      5  * one gets killed by killgs
      6  */
      7 #include <u.h>
      8 #include <libc.h>
      9 #include <draw.h>
     10 #include <thread.h>
     11 #include <bio.h>
     12 #include <cursor.h>
     13 #include "page.h"
     14 
     15 static int gspid;	/* globals for atexit */
     16 static int gsfd;
     17 static void	killgs(void);
     18 
     19 static void
     20 killgs(void)
     21 {
     22 	char tmpfile[100];
     23 
     24 	close(gsfd);
     25 	postnote(PNGROUP, getpid(), "die");
     26 
     27 	/*
     28 	 * from ghostscript's use.txt:
     29 	 * ``Ghostscript currently doesn't do a very good job of deleting temporary
     30 	 * files when it exits; you may have to delete them manually from time to
     31 	 * time.''
     32 	 */
     33 	sprint(tmpfile, "/tmp/gs_%.5da", (gspid+300000)%100000);
     34 	if(chatty) fprint(2, "remove %s...\n", tmpfile);
     35 	remove(tmpfile);
     36 	sleep(100);
     37 	postnote(PNPROC, gspid, "die yankee pig dog");
     38 }
     39 
     40 void
     41 spawnreader(void *cp)
     42 {
     43 	int n, fd, pfd[2];
     44 	char buf[1024];
     45 
     46 	recv(cp, &fd);
     47 
     48 	if(pipe(pfd)<0)
     49 		wexits("pipe failed");
     50 
     51 	send(cp, &pfd[1]);
     52 
     53 	while((n=read(pfd[0], buf, sizeof buf)) > 0) {
     54 		write(1, buf, n);
     55 		write(fd, buf, n);
     56 	}
     57 
     58 	close(pfd[0]);
     59 	threadexits(0);
     60 }
     61 
     62 void
     63 spawnmonitor(void *cp)
     64 {
     65 	char buf[4096];
     66 	char *xbuf;
     67 	int fd;
     68 	int n;
     69 	int out;
     70 	int first;
     71 
     72 	recv(cp, &fd);
     73 
     74 	out = open("/dev/tty", OWRITE);
     75 	if(out < 0)
     76 		out = 2;
     77 
     78 	xbuf = buf;	/* for ease of acid */
     79 	first = 1;
     80 	while((n = read(fd, xbuf, sizeof buf)) > 0){
     81 		if(first){
     82 			first = 0;
     83 			fprint(2, "Ghostscript Error:\n");
     84 		}
     85 		write(out, xbuf, n);
     86 		alarm(500);
     87 	}
     88 	threadexits(0);
     89 }
     90 
     91 int
     92 spawngs(GSInfo *g, char *safer)
     93 {
     94 	Channel *cp;
     95 	char *args[16];
     96 	char tb[32], gb[32];
     97 	int i, nargs;
     98 	int devnull;
     99 	int stdinp[2];
    100 	int stdoutp[2];
    101 	int dataout[2];
    102 	int errout[2];
    103 
    104 	/*
    105 	 * spawn gs
    106 	 *
    107  	 * gs's standard input is fed from stdinout.
    108 	 * gs output written to fd-2 (i.e. output we generate intentionally) is fed to stdinout.
    109 	 * gs output written to fd 1 (i.e. ouptut gs generates on error) is fed to errout.
    110 	 * gs data output is written to fd 3, which is dataout.
    111 	 */
    112 	if(pipe(stdinp)<0 || pipe(stdoutp)<0 || pipe(dataout)<0 || pipe(errout)<0)
    113 		return -1;
    114 
    115 	nargs = 0;
    116 	args[nargs++] = "gs";
    117 	args[nargs++] = "-dNOPAUSE";
    118 	args[nargs++] = "-dNOPROMPT";
    119 	args[nargs++] = "-dDELAYSAFER";
    120 	args[nargs++] = "-dQUIET";
    121 	args[nargs++] = "-sDEVICE=bmp16m";
    122 	args[nargs++] = "-sOutputFile=/dev/fd/3";
    123 	args[nargs++] = "-r100";
    124 	sprint(tb, "-dTextAlphaBits=%d", textbits);
    125 	sprint(gb, "-dGraphicsAlphaBits=%d", gfxbits);
    126 	if(textbits)
    127 		args[nargs++] = tb;
    128 	if(gfxbits)
    129 		args[nargs++] = gb;
    130 	args[nargs] = nil;
    131 
    132 	gspid = fork();
    133 	if(gspid == 0) {
    134 		close(stdinp[1]);
    135 		close(stdoutp[0]);
    136 		close(dataout[0]);
    137 		close(errout[0]);
    138 
    139 		/*
    140 		 * Horrible problem: we want to dup fd's 0-4 below,
    141 		 * but some of the source fd's might have those small numbers.
    142 		 * So we need to reallocate those.  In order to not step on
    143 		 * anything else, we'll dup the fd's to higher ones using
    144 		 * dup(x, -1), but we need to use up the lower ones first.
    145 		 */
    146 		while((devnull = open("/dev/null", ORDWR)) < 5)
    147 			;
    148 
    149 		stdinp[0] = dup(stdinp[0], -1);
    150 		stdoutp[1] = dup(stdoutp[1], -1);
    151 		errout[1] = dup(errout[1], -1);
    152 		dataout[1] = dup(dataout[1], -1);
    153 
    154 		dup(stdinp[0], 0);
    155 		dup(errout[1], 1);
    156 		dup(devnull, 2);	/* never anything useful */
    157 		dup(dataout[1], 3);
    158 		dup(stdoutp[1], 4);
    159 		for(i=5; i<20; i++)
    160 			close(i);
    161 		execvp("gs", args);
    162 		wexits("exec");
    163 	}
    164 	close(stdinp[0]);
    165 	close(stdoutp[1]);
    166 	close(errout[1]);
    167 	close(dataout[1]);
    168 	atexit(killgs);
    169 
    170 	cp = chancreate(sizeof(int), 0);
    171 	if(teegs) {
    172 		proccreate(spawnreader, cp, mainstacksize);
    173 		send(cp, &stdoutp[0]);
    174 		recv(cp, &stdoutp[0]);
    175 	}
    176 
    177 	gsfd = g->gsfd = stdinp[1];
    178 	g->gspid = gspid;
    179 	g->g.fd = dataout[0];
    180 	g->g.name = "gs pipe";
    181 	g->g.type = Ibmp;
    182 
    183 	proccreate(spawnmonitor, cp, mainstacksize);
    184 	send(cp, &errout[0]);
    185 	chanfree(cp);
    186 
    187 	Binit(&g->gsrd, stdoutp[0], OREAD);
    188 
    189 	gscmd(g, "/PAGEDIDSHOWPAGE false def\n");
    190 	gscmd(g, "/showpage { /PAGEDIDSHOWPAGE true def showpage } bind def\n");
    191 	gscmd(g, "/PAGEFLUSH { PAGEDIDSHOWPAGE not {showpage} if /PAGEDIDSHOWPAGE false def } def\n");
    192 
    193 	gscmd(g, "/PAGEOUT (/dev/fd/4) (w) file def\n");
    194 	if(!strcmp(safer, "-dSAFER"))
    195 		gscmd(g, ".setsafe\n");
    196 	gscmd(g, "/PAGE== { PAGEOUT exch write==only PAGEOUT (\\n) writestring PAGEOUT flushfile } def\n");
    197 	waitgs(g);
    198 
    199 	return 0;
    200 }
    201 
    202 int
    203 gscmd(GSInfo *gs, char *fmt, ...)
    204 {
    205 	char buf[1024];
    206 	int n;
    207 
    208 	va_list v;
    209 	va_start(v, fmt);
    210 	n = vseprint(buf, buf+sizeof buf, fmt, v) - buf;
    211 	if(n <= 0)
    212 		return n;
    213 
    214 	if(chatty) {
    215 		fprint(2, "cmd: ");
    216 		write(2, buf, n);
    217 	}
    218 
    219 	if(write(gs->gsfd, buf, n) != 0)
    220 		return -1;
    221 
    222 	return n;
    223 }
    224 
    225 /*
    226  * set the dimensions of the bitmap we expect to get back from GS.
    227  */
    228 void
    229 setdim(GSInfo *gs, Rectangle bbox, int ppi, int landscape)
    230 {
    231 	Rectangle pbox;
    232 
    233 	if(chatty)
    234 		fprint(2, "setdim: bbox=%R\n", bbox);
    235 
    236 	if(ppi)
    237 		gs->ppi = ppi;
    238 
    239 	gscmd(gs, "mark\n");
    240 	if(ppi)
    241 		gscmd(gs, "/HWResolution [%d %d]\n", ppi, ppi);
    242 
    243 	if(!Dx(bbox))
    244 		bbox = Rect(0, 0, 612, 792);	/* 8½×11 */
    245 
    246 	switch(landscape){
    247 	case 0:
    248 		pbox = bbox;
    249 		break;
    250 	default:
    251 		pbox = Rect(bbox.min.y, bbox.min.x, bbox.max.y, bbox.max.x);
    252 		break;
    253 	}
    254 	gscmd(gs, "/PageSize [%d %d]\n", Dx(pbox), Dy(pbox));
    255 	gscmd(gs, "/Margins [%d %d]\n", -pbox.min.x, -pbox.min.y);
    256 	gscmd(gs, "currentdevice putdeviceprops pop\n");
    257 	gscmd(gs, "/#copies 1 store\n");
    258 
    259 	if(!eqpt(bbox.min, ZP))
    260 		gscmd(gs, "%d %d translate\n", -bbox.min.x, -bbox.min.y);
    261 
    262 	switch(landscape){
    263 	case 0:
    264 		break;
    265 	case 1:
    266 		gscmd(gs, "%d 0 translate\n", Dy(bbox));
    267 		gscmd(gs, "90 rotate\n");
    268 		break;
    269 	}
    270 
    271 	waitgs(gs);
    272 }
    273 
    274 void
    275 waitgs(GSInfo *gs)
    276 {
    277 	/* we figure out that gs is done by telling it to
    278 	 * print something and waiting until it does.
    279 	 */
    280 	char *p;
    281 	Biobuf *b = &gs->gsrd;
    282 	uchar buf[1024];
    283 	int n;
    284 
    285 //	gscmd(gs, "(\\n**bstack\\n) print flush\n");
    286 //	gscmd(gs, "stack flush\n");
    287 //	gscmd(gs, "(**estack\\n) print flush\n");
    288 	gscmd(gs, "(\\n//GO.SYSIN DD\\n) PAGE==\n");
    289 
    290 	alarm(300*1000);
    291 	for(;;) {
    292 		p = Brdline(b, '\n');
    293 		if(p == nil) {
    294 			n = Bbuffered(b);
    295 			if(n <= 0)
    296 				break;
    297 			if(n > sizeof buf)
    298 				n = sizeof buf;
    299 			Bread(b, buf, n);
    300 			continue;
    301 		}
    302 		p[Blinelen(b)-1] = 0;
    303 		if(chatty) fprint(2, "p: ");
    304 		if(chatty) write(2, p, Blinelen(b)-1);
    305 		if(chatty) fprint(2, "\n");
    306 		if(strstr(p, "Error:")) {
    307 			alarm(0);
    308 			fprint(2, "ghostscript error: %s\n", p);
    309 			wexits("gs error");
    310 		}
    311 
    312 		if(strstr(p, "//GO.SYSIN DD")) {
    313 			break;
    314 		}
    315 	}
    316 	alarm(0);
    317 }