1 #ifndef UNICODE
2 #define UNICODE
3 #endif
4 #ifndef _UNICODE
5 #define _UNICODE
6 #endif
7 #define WIN32_LEAN_AND_MEAN
8 #include <windows.h>
9 #include <commdlg.h>
10 #include <shellapi.h>
11 
12 /* Include pdfapp.h *AFTER* the UNICODE defines */
13 #include "pdfapp.h"
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <assert.h>
18 
19 #ifndef WM_MOUSEWHEEL
20 #define WM_MOUSEWHEEL 0x020A
21 #endif
22 
23 #ifndef PATH_MAX
24 #define PATH_MAX 4096
25 #endif
26 
27 #define MIN(x,y) ((x) < (y) ? (x) : (y))
28 
29 #define ID_ABOUT	0x1000
30 #define ID_DOCINFO	0x1001
31 
32 static HWND hwndframe = NULL;
33 static HWND hwndview = NULL;
34 static HDC hdc;
35 static HBRUSH bgbrush;
36 static BITMAPINFO *dibinf = NULL;
37 static HCURSOR arrowcurs, handcurs, waitcurs, caretcurs;
38 static LRESULT CALLBACK frameproc(HWND, UINT, WPARAM, LPARAM);
39 static LRESULT CALLBACK viewproc(HWND, UINT, WPARAM, LPARAM);
40 static int timer_pending = 0;
41 static char *password = NULL;
42 
43 static int justcopied = 0;
44 
45 static pdfapp_t gapp;
46 
47 static wchar_t wbuf[PATH_MAX];
48 static char filename[PATH_MAX];
49 
50 /*
51  * Create registry keys to associate MuPDF with PDF and XPS files.
52  */
53 
54 #define OPEN_KEY(parent, name, ptr) \
55 	RegCreateKeyExA(parent, name, 0, 0, 0, KEY_WRITE, 0, &ptr, 0)
56 
57 #define SET_KEY(parent, name, value) \
58 	RegSetValueExA(parent, name, 0, REG_SZ, (const BYTE *)(value), (DWORD)strlen(value) + 1)
59 
install_app(char * argv0)60 static void install_app(char *argv0)
61 {
62 	char buf[512];
63 	HKEY software, classes, mupdf, dotpdf, dotxps, dotepub, dotfb2;
64 	HKEY shell, open, command, supported_types;
65 	HKEY pdf_progids, xps_progids, epub_progids, fb2_progids;
66 
67 	OPEN_KEY(HKEY_CURRENT_USER, "Software", software);
68 	OPEN_KEY(software, "Classes", classes);
69 	OPEN_KEY(classes, ".pdf", dotpdf);
70 	OPEN_KEY(dotpdf, "OpenWithProgids", pdf_progids);
71 	OPEN_KEY(classes, ".xps", dotxps);
72 	OPEN_KEY(dotxps, "OpenWithProgids", xps_progids);
73 	OPEN_KEY(classes, ".epub", dotepub);
74 	OPEN_KEY(dotepub, "OpenWithProgids", epub_progids);
75 	OPEN_KEY(classes, ".fb2", dotfb2);
76 	OPEN_KEY(dotfb2, "OpenWithProgids", fb2_progids);
77 	OPEN_KEY(classes, "MuPDF", mupdf);
78 	OPEN_KEY(mupdf, "SupportedTypes", supported_types);
79 	OPEN_KEY(mupdf, "shell", shell);
80 	OPEN_KEY(shell, "open", open);
81 	OPEN_KEY(open, "command", command);
82 
83 	sprintf(buf, "\"%s\" \"%%1\"", argv0);
84 
85 	SET_KEY(open, "FriendlyAppName", "MuPDF");
86 	SET_KEY(command, "", buf);
87 	SET_KEY(supported_types, ".pdf", "");
88 	SET_KEY(supported_types, ".xps", "");
89 	SET_KEY(supported_types, ".epub", "");
90 	SET_KEY(pdf_progids, "MuPDF", "");
91 	SET_KEY(xps_progids, "MuPDF", "");
92 	SET_KEY(epub_progids, "MuPDF", "");
93 	SET_KEY(fb2_progids, "MuPDF", "");
94 
95 	RegCloseKey(dotfb2);
96 	RegCloseKey(dotepub);
97 	RegCloseKey(dotxps);
98 	RegCloseKey(dotpdf);
99 	RegCloseKey(mupdf);
100 	RegCloseKey(classes);
101 	RegCloseKey(software);
102 }
103 
104 /*
105  * Dialog boxes
106  */
107 
winwarn(pdfapp_t * app,char * msg)108 void winwarn(pdfapp_t *app, char *msg)
109 {
110 	MessageBoxA(hwndframe, msg, "MuPDF: Warning", MB_ICONWARNING);
111 }
112 
winerror(pdfapp_t * app,char * msg)113 void winerror(pdfapp_t *app, char *msg)
114 {
115 	MessageBoxA(hwndframe, msg, "MuPDF: Error", MB_ICONERROR);
116 	exit(1);
117 }
118 
winalert(pdfapp_t * app,pdf_alert_event * alert)119 void winalert(pdfapp_t *app, pdf_alert_event *alert)
120 {
121 	int buttons = MB_OK;
122 	int icon = MB_ICONWARNING;
123 	int pressed = PDF_ALERT_BUTTON_NONE;
124 
125 	switch (alert->icon_type)
126 	{
127 	case PDF_ALERT_ICON_ERROR:
128 		icon = MB_ICONERROR;
129 		break;
130 	case PDF_ALERT_ICON_WARNING:
131 		icon = MB_ICONWARNING;
132 		break;
133 	case PDF_ALERT_ICON_QUESTION:
134 		icon = MB_ICONQUESTION;
135 		break;
136 	case PDF_ALERT_ICON_STATUS:
137 		icon = MB_ICONINFORMATION;
138 		break;
139 	}
140 
141 	switch (alert->button_group_type)
142 	{
143 	case PDF_ALERT_BUTTON_GROUP_OK:
144 		buttons = MB_OK;
145 		break;
146 	case PDF_ALERT_BUTTON_GROUP_OK_CANCEL:
147 		buttons = MB_OKCANCEL;
148 		break;
149 	case PDF_ALERT_BUTTON_GROUP_YES_NO:
150 		buttons = MB_YESNO;
151 		break;
152 	case PDF_ALERT_BUTTON_GROUP_YES_NO_CANCEL:
153 		buttons = MB_YESNOCANCEL;
154 		break;
155 	}
156 
157 	pressed = MessageBoxA(hwndframe, alert->message, alert->title, icon|buttons);
158 
159 	switch (pressed)
160 	{
161 	case IDOK:
162 		alert->button_pressed = PDF_ALERT_BUTTON_OK;
163 		break;
164 	case IDCANCEL:
165 		alert->button_pressed = PDF_ALERT_BUTTON_CANCEL;
166 		break;
167 	case IDNO:
168 		alert->button_pressed = PDF_ALERT_BUTTON_NO;
169 		break;
170 	case IDYES:
171 		alert->button_pressed = PDF_ALERT_BUTTON_YES;
172 	}
173 }
174 
winprint(pdfapp_t * app)175 void winprint(pdfapp_t *app)
176 {
177 	MessageBoxA(hwndframe, "The MuPDF library supports printing, but this application currently does not", "Print document", MB_ICONWARNING);
178 }
179 
winsavequery(pdfapp_t * app)180 int winsavequery(pdfapp_t *app)
181 {
182 	switch(MessageBoxA(hwndframe, "File has unsaved changes. Do you want to save", "MuPDF", MB_YESNOCANCEL))
183 	{
184 	case IDYES: return SAVE;
185 	case IDNO: return DISCARD;
186 	default: return CANCEL;
187 	}
188 }
189 
winquery(pdfapp_t * app,const char * query)190 int winquery(pdfapp_t *app, const char *query)
191 {
192 	switch(MessageBoxA(hwndframe, query, "MuPDF", MB_YESNOCANCEL))
193 	{
194 	case IDYES: return QUERY_YES;
195 	case IDNO:
196 	default: return QUERY_NO;
197 	}
198 }
199 
winfilename(wchar_t * buf,int len)200 static int winfilename(wchar_t *buf, int len)
201 {
202 	OPENFILENAME ofn;
203 	buf[0] = 0;
204 	memset(&ofn, 0, sizeof(OPENFILENAME));
205 	ofn.lStructSize = sizeof(OPENFILENAME);
206 	ofn.hwndOwner = hwndframe;
207 	ofn.lpstrFile = buf;
208 	ofn.nMaxFile = len;
209 	ofn.lpstrInitialDir = NULL;
210 	ofn.lpstrTitle = L"MuPDF: Open PDF file";
211 	ofn.lpstrFilter = L"Documents (*.pdf;*.xps;*.cbz;*.epub;*.fb2;*.zip;*.png;*.jpeg;*.tiff)\0*.zip;*.cbz;*.xps;*.epub;*.fb2;*.pdf;*.jpe;*.jpg;*.jpeg;*.jfif;*.tif;*.tiff\0PDF Files (*.pdf)\0*.pdf\0XPS Files (*.xps)\0*.xps\0CBZ Files (*.cbz;*.zip)\0*.zip;*.cbz\0EPUB Files (*.epub)\0*.epub\0FictionBook 2 Files (*.fb2)\0*.fb2\0Image Files (*.png;*.jpeg;*.tiff)\0*.png;*.jpg;*.jpe;*.jpeg;*.jfif;*.tif;*.tiff\0All Files\0*\0\0";
212 	ofn.Flags = OFN_FILEMUSTEXIST|OFN_HIDEREADONLY;
213 	return GetOpenFileNameW(&ofn);
214 }
215 
wingetcertpath(pdfapp_t * app,char * buf,int len)216 int wingetcertpath(pdfapp_t *app, char *buf, int len)
217 {
218 	wchar_t twbuf[PATH_MAX] = {0};
219 	OPENFILENAME ofn;
220 	buf[0] = 0;
221 	memset(&ofn, 0, sizeof(OPENFILENAME));
222 	ofn.lStructSize = sizeof(OPENFILENAME);
223 	ofn.hwndOwner = hwndframe;
224 	ofn.lpstrFile = twbuf;
225 	ofn.nMaxFile = PATH_MAX;
226 	ofn.lpstrInitialDir = NULL;
227 	ofn.lpstrTitle = L"MuPDF: Select certificate file";
228 	ofn.lpstrFilter = L"Certificates (*.pfx)\0*.pfx\0All files\0*\0\0";
229 	ofn.Flags = OFN_FILEMUSTEXIST;
230 	if (GetOpenFileNameW(&ofn))
231 	{
232 		int code = WideCharToMultiByte(CP_UTF8, 0, twbuf, -1, buf, MIN(PATH_MAX, len), NULL, NULL);
233 		if (code == 0)
234 		{
235 			pdfapp_error(app, "cannot convert filename to utf-8");
236 			return 0;
237 		}
238 
239 		return 1;
240 	}
241 	else
242 	{
243 		return 0;
244 	}
245 }
246 
wingetsavepath(pdfapp_t * app,char * buf,int len)247 int wingetsavepath(pdfapp_t *app, char *buf, int len)
248 {
249 	wchar_t twbuf[PATH_MAX];
250 	OPENFILENAME ofn;
251 
252 	wcscpy(twbuf, wbuf);
253 	memset(&ofn, 0, sizeof(OPENFILENAME));
254 	ofn.lStructSize = sizeof(OPENFILENAME);
255 	ofn.hwndOwner = hwndframe;
256 	ofn.lpstrFile = twbuf;
257 	ofn.nMaxFile = PATH_MAX;
258 	ofn.lpstrInitialDir = NULL;
259 	ofn.lpstrTitle = L"MuPDF: Save PDF file";
260 	ofn.lpstrFilter = L"PDF Documents (*.pdf)\0*.pdf\0All Files\0*\0\0";
261 	ofn.Flags = OFN_HIDEREADONLY;
262 	if (GetSaveFileName(&ofn))
263 	{
264 		int code = WideCharToMultiByte(CP_UTF8, 0, twbuf, -1, buf, MIN(PATH_MAX, len), NULL, NULL);
265 		if (code == 0)
266 		{
267 			pdfapp_error(app, "cannot convert filename to utf-8");
268 			return 0;
269 		}
270 
271 		wcscpy(wbuf, twbuf);
272 		strcpy(filename, buf);
273 		return 1;
274 	}
275 	else
276 	{
277 		return 0;
278 	}
279 }
280 
winreplacefile(pdfapp_t * app,char * source,char * target)281 void winreplacefile(pdfapp_t *app, char *source, char *target)
282 {
283 	wchar_t wsource[PATH_MAX];
284 	wchar_t wtarget[PATH_MAX];
285 
286 	int sz = MultiByteToWideChar(CP_UTF8, 0, source, -1, wsource, PATH_MAX);
287 	if (sz == 0)
288 	{
289 		pdfapp_error(app, "cannot convert filename to Unicode");
290 		return;
291 	}
292 
293 	sz = MultiByteToWideChar(CP_UTF8, 0, target, -1, wtarget, PATH_MAX);
294 	if (sz == 0)
295 	{
296 		pdfapp_error(app, "cannot convert filename to Unicode");
297 		return;
298 	}
299 
300 #if (_WIN32_WINNT >= 0x0500)
301 	ReplaceFile(wtarget, wsource, NULL, REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL);
302 #else
303 	DeleteFile(wtarget);
304 	MoveFile(wsource, wtarget);
305 #endif
306 }
307 
wincopyfile(pdfapp_t * app,char * source,char * target)308 void wincopyfile(pdfapp_t *app, char *source, char *target)
309 {
310 	wchar_t wsource[PATH_MAX];
311 	wchar_t wtarget[PATH_MAX];
312 
313 	int sz = MultiByteToWideChar(CP_UTF8, 0, source, -1, wsource, PATH_MAX);
314 	if (sz == 0)
315 	{
316 		pdfapp_error(app, "cannot convert filename to Unicode");
317 		return;
318 	}
319 
320 	sz = MultiByteToWideChar(CP_UTF8, 0, target, -1, wtarget, PATH_MAX);
321 	if (sz == 0)
322 	{
323 		pdfapp_error(app, "cannot convert filename to Unicode");
324 		return;
325 	}
326 
327 	CopyFile(wsource, wtarget, FALSE);
328 }
329 
330 static char pd_filename[256] = "The file is encrypted.";
331 static char pd_password[256] = "";
332 static wchar_t pd_passwordw[256] = {0};
333 static char td_textinput[1024] = "";
334 static int td_retry = 0;
335 static int cd_nopts;
336 static int *cd_nvals;
337 static const char **cd_opts;
338 static const char **cd_vals;
339 static int pd_okay = 0;
340 
341 static INT_PTR CALLBACK
dlogpassproc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)342 dlogpassproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
343 {
344 	switch(message)
345 	{
346 	case WM_INITDIALOG:
347 		SetDlgItemTextA(hwnd, 4, pd_filename);
348 		return TRUE;
349 	case WM_COMMAND:
350 		switch(wParam)
351 		{
352 		case 1:
353 			pd_okay = 1;
354 			GetDlgItemTextW(hwnd, 3, pd_passwordw, nelem(pd_passwordw));
355 			EndDialog(hwnd, 1);
356 			WideCharToMultiByte(CP_UTF8, 0, pd_passwordw, -1, pd_password, sizeof pd_password, NULL, NULL);
357 			return TRUE;
358 		case 2:
359 			pd_okay = 0;
360 			EndDialog(hwnd, 1);
361 			return TRUE;
362 		}
363 		break;
364 	}
365 	return FALSE;
366 }
367 
368 static INT_PTR CALLBACK
dlogtextproc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)369 dlogtextproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
370 {
371 	switch(message)
372 	{
373 	case WM_INITDIALOG:
374 		SetDlgItemTextA(hwnd, 3, td_textinput);
375 		if (!td_retry)
376 			ShowWindow(GetDlgItem(hwnd, 4), SW_HIDE);
377 		return TRUE;
378 	case WM_COMMAND:
379 		switch(wParam)
380 		{
381 		case 1:
382 			pd_okay = 1;
383 			GetDlgItemTextA(hwnd, 3, td_textinput, sizeof td_textinput);
384 			EndDialog(hwnd, 1);
385 			return TRUE;
386 		case 2:
387 			pd_okay = 0;
388 			EndDialog(hwnd, 1);
389 			return TRUE;
390 		}
391 		break;
392 	case WM_CTLCOLORSTATIC:
393 		if ((HWND)lParam == GetDlgItem(hwnd, 4))
394 		{
395 			SetTextColor((HDC)wParam, RGB(255,0,0));
396 			SetBkMode((HDC)wParam, TRANSPARENT);
397 
398 			return (INT_PTR)GetStockObject(NULL_BRUSH);
399 		}
400 		break;
401 	}
402 	return FALSE;
403 }
404 
405 static INT_PTR CALLBACK
dlogchoiceproc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)406 dlogchoiceproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
407 {
408 	HWND listbox;
409 	int i;
410 	int item;
411 	int sel;
412 	switch(message)
413 	{
414 	case WM_INITDIALOG:
415 		listbox = GetDlgItem(hwnd, 3);
416 		for (i = 0; i < cd_nopts; i++)
417 			SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)cd_opts[i]);
418 
419 		/* FIXME: handle multiple select */
420 		if (*cd_nvals > 0)
421 		{
422 			item = SendMessageA(listbox, LB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)cd_vals[0]);
423 			if (item != LB_ERR)
424 				SendMessageA(listbox, LB_SETCURSEL, item, 0);
425 		}
426 		return TRUE;
427 	case WM_COMMAND:
428 		switch(wParam)
429 		{
430 		case 1:
431 			listbox = GetDlgItem(hwnd, 3);
432 			*cd_nvals = 0;
433 			for (i = 0; i < cd_nopts; i++)
434 			{
435 				item = SendMessageA(listbox, LB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)cd_opts[i]);
436 				sel = SendMessageA(listbox, LB_GETSEL, item, 0);
437 				if (sel && sel != LB_ERR)
438 					cd_vals[(*cd_nvals)++] = cd_opts[i];
439 			}
440 			pd_okay = 1;
441 			EndDialog(hwnd, 1);
442 			return TRUE;
443 		case 2:
444 			pd_okay = 0;
445 			EndDialog(hwnd, 1);
446 			return TRUE;
447 		}
448 		break;
449 	}
450 	return FALSE;
451 }
452 
winpassword(pdfapp_t * app,char * filename)453 char *winpassword(pdfapp_t *app, char *filename)
454 {
455 	char buf[1024], *s;
456 	int code;
457 
458 	if (password)
459 	{
460 		char *p = password;
461 		password = NULL;
462 		return p;
463 	}
464 
465 	strcpy(buf, filename);
466 	s = buf;
467 	if (strrchr(s, '\\')) s = strrchr(s, '\\') + 1;
468 	if (strrchr(s, '/')) s = strrchr(s, '/') + 1;
469 	if (strlen(s) > 32)
470 		strcpy(s + 30, "...");
471 	sprintf(pd_filename, "The file \"%s\" is encrypted.", s);
472 	code = DialogBoxW(NULL, L"IDD_DLOGPASS", hwndframe, dlogpassproc);
473 	if (code <= 0)
474 		pdfapp_error(app, "cannot create password dialog");
475 	if (pd_okay)
476 		return pd_password;
477 	return NULL;
478 }
479 
wintextinput(pdfapp_t * app,char * inittext,int retry)480 char *wintextinput(pdfapp_t *app, char *inittext, int retry)
481 {
482 	int code;
483 	td_retry = retry;
484 	fz_strlcpy(td_textinput, inittext ? inittext : "", sizeof td_textinput);
485 	code = DialogBoxW(NULL, L"IDD_DLOGTEXT", hwndframe, dlogtextproc);
486 	if (code <= 0)
487 		pdfapp_error(app, "cannot create text input dialog");
488 	if (pd_okay)
489 		return td_textinput;
490 	return NULL;
491 }
492 
winchoiceinput(pdfapp_t * app,int nopts,const char * opts[],int * nvals,const char * vals[])493 int winchoiceinput(pdfapp_t *app, int nopts, const char *opts[], int *nvals, const char *vals[])
494 {
495 	int code;
496 	cd_nopts = nopts;
497 	cd_nvals = nvals;
498 	cd_opts = opts;
499 	cd_vals = vals;
500 	code = DialogBoxW(NULL, L"IDD_DLOGLIST", hwndframe, dlogchoiceproc);
501 	if (code <= 0)
502 		pdfapp_error(app, "cannot create text input dialog");
503 	return pd_okay;
504 }
505 
506 static INT_PTR CALLBACK
dloginfoproc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)507 dloginfoproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
508 {
509 	char buf[256];
510 	wchar_t bufx[256];
511 	fz_context *ctx = gapp.ctx;
512 	fz_document *doc = gapp.doc;
513 
514 	switch(message)
515 	{
516 	case WM_INITDIALOG:
517 
518 		SetDlgItemTextW(hwnd, 0x10, wbuf);
519 
520 		if (fz_lookup_metadata(ctx, doc, FZ_META_FORMAT, buf, sizeof buf) >= 0)
521 		{
522 			SetDlgItemTextA(hwnd, 0x11, buf);
523 		}
524 		else
525 		{
526 			SetDlgItemTextA(hwnd, 0x11, "Unknown");
527 			SetDlgItemTextA(hwnd, 0x12, "None");
528 			SetDlgItemTextA(hwnd, 0x13, "n/a");
529 			return TRUE;
530 		}
531 
532 		if (fz_lookup_metadata(ctx, doc, FZ_META_ENCRYPTION, buf, sizeof buf) >= 0)
533 		{
534 			SetDlgItemTextA(hwnd, 0x12, buf);
535 		}
536 		else
537 		{
538 			SetDlgItemTextA(hwnd, 0x12, "None");
539 		}
540 
541 		buf[0] = 0;
542 		if (fz_has_permission(ctx, doc, FZ_PERMISSION_PRINT))
543 			strcat(buf, "print, ");
544 		if (fz_has_permission(ctx, doc, FZ_PERMISSION_COPY))
545 			strcat(buf, "copy, ");
546 		if (fz_has_permission(ctx, doc, FZ_PERMISSION_EDIT))
547 			strcat(buf, "edit, ");
548 		if (fz_has_permission(ctx, doc, FZ_PERMISSION_ANNOTATE))
549 			strcat(buf, "annotate, ");
550 		if (strlen(buf) > 2)
551 			buf[strlen(buf)-2] = 0;
552 		else
553 			strcpy(buf, "none");
554 		SetDlgItemTextA(hwnd, 0x13, buf);
555 
556 #define SETUTF8(ID, STRING) \
557 		if (fz_lookup_metadata(ctx, doc, "info:" STRING, buf, sizeof buf) >= 0) \
558 		{ \
559 			MultiByteToWideChar(CP_UTF8, 0, buf, -1, bufx, nelem(bufx)); \
560 			SetDlgItemTextW(hwnd, ID, bufx); \
561 		}
562 
563 		SETUTF8(0x20, "Title");
564 		SETUTF8(0x21, "Author");
565 		SETUTF8(0x22, "Subject");
566 		SETUTF8(0x23, "Keywords");
567 		SETUTF8(0x24, "Creator");
568 		SETUTF8(0x25, "Producer");
569 		SETUTF8(0x26, "CreationDate");
570 		SETUTF8(0x27, "ModDate");
571 		return TRUE;
572 
573 	case WM_COMMAND:
574 		EndDialog(hwnd, 1);
575 		return TRUE;
576 	}
577 	return FALSE;
578 }
579 
info()580 static void info()
581 {
582 	int code = DialogBoxW(NULL, L"IDD_DLOGINFO", hwndframe, dloginfoproc);
583 	if (code <= 0)
584 		pdfapp_error(&gapp, "cannot create info dialog");
585 }
586 
587 static INT_PTR CALLBACK
dlogaboutproc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)588 dlogaboutproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
589 {
590 	switch(message)
591 	{
592 	case WM_INITDIALOG:
593 		SetDlgItemTextA(hwnd, 2, pdfapp_version(&gapp));
594 		SetDlgItemTextA(hwnd, 3, pdfapp_usage(&gapp));
595 		return TRUE;
596 	case WM_COMMAND:
597 		EndDialog(hwnd, 1);
598 		return TRUE;
599 	}
600 	return FALSE;
601 }
602 
winhelp(pdfapp_t * app)603 void winhelp(pdfapp_t*app)
604 {
605 	int code = DialogBoxW(NULL, L"IDD_DLOGABOUT", hwndframe, dlogaboutproc);
606 	if (code <= 0)
607 		pdfapp_error(&gapp, "cannot create help dialog");
608 }
609 
610 /*
611  * Main window
612  */
613 
winopen()614 static void winopen()
615 {
616 	WNDCLASS wc;
617 	HMENU menu;
618 	RECT r;
619 	ATOM a;
620 
621 	/* Create and register window frame class */
622 	memset(&wc, 0, sizeof(wc));
623 	wc.style = 0;
624 	wc.lpfnWndProc = frameproc;
625 	wc.cbClsExtra = 0;
626 	wc.cbWndExtra = 0;
627 	wc.hInstance = GetModuleHandle(NULL);
628 	wc.hIcon = LoadIconA(wc.hInstance, "IDI_ICONAPP");
629 	wc.hCursor = NULL; //LoadCursor(NULL, IDC_ARROW);
630 	wc.hbrBackground = NULL;
631 	wc.lpszMenuName = NULL;
632 	wc.lpszClassName = L"FrameWindow";
633 	a = RegisterClassW(&wc);
634 	if (!a)
635 		pdfapp_error(&gapp, "cannot register frame window class");
636 
637 	/* Create and register window view class */
638 	memset(&wc, 0, sizeof(wc));
639 	wc.style = CS_HREDRAW | CS_VREDRAW;
640 	wc.lpfnWndProc = viewproc;
641 	wc.cbClsExtra = 0;
642 	wc.cbWndExtra = 0;
643 	wc.hInstance = GetModuleHandle(NULL);
644 	wc.hIcon = NULL;
645 	wc.hCursor = NULL;
646 	wc.hbrBackground = NULL;
647 	wc.lpszMenuName = NULL;
648 	wc.lpszClassName = L"ViewWindow";
649 	a = RegisterClassW(&wc);
650 	if (!a)
651 		pdfapp_error(&gapp, "cannot register view window class");
652 
653 	/* Get screen size */
654 	SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
655 	gapp.scrw = r.right - r.left;
656 	gapp.scrh = r.bottom - r.top;
657 
658 	/* Create cursors */
659 	arrowcurs = LoadCursor(NULL, IDC_ARROW);
660 	handcurs = LoadCursor(NULL, IDC_HAND);
661 	waitcurs = LoadCursor(NULL, IDC_WAIT);
662 	caretcurs = LoadCursor(NULL, IDC_IBEAM);
663 
664 	/* And a background color */
665 	bgbrush = CreateSolidBrush(RGB(0x70,0x70,0x70));
666 
667 	/* Init DIB info for buffer */
668 	dibinf = malloc(sizeof(BITMAPINFO) + 12);
669 	assert(dibinf);
670 	dibinf->bmiHeader.biSize = sizeof(dibinf->bmiHeader);
671 	dibinf->bmiHeader.biPlanes = 1;
672 	dibinf->bmiHeader.biBitCount = 32;
673 	dibinf->bmiHeader.biCompression = BI_RGB;
674 	dibinf->bmiHeader.biXPelsPerMeter = 2834;
675 	dibinf->bmiHeader.biYPelsPerMeter = 2834;
676 	dibinf->bmiHeader.biClrUsed = 0;
677 	dibinf->bmiHeader.biClrImportant = 0;
678 	dibinf->bmiHeader.biClrUsed = 0;
679 
680 	/* Create window */
681 	hwndframe = CreateWindowW(L"FrameWindow", // window class name
682 	NULL, // window caption
683 	WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
684 	CW_USEDEFAULT, CW_USEDEFAULT, // initial position
685 	300, // initial x size
686 	300, // initial y size
687 	0, // parent window handle
688 	0, // window menu handle
689 	0, // program instance handle
690 	0); // creation parameters
691 	if (!hwndframe)
692 		pdfapp_error(&gapp, "cannot create frame");
693 
694 	hwndview = CreateWindowW(L"ViewWindow", // window class name
695 	NULL,
696 	WS_VISIBLE | WS_CHILD,
697 	CW_USEDEFAULT, CW_USEDEFAULT,
698 	CW_USEDEFAULT, CW_USEDEFAULT,
699 	hwndframe, 0, 0, 0);
700 	if (!hwndview)
701 		pdfapp_error(&gapp, "cannot create view");
702 
703 	hdc = NULL;
704 
705 	SetWindowTextW(hwndframe, L"MuPDF");
706 
707 	menu = GetSystemMenu(hwndframe, 0);
708 	AppendMenuW(menu, MF_SEPARATOR, 0, NULL);
709 	AppendMenuW(menu, MF_STRING, ID_ABOUT, L"About MuPDF...");
710 	AppendMenuW(menu, MF_STRING, ID_DOCINFO, L"Document Properties...");
711 
712 	SetCursor(arrowcurs);
713 }
714 
715 static void
do_close(pdfapp_t * app)716 do_close(pdfapp_t *app)
717 {
718 	fz_context *ctx = app->ctx;
719 	pdfapp_close(app);
720 	free(dibinf);
721 	fz_drop_context(ctx);
722 }
723 
winclose(pdfapp_t * app)724 void winclose(pdfapp_t *app)
725 {
726 	if (pdfapp_preclose(app))
727 	{
728 		do_close(app);
729 		exit(0);
730 	}
731 }
732 
wincursor(pdfapp_t * app,int curs)733 void wincursor(pdfapp_t *app, int curs)
734 {
735 	if (curs == ARROW)
736 		SetCursor(arrowcurs);
737 	if (curs == HAND)
738 		SetCursor(handcurs);
739 	if (curs == WAIT)
740 		SetCursor(waitcurs);
741 	if (curs == CARET)
742 		SetCursor(caretcurs);
743 }
744 
wintitle(pdfapp_t * app,char * title)745 void wintitle(pdfapp_t *app, char *title)
746 {
747 	wchar_t wide[256], *dp;
748 	char *sp;
749 	int rune;
750 
751 	dp = wide;
752 	sp = title;
753 	while (*sp && dp < wide + 255)
754 	{
755 		sp += fz_chartorune(&rune, sp);
756 		*dp++ = rune;
757 	}
758 	*dp = 0;
759 
760 	SetWindowTextW(hwndframe, wide);
761 }
762 
windrawrect(pdfapp_t * app,int x0,int y0,int x1,int y1)763 static void windrawrect(pdfapp_t *app, int x0, int y0, int x1, int y1)
764 {
765 	RECT r;
766 	r.left = x0;
767 	r.top = y0;
768 	r.right = x1;
769 	r.bottom = y1;
770 	FillRect(hdc, &r, (HBRUSH)GetStockObject(WHITE_BRUSH));
771 }
772 
windrawstring(pdfapp_t * app,int x,int y,char * s)773 void windrawstring(pdfapp_t *app, int x, int y, char *s)
774 {
775 	HFONT font = (HFONT)GetStockObject(ANSI_FIXED_FONT);
776 	SelectObject(hdc, font);
777 	TextOutA(hdc, x, y - 12, s, (int)strlen(s));
778 }
779 
winblitsearch()780 static void winblitsearch()
781 {
782 	if (gapp.issearching)
783 	{
784 		char buf[sizeof(gapp.search) + 50];
785 		sprintf(buf, "Search: %s", gapp.search);
786 		windrawrect(&gapp, 0, 0, gapp.winw, 30);
787 		windrawstring(&gapp, 10, 20, buf);
788 	}
789 }
790 
winblit()791 static void winblit()
792 {
793 	int image_w = fz_pixmap_width(gapp.ctx, gapp.image);
794 	int image_h = fz_pixmap_height(gapp.ctx, gapp.image);
795 	int image_n = fz_pixmap_components(gapp.ctx, gapp.image);
796 	unsigned char *samples = fz_pixmap_samples(gapp.ctx, gapp.image);
797 	int x0 = gapp.panx;
798 	int y0 = gapp.pany;
799 	int x1 = gapp.panx + image_w;
800 	int y1 = gapp.pany + image_h;
801 	RECT r;
802 	HBRUSH brush;
803 
804 	if (gapp.image)
805 	{
806 		if (gapp.iscopying || justcopied)
807 		{
808 			pdfapp_invert(&gapp, gapp.selr);
809 			justcopied = 1;
810 		}
811 
812 		pdfapp_inverthit(&gapp);
813 
814 		dibinf->bmiHeader.biWidth = image_w;
815 		dibinf->bmiHeader.biHeight = -image_h;
816 		dibinf->bmiHeader.biSizeImage = image_h * 4;
817 
818 		if (image_n == 2)
819 		{
820 			size_t i = image_w * (size_t)image_h;
821 			unsigned char *color = malloc(i*4);
822 			unsigned char *s = samples;
823 			unsigned char *d = color;
824 			for (; i > 0 ; i--)
825 			{
826 				d[2] = d[1] = d[0] = *s++;
827 				d[3] = *s++;
828 				d += 4;
829 			}
830 			SetDIBitsToDevice(hdc,
831 				gapp.panx, gapp.pany, image_w, image_h,
832 				0, 0, 0, image_h, color,
833 				dibinf, DIB_RGB_COLORS);
834 			free(color);
835 		}
836 		if (image_n == 4)
837 		{
838 			SetDIBitsToDevice(hdc,
839 				gapp.panx, gapp.pany, image_w, image_h,
840 				0, 0, 0, image_h, samples,
841 				dibinf, DIB_RGB_COLORS);
842 		}
843 
844 		pdfapp_inverthit(&gapp);
845 
846 		if (gapp.iscopying || justcopied)
847 		{
848 			pdfapp_invert(&gapp, gapp.selr);
849 			justcopied = 1;
850 		}
851 	}
852 
853 	if (gapp.invert)
854 		brush = (HBRUSH)GetStockObject(BLACK_BRUSH);
855 	else
856 		brush = bgbrush;
857 
858 	/* Grey background */
859 	r.top = 0; r.bottom = gapp.winh;
860 	r.left = 0; r.right = x0;
861 	FillRect(hdc, &r, brush);
862 	r.left = x1; r.right = gapp.winw;
863 	FillRect(hdc, &r, brush);
864 	r.left = 0; r.right = gapp.winw;
865 	r.top = 0; r.bottom = y0;
866 	FillRect(hdc, &r, brush);
867 	r.top = y1; r.bottom = gapp.winh;
868 	FillRect(hdc, &r, brush);
869 
870 	winblitsearch();
871 }
872 
winresize(pdfapp_t * app,int w,int h)873 void winresize(pdfapp_t *app, int w, int h)
874 {
875 	ShowWindow(hwndframe, SW_SHOWDEFAULT);
876 	w += GetSystemMetrics(SM_CXFRAME) * 2;
877 	h += GetSystemMetrics(SM_CYFRAME) * 2;
878 	h += GetSystemMetrics(SM_CYCAPTION);
879 	SetWindowPos(hwndframe, 0, 0, 0, w, h, SWP_NOZORDER | SWP_NOMOVE);
880 }
881 
winrepaint(pdfapp_t * app)882 void winrepaint(pdfapp_t *app)
883 {
884 	InvalidateRect(hwndview, NULL, 0);
885 }
886 
winrepaintsearch(pdfapp_t * app)887 void winrepaintsearch(pdfapp_t *app)
888 {
889 	// TODO: invalidate only search area and
890 	// call only search redraw routine.
891 	InvalidateRect(hwndview, NULL, 0);
892 }
893 
winfullscreen(pdfapp_t * app,int state)894 void winfullscreen(pdfapp_t *app, int state)
895 {
896 	static WINDOWPLACEMENT savedplace;
897 	static int isfullscreen = 0;
898 	if (state && !isfullscreen)
899 	{
900 		GetWindowPlacement(hwndframe, &savedplace);
901 		SetWindowLong(hwndframe, GWL_STYLE, WS_POPUP | WS_VISIBLE);
902 		SetWindowPos(hwndframe, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED);
903 		ShowWindow(hwndframe, SW_SHOWMAXIMIZED);
904 		isfullscreen = 1;
905 	}
906 	if (!state && isfullscreen)
907 	{
908 		SetWindowLong(hwndframe, GWL_STYLE, WS_OVERLAPPEDWINDOW);
909 		SetWindowPos(hwndframe, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED);
910 		SetWindowPlacement(hwndframe, &savedplace);
911 		isfullscreen = 0;
912 	}
913 }
914 
915 /*
916  * Event handling
917  */
918 
windocopy(pdfapp_t * app)919 void windocopy(pdfapp_t *app)
920 {
921 	HGLOBAL handle;
922 	unsigned short *ucsbuf;
923 
924 	if (!OpenClipboard(hwndframe))
925 		return;
926 	EmptyClipboard();
927 
928 	handle = GlobalAlloc(GMEM_MOVEABLE, 4096 * sizeof(unsigned short));
929 	if (!handle)
930 	{
931 		CloseClipboard();
932 		return;
933 	}
934 
935 	ucsbuf = GlobalLock(handle);
936 	pdfapp_oncopy(&gapp, ucsbuf, 4096);
937 	GlobalUnlock(handle);
938 
939 	SetClipboardData(CF_UNICODETEXT, handle);
940 	CloseClipboard();
941 
942 	justcopied = 1;	/* keep inversion around for a while... */
943 }
944 
winreloadpage(pdfapp_t * app)945 void winreloadpage(pdfapp_t *app)
946 {
947 	SendMessage(hwndview, WM_APP, 0, 0);
948 }
949 
winopenuri(pdfapp_t * app,char * buf)950 void winopenuri(pdfapp_t *app, char *buf)
951 {
952 	ShellExecuteA(hwndframe, "open", buf, 0, 0, SW_SHOWNORMAL);
953 }
954 
955 #define OUR_TIMER_ID 1
956 
winadvancetimer(pdfapp_t * app,float delay)957 void winadvancetimer(pdfapp_t *app, float delay)
958 {
959 	timer_pending = 1;
960 	SetTimer(hwndview, OUR_TIMER_ID, (unsigned int)(1000*delay), NULL);
961 }
962 
killtimer(pdfapp_t * app)963 static void killtimer(pdfapp_t *app)
964 {
965 	timer_pending = 0;
966 }
967 
handlekey(int c)968 static void handlekey(int c)
969 {
970 	int modifier = (GetAsyncKeyState(VK_SHIFT) < 0);
971 	modifier |= ((GetAsyncKeyState(VK_CONTROL) < 0)<<2);
972 
973 	if (timer_pending)
974 		killtimer(&gapp);
975 
976 	if (GetCapture() == hwndview)
977 		return;
978 
979 	if (justcopied)
980 	{
981 		justcopied = 0;
982 		winrepaint(&gapp);
983 	}
984 
985 	/* translate VK into ASCII equivalents */
986 	if (c > 256)
987 	{
988 		switch (c - 256)
989 		{
990 		case VK_ESCAPE: c = '\033'; break;
991 		case VK_DOWN: c = 'j'; break;
992 		case VK_UP: c = 'k'; break;
993 		case VK_LEFT: c = 'h'; break;
994 		case VK_RIGHT: c = 'l'; break;
995 		case VK_PRIOR: c = ','; break;
996 		case VK_NEXT: c = '.'; break;
997 		}
998 	}
999 
1000 	pdfapp_onkey(&gapp, c, modifier);
1001 	winrepaint(&gapp);
1002 }
1003 
handlemouse(int x,int y,int btn,int state)1004 static void handlemouse(int x, int y, int btn, int state)
1005 {
1006 	int modifier = (GetAsyncKeyState(VK_SHIFT) < 0);
1007 	modifier |= ((GetAsyncKeyState(VK_CONTROL) < 0)<<2);
1008 
1009 	if (state != 0 && timer_pending)
1010 		killtimer(&gapp);
1011 
1012 	if (state != 0 && justcopied)
1013 	{
1014 		justcopied = 0;
1015 		winrepaint(&gapp);
1016 	}
1017 
1018 	if (state == 1)
1019 		SetCapture(hwndview);
1020 	if (state == -1)
1021 		ReleaseCapture();
1022 
1023 	pdfapp_onmouse(&gapp, x, y, btn, modifier, state);
1024 }
1025 
1026 static LRESULT CALLBACK
frameproc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)1027 frameproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
1028 {
1029 	switch(message)
1030 	{
1031 	case WM_SETFOCUS:
1032 		PostMessage(hwnd, WM_APP+5, 0, 0);
1033 		return 0;
1034 	case WM_APP+5:
1035 		SetFocus(hwndview);
1036 		return 0;
1037 
1038 	case WM_DESTROY:
1039 		PostQuitMessage(0);
1040 		return 0;
1041 
1042 	case WM_SYSCOMMAND:
1043 		if (wParam == ID_ABOUT)
1044 		{
1045 			winhelp(&gapp);
1046 			return 0;
1047 		}
1048 		if (wParam == ID_DOCINFO)
1049 		{
1050 			info();
1051 			return 0;
1052 		}
1053 		break;
1054 
1055 	case WM_SIZE:
1056 	{
1057 		// More generally, you should use GetEffectiveClientRect
1058 		// if you have a toolbar etc.
1059 		RECT rect;
1060 		GetClientRect(hwnd, &rect);
1061 		MoveWindow(hwndview, rect.left, rect.top,
1062 		rect.right-rect.left, rect.bottom-rect.top, TRUE);
1063 		if (wParam == SIZE_MAXIMIZED)
1064 			gapp.shrinkwrap = 0;
1065 		return 0;
1066 	}
1067 
1068 	case WM_SIZING:
1069 		gapp.shrinkwrap = 0;
1070 		break;
1071 
1072 	case WM_NOTIFY:
1073 	case WM_COMMAND:
1074 		return SendMessage(hwndview, message, wParam, lParam);
1075 
1076 	case WM_CLOSE:
1077 		if (!pdfapp_preclose(&gapp))
1078 			return 0;
1079 	}
1080 
1081 	return DefWindowProc(hwnd, message, wParam, lParam);
1082 }
1083 
1084 static LRESULT CALLBACK
viewproc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)1085 viewproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
1086 {
1087 	static int oldx = 0;
1088 	static int oldy = 0;
1089 	int x = (signed short) LOWORD(lParam);
1090 	int y = (signed short) HIWORD(lParam);
1091 
1092 	switch (message)
1093 	{
1094 	case WM_SIZE:
1095 		if (wParam == SIZE_MINIMIZED)
1096 			return 0;
1097 		if (wParam == SIZE_MAXIMIZED)
1098 			gapp.shrinkwrap = 0;
1099 		pdfapp_onresize(&gapp, LOWORD(lParam), HIWORD(lParam));
1100 		break;
1101 
1102 	/* Paint events are low priority and automagically catenated
1103 	 * so we don't need to do any fancy waiting to defer repainting.
1104 	 */
1105 	case WM_PAINT:
1106 	{
1107 		//puts("WM_PAINT");
1108 		PAINTSTRUCT ps;
1109 		hdc = BeginPaint(hwnd, &ps);
1110 		winblit();
1111 		hdc = NULL;
1112 		EndPaint(hwnd, &ps);
1113 		pdfapp_postblit(&gapp);
1114 		return 0;
1115 	}
1116 
1117 	case WM_ERASEBKGND:
1118 		return 1; // well, we don't need to erase to redraw cleanly
1119 
1120 	/* Mouse events */
1121 
1122 	case WM_LBUTTONDOWN:
1123 		SetFocus(hwndview);
1124 		oldx = x; oldy = y;
1125 		handlemouse(x, y, 1, 1);
1126 		return 0;
1127 	case WM_MBUTTONDOWN:
1128 		SetFocus(hwndview);
1129 		oldx = x; oldy = y;
1130 		handlemouse(x, y, 2, 1);
1131 		return 0;
1132 	case WM_RBUTTONDOWN:
1133 		SetFocus(hwndview);
1134 		oldx = x; oldy = y;
1135 		handlemouse(x, y, 3, 1);
1136 		return 0;
1137 
1138 	case WM_LBUTTONUP:
1139 		oldx = x; oldy = y;
1140 		handlemouse(x, y, 1, -1);
1141 		return 0;
1142 	case WM_MBUTTONUP:
1143 		oldx = x; oldy = y;
1144 		handlemouse(x, y, 2, -1);
1145 		return 0;
1146 	case WM_RBUTTONUP:
1147 		oldx = x; oldy = y;
1148 		handlemouse(x, y, 3, -1);
1149 		return 0;
1150 
1151 	case WM_MOUSEMOVE:
1152 		oldx = x; oldy = y;
1153 		handlemouse(x, y, 0, 0);
1154 		return 0;
1155 
1156 	/* Mouse wheel */
1157 
1158 	case WM_MOUSEWHEEL:
1159 		if ((signed short)HIWORD(wParam) <= 0)
1160 		{
1161 			handlemouse(oldx, oldy, 5, 1);
1162 			handlemouse(oldx, oldy, 5, -1);
1163 		}
1164 		else
1165 		{
1166 			handlemouse(oldx, oldy, 4, 1);
1167 			handlemouse(oldx, oldy, 4, -1);
1168 		}
1169 		return 0;
1170 
1171 	/* Timer */
1172 	case WM_TIMER:
1173 		if (wParam == OUR_TIMER_ID && timer_pending && gapp.presentation_mode)
1174 		{
1175 			timer_pending = 0;
1176 			handlekey(VK_RIGHT + 256);
1177 			handlemouse(oldx, oldy, 0, 0); /* update cursor */
1178 			return 0;
1179 		}
1180 		break;
1181 
1182 	/* Keyboard events */
1183 
1184 	case WM_KEYDOWN:
1185 		/* only handle special keys */
1186 		switch (wParam)
1187 		{
1188 		case VK_F1:
1189 			winhelp(&gapp);
1190 			return 0;
1191 		case VK_LEFT:
1192 		case VK_UP:
1193 		case VK_PRIOR:
1194 		case VK_RIGHT:
1195 		case VK_DOWN:
1196 		case VK_NEXT:
1197 		case VK_ESCAPE:
1198 			handlekey(wParam + 256);
1199 			handlemouse(oldx, oldy, 0, 0);	/* update cursor */
1200 			return 0;
1201 		}
1202 		return 1;
1203 
1204 	/* unicode encoded chars, including escape, backspace etc... */
1205 	case WM_CHAR:
1206 		if (wParam < 256)
1207 		{
1208 			handlekey(wParam);
1209 			handlemouse(oldx, oldy, 0, 0);	/* update cursor */
1210 		}
1211 		return 0;
1212 
1213 	/* We use WM_APP to trigger a reload and repaint of a page */
1214 	case WM_APP:
1215 		pdfapp_reloadpage(&gapp);
1216 		break;
1217 	}
1218 
1219 	fflush(stdout);
1220 
1221 	/* Pass on unhandled events to Windows */
1222 	return DefWindowProc(hwnd, message, wParam, lParam);
1223 }
1224 
1225 typedef BOOL (SetProcessDPIAwareFn)(void);
1226 
1227 static int
get_system_dpi(void)1228 get_system_dpi(void)
1229 {
1230 	HMODULE hUser32 = LoadLibrary(TEXT("user32.dll"));
1231 	SetProcessDPIAwareFn *ptr;
1232 	int hdpi, vdpi;
1233 	HDC desktopDC;
1234 
1235 	ptr = (SetProcessDPIAwareFn *)GetProcAddress(hUser32, "SetProcessDPIAware");
1236 	if (ptr != NULL)
1237 		ptr();
1238 	FreeLibrary(hUser32);
1239 
1240 	desktopDC = GetDC(NULL);
1241 	hdpi = GetDeviceCaps(desktopDC, LOGPIXELSX);
1242 	vdpi = GetDeviceCaps(desktopDC, LOGPIXELSY);
1243 	/* hdpi,vdpi = 100 means 96dpi. */
1244 	return ((hdpi + vdpi) * 96 + 0.5f) / 200;
1245 }
1246 
usage(const char * argv0)1247 static void usage(const char *argv0)
1248 {
1249 	const char *msg =
1250 		"usage: mupdf [options] file.pdf [page]\n"
1251 		"\t-p -\tpassword\n"
1252 		"\t-r -\tresolution\n"
1253 		"\t-A -\tset anti-aliasing quality in bits (0=off, 8=best)\n"
1254 		"\t-C -\tRRGGBB (tint color in hexadecimal syntax)\n"
1255 		"\t-W -\tpage width for EPUB layout\n"
1256 		"\t-H -\tpage height for EPUB layout\n"
1257 		"\t-I -\tinvert colors\n"
1258 		"\t-S -\tfont size for EPUB layout\n"
1259 		"\t-U -\tuser style sheet for EPUB layout\n"
1260 		"\t-X\tdisable document styles for EPUB layout\n";
1261 	MessageBoxA(NULL, msg, "MuPDF: Usage", MB_OK);
1262 	exit(1);
1263 }
1264 
1265 int WINAPI
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd)1266 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
1267 {
1268 	int argc;
1269 	LPWSTR *wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
1270 	char **argv;
1271 	char argv0[256];
1272 	MSG msg;
1273 	int code;
1274 	fz_context *ctx;
1275 	int kbps = 0;
1276 	int displayRes = get_system_dpi();
1277 	int c;
1278 
1279 	ctx = fz_new_context(NULL, NULL, FZ_STORE_DEFAULT);
1280 	if (!ctx)
1281 	{
1282 		MessageBoxA(NULL, "Cannot initialize MuPDF context.", "MuPDF: Error", MB_OK);
1283 		exit(1);
1284 	}
1285 	pdfapp_init(ctx, &gapp);
1286 
1287 	argv = fz_argv_from_wargv(argc, wargv);
1288 
1289 	while ((c = fz_getopt(argc, argv, "Ip:r:A:C:W:H:S:U:Xb:")) != -1)
1290 	{
1291 		switch (c)
1292 		{
1293 		case 'C':
1294 			c = strtol(fz_optarg, NULL, 16);
1295 			gapp.tint = 1;
1296 			gapp.tint_white = c;
1297 			break;
1298 		case 'p': password = fz_optarg; break;
1299 		case 'r': displayRes = fz_atoi(fz_optarg); break;
1300 		case 'I': gapp.invert = 1; break;
1301 		case 'A': fz_set_aa_level(ctx, fz_atoi(fz_optarg)); break;
1302 		case 'W': gapp.layout_w = fz_atoi(fz_optarg); break;
1303 		case 'H': gapp.layout_h = fz_atoi(fz_optarg); break;
1304 		case 'S': gapp.layout_em = fz_atoi(fz_optarg); break;
1305 		case 'b': kbps = fz_atoi(fz_optarg); break;
1306 		case 'U': gapp.layout_css = fz_optarg; break;
1307 		case 'X': gapp.layout_use_doc_css = 0; break;
1308 		default: usage(argv[0]);
1309 		}
1310 	}
1311 
1312 	pdfapp_setresolution(&gapp, displayRes);
1313 
1314 	GetModuleFileNameA(NULL, argv0, sizeof argv0);
1315 	install_app(argv0);
1316 
1317 	winopen();
1318 
1319 	if (fz_optind < argc)
1320 	{
1321 		strcpy(filename, argv[fz_optind++]);
1322 	}
1323 	else
1324 	{
1325 		if (!winfilename(wbuf, nelem(wbuf)))
1326 			exit(0);
1327 		code = WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, filename, sizeof filename, NULL, NULL);
1328 		if (code == 0)
1329 			pdfapp_error(&gapp, "cannot convert filename to utf-8");
1330 	}
1331 
1332 	if (fz_optind < argc)
1333 		gapp.pageno = atoi(argv[fz_optind++]);
1334 
1335 	if (kbps)
1336 		pdfapp_open_progressive(&gapp, filename, 0, kbps);
1337 	else
1338 		pdfapp_open(&gapp, filename, 0);
1339 
1340 	while (GetMessage(&msg, NULL, 0, 0))
1341 	{
1342 		TranslateMessage(&msg);
1343 		DispatchMessage(&msg);
1344 	}
1345 
1346 	fz_free_argv(argc, argv);
1347 
1348 	do_close(&gapp);
1349 
1350 	return 0;
1351 }
1352