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