summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jamrules1
-rw-r--r--apps/unix/x11pdf.c197
-rw-r--r--apps/windows/winmain.c1526
-rw-r--r--apps/windows/winres.rc16
-rw-r--r--mupdf/pdfapp.c1288
5 files changed, 1585 insertions, 1443 deletions
diff --git a/Jamrules b/Jamrules
index 86cec232..cf0c3e63 100644
--- a/Jamrules
+++ b/Jamrules
@@ -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);
+}
+