URI:
       tps.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tps.c (9072B)
       ---
            1 /*
            2  * ps.c
            3  *
            4  * provide postscript file reading support for page
            5  */
            6 
            7 #include <u.h>
            8 #include <libc.h>
            9 #include <draw.h>
           10 #include <cursor.h>
           11 #include <thread.h>
           12 #include <bio.h>
           13 #include <ctype.h>
           14 #include "page.h"
           15 
           16 static int        pswritepage(Document *d, int fd, int page);
           17 static Image*        psdrawpage(Document *d, int page);
           18 static char*        pspagename(Document*, int);
           19 
           20 #define R(r) (r).min.x, (r).min.y, (r).max.x, (r).max.y
           21 Rectangle
           22 rdbbox(char *p)
           23 {
           24         Rectangle r;
           25         int a;
           26         char *f[4];
           27         while(*p == ':' || *p == ' ' || *p == '\t')
           28                 p++;
           29         if(tokenize(p, f, 4) != 4)
           30                 return Rect(0,0,0,0);
           31         r = Rect(atoi(f[0]), atoi(f[1]), atoi(f[2]), atoi(f[3]));
           32         r = canonrect(r);
           33         if(Dx(r) <= 0 || Dy(r) <= 0)
           34                 return Rect(0,0,0,0);
           35 
           36         if(truetoboundingbox)
           37                 return r;
           38 
           39         /* initdraw not called yet, can't use %R */
           40         if(chatty) fprint(2, "[%d %d %d %d] -> ", R(r));
           41         /*
           42          * attempt to sniff out A4, 8½×11, others
           43          * A4 is 596×842
           44          * 8½×11 is 612×792
           45          */
           46 
           47         a = Dx(r)*Dy(r);
           48         if(a < 300*300){        /* really small, probably supposed to be */
           49                 /* empty */
           50         } else if(Dx(r) <= 596 && r.max.x <= 596 && Dy(r) > 792 && Dy(r) <= 842 && r.max.y <= 842)        /* A4 */
           51                 r = Rect(0, 0, 596, 842);
           52         else {        /* cast up to 8½×11 */
           53                 if(Dx(r) <= 612 && r.max.x <= 612){
           54                         r.min.x = 0;
           55                         r.max.x = 612;
           56                 }
           57                 if(Dy(r) <= 792 && r.max.y <= 792){
           58                         r.min.y = 0;
           59                         r.max.y = 792;
           60                 }
           61         }
           62         if(chatty) fprint(2, "[%d %d %d %d]\n", R(r));
           63         return r;
           64 }
           65 
           66 #define RECT(X) X.min.x, X.min.y, X.max.x, X.max.y
           67 
           68 int
           69 prefix(char *x, char *y)
           70 {
           71         return strncmp(x, y, strlen(y)) == 0;
           72 }
           73 
           74 /*
           75  * document ps is really being printed as n-up pages.
           76  * we need to treat every n pages as 1.
           77  */
           78 void
           79 repaginate(PSInfo *ps, int n)
           80 {
           81         int i, np, onp;
           82         Page *page;
           83 
           84         page = ps->page;
           85         onp = ps->npage;
           86         np = (ps->npage+n-1)/n;
           87 
           88         if(chatty) {
           89                 for(i=0; i<=onp+1; i++)
           90                         print("page %d: %d\n", i, page[i].offset);
           91         }
           92 
           93         for(i=0; i<np; i++)
           94                 page[i] = page[n*i];
           95 
           96         /* trailer */
           97         page[np] = page[onp];
           98 
           99         /* EOF */
          100         page[np+1] = page[onp+1];
          101 
          102         ps->npage = np;
          103 
          104         if(chatty) {
          105                 for(i=0; i<=np+1; i++)
          106                         print("page %d: %d\n", i, page[i].offset);
          107         }
          108 
          109 }
          110 
          111 Document*
          112 initps(Biobuf *b, int argc, char **argv, uchar *buf, int nbuf)
          113 {
          114         Document *d;
          115         PSInfo *ps;
          116         char *p;
          117         char *q, *r;
          118         char eol;
          119         char *nargv[1];
          120         char fdbuf[20];
          121         char tmp[32];
          122         int fd;
          123         int i;
          124         int incomments;
          125         int cantranslate;
          126         int trailer=0;
          127         int nesting=0;
          128         int dumb=0;
          129         int landscape=0;
          130         long psoff;
          131         long npage, mpage;
          132         Page *page;
          133         Rectangle bbox = Rect(0,0,0,0);
          134 
          135         if(argc > 1) {
          136                 fprint(2, "can only view one ps file at a time\n");
          137                 return nil;
          138         }
          139 
          140         fprint(2, "reading through postscript...\n");
          141         if(b == nil){        /* standard input; spool to disk (ouch) */
          142                 fd = spooltodisk(buf, nbuf, nil);
          143                 sprint(fdbuf, "/dev/fd/%d", fd);
          144                 b = Bopen(fdbuf, OREAD);
          145                 if(b == nil){
          146                         fprint(2, "cannot open disk spool file\n");
          147                         wexits("Bopen temp");
          148                 }
          149                 nargv[0] = fdbuf;
          150                 argv = nargv;
          151         }
          152 
          153         /* find %!, perhaps after PCL nonsense */
          154         Bseek(b, 0, 0);
          155         psoff = 0;
          156         eol = 0;
          157         for(i=0; i<16; i++){
          158                 psoff = Boffset(b);
          159                 if(!(p = Brdline(b, eol='\n')) && !(p = Brdline(b, eol='\r'))) {
          160                         fprint(2, "cannot find end of first line\n");
          161                         wexits("initps");
          162                 }
          163                 if(p[0]=='\x1B')
          164                         p++, psoff++;
          165                 if(p[0] == '%' && p[1] == '!')
          166                         break;
          167         }
          168         if(i == 16){
          169                 werrstr("not ps");
          170                 return nil;
          171         }
          172 
          173         /* page counting */
          174         npage = 0;
          175         mpage = 16;
          176         page = emalloc(mpage*sizeof(*page));
          177         memset(page, 0, mpage*sizeof(*page));
          178 
          179         cantranslate = goodps;
          180         incomments = 1;
          181 Keepreading:
          182         while(p = Brdline(b, eol)) {
          183                 if(p[0] == '%')
          184                         if(chatty) fprint(2, "ps %.*s\n", utfnlen(p, Blinelen(b)-1), p);
          185                 if(npage == mpage) {
          186                         mpage *= 2;
          187                         page = erealloc(page, mpage*sizeof(*page));
          188                         memset(&page[npage], 0, npage*sizeof(*page));
          189                 }
          190 
          191                 if(p[0] != '%' || p[1] != '%')
          192                         continue;
          193 
          194                 if(prefix(p, "%%BeginDocument")) {
          195                         nesting++;
          196                         continue;
          197                 }
          198                 if(nesting > 0 && prefix(p, "%%EndDocument")) {
          199                         nesting--;
          200                         continue;
          201                 }
          202                 if(nesting)
          203                         continue;
          204 
          205                 if(prefix(p, "%%EndComment")) {
          206                         incomments = 0;
          207                         continue;
          208                 }
          209                 if(reverse == -1 && prefix(p, "%%PageOrder")) {
          210                         /* glean whether we should reverse the viewing order */
          211                         p[Blinelen(b)-1] = 0;
          212                         if(strstr(p, "Ascend"))
          213                                 reverse = 0;
          214                         else if(strstr(p, "Descend"))
          215                                 reverse = 1;
          216                         else if(strstr(p, "Special"))
          217                                 dumb = 1;
          218                         p[Blinelen(b)-1] = '\n';
          219                         continue;
          220                 } else if(prefix(p, "%%Trailer")) {
          221                         incomments = 1;
          222                         page[npage].offset = Boffset(b)-Blinelen(b);
          223                         trailer = 1;
          224                         continue;
          225                 } else if(incomments && prefix(p, "%%Orientation")) {
          226                         if(strstr(p, "Landscape"))
          227                                 landscape = 1;
          228                 } else if(incomments && Dx(bbox)==0 && prefix(p, q="%%BoundingBox")) {
          229                         bbox = rdbbox(p+strlen(q)+1);
          230                         if(chatty)
          231                                 /* can't use %R because haven't initdraw() */
          232                                 fprint(2, "document bbox [%d %d %d %d]\n",
          233                                         RECT(bbox));
          234                         continue;
          235                 }
          236 
          237                 /*
          238                  * If they use the initgraphics command, we can't play our translation tricks.
          239                  */
          240                 p[Blinelen(b)-1] = 0;
          241                 if((q=strstr(p, "initgraphics")) && ((r=strchr(p, '%'))==nil || r > q))
          242                         cantranslate = 0;
          243                 p[Blinelen(b)-1] = eol;
          244 
          245                 if(!prefix(p, "%%Page:"))
          246                         continue;
          247 
          248                 /*
          249                  * figure out of the %%Page: line contains a page number
          250                  * or some other page description to use in the menu bar.
          251                  *
          252                  * lines look like %%Page: x y or %%Page: x
          253                  * we prefer just x, and will generate our
          254                  * own if necessary.
          255                  */
          256                 p[Blinelen(b)-1] = 0;
          257                 if(chatty) fprint(2, "page %s\n", p);
          258                 r = p+7;
          259                 while(*r == ' ' || *r == '\t')
          260                         r++;
          261                 q = r;
          262                 while(*q && *q != ' ' && *q != '\t')
          263                         q++;
          264                 free(page[npage].name);
          265                 if(*r) {
          266                         if(*r == '"' && *q == '"')
          267                                 r++, q--;
          268                         if(*q)
          269                                 *q = 0;
          270                         page[npage].name = estrdup(r);
          271                         *q = 'x';
          272                 } else {
          273                         snprint(tmp, sizeof tmp, "p %ld", npage+1);
          274                         page[npage].name = estrdup(tmp);
          275                 }
          276 
          277                 /*
          278                  * store the offset info for later viewing
          279                  */
          280                 trailer = 0;
          281                 p[Blinelen(b)-1] = eol;
          282                 page[npage++].offset = Boffset(b)-Blinelen(b);
          283         }
          284         if(Blinelen(b) > 0){
          285                 fprint(2, "page: linelen %d\n", Blinelen(b));
          286                 Bseek(b, Blinelen(b), 1);
          287                 goto Keepreading;
          288         }
          289 
          290         if(Dx(bbox) == 0 || Dy(bbox) == 0)
          291                 bbox = Rect(0,0,612,792);        /* 8½×11 */
          292         /*
          293          * if we didn't find any pages, assume the document
          294          * is one big page
          295          */
          296         if(npage == 0) {
          297                 dumb = 1;
          298                 if(chatty) fprint(2, "don't know where pages are\n");
          299                 reverse = 0;
          300                 goodps = 0;
          301                 trailer = 0;
          302                 page[npage].name = "p 1";
          303                 page[npage++].offset = 0;
          304         }
          305 
          306         if(npage+2 > mpage) {
          307                 mpage += 2;
          308                 page = erealloc(page, mpage*sizeof(*page));
          309                 memset(&page[mpage-2], 0, 2*sizeof(*page));
          310         }
          311 
          312         if(!trailer)
          313                 page[npage].offset = Boffset(b);
          314 
          315         Bseek(b, 0, 2); /* EOF */
          316         page[npage+1].offset = Boffset(b);
          317 
          318         d = emalloc(sizeof(*d));
          319         ps = emalloc(sizeof(*ps));
          320         ps->page = page;
          321         ps->npage = npage;
          322         ps->bbox = bbox;
          323         ps->psoff = psoff;
          324 
          325         d->extra = ps;
          326         d->npage = ps->npage;
          327         d->b = b;
          328         d->drawpage = psdrawpage;
          329         d->pagename = pspagename;
          330 
          331         d->fwdonly = ps->clueless = dumb;
          332         d->docname = argv[0];
          333         /*
          334          * "tag" the doc as an image for now since there still is the "blank page"
          335          * problem for ps files.
          336          */
          337         d->type = Tgfx;
          338 
          339         if(spawngs(&ps->gs, "-dSAFER") < 0)
          340                 return nil;
          341 
          342         if(!cantranslate)
          343                 bbox.min = ZP;
          344         setdim(&ps->gs, bbox, ppi, landscape);
          345 
          346         if(goodps){
          347                 /*
          348                  * We want to only send the page (i.e. not header and trailer) information
          349                   * for each page, so initialize the device by sending the header now.
          350                  */
          351                 pswritepage(d, ps->gs.gsfd, -1);
          352                 waitgs(&ps->gs);
          353         }
          354 
          355         if(dumb) {
          356                 fprint(ps->gs.gsfd, "(%s) run PAGEFLUSH\n", argv[0]);
          357                 fprint(ps->gs.gsfd, "(/dev/fd/3) (w) file dup (THIS IS NOT A PLAN9 BITMAP 01234567890123456789012345678901234567890123456789\\n) writestring flushfile\n");
          358                 close(ps->gs.gsfd);
          359         }
          360 
          361         ps->bbox = bbox;
          362 
          363         return d;
          364 }
          365 
          366 static int
          367 pswritepage(Document *d, int fd, int page)
          368 {
          369         Biobuf *b = d->b;
          370         PSInfo *ps = d->extra;
          371         int t, n, i;
          372         long begin, end;
          373         char buf[8192];
          374 
          375         if(page == -1)
          376                 begin = ps->psoff;
          377         else
          378                 begin = ps->page[page].offset;
          379 
          380         end = ps->page[page+1].offset;
          381 
          382         if(chatty) {
          383                 fprint(2, "writepage(%d)... from #%ld to #%ld...\n",
          384                         page, begin, end);
          385         }
          386         Bseek(b, begin, 0);
          387 
          388         t = end-begin;
          389         n = sizeof(buf);
          390         if(n > t) n = t;
          391         while(t > 0 && (i=Bread(b, buf, n)) > 0) {
          392                 if(write(fd, buf, i) != i)
          393                         return -1;
          394                 t -= i;
          395                 if(n > t)
          396                         n = t;
          397         }
          398         return end-begin;
          399 }
          400 
          401 static Image*
          402 psdrawpage(Document *d, int page)
          403 {
          404         PSInfo *ps = d->extra;
          405         Image *im;
          406 
          407         if(ps->clueless)
          408                 return convert(&ps->gs.g);
          409 
          410         waitgs(&ps->gs);
          411 
          412         if(goodps)
          413                 pswritepage(d, ps->gs.gsfd, page);
          414         else {
          415                 pswritepage(d, ps->gs.gsfd, -1);
          416                 pswritepage(d, ps->gs.gsfd, page);
          417                 pswritepage(d, ps->gs.gsfd, d->npage);
          418         }
          419         /*
          420          * If last line terminator is \r, gs will read ahead to check for \n
          421          * so send one to avoid deadlock.
          422          */
          423         write(ps->gs.gsfd, "\n", 1);
          424         fprint(ps->gs.gsfd, "\nPAGEFLUSH\n");
          425         im = convert(&ps->gs.g);
          426         if(im == nil) {
          427                 fprint(2, "fatal: readimage error %r\n");
          428                 wexits("readimage");
          429         }
          430         waitgs(&ps->gs);
          431 
          432         return im;
          433 }
          434 
          435 static char*
          436 pspagename(Document *d, int page)
          437 {
          438         PSInfo *ps = (PSInfo *) d->extra;
          439         return ps->page[page].name;
          440 }