plan9fox/sys/src/cmd/page/gs.c
2011-03-30 19:35:09 +03:00

343 lines
6.4 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 *safer)
{
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++] = safer;
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);
}