diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/gtkpdf.c | 670 | ||||
-rw-r--r-- | apps/macpdf.app/Contents/Info.plist | 24 | ||||
-rw-r--r-- | apps/macpdf.app/Contents/PkgInfo | 1 | ||||
-rw-r--r-- | apps/macpdf.app/Contents/Resources/English.lproj/main.nib/classes.nib | 4 | ||||
-rw-r--r-- | apps/macpdf.app/Contents/Resources/English.lproj/main.nib/info.nib | 24 | ||||
-rw-r--r-- | apps/macpdf.app/Contents/Resources/English.lproj/main.nib/objects.xib | 516 | ||||
-rw-r--r-- | apps/macpdf.app/Contents/Resources/macpdf.icns | bin | 0 -> 31574 bytes | |||
-rw-r--r-- | apps/macpdf.app/macpdf.c | 69 | ||||
-rw-r--r-- | apps/macpdf.app/macpdf.h | 31 | ||||
-rw-r--r-- | apps/macpdf.app/view.c | 525 | ||||
-rw-r--r-- | apps/pdfclean.c | 173 | ||||
-rw-r--r-- | apps/pdfdebug.c | 188 | ||||
-rw-r--r-- | apps/pdffunction.c | 263 | ||||
-rw-r--r-- | apps/pdfmerge.c | 230 | ||||
-rw-r--r-- | apps/pdfrip.c | 182 | ||||
-rw-r--r-- | apps/showcmap.c | 22 | ||||
-rw-r--r-- | apps/w32pdf.c | 656 | ||||
-rw-r--r-- | apps/x11pdf.c | 590 | ||||
-rw-r--r-- | apps/ximage.c | 666 |
19 files changed, 4834 insertions, 0 deletions
diff --git a/apps/gtkpdf.c b/apps/gtkpdf.c new file mode 100644 index 00000000..07730b38 --- /dev/null +++ b/apps/gtkpdf.c @@ -0,0 +1,670 @@ +/* + +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; + +enum { ZOOM, FITWIDTH, FITPAGE }; + +struct PDFApp +{ + GtkWidget *canvas; + GtkWidget *status; + GtkWidget *scroll; + int statusid; + int viewmode; + + 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; + float scalex, scaley, scale; + fz_matrix ctm; + fz_irect 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 ... "); + + ctm = fz_identity(); + ctm = fz_concat(ctm, fz_translate(0, -gapp->page->mediabox.max.y)); + ctm = fz_concat(ctm, fz_rotate(gapp->rotate + gapp->page->rotate)); + bbox = fz_roundrect(fz_transformaabb(ctm, gapp->page->mediabox)); + + scale = gapp->zoom; + scalex = scaley = 1.0; + + if (gapp->viewmode == FITWIDTH || gapp->viewmode == FITPAGE) + { + GtkAdjustment *hadj; + int w; + hadj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(gapp->scroll)); + w = bbox.max.x - bbox.min.x; + if (w != 0) + scalex = hadj->page_size / (float)w; + scale = scalex; + } + + if (gapp->viewmode == FITPAGE) + { + GtkAdjustment *vadj; + int h; + vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(gapp->scroll)); + h = bbox.max.y - bbox.min.y; + if (h != 0) + scaley = vadj->page_size / (float)h; + scale = MIN(scale, scaley); + } + + gapp->zoom = scale; + + 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_roundrect(fz_transformaabb(ctm, gapp->page->mediabox)); + + error = fz_rendertree(&gapp->image, gapp->gc, gapp->page->tree, ctm, 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->viewmode = ZOOM; + gapp->zoom *= 1.25; + forkwork(drawpage); +} + +static void onzoomout(GtkWidget * widget, void *data) +{ + gapp->viewmode = ZOOM; + gapp->zoom *= 0.8; + forkwork(drawpage); +} + +static void onzoom100(GtkWidget * widget, void *data) +{ + gapp->viewmode = ZOOM; + 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) +{ + gapp->viewmode = FITWIDTH; + forkwork(drawpage); +} + +static void onfitpage(GtkWidget *widget, void *data) +{ + gapp->viewmode = FITPAGE; + forkwork(drawpage); +} + +static int startxpos; +static int startypos; +static int dopan = 0; +guint32 pangrabtime = 0; + +static void mousedown(GtkWidget *widget, GdkEventMotion *event, void *data) +{ + GdkModifierType mods; + gtk_widget_grab_focus(gapp->scroll); + + gdk_window_get_pointer(gapp->scroll->window, &startxpos, &startypos, &mods); + if (mods & GDK_BUTTON2_MASK) { + GtkAdjustment *adj; + adj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(gapp->scroll)); + startxpos += adj->value; + + adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(gapp->scroll)); + startypos += adj->value; + + gdk_pointer_grab(gapp->scroll->window, TRUE, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, NULL, NULL /* TODO: pan cursor */, event->time); + + dopan = 1; + } + else { + dopan = 0; + } +} + +static void mouseup(GtkWidget *widget, GdkEventMotion *event, void *data) +{ + dopan = 0; + gdk_pointer_ungrab(event->time); +} + +static void mousemove(GtkWidget *widget, GdkEventMotion *event, void *data) +{ + int xpos, ypos; + GdkModifierType mods; + GtkAdjustment *adj; + + if (!dopan) return; + + gdk_window_get_pointer(gapp->scroll->window, &xpos, &ypos, &mods); + + if (mods & GDK_BUTTON2_MASK) { + adj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(gapp->scroll)); + adj->value = startxpos - xpos; + adj->value = CLAMP(adj->value, adj->lower, adj->upper - adj->page_size); + + gtk_adjustment_value_changed(adj); + + adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(gapp->scroll)); + adj->value = startypos - ypos; + adj->value = CLAMP(adj->value, adj->lower, adj->upper - adj->page_size); + + /* clamp to viewport... */ + + gtk_adjustment_value_changed(adj); + } + else + mouseup(widget, event, 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_set_events (da, + GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_POINTER_MOTION_MASK); + 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; +} + diff --git a/apps/macpdf.app/Contents/Info.plist b/apps/macpdf.app/Contents/Info.plist new file mode 100644 index 00000000..71dd646b --- /dev/null +++ b/apps/macpdf.app/Contents/Info.plist @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>macpdf</string> + <key>CFBundleIconFile</key> + <string>macpdf.icns</string> + <key>CFBundleIdentifier</key> + <string>com.artofcode.MuPDF</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>0.1</string> + <key>CSResourcesFileMapped</key> + <true/> +</dict> +</plist> diff --git a/apps/macpdf.app/Contents/PkgInfo b/apps/macpdf.app/Contents/PkgInfo new file mode 100644 index 00000000..bd04210f --- /dev/null +++ b/apps/macpdf.app/Contents/PkgInfo @@ -0,0 +1 @@ +APPL????
\ No newline at end of file diff --git a/apps/macpdf.app/Contents/Resources/English.lproj/main.nib/classes.nib b/apps/macpdf.app/Contents/Resources/English.lproj/main.nib/classes.nib new file mode 100644 index 00000000..ea58db11 --- /dev/null +++ b/apps/macpdf.app/Contents/Resources/English.lproj/main.nib/classes.nib @@ -0,0 +1,4 @@ +{ +IBClasses = (); +IBVersion = 1; +} diff --git a/apps/macpdf.app/Contents/Resources/English.lproj/main.nib/info.nib b/apps/macpdf.app/Contents/Resources/English.lproj/main.nib/info.nib new file mode 100644 index 00000000..8d83474f --- /dev/null +++ b/apps/macpdf.app/Contents/Resources/English.lproj/main.nib/info.nib @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>IBDocumentLocation</key> + <string>54 19 356 240 0 0 1024 746 </string> + <key>IBEditorPositions</key> + <dict> + <key>29</key> + <string>15 273 218 44 0 0 1024 746 </string> + </dict> + <key>IBFramework Version</key> + <string>349.0</string> + <key>IBOpenObjects</key> + <array> + <integer>29</integer> + <integer>231</integer> + </array> + <key>IBSystem Version</key> + <string>7S215</string> + <key>targetFramework</key> + <string>IBCarbonFramework</string> +</dict> +</plist> diff --git a/apps/macpdf.app/Contents/Resources/English.lproj/main.nib/objects.xib b/apps/macpdf.app/Contents/Resources/English.lproj/main.nib/objects.xib new file mode 100644 index 00000000..6e3aad82 --- /dev/null +++ b/apps/macpdf.app/Contents/Resources/English.lproj/main.nib/objects.xib @@ -0,0 +1,516 @@ +<?xml version="1.0" standalone="yes"?> +<object class="NSIBObjectData"> + <string name="targetFramework">IBCarbonFramework</string> + <object name="rootObject" class="NSCustomObject" id="1"> + <string name="customClass">NSApplication</string> + </object> + <array count="58" name="allObjects"> + <object class="IBCarbonMenu" id="29"> + <string name="title">MuPDF</string> + <array count="4" name="items"> + <object class="IBCarbonMenuItem" id="185"> + <string name="title">MuPDF</string> + <object name="submenu" class="IBCarbonMenu" id="184"> + <string name="title">MuPDF</string> + <array count="1" name="items"> + <object class="IBCarbonMenuItem" id="187"> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">About MuPDF</string> + <int name="keyEquivalentModifier">0</int> + <ostype name="command">abou</ostype> + </object> + </array> + <string name="name">_NSAppleMenu</string> + </object> + </object> + <object class="IBCarbonMenuItem" id="127"> + <string name="title">File</string> + <object name="submenu" class="IBCarbonMenu" id="131"> + <string name="title">File</string> + <array count="10" name="items"> + <object class="IBCarbonMenuItem" id="139"> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">New</string> + <string name="keyEquivalent">n</string> + <ostype name="command">new </ostype> + </object> + <object class="IBCarbonMenuItem" id="134"> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">Open…</string> + <string name="keyEquivalent">o</string> + <ostype name="command">open</ostype> + </object> + <object class="IBCarbonMenuItem" id="133"> + <boolean name="separator">TRUE</boolean> + </object> + <object class="IBCarbonMenuItem" id="130"> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">Close</string> + <string name="keyEquivalent">w</string> + <ostype name="command">clos</ostype> + </object> + <object class="IBCarbonMenuItem" id="138"> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">Save</string> + <string name="keyEquivalent">s</string> + <ostype name="command">save</ostype> + </object> + <object class="IBCarbonMenuItem" id="137"> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">Save As…</string> + <string name="keyEquivalent">S</string> + <ostype name="command">svas</ostype> + </object> + <object class="IBCarbonMenuItem" id="132"> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">Revert</string> + <string name="keyEquivalent">r</string> + <ostype name="command">rvrt</ostype> + </object> + <object class="IBCarbonMenuItem" id="128"> + <boolean name="separator">TRUE</boolean> + </object> + <object class="IBCarbonMenuItem" id="135"> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">Page Setup…</string> + <string name="keyEquivalent">P</string> + <ostype name="command">page</ostype> + </object> + <object class="IBCarbonMenuItem" id="136"> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">Print…</string> + <string name="keyEquivalent">p</string> + <ostype name="command">prnt</ostype> + </object> + </array> + </object> + </object> + <object class="IBCarbonMenuItem" id="152"> + <string name="title">Edit</string> + <object name="submenu" class="IBCarbonMenu" id="147"> + <string name="title">Edit</string> + <array count="10" name="items"> + <object class="IBCarbonMenuItem" id="141"> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">Undo</string> + <string name="keyEquivalent">z</string> + <ostype name="command">undo</ostype> + </object> + <object class="IBCarbonMenuItem" id="146"> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">Redo</string> + <string name="keyEquivalent">Z</string> + <ostype name="command">redo</ostype> + </object> + <object class="IBCarbonMenuItem" id="142"> + <boolean name="separator">TRUE</boolean> + </object> + <object class="IBCarbonMenuItem" id="143"> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">Cut</string> + <string name="keyEquivalent">x</string> + <ostype name="command">cut </ostype> + </object> + <object class="IBCarbonMenuItem" id="149"> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">Copy</string> + <string name="keyEquivalent">c</string> + <ostype name="command">copy</ostype> + </object> + <object class="IBCarbonMenuItem" id="144"> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">Paste</string> + <string name="keyEquivalent">v</string> + <ostype name="command">past</ostype> + </object> + <object class="IBCarbonMenuItem" id="151"> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">Delete</string> + <ostype name="command">clea</ostype> + </object> + <object class="IBCarbonMenuItem" id="148"> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">Select All</string> + <string name="keyEquivalent">a</string> + <ostype name="command">sall</ostype> + </object> + <object class="IBCarbonMenuItem" id="192"> + <boolean name="separator">TRUE</boolean> + </object> + <object class="IBCarbonMenuItem" id="191"> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">Special Characters…</string> + <ostype name="command">chrp</ostype> + </object> + </array> + </object> + </object> + <object class="IBCarbonMenuItem" id="153"> + <string name="title">Window</string> + <object name="submenu" class="IBCarbonMenu" id="154"> + <string name="title">Window</string> + <array count="6" name="items"> + <object class="IBCarbonMenuItem" id="155"> + <boolean name="dynamic">TRUE</boolean> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">Minimize Window</string> + <string name="keyEquivalent">m</string> + <ostype name="command">mini</ostype> + </object> + <object class="IBCarbonMenuItem" id="188"> + <boolean name="dynamic">TRUE</boolean> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">Minimize All Windows</string> + <string name="keyEquivalent">m</string> + <int name="keyEquivalentModifier">1572864</int> + <ostype name="command">mina</ostype> + </object> + <object class="IBCarbonMenuItem" id="190"> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">Zoom</string> + <ostype name="command">zoom</ostype> + </object> + <object class="IBCarbonMenuItem" id="156"> + <boolean name="separator">TRUE</boolean> + </object> + <object class="IBCarbonMenuItem" id="157"> + <boolean name="dynamic">TRUE</boolean> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">Bring All to Front</string> + <ostype name="command">bfrt</ostype> + </object> + <object class="IBCarbonMenuItem" id="189"> + <boolean name="dynamic">TRUE</boolean> + <boolean name="updateSingleItem">TRUE</boolean> + <string name="title">Arrange in Front</string> + <int name="keyEquivalentModifier">1572864</int> + <ostype name="command">frnt</ostype> + </object> + </array> + <string name="name">_NSWindowsMenu</string> + </object> + </object> + </array> + <string name="name">_NSMainMenu</string> + </object> + <reference idRef="127"/> + <reference idRef="128"/> + <reference idRef="130"/> + <reference idRef="131"/> + <reference idRef="132"/> + <reference idRef="133"/> + <reference idRef="134"/> + <reference idRef="135"/> + <reference idRef="136"/> + <reference idRef="137"/> + <reference idRef="138"/> + <reference idRef="139"/> + <reference idRef="141"/> + <reference idRef="142"/> + <reference idRef="143"/> + <reference idRef="144"/> + <reference idRef="146"/> + <reference idRef="147"/> + <reference idRef="148"/> + <reference idRef="149"/> + <reference idRef="151"/> + <reference idRef="152"/> + <reference idRef="153"/> + <reference idRef="154"/> + <reference idRef="155"/> + <reference idRef="156"/> + <reference idRef="157"/> + <object class="IBCarbonWindow" id="166"> + <string name="windowRect">56 24 553 587 </string> + <string name="title">Window</string> + <object name="rootControl" class="IBCarbonRootControl" id="167"> + <string name="bounds">0 0 497 563 </string> + <string name="viewFrame">0 0 563 497 </string> + <array count="13" name="subviews"> + <object class="IBCarbonScrollBar" id="197"> + <string name="bounds">482 248 497 549 </string> + <string name="viewFrame">248 482 301 15 </string> + <object name="layoutInfo" class="IBCarbonHILayoutInfo"> + <int name="bindingLeftKind">1</int> + <int name="bindingBottomKind">2</int> + <int name="bindingRightKind">2</int> + </object> + </object> + <object class="IBCarbonScrollBar" id="198"> + <string name="bounds">-1 548 483 563 </string> + <string name="viewFrame">548 -1 15 484 </string> + <object name="layoutInfo" class="IBCarbonHILayoutInfo"> + <int name="bindingTopKind">1</int> + <int name="bindingBottomKind">2</int> + <int name="bindingRightKind">2</int> + </object> + </object> + <object class="IBCarbonBevelButton" id="201"> + <string name="bounds">482 23 498 47 </string> + <string name="viewFrame">23 482 24 16 </string> + <string name="title"><</string> + <object name="layoutInfo" class="IBCarbonHILayoutInfo"> + <int name="bindingLeftKind">1</int> + <int name="bindingBottomKind">2</int> + </object> + <int name="contentType">0</int> + <int name="textPlacement">3</int> + </object> + <object class="IBCarbonBevelButton" id="204"> + <string name="bounds">482 46 498 70 </string> + <string name="viewFrame">46 482 24 16 </string> + <string name="title">></string> + <object name="layoutInfo" class="IBCarbonHILayoutInfo"> + <int name="bindingBottomKind">2</int> + </object> + <int name="contentType">0</int> + <int name="textPlacement">3</int> + </object> + <object class="IBCarbonBevelButton" id="205"> + <string name="bounds">482 92 498 112 </string> + <string name="viewFrame">92 482 20 16 </string> + <string name="title">(</string> + <object name="layoutInfo" class="IBCarbonHILayoutInfo"> + <int name="bindingBottomKind">2</int> + </object> + <int name="contentType">0</int> + <int name="textPlacement">3</int> + </object> + <object class="IBCarbonBevelButton" id="206"> + <string name="bounds">482 111 498 131 </string> + <string name="viewFrame">111 482 20 16 </string> + <string name="title">)</string> + <object name="layoutInfo" class="IBCarbonHILayoutInfo"> + <int name="bindingBottomKind">2</int> + </object> + <int name="contentType">0</int> + <int name="textPlacement">3</int> + </object> + <object class="IBCarbonEditText" id="209"> + <string name="bounds">485 199 495 237 </string> + <string name="viewFrame">199 485 38 10 </string> + <boolean name="small">TRUE</boolean> + <int name="controlSize">3</int> + <object name="layoutInfo" class="IBCarbonHILayoutInfo"> + <int name="bindingBottomKind">2</int> + </object> + <boolean name="isUnicode">TRUE</boolean> + </object> + <object class="IBCarbonBevelButton" id="210"> + <string name="bounds">482 0 498 24 </string> + <string name="viewFrame">0 482 24 16 </string> + <string name="title"><<</string> + <object name="layoutInfo" class="IBCarbonHILayoutInfo"> + <int name="bindingLeftKind">1</int> + <int name="bindingBottomKind">2</int> + </object> + <int name="contentType">0</int> + <int name="textPlacement">3</int> + </object> + <object class="IBCarbonBevelButton" id="211"> + <string name="bounds">482 69 498 93 </string> + <string name="viewFrame">69 482 24 16 </string> + <string name="title">>></string> + <object name="layoutInfo" class="IBCarbonHILayoutInfo"> + <int name="bindingBottomKind">2</int> + </object> + <int name="contentType">0</int> + <int name="textPlacement">3</int> + </object> + <object class="IBCarbonBevelButton" id="227"> + <string name="bounds">482 168 498 188 </string> + <string name="viewFrame">168 482 20 16 </string> + <string name="title">+</string> + <object name="layoutInfo" class="IBCarbonHILayoutInfo"> + <int name="bindingBottomKind">2</int> + </object> + <int name="contentType">0</int> + <int name="textPlacement">3</int> + </object> + <object class="IBCarbonBevelButton" id="228"> + <string name="bounds">482 130 498 150 </string> + <string name="viewFrame">130 482 20 16 </string> + <string name="title">-</string> + <object name="layoutInfo" class="IBCarbonHILayoutInfo"> + <int name="bindingBottomKind">2</int> + </object> + <int name="contentType">0</int> + <int name="textPlacement">3</int> + </object> + <object class="IBCarbonBevelButton" id="229"> + <string name="bounds">482 149 498 169 </string> + <string name="viewFrame">149 482 20 16 </string> + <string name="title">%</string> + <object name="layoutInfo" class="IBCarbonHILayoutInfo"> + <int name="bindingBottomKind">2</int> + </object> + <int name="contentType">0</int> + <int name="textPlacement">3</int> + </object> + <object class="IBCarbonHIView" id="230"> + <string name="bounds">0 0 484 549 </string> + <string name="viewFrame">0 0 549 484 </string> + <ostype name="controlSignature">Poof</ostype> + <int name="controlID">666</int> + <object name="layoutInfo" class="IBCarbonHILayoutInfo"> + <int name="bindingTopKind">1</int> + <int name="bindingLeftKind">1</int> + <int name="bindingBottomKind">2</int> + <int name="bindingRightKind">2</int> + </object> + <string name="classID">com.artofcode.mupdf.View</string> + </object> + </array> + </object> + <boolean name="receiveUpdates">FALSE</boolean> + <boolean name="liveResize">TRUE</boolean> + <boolean name="compositing">TRUE</boolean> + <boolean name="isConstrained">FALSE</boolean> + </object> + <reference idRef="167"/> + <reference idRef="184"/> + <reference idRef="185"/> + <reference idRef="187"/> + <reference idRef="188"/> + <reference idRef="189"/> + <reference idRef="190"/> + <reference idRef="191"/> + <reference idRef="192"/> + <reference idRef="197"/> + <reference idRef="198"/> + <reference idRef="201"/> + <reference idRef="204"/> + <reference idRef="205"/> + <reference idRef="206"/> + <reference idRef="209"/> + <reference idRef="210"/> + <reference idRef="211"/> + <reference idRef="227"/> + <reference idRef="228"/> + <reference idRef="229"/> + <reference idRef="230"/> + <object class="IBCarbonWindow" id="231"> + <string name="windowRect">200 83 330 337 </string> + <string name="title">Password</string> + <object name="rootControl" class="IBCarbonRootControl" id="232"> + <string name="bounds">0 0 130 254 </string> + <string name="viewFrame">0 0 254 130 </string> + <array count="5" name="subviews"> + <object class="IBCarbonStaticText" id="233"> + <string name="bounds">20 20 36 234 </string> + <string name="viewFrame">20 20 214 16 </string> + <string name="title">This PDF document is encrypted.</string> + </object> + <object class="IBCarbonStaticText" id="234"> + <string name="bounds">50 32 66 146 </string> + <string name="viewFrame">32 50 114 16 </string> + <string name="title">Password:</string> + </object> + <object class="IBCarbonEditText" id="235"> + <string name="bounds">51 106 67 215 </string> + <string name="viewFrame">106 51 109 16 </string> + <boolean name="isPassword">TRUE</boolean> + <boolean name="isUnicode">TRUE</boolean> + <boolean name="isSingleLine">TRUE</boolean> + </object> + <object class="IBCarbonButton" id="237"> + <string name="bounds">90 132 110 202 </string> + <string name="viewFrame">132 90 70 20 </string> + <string name="title">OK</string> + <int name="buttonType">1</int> + </object> + <object class="IBCarbonButton" id="238"> + <string name="bounds">90 50 110 120 </string> + <string name="viewFrame">50 90 70 20 </string> + <string name="title">Cancel</string> + </object> + </array> + </object> + <boolean name="receiveUpdates">FALSE</boolean> + <boolean name="isResizable">FALSE</boolean> + <boolean name="liveResize">TRUE</boolean> + <boolean name="compositing">TRUE</boolean> + <int name="carbonWindowClass">11</int> + <boolean name="isConstrained">FALSE</boolean> + </object> + <reference idRef="232"/> + <reference idRef="233"/> + <reference idRef="234"/> + <reference idRef="235"/> + <reference idRef="237"/> + <reference idRef="238"/> + </array> + <array count="58" name="allParents"> + <reference idRef="1"/> + <reference idRef="29"/> + <reference idRef="131"/> + <reference idRef="131"/> + <reference idRef="127"/> + <reference idRef="131"/> + <reference idRef="131"/> + <reference idRef="131"/> + <reference idRef="131"/> + <reference idRef="131"/> + <reference idRef="131"/> + <reference idRef="131"/> + <reference idRef="131"/> + <reference idRef="147"/> + <reference idRef="147"/> + <reference idRef="147"/> + <reference idRef="147"/> + <reference idRef="147"/> + <reference idRef="152"/> + <reference idRef="147"/> + <reference idRef="147"/> + <reference idRef="147"/> + <reference idRef="29"/> + <reference idRef="29"/> + <reference idRef="153"/> + <reference idRef="154"/> + <reference idRef="154"/> + <reference idRef="154"/> + <reference idRef="1"/> + <reference idRef="166"/> + <reference idRef="185"/> + <reference idRef="29"/> + <reference idRef="184"/> + <reference idRef="154"/> + <reference idRef="154"/> + <reference idRef="154"/> + <reference idRef="147"/> + <reference idRef="147"/> + <reference idRef="167"/> + <reference idRef="167"/> + <reference idRef="167"/> + <reference idRef="167"/> + <reference idRef="167"/> + <reference idRef="167"/> + <reference idRef="167"/> + <reference idRef="167"/> + <reference idRef="167"/> + <reference idRef="167"/> + <reference idRef="167"/> + <reference idRef="167"/> + <reference idRef="167"/> + <reference idRef="1"/> + <reference idRef="231"/> + <reference idRef="232"/> + <reference idRef="232"/> + <reference idRef="232"/> + <reference idRef="232"/> + <reference idRef="232"/> + </array> + <dictionary count="5" name="nameTable"> + <string>Files Owner</string> + <reference idRef="1"/> + <string>MainWindow</string> + <reference idRef="166"/> + <string>MenuBar</string> + <reference idRef="29"/> + <string>PasswordWindow</string> + <reference idRef="231"/> + <string>View1</string> + <reference idRef="230"/> + </dictionary> + <unsigned_int name="nextObjectID">239</unsigned_int> +</object> diff --git a/apps/macpdf.app/Contents/Resources/macpdf.icns b/apps/macpdf.app/Contents/Resources/macpdf.icns Binary files differnew file mode 100644 index 00000000..d11b8be1 --- /dev/null +++ b/apps/macpdf.app/Contents/Resources/macpdf.icns diff --git a/apps/macpdf.app/macpdf.c b/apps/macpdf.app/macpdf.c new file mode 100644 index 00000000..88ea3351 --- /dev/null +++ b/apps/macpdf.app/macpdf.c @@ -0,0 +1,69 @@ +#include "macpdf.h" + +static void +init_window(viewctx *ctx) +{ + WindowRef window = ctx->window; + HIViewRef viewPane; + static const HIViewID viewPaneID = { 'Poof', 666 }; + OSStatus err; + + err = HIViewFindByID(HIViewGetRoot(window), viewPaneID, &viewPane); + printf("err from findbyid: %d\n", err); +} + +int +openpdf(WindowRef window, const char *filename) +{ + HIViewRef viewPane; + static const HIViewID viewPaneID = { 'Poof', 666 }; + OSStatus err; + + err = HIViewFindByID(HIViewGetRoot(window), viewPaneID, &viewPane); + require_noerr(err, cleanup); + + err = view_openpdf(viewPane, filename); + require_noerr(err, cleanup); + + err = view_showpage(viewPane); + + cleanup: + return err; +} + +int main(int argc, char *argv[]) +{ + IBNibRef nibRef; + OSStatus err; + WindowRef window; + + fz_cpudetect(); + fz_accelerate(); + + err = view_register(); + require_noerr(err, CantRegisterView); + + err = CreateNibReference(CFSTR("main"), &nibRef); + printf("err = %d\n", err); + require_noerr(err, CantGetNibRef); + + err = SetMenuBarFromNib(nibRef, CFSTR("MenuBar")); + require_noerr(err, CantSetMenuBar); + + err = CreateWindowFromNib(nibRef, CFSTR("MainWindow"), &window); + require_noerr(err, CantCreateWindow); + + openpdf(window, "tiger.pdf"); + + DisposeNibReference(nibRef); + + ShowWindow(window); + RunApplicationEventLoop(); + + CantGetNibRef: + CantSetMenuBar: + CantCreateWindow: + CantRegisterView: + + return err; +} diff --git a/apps/macpdf.app/macpdf.h b/apps/macpdf.app/macpdf.h new file mode 100644 index 00000000..dde4dca7 --- /dev/null +++ b/apps/macpdf.app/macpdf.h @@ -0,0 +1,31 @@ +#include <Carbon/Carbon.h> + +#include <fitz.h> +#include <mupdf.h> + +typedef struct viewctx +{ + WindowRef window; + HIViewRef view; + char *doctitle; + + float zoom; + int rotate; + int pageno; + + pdf_page *page; + fz_obj *pageobj; + + pdf_xref *xref; + pdf_pagetree *pages; + pdf_outline *outline; + fz_renderer *rast; + fz_pixmap *image; +} viewctx; + +OSStatus view_register(void); + +OSStatus +view_openpdf(HIViewRef view, char *filename); + +OSStatus view_showpage(HIViewRef view); diff --git a/apps/macpdf.app/view.c b/apps/macpdf.app/view.c new file mode 100644 index 00000000..de780920 --- /dev/null +++ b/apps/macpdf.app/view.c @@ -0,0 +1,525 @@ +/* This module implements a Carbon HIView object for a pattern plate + editor. */ + +#include "macpdf.h" + +#define kViewClassID CFSTR("com.artofcode.mupdf.View") +#define kViewPrivate 'MU_v' + +static OSStatus +view_construct(EventRef inEvent) +{ + OSStatus err; + viewctx *ctx; + + ctx = (viewctx *)malloc(sizeof(viewctx)); + require_action(ctx != NULL, CantMalloc, err = memFullErr); + err = GetEventParameter(inEvent, kEventParamHIObjectInstance, + typeHIObjectRef, NULL, sizeof(HIObjectRef), NULL, + (HIObjectRef *)&ctx->view); + require_noerr(err, ParameterMissing); + err = SetEventParameter(inEvent, kEventParamHIObjectInstance, + typeVoidPtr, sizeof(viewctx *), &ctx); + + ParameterMissing: + if (err != noErr) + free(ctx); + + CantMalloc: + return err; +} + +static OSStatus +view_destruct(EventRef inEvent, viewctx *inData) +{ + free(inData); + return noErr; +} + +static OSStatus +view_initialize(EventHandlerCallRef inCallRef, EventRef inEvent, + viewctx *ctx) +{ + OSStatus err; + HIRect bounds; + + err = CallNextEventHandler(inCallRef, inEvent); + require_noerr(err, TroubleInSuperClass); + + ctx->zoom = 1.0; + ctx->rotate = 0; + ctx->pageno = 1; + ctx->window = nil; + + ParameterMissing: + TroubleInSuperClass: + return err; +} + +#ifndef M_PI +#define M_PI 3.14159265358979323846 /* pi */ +#endif + +static void +cgcontext_set_rgba(CGContextRef ctx, unsigned int rgba) +{ + const double norm = 1.0 / 255; + CGContextSetRGBFillColor(ctx, + ((rgba >> 24) & 0xff) * norm, + ((rgba >> 16) & 0xff) * norm, + ((rgba >> 8) & 0xff) * norm, + (rgba & 0xff) * norm); +} + +static void +draw_dot(CGContextRef ctx, double x, double y, double r, + unsigned int rgba) +{ + cgcontext_set_rgba(ctx, rgba); + CGMutablePathRef path = CGPathCreateMutable(); + CGPathAddArc(path, NULL, x, y, r, 0, 2 * M_PI, false); + CGContextAddPath(ctx, path); + CGPathRelease(path); + CGContextFillPath(ctx); +} + +static void +draw_raw_rect(CGContextRef ctx, double x0, double y0, double x1, double y1, + unsigned int rgba) +{ + HIRect rect; + + cgcontext_set_rgba(ctx, rgba); + rect.origin.x = x0; + rect.origin.y = y0; + rect.size.width = x1 - x0; + rect.size.height = y1 - y0; + CGContextFillRect(ctx, rect); +} + +static void +draw_rect(CGContextRef ctx, double x, double y, double r, + unsigned int rgba) +{ + draw_raw_rect(ctx, x - r, y - r, x + r, y + r, rgba); +} + +static OSStatus +view_draw(EventRef inEvent, viewctx *ctx) +{ + OSStatus err; + CGContextRef gc; + CGDataProviderRef provider; + CGImageRef image; + CGColorSpaceRef colorspace; + CGRect rect; + + err = GetEventParameter(inEvent, kEventParamCGContextRef, typeCGContextRef, + NULL, sizeof(CGContextRef), NULL, &gc); + require_noerr(err, cleanup); + + colorspace = CGColorSpaceCreateDeviceRGB(); + provider = CGDataProviderCreateWithData(NULL, ctx->image->samples, + ctx->image->w * ctx->image->h * 4, + NULL); + image = CGImageCreate(ctx->image->w, ctx->image->h, + 8, 32, ctx->image->w * 4, + colorspace, kCGImageAlphaNoneSkipFirst, provider, + NULL, 0, kCGRenderingIntentDefault); + + rect.origin.x = 0; + rect.origin.y = 0; + rect.size.width = ctx->image->w; + rect.size.height = ctx->image->h; + HIViewDrawCGImage(gc, &rect, image); + + CGColorSpaceRelease(colorspace); + CGDataProviderRelease(provider); + + cleanup: + return err; +} + +static OSStatus +view_get_data(EventRef inEvent, viewctx *inData) +{ + OSStatus err; + OSType tag; + Ptr ptr; + Size outSize; + + /* Probably could use a bit more error checking here, for type + and size match. Also, just returning a viewctx seems a + little hacky. */ + err = GetEventParameter(inEvent, kEventParamControlDataTag, typeEnumeration, + NULL, sizeof(OSType), NULL, &tag); + require_noerr(err, ParameterMissing); + + err = GetEventParameter(inEvent, kEventParamControlDataBuffer, typePtr, + NULL, sizeof(Ptr), NULL, &ptr); + + if (tag == kViewPrivate) { + *((viewctx **)ptr) = inData; + outSize = sizeof(viewctx *); + } else + err = errDataNotSupported; + + if (err == noErr) + err = SetEventParameter(inEvent, kEventParamControlDataBufferSize, typeLongInteger, + sizeof(Size), &outSize); + + ParameterMissing: + return err; +} + +static OSStatus +view_set_data(EventRef inEvent, viewctx *inData) +{ + OSStatus err; + Ptr ptr; + OSType tag; + + err = GetEventParameter(inEvent, kEventParamControlDataTag, typeEnumeration, + NULL, sizeof(OSType), NULL, &tag); + require_noerr(err, ParameterMissing); + + err = GetEventParameter(inEvent, kEventParamControlDataBuffer, typePtr, + NULL, sizeof(Ptr), NULL, &ptr); + require_noerr(err, ParameterMissing); + + if (tag == 'Plat') { + //inData->p = *(plate **)ptr; + } else + err = errDataNotSupported; + + ParameterMissing: + return err; +} + +static OSStatus +view_hittest(EventRef inEvent, viewctx *inData) +{ + OSStatus err; + HIPoint where; + HIRect bounds; + ControlPartCode part; + + err = GetEventParameter(inEvent, kEventParamMouseLocation, typeHIPoint, + NULL, sizeof(HIPoint), NULL, &where); + require_noerr(err, ParameterMissing); + + err = HIViewGetBounds(inData->view, &bounds); + require_noerr(err, ParameterMissing); + + if (CGRectContainsPoint(bounds, where)) + part = 1; + else + part = kControlNoPart; + err = SetEventParameter(inEvent, kEventParamControlPart, + typeControlPartCode, sizeof(ControlPartCode), + &part); + printf("hittest %g, %g!\n", where.x, where.y); + + ParameterMissing: + return err; +} + +static void +view_queue_draw(viewctx *pe) +{ + HIViewSetNeedsDisplay(pe->view, true); +} + + +static int +view_motion(viewctx *pe, double x, double y) +{ + //if (pe->p->motmode == MOTION_MODE_MOVE) + //plate_motion_move(pe->p, x, y); + //else if (pe->p->motmode == MOTION_MODE_SELECT) + //plate_motion_select(pe->p, x, y); + view_queue_draw(pe); + return 1; +} + +static int +view_button_release(viewctx *pe) +{ + int need_redraw; + + //need_redraw = (pe->p->motmode == MOTION_MODE_SELECT); + + //plate_unpress(pe->p); + + if (need_redraw) + view_queue_draw(pe); + return 1; +} + +pascal OSStatus +view_handler(EventHandlerCallRef inCallRef, + EventRef inEvent, + void* inUserData ) +{ + OSStatus err = eventNotHandledErr; + UInt32 eventClass = GetEventClass(inEvent); + UInt32 eventKind = GetEventKind(inEvent); + viewctx *data = (viewctx *)inUserData; + + switch (eventClass) { + case kEventClassHIObject: + switch (eventKind) { + case kEventHIObjectConstruct: + err = view_construct(inEvent); + break; + case kEventHIObjectInitialize: + err = view_initialize(inCallRef, inEvent, data); + break; + case kEventHIObjectDestruct: + err = view_destruct(inEvent, data); + break; + } + break; + case kEventClassControl: + switch (eventKind) { + case kEventControlInitialize: + err = noErr; + break; + case kEventControlDraw: + err = view_draw(inEvent, data); + break; + case kEventControlGetData: + err = view_get_data(inEvent, data); + break; + case kEventControlSetData: + err = view_set_data(inEvent, data); + break; + case kEventControlHitTest: + err = view_hittest(inEvent, data); + break; + /*...*/ + } + break; + } + return err; +} + +OSStatus +view_register(void) +{ + OSStatus err = noErr; + static HIObjectClassRef view_ClassRef = NULL; + + if (view_ClassRef == NULL) { + EventTypeSpec eventList[] = { + { kEventClassHIObject, kEventHIObjectConstruct }, + { kEventClassHIObject, kEventHIObjectInitialize }, + { kEventClassHIObject, kEventHIObjectDestruct }, + + { kEventClassControl, kEventControlActivate }, + { kEventClassControl, kEventControlDeactivate }, + { kEventClassControl, kEventControlDraw }, + { kEventClassControl, kEventControlHiliteChanged }, + { kEventClassControl, kEventControlHitTest }, + { kEventClassControl, kEventControlInitialize }, + { kEventClassControl, kEventControlGetData }, + { kEventClassControl, kEventControlSetData }, + }; + err = HIObjectRegisterSubclass(kViewClassID, + kHIViewClassID, + NULL, + view_handler, + GetEventTypeCount(eventList), + eventList, + NULL, + &view_ClassRef); + } + return err; +} + +OSStatus view_create( + WindowRef inWindow, + const HIRect* inBounds, + HIViewRef* outView) +{ + OSStatus err; + EventRef event; + + err = view_register(); + require_noerr(err, CantRegister); + + err = CreateEvent(NULL, kEventClassHIObject, kEventHIObjectInitialize, + GetCurrentEventTime(), 0, &event); + require_noerr(err, CantCreateEvent); + + if (inBounds != NULL) { + err = SetEventParameter(event, 'Boun', typeHIRect, sizeof(HIRect), + inBounds); + require_noerr(err, CantSetParameter); + } + + err = HIObjectCreate(kViewClassID, event, (HIObjectRef*)outView); + require_noerr(err, CantCreate); + + if (inWindow != NULL) { + HIViewRef root; + err = GetRootControl(inWindow, &root); + require_noerr(err, CantGetRootView); + err = HIViewAddSubview(root, *outView); + } + CantCreate: + CantGetRootView: + CantSetParameter: + CantCreateEvent: + ReleaseEvent(event); + CantRegister: + return err; +} + +OSStatus +view_openpdf(HIViewRef view, char *filename) +{ + OSStatus err; + viewctx *ctx; + + err = GetControlData(view, 1, kViewPrivate, 4, &ctx, NULL); + require_noerr(err, CantGetPrivate); + + fz_error *error; + fz_obj *obj; + pdf_xref *xref; + + error = pdf_newxref(&xref); + if (error) + fz_abort(error); + ctx->xref = xref; + + 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 0 + if (xref->crypt) + { + error = pdf_setpassword(xref->crypt, password); + if (error) fz_abort(error); + } +#endif + + 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(&ctx->outline, xref); + if (error) fz_abort(error); + + ctx->doctitle = filename; + if (xref->info) + { + obj = fz_dictgets(xref->info, "Title"); + if (obj) + { + error = pdf_toutf8(&ctx->doctitle, obj); + if (error) fz_abort(error); + } + } + + error = pdf_loadpagetree(&ctx->pages, xref); + if (error) fz_abort(error); + + //count = pdf_getpagecount(ctx->pages); + + error = fz_newrenderer(&ctx->rast, pdf_devicergb, 0, 1024 * 512); + if (error) fz_abort(error); + + ctx->image = nil; + printf("hit bottom\n"); + + + CantGetPrivate: + return err; +} + +OSStatus view_showpage(HIViewRef view) +{ + OSStatus err; + viewctx *ctx; + + err = GetControlData(view, 1, kViewPrivate, 4, &ctx, NULL); + require_noerr(err, CantGetPrivate); + + fz_error *error; + fz_matrix ctm; + fz_rect bbox; + fz_obj *obj; + char s[256]; + + assert(ctx->pageno > 0 && ctx->pageno <= pdf_getpagecount(ctx->pages)); + + //XDefineCursor(xdpy, xwin, xcwait); + + if (ctx->image) + fz_droppixmap(ctx->image); + ctx->image = nil; + + obj = pdf_getpageobject(ctx->pages, ctx->pageno - 1); + if (obj == ctx->pageobj) + goto Lskipload; + ctx->pageobj = obj; + + if (ctx->page) + pdf_droppage(ctx->page); + + error = pdf_loadpage(&ctx->page, ctx->xref, ctx->pageobj); + if (error) + fz_abort(error); + +Lskipload: + + ctm = fz_identity(); + ctm = fz_concat(ctm, fz_translate(0, -ctx->page->mediabox.max.y)); + ctm = fz_concat(ctm, fz_scale(ctx->zoom, -ctx->zoom)); + ctm = fz_concat(ctm, fz_rotate(ctx->rotate + ctx->page->rotate)); + + bbox = fz_transformaabb(ctm, ctx->page->mediabox); + + error = fz_rendertree(&ctx->image, ctx->rast, ctx->page->tree, ctm, fz_roundrect(bbox), 1); + if (error) + fz_abort(error); + + //XDefineCursor(xdpy, xwin, xcarrow); + + { + char buf[512]; + int count = pdf_getpagecount(ctx->pages); + sprintf(buf, "%s - %d/%d", ctx->doctitle, ctx->pageno, count); + //xtitle(buf); + } + + //xresize(); + //xblit(); + CantGetPrivate: + return err; +} diff --git a/apps/pdfclean.c b/apps/pdfclean.c new file mode 100644 index 00000000..a55b94ef --- /dev/null +++ b/apps/pdfclean.c @@ -0,0 +1,173 @@ +#include <fitz.h> +#include <mupdf.h> + +void usage() +{ + fprintf(stderr, + "usage: pdfclean [options] infile.pdf outfile.pdf\n" + " -r\trebuild xref table\n" + " -g\tgarbage collect unused objects\n" + " -x\texpand compressed streams\n" + " -d -\tset user password for decryption\n" + " -e\tencrypt outfile\n" + " -u -\tset user password for encryption\n" + " -o -\tset owner password\n" + " -p -\tset permissions\n" + " -n -\tkey length in bits: 40 <= n <= 128\n" + ); + exit(1); +} + +void preloadobjstms(pdf_xref *xref) +{ + fz_error *error; + fz_obj *obj; + int i; + + for (i = 0; i < xref->len; i++) + { + if (xref->table[i].type == 'o') + { + error = pdf_loadobject(&obj, xref, i, 0); + if (error) fz_abort(error); + fz_dropobj(obj); + } + } +} + +void expandstreams(pdf_xref *xref) +{ + fz_error *error; + fz_obj *stmobj; + fz_buffer *buf; + fz_obj *stmlen; + int i, gen; + + for (i = 0; i < xref->len; i++) + { + if (xref->table[i].type == 'n') + { + gen = xref->table[i].gen; + + if (pdf_isstream(xref, i, gen)) + { + error = pdf_loadobject(&stmobj, xref, i, gen); + if (error) fz_abort(error); + + error = pdf_loadstream(&buf, xref, i, gen); + if (error) fz_abort(error); + + fz_dictdels(stmobj, "Filter"); + fz_dictdels(stmobj, "DecodeParms"); + + error = fz_newint(&stmlen, buf->wp - buf->rp); + if (error) fz_abort(error); + error = fz_dictputs(stmobj, "Length", stmlen); + if (error) fz_abort(error); + fz_dropobj(stmlen); + + pdf_updateobject(xref, i, gen, stmobj); + pdf_updatestream(xref, i, gen, buf); + + fz_dropobj(stmobj); + } + } + } +} + +int main(int argc, char **argv) +{ + fz_error *error; + char *infile; + char *outfile; + pdf_xref *xref; + int c; + + pdf_crypt *encrypt = 0; + int doencrypt = 0; + int dorepair = 0; + int doexpand = 0; + int dogc = 0; + + char *userpw = ""; + char *ownerpw = ""; + int perms = -4; /* 0xfffffffc */ + int keylen = 40; + char *password = ""; + + while ((c = getopt(argc, argv, "rgxd:eu:o:p:n:")) != -1) + { + switch (c) + { + case 'r': ++ dorepair; break; + case 'x': ++ doexpand; break; + case 'g': ++ dogc; break; + case 'e': ++ doencrypt; break; + case 'u': userpw = optarg; break; + case 'o': ownerpw = optarg; break; + case 'p': perms = atoi(optarg); break; + case 'n': keylen = atoi(optarg); break; + case 'd': password = optarg; break; + default: usage(); + } + } + + if (argc - optind < 2) + usage(); + + infile = argv[optind++]; + outfile = argv[optind++]; + + error = pdf_newxref(&xref); + + if (dorepair) + error = pdf_repairxref(xref, infile); + else + error = pdf_loadxref(xref, infile); + 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); + } + + if (doencrypt) + { + fz_obj *id = fz_dictgets(xref->trailer, "ID"); + if (!id) + fz_packobj(&id, "[(ABCDEFGHIJKLMNOP)(ABCDEFGHIJKLMNOP)]"); + else + fz_keepobj(id); + error = pdf_newencrypt(&encrypt, userpw, ownerpw, perms, keylen, id); + if (error) + fz_abort(error); + fz_dropobj(id); + } + + if (doexpand) + expandstreams(xref); + + if (dogc) + { + preloadobjstms(xref); + pdf_garbagecollect(xref); + } + + error = pdf_savexref(xref, outfile, encrypt); + if (error) + fz_abort(error); + + if (encrypt) + pdf_dropcrypt(encrypt); + + pdf_closexref(xref); + + return 0; +} + diff --git a/apps/pdfdebug.c b/apps/pdfdebug.c new file mode 100644 index 00000000..2a18ade7 --- /dev/null +++ b/apps/pdfdebug.c @@ -0,0 +1,188 @@ +#include <fitz.h> +#include <mupdf.h> + +static char *password = ""; +static int dodecode = 0; +static int doprintxref = 0; + +void usage() +{ + fprintf(stderr, "usage: pdfdebug [-dxs] [-u password] file.pdf [oid ...]\n"); + exit(1); +} + +/* + * Debug-print stream contents + */ + +static int safecol = 0; + +void printsafe(unsigned char *buf, int n) +{ + int i; + for (i = 0; i < n; i++) { + if (buf[i] == '\r' || buf[i] == '\n') { + printf("\n"); + safecol = 0; + } + else if (buf[i] < 32 || buf[i] > 126) { + printf("."); + safecol ++; + } + else { + printf("%c", buf[i]); + safecol ++; + } + if (safecol == 79) { + printf("\n"); + safecol = 0; + } + } +} + +void decodestream(pdf_xref *xref, int oid, int gid) +{ + fz_error *error; + unsigned char buf[512]; + + safecol = 0; + + error = pdf_openstream(xref, oid, gid); + if (error) fz_abort(error); + + while (1) + { + int n = fz_read(xref->stream, buf, sizeof buf); + if (n == 0) + break; + if (n < 0) + fz_abort(fz_ferror(xref->stream)); + printsafe(buf, n); + } + + pdf_closestream(xref); +} + +void copystream(pdf_xref *xref, int oid, int gid) +{ + fz_error *error; + unsigned char buf[512]; + + safecol = 0; + + error = pdf_openrawstream(xref, oid, gid); + if (error) fz_abort(error); + + while (1) + { + int n = fz_read(xref->stream, buf, sizeof buf); + if (n == 0) + break; + if (n < 0) + fz_abort(fz_ferror(xref->stream)); + printsafe(buf, n); + } + + pdf_closestream(xref); +} + +void printobject(pdf_xref *xref, int oid, int gid) +{ + fz_error *error; + fz_obj *obj; + + error = pdf_loadobject(&obj, xref, oid, gid); + if (error) fz_abort(error); + + printf("%d %d obj\n", oid, gid); + fz_debugobj(obj); + printf("\n"); + + if (xref->table[oid].stmofs) { + printf("stream\n"); + if (dodecode) + decodestream(xref, oid, gid); + else + copystream(xref, oid, gid); + printf("endstream\n"); + } + + printf("endobj\n"); + + fz_dropobj(obj); +} + +int main(int argc, char **argv) +{ + fz_error *error; + char *filename; + pdf_xref *xref; + int c; + + while ((c = getopt(argc, argv, "drxopu:")) != -1) + { + switch (c) + { + case 'd': + dodecode ++; + break; + case 'x': + doprintxref ++; + break; + case 'u': + password = optarg; + break; + default: + usage(); + } + } + + if (argc - optind == 0) + usage(); + + filename = argv[optind++]; + + error = pdf_newxref(&xref); + if (error) + fz_abort(error); + + error = pdf_loadxref(xref, filename); + if (error) + { + fz_warn("trying to repair"); + 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); + } + + if (optind == argc) + { + printf("trailer\n"); + fz_debugobj(xref->trailer); + printf("\n"); + } + + for ( ; optind < argc; optind++) + { + printobject(xref, atoi(argv[optind]), 0); + printf("\n"); + } + + if (doprintxref) + pdf_debugxref(xref); + + pdf_closexref(xref); + + return 0; +} + diff --git a/apps/pdffunction.c b/apps/pdffunction.c new file mode 100644 index 00000000..20dabf1e --- /dev/null +++ b/apps/pdffunction.c @@ -0,0 +1,263 @@ +#include <fitz.h> +#include <mupdf.h> + +static char *password = ""; +static int dodecode = 0; +static int dorepair = 0; +static int doprintxref = 0; + +typedef struct psobj_s psobj; + +struct pdf_function_s +{ + unsigned short type; /* 0=sample 2=exponential 3=stitching 4=postscript */ + int m; /* number of input values */ + int n; /* number of output values */ + float *domain; /* even index : min value, odd index : max value */ + float *range; /* even index : min value, odd index : max value */ + union + { + struct { + unsigned short bps; + unsigned short order; + int *size; /* the num of samples in each input dimension */ + float *encode; + float *decode; + int *samples; + } sa; + struct { + float n; + float *c0; + float *c1; + } e; + struct { + int k; + pdf_function **funcs; + float *bounds; + float *encode; + } st; + struct { + psobj *code; + int cap; + } p; + }u; +}; + +void usage() +{ + fprintf(stderr, "usage: pdffunction [-drxs] [-u password] file.pdf oid [input ...]\n"); + exit(1); +} + +static int safecol = 0; + +void printsafe(unsigned char *buf, int n) +{ + int i; + for (i = 0; i < n; i++) { + if (buf[i] == '\r' || buf[i] == '\n') { + printf("\n"); + safecol = 0; + } + else if (buf[i] < 32 || buf[i] > 126) { + printf("."); + safecol ++; + } + else { + printf("%c", buf[i]); + safecol ++; + } + if (safecol == 79) { + printf("\n"); + safecol = 0; + } + } +} + +void decodestream(pdf_xref *xref, int oid, int gid) +{ + fz_error *error; + unsigned char buf[512]; + + safecol = 0; + + error = pdf_openstream(xref, oid, gid); + if (error) fz_abort(error); + + while (1) + { + int n = fz_read(xref->stream, buf, sizeof buf); + if (n == 0) + break; + if (n < 0) + fz_abort(fz_ferror(xref->stream)); + printsafe(buf, n); + } + + pdf_closestream(xref); +} + +void copystream(pdf_xref *xref, int oid, int gid) +{ + fz_error *error; + unsigned char buf[512]; + + safecol = 0; + + error = pdf_openrawstream(xref, oid, gid); + if (error) fz_abort(error); + + while (1) + { + int n = fz_read(xref->stream, buf, sizeof buf); + if (n == 0) + break; + if (n < 0) + fz_abort(fz_ferror(xref->stream)); + printsafe(buf, n); + } + + pdf_closestream(xref); +} + +void printobject(pdf_xref *xref, int oid, int gid) +{ + fz_error *error; + fz_obj *obj; + + error = pdf_loadobject(&obj, xref, oid, gid); + if (error) fz_abort(error); + + printf("%d %d obj\n", oid, gid); + fz_debugobj(obj); + printf("\n"); + + if (xref->table[oid].stmofs) { + printf("stream\n"); + if (dodecode) + decodestream(xref, oid, gid); + else + copystream(xref, oid, gid); + printf("endstream\n"); + } + + printf("endobj\n"); + + fz_dropobj(obj); +} + +int main(int argc, char **argv) +{ + fz_error *error; + char *filename; + pdf_xref *xref; + int c; + + while ((c = getopt(argc, argv, "drxopu:")) != -1) + { + switch (c) + { + case 'd': + dodecode ++; + break; + case 'r': + dorepair ++; + break; + case 'x': + doprintxref ++; + break; + case 'u': + password = optarg; + break; + default: + usage(); + } + } + + if (argc - optind == 0) + usage(); + + filename = argv[optind++]; + + if (dorepair) + error = pdf_repairpdf(&xref, filename); + else + error = pdf_openpdf(&xref, filename); + if (error) + fz_abort(error); + + error = pdf_decryptpdf(xref); + if (error) + fz_abort(error); + + if (xref->crypt) + { + error = pdf_setpassword(xref->crypt, password); + if (error) fz_abort(error); + } + + if (optind == argc) + { + printf("trailer\n"); + fz_debugobj(xref->trailer); + printf("\n"); + } + else + { + int oid = atoi(argv[optind++]); + if(optind == argc) + printobject(xref, oid, 0); + else + { + float *in = nil, *out = nil; + pdf_function *func; + fz_obj *funcobj; + int i; + + /* type 0 and type 4 funcs must be indirect to read stream */ + error = fz_newindirect(&funcobj,oid,0); + if(error) fz_abort(error); + error = pdf_loadfunction(&func,xref,funcobj); + if(error) fz_abort(error); + in = fz_malloc(func->m * sizeof(float)); + out = fz_malloc(func->n * sizeof(float)); + + if(!in || !out) + fz_abort(fz_outofmem); + + for(i = 0; optind < argc; optind++, i++) + { + if(i >= func->m) + fz_abort(fz_throw("too much input values")); + + in[i] = atof(argv[optind]); + } + + if(i < func->m) + fz_abort(fz_throw("too few input values")); + + error = pdf_evalfunction(func, in, func->m, out, func->n); + if(error) fz_abort(error); + + for(i = 0; i < func->n; ++i) + fprintf(stderr, "output[%d] : %f\n", i, out[i]); + + fz_dropobj(funcobj); + pdf_dropfunction(func); + } + } + + for ( ; optind < argc; optind++) + { + printobject(xref, atoi(argv[optind]), 0); + printf("\n"); + } + + if (doprintxref) + pdf_debugpdf(xref); + + pdf_closepdf(xref); + + return 0; +} + diff --git a/apps/pdfmerge.c b/apps/pdfmerge.c new file mode 100644 index 00000000..3bb7c868 --- /dev/null +++ b/apps/pdfmerge.c @@ -0,0 +1,230 @@ +#include <fitz.h> +#include <mupdf.h> + +void usage() +{ + fprintf(stderr, + "usage: pdfmerge [options] file.pdf pages ...\n" + " -o -\toutput file name (default out.pdf)\n" + " -d -\tset user password for decryption\n" + " -e\tencrypt outfile\n" + " -U -\tset user password for encryption\n" + " -O -\tset owner password\n" + " -P -\tset permissions\n" + " -N -\tkey length in bits: 40 <= n <= 128\n" + ); + exit(1); +} + +int main(int argc, char **argv) +{ + fz_error *error; + char *savename = "out.pdf"; + pdf_pagetree *srcpages; + fz_obj *srcrefs; + fz_obj *newsrcrefs; + fz_obj *dstrefs; + pdf_xref *dst; + pdf_xref *src; + int rootoid; + int rootgid; + int pagesoid; + int pagesgid; + fz_obj *pagesref; + fz_obj *obj; + int i, k; + int c; + + pdf_crypt *encrypt = 0; + int doencrypt = 0; + + char *userpw = ""; + char *ownerpw = ""; + int perms = -4; /* 0xfffffffc */ + int keylen = 40; + char *password = ""; + + while ((c = getopt(argc, argv, "reo:U:O:P:N:")) != -1) + { + switch (c) + { + case 'e': ++ doencrypt; break; + case 'o': savename = optarg; break; + case 'U': userpw = optarg; break; + case 'O': ownerpw = optarg; break; + case 'P': perms = atoi(optarg); break; + case 'N': keylen = atoi(optarg); break; + case 'd': password = optarg; break; + default: usage(); + } + } + + if (argc - optind < 1) + usage(); + + /* + * Create new blank xref table + */ + + error = pdf_newpdf(&dst); + if (error) + fz_abort(error); + + error = fz_newarray(&dstrefs, 100); + if (error) + fz_abort(error); + + /* + * Copy pages saving refs in dstrefs + */ + + for (i = optind; i < argc; i++) + { + error = pdf_openpdf(&src, argv[i]); + if (error) + fz_abort(error); + + error = pdf_decryptpdf(src); + if (error) + fz_abort(error); + + if (src->crypt) + { + error = pdf_setpassword(src->crypt, password); + if (error) + fz_abort(error); + } + + error = pdf_loadpagetree(&srcpages, src); + if (error) + fz_abort(error); + + error = fz_newarray(&srcrefs, 100); + if (error) + fz_abort(error); + + for (k = 0; k < srcpages->count; k++) + { + fz_dictdels(srcpages->pobj[k], "Parent"); + fz_dictdels(srcpages->pobj[k], "B"); + fz_dictdels(srcpages->pobj[k], "PieceInfo"); + fz_dictdels(srcpages->pobj[k], "Metadata"); + fz_dictdels(srcpages->pobj[k], "Annots"); + fz_dictdels(srcpages->pobj[k], "Tabs"); + + pdf_updateobject(src, + fz_tonum(srcpages->pref[k]), + fz_togen(srcpages->pref[k]), + srcpages->pobj[k]); + error = fz_arraypush(srcrefs, srcpages->pref[k]); + if (error) + fz_abort(error); + } + + error = pdf_transplant(dst, src, &newsrcrefs, srcrefs); + if (error) + fz_abort(error); + + for (k = 0; k < fz_arraylen(newsrcrefs); k++) + { + error = fz_arraypush(dstrefs, fz_arrayget(newsrcrefs, k)); + if (error) + fz_abort(error); + } + + fz_dropobj(srcrefs); + fz_dropobj(newsrcrefs); + + pdf_droppagetree(srcpages); + + pdf_closepdf(src); + } + + /* + * Create and relink Pages object + */ + + error = pdf_allocobject(dst, &pagesoid, &pagesgid); + if (error) + fz_abort(error); + + error = fz_packobj(&obj, + "<</Type/Pages/Count %i/Kids %o>>", + fz_arraylen(dstrefs), + dstrefs); + if (error) + fz_abort(error); + + pdf_updateobject(dst, pagesoid, pagesgid, obj); + + fz_dropobj(obj); + + error = fz_newindirect(&pagesref, pagesoid, pagesgid); + if (error) + fz_abort(error); + + for (i = 0; i < fz_arraylen(dstrefs); i++) + { + int oid = fz_tonum(fz_arrayget(dstrefs, i)); + int gid = fz_togen(fz_arrayget(dstrefs, i)); + error = pdf_loadobject(&obj, dst, oid, gid); + if (error) + fz_abort(error); + error = fz_dictputs(obj, "Parent", pagesref); + if (error) + fz_abort(error); + pdf_updateobject(dst, oid, gid, obj); + fz_dropobj(obj); + } + + fz_dropobj(pagesref); + + /* + * Create Catalog and trailer + */ + + error = pdf_allocobject(dst, &rootoid, &rootgid); + if (error) + fz_abort(error); + + error = fz_packobj(&obj, + "<</Type/Catalog/Pages %r>>", + pagesoid, pagesgid); + if (error) + fz_abort(error); + + pdf_updateobject(dst, rootoid, rootgid, obj); + + fz_dropobj(obj); + + error = fz_packobj(&dst->trailer, "<</Root %r>>", rootoid, rootgid); + if (error) + fz_abort(error); + + /* + * Write out the new PDF + */ + + if (doencrypt) + { + fz_obj *id = fz_dictgets(dst->trailer, "ID"); + if (!id) + fz_packobj(&id, "[(ABCDEFGHIJKLMNOP)(ABCDEFGHIJKLMNOP)]"); + else + fz_keepobj(id); + error = pdf_newencrypt(&encrypt, userpw, ownerpw, perms, keylen, id); + if (error) + fz_abort(error); + fz_dropobj(id); + } + + error = pdf_savepdf(dst, savename, encrypt); + if (error) + fz_abort(error); + + fz_dropobj(dstrefs); + pdf_closepdf(dst); + + return 0; +} + diff --git a/apps/pdfrip.c b/apps/pdfrip.c new file mode 100644 index 00000000..b6d2b9d8 --- /dev/null +++ b/apps/pdfrip.c @@ -0,0 +1,182 @@ +#include <fitz.h> +#include <mupdf.h> + +int showtree = 0; +float zoom = 1.0; +char *namefmt = "out-%02d.ppm"; +fz_renderer *gc; +int nbands = 1; + +void usage() +{ + fprintf(stderr, "usage: pdfrip [-d] [-b bands] [-o out-%%02d.ppm] [-p password] [-z zoom] file.pdf pages...\n"); + exit(1); +} + +/* + * Draw page + */ + +void showpage(pdf_xref *xref, fz_obj *pageobj, int pagenum) +{ + fz_error *error; + pdf_page *page; + char namebuf[256]; + fz_pixmap *pix; + fz_matrix ctm; + fz_irect bbox; + fz_irect band; + FILE *f; + int x, y; + int w, h; + int b; + + sprintf(namebuf, namefmt, pagenum); + + error = pdf_loadpage(&page, xref, pageobj); + if (error) + fz_abort(error); + + if (showtree) + { + fz_debugobj(pageobj); + printf("\n"); + + printf("page\n"); + printf(" mediabox [ %g %g %g %g ]\n", + page->mediabox.min.x, page->mediabox.min.y, + page->mediabox.max.x, page->mediabox.max.y); + printf(" rotate %d\n", page->rotate); + + printf(" resources\n"); + fz_debugobj(page->resources); + printf("\n"); + + printf("tree\n"); + fz_debugtree(page->tree); + printf("endtree\n"); + } + + ctm = fz_concat(fz_translate(0, -page->mediabox.max.y), + fz_scale(zoom, -zoom)); + + bbox = fz_roundrect(page->mediabox); + bbox.min.x = bbox.min.x * zoom; + bbox.min.y = bbox.min.y * zoom; + bbox.max.x = bbox.max.x * zoom; + bbox.max.y = bbox.max.y * zoom; + w = bbox.max.x - bbox.min.x; + h = bbox.max.y - bbox.min.y; + + f = fopen(namebuf, "wb"); + fprintf(f, "P6\n%d %d\n255\n", w, h); + + for (b = 0; b < nbands; b++) + { + printf("band %d / %d\n", b, nbands); + + band.min.x = bbox.min.x; + band.max.x = bbox.max.x; + band.min.y = bbox.min.y + (h * b) / nbands; + band.max.y = bbox.min.y + (h * (b + 1)) / nbands; + + error = fz_rendertree(&pix, gc, page->tree, ctm, band, 1); + if (error) + fz_abort(error); + + for (y = 0; y < pix->h; y++) + { + for (x = 0; x < pix->w; x++) + { + putc(pix->samples[y * pix->w * 4 + x * 4 + 1], f); + putc(pix->samples[y * pix->w * 4 + x * 4 + 2], f); + putc(pix->samples[y * pix->w * 4 + x * 4 + 3], f); + } + } + + fz_droppixmap(pix); + } + + fclose(f); + +} + +int main(int argc, char **argv) +{ + fz_error *error; + char *filename; + pdf_xref *xref; + pdf_pagetree *pages; + int c; + + char *password = ""; + + fz_cpudetect(); + fz_accelerate(); + + while ((c = getopt(argc, argv, "dz:p:o:b:")) != -1) + { + switch (c) + { + case 'p': password = optarg; break; + case 'z': zoom = atof(optarg); break; + case 'd': ++showtree; break; + case 'o': namefmt = optarg; break; + case 'b': nbands = atoi(optarg); break; + default: usage(); + } + } + + if (argc - optind == 0) + usage(); + + filename = argv[optind++]; + + error = pdf_newxref(&xref); + if (error) + fz_abort(error); + + error = pdf_loadxref(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); + } + + error = pdf_loadpagetree(&pages, xref); + if (error) + fz_abort(error); + + if (optind == argc) + { + printf("number of pages: %d\n", pdf_getpagecount(pages)); + } + + error = fz_newrenderer(&gc, pdf_devicergb, 0, 1024 * 512); + if (error) + fz_abort(error); + + for ( ; optind < argc; optind++) + { + int page = atoi(argv[optind]); + if (page < 1 || page > pdf_getpagecount(pages)) + fprintf(stderr, "page out of bounds: %d\n", page); + printf("page %d\n", page); + showpage(xref, pdf_getpageobject(pages, page - 1), page); + } + + fz_droprenderer(gc); + + pdf_closexref(xref); + + return 0; +} + diff --git a/apps/showcmap.c b/apps/showcmap.c new file mode 100644 index 00000000..3e6e5e8e --- /dev/null +++ b/apps/showcmap.c @@ -0,0 +1,22 @@ +#include <fitz.h> +#include <mupdf.h> + +int main(int argc, char **argv) +{ + fz_error *err; + fz_cmap *cmap; + fz_file *file; + + err = fz_openfile(&file, argv[1], FZ_READ); + if (err) + fz_abort(err); + + err = pdf_parsecmap(&cmap, file); + if (err) + fz_abort(err); + + fz_debugcmap(cmap); + + return 0; +} + diff --git a/apps/w32pdf.c b/apps/w32pdf.c new file mode 100644 index 00000000..33fb589c --- /dev/null +++ b/apps/w32pdf.c @@ -0,0 +1,656 @@ +#include <fitz.h> +#include <mupdf.h> + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +static HWND hwnd; +static HDC hdc; +static BITMAPINFO *dibinf; +static TCHAR szAppName[] = TEXT("mupdf"); +static HCURSOR arrowcurs, handcurs, waitcurs; +static LRESULT CALLBACK winproc(HWND, UINT, WPARAM, LPARAM); + +static char *doctitle = "<untitled>"; +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: w32pdf [-b] [-pzr page/zoom/rotate] [-u password] file.pdf\n"); + exit(1); +} + +void winabort(const char *msg) +{ + MessageBoxA(NULL, msg, "Fatal error", MB_ICONERROR); + abort(); +} + +void winopen() +{ + WNDCLASS wc; + + /* Create and register window class */ + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wc.lpfnWndProc = winproc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = 0; //hInstance; + wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); + wc.hCursor = NULL; //LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = NULL;//(HBRUSH) GetStockObject(BLACK_BRUSH); + wc.lpszMenuName = NULL; + wc.lpszClassName = szAppName; + assert(RegisterClass(&wc) && "Register window class"); + + /* Create cursors */ + arrowcurs = LoadCursor(NULL, IDC_ARROW); + handcurs = LoadCursor(NULL, IDC_HAND); + waitcurs = LoadCursor(NULL, IDC_WAIT); + + /* Init DIB info for buffer */ + dibinf = malloc(sizeof(BITMAPINFO) + 12); + assert(dibinf != NULL); + dibinf->bmiHeader.biSize = sizeof(dibinf->bmiHeader); + dibinf->bmiHeader.biPlanes = 1; + dibinf->bmiHeader.biBitCount = 24; + dibinf->bmiHeader.biCompression = BI_RGB; + dibinf->bmiHeader.biXPelsPerMeter = 2834; + dibinf->bmiHeader.biYPelsPerMeter = 2834; + dibinf->bmiHeader.biClrUsed = 0; + dibinf->bmiHeader.biClrImportant = 0; + dibinf->bmiHeader.biClrUsed = 0; + + /* Create window */ + hwnd = CreateWindow(szAppName, // window class name + NULL, // window caption + //WS_OVERLAPPEDWINDOW, // window style + WS_CAPTION|WS_THICKFRAME|WS_SYSMENU|WS_MINIMIZEBOX, + CW_USEDEFAULT, // initial x position + 5, // CW_USEDEFAULT, // initial y position + 300, // initial x size + 300, // initial y size + NULL, // parent window handle + NULL, // window menu handle + 0,//hInstance, // program instance handle + NULL); // creation parameters + + hdc = NULL; + + SetWindowTextA(hwnd, "MuPDF"); + + SetCursor(arrowcurs); +} + +void winblit() +{ + /* TODO: repack image in windows format */ + int stride = ((image->w * 3 + 3) / 4) * 4; + char *buf; + char *s; + char *p; + int y, x; + + buf = fz_malloc(image->h * stride); + if (!buf) + return; + + for (y = 0; y < image->h; y++) + { + p = buf + y * stride; + s = image->samples + y * image->w * 4; + for (x = 0; x < image->w; x++) + { + p[x * 3 + 0] = s[x * 4 + 3]; + p[x * 3 + 1] = s[x * 4 + 2]; + p[x * 3 + 2] = s[x * 4 + 1]; + } + } + + dibinf->bmiHeader.biWidth = image->w; + dibinf->bmiHeader.biHeight = -image->h; + dibinf->bmiHeader.biSizeImage = image->h * stride; + + assert(hdc != NULL); + SetDIBitsToDevice(hdc, + 0, /* destx */ + 0, /* desty */ + image->w, /* destw */ + image->h, /* desth */ + 0, /* srcx */ + 0, /* srcy */ + 0, /* startscan */ + image->h, /* numscans */ + buf, /* pBits */ + dibinf, /* pInfo */ + DIB_RGB_COLORS /* color use flag */ + ); + + fz_free(buf); +} + +void winresize(int w, int h) +{ + ShowWindow(hwnd, SW_SHOWDEFAULT); + w += GetSystemMetrics(SM_CXFRAME) * 2; + h += GetSystemMetrics(SM_CYFRAME) * 2; + h += GetSystemMetrics(SM_CYCAPTION); + SetWindowPos(hwnd, 0, 0, 0, w, h, SWP_NOZORDER | SWP_NOMOVE); +} + +void winrepaint(void) +{ + RECT wr = (RECT){0, 0, image->w, image->h}; + InvalidateRect(hwnd, &wr, 0); +} + +void dragndrop() +{ + SetCapture(hwnd); + ReleaseCapture(); +} + +static void showpage(void) +{ + fz_error *error; + fz_matrix ctm; + fz_rect bbox; + fz_obj *obj; + + assert(pageno > 0 && pageno <= pdf_getpagecount(pages)); + + SetCursor(waitcurs); + + 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); + + error = pdf_loadpage(&page, xref, pageobj); + if (error) + fz_abort(error); + +Lskipload: + + 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); + + SetCursor(arrowcurs); + { + char buf[512]; + sprintf(buf, "%s - %d/%d", doctitle, pageno, count); + SetWindowTextA(hwnd, buf); + } + + winresize(image->w, image->h); + winrepaint(); +} + +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 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 + sprintf(cmd, "start %s", buf); + 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 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(int 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) + { + SetCursor(handcurs); + if (btn) + { + if (fz_isstring(link->dest)) + gotouri(link->dest); + if (fz_isindirect(link->dest)) + gotopage(link->dest); + } + } + else + { + SetCursor(arrowcurs); + } +} + +LRESULT CALLBACK +winproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int x = (signed short) LOWORD(lParam); + int y = (signed short) HIWORD(lParam); + int key; + + switch (message) + { + case WM_CREATE: + //puts("WM_CREATE"); + return 0; + + case WM_DESTROY: + //puts("WM_DESTROY"); + PostQuitMessage(0); + return 0; + + case WM_CLOSE: + //puts("WM_CLOSE"); + PostQuitMessage(0); + return 0; + + case WM_KILLFOCUS: + //puts("WM_KILLFOCUS"); + return 0; + + case WM_SIZING: + //puts("WM_SIZING"); + return 0; + + /* Paint events are low priority and automagically catenated + * so we don't need to do any fancy waiting to defer repainting. + */ + case WM_PAINT: + { + //puts("WM_PAINT"); + PAINTSTRUCT ps; + hdc = BeginPaint(hwnd, &ps); + winblit(); + hdc = NULL; + EndPaint(hwnd, &ps); + return 0; + } + + /* Mouse events */ + + case WM_LBUTTONDOWN: + //puts("WM_LBUTTONDOWN"); + handlemouse(x, y, 1); + return 0; + case WM_RBUTTONDOWN: + //puts("WM_RBUTTONDOWN"); + handlemouse(x, y, 4); + return 0; + case WM_MBUTTONDOWN: + //puts("WM_MBUTTONDOWN"); + handlemouse(x, y, 2); + return 0; + + case WM_LBUTTONUP: + //puts("WM_LBUTTONUP"); + handlemouse(x, y, 0); + return 0; + case WM_RBUTTONUP: + //puts("WM_RBUTTONUP"); + handlemouse(x, y, 0); + return 0; + case WM_MBUTTONUP: + //puts("WM_RBUTTONUP"); + handlemouse(x, y, 0); + return 0; + + case WM_MOUSEMOVE: + //puts("WM_MOUSEMOVE"); + handlemouse(x, y, 0); + return 0; + + /* Mouse wheel */ + case WM_MOUSEWHEEL: + if ((signed short)HIWORD(wParam) > 0) { + // wheel-up + } + else { + // wheel-down + } + return 0; + + /* Keyboard events */ + + /* Only deal with key-down */ + case WM_KEYUP: + return 0; + case WM_SYSKEYUP: + return 0; + + case WM_SYSCHAR: + //printf("WM_SYSCHAR: %d '%c'\n", wParam, wParam); + return 0; + + case WM_SYSKEYDOWN: + //printf("WM_SYSKEYDOWN: %d '%c'\n", wParam, wParam); + return 0; + + case WM_KEYDOWN: + //printf("WM_KEYDOWN: %d '%c'\n", wParam, wParam); + return 0; + + /* unicode encoded chars, including escape, backspace etc... */ + case WM_CHAR: + //printf("WM_CHAR: %d '%c'\n", wParam, wParam); + key = wParam; + handlekey(key); + return 0; + } + + fflush(stdout); + + /* Pass on unhandled events to Windows */ + return DefWindowProc(hwnd, message, wParam, lParam); +} + +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++]; + + winopen(); + pdfopen(filename, password); + showpage(); + + if (benchmark) + { + while (pageno < count) + { + pageno ++; + showpage(); + } + return 0; + } + + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + pdf_closexref(xref); + + return 0; +} + diff --git a/apps/x11pdf.c b/apps/x11pdf.c new file mode 100644 index 00000000..e35f90a6 --- /dev/null +++ b/apps/x11pdf.c @@ -0,0 +1,590 @@ +#include <fitz.h> +#include <mupdf.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> +#include <X11/Intrinsic.h> +#include <X11/cursorfont.h> +#include <X11/keysym.h> + +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 = "<untitled>"; + +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; +} + diff --git a/apps/ximage.c b/apps/ximage.c new file mode 100644 index 00000000..9f7ec064 --- /dev/null +++ b/apps/ximage.c @@ -0,0 +1,666 @@ +/* + * Blit ARGB images to X with X(Shm)Images + */ + +#include <fitz.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <X11/extensions/XShm.h> + +typedef void (*ximage_convert_func_t) + ( + const unsigned char *src, + int srcstride, + unsigned char *dst, + int dststride, + int w, + int h + ); + +#define POOLSIZE 4 +#define WIDTH 256 +#define HEIGHT 256 + +enum { + ARGB8888, + BGRA8888, + RGBA8888, + ABGR8888, + RGB888, + BGR888, + RGB565, + RGB565_BR, + RGB555, + RGB555_BR, + BGR233, + UNKNOWN +}; + +static char *modename[] = { + "ARGB8888", + "BGRA8888", + "RGBA8888", + "ABGR8888", + "RGB888", + "BGR888", + "RGB565", + "RGB565_BR", + "RGB555", + "RGB555_BR", + "BGR233", + "UNKNOWN" +}; + +extern ximage_convert_func_t ximage_convert_funcs[]; + +static struct +{ + Display *display; + int screen; + XVisualInfo visual; + Colormap colormap; + + int bitsperpixel; + int mode; + + XColor rgbcube[256]; + + ximage_convert_func_t convert_func; + + int useshm; + XImage *pool[POOLSIZE]; + /* MUST exist during the lifetime of the shared ximage according to the + xc/doc/hardcopy/Xext/mit-shm.PS.gz */ + XShmSegmentInfo shminfo[POOLSIZE]; + int lastused; +} info; + +static XImage * +createximage(Display *dpy, Visual *vis, XShmSegmentInfo *xsi, int depth, int w, int h) +{ + XImage *img; + Status status; + + if (!XShmQueryExtension(dpy)) goto fallback; + + img = XShmCreateImage(dpy, vis, depth, ZPixmap, nil, xsi, w, h); + if (!img) + { + fprintf(stderr, "warn: could not XShmCreateImage\n"); + goto fallback; + } + + xsi->shmid = shmget(IPC_PRIVATE, + img->bytes_per_line * img->height, + IPC_CREAT | 0777); + if (xsi->shmid < 0) + { + XDestroyImage(img); + fprintf(stderr, "warn: could not shmget\n"); + goto fallback; + } + + img->data = xsi->shmaddr = shmat(xsi->shmid, 0, 0); + if (img->data == (char*)-1) + { + XDestroyImage(img); + fprintf(stderr, "warn: could not shmat\n"); + goto fallback; + } + + xsi->readOnly = False; + status = XShmAttach(dpy, xsi); + if (!status) + { + shmdt(xsi->shmaddr); + XDestroyImage(img); + fprintf(stderr, "warn: could not XShmAttach\n"); + goto fallback; + } + + XSync(dpy, False); + + shmctl(xsi->shmid, IPC_RMID, 0); + + return img; + +fallback: + info.useshm = 0; + + img = XCreateImage(dpy, vis, depth, ZPixmap, 0, 0, w, h, 32, 0); + if (!img) + { + fprintf(stderr, "fail: could not XCreateImage"); + abort(); + } + + img->data = malloc(h * img->bytes_per_line); + if (!img->data) + { + fprintf(stderr, "fail: could not malloc"); + abort(); + } + + return img; +} + +static void +make_colormap(void) +{ + if (info.visual.class == PseudoColor && info.visual.depth == 8) + { + int i, r, g, b; + i = 0; + for (b = 0; b < 4; b++) { + for (g = 0; g < 8; g++) { + for (r = 0; r < 8; r++) { + info.rgbcube[i].pixel = i; + info.rgbcube[i].red = (r * 36) << 8; + info.rgbcube[i].green = (g * 36) << 8; + info.rgbcube[i].blue = (b * 85) << 8; + info.rgbcube[i].flags = + DoRed | DoGreen | DoBlue; + i++; + } + } + } + info.colormap = XCreateColormap(info.display, + RootWindow(info.display, info.screen), + info.visual.visual, + AllocAll); + XStoreColors(info.display, info.colormap, info.rgbcube, 256); + return; + } + else if (info.visual.class == TrueColor) + { + info.colormap = 0; + return; + } + fprintf(stderr, "Cannot handle visual class %d with depth: %d\n", + info.visual.class, info.visual.depth); + return; +} + +static void +select_mode(void) +{ + + int byteorder; + int byterev; + unsigned long rm, gm, bm; + unsigned long rs, gs, bs; + + byteorder = ImageByteOrder(info.display); +#if BYTE_ORDER == BIG_ENDIAN + byterev = byteorder != MSBFirst; +#else + byterev = byteorder != LSBFirst; +#endif + + rm = info.visual.red_mask; + gm = info.visual.green_mask; + bm = info.visual.blue_mask; + + rs = ffs(rm) - 1; + gs = ffs(gm) - 1; + bs = ffs(bm) - 1; + + printf("ximage: mode %d/%d %08lx %08lx %08lx (%ld,%ld,%ld) %s%s\n", + info.visual.depth, + info.bitsperpixel, + rm, gm, bm, rs, gs, bs, + byteorder == MSBFirst ? "msb" : "lsb", + byterev ? " <swap>":""); + + info.mode = UNKNOWN; + if (info.bitsperpixel == 8) { + /* Either PseudoColor with BGR233 colormap, or TrueColor */ + info.mode = BGR233; + } + else if (info.bitsperpixel == 16) { + if (rm == 0xF800 && gm == 0x07E0 && bm == 0x001F) + info.mode = !byterev ? RGB565 : RGB565_BR; + if (rm == 0x7C00 && gm == 0x03E0 && bm == 0x001F) + info.mode = !byterev ? RGB555 : RGB555_BR; + } + else if (info.bitsperpixel == 24) { + if (rs == 0 && gs == 8 && bs == 16) + info.mode = byteorder == MSBFirst ? RGB888 : BGR888; + if (rs == 16 && gs == 8 && bs == 0) + info.mode = byteorder == MSBFirst ? BGR888 : RGB888; + } + else if (info.bitsperpixel == 32) { + if (rs == 0 && gs == 8 && bs == 16) + info.mode = byteorder == MSBFirst ? ABGR8888 : RGBA8888; + if (rs == 8 && gs == 16 && bs == 24) + info.mode = byteorder == MSBFirst ? BGRA8888 : ARGB8888; + if (rs == 16 && gs == 8 && bs == 0) + info.mode = byteorder == MSBFirst ? ARGB8888 : BGRA8888; + if (rs == 24 && gs == 16 && bs == 8) + info.mode = byteorder == MSBFirst ? RGBA8888 : ABGR8888; + } + + printf("ximage: ARGB8888 to %s\n", modename[info.mode]); + + /* select conversion function */ + info.convert_func = ximage_convert_funcs[info.mode]; +} + +static int +create_pool(void) +{ + int i; + + info.lastused = 0; + + for (i = 0; i < POOLSIZE; i++) { + info.pool[i] = nil; + } + + for (i = 0; i < POOLSIZE; i++) { + info.pool[i] = createximage(info.display, + info.visual.visual, &info.shminfo[i], info.visual.depth, + WIDTH, HEIGHT); + if (info.pool[i] == nil) { + return 0; + } + } + + return 1; +} + +static XImage * +next_pool_image(void) +{ + if (info.lastused + 1 >= POOLSIZE) { + if (info.useshm) + XSync(info.display, False); + else + XFlush(info.display); + info.lastused = 0; + } + return info.pool[info.lastused ++]; +} + +int +ximage_init(Display *display, int screen, Visual *visual) +{ + XVisualInfo template; + XVisualInfo *visuals; + int nvisuals; + XPixmapFormatValues *formats; + int nformats; + int ok; + int i; + + info.display = display; + info.screen = screen; + info.colormap = 0; + + /* Get XVisualInfo for this visual */ + template.visualid = XVisualIDFromVisual(visual); + visuals = XGetVisualInfo(display, VisualIDMask, &template, &nvisuals); + if (nvisuals != 1) { + fprintf(stderr, "Visual not found!\n"); + XFree(visuals); + return 0; + } + memcpy(&info.visual, visuals, sizeof (XVisualInfo)); + XFree(visuals); + + /* Get appropriate PixmapFormat for this visual */ + formats = XListPixmapFormats(info.display, &nformats); + for (i = 0; i < nformats; i++) { + if (formats[i].depth == info.visual.depth) { + info.bitsperpixel = formats[i].bits_per_pixel; + break; + } + } + XFree(formats); + if (i == nformats) { + fprintf(stderr, "PixmapFormat not found!\n"); + return 0; + } + + /* extract mode */ + select_mode(); + + /* prepare colormap */ + make_colormap(); + + /* prepare pool of XImages */ + info.useshm = 1; + ok = create_pool(); + if (!ok) + return 0; + + printf("ximage: %sPutImage\n", info.useshm ? "XShm" : "X"); + + return 1; +} + +int +ximage_get_depth(void) +{ + return info.visual.depth; +} + +Visual * +ximage_get_visual(void) +{ + return info.visual.visual; +} + +Colormap +ximage_get_colormap(void) +{ + return info.colormap; +} + +void +ximage_blit(Drawable d, GC gc, + int dstx, int dsty, + unsigned char *srcdata, + int srcx, int srcy, + int srcw, int srch, + int srcstride) +{ + XImage *image; + int ax, ay; + int w, h; + unsigned char *srcptr; + + for (ay = 0; ay < srch; ay += HEIGHT) + { + h = MIN(srch - ay, HEIGHT); + for (ax = 0; ax < srcw; ax += WIDTH) + { + w = MIN(srcw - ax, WIDTH); + + image = next_pool_image(); + + srcptr = srcdata + + (ay + srcy) * srcstride + + (ax + srcx) * 4; + + info.convert_func(srcptr, srcstride, + image->data, + image->bytes_per_line, w, h); + + if (info.useshm) + { + XShmPutImage(info.display, d, gc, image, + 0, 0, dstx + ax, dsty + ay, + w, h, False); + } + else + { + XPutImage(info.display, d, gc, image, + 0, 0, + dstx + ax, + dsty + ay, + w, h); + } + } + } +} + +/* + * + */ +#ifndef _C99 +#ifdef __GNUC__ +#define restrict __restrict__ +#else +#define restrict +#endif +#endif + +#define PARAMS \ + const unsigned char * restrict src, \ + int srcstride, \ + unsigned char * restrict dst, \ + int dststride, \ + int w, \ + int h + +/* + * Convert byte:ARGB8888 to various formats + */ + +static void +ximage_convert_argb8888(PARAMS) +{ + int x, y; + unsigned *s = (unsigned *)src; + unsigned *d = (unsigned *)dst; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + d[x] = s[x]; + } + d += dststride>>2; + s += srcstride>>2; + } +} + +static void +ximage_convert_bgra8888(PARAMS) +{ + int x, y; + unsigned *s = (unsigned *)src; + unsigned *d = (unsigned *)dst; + unsigned val; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + val = s[x]; + d[x] = + (val >> 24) | + ((val >> 8) & 0xff00) | + (val << 24) | + ((val << 8) & 0xff0000); +/* + d[x] = + (((val >> 24) & 0xff) << 0) | + (((val >> 16) & 0xff) << 8) | + (((val >> 8) & 0xff) << 16) | + (((val >> 0) & 0xff) << 24); +*/ + } + d += dststride>>2; + s += srcstride>>2; + } +} + +/* following have yet to recieve some MMX love ;-) */ + +static void +ximage_convert_abgr8888(PARAMS) +{ + int x, y; + unsigned *s = (unsigned *)src; + unsigned *d = (unsigned *)dst; + unsigned val; + + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + val = s[x]; +#if 1 /* FZ_MSB */ + d[x] = (val & 0xff00ff00) | + (((val << 16) | (val >> 16)) & 0x00ff00ff); +#else /* FZ_LSB */ + d[x] = (val << 24) | ((val >> 8) & 0xff); +#endif + } + d += dststride>>2; + s += srcstride>>2; + } +} + +static void +ximage_convert_rgba8888(PARAMS) +{ + int x, y; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + dst[x * 4 + 0] = src[x * 4 + 1]; + dst[x * 4 + 1] = src[x * 4 + 2]; + dst[x * 4 + 2] = src[x * 4 + 3]; + dst[x * 4 + 3] = src[x * 4 + 0]; + } + dst += dststride; + src += srcstride; + } +} + +static void +ximage_convert_bgr888(PARAMS) +{ + int x, y; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + dst[3*x + 0] = src[4*x + 3]; + dst[3*x + 1] = src[4*x + 2]; + dst[3*x + 2] = src[4*x + 1]; + } + src += srcstride; + dst += dststride; + } +} + +static void +ximage_convert_rgb888(PARAMS) +{ + int x, y; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + dst[3*x + 0] = src[4*x + 1]; + dst[3*x + 1] = src[4*x + 2]; + dst[3*x + 2] = src[4*x + 3]; + } + src += srcstride; + dst += dststride; + } +} + +static void +ximage_convert_rgb565(PARAMS) +{ + unsigned char r, g, b; + int x, y; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + r = src[4*x + 1]; + g = src[4*x + 2]; + b = src[4*x + 3]; + ((unsigned short *)dst)[x] = + ((r & 0xF8) << 8) | + ((g & 0xFC) << 3) | + (b >> 3); + } + src += srcstride; + dst += dststride; + } +} + +static void +ximage_convert_rgb565_br(PARAMS) +{ + unsigned char r, g, b; + int x, y; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + r = src[4*x + 1]; + g = src[4*x + 2]; + b = src[4*x + 3]; + /* final word is: + g4 g3 g2 b7 b6 b5 b4 b3 r7 r6 r5 r4 r3 g7 g6 g5 + */ + ((unsigned short *)dst)[x] = + (r & 0xF8) | + ((g & 0xE0) >> 5) | + ((g & 0x1C) << 11) | + ((b & 0xF8) << 5); + } + src += srcstride; + dst += dststride; + } +} + +static void +ximage_convert_rgb555(PARAMS) +{ + unsigned char r, g, b; + int x, y; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + r = src[4*x + 1]; + g = src[4*x + 2]; + b = src[4*x + 3]; + ((unsigned short *)dst)[x] = + ((r & 0xF8) << 7) | + ((g & 0xF8) << 2) | + (b >> 3); + } + src += srcstride; + dst += dststride; + } +} + +static void +ximage_convert_rgb555_br(PARAMS) +{ + unsigned char r, g, b; + int x, y; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + r = src[4*x + 1]; + g = src[4*x + 2]; + b = src[4*x + 3]; + /* final word is: + g5 g4 g3 b7 b6 b5 b4 b3 0 r7 r6 r5 r4 r3 g7 g6 + */ + ((unsigned short *)dst)[x] = + ((r & 0xF8) >> 1) | + ((g & 0xC0) >> 6) | + ((g & 0x38) << 10) | + ((b & 0xF8) << 5); + } + src += srcstride; + dst += dststride; + } +} + +static void +ximage_convert_bgr233(PARAMS) +{ + unsigned char r, g, b; + int x,y; + for(y = 0; y < h; y++) { + for(x = 0; x < w; x++) { + r = src[4*x + 1]; + g = src[4*x + 2]; + b = src[4*x + 3]; + /* format: b7 b6 g7 g6 g5 r7 r6 r5 */ + dst[x] = (b&0xC0) | ((g>>2)&0x38) | ((r>>5)&0x7); + } + src += srcstride; + dst += dststride; + } +} + +ximage_convert_func_t ximage_convert_funcs[] = { + ximage_convert_argb8888, + ximage_convert_bgra8888, + ximage_convert_rgba8888, + ximage_convert_abgr8888, + ximage_convert_rgb888, + ximage_convert_bgr888, + ximage_convert_rgb565, + ximage_convert_rgb565_br, + ximage_convert_rgb555, + ximage_convert_rgb555_br, + ximage_convert_bgr233, +}; |