summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/gtkpdf.c670
-rw-r--r--apps/macpdf.app/Contents/Info.plist24
-rw-r--r--apps/macpdf.app/Contents/PkgInfo1
-rw-r--r--apps/macpdf.app/Contents/Resources/English.lproj/main.nib/classes.nib4
-rw-r--r--apps/macpdf.app/Contents/Resources/English.lproj/main.nib/info.nib24
-rw-r--r--apps/macpdf.app/Contents/Resources/English.lproj/main.nib/objects.xib516
-rw-r--r--apps/macpdf.app/Contents/Resources/macpdf.icnsbin0 -> 31574 bytes
-rw-r--r--apps/macpdf.app/macpdf.c69
-rw-r--r--apps/macpdf.app/macpdf.h31
-rw-r--r--apps/macpdf.app/view.c525
-rw-r--r--apps/pdfclean.c173
-rw-r--r--apps/pdfdebug.c188
-rw-r--r--apps/pdffunction.c263
-rw-r--r--apps/pdfmerge.c230
-rw-r--r--apps/pdfrip.c182
-rw-r--r--apps/showcmap.c22
-rw-r--r--apps/w32pdf.c656
-rw-r--r--apps/x11pdf.c590
-rw-r--r--apps/ximage.c666
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">&lt;</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">&gt;</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">&lt;&lt;</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">&gt;&gt;</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
new file mode 100644
index 00000000..d11b8be1
--- /dev/null
+++ b/apps/macpdf.app/Contents/Resources/macpdf.icns
Binary files differ
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,
+};