#include #include #include #include #include #include #include #include extern int ximage_init(Display *display, int screen, Visual *visual); extern int ximage_get_depth(void); extern Visual *ximage_get_visual(void); extern Colormap ximage_get_colormap(void); extern void ximage_blit(Drawable d, GC gc, int dstx, int dsty, unsigned char *srcdata, int srcx, int srcy, int srcw, int srch, int srcstride); static Display *xdpy; static int xscr; static Window xwin; static GC xgc; static XEvent xevt; static int mapped = 0; static Cursor xcarrow, xchand, xcwait; static char *doctitle = ""; static float zoom = 1.0; static int rotate = 0; static int pageno = 1; static int count = 0; static pdf_page *page = nil; static fz_obj *pageobj = nil; static int hist[256]; static int histlen = 0; /* for 123G commands */ static unsigned char pagebuf[256]; static int pagebufidx = 0; static pdf_xref *xref; static pdf_pagetree *pages; static pdf_outline *outline; static fz_renderer *rast; static fz_pixmap *image; void usage() { fprintf(stderr, "usage: x11pdf [-b] [-pzr page/zoom/rotate] [-u password] file.pdf\n"); fprintf(stderr, "\n" ); exit(1); } /* * X11 magic */ static void xopen(void) { xdpy = XOpenDisplay(nil); assert(xdpy != nil); xscr = DefaultScreen(xdpy); ximage_init(xdpy, xscr, DefaultVisual(xdpy, xscr)); xcarrow = XCreateFontCursor(xdpy, XC_left_ptr); xchand = XCreateFontCursor(xdpy, XC_hand2); xcwait = XCreateFontCursor(xdpy, XC_watch); xwin = XCreateWindow(xdpy, DefaultRootWindow(xdpy), 10, 10, 200, 100, 1, ximage_get_depth(), InputOutput, ximage_get_visual(), 0, nil); XSetWindowColormap(xdpy, xwin, ximage_get_colormap()); XSelectInput(xdpy, xwin, StructureNotifyMask | ExposureMask | KeyPressMask | PointerMotionMask | ButtonPressMask); mapped = 0; xgc = XCreateGC(xdpy, xwin, 0, nil); } static void xresize(void) { XWindowChanges values; int mask; mask = CWWidth | CWHeight; values.width = image->w; values.height = image->h; XConfigureWindow(xdpy, xwin, mask, &values); if (!mapped) { XMapWindow(xdpy, xwin); XFlush(xdpy); while (1) { XNextEvent(xdpy, &xevt); if (xevt.type == MapNotify) break; } XSetForeground(xdpy, xgc, WhitePixel(xdpy, xscr)); XFillRectangle(xdpy, xwin, xgc, 0, 0, image->w, image->h); XFlush(xdpy); mapped = 1; } } static void xblit(void) { ximage_blit(xwin, xgc, 0, 0, image->samples, 0, 0, image->w, image->h, image->w * image->n); } static void xtitle(char *s) { XmbSetWMProperties(xdpy, xwin, s, s, 0, 0, 0, 0, 0); } static void showpage(void) { fz_error *error; fz_matrix ctm; fz_rect bbox; fz_obj *obj; char s[256]; assert(pageno > 0 && pageno <= pdf_getpagecount(pages)); XDefineCursor(xdpy, xwin, xcwait); XFlush(xdpy); if (image) fz_droppixmap(image); image = nil; obj = pdf_getpageobject(pages, pageno - 1); if (obj == pageobj) goto Lskipload; pageobj = obj; if (page) pdf_droppage(page); sprintf(s, "Loading page %d", pageno); XSetForeground(xdpy, xgc, BlackPixel(xdpy, xscr)); XDrawString(xdpy, xwin, xgc, 10, 20, s, strlen(s)); XFlush(xdpy); error = pdf_loadpage(&page, xref, pageobj); if (error) fz_abort(error); Lskipload: sprintf(s, "Rendering..."); XSetForeground(xdpy, xgc, BlackPixel(xdpy, xscr)); XDrawString(xdpy, xwin, xgc, 10, 30, s, strlen(s)); XFlush(xdpy); ctm = fz_identity(); ctm = fz_concat(ctm, fz_translate(0, -page->mediabox.max.y)); ctm = fz_concat(ctm, fz_scale(zoom, -zoom)); ctm = fz_concat(ctm, fz_rotate(rotate + page->rotate)); bbox = fz_transformaabb(ctm, page->mediabox); error = fz_rendertree(&image, rast, page->tree, ctm, fz_roundrect(bbox), 1); if (error) fz_abort(error); XDefineCursor(xdpy, xwin, xcarrow); XFlush(xdpy); { char buf[512]; sprintf(buf, "%s - %d/%d", doctitle, pageno, count); xtitle(buf); } xresize(); xblit(); } static void pdfopen(char *filename, char *password) { fz_error *error; fz_obj *obj; error = pdf_newxref(&xref); if (error) fz_abort(error); error = pdf_loadxref(xref, filename); if (error) { fz_warn(error->msg); printf("trying to repair...\n"); error = pdf_repairxref(xref, filename); if (error) fz_abort(error); } error = pdf_decryptxref(xref); if (error) fz_abort(error); if (xref->crypt) { error = pdf_setpassword(xref->crypt, password); if (error) fz_abort(error); } obj = fz_dictgets(xref->trailer, "Root"); if (!obj) fz_abort(fz_throw("syntaxerror: missing Root object")); error = pdf_loadindirect(&xref->root, xref, obj); if (error) fz_abort(error); obj = fz_dictgets(xref->trailer, "Info"); if (obj) { error = pdf_loadindirect(&xref->info, xref, obj); if (error) fz_abort(error); } error = pdf_loadnametrees(xref); if (error) fz_abort(error); error = pdf_loadoutline(&outline, xref); if (error) fz_abort(error); doctitle = filename; if (xref->info) { obj = fz_dictgets(xref->info, "Title"); if (obj) { error = pdf_toutf8(&doctitle, obj); if (error) fz_abort(error); } } if (outline) pdf_debugoutline(outline, 0); error = pdf_loadpagetree(&pages, xref); if (error) fz_abort(error); count = pdf_getpagecount(pages); error = fz_newrenderer(&rast, pdf_devicergb, 0, 1024 * 512); if (error) fz_abort(error); image = nil; } static void dumptext() { fz_error *error; pdf_textline *line; printf("----"); error = pdf_loadtextfromtree(&line, page->tree); if (error) fz_abort(error); pdf_debugtextline(line); pdf_droptextline(line); } static void gotouri(fz_obj *uri) { char cmd[2048]; char buf[2048]; printf("goto uri: "); fz_debugobj(uri); printf("\n"); memcpy(buf, fz_tostrbuf(uri), fz_tostrlen(uri)); buf[fz_tostrlen(uri)] = 0; if (getenv("BROWSER")) sprintf(cmd, "$BROWSER %s &", buf); else #ifdef WIN32 sprintf(cmd, "start %s", buf); #else sprintf(cmd, "open %s", buf); #endif system(cmd); } static void gotopage(fz_obj *obj) { int oid = fz_tonum(obj); int gen = fz_togen(obj); int i; printf("goto page: %d %d R\n", oid, gen); for (i = 0; i < count; i++) { if (fz_tonum(pages->pref[i]) == oid) { if (histlen + 1 == 256) { memmove(hist, hist + 1, sizeof(int) * 255); histlen --; } hist[histlen++] = pageno; pageno = i + 1; showpage(); return; } } } static void drawlinks(void) { pdf_link *link; fz_matrix ctm; fz_point a, b, c, d; fz_rect r; ctm = fz_identity(); ctm = fz_concat(ctm, fz_translate(0, -page->mediabox.max.y)); ctm = fz_concat(ctm, fz_scale(zoom, -zoom)); ctm = fz_concat(ctm, fz_rotate(rotate + page->rotate)); ctm = fz_concat(ctm, fz_translate(-image->x, -image->y)); for (link = page->links; link; link = link->next) { r = link->rect; a.x = r.min.x; a.y = r.min.y; b.x = r.min.x; b.y = r.max.y; c.x = r.max.x; c.y = r.max.y; d.x = r.max.x; d.y = r.min.y; a = fz_transformpoint(ctm, a); b = fz_transformpoint(ctm, b); c = fz_transformpoint(ctm, c); d = fz_transformpoint(ctm, d); XDrawLine(xdpy, xwin, xgc, a.x, a.y, b.x, b.y); XDrawLine(xdpy, xwin, xgc, b.x, b.y, c.x, c.y); XDrawLine(xdpy, xwin, xgc, c.x, c.y, d.x, d.y); XDrawLine(xdpy, xwin, xgc, d.x, d.y, a.x, a.y); } XFlush(xdpy); } static void handlekey(int c) { int oldpage = pageno; float oldzoom = zoom; int oldrotate = rotate; if (c >= '0' && c <= '9') pagebuf[pagebufidx++] = c; else if (c != 'g' && c != 'G') pagebufidx = 0; switch (c) { case 'd': fz_debugglyphcache(rast->cache); break; case 'a': rotate -= 5; break; case 's': rotate += 5; break; case 'x': dumptext(); break; case 'o': drawlinks(); break; case '\b': case 'b': pageno--; if (pageno < 1) pageno = 1; break; case 'B': pageno -= 10; if (pageno < 1) pageno = 1; break; case ' ': case 'f': pageno++; if (pageno > count) pageno = count; break; case 'F': pageno += 10; if (pageno > count) pageno = count; break; case 'm': if (histlen + 1 == 256) { memmove(hist, hist + 1, sizeof(int) * 255); histlen --; } hist[histlen++] = pageno; break; case 't': case 'T': if (histlen > 0) pageno = hist[--histlen]; break; case '-': zoom -= 0.1; if (zoom < 0.1) zoom = 0.1; break; case '+': zoom += 0.1; if (zoom > 3.0) zoom = 3.0; break; case '<': rotate -= 90; break; case '>': rotate += 90; break; case 'q': exit(0); case 'g': case 'G': if (pagebufidx > 0) { pagebuf[pagebufidx] = '\0'; pageno = atoi(pagebuf); pagebufidx = 0; if (pageno < 1) pageno = 1; if (pageno > count) pageno = count; } else { if (c == 'G') { pageno = count; } } break; } if (pageno != oldpage || zoom != oldzoom || rotate != oldrotate) showpage(); } static void handlemouse(float x, int y, int btn) { pdf_link *link; fz_matrix ctm; fz_point p; p.x = x + image->x; p.y = y + image->y; ctm = fz_identity(); ctm = fz_concat(ctm, fz_translate(0, -page->mediabox.max.y)); ctm = fz_concat(ctm, fz_scale(zoom, -zoom)); ctm = fz_concat(ctm, fz_rotate(rotate + page->rotate)); ctm = fz_invertmatrix(ctm); p = fz_transformpoint(ctm, p); for (link = page->links; link; link = link->next) { if (p.x >= link->rect.min.x && p.x <= link->rect.max.x) if (p.y >= link->rect.min.y && p.y <= link->rect.max.y) break; } if (link) { XDefineCursor(xdpy, xwin, xchand); if (btn) { if (fz_isstring(link->dest)) gotouri(link->dest); if (fz_isindirect(link->dest)) gotopage(link->dest); } } else { XDefineCursor(xdpy, xwin, xcarrow); } } int main(int argc, char **argv) { char *filename; int c; int benchmark = 0; char *password = ""; while ((c = getopt(argc, argv, "bz:r:p:u:")) != -1) { switch (c) { case 'b': ++benchmark; break; case 'u': password = optarg; break; case 'p': pageno = atoi(optarg); break; case 'z': zoom = atof(optarg); break; case 'r': rotate = atoi(optarg); break; default: usage(); } } if (argc - optind == 0) usage(); fz_cpudetect(); fz_accelerate(); filename = argv[optind++]; xopen(); pdfopen(filename, password); showpage(); if (benchmark) { while (pageno < count) { pageno ++; showpage(); } return 0; } while (1) { int len; unsigned char buf[128]; KeySym keysym; XNextEvent(xdpy, &xevt); switch (xevt.type) { case Expose: if (xevt.xexpose.count == 0) xblit(); break; case KeyPress: len = XLookupString(&xevt.xkey, buf, sizeof buf, &keysym, 0); if (len) handlekey(buf[0]); break; case MotionNotify: handlemouse(xevt.xbutton.x, xevt.xbutton.y, 0); break; case ButtonPress: handlemouse(xevt.xbutton.x, xevt.xbutton.y, 1); break; } } pdf_closexref(xref); return 0; }