diff options
author | Tor Andersson <tor@ghostscript.com> | 2004-11-30 06:52:27 +0100 |
---|---|---|
committer | Tor Andersson <tor@ghostscript.com> | 2004-11-30 06:52:27 +0100 |
commit | 438b50b77b7d5473fe89f0bf98a79243d84d4173 (patch) | |
tree | 68f0ba50e84513a40ade3d8218f96fbef26d851d | |
parent | 951f2e23a5ec055c358ea8b6b542d9fd1a4668b0 (diff) | |
download | mupdf-438b50b77b7d5473fe89f0bf98a79243d84d4173.tar.xz |
gtk+ viewer
-rw-r--r-- | Jamfile | 6 | ||||
-rw-r--r-- | test/gtkpdf.c | 560 |
2 files changed, 566 insertions, 0 deletions
@@ -203,6 +203,11 @@ Main pdffunction : test/pdffunction.c ; Main x11pdf : test/x11pdf.c test/ximage.c ; +CCFLAGS += `gtk-config --cflags` ; +LINKLIBS on gtkpdf = $(LINKLIBS) `gtk-config --libs` -lgthread ; + +Main gtkpdf : test/gtkpdf.c ; + LinkLibraries pdfclean pdfdebug @@ -210,5 +215,6 @@ LinkLibraries pdfrip pdffunction x11pdf + gtkpdf : libmupdf libfonts libfitz ; diff --git a/test/gtkpdf.c b/test/gtkpdf.c new file mode 100644 index 00000000..84596702 --- /dev/null +++ b/test/gtkpdf.c @@ -0,0 +1,560 @@ +/* + +TODO: + - threaded pdf/page loading + - info dialog + - resource dialog + - password dialog + - outline tree + - magnifying glass + - text selection + - text search + +*/ + +#include <fitz.h> +#include <mupdf.h> + +#include <gtk/gtk.h> +#include <glib.h> +#include <pthread.h> + +typedef struct PDFApp PDFApp; + +struct PDFApp +{ + GtkWidget *canvas; + GtkWidget *status; + GtkWidget *scroll; + int statusid; + + char *filename; + int pageno; + int rotate; + float zoom; + + fz_renderer *gc; + pdf_xref *xref; + pdf_pagetree *pagetree; + fz_obj *pageobj; + pdf_page *page; + fz_pixmap *image; +}; + +static volatile int busy = 0; + +PDFApp *gapp; + +static void showstatus(char *fmt, ...) +{ + char msg[256]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(msg, 256, fmt, ap); + va_end(ap); + + gtk_statusbar_pop(GTK_STATUSBAR(gapp->status), gapp->statusid); + gtk_statusbar_push(GTK_STATUSBAR(gapp->status), gapp->statusid, msg); +} + +static void panic(fz_error *error) +{ + gapp->filename = ""; + gapp->pageno = 1; + gapp->rotate = 0; + gapp->zoom = 1.0; + + gapp->gc = nil; + gapp->xref = nil; + gapp->pagetree = nil; + gapp->page = nil; + gapp->image = nil; + + fz_abort(error); +} + +static void forkwork(void*(*func)(void*)) +{ + pthread_t tid; + if (busy) return; + pthread_create(&tid, NULL, func, nil); +} + +static void* drawpage(void*args) +{ + char msg[256]; + fz_error *error; + fz_matrix ctm; + fz_rect bbox; + fz_obj *obj; + + if (!gapp->xref) + return nil; + + busy = 1; + + while (gapp->rotate < 0) + gapp->rotate += 360; + gapp->rotate = gapp->rotate % 360; + + obj = pdf_getpageobject(gapp->pagetree, gapp->pageno - 1); + if (obj == gapp->pageobj) + goto Lskipload; + gapp->pageobj = obj; + + if (gapp->page) + pdf_droppage(gapp->page); + + gdk_threads_enter(); + sprintf(msg, " loading page %d ... ", gapp->pageno); + showstatus(msg); + gdk_threads_leave(); + + error = pdf_loadpage(&gapp->page, gapp->xref, gapp->pageobj); + if (error) + panic(error); + +Lskipload: + + gdk_threads_enter(); + showstatus(" drawing ... "); + gdk_threads_leave(); + + if (gapp->image) + fz_droppixmap(gapp->image); + gapp->image = nil; + + ctm = fz_identity(); + ctm = fz_concat(ctm, fz_translate(0, -gapp->page->mediabox.max.y)); + ctm = fz_concat(ctm, fz_scale(gapp->zoom, -gapp->zoom)); + ctm = fz_concat(ctm, fz_rotate(gapp->rotate + gapp->page->rotate)); + + bbox = fz_transformaabb(ctm, gapp->page->mediabox); + + error = fz_rendertree(&gapp->image, gapp->gc, gapp->page->tree, + ctm, fz_roundrect(bbox), 1); + if (error) + panic(error); + + gdk_threads_enter(); + sprintf(msg, " page %d of %d zoom %g rotate %d ", + gapp->pageno, pdf_getpagecount(gapp->pagetree), + gapp->zoom, gapp->rotate); + showstatus(msg); + gtk_widget_set_usize(gapp->canvas, gapp->image->w, gapp->image->h); + gtk_widget_queue_draw(gapp->canvas); + gdk_threads_leave(); + + busy = 0; + + return nil; +} + +static void* openpdf(void*args) +{ + fz_error *error; + char msg[256]; + + busy = 1; + + gdk_threads_enter(); + sprintf(msg, " Loading %s...", gapp->filename); + showstatus(msg); + gdk_threads_leave(); + + if (gapp->xref) + { + pdf_droppage(gapp->page); + pdf_droppagetree(gapp->pagetree); + pdf_closepdf(gapp->xref); + gapp->page = nil; + gapp->pagetree = nil; + gapp->xref = nil; + } + + error = pdf_openpdf(&gapp->xref, gapp->filename); + if (error) panic(error); + + error = pdf_decryptpdf(gapp->xref); + if (error) panic(error); + + /* TODO: ask for password */ + if (gapp->xref->crypt) + { + error = pdf_setpassword(gapp->xref->crypt, ""); + if (error) panic(error); + } + + error = pdf_loadpagetree(&gapp->pagetree, gapp->xref); + if (error) panic(error); + + gdk_threads_enter(); + showstatus(""); + gdk_threads_leave(); + + gapp->pageno = 1; + drawpage(nil); + + busy = 0; + + return nil; +} + +/* + * Handle event callbacks + */ + +static void onquit(GtkWidget *widget, void *data) +{ + exit(0); +} + +static void onopenokay(GtkWidget *w, GtkFileSelection *fs) +{ + gapp->filename = strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs))); + gtk_widget_destroy(GTK_WIDGET(fs)); + forkwork(openpdf); +} + +static void onopen(GtkWidget *widget, void *data) +{ + GtkWidget *filew; + if (busy) return; + filew = gtk_file_selection_new ("Open PDF file"); + gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(filew)->ok_button), + "clicked", (GtkSignalFunc)onopenokay, filew); + gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(filew)->cancel_button), + "clicked", (GtkSignalFunc)gtk_widget_destroy, GTK_OBJECT(filew)); + gtk_widget_show(filew); +} + +static void oninfo(GtkWidget *widget, void *data) +{ +} + +static void onback10(GtkWidget *widget, void *data) +{ + if (busy) return; + if (!gapp->xref) return; + gapp->pageno -= 10; + if (gapp->pageno < 1) + gapp->pageno = 1; + forkwork(drawpage); +} + +static void onnext10(GtkWidget *widget, void *data) +{ + if (busy) return; + if (!gapp->xref) return; + gapp->pageno += 10; + if (gapp->pageno > pdf_getpagecount(gapp->pagetree)) + gapp->pageno = pdf_getpagecount(gapp->pagetree); + forkwork(drawpage); +} + +static void onback(GtkWidget *widget, void *data) +{ + if (busy) return; + if (!gapp->xref) return; + if (gapp->pageno > 1) + { + gapp->pageno --; + forkwork(drawpage); + } +} + +static void onnext(GtkWidget *widget, void *data) +{ + if (busy) return; + if (!gapp->xref) return; + if (gapp->pageno < pdf_getpagecount(gapp->pagetree)) + { + gapp->pageno ++; + forkwork(drawpage); + } +} + +static void onfirst(GtkWidget *widget, void *data) +{ + if (busy) return; + if (!gapp->xref) return; + gapp->pageno = 1; + forkwork(drawpage); +} + +static void onlast(GtkWidget *widget, void *data) +{ + if (busy) return; + if (!gapp->xref) return; + gapp->pageno = pdf_getpagecount(gapp->pagetree); + forkwork(drawpage); +} + +static void onzoomin(GtkWidget *widget, void *data) { gapp->zoom *= 1.25; forkwork(drawpage); } +static void onzoomout(GtkWidget *widget, void *data) { gapp->zoom *= 0.8; forkwork(drawpage); } +static void onzoom100(GtkWidget *widget, void *data) { gapp->zoom = 1.0; forkwork(drawpage); } + +static void onrotl(GtkWidget *widget, void *data) { gapp->rotate -= 90; forkwork(drawpage); } +static void onrotr(GtkWidget *widget, void *data) { gapp->rotate += 90; forkwork(drawpage); } + +static void onfitwidth(GtkWidget *widget, void *data) +{ +} + +static void onfitpage(GtkWidget *widget, void *data) +{ +} + +static void mousedown(GtkWidget *widget, GdkEventMotion *event, void *data) +{ + gtk_widget_grab_focus(gapp->scroll); +} + +static void mousemove(GtkWidget *widget, GdkEventMotion *event, void *data) +{ +} + +static void mouseup(GtkWidget *widget, GdkEventMotion *event, void *data) +{ +} + +static void keypress(GtkWidget *widget, GdkEventKey *event, void *data) +{ + if (busy) return; + switch (event->string[0]) + { + case 'q': onquit(widget, data); break; + case 'g': onlast(widget, data); break; + case '0': onfirst(widget, data); break; + case 'b': onback(widget, data); break; + case 'B': onback10(widget, data); break; + case ' ': onnext(widget, data); break; + case 'f': onnext(widget, data); break; + case 'F': onnext10(widget, data); break; + case '+': onzoomin(widget, data); break; + case '-': onzoomout(widget, data); break; + case '1': onzoom100(widget, data); break; + case 'l': onrotl(widget, data); break; + case 'r': onrotr(widget, data); break; + case 'L': gapp->rotate -= 15; forkwork(drawpage); break; + case 'R': gapp->rotate += 15; forkwork(drawpage); break; + } +} + +static void onexpose(GtkWidget *widget, GdkEventExpose *event, void *data) +{ + PDFApp *app = data; + if (app->image) + { + int x0 = event->area.x; + int y0 = event->area.y; + int w = event->area.width; + int h = event->area.height; + int x1, y1; + int x, y; + + unsigned char *rgb = fz_malloc(w * h * 3); + unsigned char *s, *d; + + x1 = MIN(x0 + w, app->image->w); + y1 = MIN(y0 + h, app->image->h); + + if (x0 + w > x1 || y0 + h > y1) + memset(rgb, 200, w * h * 3); + + for (y = y0; y < y1; y++) + { + s = app->image->samples + (y * app->image->w + x0) * 4; + d = rgb + (y - y0) * w * 3; + for (x = x0; x < x1; x++) + { + d[0] = s[1]; + d[1] = s[2]; + d[2] = s[3]; + s += 4; + d += 3; + } + } + + gdk_draw_rgb_image(widget->window, + widget->style->black_gc, + x0, y0, w, h, + GDK_RGB_DITHER_NONE, rgb, + w * 3); + + fz_free(rgb); + } +} + +/* + * Construct widgets + */ + +static GtkWidget * +addmenuitem(GtkWidget *menu, const char *name, GtkSignalFunc callback, + void *callback_data, GtkAccelGroup *ag, const char *accel) +{ + GtkWidget *menuitem; + menuitem = gtk_menu_item_new_with_label(name); + gtk_menu_append(GTK_MENU(menu), menuitem); + gtk_widget_show(menuitem); + if (accel != NULL) + { + guint accel_key, accel_mods; + gtk_accelerator_parse(accel, &accel_key, &accel_mods); + gtk_widget_add_accelerator(menuitem, "activate", ag, + accel_key, accel_mods, GTK_ACCEL_VISIBLE); + } + gtk_signal_connect(GTK_OBJECT(menuitem), "activate", + (GtkSignalFunc)callback, callback_data); + return menuitem; +} + +static void +addmenuseparator(GtkWidget *menu) +{ + GtkWidget *menuitem; + menuitem = gtk_menu_item_new(); + gtk_menu_append(GTK_MENU(menu), menuitem); + gtk_widget_show(menuitem); +} + +void makeapp(PDFApp *app) +{ + GtkWidget *frame; + GtkWidget *menubar; + GtkWidget *menu; + GtkWidget *menuitem; + GtkWidget *da; + GtkWidget *sb; + GtkWidget *sv; + GtkWidget *vbox; + GtkAccelGroup *ag; + void *data = app; + + frame = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_signal_connect(GTK_OBJECT(frame), "destroy", (GtkSignalFunc)onquit, "WM destroy"); + gtk_window_set_title(GTK_WINDOW(frame), "MuPDF"); + gtk_widget_set_usize(GTK_WIDGET(frame), 300, 200); + + vbox = gtk_vbox_new(FALSE, 1); + gtk_container_add(GTK_CONTAINER(frame), vbox); + gtk_widget_show(vbox); + + menubar = gtk_menu_bar_new(); + ag = gtk_accel_group_new(); + gtk_window_add_accel_group(GTK_WINDOW(frame), ag); + gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0); + gtk_widget_show(menubar); + + menu = gtk_menu_new(); + menuitem = gtk_menu_item_new_with_label("File"); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu); + gtk_menu_bar_append(GTK_MENU_BAR(menubar), menuitem); + gtk_widget_show(menuitem); + gtk_menu_set_accel_group(GTK_MENU(menu), ag); + addmenuitem(menu, "Info...", (GtkSignalFunc)oninfo, data, ag, "<ctrl>I"); + addmenuitem(menu, "Open...", (GtkSignalFunc)onopen, data, ag, "<ctrl>O"); + addmenuseparator(menu); + addmenuitem(menu, "Quit", (GtkSignalFunc)onquit, data, ag, "<ctrl>Q"); + + menu = gtk_menu_new(); + menuitem = gtk_menu_item_new_with_label("Go"); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu); + gtk_menu_bar_append(GTK_MENU_BAR(menubar), menuitem); + gtk_widget_show(menuitem); + gtk_menu_set_accel_group(GTK_MENU(menu), ag); + addmenuitem(menu, "Back", (GtkSignalFunc)onback10, data, ag, "<ctrl>B"); + addmenuitem(menu, "Next", (GtkSignalFunc)onnext10, data, ag, "<ctrl>F"); + addmenuitem(menu, "Back 10", (GtkSignalFunc)onback10, data, ag, nil); + addmenuitem(menu, "Next 10", (GtkSignalFunc)onnext10, data, ag, nil); + addmenuseparator(menu); + addmenuitem(menu, "First", (GtkSignalFunc)onfirst, data, ag, nil); + addmenuitem(menu, "Last", (GtkSignalFunc)onlast, data, ag, "<ctrl>G"); + + menu = gtk_menu_new(); + menuitem = gtk_menu_item_new_with_label("View"); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu); + gtk_menu_bar_append(GTK_MENU_BAR(menubar), menuitem); + gtk_widget_show(menuitem); + gtk_menu_set_accel_group(GTK_MENU(menu), ag); + addmenuitem(menu, "Zoom in", (GtkSignalFunc)onzoomin, data, ag, "<ctrl>'+'"); + addmenuitem(menu, "Zoom out", (GtkSignalFunc)onzoomout, data, ag, "<ctrl>'-'"); + addmenuitem(menu, "Zoom 100%", (GtkSignalFunc)onzoom100, data, ag, "<ctrl>1"); + addmenuitem(menu, "Fit width", (GtkSignalFunc)onfitwidth, data, ag, nil); + addmenuitem(menu, "Fit page", (GtkSignalFunc)onfitpage, data, ag, nil); + addmenuseparator(menu); + addmenuitem(menu, "Rotate left", (GtkSignalFunc)onrotl, data, ag, "<ctrl>L"); + addmenuitem(menu, "Rotate right", (GtkSignalFunc)onrotr, data, ag, "<ctrl>R"); + + sv = gtk_scrolled_window_new(NULL, NULL); + app->scroll = sv; + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sv), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + GTK_WIDGET_SET_FLAGS(sv, GTK_CAN_FOCUS); + gtk_widget_set_extension_events(sv, GDK_EXTENSION_EVENTS_ALL); + gtk_signal_connect(GTK_OBJECT(sv), "button_press_event", (GtkSignalFunc)mousedown, data); + gtk_signal_connect(GTK_OBJECT(sv), "motion_notify_event", (GtkSignalFunc)mousemove, data); + gtk_signal_connect(GTK_OBJECT(sv), "button_release_event", (GtkSignalFunc)mouseup, data); + gtk_signal_connect(GTK_OBJECT(sv), "key_press_event", (GtkSignalFunc)keypress, data); + gtk_box_pack_start(GTK_BOX(vbox), sv, TRUE, TRUE, 0); + gtk_widget_show(sv); + + da = gtk_drawing_area_new(); + app->canvas = da; + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sv), da); + gtk_signal_connect(GTK_OBJECT(da), "expose_event", (GtkSignalFunc)onexpose, data); + gtk_widget_show(da); + + sb = gtk_statusbar_new(); + app->status = sb; + app->statusid = gtk_statusbar_get_context_id(GTK_STATUSBAR(sb), "mupdf"); + gtk_box_pack_start(GTK_BOX(vbox), sb, FALSE, FALSE, 0); + gtk_widget_show(sb); + + gtk_window_set_default_size(GTK_WINDOW(frame), 512, 512); + gtk_widget_grab_focus(sv); + gtk_widget_show(frame); +} + +int main(int argc, char **argv) +{ + fz_error *error; + PDFApp theapp; + + g_thread_init(NULL); + gtk_init(&argc, &argv); + gtk_widget_set_default_colormap(gdk_rgb_get_cmap()); + gtk_widget_set_default_visual(gdk_rgb_get_visual()); + + makeapp(&theapp); + gapp = &theapp; + + gapp->pageno = 1; + gapp->rotate = 0; + gapp->zoom = 1.0; + + gapp->image = nil; + gapp->xref = nil; + gapp->pagetree = nil; + gapp->page = nil; + + fz_cpudetect(); + fz_accelerate(); + + error = fz_newrenderer(&gapp->gc, pdf_devicergb, 0, 1024 * 512); + if (error) + fz_abort(error); + + if (argc > 1) + { + gapp->filename = argv[1]; + forkwork(openpdf); + } + + gdk_threads_enter(); + gtk_main(); + gdk_threads_leave(); + return 0; +} + |