xref: /plan9/sys/src/cmd/page/gs.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
17dd7cddfSDavid du Colombier /*
27dd7cddfSDavid du Colombier  * gs interface for page.
37dd7cddfSDavid du Colombier  * ps.c and pdf.c both use these routines.
47dd7cddfSDavid du Colombier  * a caveat: if you run more than one gs, only the last
57dd7cddfSDavid du Colombier  * one gets killed by killgs
67dd7cddfSDavid du Colombier  */
77dd7cddfSDavid du Colombier #include <u.h>
87dd7cddfSDavid du Colombier #include <libc.h>
97dd7cddfSDavid du Colombier #include <draw.h>
107dd7cddfSDavid du Colombier #include <event.h>
117dd7cddfSDavid du Colombier #include <bio.h>
127dd7cddfSDavid du Colombier #include "page.h"
137dd7cddfSDavid du Colombier 
147dd7cddfSDavid du Colombier static int gspid;	/* globals for atexit */
157dd7cddfSDavid du Colombier static int gsfd;
167dd7cddfSDavid du Colombier static void	killgs(void);
177dd7cddfSDavid du Colombier 
187dd7cddfSDavid du Colombier static void
killgs(void)197dd7cddfSDavid du Colombier killgs(void)
207dd7cddfSDavid du Colombier {
217dd7cddfSDavid du Colombier 	char tmpfile[100];
227dd7cddfSDavid du Colombier 
237dd7cddfSDavid du Colombier 	close(gsfd);
247dd7cddfSDavid du Colombier 	postnote(PNGROUP, getpid(), "die");
257dd7cddfSDavid du Colombier 
267dd7cddfSDavid du Colombier 	/*
277dd7cddfSDavid du Colombier 	 * from ghostscript's use.txt:
287dd7cddfSDavid du Colombier 	 * ``Ghostscript currently doesn't do a very good job of deleting temporary
29d9306527SDavid du Colombier 	 * files when it exits; you may have to delete them manually from time to
307dd7cddfSDavid du Colombier 	 * time.''
317dd7cddfSDavid du Colombier 	 */
327dd7cddfSDavid du Colombier 	sprint(tmpfile, "/tmp/gs_%.5da", (gspid+300000)%100000);
337dd7cddfSDavid du Colombier 	if(chatty) fprint(2, "remove %s...\n", tmpfile);
347dd7cddfSDavid du Colombier 	remove(tmpfile);
357dd7cddfSDavid du Colombier 	sleep(100);
367dd7cddfSDavid du Colombier 	postnote(PNPROC, gspid, "die yankee pig dog");
377dd7cddfSDavid du Colombier }
387dd7cddfSDavid du Colombier 
397dd7cddfSDavid du Colombier int
spawnwriter(GSInfo * g,Biobuf * b)407dd7cddfSDavid du Colombier spawnwriter(GSInfo *g, Biobuf *b)
417dd7cddfSDavid du Colombier {
427dd7cddfSDavid du Colombier 	char buf[4096];
437dd7cddfSDavid du Colombier 	int n;
447dd7cddfSDavid du Colombier 	int fd;
457dd7cddfSDavid du Colombier 
467dd7cddfSDavid du Colombier 	switch(fork()){
477dd7cddfSDavid du Colombier 	case -1:	return -1;
487dd7cddfSDavid du Colombier 	case 0:	break;
497dd7cddfSDavid du Colombier 	default:	return 0;
507dd7cddfSDavid du Colombier 	}
517dd7cddfSDavid du Colombier 
527dd7cddfSDavid du Colombier 	Bseek(b, 0, 0);
537dd7cddfSDavid du Colombier 	fd = g->gsfd;
547dd7cddfSDavid du Colombier 	while((n = Bread(b, buf, sizeof buf)) > 0)
557dd7cddfSDavid du Colombier 		write(fd, buf, n);
567dd7cddfSDavid du Colombier 	fprint(fd, "(/fd/3) (w) file dup (THIS IS NOT AN INFERNO BITMAP\\n) writestring flushfile\n");
577dd7cddfSDavid du Colombier 	_exits(0);
587dd7cddfSDavid du Colombier 	return -1;
597dd7cddfSDavid du Colombier }
607dd7cddfSDavid du Colombier 
617dd7cddfSDavid du Colombier int
spawnreader(int fd)627dd7cddfSDavid du Colombier spawnreader(int fd)
637dd7cddfSDavid du Colombier {
647dd7cddfSDavid du Colombier 	int n, pfd[2];
657dd7cddfSDavid du Colombier 	char buf[1024];
667dd7cddfSDavid du Colombier 
677dd7cddfSDavid du Colombier 	if(pipe(pfd)<0)
687dd7cddfSDavid du Colombier 		return -1;
697dd7cddfSDavid du Colombier 	switch(fork()){
707dd7cddfSDavid du Colombier 	case -1:
717dd7cddfSDavid du Colombier 		return -1;
727dd7cddfSDavid du Colombier 	case 0:
737dd7cddfSDavid du Colombier 		break;
747dd7cddfSDavid du Colombier 	default:
757dd7cddfSDavid du Colombier 		close(pfd[0]);
767dd7cddfSDavid du Colombier 		return pfd[1];
777dd7cddfSDavid du Colombier 	}
787dd7cddfSDavid du Colombier 
797dd7cddfSDavid du Colombier 	close(pfd[1]);
807dd7cddfSDavid du Colombier 	switch(fork()){
817dd7cddfSDavid du Colombier 	case -1:
827dd7cddfSDavid du Colombier 		wexits("fork failed");
837dd7cddfSDavid du Colombier 	case 0:
847dd7cddfSDavid du Colombier 		while((n=read(fd, buf, sizeof buf)) > 0) {
857dd7cddfSDavid du Colombier 			write(1, buf, n);
867dd7cddfSDavid du Colombier 			write(pfd[0], buf, n);
877dd7cddfSDavid du Colombier 		}
887dd7cddfSDavid du Colombier 		break;
897dd7cddfSDavid du Colombier 	default:
907dd7cddfSDavid du Colombier 		while((n=read(pfd[0], buf, sizeof buf)) > 0) {
917dd7cddfSDavid du Colombier 			write(1, buf, n);
927dd7cddfSDavid du Colombier 			write(fd, buf, n);
937dd7cddfSDavid du Colombier 		}
947dd7cddfSDavid du Colombier 		break;
957dd7cddfSDavid du Colombier 	}
967dd7cddfSDavid du Colombier 	postnote(PNGROUP, getpid(), "i'm die-ing");
977dd7cddfSDavid du Colombier 	_exits(0);
987dd7cddfSDavid du Colombier 	return -1;
997dd7cddfSDavid du Colombier }
1007dd7cddfSDavid du Colombier 
1017dd7cddfSDavid du Colombier void
spawnmonitor(int fd)1027dd7cddfSDavid du Colombier spawnmonitor(int fd)
1037dd7cddfSDavid du Colombier {
1047dd7cddfSDavid du Colombier 	char buf[4096];
1057dd7cddfSDavid du Colombier 	char *xbuf;
1067dd7cddfSDavid du Colombier 	int n;
1077dd7cddfSDavid du Colombier 	int out;
1087dd7cddfSDavid du Colombier 	int first;
1097dd7cddfSDavid du Colombier 
1107dd7cddfSDavid du Colombier 	switch(rfork(RFFDG|RFNOTEG|RFPROC)){
1117dd7cddfSDavid du Colombier 	case -1:
1127dd7cddfSDavid du Colombier 	default:
1137dd7cddfSDavid du Colombier 		return;
1147dd7cddfSDavid du Colombier 
1157dd7cddfSDavid du Colombier 	case 0:
1167dd7cddfSDavid du Colombier 		break;
1177dd7cddfSDavid du Colombier 	}
1187dd7cddfSDavid du Colombier 
1197dd7cddfSDavid du Colombier 	out = open("/dev/cons", OWRITE);
1207dd7cddfSDavid du Colombier 	if(out < 0)
1217dd7cddfSDavid du Colombier 		out = 2;
1227dd7cddfSDavid du Colombier 
1237dd7cddfSDavid du Colombier 	xbuf = buf;	/* for ease of acid */
1247dd7cddfSDavid du Colombier 	first = 1;
1257dd7cddfSDavid du Colombier 	while((n = read(fd, xbuf, sizeof buf)) > 0){
1267dd7cddfSDavid du Colombier 		if(first){
1277dd7cddfSDavid du Colombier 			first = 0;
1287dd7cddfSDavid du Colombier 			fprint(2, "Ghostscript Error:\n");
1297dd7cddfSDavid du Colombier 		}
1307dd7cddfSDavid du Colombier 		write(out, xbuf, n);
1317dd7cddfSDavid du Colombier 		alarm(500);
1327dd7cddfSDavid du Colombier 	}
1337dd7cddfSDavid du Colombier 	_exits(0);
1347dd7cddfSDavid du Colombier }
1357dd7cddfSDavid du Colombier 
1367dd7cddfSDavid du Colombier int
spawngs(GSInfo * g,char * safer)137*593dc095SDavid du Colombier spawngs(GSInfo *g, char *safer)
1387dd7cddfSDavid du Colombier {
1397dd7cddfSDavid du Colombier 	char *args[16];
1407dd7cddfSDavid du Colombier 	char tb[32], gb[32];
1417dd7cddfSDavid du Colombier 	int i, nargs;
1427dd7cddfSDavid du Colombier 	int devnull;
1437dd7cddfSDavid du Colombier 	int stdinout[2];
1447dd7cddfSDavid du Colombier 	int dataout[2];
1457dd7cddfSDavid du Colombier 	int errout[2];
1467dd7cddfSDavid du Colombier 
1477dd7cddfSDavid du Colombier 	/*
1487dd7cddfSDavid du Colombier 	 * spawn gs
1497dd7cddfSDavid du Colombier 	 *
1507dd7cddfSDavid du Colombier  	 * gs's standard input is fed from stdinout.
1517dd7cddfSDavid du Colombier 	 * gs output written to fd-2 (i.e. output we generate intentionally) is fed to stdinout.
1527dd7cddfSDavid du Colombier 	 * gs output written to fd 1 (i.e. ouptut gs generates on error) is fed to errout.
1537dd7cddfSDavid du Colombier 	 * gs data output is written to fd 3, which is dataout.
1547dd7cddfSDavid du Colombier 	 */
1557dd7cddfSDavid du Colombier 	if(pipe(stdinout) < 0 || pipe(dataout)<0 || pipe(errout)<0)
1567dd7cddfSDavid du Colombier 		return -1;
1577dd7cddfSDavid du Colombier 
1587dd7cddfSDavid du Colombier 	nargs = 0;
1597dd7cddfSDavid du Colombier 	args[nargs++] = "gs";
1607dd7cddfSDavid du Colombier 	args[nargs++] = "-dNOPAUSE";
161*593dc095SDavid du Colombier 	args[nargs++] = safer;
1623ff48bf5SDavid du Colombier 	args[nargs++] = "-sDEVICE=plan9";
1637dd7cddfSDavid du Colombier 	args[nargs++] = "-sOutputFile=/fd/3";
1647dd7cddfSDavid du Colombier 	args[nargs++] = "-dQUIET";
1657dd7cddfSDavid du Colombier 	args[nargs++] = "-r100";
1667dd7cddfSDavid du Colombier 	sprint(tb, "-dTextAlphaBits=%d", textbits);
1677dd7cddfSDavid du Colombier 	sprint(gb, "-dGraphicsAlphaBits=%d", gfxbits);
1687dd7cddfSDavid du Colombier 	if(textbits)
1697dd7cddfSDavid du Colombier 		args[nargs++] = tb;
1707dd7cddfSDavid du Colombier 	if(gfxbits)
1717dd7cddfSDavid du Colombier 		args[nargs++] = gb;
1727dd7cddfSDavid du Colombier 	args[nargs++] = "-";
1737dd7cddfSDavid du Colombier 	args[nargs] = nil;
1747dd7cddfSDavid du Colombier 
1757dd7cddfSDavid du Colombier 	gspid = fork();
1767dd7cddfSDavid du Colombier 	if(gspid == 0) {
1777dd7cddfSDavid du Colombier 		close(stdinout[1]);
1787dd7cddfSDavid du Colombier 		close(dataout[1]);
1797dd7cddfSDavid du Colombier 		close(errout[1]);
1807dd7cddfSDavid du Colombier 
1817dd7cddfSDavid du Colombier 		/*
1827dd7cddfSDavid du Colombier 		 * Horrible problem: we want to dup fd's 0-4 below,
1837dd7cddfSDavid du Colombier 		 * but some of the source fd's might have those small numbers.
1847dd7cddfSDavid du Colombier 		 * So we need to reallocate those.  In order to not step on
1857dd7cddfSDavid du Colombier 		 * anything else, we'll dup the fd's to higher ones using
1867dd7cddfSDavid du Colombier 		 * dup(x, -1), but we need to use up the lower ones first.
1877dd7cddfSDavid du Colombier 		 */
1887dd7cddfSDavid du Colombier 		while((devnull = open("/dev/null", ORDWR)) < 5)
1897dd7cddfSDavid du Colombier 			;
1907dd7cddfSDavid du Colombier 
1917dd7cddfSDavid du Colombier 		stdinout[0] = dup(stdinout[0], -1);
1927dd7cddfSDavid du Colombier 		errout[0] = dup(errout[0], -1);
1937dd7cddfSDavid du Colombier 		dataout[0] = dup(dataout[0], -1);
1947dd7cddfSDavid du Colombier 
1957dd7cddfSDavid du Colombier 		dup(stdinout[0], 0);
1967dd7cddfSDavid du Colombier 		dup(errout[0], 1);
1977dd7cddfSDavid du Colombier 		dup(devnull, 2);	/* never anything useful */
1987dd7cddfSDavid du Colombier 		dup(dataout[0], 3);
1997dd7cddfSDavid du Colombier 		dup(stdinout[0], 4);
2007dd7cddfSDavid du Colombier 		for(i=5; i<20; i++)
2017dd7cddfSDavid du Colombier 			close(i);
2027dd7cddfSDavid du Colombier 		exec("/bin/gs", args);
2037dd7cddfSDavid du Colombier 		wexits("exec");
2047dd7cddfSDavid du Colombier 	}
2057dd7cddfSDavid du Colombier 	close(stdinout[0]);
2067dd7cddfSDavid du Colombier 	close(errout[0]);
2077dd7cddfSDavid du Colombier 	close(dataout[0]);
2087dd7cddfSDavid du Colombier 	atexit(killgs);
2097dd7cddfSDavid du Colombier 
2107dd7cddfSDavid du Colombier 	if(teegs)
2117dd7cddfSDavid du Colombier 		stdinout[1] = spawnreader(stdinout[1]);
2127dd7cddfSDavid du Colombier 
2137dd7cddfSDavid du Colombier 	gsfd = g->gsfd = stdinout[1];
2147dd7cddfSDavid du Colombier 	g->gsdfd = dataout[1];
2157dd7cddfSDavid du Colombier 	g->gspid = gspid;
2167dd7cddfSDavid du Colombier 
2177dd7cddfSDavid du Colombier 	spawnmonitor(errout[1]);
2187dd7cddfSDavid du Colombier 	Binit(&g->gsrd, g->gsfd, OREAD);
2197dd7cddfSDavid du Colombier 
2207dd7cddfSDavid du Colombier 	gscmd(g, "/PAGEOUT (/fd/4) (w) file def\n");
2217dd7cddfSDavid du Colombier 	gscmd(g, "/PAGE== { PAGEOUT exch write==only PAGEOUT (\\n) writestring PAGEOUT flushfile } def\n");
2227dd7cddfSDavid du Colombier 	waitgs(g);
2237dd7cddfSDavid du Colombier 
2247dd7cddfSDavid du Colombier 	return 0;
2257dd7cddfSDavid du Colombier }
2267dd7cddfSDavid du Colombier 
2277dd7cddfSDavid du Colombier int
gscmd(GSInfo * gs,char * fmt,...)2287dd7cddfSDavid du Colombier gscmd(GSInfo *gs, char *fmt, ...)
2297dd7cddfSDavid du Colombier {
2307dd7cddfSDavid du Colombier 	char buf[1024];
2317dd7cddfSDavid du Colombier 	int n;
2327dd7cddfSDavid du Colombier 
2337dd7cddfSDavid du Colombier 	va_list v;
2347dd7cddfSDavid du Colombier 	va_start(v, fmt);
2359a747e4fSDavid du Colombier 	n = vseprint(buf, buf+sizeof buf, fmt, v) - buf;
2367dd7cddfSDavid du Colombier 	if(n <= 0)
2377dd7cddfSDavid du Colombier 		return n;
2387dd7cddfSDavid du Colombier 
2397dd7cddfSDavid du Colombier 	if(chatty) {
2407dd7cddfSDavid du Colombier 		fprint(2, "cmd: ");
2417dd7cddfSDavid du Colombier 		write(2, buf, n);
2427dd7cddfSDavid du Colombier 	}
2437dd7cddfSDavid du Colombier 
2447dd7cddfSDavid du Colombier 	if(write(gs->gsfd, buf, n) != 0)
2457dd7cddfSDavid du Colombier 		return -1;
2467dd7cddfSDavid du Colombier 
2477dd7cddfSDavid du Colombier 	return n;
2487dd7cddfSDavid du Colombier }
2497dd7cddfSDavid du Colombier 
2507dd7cddfSDavid du Colombier /*
2517dd7cddfSDavid du Colombier  * set the dimensions of the bitmap we expect to get back from GS.
2527dd7cddfSDavid du Colombier  */
2537dd7cddfSDavid du Colombier void
setdim(GSInfo * gs,Rectangle bbox,int ppi,int landscape)254d9306527SDavid du Colombier setdim(GSInfo *gs, Rectangle bbox, int ppi, int landscape)
2557dd7cddfSDavid du Colombier {
256d9306527SDavid du Colombier 	Rectangle pbox;
257d9306527SDavid du Colombier 
2587dd7cddfSDavid du Colombier 	if(chatty)
2597dd7cddfSDavid du Colombier 		fprint(2, "setdim: bbox=%R\n", bbox);
2607dd7cddfSDavid du Colombier 
2617dd7cddfSDavid du Colombier 	if(ppi)
2627dd7cddfSDavid du Colombier 		gs->ppi = ppi;
2637dd7cddfSDavid du Colombier 
2647dd7cddfSDavid du Colombier 	gscmd(gs, "mark\n");
2657dd7cddfSDavid du Colombier 	if(ppi)
2667dd7cddfSDavid du Colombier 		gscmd(gs, "/HWResolution [%d %d]\n", ppi, ppi);
267d9306527SDavid du Colombier 
268d9306527SDavid du Colombier 	if(!Dx(bbox))
269d9306527SDavid du Colombier 		bbox = Rect(0, 0, 612, 792);	/* 8½×11 */
270d9306527SDavid du Colombier 
271d9306527SDavid du Colombier 	switch(landscape){
272d9306527SDavid du Colombier 	case 0:
273d9306527SDavid du Colombier 		pbox = bbox;
274d9306527SDavid du Colombier 		break;
275d9306527SDavid du Colombier 	case 1:
276d9306527SDavid du Colombier 		pbox = Rect(bbox.min.y, bbox.min.x, bbox.max.y, bbox.max.x);
277d9306527SDavid du Colombier 		break;
278d9306527SDavid du Colombier 	}
279d9306527SDavid du Colombier 	gscmd(gs, "/PageSize [%d %d]\n", Dx(pbox), Dy(pbox));
280d9306527SDavid du Colombier 	gscmd(gs, "/Margins [%d %d]\n", -pbox.min.x, -pbox.min.y);
2817dd7cddfSDavid du Colombier 	gscmd(gs, "currentdevice putdeviceprops pop\n");
2827dd7cddfSDavid du Colombier 	gscmd(gs, "/#copies 1 store\n");
2837dd7cddfSDavid du Colombier 
2847dd7cddfSDavid du Colombier 	if(!eqpt(bbox.min, ZP))
2857dd7cddfSDavid du Colombier 		gscmd(gs, "%d %d translate\n", -bbox.min.x, -bbox.min.y);
2867dd7cddfSDavid du Colombier 
287d9306527SDavid du Colombier 	switch(landscape){
288d9306527SDavid du Colombier 	case 0:
289d9306527SDavid du Colombier 		break;
290d9306527SDavid du Colombier 	case 1:
291d9306527SDavid du Colombier 		gscmd(gs, "%d 0 translate\n", Dy(bbox));
292d9306527SDavid du Colombier 		gscmd(gs, "90 rotate\n");
293d9306527SDavid du Colombier 		break;
294d9306527SDavid du Colombier 	}
295d9306527SDavid du Colombier 
2967dd7cddfSDavid du Colombier 	waitgs(gs);
2977dd7cddfSDavid du Colombier }
2987dd7cddfSDavid du Colombier 
2997dd7cddfSDavid du Colombier void
waitgs(GSInfo * gs)3007dd7cddfSDavid du Colombier waitgs(GSInfo *gs)
3017dd7cddfSDavid du Colombier {
3027dd7cddfSDavid du Colombier 	/* we figure out that gs is done by telling it to
3037dd7cddfSDavid du Colombier 	 * print something and waiting until it does.
3047dd7cddfSDavid du Colombier 	 */
3057dd7cddfSDavid du Colombier 	char *p;
3067dd7cddfSDavid du Colombier 	Biobuf *b = &gs->gsrd;
3077dd7cddfSDavid du Colombier 	uchar buf[1024];
3087dd7cddfSDavid du Colombier 	int n;
3097dd7cddfSDavid du Colombier 
3107dd7cddfSDavid du Colombier //	gscmd(gs, "(\\n**bstack\\n) print flush\n");
3117dd7cddfSDavid du Colombier //	gscmd(gs, "stack flush\n");
3127dd7cddfSDavid du Colombier //	gscmd(gs, "(**estack\\n) print flush\n");
3137dd7cddfSDavid du Colombier 	gscmd(gs, "(\\n//GO.SYSIN DD\\n) PAGE==\n");
3147dd7cddfSDavid du Colombier 
3157dd7cddfSDavid du Colombier 	alarm(300*1000);
3167dd7cddfSDavid du Colombier 	for(;;) {
3177dd7cddfSDavid du Colombier 		p = Brdline(b, '\n');
3187dd7cddfSDavid du Colombier 		if(p == nil) {
3197dd7cddfSDavid du Colombier 			n = Bbuffered(b);
3207dd7cddfSDavid du Colombier 			if(n <= 0)
3217dd7cddfSDavid du Colombier 				break;
3227dd7cddfSDavid du Colombier 			if(n > sizeof buf)
3237dd7cddfSDavid du Colombier 				n = sizeof buf;
3247dd7cddfSDavid du Colombier 			Bread(b, buf, n);
3257dd7cddfSDavid du Colombier 			continue;
3267dd7cddfSDavid du Colombier 		}
3277dd7cddfSDavid du Colombier 		p[Blinelen(b)-1] = 0;
3287dd7cddfSDavid du Colombier 		if(chatty) fprint(2, "p: ");
3297dd7cddfSDavid du Colombier 		if(chatty) write(2, p, Blinelen(b)-1);
3307dd7cddfSDavid du Colombier 		if(chatty) fprint(2, "\n");
3317dd7cddfSDavid du Colombier 		if(strstr(p, "Error:")) {
3327dd7cddfSDavid du Colombier 			alarm(0);
3337dd7cddfSDavid du Colombier 			fprint(2, "ghostscript error: %s\n", p);
3347dd7cddfSDavid du Colombier 			wexits("gs error");
3357dd7cddfSDavid du Colombier 		}
3367dd7cddfSDavid du Colombier 
3377dd7cddfSDavid du Colombier 		if(strstr(p, "//GO.SYSIN DD")) {
3387dd7cddfSDavid du Colombier 			break;
3397dd7cddfSDavid du Colombier 		}
3407dd7cddfSDavid du Colombier 	}
3417dd7cddfSDavid du Colombier 	alarm(0);
3427dd7cddfSDavid du Colombier }
343