/* * ps.c * * provide postscript file reading support for page */ #include #include #include #include #include #include #include "page.h" typedef struct PSInfo PSInfo; typedef struct Page Page; struct Page { char *name; int offset; /* offset of page beginning within file */ }; struct PSInfo { GSInfo; Rectangle bbox; /* default bounding box */ Page *page; int npage; int clueless; /* don't know where page boundaries are */ long psoff; /* location of %! in file */ char ctm[256]; }; static int pswritepage(Document *d, int fd, int page); static Image* psdrawpage(Document *d, int page); static char* pspagename(Document*, int); #define R(r) (r).min.x, (r).min.y, (r).max.x, (r).max.y Rectangle rdbbox(char *p) { Rectangle r; int a; char *f[4]; while(*p == ':' || *p == ' ' || *p == '\t') p++; if(tokenize(p, f, 4) != 4) return Rect(0,0,0,0); r = Rect(atoi(f[0]), atoi(f[1]), atoi(f[2]), atoi(f[3])); r = canonrect(r); if(Dx(r) <= 0 || Dy(r) <= 0) return Rect(0,0,0,0); if(truetoboundingbox) return r; /* initdraw not called yet, can't use %R */ if(chatty) fprint(2, "[%d %d %d %d] -> ", R(r)); /* * attempt to sniff out A4, 8½×11, others * A4 is 596×842 * 8½×11 is 612×792 */ a = Dx(r)*Dy(r); if(a < 300*300){ /* really small, probably supposed to be */ /* empty */ } else if(Dx(r) <= 596 && r.max.x <= 596 && Dy(r) > 792 && Dy(r) <= 842 && r.max.y <= 842) /* A4 */ r = Rect(0, 0, 596, 842); else { /* cast up to 8½×11 */ if(Dx(r) <= 612 && r.max.x <= 612){ r.min.x = 0; r.max.x = 612; } if(Dy(r) <= 792 && r.max.y <= 792){ r.min.y = 0; r.max.y = 792; } } if(chatty) fprint(2, "[%d %d %d %d]\n", R(r)); return r; } #define RECT(X) X.min.x, X.min.y, X.max.x, X.max.y int prefix(char *x, char *y) { return strncmp(x, y, strlen(y)) == 0; } /* * document ps is really being printed as n-up pages. * we need to treat every n pages as 1. */ void repaginate(PSInfo *ps, int n) { int i, np, onp; Page *page; page = ps->page; onp = ps->npage; np = (ps->npage+n-1)/n; if(chatty) { for(i=0; i<=onp+1; i++) print("page %d: %d\n", i, page[i].offset); } for(i=0; inpage = np; if(chatty) { for(i=0; i<=np+1; i++) print("page %d: %d\n", i, page[i].offset); } } Document* initps(Biobuf *b, int argc, char **argv, uchar *buf, int nbuf) { Document *d; PSInfo *ps; char *p; char *q, *r; char eol; char *nargv[1]; char fdbuf[20]; char tmp[32]; int fd; int i; int incomments; int cantranslate; int trailer=0; int nesting=0; int dumb=0; int landscape=0; long psoff; long npage, mpage; Page *page; Rectangle bbox = Rect(0,0,0,0); if(argc > 1) { fprint(2, "can only view one ps file at a time\n"); return nil; } fprint(2, "reading through postscript...\n"); if(b == nil){ /* standard input; spool to disk (ouch) */ fd = spooltodisk(buf, nbuf, nil); sprint(fdbuf, "/fd/%d", fd); b = Bopen(fdbuf, OREAD); if(b == nil){ fprint(2, "cannot open disk spool file\n"); wexits("Bopen temp"); } nargv[0] = fdbuf; argv = nargv; } /* find %!, perhaps after PCL nonsense */ Bseek(b, 0, 0); psoff = 0; eol = 0; for(i=0; i<16; i++){ psoff = Boffset(b); if(!(p = Brdline(b, eol='\n')) && !(p = Brdline(b, eol='\r'))) { fprint(2, "cannot find end of first line\n"); wexits("initps"); } if(p[0]=='\x1B') p++, psoff++; if(p[0] == '%' && p[1] == '!') break; } if(i == 16){ werrstr("not ps"); return nil; } /* page counting */ npage = 0; mpage = 16; page = emalloc(mpage*sizeof(*page)); memset(page, 0, mpage*sizeof(*page)); cantranslate = goodps; incomments = 1; Keepreading: while(p = Brdline(b, eol)) { if(p[0] == '%') if(chatty > 1) fprint(2, "ps %.*s\n", utfnlen(p, Blinelen(b)-1), p); if(npage == mpage) { mpage *= 2; page = erealloc(page, mpage*sizeof(*page)); memset(&page[npage], 0, npage*sizeof(*page)); } if(p[0] != '%' || p[1] != '%') continue; if(prefix(p, "%%BeginDocument")) { nesting++; continue; } if(nesting > 0 && prefix(p, "%%EndDocument")) { nesting--; continue; } if(nesting) continue; if(prefix(p, "%%EndComment")) { incomments = 0; continue; } if(reverse == -1 && prefix(p, "%%PageOrder")) { /* glean whether we should reverse the viewing order */ p[Blinelen(b)-1] = 0; if(strstr(p, "Ascend")) reverse = 0; else if(strstr(p, "Descend")) reverse = 1; else if(strstr(p, "Special")) dumb = 1; p[Blinelen(b)-1] = '\n'; continue; } else if(prefix(p, "%%Trailer")) { incomments = 1; page[npage].offset = Boffset(b)-Blinelen(b); trailer = 1; continue; } else if(incomments && prefix(p, "%%Orientation")) { if(strstr(p, "Landscape")) landscape = 1; } else if(incomments && Dx(bbox)==0 && prefix(p, q="%%BoundingBox")) { bbox = rdbbox(p+strlen(q)+1); if(chatty) /* can't use %R because haven't initdraw() */ fprint(2, "document bbox [%d %d %d %d]\n", RECT(bbox)); continue; } /* * If they use the initgraphics command, we can't play our translation tricks. */ p[Blinelen(b)-1] = 0; if((q=strstr(p, "initgraphics")) && ((r=strchr(p, '%'))==nil || r > q)) cantranslate = 0; p[Blinelen(b)-1] = eol; if(!prefix(p, "%%Page:")) continue; /* * figure out of the %%Page: line contains a page number * or some other page description to use in the menu bar. * * lines look like %%Page: x y or %%Page: x * we prefer just x, and will generate our * own if necessary. */ p[Blinelen(b)-1] = 0; if(chatty) fprint(2, "page %s\n", p); r = p+7; while(*r == ' ' || *r == '\t') r++; q = r; while(*q && *q != ' ' && *q != '\t') q++; free(page[npage].name); if(*r) { if(*r == '"' && *q == '"') r++, q--; if(*q) *q = 0; page[npage].name = estrdup(r); *q = 'x'; } else { snprint(tmp, sizeof tmp, "p %ld", npage+1); page[npage].name = estrdup(tmp); } /* * store the offset info for later viewing */ trailer = 0; p[Blinelen(b)-1] = eol; page[npage++].offset = Boffset(b)-Blinelen(b); } if(Blinelen(b) > 0){ fprint(2, "page: linelen %d\n", Blinelen(b)); Bseek(b, Blinelen(b), 1); goto Keepreading; } if(Dx(bbox) == 0 || Dy(bbox) == 0) bbox = Rect(0,0,612,792); /* 8½×11 */ /* * if we didn't find any pages, assume the document * is one big page */ if(npage == 0) { dumb = 1; if(chatty) fprint(2, "don't know where pages are\n"); reverse = 0; goodps = 0; trailer = 0; page[npage].name = "p 1"; page[npage++].offset = 0; } if(npage+2 > mpage) { mpage += 2; page = erealloc(page, mpage*sizeof(*page)); memset(&page[mpage-2], 0, 2*sizeof(*page)); } if(!trailer) page[npage].offset = Boffset(b); Bseek(b, 0, 2); /* EOF */ page[npage+1].offset = Boffset(b); d = emalloc(sizeof(*d)); ps = emalloc(sizeof(*ps)); ps->page = page; ps->npage = npage; ps->bbox = bbox; ps->psoff = psoff; d->extra = ps; d->npage = ps->npage; d->b = b; d->drawpage = psdrawpage; d->pagename = pspagename; d->fwdonly = ps->clueless = dumb; d->docname = argv[0]; if(spawngs(ps, "-dSAFER") < 0) return nil; if(!cantranslate) bbox.min = ZP; setdim(ps, bbox, ppi, landscape); if(goodps){ /* * We want to only send the page (i.e. not header and trailer) information * for each page, so initialize the device by sending the header now. */ pswritepage(d, ps->gsfd, -1); waitgs(ps); } if(dumb) { fprint(ps->gsfd, "(%s) run\n", argv[0]); fprint(ps->gsfd, "(/fd/3) (w) file dup (THIS IS NOT A PLAN9 BITMAP 01234567890123456789012345678901234567890123456789\\n) writestring flushfile\n"); } ps->bbox = bbox; return d; } static int pswritepage(Document *d, int fd, int page) { Biobuf *b = d->b; PSInfo *ps = d->extra; int t, n, i; long begin, end; char buf[8192]; if(page == -1) begin = ps->psoff; else begin = ps->page[page].offset; end = ps->page[page+1].offset; if(chatty) { fprint(2, "writepage(%d)... from #%ld to #%ld...\n", page, begin, end); } Bseek(b, begin, 0); t = end-begin; n = sizeof(buf); if(n > t) n = t; while(t > 0 && (i=Bread(b, buf, n)) > 0) { if(write(fd, buf, i) != i) return -1; t -= i; if(n > t) n = t; } return end-begin; } static Image* psdrawpage(Document *d, int page) { PSInfo *ps = d->extra; Image *im; if(ps->clueless) return readimage(display, ps->gsdfd, 0); waitgs(ps); if(goodps) pswritepage(d, ps->gsfd, page); else { pswritepage(d, ps->gsfd, -1); pswritepage(d, ps->gsfd, page); pswritepage(d, ps->gsfd, d->npage); } /* * If last line terminator is \r, gs will read ahead to check for \n * so send one to avoid deadlock. */ write(ps->gsfd, "\n", 1); im = readimage(display, ps->gsdfd, 0); if(im == nil) { fprint(2, "fatal: readimage error %r\n"); wexits("readimage"); } waitgs(ps); return im; } static char* pspagename(Document *d, int page) { PSInfo *ps = (PSInfo *) d->extra; return ps->page[page].name; }