#include #include #include #define kViewClassID CFSTR("com.artofcode.mupdf.View") #define kViewPrivate 'MU_v' 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; 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.y1)); 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; } 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, "/Users/tor/src/pdf/tiger.pdf"); DisposeNibReference(nibRef); ShowWindow(window); RunApplicationEventLoop(); CantGetNibRef: CantSetMenuBar: CantCreateWindow: CantRegisterView: return err; }