123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- /*
- * ps.c
- *
- * provide postscript file reading support for page
- */
- #include <u.h>
- #include <libc.h>
- #include <draw.h>
- #include <event.h>
- #include <bio.h>
- #include <ctype.h>
- #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; i<np; i++)
- page[i] = page[n*i];
- /* trailer */
- page[np] = page[onp];
- /* EOF */
- page[np+1] = page[onp+1];
- ps->npage = 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;
- }
|