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