1 /* Copyright (C) 2001-2019 Artifex Software, Inc.
2 All Rights Reserved.
3
4 This software is provided AS-IS with no warranty, either express or
5 implied.
6
7 This software is distributed under license and may not be copied,
8 modified or distributed except as expressly authorized under the terms
9 of the license contained in the file LICENSE in this distribution.
10
11 Refer to licensing information at http://www.artifex.com or contact
12 Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato,
13 CA 94945, U.S.A., +1(415)492-9861, for further information.
14 */
15
16 /* Ghostscript DLL loader for Windows */
17
18 #include "windows_.h"
19 #include <shellapi.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include "gscdefs.h"
24 #define GSREVISION gs_revision
25 #include "ierrors.h"
26 #include "iapi.h"
27
28 #include "dwres.h"
29 #include "dwdll.h"
30 #include "dwtext.h"
31 #include "dwimg.h"
32 #include "dwtrace.h"
33 #include "dwreg.h"
34 #include "gdevdsp.h"
35
36 /* public handles */
37 HINSTANCE ghInstance;
38
39 /* redirected stdio */
40 TW *tw;
41
42 static const LPSTR szAppName = "Ghostscript";
43
44 #ifdef _WIN64
45 const LPSTR szIniName = "gswin64.ini";
46 const char *szDllName = "gsdll64.dll";
47 #else
48 const LPSTR szIniName = "gswin32.ini";
49 const char *szDllName = "gsdll32.dll";
50 #endif
51 const LPSTR szIniSection = "Text";
52
53 GSDLL gsdll;
54 void *instance = NULL;
55 HWND hwndtext;
56
57 char start_string[] = "systemdict /start get exec\n";
58
poll(void)59 static int poll(void)
60 {
61 MSG msg;
62 while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
63 TranslateMessage(&msg);
64 DispatchMessage(&msg);
65 }
66 /* If text window closing then abort Ghostscript */
67 if (tw->quitnow)
68 return gs_error_Fatal;
69 return 0;
70 }
71
72 /*********************************************************************/
73 /* stdio functions */
74 static int GSDLLCALL
gsdll_stdin(void * instance,char * buf,int len)75 gsdll_stdin(void *instance, char *buf, int len)
76 {
77 return text_read_line(tw, buf, len);
78 }
79
80 static int GSDLLCALL
gsdll_stdout(void * instance,const char * str,int len)81 gsdll_stdout(void *instance, const char *str, int len)
82 {
83 text_write_buf(tw, str, len);
84 return len;
85 }
86
87 static int GSDLLCALL
gsdll_stderr(void * instance,const char * str,int len)88 gsdll_stderr(void *instance, const char *str, int len)
89 {
90 text_write_buf(tw, str, len);
91 return len;
92 }
93
94 /* Poll the caller for cooperative multitasking. */
95 /* If this function is NULL, polling is not needed */
gsdll_poll(void * handle)96 static int GSDLLCALL gsdll_poll(void *handle)
97 {
98 return poll();
99 }
100
101 /* make a file readable, for drag'n'drop */
dwmain_add_file_control_path(const TCHAR * pathfile)102 int dwmain_add_file_control_path(const TCHAR *pathfile)
103 {
104 LPSTR p;
105 int code, i;
106 p = malloc(wchar_to_utf8(NULL, (wchar_t *)pathfile));
107 if (p) {
108 wchar_to_utf8(p, (wchar_t *)pathfile);
109 for (i = 0; i < strlen(p); i++) {
110 if (p[i] == '\\') {
111 p[i] = '/';
112 }
113 }
114 code = gsdll.add_control_path(instance, GS_PERMIT_FILE_READING, p);
115 free(p);
116 }
117 else {
118 code = -1;
119 }
120 return code;
121 }
dwmain_remove_file_control_path(const TCHAR * pathfile)122 void dwmain_remove_file_control_path(const TCHAR *pathfile)
123 {
124 LPSTR p;
125 int i;
126 p = malloc(wchar_to_utf8(NULL, (wchar_t *)pathfile));
127 if (p) {
128 wchar_to_utf8(p, (wchar_t *)pathfile);
129 for (i = 0; i < strlen(p); i++) {
130 if (p[i] == '\\') {
131 p[i] = '/';
132 }
133 }
134 gsdll.remove_control_path(instance, GS_PERMIT_FILE_READING, p);
135 free(p);
136 }
137 }
138
139 /*********************************************************************/
140
141 /* new dll display device */
142 /*
143 #define DISPLAY_DEBUG
144 */
145
146 /* New device has been opened */
147 /* This is the first event from this device. */
display_open(void * handle,void * device)148 static int display_open(void *handle, void *device)
149 {
150 IMAGE *img;
151 #ifdef DISPLAY_DEBUG
152 char buf[256];
153 sprintf(buf, "display_open(0x%x, 0x%x)\n", handle, device);
154 text_puts(tw, buf);
155 #endif
156 img = image_new(handle, device); /* create and add to list */
157 if (img)
158 image_open(img);
159 return 0;
160 }
161
162 /* Device is about to be closed. */
163 /* Device will not be closed until this function returns. */
display_preclose(void * handle,void * device)164 static int display_preclose(void *handle, void *device)
165 {
166 #ifdef DISPLAY_DEBUG
167 char buf[256];
168 sprintf(buf, "display_preclose(0x%x, 0x$x)\n", handle, device);
169 text_puts(tw, buf);
170 #endif
171 /* do nothing - no thread synchonisation needed */
172 return 0;
173 }
174
175 /* Device has been closed. */
176 /* This is the last event from this device. */
display_close(void * handle,void * device)177 static int display_close(void *handle, void *device)
178 {
179 IMAGE *img;
180 #ifdef DISPLAY_DEBUG
181 char buf[256];
182 sprintf(buf, "display_close(0x%x, 0x$x)\n", handle, device);
183 text_puts(tw, buf);
184 #endif
185 img = image_find(handle, device);
186 if (img) {
187 image_delete(img); /* remove from list but don't free */
188 image_close(img);
189 }
190 return 0;
191 }
192
193 /* Device is about to be resized. */
194 /* Resize will only occur if this function returns 0. */
display_presize(void * handle,void * device,int width,int height,int raster,unsigned int format)195 static int display_presize(void *handle, void *device, int width, int height,
196 int raster, unsigned int format)
197 {
198 #ifdef DISPLAY_DEBUG
199 char buf[256];
200 sprintf(buf, "display_presize(0x%x, 0x%x, width=%d height=%d raster=%d\n\
201 format=%d)\n",
202 handle, device, width, height, raster, format);
203 text_puts(tw, buf);
204 #endif
205 return 0;
206 }
207
208 /* Device has been resized. */
209 /* New pointer to raster returned in pimage */
display_size(void * handle,void * device,int width,int height,int raster,unsigned int format,unsigned char * pimage)210 static int display_size(void *handle, void *device, int width, int height,
211 int raster, unsigned int format, unsigned char *pimage)
212 {
213 IMAGE *img;
214 #ifdef DISPLAY_DEBUG
215 char buf[256];
216 sprintf(buf, "display_size(0x%x, 0x%x, width=%d height=%d raster=%d\n\
217 format=%d image=0x%x)\n",
218 handle, device, width, height, raster, format, pimage);
219 text_puts(tw, buf);
220 #endif
221 img = image_find(handle, device);
222 if (img != NULL) {
223 image_size(img, width, height, raster, format, pimage);
224 image_updatesize(img);
225 }
226 return 0;
227 }
228
229 /* flushpage */
display_sync(void * handle,void * device)230 static int display_sync(void *handle, void *device)
231 {
232 IMAGE *img;
233 #ifdef DISPLAY_DEBUG
234 char buf[256];
235 sprintf(buf, "display_sync(0x%x, 0x%x)\n", handle, device);
236 text_puts(tw, buf);
237 #endif
238 img = image_find(handle, device);
239 if (img != NULL)
240 image_sync(img);
241 return 0;
242 }
243
244 /* showpage */
245 /* If you want to pause on showpage, then don't return immediately */
display_page(void * handle,void * device,int copies,int flush)246 static int display_page(void *handle, void *device, int copies, int flush)
247 {
248 IMAGE *img;
249 #ifdef DISPLAY_DEBUG
250 char buf[256];
251 sprintf(buf, "display_page(0x%x, 0x%x, copies=%d flush=%d)\n",
252 handle, device, copies, flush);
253 text_puts(tw, buf);
254 #endif
255 img = image_find(handle, device);
256 if (img != NULL)
257 image_page(img);
258 return 0;
259 }
260
261 /* Poll the caller for cooperative multitasking. */
262 /* If this function is NULL, polling is not needed */
display_update(void * handle,void * device,int x,int y,int w,int h)263 static int display_update(void *handle, void *device,
264 int x, int y, int w, int h)
265 {
266 IMAGE *img;
267 img = image_find(handle, device);
268 if (img != NULL)
269 image_poll(img); /* redraw the window periodically */
270 return poll();
271 }
272
display_separation(void * handle,void * device,int comp_num,const char * name,unsigned short c,unsigned short m,unsigned short y,unsigned short k)273 int display_separation(void *handle, void *device,
274 int comp_num, const char *name,
275 unsigned short c, unsigned short m,
276 unsigned short y, unsigned short k)
277 {
278 IMAGE *img;
279 #ifdef DISPLAY_DEBUG
280 fprintf(stdout, "display_separation(0x%x, 0x%x, %d '%s' %d,%d,%d,%d)\n",
281 handle, device, comp_num, name, (int)c, (int)m, (int)y, (int)k);
282 #endif
283 img = image_find(handle, device);
284 if (img)
285 image_separation(img, comp_num, name, c, m, y, k);
286 return 0;
287 }
288
289 display_callback display = {
290 sizeof(display_callback),
291 DISPLAY_VERSION_MAJOR,
292 DISPLAY_VERSION_MINOR,
293 display_open,
294 display_preclose,
295 display_close,
296 display_presize,
297 display_size,
298 display_sync,
299 display_page,
300 display_update,
301 NULL, /* memalloc */
302 NULL, /* memfree */
303 display_separation
304 };
305
306 /*********************************************************************/
307
308 /* program really starts at WinMain */
new_main(int argc,char * argv[])309 int new_main(int argc, char *argv[])
310 {
311 int code, code1;
312 int exit_status;
313 int exit_code;
314 int nargc;
315 char **nargv;
316 char dformat[64];
317 char ddpi[64];
318 char buf[256];
319
320 memset(buf, 0, sizeof(buf));
321 if (load_dll(&gsdll, buf, sizeof(buf))) {
322 text_puts(tw, "Can't load Ghostscript DLL\n");
323 text_puts(tw, buf);
324 text_puts(tw, "\n");
325 return 1;
326 }
327
328 if (gsdll.new_instance(&instance, NULL) < 0) {
329 text_puts(tw, "Can't create Ghostscript instance\n");
330 return 1;
331 }
332
333 gsdll.set_stdio(instance, gsdll_stdin, gsdll_stdout, gsdll_stderr);
334 gsdll.set_poll(instance, gsdll_poll);
335 gsdll.set_display_callback(instance, &display);
336
337 /* insert display device defaults as first arguments */
338 { int format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
339 DISPLAY_DEPTH_1 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
340 HDC hdc = GetDC(NULL); /* get hdc for desktop */
341 int depth = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL);
342 sprintf(ddpi, "-dDisplayResolution=%d", GetDeviceCaps(hdc, LOGPIXELSY));
343 ReleaseDC(NULL, hdc);
344 if (depth == 32)
345 format = DISPLAY_COLORS_RGB | DISPLAY_UNUSED_LAST |
346 DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
347 else if (depth == 16)
348 format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
349 DISPLAY_DEPTH_16 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST |
350 DISPLAY_NATIVE_555;
351 else if (depth > 8)
352 format = DISPLAY_COLORS_RGB | DISPLAY_ALPHA_NONE |
353 DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
354 else if (depth >= 8)
355 format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
356 DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
357 else if (depth >= 4)
358 format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
359 DISPLAY_DEPTH_4 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
360 sprintf(dformat, "-dDisplayFormat=%d", format);
361 }
362 nargc = argc + 2;
363 nargv = (char **)malloc(nargc * sizeof(char *));
364 nargv[0] = argv[0];
365 nargv[1] = dformat;
366 nargv[2] = ddpi;
367 memcpy(&nargv[3], &argv[1], (argc-1) * sizeof(char *));
368
369 #if defined(_MSC_VER) || defined(__BORLANDC__)
370 __try {
371 #endif
372 code = gsdll.set_arg_encoding(instance, GS_ARG_ENCODING_UTF8);
373 if (code == 0)
374 code = gsdll.init_with_args(instance, nargc, nargv);
375 if (code == 0)
376 code = gsdll.run_string(instance, start_string, 0, &exit_code);
377 code1 = gsdll.exit(instance);
378 if (code == 0 || (code == gs_error_Quit && code1 != 0))
379 code = code1;
380 #if defined(_MSC_VER) || defined(__BORLANDC__)
381 } __except(exception_code() == EXCEPTION_STACK_OVERFLOW) {
382 code = gs_error_Fatal;
383 text_puts(tw, "*** C stack overflow. Quiting...\n");
384 }
385 #endif
386 text_clear_drag_and_drop_list(tw, 1);
387 gsdll.delete_instance(instance);
388
389 unload_dll(&gsdll);
390
391 free(nargv);
392
393 exit_status = 0;
394 switch (code) {
395 case 0:
396 case gs_error_Quit:
397 break;
398 case gs_error_Fatal:
399 exit_status = 1;
400 break;
401 case gs_error_Info:
402 default:
403 exit_status = 255;
404 }
405
406 return exit_status;
407 }
408
409 void
set_font(void)410 set_font(void)
411 {
412 int fontsize;
413 char fontname[256];
414 char buf[32];
415
416 /* read ini file */
417 GetPrivateProfileString(szIniSection, "FontName", "Courier New", fontname, sizeof(fontname), szIniName);
418 fontsize = GetPrivateProfileInt(szIniSection, "FontSize", 10, szIniName);
419
420 /* set font */
421 text_font(tw, fontname, fontsize);
422
423 /* write ini file */
424 WritePrivateProfileString(szIniSection, "FontName", fontname, szIniName);
425 sprintf(buf, "%d", fontsize);
426 WritePrivateProfileString(szIniSection, "FontSize", buf, szIniName);
427 }
428
429 typedef BOOL (SetProcessDPIAwareFn)(void);
430
431 static void
avoid_windows_scale(void)432 avoid_windows_scale(void)
433 {
434 /* Fetch the function address and only call it if it is there; this keeps
435 * compatability with Windows < 8.1 */
436 HMODULE hUser32 = LoadLibrary(TEXT("user32.dll"));
437 SetProcessDPIAwareFn *ptr;
438
439 ptr = (SetProcessDPIAwareFn *)GetProcAddress(hUser32, "SetProcessDPIAware");
440 if (ptr != NULL)
441 ptr();
442 FreeLibrary(hUser32);
443 }
444
445 int PASCAL
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpszCmdLine,int cmdShow)446 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int cmdShow)
447 {
448 int dll_exit_status;
449 #define MAXCMDTOKENS 128
450 /* BC++ 4.5 will give us _argc and _argv[], but they will be */
451 /* incorrect if there is a space in the program name. */
452 /* Provide our own parsing code to create argc and argv[]. */
453 int argc;
454 LPSTR argv[MAXCMDTOKENS];
455 LPSTR p;
456 LPSTR pstart;
457 char command[256];
458 char *args;
459 char *d, *e;
460 char winposbuf[256];
461 int len = sizeof(winposbuf);
462 int x, y, cx, cy;
463
464 /* Mark us as being 'system dpi aware' to avoid horrid scaling */
465 avoid_windows_scale();
466
467 /* copy the hInstance into a variable so it can be used */
468 ghInstance = hInstance;
469
470 if (hPrevInstance) {
471 MessageBox((HWND)NULL,"Can't run twice", szAppName,
472 MB_ICONHAND | MB_OK);
473 return FALSE;
474 }
475
476 /* If called with "gswin32c.exe arg1 arg2"
477 * lpszCmdLine returns:
478 * "arg1 arg2" if called with CreateProcess(NULL, command, ...)
479 * "arg2" if called with CreateProcess(command, args, ...)
480 * GetCommandLine() returns
481 * ""gswin32c.exe" arg1 arg2"
482 * if called with CreateProcess(NULL, command, ...)
483 * " arg1 arg2"
484 * if called with CreateProcess(command, args, ...)
485 * Consequently we must use GetCommandLine()
486 */
487 {
488 wchar_t *uni = GetCommandLineW();
489 pstart = p = malloc(wchar_to_utf8(NULL, uni));
490 if (p != NULL)
491 wchar_to_utf8(p, uni);
492 }
493
494 argc = 0;
495 args = (char *)malloc(lstrlen(p)+1);
496 if (args == (char *)NULL) {
497 fprintf(stdout, "Insufficient memory in WinMain()\n");
498 return 1;
499 }
500
501 /* Parse command line handling quotes. */
502 d = args;
503 while (*p) {
504 /* for each argument */
505
506 if (argc >= MAXCMDTOKENS - 1)
507 break;
508
509 e = d;
510 while ((*p) && (*p != ' ')) {
511 if (*p == '\042') {
512 /* Remove quotes, skipping over embedded spaces. */
513 /* Doesn't handle embedded quotes. */
514 p++;
515 while ((*p) && (*p != '\042'))
516 *d++ =*p++;
517 }
518 else
519 *d++ = *p;
520 if (*p)
521 p++;
522 }
523 *d++ = '\0';
524 argv[argc++] = e;
525
526 while ((*p) && (*p == ' '))
527 p++; /* Skip over trailing spaces */
528 }
529 argv[argc] = NULL;
530
531 free(pstart);
532
533 if (strlen(argv[0]) == 0) {
534 GetModuleFileName(hInstance, command, sizeof(command)-1);
535 argv[0] = command;
536 }
537
538 tw = text_new();
539 if (tw == NULL) {
540 MessageBox((HWND)NULL, "Can't create text window",
541 szAppName, MB_OK | MB_ICONSTOP);
542 return 1;
543 }
544
545 /* start up the text window */
546 if (!hPrevInstance) {
547 HICON hicon = LoadIcon(hInstance, (LPSTR)MAKEINTRESOURCE(GSTEXT_ICON));
548 text_register_class(tw, hicon);
549 }
550 set_font();
551 text_size(tw, 80, 80);
552 text_drag(tw, "(", ") run\r");
553 if (win_get_reg_value("Text", winposbuf, &len) == 0) {
554 if (sscanf(winposbuf, "%d %d %d %d", &x, &y, &cx, &cy) == 4)
555 text_setpos(tw, x, y, cx, cy);
556 }
557
558 /* create the text window */
559 if (text_create(tw, szAppName, cmdShow))
560 exit(1);
561
562 hwndtext = text_get_handle(tw);
563 image_textwindow(hwndtext);
564
565 dll_exit_status = new_main(argc, argv);
566
567 if (dll_exit_status && !tw->quitnow) {
568 /* display error message in text window */
569 MSG msg;
570 text_puts(tw, "\nClose this window with the close button on the title bar or the system menu.\n");
571 if (IsIconic(text_get_handle(tw)))
572 ShowWindow(text_get_handle(tw), SW_SHOWNORMAL);
573 BringWindowToTop(text_get_handle(tw)); /* make text window visible */
574 FlashWindow(text_get_handle(tw), TRUE);
575 /* Wait until error message is read */
576 while (!tw->quitnow && GetMessage(&msg, (HWND)NULL, 0, 0)) {
577 TranslateMessage(&msg);
578 DispatchMessage(&msg);
579 }
580 }
581
582 /* Save the text window size */
583 if (text_getpos(tw, &x, &y, &cx, &cy) == 0) {
584 sprintf(winposbuf, "%d %d %d %d", x, y, cx, cy);
585 win_set_reg_value("Text", winposbuf);
586 }
587
588 text_destroy(tw);
589 tw = NULL;
590
591 return dll_exit_status;
592 }
593