diff options
-rw-r--r-- | Jamrules | 1 | ||||
-rw-r--r-- | apps/unix/x11pdf.c | 197 | ||||
-rw-r--r-- | apps/windows/winmain.c | 1526 | ||||
-rw-r--r-- | apps/windows/winres.rc | 16 | ||||
-rw-r--r-- | mupdf/pdfapp.c | 1288 |
5 files changed, 1585 insertions, 1443 deletions
@@ -57,6 +57,7 @@ switch $(OS) } case MACOSX : + FLAG_LDALL += -L/usr/X11R6/lib ; FLAG_CCALL += -std=gnu99 -DHAVE_C99 ; FLAG_CCRELEASE = -DARCH_PPC -faltivec -fast ; diff --git a/apps/unix/x11pdf.c b/apps/unix/x11pdf.c index 524cbeff..3fad2fb8 100644 --- a/apps/unix/x11pdf.c +++ b/apps/unix/x11pdf.c @@ -20,6 +20,9 @@ extern void ximage_blit(Drawable d, GC gc, int dstx, int dsty, int srcx, int srcy, int srcw, int srch, int srcstride); static Display *xdpy; +static Atom XA_TARGETS; +static Atom XA_TIMESTAMP; +static Atom XA_UTF8_STRING; static int xscr; static Window xwin; static GC xgc; @@ -31,6 +34,11 @@ static int dirty = 0; static char *password = ""; static XColor xbgcolor; static XColor xshcolor; +static int reqw = 0; +static int reqh = 0; +static char copylatin1[1024 * 16] = ""; +static char copyutf8[1024 * 48] = ""; +static Time copytime; static pdfapp_t gapp; @@ -65,7 +73,12 @@ void winopen(void) XWMHints *hints; xdpy = XOpenDisplay(nil); - assert(xdpy != nil); + if (!xdpy) + winerror(&gapp, "could not open display."); + + XA_TARGETS = XInternAtom(xdpy, "TARGETS", False); + XA_TIMESTAMP = XInternAtom(xdpy, "TIMESTAMP", False); + XA_UTF8_STRING = XInternAtom(xdpy, "UTF8_STRING", False); xscr = DefaultScreen(xdpy); @@ -149,6 +162,9 @@ void winresize(pdfapp_t *app, int w, int h) values.height = h; XConfigureWindow(xdpy, xwin, mask, &values); + reqw = w; + reqh = h; + if (!mapped) { XMapWindow(xdpy, xwin); @@ -169,6 +185,43 @@ void winresize(pdfapp_t *app, int w, int h) } } +static void fillrect(int x, int y, int w, int h) +{ + if (w > 0 && h > 0) + XFillRectangle(xdpy, xwin, xgc, x, y, w, h); +} + +static void invertcopyrect() +{ + unsigned char *p; + int x, y; + + int x0 = gapp.selr.x0 - gapp.panx; + int x1 = gapp.selr.x1 - gapp.panx; + int y0 = gapp.selr.y0 - gapp.pany; + int y1 = gapp.selr.y1 - gapp.pany; + + x0 = CLAMP(x0, 0, gapp.image->w - 1); + x1 = CLAMP(x1, 0, gapp.image->w - 1); + y0 = CLAMP(y0, 0, gapp.image->h - 1); + y1 = CLAMP(y1, 0, gapp.image->h - 1); + + for (y = y0; y < y1; y++) + { + p = gapp.image->samples + (y * gapp.image->w + x0) * 4; + for (x = x0; x < x1; x++) + { + p[0] = 255 - p[0]; + p[1] = 255 - p[1]; + p[2] = 255 - p[2]; + p[3] = 255 - p[3]; + p += 4; + } + } + + justcopied = 1; +} + void winblit(pdfapp_t *app) { int x0 = gapp.panx; @@ -177,32 +230,28 @@ void winblit(pdfapp_t *app) int y1 = gapp.pany + gapp.image->h; XSetForeground(xdpy, xgc, xbgcolor.pixel); - XFillRectangle(xdpy, xwin, xgc, 0, 0, x0, gapp.winh); - XFillRectangle(xdpy, xwin, xgc, x1, 0, gapp.winw - x1, gapp.winh); - XFillRectangle(xdpy, xwin, xgc, 0, 0, gapp.winw, y0); - XFillRectangle(xdpy, xwin, xgc, 0, y1, gapp.winw, gapp.winh - y1); + fillrect(0, 0, x0, gapp.winh); + fillrect(x1, 0, gapp.winw - x1, gapp.winh); + fillrect(0, 0, gapp.winw, y0); + fillrect(0, y1, gapp.winw, gapp.winh - y1); XSetForeground(xdpy, xgc, xshcolor.pixel); - XFillRectangle(xdpy, xwin, xgc, x0+2, y1, gapp.image->w, 2); - XFillRectangle(xdpy, xwin, xgc, x1, y0+2, 2, gapp.image->h); - - if (0) - { - ximage_blit(xwin, xgc, - x0, y0, - gapp.image->samples, - 0, 0, - gapp.image->w, - gapp.image->h, - gapp.image->w * gapp.image->n); - } - else - { - XSetForeground(xdpy, xgc, WhitePixel(xdpy, xscr)); - XFillRectangle(xdpy, xwin, xgc, - x0, y0, x1 - x0, y1 - y0); - } - + fillrect(x0+2, y1, gapp.image->w, 2); + fillrect(x1, y0+2, 2, gapp.image->h); + + if (gapp.iscopying || justcopied) + invertcopyrect(); + + ximage_blit(xwin, xgc, + x0, y0, + gapp.image->samples, + 0, 0, + gapp.image->w, + gapp.image->h, + gapp.image->w * gapp.image->n); + + if (gapp.iscopying || justcopied) + invertcopyrect(); } void winrepaint(pdfapp_t *app) @@ -212,7 +261,90 @@ void winrepaint(pdfapp_t *app) void windocopy(pdfapp_t *app) { - /* yeah, right. not right now. */ + unsigned short copyucs2[16 * 1024]; + char *latin1 = copylatin1; + char *utf8 = copyutf8; + unsigned short *ucs2; + int ucs; + + pdfapp_oncopy(&gapp, copyucs2, 16 * 1024); + + for (ucs2 = copyucs2; ucs2[0] != 0; ucs2++) + { + ucs = ucs2[0]; + + utf8 += runetochar(utf8, &ucs); + + if (ucs < 256) + *latin1++ = ucs; + else + *latin1++ = '?'; + } + + *utf8 = 0; + *latin1 = 0; + +printf("oncopy utf8=%d latin1=%d\n", strlen(copyutf8), strlen(copylatin1)); + + XSetSelectionOwner(xdpy, XA_PRIMARY, xwin, copytime); + + justcopied = 1; +} + +void onselreq(Window requestor, Atom selection, Atom target, Atom property, Time time) +{ + XEvent nevt; + +printf("onselreq\n"); + + if (property == None) + property = target; + + nevt.xselection.type = SelectionNotify; + nevt.xselection.send_event = True; + nevt.xselection.display = xdpy; + nevt.xselection.requestor = requestor; + nevt.xselection.selection = selection; + nevt.xselection.target = target; + nevt.xselection.property = property; + nevt.xselection.time = time; + + if (target == XA_TARGETS) + { + Atom atomlist[4]; + atomlist[0] = XA_TARGETS; + atomlist[1] = XA_TIMESTAMP; + atomlist[2] = XA_STRING; + atomlist[3] = XA_UTF8_STRING; +printf(" -> targets\n"); + XChangeProperty(xdpy, requestor, property, target, + 32, PropModeReplace, + (unsigned char *)atomlist, sizeof(atomlist)/sizeof(Atom)); + } + + else if (target == XA_STRING) + { +printf(" -> string %d\n", strlen(copylatin1)); + XChangeProperty(xdpy, requestor, property, target, + 8, PropModeReplace, + (unsigned char *)copylatin1, strlen(copylatin1)); + } + + else if (target == XA_UTF8_STRING) + { +printf(" -> utf8string\n"); + XChangeProperty(xdpy, requestor, property, target, + 8, PropModeReplace, + (unsigned char *)copyutf8, strlen(copyutf8)); + } + + else + { +printf(" -> unknown\n"); + nevt.xselection.property = None; + } + + XSendEvent(xdpy, requestor, False, SelectionNotify, &nevt); } void winopenuri(pdfapp_t *app, char *buf) @@ -304,8 +436,8 @@ int main(int argc, char **argv) case ConfigureNotify: if (gapp.image) { - if (xevt.xconfigure.width != gapp.image->w || - xevt.xconfigure.height != gapp.image->h) + if (xevt.xconfigure.width != reqw || + xevt.xconfigure.height != reqh) gapp.shrinkwrap = 0; } pdfapp_onresize(&gapp, @@ -328,8 +460,17 @@ int main(int argc, char **argv) break; case ButtonRelease: + copytime = xevt.xbutton.time; onmouse(xevt.xbutton.x, xevt.xbutton.y, xevt.xbutton.button, -1); break; + + case SelectionRequest: + onselreq(xevt.xselectionrequest.requestor, + xevt.xselectionrequest.selection, + xevt.xselectionrequest.target, + xevt.xselectionrequest.property, + xevt.xselectionrequest.time); + break; } } while (XPending(xdpy)); diff --git a/apps/windows/winmain.c b/apps/windows/winmain.c index 4d7961d8..1e83131b 100644 --- a/apps/windows/winmain.c +++ b/apps/windows/winmain.c @@ -1,763 +1,763 @@ -#include <fitz.h>
-#include <mupdf.h>
-#include "pdfapp.h"
-
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <commdlg.h>
-#include <shellapi.h>
-
-#define ID_ABOUT 0x1000
-#define ID_DOCINFO 0x1001
-
-static HWND hwnd = NULL;
-static HDC hdc;
-static HBRUSH bgbrush;
-static HBRUSH shbrush;
-static BITMAPINFO *dibinf;
-static TCHAR szAppName[] = TEXT("ghostpdf");
-static HCURSOR arrowcurs, handcurs, waitcurs;
-static LRESULT CALLBACK windproc(HWND, UINT, WPARAM, LPARAM);
-
-static int bmpstride = 0;
-static char *bmpdata = NULL;
-static int justcopied = 0;
-
-static pdfapp_t gapp;
-
-/*
- * Associate GhostPDF with PDF files.
- */
-
-void associate(char *argv0)
-{
- char tmp[256];
- char *name = "Adobe PDF Document";
- HKEY key, kicon, kshell, kopen, kcmd;
- DWORD disp;
-
- /* HKEY_CLASSES_ROOT\.pdf */
-
- if (RegCreateKeyEx(HKEY_CLASSES_ROOT,
- ".pdf", 0, NULL, REG_OPTION_NON_VOLATILE,
- KEY_WRITE, NULL, &key, &disp))
- return;
-
- if (RegSetValueEx(key, "", 0, REG_SZ, "GhostPDF", strlen("GhostPDF")+1))
- return;
-
- RegCloseKey(key);
-
- /* HKEY_CLASSES_ROOT\GhostPDF */
-
- if (RegCreateKeyEx(HKEY_CLASSES_ROOT,
- "GhostPDF", 0, NULL, REG_OPTION_NON_VOLATILE,
- KEY_WRITE, NULL, &key, &disp))
- return;
-
- if (RegSetValueEx(key, "", 0, REG_SZ, name, strlen(name)+1))
- return;
-
- /* HKEY_CLASSES_ROOT\GhostPDF\DefaultIcon */
-
- if (RegCreateKeyEx(key,
- "DefaultIcon", 0, NULL, REG_OPTION_NON_VOLATILE,
- KEY_WRITE, NULL, &kicon, &disp))
- return;
-
- sprintf(tmp, "%s,1", argv0);
- if (RegSetValueEx(kicon, "", 0, REG_SZ, tmp, strlen(tmp)+1))
- return;
-
- RegCloseKey(kicon);
-
- /* HKEY_CLASSES_ROOT\GhostPDF\Shell\Open\Command */
-
- if (RegCreateKeyEx(key,
- "shell", 0, NULL, REG_OPTION_NON_VOLATILE,
- KEY_WRITE, NULL, &kshell, &disp))
- return;
- if (RegCreateKeyEx(kshell,
- "open", 0, NULL, REG_OPTION_NON_VOLATILE,
- KEY_WRITE, NULL, &kopen, &disp))
- return;
- if (RegCreateKeyEx(kopen,
- "command", 0, NULL, REG_OPTION_NON_VOLATILE,
- KEY_WRITE, NULL, &kcmd, &disp))
- return;
-
- sprintf(tmp, "\"%s\" \"%%1\"", argv0);
- if (RegSetValueEx(kcmd, "", 0, REG_SZ, tmp, strlen(tmp)+1))
- return;
-
- RegCloseKey(kcmd);
- RegCloseKey(kopen);
- RegCloseKey(kshell);
-
- RegCloseKey(key);
-}
-
-/*
- * Dialog boxes
- */
-
-void winwarn(pdfapp_t *app, char *msg)
-{
- MessageBoxA(hwnd, msg, "GhostPDF: Warning", MB_ICONWARNING);
-}
-
-void winerror(pdfapp_t *app, char *msg)
-{
- MessageBoxA(hwnd, msg, "GhostPDF: Error", MB_ICONERROR);
- exit(1);
-}
-
-int winfilename(char *buf, int len)
-{
- OPENFILENAME ofn;
- strcpy(buf, "");
- memset(&ofn, 0, sizeof(OPENFILENAME));
- ofn.lStructSize = sizeof(OPENFILENAME);
- ofn.hwndOwner = hwnd;
- ofn.lpstrFile = buf;
- ofn.nMaxFile = len;
- ofn.lpstrInitialDir = NULL;
- ofn.lpstrTitle = "GhostPDF: Open PDF file";
- ofn.lpstrFilter = "PDF Files (*.pdf)\0*.pdf\0All Files\0*\0\0";
- ofn.Flags = OFN_FILEMUSTEXIST|OFN_HIDEREADONLY;
- return GetOpenFileName(&ofn);
-}
-
-static char pd_filename[256] = "The file is encrypted.";
-static char pd_password[256] = "";
-static int pd_okay = 0;
-
-INT CALLBACK
-dlogpassproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
-{
- switch(message)
- {
- case WM_INITDIALOG:
- SetDlgItemText(hwnd, 4, pd_filename);
- return TRUE;
- case WM_COMMAND:
- switch(wParam)
- {
- case 1:
- pd_okay = 1;
- GetDlgItemText(hwnd, 3, pd_password, sizeof pd_password);
- EndDialog(hwnd, 0);
- return TRUE;
- case 2:
- pd_okay = 0;
- EndDialog(hwnd, 0);
- return TRUE;
- }
- break;
- }
- return FALSE;
-}
-
-char *winpassword(pdfapp_t *app, char *filename)
-{
- char buf[124], *s;
- strcpy(buf, filename);
- s = buf;
- if (strrchr(s, '\\')) s = strrchr(s, '\\') + 1;
- if (strrchr(s, '/')) s = strrchr(s, '/') + 1;
- if (strlen(s) > 32)
- strcpy(s + 30, "...");
- sprintf(pd_filename, "The file \"%s\" is encrypted.", s);
- DialogBox(NULL, "IDD_DLOGPASS", hwnd, dlogpassproc);
- if (pd_okay)
- return pd_password;
- return NULL;
-}
-
-INT CALLBACK
-dloginfoproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
-{
- char buf[256];
- pdf_xref *xref = gapp.xref;
- fz_obj *obj;
-
- switch(message)
- {
- case WM_INITDIALOG:
-
- SetDlgItemTextA(hwnd, 0x10, gapp.filename);
-
- sprintf(buf, "PDF %g", xref->version);
- SetDlgItemTextA(hwnd, 0x11, buf);
-
- if (xref->crypt)
- {
- sprintf(buf, "Standard %d bit RC4", xref->crypt->n * 8);
- SetDlgItemTextA(hwnd, 0x12, buf);
- strcpy(buf, "");
- if (xref->crypt->p & (1 << 2))
- strcat(buf, "print, ");
- if (xref->crypt->p & (1 << 3))
- strcat(buf, "modify, ");
- if (xref->crypt->p & (1 << 4))
- strcat(buf, "copy, ");
- if (xref->crypt->p & (1 << 5))
- strcat(buf, "annotate, ");
- if (strlen(buf) > 2)
- buf[strlen(buf)-2] = 0;
- else
- strcpy(buf, "none");
- SetDlgItemTextA(hwnd, 0x13, buf);
- }
- else
- {
- SetDlgItemTextA(hwnd, 0x12, "None");
- SetDlgItemTextA(hwnd, 0x13, "n/a");
- }
-
- if (!xref->info)
- return TRUE;
-
- #define SETUCS(ID) \
- { \
- fz_error *error; \
- unsigned short *ucs; \
- error = pdf_toucs2(&ucs, obj); \
- if (!error) \
- { \
- SetDlgItemTextW(hwnd, ID, ucs); \
- fz_free(ucs); \
- } \
- }
-
- if ((obj = fz_dictgets(xref->info, "Title"))) SETUCS(0x20)
- if ((obj = fz_dictgets(xref->info, "Author"))) SETUCS(0x21)
- if ((obj = fz_dictgets(xref->info, "Subject"))) SETUCS(0x22)
- if ((obj = fz_dictgets(xref->info, "Keywords"))) SETUCS(0x23)
- if ((obj = fz_dictgets(xref->info, "Creator"))) SETUCS(0x24)
- if ((obj = fz_dictgets(xref->info, "Producer"))) SETUCS(0x25)
- if ((obj = fz_dictgets(xref->info, "CreationDate"))) SETUCS(0x26)
- if ((obj = fz_dictgets(xref->info, "ModDate"))) SETUCS(0x27)
-
- return TRUE;
-
- case WM_COMMAND:
- EndDialog(hwnd, 0);
- return TRUE;
- }
- return FALSE;
-}
-
-void info()
-{
- DialogBox(NULL, "IDD_DLOGINFO", hwnd, dloginfoproc);
-}
-
-INT CALLBACK
-dlogaboutproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
-{
- char msg[256];
- switch(message)
- {
- case WM_INITDIALOG:
- SetDlgItemTextA(hwnd, 0x10, gapp.filename);
- sprintf(msg, "GhostPDF v%0.2f is %s",
- PDF_VERSION / 100.0, PDF_COPYRIGHT);
- SetDlgItemTextA(hwnd, 2, msg);
- SetDlgItemTextA(hwnd, 3, pdfapp_usage(&gapp));
- return TRUE;
- case WM_COMMAND:
- EndDialog(hwnd, 0);
- return TRUE;
- }
- return FALSE;
-}
-
-void help()
-{
- DialogBox(NULL, "IDD_DLOGABOUT", hwnd, dlogaboutproc);
-}
-
-/*
- * Main window
- */
-
-void winopen()
-{
- WNDCLASS wc;
- HMENU menu;
- RECT r;
-
- /* Create and register window class */
- wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
- wc.lpfnWndProc = windproc;
- wc.cbClsExtra = 0;
- wc.cbWndExtra = 0;
- wc.hInstance = GetModuleHandle(NULL);
- wc.hIcon = LoadIcon(wc.hInstance, "IDI_ICONAPP");
- 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");
-
- /* Get screen size */
- SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
- gapp.scrw = r.right - r.left;
- gapp.scrh = r.bottom - r.top;
-
- /* Create cursors */
- arrowcurs = LoadCursor(NULL, IDC_ARROW);
- handcurs = LoadCursor(NULL, IDC_HAND);
- waitcurs = LoadCursor(NULL, IDC_WAIT);
-
- /* And a background color */
- bgbrush = CreateSolidBrush(RGB(0x70,0x70,0x70));
- shbrush = CreateSolidBrush(RGB(0x40,0x40,0x40));
-
- /* 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,
- 5, //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, "GhostPDF");
-
- menu = GetSystemMenu(hwnd, 0);
- AppendMenu(menu, MF_SEPARATOR, 0, NULL);
- AppendMenu(menu, MF_STRING, ID_ABOUT, "About GhostPDF...");
- AppendMenu(menu, MF_STRING, ID_DOCINFO, "Document Properties...");
-
- SetCursor(arrowcurs);
-}
-
-void wincursor(pdfapp_t *app, int curs)
-{
- if (curs == ARROW)
- SetCursor(arrowcurs);
- if (curs == HAND)
- SetCursor(handcurs);
- if (curs == WAIT)
- SetCursor(waitcurs);
-}
-
-void wintitle(pdfapp_t *app, char *title)
-{
- unsigned short wide[256], *dp;
- char *sp;
- int rune;
-
- dp = wide;
- sp = title;
- while (*sp && dp < wide + 255)
- {
- sp += chartorune(&rune, sp);
- *dp++ = rune;
- }
- *dp = 0;
-
- SetWindowTextW(hwnd, wide);
-}
-
-void winconvert(pdfapp_t *app, fz_pixmap *image)
-{
- int y, x;
-
- if (bmpdata)
- fz_free(bmpdata);
-
- bmpstride = ((image->w * 3 + 3) / 4) * 4;
- bmpdata = fz_malloc(image->h * bmpstride);
- if (!bmpdata)
- return;
-
- for (y = 0; y < image->h; y++)
- {
- char *p = bmpdata + y * bmpstride;
- 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];
- }
- }
-}
-
-void invertcopyrect(void)
-{
- int x0 = gapp.selr.x0 - gapp.panx;
- int x1 = gapp.selr.x1 - gapp.panx;
- int y0 = gapp.selr.y0 - gapp.pany;
- int y1 = gapp.selr.y1 - gapp.pany;
- int x, y;
-
- x0 = CLAMP(x0, 0, gapp.image->w - 1);
- x1 = CLAMP(x1, 0, gapp.image->w - 1);
- y0 = CLAMP(y0, 0, gapp.image->h - 1);
- y1 = CLAMP(y1, 0, gapp.image->h - 1);
-
- unsigned char *p;
- for (y = y0; y < y1; y++)
- {
- p = bmpdata + y * bmpstride + x0 * 3;
- for (x = x0; x < x1; x++)
- {
- p[0] = 255 - p[0];
- p[1] = 255 - p[1];
- p[2] = 255 - p[2];
- p += 3;
- }
- }
-
- justcopied = 1;
-}
-
-void winblit()
-{
- int x0 = gapp.panx;
- int y0 = gapp.pany;
- int x1 = gapp.panx + gapp.image->w;
- int y1 = gapp.pany + gapp.image->h;
- RECT r;
-
- if (bmpdata)
- {
- if (gapp.iscopying || justcopied)
- invertcopyrect();
-
- dibinf->bmiHeader.biWidth = gapp.image->w;
- dibinf->bmiHeader.biHeight = -gapp.image->h;
- dibinf->bmiHeader.biSizeImage = gapp.image->h * bmpstride;
- SetDIBitsToDevice(hdc,
- gapp.panx, /* destx */
- gapp.pany, /* desty */
- gapp.image->w, /* destw */
- gapp.image->h, /* desth */
- 0, /* srcx */
- 0, /* srcy */
- 0, /* startscan */
- gapp.image->h, /* numscans */
- bmpdata, /* pBits */
- dibinf, /* pInfo */
- DIB_RGB_COLORS /* color use flag */
- );
-
- if (gapp.iscopying || justcopied)
- invertcopyrect();
- }
-
- /* Grey background */
- r.top = 0; r.bottom = gapp.winh;
- r.left = 0; r.right = x0;
- FillRect(hdc, &r, bgbrush);
- r.left = x1; r.right = gapp.winw;
- FillRect(hdc, &r, bgbrush);
- r.left = 0; r.right = gapp.winw;
- r.top = 0; r.bottom = y0;
- FillRect(hdc, &r, bgbrush);
- r.top = y1; r.bottom = gapp.winh;
- FillRect(hdc, &r, bgbrush);
-
- /* Drop shadow */
- r.left = x0 + 2;
- r.right = x1 + 2;
- r.top = y1;
- r.bottom = y1 + 2;
- FillRect(hdc, &r, shbrush);
- r.left = x1;
- r.right = x1 + 2;
- r.top = y0 + 2;
- r.bottom = y1;
- FillRect(hdc, &r, shbrush);
-}
-
-void winresize(pdfapp_t *app, 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(pdfapp_t *app)
-{
- InvalidateRect(hwnd, NULL, 0);
-}
-
-/*
- * Event handling
- */
-
-void windocopy(pdfapp_t *app)
-{
- HGLOBAL handle;
- unsigned short *ucsbuf;
-
- if (!OpenClipboard(hwnd))
- return;
- EmptyClipboard();
-
- handle = GlobalAlloc(GMEM_MOVEABLE, 4096 * sizeof(unsigned short));
- if (!handle)
- {
- CloseClipboard();
- return;
- }
-
- ucsbuf = GlobalLock(handle);
- pdfapp_oncopy(&gapp, ucsbuf, 4096);
- GlobalUnlock(handle);
-
- SetClipboardData(CF_UNICODETEXT, handle);
- CloseClipboard();
-
- justcopied = 1; /* keep inversion around for a while... */
-}
-
-void winopenuri(pdfapp_t *app, char *buf)
-{
- ShellExecute(hwnd, "open", buf, 0, 0, SW_SHOWNORMAL);
-}
-
-void handlekey(int c)
-{
- if (GetCapture() == hwnd)
- return;
-
- if (justcopied)
- {
- justcopied = 0;
- winrepaint(&gapp);
- }
-
- /* translate VK into ascii equivalents */
- switch (c)
- {
- case VK_F1: c = '?'; break;
- case VK_ESCAPE: c = 'q'; break;
- case VK_DOWN: c = 'd'; break;
- case VK_UP: c = 'u'; break;
- case VK_LEFT: c = 'p'; break;
- case VK_RIGHT: c = 'n'; break;
- case VK_PRIOR: c = 'b'; break;
- case VK_NEXT: c = ' '; break;
- }
-
- if (c == 'q')
- exit(0);
- else if (c == '?' || c == 'h')
- help();
- else
- pdfapp_onkey(&gapp, c);
-}
-
-void handlemouse(int x, int y, int btn, int state)
-{
- if (state != 0 && justcopied)
- {
- justcopied = 0;
- winrepaint(&gapp);
- }
-
- if (state == 1)
- SetCapture(hwnd);
- if (state == -1)
- ReleaseCapture();
-
- pdfapp_onmouse(&gapp, x, y, btn, state);
-}
-
-LRESULT CALLBACK
-windproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
-{
- static int oldx = 0;
- static int oldy = 0;
- int x = (signed short) LOWORD(lParam);
- int y = (signed short) HIWORD(lParam);
-
- switch (message)
- {
- case WM_CREATE:
- return 0;
-
- case WM_DESTROY:
- case WM_CLOSE:
- PostQuitMessage(0);
- return 0;
-
- case WM_SYSCOMMAND:
- if (wParam == ID_ABOUT)
- {
- help();
- return 0;
- }
- if (wParam == ID_DOCINFO)
- {
- info();
- return 0;
- }
- break;
-
- case WM_SIZE:
- if (wParam == SIZE_MINIMIZED)
- return 0;
- if (wParam == SIZE_MAXIMIZED)
- gapp.shrinkwrap = 0;
- pdfapp_onresize(&gapp, LOWORD(lParam), HIWORD(lParam));
- return 0;
-
- case WM_SIZING:
- gapp.shrinkwrap = 0;
- 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:
- SetFocus(hwnd);
- oldx = x; oldy = y;
- handlemouse(x, y, 1, 1);
- return 0;
- case WM_MBUTTONDOWN:
- SetFocus(hwnd);
- oldx = x; oldy = y;
- handlemouse(x, y, 2, 1);
- return 0;
- case WM_RBUTTONDOWN:
- SetFocus(hwnd);
- oldx = x; oldy = y;
- handlemouse(x, y, 3, 1);
- return 0;
-
- case WM_LBUTTONUP:
- oldx = x; oldy = y;
- handlemouse(x, y, 1, -1);
- return 0;
- case WM_MBUTTONUP:
- oldx = x; oldy = y;
- handlemouse(x, y, 2, -1);
- return 0;
- case WM_RBUTTONUP:
- oldx = x; oldy = y;
- handlemouse(x, y, 3, -1);
- return 0;
-
- case WM_MOUSEMOVE:
- oldx = x; oldy = y;
- handlemouse(x, y, 0, 0);
- return 0;
-
- /* Mouse wheel */
- case WM_MOUSEWHEEL:
- if ((signed short)HIWORD(wParam) > 0)
- handlekey(LOWORD(wParam) & MK_SHIFT ? '+' : 'u');
- else
- handlekey(LOWORD(wParam) & MK_SHIFT ? '-' : 'd');
- return 0;
-
- /* Keyboard events */
-
- case WM_KEYDOWN:
- /* only handle special keys */
- switch (wParam)
- {
- case VK_F1:
- case VK_LEFT:
- case VK_UP:
- case VK_PRIOR:
- case VK_RIGHT:
- case VK_DOWN:
- case VK_NEXT:
- case VK_ESCAPE:
- handlekey(wParam);
- handlemouse(oldx, oldy, 0, 0); /* update cursor */
- return 0;
- }
- return 1;
-
- /* unicode encoded chars, including escape, backspace etc... */
- case WM_CHAR:
- handlekey(wParam);
- handlemouse(oldx, oldy, 0, 0); /* update cursor */
- return 0;
- }
-
- fflush(stdout);
-
- /* Pass on unhandled events to Windows */
- return DefWindowProc(hwnd, message, wParam, lParam);
-}
-
-int main(int argc, char **argv)
-{
- char buf[1024];
- char *filename;
-
- fz_cpudetect();
- fz_accelerate();
-
- pdfapp_init(&gapp);
-
- associate(argv[0]);
- winopen();
-
- if (argc == 2)
- filename = strdup(argv[1]);
- else
- {
- if (!winfilename(buf, sizeof buf))
- exit(0);
- filename = buf;
- }
-
- pdfapp_open(&gapp, filename);
-
- MSG msg;
- while (GetMessage(&msg, NULL, 0, 0))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
-
- pdfapp_close(&gapp);
-
- return 0;
-}
-
+#include <fitz.h> +#include <mupdf.h> +#include "pdfapp.h" + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <commdlg.h> +#include <shellapi.h> + +#define ID_ABOUT 0x1000 +#define ID_DOCINFO 0x1001 + +static HWND hwnd = NULL; +static HDC hdc; +static HBRUSH bgbrush; +static HBRUSH shbrush; +static BITMAPINFO *dibinf; +static TCHAR szAppName[] = TEXT("ghostpdf"); +static HCURSOR arrowcurs, handcurs, waitcurs; +static LRESULT CALLBACK windproc(HWND, UINT, WPARAM, LPARAM); + +static int bmpstride = 0; +static char *bmpdata = NULL; +static int justcopied = 0; + +static pdfapp_t gapp; + +/* + * Associate GhostPDF with PDF files. + */ + +void associate(char *argv0) +{ + char tmp[256]; + char *name = "Adobe PDF Document"; + HKEY key, kicon, kshell, kopen, kcmd; + DWORD disp; + + /* HKEY_CLASSES_ROOT\.pdf */ + + if (RegCreateKeyEx(HKEY_CLASSES_ROOT, + ".pdf", 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_WRITE, NULL, &key, &disp)) + return; + + if (RegSetValueEx(key, "", 0, REG_SZ, "GhostPDF", strlen("GhostPDF")+1)) + return; + + RegCloseKey(key); + + /* HKEY_CLASSES_ROOT\GhostPDF */ + + if (RegCreateKeyEx(HKEY_CLASSES_ROOT, + "GhostPDF", 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_WRITE, NULL, &key, &disp)) + return; + + if (RegSetValueEx(key, "", 0, REG_SZ, name, strlen(name)+1)) + return; + + /* HKEY_CLASSES_ROOT\GhostPDF\DefaultIcon */ + + if (RegCreateKeyEx(key, + "DefaultIcon", 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_WRITE, NULL, &kicon, &disp)) + return; + + sprintf(tmp, "%s,1", argv0); + if (RegSetValueEx(kicon, "", 0, REG_SZ, tmp, strlen(tmp)+1)) + return; + + RegCloseKey(kicon); + + /* HKEY_CLASSES_ROOT\GhostPDF\Shell\Open\Command */ + + if (RegCreateKeyEx(key, + "shell", 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_WRITE, NULL, &kshell, &disp)) + return; + if (RegCreateKeyEx(kshell, + "open", 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_WRITE, NULL, &kopen, &disp)) + return; + if (RegCreateKeyEx(kopen, + "command", 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_WRITE, NULL, &kcmd, &disp)) + return; + + sprintf(tmp, "\"%s\" \"%%1\"", argv0); + if (RegSetValueEx(kcmd, "", 0, REG_SZ, tmp, strlen(tmp)+1)) + return; + + RegCloseKey(kcmd); + RegCloseKey(kopen); + RegCloseKey(kshell); + + RegCloseKey(key); +} + +/* + * Dialog boxes + */ + +void winwarn(pdfapp_t *app, char *msg) +{ + MessageBoxA(hwnd, msg, "GhostPDF: Warning", MB_ICONWARNING); +} + +void winerror(pdfapp_t *app, char *msg) +{ + MessageBoxA(hwnd, msg, "GhostPDF: Error", MB_ICONERROR); + exit(1); +} + +int winfilename(char *buf, int len) +{ + OPENFILENAME ofn; + strcpy(buf, ""); + memset(&ofn, 0, sizeof(OPENFILENAME)); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hwnd; + ofn.lpstrFile = buf; + ofn.nMaxFile = len; + ofn.lpstrInitialDir = NULL; + ofn.lpstrTitle = "GhostPDF: Open PDF file"; + ofn.lpstrFilter = "PDF Files (*.pdf)\0*.pdf\0All Files\0*\0\0"; + ofn.Flags = OFN_FILEMUSTEXIST|OFN_HIDEREADONLY; + return GetOpenFileName(&ofn); +} + +static char pd_filename[256] = "The file is encrypted."; +static char pd_password[256] = ""; +static int pd_okay = 0; + +INT CALLBACK +dlogpassproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch(message) + { + case WM_INITDIALOG: + SetDlgItemText(hwnd, 4, pd_filename); + return TRUE; + case WM_COMMAND: + switch(wParam) + { + case 1: + pd_okay = 1; + GetDlgItemText(hwnd, 3, pd_password, sizeof pd_password); + EndDialog(hwnd, 0); + return TRUE; + case 2: + pd_okay = 0; + EndDialog(hwnd, 0); + return TRUE; + } + break; + } + return FALSE; +} + +char *winpassword(pdfapp_t *app, char *filename) +{ + char buf[124], *s; + strcpy(buf, filename); + s = buf; + if (strrchr(s, '\\')) s = strrchr(s, '\\') + 1; + if (strrchr(s, '/')) s = strrchr(s, '/') + 1; + if (strlen(s) > 32) + strcpy(s + 30, "..."); + sprintf(pd_filename, "The file \"%s\" is encrypted.", s); + DialogBox(NULL, "IDD_DLOGPASS", hwnd, dlogpassproc); + if (pd_okay) + return pd_password; + return NULL; +} + +INT CALLBACK +dloginfoproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + char buf[256]; + pdf_xref *xref = gapp.xref; + fz_obj *obj; + + switch(message) + { + case WM_INITDIALOG: + + SetDlgItemTextA(hwnd, 0x10, gapp.filename); + + sprintf(buf, "PDF %g", xref->version); + SetDlgItemTextA(hwnd, 0x11, buf); + + if (xref->crypt) + { + sprintf(buf, "Standard %d bit RC4", xref->crypt->n * 8); + SetDlgItemTextA(hwnd, 0x12, buf); + strcpy(buf, ""); + if (xref->crypt->p & (1 << 2)) + strcat(buf, "print, "); + if (xref->crypt->p & (1 << 3)) + strcat(buf, "modify, "); + if (xref->crypt->p & (1 << 4)) + strcat(buf, "copy, "); + if (xref->crypt->p & (1 << 5)) + strcat(buf, "annotate, "); + if (strlen(buf) > 2) + buf[strlen(buf)-2] = 0; + else + strcpy(buf, "none"); + SetDlgItemTextA(hwnd, 0x13, buf); + } + else + { + SetDlgItemTextA(hwnd, 0x12, "None"); + SetDlgItemTextA(hwnd, 0x13, "n/a"); + } + + if (!xref->info) + return TRUE; + + #define SETUCS(ID) \ + { \ + fz_error *error; \ + unsigned short *ucs; \ + error = pdf_toucs2(&ucs, obj); \ + if (!error) \ + { \ + SetDlgItemTextW(hwnd, ID, ucs); \ + fz_free(ucs); \ + } \ + } + + if ((obj = fz_dictgets(xref->info, "Title"))) SETUCS(0x20) + if ((obj = fz_dictgets(xref->info, "Author"))) SETUCS(0x21) + if ((obj = fz_dictgets(xref->info, "Subject"))) SETUCS(0x22) + if ((obj = fz_dictgets(xref->info, "Keywords"))) SETUCS(0x23) + if ((obj = fz_dictgets(xref->info, "Creator"))) SETUCS(0x24) + if ((obj = fz_dictgets(xref->info, "Producer"))) SETUCS(0x25) + if ((obj = fz_dictgets(xref->info, "CreationDate"))) SETUCS(0x26) + if ((obj = fz_dictgets(xref->info, "ModDate"))) SETUCS(0x27) + + return TRUE; + + case WM_COMMAND: + EndDialog(hwnd, 0); + return TRUE; + } + return FALSE; +} + +void info() +{ + DialogBox(NULL, "IDD_DLOGINFO", hwnd, dloginfoproc); +} + +INT CALLBACK +dlogaboutproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + char msg[256]; + switch(message) + { + case WM_INITDIALOG: + SetDlgItemTextA(hwnd, 0x10, gapp.filename); + sprintf(msg, "GhostPDF v%0.2f is %s", + PDF_VERSION / 100.0, PDF_COPYRIGHT); + SetDlgItemTextA(hwnd, 2, msg); + SetDlgItemTextA(hwnd, 3, pdfapp_usage(&gapp)); + return TRUE; + case WM_COMMAND: + EndDialog(hwnd, 0); + return TRUE; + } + return FALSE; +} + +void help() +{ + DialogBox(NULL, "IDD_DLOGABOUT", hwnd, dlogaboutproc); +} + +/* + * Main window + */ + +void winopen() +{ + WNDCLASS wc; + HMENU menu; + RECT r; + + /* Create and register window class */ + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wc.lpfnWndProc = windproc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = GetModuleHandle(NULL); + wc.hIcon = LoadIcon(wc.hInstance, "IDI_ICONAPP"); + 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"); + + /* Get screen size */ + SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0); + gapp.scrw = r.right - r.left; + gapp.scrh = r.bottom - r.top; + + /* Create cursors */ + arrowcurs = LoadCursor(NULL, IDC_ARROW); + handcurs = LoadCursor(NULL, IDC_HAND); + waitcurs = LoadCursor(NULL, IDC_WAIT); + + /* And a background color */ + bgbrush = CreateSolidBrush(RGB(0x70,0x70,0x70)); + shbrush = CreateSolidBrush(RGB(0x40,0x40,0x40)); + + /* 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, + 5, //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, "GhostPDF"); + + menu = GetSystemMenu(hwnd, 0); + AppendMenu(menu, MF_SEPARATOR, 0, NULL); + AppendMenu(menu, MF_STRING, ID_ABOUT, "About GhostPDF..."); + AppendMenu(menu, MF_STRING, ID_DOCINFO, "Document Properties..."); + + SetCursor(arrowcurs); +} + +void wincursor(pdfapp_t *app, int curs) +{ + if (curs == ARROW) + SetCursor(arrowcurs); + if (curs == HAND) + SetCursor(handcurs); + if (curs == WAIT) + SetCursor(waitcurs); +} + +void wintitle(pdfapp_t *app, char *title) +{ + unsigned short wide[256], *dp; + char *sp; + int rune; + + dp = wide; + sp = title; + while (*sp && dp < wide + 255) + { + sp += chartorune(&rune, sp); + *dp++ = rune; + } + *dp = 0; + + SetWindowTextW(hwnd, wide); +} + +void winconvert(pdfapp_t *app, fz_pixmap *image) +{ + int y, x; + + if (bmpdata) + fz_free(bmpdata); + + bmpstride = ((image->w * 3 + 3) / 4) * 4; + bmpdata = fz_malloc(image->h * bmpstride); + if (!bmpdata) + return; + + for (y = 0; y < image->h; y++) + { + char *p = bmpdata + y * bmpstride; + 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]; + } + } +} + +void invertcopyrect(void) +{ + int x0 = gapp.selr.x0 - gapp.panx; + int x1 = gapp.selr.x1 - gapp.panx; + int y0 = gapp.selr.y0 - gapp.pany; + int y1 = gapp.selr.y1 - gapp.pany; + int x, y; + + x0 = CLAMP(x0, 0, gapp.image->w - 1); + x1 = CLAMP(x1, 0, gapp.image->w - 1); + y0 = CLAMP(y0, 0, gapp.image->h - 1); + y1 = CLAMP(y1, 0, gapp.image->h - 1); + + unsigned char *p; + for (y = y0; y < y1; y++) + { + p = bmpdata + y * bmpstride + x0 * 3; + for (x = x0; x < x1; x++) + { + p[0] = 255 - p[0]; + p[1] = 255 - p[1]; + p[2] = 255 - p[2]; + p += 3; + } + } + + justcopied = 1; +} + +void winblit() +{ + int x0 = gapp.panx; + int y0 = gapp.pany; + int x1 = gapp.panx + gapp.image->w; + int y1 = gapp.pany + gapp.image->h; + RECT r; + + if (bmpdata) + { + if (gapp.iscopying || justcopied) + invertcopyrect(); + + dibinf->bmiHeader.biWidth = gapp.image->w; + dibinf->bmiHeader.biHeight = -gapp.image->h; + dibinf->bmiHeader.biSizeImage = gapp.image->h * bmpstride; + SetDIBitsToDevice(hdc, + gapp.panx, /* destx */ + gapp.pany, /* desty */ + gapp.image->w, /* destw */ + gapp.image->h, /* desth */ + 0, /* srcx */ + 0, /* srcy */ + 0, /* startscan */ + gapp.image->h, /* numscans */ + bmpdata, /* pBits */ + dibinf, /* pInfo */ + DIB_RGB_COLORS /* color use flag */ + ); + + if (gapp.iscopying || justcopied) + invertcopyrect(); + } + + /* Grey background */ + r.top = 0; r.bottom = gapp.winh; + r.left = 0; r.right = x0; + FillRect(hdc, &r, bgbrush); + r.left = x1; r.right = gapp.winw; + FillRect(hdc, &r, bgbrush); + r.left = 0; r.right = gapp.winw; + r.top = 0; r.bottom = y0; + FillRect(hdc, &r, bgbrush); + r.top = y1; r.bottom = gapp.winh; + FillRect(hdc, &r, bgbrush); + + /* Drop shadow */ + r.left = x0 + 2; + r.right = x1 + 2; + r.top = y1; + r.bottom = y1 + 2; + FillRect(hdc, &r, shbrush); + r.left = x1; + r.right = x1 + 2; + r.top = y0 + 2; + r.bottom = y1; + FillRect(hdc, &r, shbrush); +} + +void winresize(pdfapp_t *app, 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(pdfapp_t *app) +{ + InvalidateRect(hwnd, NULL, 0); +} + +/* + * Event handling + */ + +void windocopy(pdfapp_t *app) +{ + HGLOBAL handle; + unsigned short *ucsbuf; + + if (!OpenClipboard(hwnd)) + return; + EmptyClipboard(); + + handle = GlobalAlloc(GMEM_MOVEABLE, 4096 * sizeof(unsigned short)); + if (!handle) + { + CloseClipboard(); + return; + } + + ucsbuf = GlobalLock(handle); + pdfapp_oncopy(&gapp, ucsbuf, 4096); + GlobalUnlock(handle); + + SetClipboardData(CF_UNICODETEXT, handle); + CloseClipboard(); + + justcopied = 1; /* keep inversion around for a while... */ +} + +void winopenuri(pdfapp_t *app, char *buf) +{ + ShellExecute(hwnd, "open", buf, 0, 0, SW_SHOWNORMAL); +} + +void handlekey(int c) +{ + if (GetCapture() == hwnd) + return; + + if (justcopied) + { + justcopied = 0; + winrepaint(&gapp); + } + + /* translate VK into ascii equivalents */ + switch (c) + { + case VK_F1: c = '?'; break; + case VK_ESCAPE: c = 'q'; break; + case VK_DOWN: c = 'd'; break; + case VK_UP: c = 'u'; break; + case VK_LEFT: c = 'p'; break; + case VK_RIGHT: c = 'n'; break; + case VK_PRIOR: c = 'b'; break; + case VK_NEXT: c = ' '; break; + } + + if (c == 'q') + exit(0); + else if (c == '?' || c == 'h') + help(); + else + pdfapp_onkey(&gapp, c); +} + +void handlemouse(int x, int y, int btn, int state) +{ + if (state != 0 && justcopied) + { + justcopied = 0; + winrepaint(&gapp); + } + + if (state == 1) + SetCapture(hwnd); + if (state == -1) + ReleaseCapture(); + + pdfapp_onmouse(&gapp, x, y, btn, state); +} + +LRESULT CALLBACK +windproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + static int oldx = 0; + static int oldy = 0; + int x = (signed short) LOWORD(lParam); + int y = (signed short) HIWORD(lParam); + + switch (message) + { + case WM_CREATE: + return 0; + + case WM_DESTROY: + case WM_CLOSE: + PostQuitMessage(0); + return 0; + + case WM_SYSCOMMAND: + if (wParam == ID_ABOUT) + { + help(); + return 0; + } + if (wParam == ID_DOCINFO) + { + info(); + return 0; + } + break; + + case WM_SIZE: + if (wParam == SIZE_MINIMIZED) + return 0; + if (wParam == SIZE_MAXIMIZED) + gapp.shrinkwrap = 0; + pdfapp_onresize(&gapp, LOWORD(lParam), HIWORD(lParam)); + return 0; + + case WM_SIZING: + gapp.shrinkwrap = 0; + 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: + SetFocus(hwnd); + oldx = x; oldy = y; + handlemouse(x, y, 1, 1); + return 0; + case WM_MBUTTONDOWN: + SetFocus(hwnd); + oldx = x; oldy = y; + handlemouse(x, y, 2, 1); + return 0; + case WM_RBUTTONDOWN: + SetFocus(hwnd); + oldx = x; oldy = y; + handlemouse(x, y, 3, 1); + return 0; + + case WM_LBUTTONUP: + oldx = x; oldy = y; + handlemouse(x, y, 1, -1); + return 0; + case WM_MBUTTONUP: + oldx = x; oldy = y; + handlemouse(x, y, 2, -1); + return 0; + case WM_RBUTTONUP: + oldx = x; oldy = y; + handlemouse(x, y, 3, -1); + return 0; + + case WM_MOUSEMOVE: + oldx = x; oldy = y; + handlemouse(x, y, 0, 0); + return 0; + + /* Mouse wheel */ + case WM_MOUSEWHEEL: + if ((signed short)HIWORD(wParam) > 0) + handlekey(LOWORD(wParam) & MK_SHIFT ? '+' : 'u'); + else + handlekey(LOWORD(wParam) & MK_SHIFT ? '-' : 'd'); + return 0; + + /* Keyboard events */ + + case WM_KEYDOWN: + /* only handle special keys */ + switch (wParam) + { + case VK_F1: + case VK_LEFT: + case VK_UP: + case VK_PRIOR: + case VK_RIGHT: + case VK_DOWN: + case VK_NEXT: + case VK_ESCAPE: + handlekey(wParam); + handlemouse(oldx, oldy, 0, 0); /* update cursor */ + return 0; + } + return 1; + + /* unicode encoded chars, including escape, backspace etc... */ + case WM_CHAR: + handlekey(wParam); + handlemouse(oldx, oldy, 0, 0); /* update cursor */ + return 0; + } + + fflush(stdout); + + /* Pass on unhandled events to Windows */ + return DefWindowProc(hwnd, message, wParam, lParam); +} + +int main(int argc, char **argv) +{ + char buf[1024]; + char *filename; + + fz_cpudetect(); + fz_accelerate(); + + pdfapp_init(&gapp); + + associate(argv[0]); + winopen(); + + if (argc == 2) + filename = strdup(argv[1]); + else + { + if (!winfilename(buf, sizeof buf)) + exit(0); + filename = buf; + } + + pdfapp_open(&gapp, filename); + + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + pdfapp_close(&gapp); + + return 0; +} + diff --git a/apps/windows/winres.rc b/apps/windows/winres.rc index 4b680547..dbe41204 100644 --- a/apps/windows/winres.rc +++ b/apps/windows/winres.rc @@ -40,14 +40,14 @@ BEGIN LTEXT "Created:" -1, 10,115,50,10, 0 LTEXT "Modified:" -1, 10,125,50,10, 0 - LTEXT "(unknown)" 0x20, 60,55,230,10, 0 - LTEXT "(unknown)" 0x21, 60,65,230,10, 0 - LTEXT "(unknown)" 0x22, 60,75,230,10, 0 - LTEXT "(unknown)" 0x23, 60,85,230,10, 0 - LTEXT "(unknown)" 0x24, 60,95,230,10, 0 - LTEXT "(unknown)" 0x25, 60,105,230,10, 0 - LTEXT "(unknown)" 0x26, 60,115,100,10, 0 - LTEXT "(unknown)" 0x27, 60,125,100,10, 0 + LTEXT "" 0x20, 60,55,230,10, 0 + LTEXT "" 0x21, 60,65,230,10, 0 + LTEXT "" 0x22, 60,75,230,10, 0 + LTEXT "" 0x23, 60,85,230,10, 0 + LTEXT "" 0x24, 60,95,230,10, 0 + LTEXT "" 0x25, 60,105,230,10, 0 + LTEXT "" 0x26, 60,115,100,10, 0 + LTEXT "" 0x27, 60,125,100,10, 0 END IDD_DLOGABOUT DIALOG 50, 50, 200, 210 diff --git a/mupdf/pdfapp.c b/mupdf/pdfapp.c index ffeaee5d..2b605faf 100644 --- a/mupdf/pdfapp.c +++ b/mupdf/pdfapp.c @@ -1,644 +1,644 @@ -#include <fitz.h>
-#include <mupdf.h>
-#include "pdfapp.h"
-
-void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage);
-
-void pdfapp_warn(pdfapp_t *app, const char *fmt, ...)
-{
- char buf[1024];
- va_list ap;
- va_start(ap, fmt);
- vsprintf(buf, fmt, ap);
- va_end(ap);
- winwarn(app, buf);
-}
-
-void pdfapp_error(pdfapp_t *app, fz_error *error)
-{
- winerror(app, error->msg);
-}
-
-char *pdfapp_usage(pdfapp_t *app)
-{
- return
- " l <\t\t-- rotate left\n"
- " r >\t\t-- rotate right\n"
- " u up\t\t-- scroll up\n"
- " d down\t-- scroll down\n"
- " = +\t\t-- zoom in\n"
- " -\t\t-- zoom out\n"
- " w\t\t-- shrinkwrap\n"
- "\n"
- " n pgdn space\t-- next page\n"
- " b pgup back\t-- previous page\n"
- " right\t\t-- next page\n"
- " left\t\t-- previous page\n"
- " N F\t\t-- next 10\n"
- " B\t\t-- back 10\n"
- " m\t\t-- mark page for snap back\n"
- " t\t\t-- pop back to last mark\n"
- " 123g\t\t-- go to page\n"
- "\n"
- " left drag to pan, right drag to copy text\n";
-}
-
-void pdfapp_init(pdfapp_t *app)
-{
- fz_error *error;
-
- memset(app, 0, sizeof(pdfapp_t));
-
- error = fz_newrenderer(&app->rast, pdf_devicergb, 0, 1024 * 512);
- if (error)
- pdfapp_error(app, error);
-
- app->scrw = 640;
- app->scrh = 480;
-}
-
-void pdfapp_open(pdfapp_t *app, char *filename)
-{
- fz_error *error;
- fz_obj *obj;
- char *password = "";
-
- /*
- * Open PDF and load xref table
- */
-
- app->filename = filename;
-
- error = pdf_newxref(&app->xref);
- if (error)
- pdfapp_error(app, error);
-
- error = pdf_loadxref(app->xref, filename);
- if (error)
- {
- if (!strncmp(error->msg, "ioerror", 7))
- pdfapp_error(app, error);
- pdfapp_warn(app,
- "There was a problem with file \"%s\".\n"
- "It may be corrupted, or generated by broken software.\n\n"
- "%s\n\nTrying to continue anyway...",
- filename, error->msg);
- error = pdf_repairxref(app->xref, filename);
- if (error)
- pdfapp_error(app, error);
- }
-
- /*
- * Handle encrypted PDF files
- */
-
- error = pdf_decryptxref(app->xref);
- if (error)
- pdfapp_error(app, error);
-
- if (app->xref->crypt)
- {
- error = pdf_setpassword(app->xref->crypt, password);
- while (error)
- {
- fz_droperror(error);
- password = winpassword(app, filename);
- if (!password)
- exit(1);
- error = pdf_setpassword(app->xref->crypt, password);
- if (error)
- pdfapp_warn(app, "Invalid password.");
- }
- }
-
- /*
- * Load page tree
- */
-
- error = pdf_loadpagetree(&app->pages, app->xref);
- if (error)
- pdfapp_error(app, error);
-
- /*
- * Load meta information
- * TODO: move this into mupdf library
- */
-
- obj = fz_dictgets(app->xref->trailer, "Root");
- if (!obj)
- pdfapp_error(app, fz_throw("syntaxerror: missing Root object"));
-
- error = pdf_loadindirect(&app->xref->root, app->xref, obj);
- if (error)
- pdfapp_error(app, error);
-
- obj = fz_dictgets(app->xref->trailer, "Info");
- if (obj)
- {
- error = pdf_loadindirect(&app->xref->info, app->xref, obj);
- if (error)
- pdfapp_error(app, error);
- }
-
- error = pdf_loadnametrees(app->xref);
- if (error)
- pdfapp_error(app, error);
-
- error = pdf_loadoutline(&app->outline, app->xref);
- if (error)
- pdfapp_error(app, error);
-
- app->doctitle = filename;
- if (strrchr(app->doctitle, '\\'))
- app->doctitle = strrchr(app->doctitle, '\\') + 1;
- if (strrchr(app->doctitle, '/'))
- app->doctitle = strrchr(app->doctitle, '/') + 1;
- if (app->xref->info)
- {
- obj = fz_dictgets(app->xref->info, "Title");
- if (obj)
- {
- error = pdf_toutf8(&app->doctitle, obj);
- if (error)
- pdfapp_error(app, error);
- }
- }
-
- /*
- * Start at first page
- */
-
- app->shrinkwrap = 1;
- app->pageno = 1;
- app->zoom = 1.0;
- app->rotate = 0;
- app->panx = 0;
- app->pany = 0;
-
- pdfapp_showpage(app, 1, 1);
-}
-
-void pdfapp_close(pdfapp_t *app)
-{
- if (app->pages)
- pdf_droppagetree(app->pages);
- app->pages = nil;
-
- if (app->page)
- pdf_droppage(app->page);
- app->page = nil;
-
- if (app->image)
- fz_droppixmap(app->image);
- app->image = nil;
-
- if (app->outline)
- pdf_dropoutline(app->outline);
- app->outline = nil;
-
- if (app->xref->store)
- pdf_dropstore(app->xref->store);
- app->xref->store = nil;
-
- pdf_closexref(app->xref);
- app->xref = nil;
-}
-
-fz_matrix pdfapp_viewctm(pdfapp_t *app)
-{
- fz_matrix ctm;
- ctm = fz_identity();
- ctm = fz_concat(ctm, fz_translate(0, -app->page->mediabox.y1));
- ctm = fz_concat(ctm, fz_scale(app->zoom, -app->zoom));
- ctm = fz_concat(ctm, fz_rotate(app->rotate + app->page->rotate));
- return ctm;
-}
-
-void pdfapp_panview(pdfapp_t *app, int newx, int newy)
-{
- if (newx > 0)
- newx = 0;
- if (newy > 0)
- newy = 0;
-
- if (newx + app->image->w < app->winw)
- newx = app->winw - app->image->w;
- if (newy + app->image->h < app->winh)
- newy = app->winh - app->image->h;
-
- if (app->winw >= app->image->w)
- newx = (app->winw - app->image->w) / 2;
- if (app->winh >= app->image->h)
- newy = (app->winh - app->image->h) / 2;
-
- if (newx != app->panx || newy != app->pany)
- winrepaint(app);
-
- app->panx = newx;
- app->pany = newy;
-}
-
-void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage)
-{
- char buf[256];
- fz_error *error;
- fz_matrix ctm;
- fz_rect bbox;
- fz_obj *obj;
-
- if (loadpage)
- {
- wincursor(app, WAIT);
-
- if (app->page)
- pdf_droppage(app->page);
- app->page = nil;
-
- obj = pdf_getpageobject(app->pages, app->pageno - 1);
-
- error = pdf_loadpage(&app->page, app->xref, obj);
- if (error)
- pdfapp_error(app, error);
-
- sprintf(buf, "%s - %d/%d", app->doctitle,
- app->pageno, pdf_getpagecount(app->pages));
- wintitle(app, buf);
- }
-
- if (drawpage)
- {
- wincursor(app, WAIT);
-
- if (app->image)
- fz_droppixmap(app->image);
- app->image = nil;
-
- ctm = pdfapp_viewctm(app);
- bbox = fz_transformaabb(ctm, app->page->mediabox);
-
- error = fz_rendertree(&app->image, app->rast, app->page->tree,
- ctm, fz_roundrect(bbox), 1);
- if (error)
- pdfapp_error(app, error);
-
- winconvert(app, app->image);
- }
-
- pdfapp_panview(app, app->panx, app->pany);
-
- if (app->shrinkwrap)
- {
- int w = app->image->w;
- int h = app->image->h;
- if (app->winw == w)
- app->panx = 0;
- if (app->winh == h)
- app->pany = 0;
- if (w > app->scrw * 95 / 100)
- w = app->scrw * 95 / 100;
- if (h > app->scrh * 95 / 100)
- h = app->scrh * 95 / 100;
- if (w != app->winw || h != app->winh)
- winresize(app, w, h);
- }
-
- winrepaint(app);
-}
-
-
-void pdfapp_gotouri(pdfapp_t *app, fz_obj *uri)
-{
- char buf[2048];
- memcpy(buf, fz_tostrbuf(uri), fz_tostrlen(uri));
- buf[fz_tostrlen(uri)] = 0;
- winopenuri(app, buf);
-}
-
-void pdfapp_gotopage(pdfapp_t *app, fz_obj *obj)
-{
- int oid = fz_tonum(obj);
- int i;
-
- for (i = 0; i < pdf_getpagecount(app->pages); i++)
- {
- if (fz_tonum(app->pages->pref[i]) == oid)
- {
- if (app->histlen + 1 == 256)
- {
- memmove(app->hist, app->hist + 1, sizeof(int) * 255);
- app->histlen --;
- }
- app->hist[app->histlen++] = app->pageno;
- app->pageno = i + 1;
- pdfapp_showpage(app, 1, 1);
- return;
- }
- }
-}
-
-void pdfapp_onresize(pdfapp_t *app, int w, int h)
-{
- if (app->winw != w || app->winh != h)
- {
- app->winw = w;
- app->winh = h;
- pdfapp_panview(app, app->panx, app->pany);
- winrepaint(app);
- }
-}
-
-void pdfapp_onkey(pdfapp_t *app, int c)
-{
- int oldpage = app->pageno;
- int panto = 0; /* 0 = top, 1 = bottom, 2 = leave alone */
-
- /*
- * Save numbers typed for later
- */
-
- if (c >= '0' && c <= '9')
- app->number[app->numberlen++] = c;
- else
- if (c != 'g' && c != 'G')
- app->numberlen = 0;
-
- switch (c)
- {
-
- /*
- * Zoom and rotate
- */
-
- case '+': case '=':
- app->zoom += 0.1;
- if (app->zoom > 3.0)
- app->zoom = 3.0;
- pdfapp_showpage(app, 0, 1);
- break;
- case '-':
- app->zoom -= 0.1;
- if (app->zoom < 0.1)
- app->zoom = 0.1;
- pdfapp_showpage(app, 0, 1);
- break;
- case 'l': case '<':
- app->rotate -= 90;
- pdfapp_showpage(app, 0, 1);
- break;
- case 'r': case '>':
- app->rotate += 90;
- pdfapp_showpage(app, 0, 1);
- break;
-
- /*
- * Pan view, but dont need to repaint image
- */
-
- case 'w':
- app->shrinkwrap = 1;
- app->panx = app->pany = 0;
- pdfapp_showpage(app, 0, 0);
- break;
-
- case 'd':
- app->pany -= app->image->h / 10;
- pdfapp_showpage(app, 0, 0);
- break;
-
- case 'u':
- app->pany += app->image->h / 10;
- pdfapp_showpage(app, 0, 0);
- break;
-
- case ',':
- app->panx += app->image->w / 10;
- pdfapp_showpage(app, 0, 0);
- break;
-
- case '.':
- app->panx -= app->image->w / 10;
- pdfapp_showpage(app, 0, 0);
- break;
-
- /*
- * Page navigation
- */
-
- case 'g':
- case '\n':
- case '\r':
- if (app->numberlen > 0)
- {
- app->number[app->numberlen] = '\0';
- app->pageno = atoi(app->number);
- app->numberlen = 0;
- }
- break;
-
- case 'G':
- app->pageno = pdf_getpagecount(app->pages);
- break;
-
- case 'm':
- if (app->histlen + 1 == 256)
- {
- memmove(app->hist, app->hist + 1, sizeof(int) * 255);
- app->histlen --;
- }
- app->hist[app->histlen++] = app->pageno;
- break;
-
- case 't':
- if (app->histlen > 0)
- app->pageno = app->hist[--app->histlen];
- break;
-
- /*
- * Back and forth ...
- */
-
- case 'p':
- panto = 2;
- app->pageno--;
- break;
-
- case 'b': case '\b':
- panto = 1;
- app->pageno--;
- break;
-
- case 'n':
- panto = 2;
- case 'f': case ' ':
- app->pageno++;
- break;
-
- case 'B':
- app->pageno -= 10;
- break;
-
- case 'F':
- app->pageno += 10;
- break;
- }
-
- if (app->pageno < 1)
- app->pageno = 1;
- if (app->pageno > pdf_getpagecount(app->pages))
- app->pageno = pdf_getpagecount(app->pages);
-
- if (app->pageno != oldpage)
- {
- switch (panto)
- {
- case 0: app->pany = 0; break;
- case 1: app->pany = -2000; break;
- case 2: break;
- }
- pdfapp_showpage(app, 1, 1);
- }
-}
-
-void pdfapp_onmouse(pdfapp_t *app, int x, int y, int btn, int state)
-{
- pdf_link *link;
- fz_matrix ctm;
- fz_point p;
-
- p.x = x - app->panx + app->image->x;
- p.y = y - app->pany + app->image->y;
-
- ctm = pdfapp_viewctm(app);
- ctm = fz_invertmatrix(ctm);
-
- p = fz_transformpoint(ctm, p);
-
- for (link = app->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)
- {
- wincursor(app, HAND);
- if (btn == 1 && state == 1)
- {
- if (fz_isstring(link->dest))
- pdfapp_gotouri(app, link->dest);
- if (fz_isindirect(link->dest))
- pdfapp_gotopage(app, link->dest);
- return;
- }
- }
- else
- {
- wincursor(app, ARROW);
- }
-
- if (state == 1)
- {
- if (btn == 1 && !app->iscopying)
- {
- app->ispanning = 1;
- app->selx = x;
- app->sely = y;
- }
- if (btn == 3 && !app->ispanning)
- {
- app->iscopying = 1;
- app->selx = x;
- app->sely = y;
- app->selr.x0 = x;
- app->selr.x1 = x;
- app->selr.y0 = y;
- app->selr.y1 = y;
- }
- }
-
- else if (state == -1)
- {
- if (app->iscopying)
- {
- app->iscopying = 0;
- app->selr.x0 = MIN(app->selx, x);
- app->selr.x1 = MAX(app->selx, x);
- app->selr.y0 = MIN(app->sely, y);
- app->selr.y1 = MAX(app->sely, y);
- winrepaint(app);
- if (app->selr.x0 < app->selr.x1 && app->selr.y0 < app->selr.y1)
- windocopy(app);
- }
- if (app->ispanning)
- app->ispanning = 0;
- }
-
- else if (app->ispanning)
- {
- int newx = app->panx + x - app->selx;
- int newy = app->pany + y - app->sely;
- pdfapp_panview(app, newx, newy);
- app->selx = x;
- app->sely = y;
- }
-
- else if (app->iscopying)
- {
- app->selr.x0 = MIN(app->selx, x);
- app->selr.x1 = MAX(app->selx, x);
- app->selr.y0 = MIN(app->sely, y);
- app->selr.y1 = MAX(app->sely, y);
- winrepaint(app);
- }
-
-}
-
-void pdfapp_oncopy(pdfapp_t *app, unsigned short *ucsbuf, int ucslen)
-{
- fz_error *error;
- pdf_textline *line, *ln;
- int x, y, c;
- int i, p;
-
- int x0 = app->image->x + app->selr.x0 - app->panx;
- int x1 = app->image->x + app->selr.x1 - app->panx;
- int y0 = app->image->y + app->selr.y0 - app->pany;
- int y1 = app->image->y + app->selr.y1 - app->pany;
-
- error = pdf_loadtextfromtree(&line, app->page->tree, pdfapp_viewctm(app));
- if (error)
- pdfapp_error(app, error);
-
- p = 0;
- for (ln = line; ln; ln = ln->next)
- {
- y = y0 - 1;
- for (i = 0; i < ln->len; i++)
- {
- x = ln->text[i].x;
- y = ln->text[i].y;
- c = ln->text[i].c;
- if (c < 32)
- c = '?';
- if (x >= x0 && x <= x1 && y >= y0 && y <= y1)
- if (p < ucslen - 1)
- ucsbuf[p++] = c;
- }
-
- if (y >= y0 && y <= y1)
- {
- if (p < ucslen - 1)
- ucsbuf[p++] = '\r';
-#ifdef WIN32
- if (p < ucslen - 1)
- ucsbuf[p++] = '\n';
-#endif
- }
- }
-
- ucsbuf[p] = 0;
-
- pdf_droptextline(line);
-}
-
+#include <fitz.h> +#include <mupdf.h> +#include "pdfapp.h" + +void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage); + +void pdfapp_warn(pdfapp_t *app, const char *fmt, ...) +{ + char buf[1024]; + va_list ap; + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + winwarn(app, buf); +} + +void pdfapp_error(pdfapp_t *app, fz_error *error) +{ + winerror(app, error->msg); +} + +char *pdfapp_usage(pdfapp_t *app) +{ + return + " l <\t\t-- rotate left\n" + " r >\t\t-- rotate right\n" + " u up\t\t-- scroll up\n" + " d down\t-- scroll down\n" + " = +\t\t-- zoom in\n" + " -\t\t-- zoom out\n" + " w\t\t-- shrinkwrap\n" + "\n" + " n pgdn space\t-- next page\n" + " b pgup back\t-- previous page\n" + " right\t\t-- next page\n" + " left\t\t-- previous page\n" + " N F\t\t-- next 10\n" + " B\t\t-- back 10\n" + " m\t\t-- mark page for snap back\n" + " t\t\t-- pop back to last mark\n" + " 123g\t\t-- go to page\n" + "\n" + " left drag to pan, right drag to copy text\n"; +} + +void pdfapp_init(pdfapp_t *app) +{ + fz_error *error; + + memset(app, 0, sizeof(pdfapp_t)); + + error = fz_newrenderer(&app->rast, pdf_devicergb, 0, 1024 * 512); + if (error) + pdfapp_error(app, error); + + app->scrw = 640; + app->scrh = 480; +} + +void pdfapp_open(pdfapp_t *app, char *filename) +{ + fz_error *error; + fz_obj *obj; + char *password = ""; + + /* + * Open PDF and load xref table + */ + + app->filename = filename; + + error = pdf_newxref(&app->xref); + if (error) + pdfapp_error(app, error); + + error = pdf_loadxref(app->xref, filename); + if (error) + { + if (!strncmp(error->msg, "ioerror", 7)) + pdfapp_error(app, error); + pdfapp_warn(app, + "There was a problem with file \"%s\".\n" + "It may be corrupted, or generated by broken software.\n\n" + "%s\n\nTrying to continue anyway...", + filename, error->msg); + error = pdf_repairxref(app->xref, filename); + if (error) + pdfapp_error(app, error); + } + + /* + * Handle encrypted PDF files + */ + + error = pdf_decryptxref(app->xref); + if (error) + pdfapp_error(app, error); + + if (app->xref->crypt) + { + error = pdf_setpassword(app->xref->crypt, password); + while (error) + { + fz_droperror(error); + password = winpassword(app, filename); + if (!password) + exit(1); + error = pdf_setpassword(app->xref->crypt, password); + if (error) + pdfapp_warn(app, "Invalid password."); + } + } + + /* + * Load page tree + */ + + error = pdf_loadpagetree(&app->pages, app->xref); + if (error) + pdfapp_error(app, error); + + /* + * Load meta information + * TODO: move this into mupdf library + */ + + obj = fz_dictgets(app->xref->trailer, "Root"); + if (!obj) + pdfapp_error(app, fz_throw("syntaxerror: missing Root object")); + + error = pdf_loadindirect(&app->xref->root, app->xref, obj); + if (error) + pdfapp_error(app, error); + + obj = fz_dictgets(app->xref->trailer, "Info"); + if (obj) + { + error = pdf_loadindirect(&app->xref->info, app->xref, obj); + if (error) + pdfapp_error(app, error); + } + + error = pdf_loadnametrees(app->xref); + if (error) + pdfapp_error(app, error); + + error = pdf_loadoutline(&app->outline, app->xref); + if (error) + pdfapp_error(app, error); + + app->doctitle = filename; + if (strrchr(app->doctitle, '\\')) + app->doctitle = strrchr(app->doctitle, '\\') + 1; + if (strrchr(app->doctitle, '/')) + app->doctitle = strrchr(app->doctitle, '/') + 1; + if (app->xref->info) + { + obj = fz_dictgets(app->xref->info, "Title"); + if (obj) + { + error = pdf_toutf8(&app->doctitle, obj); + if (error) + pdfapp_error(app, error); + } + } + + /* + * Start at first page + */ + + app->shrinkwrap = 1; + app->pageno = 1; + app->zoom = 1.0; + app->rotate = 0; + app->panx = 0; + app->pany = 0; + + pdfapp_showpage(app, 1, 1); +} + +void pdfapp_close(pdfapp_t *app) +{ + if (app->pages) + pdf_droppagetree(app->pages); + app->pages = nil; + + if (app->page) + pdf_droppage(app->page); + app->page = nil; + + if (app->image) + fz_droppixmap(app->image); + app->image = nil; + + if (app->outline) + pdf_dropoutline(app->outline); + app->outline = nil; + + if (app->xref->store) + pdf_dropstore(app->xref->store); + app->xref->store = nil; + + pdf_closexref(app->xref); + app->xref = nil; +} + +fz_matrix pdfapp_viewctm(pdfapp_t *app) +{ + fz_matrix ctm; + ctm = fz_identity(); + ctm = fz_concat(ctm, fz_translate(0, -app->page->mediabox.y1)); + ctm = fz_concat(ctm, fz_scale(app->zoom, -app->zoom)); + ctm = fz_concat(ctm, fz_rotate(app->rotate + app->page->rotate)); + return ctm; +} + +void pdfapp_panview(pdfapp_t *app, int newx, int newy) +{ + if (newx > 0) + newx = 0; + if (newy > 0) + newy = 0; + + if (newx + app->image->w < app->winw) + newx = app->winw - app->image->w; + if (newy + app->image->h < app->winh) + newy = app->winh - app->image->h; + + if (app->winw >= app->image->w) + newx = (app->winw - app->image->w) / 2; + if (app->winh >= app->image->h) + newy = (app->winh - app->image->h) / 2; + + if (newx != app->panx || newy != app->pany) + winrepaint(app); + + app->panx = newx; + app->pany = newy; +} + +void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage) +{ + char buf[256]; + fz_error *error; + fz_matrix ctm; + fz_rect bbox; + fz_obj *obj; + + if (loadpage) + { + wincursor(app, WAIT); + + if (app->page) + pdf_droppage(app->page); + app->page = nil; + + obj = pdf_getpageobject(app->pages, app->pageno - 1); + + error = pdf_loadpage(&app->page, app->xref, obj); + if (error) + pdfapp_error(app, error); + + sprintf(buf, "%s - %d/%d", app->doctitle, + app->pageno, pdf_getpagecount(app->pages)); + wintitle(app, buf); + } + + if (drawpage) + { + wincursor(app, WAIT); + + if (app->image) + fz_droppixmap(app->image); + app->image = nil; + + ctm = pdfapp_viewctm(app); + bbox = fz_transformaabb(ctm, app->page->mediabox); + + error = fz_rendertree(&app->image, app->rast, app->page->tree, + ctm, fz_roundrect(bbox), 1); + if (error) + pdfapp_error(app, error); + + winconvert(app, app->image); + } + + pdfapp_panview(app, app->panx, app->pany); + + if (app->shrinkwrap) + { + int w = app->image->w; + int h = app->image->h; + if (app->winw == w) + app->panx = 0; + if (app->winh == h) + app->pany = 0; + if (w > app->scrw * 90 / 100) + w = app->scrw * 90 / 100; + if (h > app->scrh * 90 / 100) + h = app->scrh * 90 / 100; + if (w != app->winw || h != app->winh) + winresize(app, w, h); + } + + winrepaint(app); +} + + +void pdfapp_gotouri(pdfapp_t *app, fz_obj *uri) +{ + char buf[2048]; + memcpy(buf, fz_tostrbuf(uri), fz_tostrlen(uri)); + buf[fz_tostrlen(uri)] = 0; + winopenuri(app, buf); +} + +void pdfapp_gotopage(pdfapp_t *app, fz_obj *obj) +{ + int oid = fz_tonum(obj); + int i; + + for (i = 0; i < pdf_getpagecount(app->pages); i++) + { + if (fz_tonum(app->pages->pref[i]) == oid) + { + if (app->histlen + 1 == 256) + { + memmove(app->hist, app->hist + 1, sizeof(int) * 255); + app->histlen --; + } + app->hist[app->histlen++] = app->pageno; + app->pageno = i + 1; + pdfapp_showpage(app, 1, 1); + return; + } + } +} + +void pdfapp_onresize(pdfapp_t *app, int w, int h) +{ + if (app->winw != w || app->winh != h) + { + app->winw = w; + app->winh = h; + pdfapp_panview(app, app->panx, app->pany); + winrepaint(app); + } +} + +void pdfapp_onkey(pdfapp_t *app, int c) +{ + int oldpage = app->pageno; + int panto = 0; /* 0 = top, 1 = bottom, 2 = leave alone */ + + /* + * Save numbers typed for later + */ + + if (c >= '0' && c <= '9') + app->number[app->numberlen++] = c; + else + if (c != 'g' && c != 'G') + app->numberlen = 0; + + switch (c) + { + + /* + * Zoom and rotate + */ + + case '+': case '=': + app->zoom += 0.1; + if (app->zoom > 3.0) + app->zoom = 3.0; + pdfapp_showpage(app, 0, 1); + break; + case '-': + app->zoom -= 0.1; + if (app->zoom < 0.1) + app->zoom = 0.1; + pdfapp_showpage(app, 0, 1); + break; + case 'l': case '<': + app->rotate -= 90; + pdfapp_showpage(app, 0, 1); + break; + case 'r': case '>': + app->rotate += 90; + pdfapp_showpage(app, 0, 1); + break; + + /* + * Pan view, but dont need to repaint image + */ + + case 'w': + app->shrinkwrap = 1; + app->panx = app->pany = 0; + pdfapp_showpage(app, 0, 0); + break; + + case 'd': + app->pany -= app->image->h / 10; + pdfapp_showpage(app, 0, 0); + break; + + case 'u': + app->pany += app->image->h / 10; + pdfapp_showpage(app, 0, 0); + break; + + case ',': + app->panx += app->image->w / 10; + pdfapp_showpage(app, 0, 0); + break; + + case '.': + app->panx -= app->image->w / 10; + pdfapp_showpage(app, 0, 0); + break; + + /* + * Page navigation + */ + + case 'g': + case '\n': + case '\r': + if (app->numberlen > 0) + { + app->number[app->numberlen] = '\0'; + app->pageno = atoi(app->number); + app->numberlen = 0; + } + break; + + case 'G': + app->pageno = pdf_getpagecount(app->pages); + break; + + case 'm': + if (app->histlen + 1 == 256) + { + memmove(app->hist, app->hist + 1, sizeof(int) * 255); + app->histlen --; + } + app->hist[app->histlen++] = app->pageno; + break; + + case 't': + if (app->histlen > 0) + app->pageno = app->hist[--app->histlen]; + break; + + /* + * Back and forth ... + */ + + case 'p': + panto = 2; + app->pageno--; + break; + + case 'b': case '\b': + panto = 1; + app->pageno--; + break; + + case 'n': + panto = 2; + case 'f': case ' ': + app->pageno++; + break; + + case 'B': + app->pageno -= 10; + break; + + case 'F': + app->pageno += 10; + break; + } + + if (app->pageno < 1) + app->pageno = 1; + if (app->pageno > pdf_getpagecount(app->pages)) + app->pageno = pdf_getpagecount(app->pages); + + if (app->pageno != oldpage) + { + switch (panto) + { + case 0: app->pany = 0; break; + case 1: app->pany = -2000; break; + case 2: break; + } + pdfapp_showpage(app, 1, 1); + } +} + +void pdfapp_onmouse(pdfapp_t *app, int x, int y, int btn, int state) +{ + pdf_link *link; + fz_matrix ctm; + fz_point p; + + p.x = x - app->panx + app->image->x; + p.y = y - app->pany + app->image->y; + + ctm = pdfapp_viewctm(app); + ctm = fz_invertmatrix(ctm); + + p = fz_transformpoint(ctm, p); + + for (link = app->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) + { + wincursor(app, HAND); + if (btn == 1 && state == 1) + { + if (fz_isstring(link->dest)) + pdfapp_gotouri(app, link->dest); + if (fz_isindirect(link->dest)) + pdfapp_gotopage(app, link->dest); + return; + } + } + else + { + wincursor(app, ARROW); + } + + if (state == 1) + { + if (btn == 1 && !app->iscopying) + { + app->ispanning = 1; + app->selx = x; + app->sely = y; + } + if (btn == 3 && !app->ispanning) + { + app->iscopying = 1; + app->selx = x; + app->sely = y; + app->selr.x0 = x; + app->selr.x1 = x; + app->selr.y0 = y; + app->selr.y1 = y; + } + } + + else if (state == -1) + { + if (app->iscopying) + { + app->iscopying = 0; + app->selr.x0 = MIN(app->selx, x); + app->selr.x1 = MAX(app->selx, x); + app->selr.y0 = MIN(app->sely, y); + app->selr.y1 = MAX(app->sely, y); + winrepaint(app); + if (app->selr.x0 < app->selr.x1 && app->selr.y0 < app->selr.y1) + windocopy(app); + } + if (app->ispanning) + app->ispanning = 0; + } + + else if (app->ispanning) + { + int newx = app->panx + x - app->selx; + int newy = app->pany + y - app->sely; + pdfapp_panview(app, newx, newy); + app->selx = x; + app->sely = y; + } + + else if (app->iscopying) + { + app->selr.x0 = MIN(app->selx, x); + app->selr.x1 = MAX(app->selx, x); + app->selr.y0 = MIN(app->sely, y); + app->selr.y1 = MAX(app->sely, y); + winrepaint(app); + } + +} + +void pdfapp_oncopy(pdfapp_t *app, unsigned short *ucsbuf, int ucslen) +{ + fz_error *error; + pdf_textline *line, *ln; + int x, y, c; + int i, p; + + int x0 = app->image->x + app->selr.x0 - app->panx; + int x1 = app->image->x + app->selr.x1 - app->panx; + int y0 = app->image->y + app->selr.y0 - app->pany; + int y1 = app->image->y + app->selr.y1 - app->pany; + + error = pdf_loadtextfromtree(&line, app->page->tree, pdfapp_viewctm(app)); + if (error) + pdfapp_error(app, error); + + p = 0; + for (ln = line; ln; ln = ln->next) + { + y = y0 - 1; + for (i = 0; i < ln->len; i++) + { + x = ln->text[i].x; + y = ln->text[i].y; + c = ln->text[i].c; + if (c < 32) + c = '?'; + if (x >= x0 && x <= x1 && y >= y0 && y <= y1) + if (p < ucslen - 1) + ucsbuf[p++] = c; + } + + if (y >= y0 && y <= y1) + { +#ifdef WIN32 + if (p < ucslen - 1) + ucsbuf[p++] = '\r'; +#endif + if (p < ucslen - 1) + ucsbuf[p++] = '\n'; + } + } + + ucsbuf[p] = 0; + + pdf_droptextline(line); +} + |