#include "fitz.h" #include "mupdf.h" #include #include #include "npapi.h" #include "npupp.h" #define PAD 5 #define MSG(s) MessageBox(0,s,"MuPDF Debug",MB_OK) typedef struct pdfmoz_s pdfmoz_t; typedef struct page_s page_t; struct page_s { fz_obj *obj; pdf_page *page; fz_pixmap *image; int w, h; /* page size in units */ int px; /* pixel height */ }; struct pdfmoz_s { NPP inst; HWND hwnd; HWND sbar; WNDPROC winproc; HCURSOR arrow, hand, wait; BITMAPINFO *dibinf; HBRUSH graybrush; int scrollpage; /* scrollbar -> page (n) */ int scrollyofs; /* scrollbar -> page offset in pixels */ int pagecount; page_t *pages; char *filename; char *doctitle; pdf_xref *xref; fz_renderer *rast; char error[1024]; /* empty if no error has occured */ }; void pdfmoz_warn(pdfmoz_t *moz, const char *fmt, ...) { char buf[1024]; va_list ap; va_start(ap, fmt); vsprintf(buf, fmt, ap); va_end(ap); strcpy(moz->error, buf); InvalidateRect(moz->hwnd, NULL, FALSE); NPN_Status(moz->inst, moz->error); } void pdfmoz_error(pdfmoz_t *moz, fz_error error) { fz_catch(error, "unhandled error"); InvalidateRect(moz->hwnd, NULL, FALSE); NPN_Status(moz->inst, "mupdf error"); } void pdfmoz_open(pdfmoz_t *moz, char *filename) { SCROLLINFO si; fz_error error; fz_obj *obj; fz_bbox bbox; int rot; int i; strcpy(moz->error, ""); error = fz_newrenderer(&moz->rast, pdf_devicergb, 0, 1024 * 512); if (error) pdfmoz_error(moz, error); /* * Open PDF and load xref table */ moz->filename = filename; moz->xref = pdf_newxref(); error = pdf_loadxref(moz->xref, filename); if (error) { error = pdf_repairxref(moz->xref, filename); if (error) pdfmoz_error(moz, error); } /* * Handle encrypted PDF files */ error = pdf_decryptxref(moz->xref); if (error) pdfmoz_error(moz, error); if (pdf_needspassword(moz->xref)) { pdfmoz_warn(moz, "PDF file is encrypted and needs a password."); } moz->pagecount = pdf_getpagecount(moz->xref); moz->pages = fz_malloc(sizeof(page_t) * moz->pagecount); for (i = 0; i < moz->pagecount; i++) { fz_obj *pageobj; pageobj = pdf_getpageobject(moz->xref, i); moz->pages[i].obj = fz_keepobj(pageobj); moz->pages[i].page = nil; moz->pages[i].image = nil; obj = fz_dictgets(moz->pages[i].obj, "CropBox"); if (!obj) obj = fz_dictgets(moz->pages[i].obj, "MediaBox"); bbox = fz_roundrect(pdf_torect(obj)); moz->pages[i].w = bbox.x1 - bbox.x0; moz->pages[i].h = bbox.y1 - bbox.y0; rot = fz_toint(fz_dictgets(moz->pages[i].obj, "Rotate")); if ((rot / 90) % 2) { int t = moz->pages[i].w; moz->pages[i].w = moz->pages[i].h; moz->pages[i].h = t; } moz->pages[i].px = 1 + PAD; } /* * Load meta information * TODO: move this into mupdf library */ obj = fz_dictgets(moz->xref->trailer, "Root"); moz->xref->root = fz_resolveindirect(obj); if (!moz->xref->root) pdfmoz_error(moz, fz_throw("syntaxerror: missing Root object")); if (moz->xref->root) fz_keepobj(moz->xref->root); obj = fz_dictgets(moz->xref->trailer, "Info"); moz->xref->info = fz_resolveindirect(obj); if (moz->xref->info) fz_keepobj(moz->xref->info); moz->doctitle = filename; if (strrchr(moz->doctitle, '\\')) moz->doctitle = strrchr(moz->doctitle, '\\') + 1; if (strrchr(moz->doctitle, '/')) moz->doctitle = strrchr(moz->doctitle, '/') + 1; if (moz->xref->info) { obj = fz_dictgets(moz->xref->info, "Title"); if (obj) moz->doctitle = pdf_toutf8(obj); } /* * Start at first page */ si.cbSize = sizeof(si); si.fMask = SIF_POS | SIF_RANGE; // XXX | SIF_PAGE; si.nPos = 0; si.nMin = 0; si.nMax = 1; si.nPage = 1; SetScrollInfo(moz->hwnd, SB_VERT, &si, TRUE); moz->scrollpage = 0; moz->scrollyofs = 0; InvalidateRect(moz->hwnd, NULL, FALSE); } static void decodescroll(pdfmoz_t *moz, int spos) { int i, y = 0; moz->scrollpage = 0; moz->scrollyofs = 0; for (i = 0; i < moz->pagecount; i++) { if (spos >= y && spos < y + moz->pages[i].px) { moz->scrollpage = i; moz->scrollyofs = spos - y; return; } y += moz->pages[i].px; } } fz_matrix pdfmoz_pagectm(pdfmoz_t *moz, int pagenum) { page_t *page = moz->pages + pagenum; fz_matrix ctm; float zoom; RECT rc; GetClientRect(moz->hwnd, &rc); zoom = (rc.right - rc.left) / (float) page->w; ctm = fz_identity; ctm = fz_concat(ctm, fz_translate(0, -page->page->mediabox.y1)); ctm = fz_concat(ctm, fz_scale(zoom, -zoom)); ctm = fz_concat(ctm, fz_rotate(page->page->rotate)); return ctm; } void pdfmoz_loadpage(pdfmoz_t *moz, int pagenum) { page_t *page = moz->pages + pagenum; fz_error error; if (page->page) return; error = pdf_loadpage(&page->page, moz->xref, page->obj); if (error) pdfmoz_error(moz, error); } void pdfmoz_drawpage(pdfmoz_t *moz, int pagenum) { page_t *page = moz->pages + pagenum; fz_error error; fz_matrix ctm; fz_rect bbox; if (page->image) return; ctm = pdfmoz_pagectm(moz, pagenum); bbox = fz_transformaabb(ctm, page->page->mediabox); error = fz_rendertree(&page->image, moz->rast, page->page->tree, ctm, fz_roundrect(bbox), 1); if (error) pdfmoz_error(moz, error); } void pdfmoz_gotouri(pdfmoz_t *moz, fz_obj *uri) { char buf[2048]; memcpy(buf, fz_tostrbuf(uri), fz_tostrlen(uri)); buf[fz_tostrlen(uri)] = 0; NPN_GetURL(moz->inst, buf, "_blank"); } int pdfmoz_getpagenum(pdfmoz_t *moz, fz_obj *obj) { return pdf_findpageobject(moz->xref, obj); } void pdfmoz_gotopage(pdfmoz_t *moz, fz_obj *obj) { int page; int i, y = 0; page = pdf_findpageobject(moz->xref, obj); for (i = 0; i < page; i++) y += moz->pages[i].px; SetScrollPos(moz->hwnd, SB_VERT, y, TRUE); InvalidateRect(moz->hwnd, NULL, FALSE); } void pdfmoz_onmouse(pdfmoz_t *moz, int x, int y, int click) { char buf[512]; pdf_link *link; fz_matrix ctm; fz_point p; int pi; int py; if (!moz->pages) return; pi = moz->scrollpage; py = -moz->scrollyofs; while (pi < moz->pagecount) { if (!moz->pages[pi].image) return; if (y > py && y < moz->pages[pi].px) break; py += moz->pages[pi].px; pi ++; } if (pi == moz->pagecount) return; p.x = x + moz->pages[pi].image->x; p.y = y + moz->pages[pi].image->y - py; ctm = pdfmoz_pagectm(moz, pi); ctm = fz_invertmatrix(ctm); p = fz_transformpoint(ctm, p); for (link = moz->pages[pi].page->links; link; link = link->next) { if (p.x >= link->rect.x0 && p.x <= link->rect.x1) if (p.y >= link->rect.y0 && p.y <= link->rect.y1) break; } if (link) { SetCursor(moz->hand); if (click) { if (link->kind == PDF_LURI) pdfmoz_gotouri(moz, link->dest); else if (link->kind == PDF_LGOTO) pdfmoz_gotopage(moz, link->dest); return; } else { if (fz_isstring(link->dest)) { memcpy(buf, fz_tostrbuf(link->dest), fz_tostrlen(link->dest)); buf[fz_tostrlen(link->dest)] = 0; NPN_Status(moz->inst, buf); } else if (fz_isindirect(link->dest)) { sprintf(buf, "Go to page %d", pdfmoz_getpagenum(moz, link->dest) + 1); NPN_Status(moz->inst, buf); } else NPN_Status(moz->inst, "Say what?"); } } else { sprintf(buf, "Page %d of %d", moz->scrollpage + 1, moz->pagecount); NPN_Status(moz->inst, buf); SetCursor(moz->arrow); } } static void drawimage(HDC hdc, pdfmoz_t *moz, fz_pixmap *image, int yofs) { int bmpstride = ((image->w * 3 + 3) / 4) * 4; unsigned char *bmpdata = fz_malloc(image->h * bmpstride); int x, y; if (!bmpdata) return; for (y = 0; y < image->h; y++) { unsigned char *p = bmpdata + y * bmpstride; unsigned char *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]; } } moz->dibinf->bmiHeader.biWidth = image->w; moz->dibinf->bmiHeader.biHeight = -image->h; moz->dibinf->bmiHeader.biSizeImage = image->h * bmpstride; SetDIBitsToDevice(hdc, 0, /* destx */ yofs, /* desty */ image->w, /* destw */ image->h, /* desth */ 0, /* srcx */ 0, /* srcy */ 0, /* startscan */ image->h, /* numscans */ bmpdata, /* pBits */ moz->dibinf, /* pInfo */ DIB_RGB_COLORS /* color use flag */ ); fz_free(bmpdata); } LRESULT CALLBACK MozWinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { pdfmoz_t *moz = (pdfmoz_t*) GetWindowLongPtr(hwnd, GWLP_USERDATA); char buf[256]; int x = (signed short) LOWORD(lParam); int y = (signed short) HIWORD(lParam); int i, h; SCROLLINFO si; PAINTSTRUCT ps; HDC hdc; RECT rc; RECT pad; WORD sendmsg; float zoom; GetClientRect(hwnd, &rc); h = rc.bottom - rc.top; if (strlen(moz->error)) { if (msg == WM_PAINT) { hdc = BeginPaint(hwnd, &ps); FillRect(hdc, &rc, GetStockBrush(WHITE_BRUSH)); rc.top += 10; rc.bottom -= 10; rc.left += 10; rc.right -= 10; DrawText(hdc, moz->error, strlen(moz->error), &rc, 0); // DT_SINGLELINE|DT_CENTER|DT_VCENTER); EndPaint(hwnd, &ps); } if (msg == WM_MOUSEMOVE) { SetCursor(moz->arrow); } return 0; } switch (msg) { case WM_PAINT: GetClientRect(moz->hwnd, &rc); si.cbSize = sizeof(si); si.fMask = SIF_ALL; GetScrollInfo(hwnd, SB_VERT, &si); decodescroll(moz, si.nPos); /* evict out-of-range images and pages */ for (i = 0; i < moz->pagecount; i++) { if (i < moz->scrollpage - 2 || i > moz->scrollpage + 6) { if (moz->pages[i].page) { pdf_droppage(moz->pages[i].page); moz->pages[i].page = nil; } } if (i < moz->scrollpage - 1 || i > moz->scrollpage + 3) { if (moz->pages[i].image) { fz_droppixmap(moz->pages[i].image); moz->pages[i].image = nil; } } } i = moz->scrollpage; pdfmoz_loadpage(moz, i); if (moz->error[0]) return 0; pdfmoz_drawpage(moz, i); if (moz->error[0]) return 0; y = -moz->scrollyofs; while (y < h && i < moz->pagecount) { pdfmoz_loadpage(moz, i); if (moz->error[0]) return 0; pdfmoz_drawpage(moz, i); if (moz->error[0]) return 0; y += moz->pages[i].image->h; i ++; } hdc = BeginPaint(hwnd, &ps); pad.left = rc.left; pad.right = rc.right; i = moz->scrollpage; y = -moz->scrollyofs; while (y < h && i < moz->pagecount) { drawimage(hdc, moz, moz->pages[i].image, y); y += moz->pages[i].image->h; i ++; pad.top = y; pad.bottom = y + PAD; FillRect(hdc, &pad, moz->graybrush); y += PAD; } if (y < h) { pad.top = y; pad.bottom = h; FillRect(hdc, &pad, moz->graybrush); } EndPaint(hwnd, &ps); return 0; case WM_SIZE: ShowScrollBar(moz->hwnd, SB_VERT, TRUE); GetClientRect(moz->hwnd, &rc); si.cbSize = sizeof(si); si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; si.nPos = 0; si.nMin = 0; si.nMax = 0; // si.nPage = MAX(30, rc.bottom - rc.top - 30); si.nPage = rc.bottom - rc.top; for (i = 0; i < moz->pagecount; i++) { zoom = (rc.right - rc.left) / (float) moz->pages[i].w; moz->pages[i].px = zoom * moz->pages[i].h + PAD; if (moz->scrollpage == i) { si.nPos = si.nMax; if (moz->pages[i].image) { si.nPos += moz->pages[i].px * moz->scrollyofs / moz->pages[i].image->h + 1; } } if (moz->pages[i].image) { fz_droppixmap(moz->pages[i].image); moz->pages[i].image = nil; } si.nMax += moz->pages[i].px; } si.nMax --; SetScrollInfo(moz->hwnd, SB_VERT, &si, TRUE); break; case WM_MOUSEMOVE: pdfmoz_onmouse(moz, x, y, 0); break; case WM_LBUTTONDOWN: SetFocus(hwnd); pdfmoz_onmouse(moz, x, y, 1); break; case WM_VSCROLL: si.cbSize = sizeof(si); si.fMask = SIF_ALL; GetScrollInfo(hwnd, SB_VERT, &si); switch (LOWORD(wParam)) { case SB_BOTTOM: si.nPos = si.nMax; break; case SB_TOP: si.nPos = 0; break; case SB_LINEUP: si.nPos -= 50; break; case SB_LINEDOWN: si.nPos += 50; break; case SB_PAGEUP: si.nPos -= si.nPage; break; case SB_PAGEDOWN: si.nPos += si.nPage; break; case SB_THUMBTRACK: si.nPos = si.nTrackPos; break; case SB_THUMBPOSITION: si.nPos = si.nTrackPos; break; } si.fMask = SIF_POS; si.nPos = MAX(0, MIN(si.nPos, si.nMax)); SetScrollInfo(hwnd, SB_VERT, &si, TRUE); InvalidateRect(moz->hwnd, NULL, FALSE); decodescroll(moz, si.nPos); sprintf(buf, "Page %d of %d", moz->scrollpage + 1, moz->pagecount); NPN_Status(moz->inst, buf); return 0; case WM_MOUSEWHEEL: if ((signed short)HIWORD(wParam) > 0) SendMessage(hwnd, WM_VSCROLL, MAKELONG(SB_LINEUP, 0), 0); else SendMessage(hwnd, WM_VSCROLL, MAKELONG(SB_LINEDOWN, 0), 0); break; case WM_KEYDOWN: sendmsg = 0xFFFF; switch (wParam) { case VK_UP: sendmsg = SB_LINEUP; break; case VK_PRIOR: sendmsg = SB_PAGEUP; break; case ' ': case VK_NEXT: sendmsg = SB_PAGEDOWN; break; case '\r': case VK_DOWN: sendmsg = SB_LINEDOWN; break; case VK_HOME: sendmsg = SB_TOP; break; case VK_END: sendmsg = SB_BOTTOM; break; } if (sendmsg != 0xFFFF) SendMessage(hwnd, WM_VSCROLL, MAKELONG(sendmsg, 0), 0); /* ick! someone eats events instead of bubbling... not my fault! */ break; default: break; } return moz->winproc(hwnd, msg, wParam, lParam); } NPError NPP_New(NPMIMEType mime, NPP inst, uint16 mode, int16 argc, char *argn[], char *argv[], NPSavedData *saved) { pdfmoz_t *moz; //MSG("NPP_New"); moz = fz_malloc(sizeof(pdfmoz_t)); if (!moz) return NPERR_OUT_OF_MEMORY_ERROR; memset(moz, 0, sizeof(pdfmoz_t)); sprintf(moz->error, "MuPDF is loading the file..."); moz->inst = inst; moz->arrow = LoadCursor(NULL, IDC_ARROW); moz->hand = LoadCursor(NULL, IDC_HAND); moz->wait = LoadCursor(NULL, IDC_WAIT); moz->dibinf = fz_malloc(sizeof(BITMAPINFO) + 12); if (!moz->dibinf) return NPERR_OUT_OF_MEMORY_ERROR; moz->dibinf->bmiHeader.biSize = sizeof(moz->dibinf->bmiHeader); moz->dibinf->bmiHeader.biPlanes = 1; moz->dibinf->bmiHeader.biBitCount = 24; moz->dibinf->bmiHeader.biCompression = BI_RGB; moz->dibinf->bmiHeader.biXPelsPerMeter = 2834; moz->dibinf->bmiHeader.biYPelsPerMeter = 2834; moz->dibinf->bmiHeader.biClrUsed = 0; moz->dibinf->bmiHeader.biClrImportant = 0; moz->dibinf->bmiHeader.biClrUsed = 0; moz->graybrush = CreateSolidBrush(RGB(0x70,0x70,0x70)); inst->pdata = moz; return NPERR_NO_ERROR; } NPError NPP_Destroy(NPP inst, NPSavedData **saved) { pdfmoz_t *moz = inst->pdata; int i; //MSG("NPP_Destroy"); inst->pdata = NULL; DeleteObject(moz->graybrush); DestroyCursor(moz->arrow); DestroyCursor(moz->hand); DestroyCursor(moz->wait); fz_free(moz->dibinf); for (i = 0; i < moz->pagecount; i++) { if (moz->pages[i].obj) fz_dropobj(moz->pages[i].obj); if (moz->pages[i].page) pdf_droppage(moz->pages[i].page); if (moz->pages[i].image) fz_droppixmap(moz->pages[i].image); } fz_free(moz->pages); if (moz->xref) { if (moz->xref->store) { pdf_dropstore(moz->xref->store); moz->xref->store = nil; } pdf_closexref(moz->xref); } fz_free(moz); return NPERR_NO_ERROR; } NPError NPP_SetWindow(NPP inst, NPWindow *npwin) { pdfmoz_t *moz = inst->pdata; if (moz->hwnd != npwin->window) { moz->hwnd = npwin->window; SetWindowLongPtr(moz->hwnd, GWLP_USERDATA, (LONG_PTR)moz); moz->winproc = (WNDPROC) SetWindowLongPtr(moz->hwnd, GWLP_WNDPROC, (LONG_PTR)MozWinProc); } SetFocus(moz->hwnd); return NPERR_NO_ERROR; } NPError NPP_NewStream(NPP inst, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype) { //MSG("NPP_NewStream"); *stype = NP_ASFILE; return NPERR_NO_ERROR; } NPError NPP_DestroyStream(NPP inst, NPStream* stream, NPReason reason) { //MSG("NPP_DestroyStream"); return NPERR_NO_ERROR; } int32 NPP_WriteReady(NPP inst, NPStream* stream) { //MSG("NPP_WriteReady"); return 2147483647; } int32 NPP_Write(NPP inst, NPStream* stream, int32 offset, int32 len, void* buffer) { //MSG("NPP_Write"); return len; } void NPP_StreamAsFile(NPP inst, NPStream* stream, const char* fname) { pdfmoz_t *moz = inst->pdata; //MSG("NPP_StreamAsFile"); pdfmoz_open(moz, (char*)fname); } void NPP_Print(NPP inst, NPPrint* platformPrint) { MSG("Sorry, printing is not supported."); } int16 NPP_HandleEvent(NPP inst, void* event) { MSG("handle event\n"); return 0; } void NPP_URLNotify(NPP inst, const char* url, NPReason reason, void* notifyData) { MSG("notify url\n"); } NPError NPP_GetValue(void* inst, NPPVariable variable, void *value) { return NPERR_NO_ERROR; } NPError NPP_SetValue(void* inst, NPNVariable variable, void *value) { return NPERR_NO_ERROR; } void* NPP_GetJavaClass(void) { return 0; } NPError NPP_Initialize(void) { // MSG("NPP_Initialize"); return NPERR_NO_ERROR; } void NPP_Shutdown(void) { // MSG("NPP_Shutdown"); }