| /* |
| * pdf.c |
| * |
| * pdf file support for page |
| */ |
| |
| #include <u.h> |
| #include <libc.h> |
| #include <draw.h> |
| #include <cursor.h> |
| #include <thread.h> |
| #include <bio.h> |
| #include "page.h" |
| |
| static Image* pdfdrawpage(Document *d, int page); |
| static char* pdfpagename(Document*, int); |
| |
| char *pdfprolog = |
| #include "pdfprolog.c" |
| ; |
| |
| Rectangle |
| pdfbbox(GSInfo *gs) |
| { |
| char *p; |
| char *f[4]; |
| Rectangle r; |
| |
| r = Rect(0,0,0,0); |
| waitgs(gs); |
| gscmd(gs, "/CropBox knownoget {} {[0 0 0 0]} ifelse PAGE==\n"); |
| p = Brdline(&gs->gsrd, '\n'); |
| p[Blinelen(&gs->gsrd)-1] ='\0'; |
| if(p[0] != '[') |
| return r; |
| if(tokenize(p+1, f, 4) != 4) |
| return r; |
| r = Rect(atoi(f[0]), atoi(f[1]), atoi(f[2]), atoi(f[3])); |
| waitgs(gs); |
| return r; |
| } |
| |
| Document* |
| initpdf(Biobuf *b, int argc, char **argv, uchar *buf, int nbuf) |
| { |
| Document *d; |
| PDFInfo *pdf; |
| char *p; |
| char *fn; |
| char fdbuf[20]; |
| int fd; |
| int i, npage; |
| Rectangle bbox; |
| |
| if(argc > 1) { |
| fprint(2, "can only view one pdf file at a time\n"); |
| return nil; |
| } |
| |
| fprint(2, "reading through pdf...\n"); |
| if(b == nil){ /* standard input; spool to disk (ouch) */ |
| fd = spooltodisk(buf, nbuf, &fn); |
| sprint(fdbuf, "/dev/fd/%d", fd); |
| b = Bopen(fdbuf, OREAD); |
| if(b == nil){ |
| fprint(2, "cannot open disk spool file\n"); |
| wexits("Bopen temp"); |
| } |
| }else |
| fn = argv[0]; |
| |
| /* sanity check */ |
| Bseek(b, 0, 0); |
| if(!(p = Brdline(b, '\n')) && !(p = Brdline(b, '\r'))) { |
| fprint(2, "cannot find end of first line\n"); |
| wexits("initps"); |
| } |
| if(strncmp(p, "%PDF-", 5) != 0) { |
| werrstr("not pdf"); |
| return nil; |
| } |
| |
| /* setup structures so one free suffices */ |
| p = emalloc(sizeof(*d) + sizeof(*pdf)); |
| d = (Document*) p; |
| p += sizeof(*d); |
| pdf = (PDFInfo*) p; |
| |
| d->extra = pdf; |
| d->b = b; |
| d->drawpage = pdfdrawpage; |
| d->pagename = pdfpagename; |
| d->fwdonly = 0; |
| d->type = Tpdf; |
| |
| if(spawngs(&pdf->gs, "-dDELAYSAFER") < 0) |
| return nil; |
| |
| gscmd(&pdf->gs, "%s", pdfprolog); |
| waitgs(&pdf->gs); |
| |
| setdim(&pdf->gs, Rect(0,0,0,0), ppi, 0); |
| gscmd(&pdf->gs, "(%s) (r) file { DELAYSAFER { .setsafe } if } stopped pop pdfopen begin\n", fn); |
| gscmd(&pdf->gs, "pdfpagecount PAGE==\n"); |
| p = Brdline(&pdf->gs.gsrd, '\n'); |
| if(p == nil) { |
| if(Blinelen(&pdf->gs.gsrd) > 0) { |
| fprint(2, "unexpected output (too long) from gs\n"); |
| return nil; |
| } |
| fprint(2, "early EOF from gs - is ghostscript installed?\n"); |
| return nil; |
| } |
| npage = atoi(p); |
| if(npage < 1) { |
| fprint(2, "no pages?\n"); |
| return nil; |
| } |
| d->npage = npage; |
| d->docname = argv[0]; |
| |
| gscmd(&pdf->gs, "Trailer\n"); |
| bbox = pdfbbox(&pdf->gs); |
| |
| pdf->pagebbox = emalloc(sizeof(Rectangle)*npage); |
| for(i=0; i<npage; i++) { |
| gscmd(&pdf->gs, "%d pdfgetpage\n", i+1); |
| pdf->pagebbox[i] = pdfbbox(&pdf->gs); |
| if(Dx(pdf->pagebbox[i]) <= 0) |
| pdf->pagebbox[i] = bbox; |
| } |
| return d; |
| } |
| |
| static Image* |
| pdfdrawpage(Document *doc, int page) |
| { |
| PDFInfo *pdf = doc->extra; |
| Image *im; |
| |
| gscmd(&pdf->gs, "%d DoPDFPage\n", page+1); |
| im = convert(&pdf->gs.g); |
| if(im == nil) { |
| fprint(2, "fatal: readimage error %r\n"); |
| wexits("readimage"); |
| } |
| waitgs(&pdf->gs); |
| return im; |
| } |
| |
| static char* |
| pdfpagename(Document *d, int page) |
| { |
| static char str[15]; |
| |
| USED(d); |
| sprint(str, "p %d", page+1); |
| return str; |
| } |