1 /*
2  * Copyright (c) 1994-2019 Paul Mattes.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the names of Paul Mattes nor the names of his contributors
13  *       may be used to endorse or promote products derived from this software
14  *       without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY PAUL MATTES "AS IS" AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL PAUL MATTES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 /*
29  *	gdi_print.c
30  *		GDI screen printing functions.
31  */
32 
33 #include "globals.h"
34 
35 #include <windows.h>
36 #include <commdlg.h>
37 #include <winspool.h>
38 #include <assert.h>
39 
40 #include "appres.h"
41 #include "3270ds.h"
42 #include "ctlr.h"
43 
44 #include "ctlrc.h"
45 
46 #include "resources.h"
47 
48 #include "fprint_screen.h"
49 #include "gdi_print.h"
50 #include "nvt.h"
51 #include "popups.h"
52 #include "task.h"
53 #include "trace.h"
54 #include "unicodec.h"
55 #include "utils.h"
56 #include "w3misc.h"
57 
58 /* Defines */
59 #define PPI			72	/* points per inch */
60 
61 /* Typedefs */
62 
63 /* Globals */
64 
65 /* Statics */
66 typedef struct {		/* user parameters: */
67     int orientation;		/*  orientation */
68     double hmargin;		/*  horizontal margin in inches */
69     double vmargin;		/*  vertical margin in inches */
70     const char *font_name;	/*  font name */
71     int font_size;		/*  font size in points */
72     int spp;			/*  screens per page */
73     bool done;		/* done fetching values */
74 } uparm_t;
75 static uparm_t uparm;
76 static struct {			/* printer characteristics: */
77     int ppiX, ppiY;		/*  points per inch */
78     int poffX, poffY;		/*  left, top physical offsets */
79     int horzres, vertres;	/*  resolution (usable area) */
80     int pwidth, pheight;	/*  physical width, height */
81 } pchar;
82 static struct {			/* printer state */
83     bool active;		/*  is GDI printing active? */
84     char *caption;		/*  caption */
85     int out_row;		/*  next row to print to */
86     int screens;		/*  number of screens on current page */
87     PRINTDLG dlg;		/*  Windows print dialog */
88     FLOAT xptscale, yptscale;	/*  x, y point-to-LU scaling factors */
89     int hmargin_pixels, vmargin_pixels; /*  margins, in pixels */
90     int usable_xpixels, usable_ypixels;/*  usable area (pixels) */
91     int usable_cols, usable_rows;/*  usable area (chars) */
92     HFONT font, bold_font, underscore_font, bold_underscore_font;
93     HFONT caption_font;
94 			        /*  fonts */
95     SIZE space_size;		/*  size of a space character */
96     INT *dx;			/*  spacing array */
97 
98     HANDLE thread;		/* thread to run the print dialog */
99     HANDLE done_event;		/* event to signal dialog is done */
100     bool cancel;		/* true if dialog canceled */
101     void *wait_context;		/* task wait context */
102 } pstate;
103 static bool pstate_initted = false;
104 
105 /* Forward declarations. */
106 static void gdi_get_params(uparm_t *up);
107 static gdi_status_t gdi_init(const char *printer_name, unsigned opts,
108 	const char **fail, void *wait_context);
109 static int gdi_screenful(struct ea *ea, unsigned short rows,
110 	unsigned short cols, const char **fail);
111 static int gdi_done(const char **fail);
112 static void gdi_abort(void);
113 static BOOL get_printer_device(const char *printer_name, HGLOBAL *pdevnames,
114 	HGLOBAL *pdevmode);
115 
116 /*
117  * Initialize printing to a GDI printer.
118  */
119 gdi_status_t
gdi_print_start(const char * printer_name,unsigned opts,void * wait_context)120 gdi_print_start(const char *printer_name, unsigned opts, void *wait_context)
121 {
122     const char *fail = "";
123 
124     if (!uparm.done) {
125 	/* Set the defaults. */
126 	uparm.orientation = 0;
127 	uparm.hmargin = 0.5;
128 	uparm.vmargin = 0.5;
129 	uparm.font_name = NULL;
130 	uparm.font_size = 0; /* auto */
131 	uparm.spp = 1;
132 
133 	/* Gather up the parameters. */
134 	gdi_get_params(&uparm);
135 
136 	/* Don't do this again. */
137 	uparm.done = true;
138     }
139 
140     /* Initialize the printer and pop up the dialog. */
141     switch (gdi_init(printer_name, opts, &fail, wait_context)) {
142     case GDI_STATUS_SUCCESS:
143 	vtrace("[gdi] initialized\n");
144 	break;
145     case GDI_STATUS_ERROR:
146 	popup_an_error("Printer initialization error: %s", fail);
147 	return GDI_STATUS_ERROR;
148     case GDI_STATUS_CANCEL:
149 	vtrace("[gdi] canceled\n");
150 	return GDI_STATUS_CANCEL;
151     case GDI_STATUS_WAIT:
152 	vtrace("[gdi] waiting\n");
153 	return GDI_STATUS_WAIT;
154     }
155 
156     return GDI_STATUS_SUCCESS;
157 }
158 
159 /* Finish printing to a GDI printer. */
160 gdi_status_t
gdi_print_finish(FILE * f,const char * caption)161 gdi_print_finish(FILE *f, const char *caption)
162 {
163     size_t nr;
164     struct ea *ea_tmp;
165     gdi_header_t h;
166     const char *fail = "";
167 
168     /* Save the caption. */
169     if (caption != NULL) {
170 	Replace(pstate.caption, NewString(caption));
171     } else {
172 	Replace(pstate.caption, NULL);
173     }
174 
175     /* Allocate the buffer. */
176     ea_tmp = Malloc((((maxROWS * maxCOLS) + 1) * sizeof(struct ea)));
177 
178     /* Set up the fake fa in location -1. */
179     memset(&ea_tmp[0], '\0', sizeof(struct ea));
180     ea_tmp[0].fa = FA_PRINTABLE | FA_MODIFY;
181 
182     /* Rewind the file. */
183     fflush(f);
184     rewind(f);
185 
186     /* Read it back. */
187     while ((nr = fread(&h, sizeof(gdi_header_t), 1, f)) == 1) {
188 	/* Check the signature. */
189 	if (h.signature != GDI_SIGNATURE) {
190 	    popup_an_error("Corrupt temporary file (signature)");
191 	    goto abort;
192 	}
193 
194 	/* Check the screen dimensions. */
195 	if (h.rows > maxROWS || h.cols > maxCOLS) {
196 	    popup_an_error("Corrupt temporary file (screen size)");
197 	    goto abort;
198 	}
199 
200 	/* Read the screen image in. */
201 	if (fread(ea_tmp + 1, sizeof(struct ea), h.rows * h.cols, f) !=
202 		h.rows * h.cols) {
203 	    popup_an_error("Truncated temporary file");
204 	    goto abort;
205 	}
206 
207 	/* Process it. */
208 	if (gdi_screenful(ea_tmp + 1, h.rows, h.cols, &fail) < 0) {
209 		popup_an_error("Printing error: %s", fail);
210 		goto abort;
211 	}
212     }
213     if (gdi_done(&fail) < 0) {
214 	popup_an_error("Final printing error: %s", fail);
215 	goto abort;
216     }
217     Free(ea_tmp);
218 
219     pstate.active = false;
220     return GDI_STATUS_SUCCESS;
221 
222 abort:
223     Free(ea_tmp);
224     gdi_abort();
225     return GDI_STATUS_ERROR;
226 }
227 
228 /*
229  * Validate and scale a margin value.
230  */
231 static double
parse_margin(char * s,const char * what)232 parse_margin(char *s, const char *what)
233 {
234     double d;
235     char *nextp;
236 
237     d = strtod(s, &nextp);
238     if (d > 0.0) {
239 	while (*nextp == ' ') {
240 	    nextp++;
241 	}
242 	if (*nextp == '\0' || *nextp == '"' ||
243 		!strcasecmp(nextp, "in") ||
244 		!strcasecmp(nextp, "inch") ||
245 		!strcasecmp(nextp, "inches")) {
246 	    /* Do nothing. */
247 	} else if (!strcasecmp(nextp, "mm")) {
248 	    d /= 25.4;
249 	} else if (!strcasecmp(nextp, "cm")) {
250 	    d /= 2.54;
251 	} else {
252 	    vtrace("gdi: unknown %s unit '%s'\n",
253 		    what, nextp);
254 	}
255     } else {
256 	vtrace("gdi: invalid %s '%s'\n", what, s);
257 	return 0;
258     }
259     return d;
260 }
261 
262 /*
263  * Gather the user parameters from resources.
264  */
265 static void
gdi_get_params(uparm_t * up)266 gdi_get_params(uparm_t *up)
267 {
268     char *s;
269     double d;
270     unsigned long l;
271     char *nextp;
272 
273     /* Orientation. */
274     if ((s = get_resource(ResPrintTextOrientation)) != NULL) {
275 	if (!strcasecmp(s, "portrait")) {
276 	    up->orientation = DMORIENT_PORTRAIT;
277 	} else if (!strcasecmp(s, "landscape")) {
278 	    up->orientation = DMORIENT_LANDSCAPE;
279 	} else {
280 	    vtrace("gdi: unknown orientation '%s'\n", s);
281 	}
282     }
283 
284     /* Horizontal margin. */
285     if ((s = get_resource(ResPrintTextHorizontalMargin)) != NULL) {
286 	d = parse_margin(s, ResPrintTextHorizontalMargin);
287 	if (d > 0) {
288 	    up->hmargin = d;
289 	}
290     }
291 
292     /* Vertical margin. */
293     if ((s = get_resource(ResPrintTextVerticalMargin)) != NULL) {
294 	d = parse_margin(s, ResPrintTextVerticalMargin);
295 	if (d > 0) {
296 	    up->vmargin = d;
297 	}
298     }
299 
300     /* Font name. */
301     if ((s = get_resource(ResPrintTextFont)) != NULL) {
302 	up->font_name = s;
303     }
304 
305     /* Font size. */
306     if ((s = get_resource(ResPrintTextSize)) != NULL) {
307 	if (strcasecmp(s, "auto")) {
308 	    l = strtoul(s, &nextp, 0);
309 	    if (l > 0) {
310 		up->font_size = (int)l;
311 	    } else {
312 		vtrace("gdi: invalid %s '%s'\n", ResPrintTextSize, s);
313 	    }
314 	}
315     }
316 
317     /* Screens per page. */
318     if ((s = get_resource(ResPrintTextScreensPerPage)) != NULL) {
319 	l = strtoul(s, &nextp, 0);
320 	if (l > 0) {
321 	    up->spp = (int)l;
322 	} else {
323 	    vtrace("gdi: invalid %s '%s'\n", ResPrintTextScreensPerPage, s);
324 	}
325     }
326 }
327 
328 /*
329  * Clean up fonts.
330  */
331 static void
cleanup_fonts(void)332 cleanup_fonts(void)
333 {
334     if (pstate.font) {
335 	DeleteObject(pstate.font);
336 	pstate.font = NULL;
337     }
338     if (pstate.bold_font) {
339 	DeleteObject(pstate.bold_font);
340 	pstate.bold_font = NULL;
341     }
342     if (pstate.underscore_font) {
343 	DeleteObject(pstate.underscore_font);
344 	pstate.underscore_font = NULL;
345     }
346     if (pstate.caption_font) {
347 	DeleteObject(pstate.caption_font);
348 	pstate.caption_font = NULL;
349     }
350 
351     pstate.active = false;
352 }
353 
354 /*
355  * Create a Roman font.
356  * Returns 0 for success, -1 for failure.
357  */
358 static int
create_roman_font(HDC dc,int fheight,int fwidth,const char ** fail)359 create_roman_font(HDC dc, int fheight, int fwidth, const char **fail)
360 {
361     char *w, *h;
362 
363     w = fwidth? xs_buffer("%d", fwidth): NewString("(auto)");
364     h = fheight? xs_buffer("%d", fheight): NewString("(auto)");
365     vtrace("[gdi] requesting a font %sx%s logical units\n", w, h);
366     Free(w);
367     Free(h);
368 
369     pstate.font = CreateFont(
370 	    fheight,		/* height */
371 	    fwidth,		/* width */
372 	    0,			/* escapement */
373 	    0,			/* orientation */
374 	    FW_NORMAL,		/* weight */
375 	    FALSE,		/* italic */
376 	    FALSE,		/* underline */
377 	    FALSE,		/* strikeout */
378 	    DEFAULT_CHARSET,	/* character set */
379 	    OUT_OUTLINE_PRECIS,	/* output precision */
380 	    CLIP_DEFAULT_PRECIS,/* clip precision */
381 	    DEFAULT_QUALITY,	/* quality */
382 	    FIXED_PITCH|FF_DONTCARE,/* pitch and family */
383 	    uparm.font_name);	/* face */
384     if (pstate.font == NULL) {
385 	*fail = "CreateFont failed";
386 	return -1;
387     }
388 
389     /* Measure a space to find out the size we got. */
390     SelectObject(dc, pstate.font);
391     if (!GetTextExtentPoint32(dc, " ", 1, &pstate.space_size)) {
392 	*fail = "GetTextExtentPoint32 failed";
393 	return -1;
394     }
395     vtrace("[gdi] space character is %dx%d logical units\n",
396 	    (int)pstate.space_size.cx, (int)pstate.space_size.cy);
397     pstate.usable_cols = pstate.usable_xpixels / pstate.space_size.cx;
398     pstate.usable_rows = pstate.usable_ypixels / pstate.space_size.cy;
399     vtrace("[gdi] usable area is %dx%d characters\n",
400 	    pstate.usable_cols, pstate.usable_rows);
401     return 0;
402 }
403 
404 /*
405  * Return the default printer name.
406  */
407 static char *
get_default_printer_name(char * errbuf,size_t errbuf_size)408 get_default_printer_name(char *errbuf, size_t errbuf_size)
409 {
410     DWORD size;
411     char *buf;
412 
413     /* Figure out how much memory to allocate. */
414     size = 0;
415     GetDefaultPrinter(NULL, &size);
416     buf = Malloc(size);
417     if (GetDefaultPrinter(buf, &size) == 0) {
418 	snprintf(errbuf, errbuf_size, "Cannot determine default printer");
419 	return NULL;
420     }
421     return buf;
422 }
423 
424 /* Thread to post the print dialog. */
425 static DWORD WINAPI
post_print_dialog(LPVOID lpParameter _is_unused)426 post_print_dialog(LPVOID lpParameter _is_unused)
427 {
428     if (!PrintDlg(&pstate.dlg)) {
429 	pstate.cancel = true;
430     }
431     SetEvent(pstate.done_event);
432     return 0;
433 }
434 
435 /* The print dialog is complete. */
436 static void
print_dialog_complete(iosrc_t fd _is_unused,ioid_t id _is_unused)437 print_dialog_complete(iosrc_t fd _is_unused, ioid_t id _is_unused)
438 {
439     vtrace("Printer dialog complete (%s)\n",
440 	    pstate.cancel? "cancel": "continue");
441     pstate.thread = INVALID_HANDLE_VALUE;
442     task_resume_xwait(pstate.wait_context, pstate.cancel,
443 	    "print dialog complete");
444 }
445 
446 /*
447  * Initalize the named GDI printer. If the name is NULL, use the default
448  * printer.
449  */
450 static gdi_status_t
gdi_init(const char * printer_name,unsigned opts,const char ** fail,void * wait_context)451 gdi_init(const char *printer_name, unsigned opts, const char **fail,
452 	void *wait_context)
453 {
454     char *default_printer_name = NULL;
455     LPDEVMODE devmode;
456     HDC dc;
457     DOCINFO docinfo;
458     DEVNAMES *devnames;
459     int rmargin, bmargin; /* right margin, bottom margin */
460     int maxphmargin, maxpvmargin;
461     int i;
462     static char get_fail[1024];
463     int fheight, fwidth;
464 
465     if (!pstate_initted) {
466 	pstate.thread = INVALID_HANDLE_VALUE;
467 	pstate.done_event = INVALID_HANDLE_VALUE;
468 	pstate_initted = true;
469     }
470 
471     if (pstate.active) {
472 	*fail = "Only one GDI document at a time";
473 	goto failed;
474     }
475 
476     if (pstate.thread != INVALID_HANDLE_VALUE) {
477 	*fail = "Print dialog already pending";
478 	goto failed;
479     }
480 
481     if (!(opts & FPS_DIALOG_COMPLETE)) {
482 	memset(&pstate.dlg, '\0', sizeof(pstate.dlg));
483 	pstate.dlg.lStructSize = sizeof(pstate.dlg);
484 	pstate.dlg.Flags = PD_RETURNDC | PD_NOPAGENUMS | PD_HIDEPRINTTOFILE |
485 	    PD_NOSELECTION;
486     }
487 
488     if (printer_name == NULL || !*printer_name) {
489 	default_printer_name = get_default_printer_name(get_fail,
490 		sizeof(get_fail));
491 	if (default_printer_name == NULL) {
492 	    *fail = get_fail;
493 	    goto failed;
494 	}
495 	printer_name = default_printer_name;
496     }
497     if (!get_printer_device(printer_name, &pstate.dlg.hDevNames,
498 		&pstate.dlg.hDevMode)) {
499 	snprintf(get_fail, sizeof(get_fail),
500 		"GetPrinter(%s) failed: %s",
501 		printer_name, win32_strerror(GetLastError()));
502 	*fail = get_fail;
503 	goto failed;
504     }
505     if (uparm.orientation) {
506 	devmode = (LPDEVMODE)GlobalLock(pstate.dlg.hDevMode);
507 	devmode->dmFields |= DM_ORIENTATION;
508 	devmode->dmOrientation = uparm.orientation;
509 	GlobalUnlock(devmode);
510     }
511 
512     if (opts & FPS_NO_DIALOG) {
513 	/* They don't want the print dialog. Allocate a DC for it. */
514 	devmode = (LPDEVMODE)GlobalLock(pstate.dlg.hDevMode);
515 	pstate.dlg.hDC = CreateDC("WINSPOOL", printer_name, NULL, devmode);
516 	GlobalUnlock(devmode);
517 	if (pstate.dlg.hDC == NULL) {
518 	    snprintf(get_fail, sizeof(get_fail), "Cannot create DC for "
519 		    "printer '%s'", printer_name);
520 	    *fail = get_fail;
521 	    goto failed;
522 	}
523     } else if (!(opts & FPS_DIALOG_COMPLETE)) {
524 	if (default_printer_name != NULL) {
525 	    Free(default_printer_name);
526 	    default_printer_name = NULL;
527 	}
528 
529 	/* Pop up the dialog to get the printer characteristics. */
530 	pstate.cancel = false;
531 	pstate.wait_context = wait_context;
532 	if (pstate.done_event == INVALID_HANDLE_VALUE) {
533 	    pstate.done_event = CreateEvent(NULL, FALSE, FALSE, NULL);
534 	    AddInput(pstate.done_event, print_dialog_complete);
535 	} else {
536 	    ResetEvent(pstate.done_event); /* just in case */
537 	}
538 	pstate.cancel = false;
539 	pstate.thread = CreateThread(NULL, 0, post_print_dialog, NULL, 0, NULL);
540 	return GDI_STATUS_WAIT;
541     }
542     dc = pstate.dlg.hDC;
543 
544     if (default_printer_name != NULL) {
545 	Free(default_printer_name);
546 	default_printer_name = NULL;
547     }
548 
549     /* Find out the printer characteristics. */
550 
551     /* LOGPIXELSX and LOGPIXELSY are the pixels-per-inch for the printer. */
552     pchar.ppiX = GetDeviceCaps(dc, LOGPIXELSX);
553     if (pchar.ppiX <= 0) {
554 	*fail = "Can't get LOGPIXELSX";
555 	goto failed;
556     }
557     pchar.ppiY = GetDeviceCaps(dc, LOGPIXELSY);
558     if (pchar.ppiY <= 0) {
559 	*fail = "Can't get LOGPIXELSY";
560 	goto failed;
561     }
562 
563     /*
564      * PHYSICALOFFSETX and PHYSICALOFFSETY are the fixed top and left-hand
565      * margins, in pixels. Whatever you print is offset by these amounts, so
566      * you have to subtract them from your coordinates. You cannot print in
567      * these areas.
568      */
569     pchar.poffX = GetDeviceCaps(dc, PHYSICALOFFSETX);
570     if (pchar.poffX < 0) {
571 	*fail = "Can't get PHYSICALOFFSETX";
572 	goto failed;
573     }
574     pchar.poffY = GetDeviceCaps(dc, PHYSICALOFFSETY);
575     if (pchar.poffY < 0) {
576 	*fail = "Can't get PHYSICALOFFSETY";
577 	goto failed;
578     }
579 
580     /*
581      * HORZRES and VERTRES are the size of the usable area of the page, in
582      * pixels. They implicitly give you the size of the right-hand and
583      * bottom physical offsets.
584      */
585     pchar.horzres = GetDeviceCaps(dc, HORZRES);
586     if (pchar.horzres <= 0) {
587 	*fail = "Can't get HORZRES";
588 	goto failed;
589     }
590     pchar.vertres = GetDeviceCaps(dc, VERTRES);
591     if (pchar.vertres <= 0) {
592 	*fail = "Can't get VERTRES";
593 	goto failed;
594     }
595 
596     /*
597      * PHYSICALWIDTH and PHYSICALHEIGHT are the size of the entire area of
598      * the page, in pixels.
599      */
600     pchar.pwidth = GetDeviceCaps(dc, PHYSICALWIDTH);
601     if (pchar.pwidth <= 0) {
602 	*fail = "Can't get PHYSICALWIDTH";
603 	goto failed;
604     }
605     pchar.pheight = GetDeviceCaps(dc, PHYSICALHEIGHT);
606     if (pchar.pheight <= 0) {
607 	*fail = "Can't get PHYSICALHEIGHT";
608 	goto failed;
609     }
610 
611     /* Trace the device characteristics. */
612     devnames = (DEVNAMES *)GlobalLock(pstate.dlg.hDevNames);
613     vtrace("[gdi] Printer '%s' capabilities:\n",
614 	    (char *)devnames + devnames->wDeviceOffset);
615     GlobalUnlock(devnames);
616     vtrace("[gdi]  LOGPIXELSX %d LOGPIXELSY %d\n",
617 	    pchar.ppiX, pchar.ppiY);
618     vtrace("[gdi]  PHYSICALOFFSETX %d PHYSICALOFFSETY %d\n",
619 	    pchar.poffX, pchar.poffY);
620     vtrace("[gdi]  HORZRES %d VERTRES %d\n",
621 	    pchar.horzres, pchar.vertres);
622     vtrace("[gdi]  PHYSICALWIDTH %d PHYSICALHEIGHT %d\n",
623 	    pchar.pwidth, pchar.pheight);
624 
625     /* Compute the scale factors (points to pixels). */
626     pstate.xptscale = (FLOAT)pchar.ppiX / (FLOAT)PPI;
627     pstate.yptscale = (FLOAT)pchar.ppiY / (FLOAT)PPI;
628 
629     /* Compute the implied right and bottom margins. */
630     rmargin = pchar.pwidth - pchar.horzres - pchar.poffX;
631     bmargin = pchar.pheight - pchar.vertres - pchar.poffY;
632     if (rmargin > pchar.poffX) {
633 	maxphmargin = rmargin;
634     } else {
635 	maxphmargin = pchar.poffX;
636     }
637     if (bmargin > pchar.poffY) {
638 	maxpvmargin = bmargin;
639     } else {
640 	maxpvmargin = pchar.poffY;
641     }
642     vtrace("[gdi] maxphmargin is %d, maxpvmargin is %d pixels\n",
643 	    maxphmargin, maxpvmargin);
644 
645     /* Compute the margins in pixels. */
646     pstate.hmargin_pixels = (int)(uparm.hmargin * pchar.ppiX);
647     pstate.vmargin_pixels = (int)(uparm.vmargin * pchar.ppiY);
648 
649     /* See if the margins are too small. */
650     if (pstate.hmargin_pixels < maxphmargin) {
651 	pstate.hmargin_pixels = maxphmargin;
652 	vtrace("[gdi] hmargin is too small, setting to %g\"\n",
653 		(float)pstate.hmargin_pixels / pchar.ppiX);
654     }
655     if (pstate.vmargin_pixels < maxpvmargin) {
656 	pstate.vmargin_pixels = maxpvmargin;
657 	vtrace("[gdi] vmargin is too small, setting to %g\"\n",
658 		(float)pstate.vmargin_pixels / pchar.ppiX);
659     }
660 
661     /* See if the margins are too big. */
662     if (pstate.hmargin_pixels * 2 >= pchar.horzres) {
663 	pstate.hmargin_pixels = pchar.ppiX;
664 	vtrace("[gdi] hmargin is too big, setting to 1\"\n");
665     }
666     if (pstate.vmargin_pixels * 2 >= pchar.vertres) {
667 	pstate.vmargin_pixels = pchar.ppiY;
668 	vtrace("[gdi] vmargin is too big, setting to 1\"\n");
669     }
670 
671     /*
672      * Compute the usable area in pixels. That's the physical page size
673      * less the margins, now that we know that the margins are reasonable.
674      */
675     pstate.usable_xpixels = pchar.pwidth - (2 * pstate.hmargin_pixels);
676     pstate.usable_ypixels = pchar.pheight - (2 * pstate.vmargin_pixels);
677     vtrace("[gdi] usable area is %dx%d pixels\n",
678 	    pstate.usable_xpixels, pstate.usable_ypixels);
679 
680     /*
681      * Create the Roman font.
682      *
683      * If they specified a particular font size, use that as the height,
684      * and let the system pick the width.
685      *
686      * If they did not specify a font size, or chose "auto", then let the
687      * "screens per page" drive what to do. If "screens per page" is set,
688      * then divide the page Y pixels by the screens-per-page times the
689      * display height to get the font height, and let the system pick the
690      * width.
691      *
692      * Otherwise, divide the page X pixels by COLS to get the font width,
693      * and let the system pick the height.
694      */
695     if (uparm.font_size) {
696 	/* User-specified fixed font size. */
697 	fheight = (int)(uparm.font_size * pstate.yptscale);
698 	fwidth = 0;
699     } else {
700 	if (uparm.spp > 1) {
701 	    /*
702 	     * Scale the height so the specified number of screens will
703 	     * fit.
704 	     */
705 	    fheight = pstate.usable_ypixels /
706 		(uparm.spp * maxROWS /* spp screens */
707 		 + (uparm.spp - 1) /* spaces between screens */
708 		 + 2 /* space and caption*/ );
709 	    fwidth = 0;
710 	} else {
711 	    /*
712 	     * Scale the width so a screen will fit the page horizonally.
713 	     */
714 	    fheight = 0;
715 	    fwidth = pstate.usable_xpixels / maxCOLS;
716 	}
717     }
718     if (create_roman_font(dc, fheight, fwidth, fail) < 0) {
719 	goto failed;
720     }
721 
722     /*
723      * If we computed the font size, see if the other dimension is too
724      * big. If it is, scale using the other dimension, which is guaranteed to
725      * make the original computed dimension no bigger.
726      *
727      * XXX: This needs more testing.
728      */
729     if (!uparm.font_size) {
730 	if (fwidth == 0) {
731 	    /*
732 	     * We computed the height because spp > 1. See if the width
733 	     * overflows.
734 	     */
735 	    if (pstate.space_size.cx * maxCOLS > pstate.usable_xpixels) {
736 		vtrace("[gdi] font too wide, retrying\n");
737 		DeleteObject(pstate.font);
738 		pstate.font = NULL;
739 
740 		fheight = 0;
741 		fwidth = pstate.usable_xpixels / maxCOLS;
742 		if (create_roman_font(dc, fheight, fwidth, fail) < 0) {
743 		    goto failed;
744 		}
745 	    }
746 	} else if (fheight == 0) {
747 	    /*
748 	     * We computed the width (spp <= 1). See if the height
749 	     * overflows.
750 	     */
751 	    if (pstate.space_size.cy * (maxROWS + 2) >
752 		    pstate.usable_xpixels) {
753 		vtrace("[gdi] font too high, retrying\n");
754 		DeleteObject(pstate.font);
755 		pstate.font = NULL;
756 
757 		fheight = pstate.usable_xpixels / (maxROWS + 2);
758 		fwidth = 0;
759 		if (create_roman_font(dc, fheight, fwidth, fail) < 0) {
760 		    goto failed;
761 		}
762 	    }
763 	}
764     }
765 
766     /* Create a bold font that is the same size, if possible. */
767     pstate.bold_font = CreateFont(
768 	    pstate.space_size.cy,	/* height */
769 	    pstate.space_size.cx,	/* width */
770 	    0,			/* escapement */
771 	    0,			/* orientation */
772 	    FW_BOLD,		/* weight */
773 	    FALSE,			/* italic */
774 	    FALSE,			/* underline */
775 	    FALSE,			/* strikeout */
776 	    ANSI_CHARSET,		/* character set */
777 	    OUT_OUTLINE_PRECIS,	/* output precision */
778 	    CLIP_DEFAULT_PRECIS,	/* clip precision */
779 	    DEFAULT_QUALITY,	/* quality */
780 	    FIXED_PITCH|FF_DONTCARE,/* pitch and family */
781 	    uparm.font_name);	/* face */
782     if (pstate.bold_font == NULL) {
783 	*fail = "CreateFont (bold) failed";
784 	goto failed;
785     }
786 
787     /* Create an underscore font that is the same size, if possible. */
788     pstate.underscore_font = CreateFont(
789 	    pstate.space_size.cy,	/* height */
790 	    pstate.space_size.cx,	/* width */
791 	    0,			/* escapement */
792 	    0,			/* orientation */
793 	    FW_NORMAL,		/* weight */
794 	    FALSE,			/* italic */
795 	    TRUE,			/* underline */
796 	    FALSE,			/* strikeout */
797 	    ANSI_CHARSET,		/* character set */
798 	    OUT_OUTLINE_PRECIS,	/* output precision */
799 	    CLIP_DEFAULT_PRECIS,	/* clip precision */
800 	    DEFAULT_QUALITY,	/* quality */
801 	    FIXED_PITCH|FF_DONTCARE,/* pitch and family */
802 	    uparm.font_name);	/* face */
803     if (pstate.underscore_font == NULL) {
804 	*fail = "CreateFont (underscore) failed";
805 	goto failed;
806     }
807 
808     /* Create a bold, underscore font that is the same size, if possible. */
809     pstate.bold_underscore_font = CreateFont(
810 	    pstate.space_size.cy,	/* height */
811 	    pstate.space_size.cx,	/* width */
812 	    0,			/* escapement */
813 	    0,			/* orientation */
814 	    FW_BOLD,		/* weight */
815 	    FALSE,			/* italic */
816 	    TRUE,			/* underline */
817 	    FALSE,			/* strikeout */
818 	    ANSI_CHARSET,		/* character set */
819 	    OUT_OUTLINE_PRECIS,	/* output precision */
820 	    CLIP_DEFAULT_PRECIS,	/* clip precision */
821 	    DEFAULT_QUALITY,	/* quality */
822 	    FIXED_PITCH|FF_DONTCARE,/* pitch and family */
823 	    uparm.font_name);	/* face */
824     if (pstate.bold_underscore_font == NULL) {
825 	*fail = "CreateFont (bold underscore) failed";
826 	goto failed;
827     }
828 
829     /* Create a caption font. */
830     pstate.caption_font = CreateFont(
831 	    pstate.space_size.cy,	/* height */
832 	    0,			/* width */
833 	    0,			/* escapement */
834 	    0,			/* orientation */
835 	    FW_NORMAL,		/* weight */
836 	    TRUE,			/* italic */
837 	    FALSE,			/* underline */
838 	    FALSE,			/* strikeout */
839 	    ANSI_CHARSET,		/* character set */
840 	    OUT_OUTLINE_PRECIS,	/* output precision */
841 	    CLIP_DEFAULT_PRECIS,	/* clip precision */
842 	    DEFAULT_QUALITY,	/* quality */
843 	    VARIABLE_PITCH|FF_DONTCARE,/* pitch and family */
844 	    "Times New Roman");	/* face */
845     if (pstate.bold_underscore_font == NULL) {
846 	*fail = "CreateFont (bold underscore) failed";
847 	goto failed;
848     }
849 
850     /* Set up the manual spacing array. */
851     pstate.dx = Malloc(sizeof(INT) * maxCOLS);
852     for (i = 0; i < maxCOLS; i++) {
853 	pstate.dx[i] = pstate.space_size.cx;
854     }
855 
856     /* Fill in the document info. */
857     memset(&docinfo, '\0', sizeof(docinfo));
858     docinfo.cbSize = sizeof(docinfo);
859     docinfo.lpszDocName = "wc3270 screen";
860 
861     /* Start the document. */
862     if (StartDoc(dc, &docinfo) <= 0) {
863 	*fail = "StartDoc failed";
864 	goto failed;
865     }
866 
867     pstate.active = true;
868     return GDI_STATUS_SUCCESS;
869 
870 failed:
871     /* Clean up what we can and return failure. */
872     if (default_printer_name != NULL) {
873 	Free(default_printer_name);
874     }
875     cleanup_fonts();
876     return GDI_STATUS_ERROR;
877 }
878 
879 /*
880  * Print one screeful to the GDI printer.
881  */
882 static int
gdi_screenful(struct ea * ea,unsigned short rows,unsigned short cols,const char ** fail)883 gdi_screenful(struct ea *ea, unsigned short rows, unsigned short cols,
884 	const char **fail)
885 {
886     HDC dc = pstate.dlg.hDC;
887     LPDEVMODE devmode;
888     int row, col, baddr;
889     int rc = 0;
890     int status;
891     int fa_addr = find_field_attribute_ea(0, ea);
892     unsigned char fa = ea[fa_addr].fa;
893     bool fa_high, high;
894     bool fa_underline, underline;
895     bool fa_reverse, reverse;
896     ucs4_t uc;
897     int usable_rows;
898     HFONT got_font = NULL, want_font;
899 #if defined(GDI_DEBUG) /*[*/
900     const char *want_font_name;
901 #endif /*]*/
902     enum { COLOR_NONE, COLOR_NORMAL, COLOR_REVERSE } got_color = COLOR_NONE,
903 	want_color;
904 
905     devmode = (LPDEVMODE)GlobalLock(pstate.dlg.hDevMode);
906 
907     /* Compute the usable rows, including the caption. */
908     usable_rows = pstate.usable_rows;
909     if (pstate.caption) {
910 	usable_rows -= 2;
911     }
912 
913     /*
914      * Does this screen fit?
915      * (Note that the first test, "pstate.out_row", is there so that if the
916      * font is so big the image won't fit at all, we still print as much
917      * of it as we can.)
918      */
919     if (pstate.out_row && pstate.out_row + ROWS > usable_rows) {
920 	if (EndPage(dc) <= 0) {
921 	    *fail = "EndPage failed";
922 	    rc = -1;
923 	    goto done;
924 	}
925 	pstate.out_row = 0;
926 	pstate.screens = 0;
927     }
928 
929     /* If there is a caption, put it on the last line. */
930     if (pstate.out_row == 0 && pstate.caption != NULL) {
931 	SelectObject(dc, pstate.caption_font);
932 	status = ExtTextOut(dc,
933 		pstate.hmargin_pixels - pchar.poffX,
934 		pstate.vmargin_pixels +
935 		    ((pstate.usable_rows - 1) * pstate.space_size.cy) -
936 		    pchar.poffY,
937 		0, NULL,
938 		pstate.caption, (UINT)strlen(pstate.caption), NULL);
939 	if (status <= 0) {
940 	    *fail = "ExtTextOut(caption) failed";
941 	    rc = -1;
942 	    goto done;
943 	}
944     }
945 
946     /* Draw a line separating the screens. */
947     if (pstate.out_row) {
948 	HPEN pen;
949 
950 	pen = CreatePen(PS_SOLID, 3, RGB(0, 0, 0));
951 	SelectObject(dc, pen);
952 	status = MoveToEx(dc,
953 		pstate.hmargin_pixels - pchar.poffX,
954 		pstate.vmargin_pixels +
955 		    (pstate.out_row * pstate.space_size.cy) +
956 		    (pstate.space_size.cy / 2) - pchar.poffY,
957 		    NULL);
958 	if (status == 0) {
959 	    *fail = "MoveToEx failed";
960 	    rc = -1;
961 	    goto done;
962 	}
963 	status = LineTo(dc,
964 		pstate.hmargin_pixels - pchar.poffX + pstate.usable_xpixels,
965 		pstate.vmargin_pixels +
966 		    (pstate.out_row * pstate.space_size.cy) +
967 		    (pstate.space_size.cy / 2) - pchar.poffY);
968 	if (status == 0) {
969 	    *fail = "LineTo failed";
970 	    rc = -1;
971 	    goto done;
972 	}
973 	DeleteObject(pen);
974     }
975 
976     /* Now dump out a screen's worth. */
977     if (ea[fa_addr].gr & GR_INTENSIFY) {
978 	fa_high = true;
979     } else {
980 	fa_high = FA_IS_HIGH(fa);
981     }
982     fa_reverse = ((ea[fa_addr].gr & GR_REVERSE) != 0);
983     fa_underline = ((ea[fa_addr].gr & GR_UNDERLINE) != 0);
984 
985     for (baddr = 0, row = 0; row < ROWS; row++) {
986 	if (pstate.out_row + row >= usable_rows) {
987 	    break;
988 	}
989 	for (col = 0; col < COLS; col++, baddr++) {
990 	    wchar_t w;
991 	    INT wdx;
992 
993 	    if (ea[baddr].fa) {
994 		fa = ea[baddr].fa;
995 		if (ea[baddr].gr & GR_INTENSIFY) {
996 		    fa_high = true;
997 		} else {
998 		    fa_high = FA_IS_HIGH(fa);
999 		}
1000 		fa_reverse = ((ea[fa_addr].gr & GR_REVERSE) != 0);
1001 		fa_underline = ((ea[fa_addr].gr & GR_UNDERLINE) != 0);
1002 
1003 		/* Just skip it. */
1004 		continue;
1005 	    }
1006 	    if (col >= pstate.usable_cols) {
1007 		continue;
1008 	    }
1009 	    if (FA_IS_ZERO(fa)) {
1010 		if (ctlr_dbcs_state_ea(baddr, ea) == DBCS_LEFT) {
1011 		    uc = 0x3000;
1012 		} else {
1013 		    uc = ' ';
1014 		}
1015 	    } else if (is_nvt(&ea[baddr], false, &uc)) {
1016 		switch (ctlr_dbcs_state(baddr)) {
1017 		case DBCS_NONE:
1018 		case DBCS_SB:
1019 		case DBCS_LEFT:
1020 		    break;
1021 		case DBCS_RIGHT:
1022 		    /* skip altogether, we took care of it above */
1023 		    continue;
1024 		default:
1025 		    uc = ' ';
1026 		    break;
1027 		}
1028 	    } else {
1029 		/* Convert EBCDIC to Unicode. */
1030 		switch (ctlr_dbcs_state(baddr)) {
1031 		case DBCS_NONE:
1032 		case DBCS_SB:
1033 		    uc = ebcdic_to_unicode(ea[baddr].ec, ea[baddr].cs,
1034 			    EUO_NONE);
1035 		    if (uc == 0) {
1036 			uc = ' ';
1037 		    }
1038 		    break;
1039 		case DBCS_LEFT:
1040 		    uc = ebcdic_to_unicode((ea[baddr].ec << 8) |
1041 				ea[baddr + 1].ec,
1042 			    CS_BASE, EUO_NONE);
1043 		    if (uc == 0) {
1044 			uc = 0x3000;
1045 		    }
1046 		    break;
1047 		case DBCS_RIGHT:
1048 		    /* skip altogether, we took care of it above */
1049 		    continue;
1050 		default:
1051 		    uc = ' ';
1052 		    break;
1053 		}
1054 	    }
1055 
1056 	    /* Figure out the attributes of the current buffer position. */
1057 	    high = ((ea[baddr].gr & GR_INTENSIFY) != 0);
1058 	    if (!high) {
1059 		high = fa_high;
1060 	    }
1061 	    reverse = ((ea[fa_addr].gr & GR_REVERSE) != 0);
1062 	    if (!reverse) {
1063 		reverse = fa_reverse;
1064 	    }
1065 	    underline = ((ea[fa_addr].gr & GR_UNDERLINE) != 0);
1066 	    if (!underline) {
1067 		underline = fa_underline;
1068 	    }
1069 
1070 	    /* Set the bg/fg color and font. */
1071 	    if (reverse) {
1072 		want_color = COLOR_REVERSE;
1073 	    } else {
1074 		want_color = COLOR_NORMAL;
1075 	    }
1076 	    if (want_color != got_color) {
1077 		switch (want_color) {
1078 		case COLOR_REVERSE:
1079 		    SetTextColor(dc, 0xffffff);
1080 		    SetBkColor(dc, 0);
1081 		    SetBkMode(dc, OPAQUE);
1082 		    break;
1083 		case COLOR_NORMAL:
1084 		    SetTextColor(dc, 0);
1085 		    SetBkColor(dc, 0xffffff);
1086 		    SetBkMode(dc, TRANSPARENT);
1087 		    break;
1088 		default:
1089 		    break;
1090 		}
1091 		got_color = want_color;
1092 	    }
1093 	    if (!high && !underline) {
1094 		want_font = pstate.font;
1095 #if defined(GDI_DEBUG) /*[*/
1096 		want_font_name = "Roman";
1097 #endif /*]*/
1098 	    } else if (high && !underline) {
1099 		want_font = pstate.bold_font;
1100 #if defined(GDI_DEBUG) /*[*/
1101 		want_font_name = "Bold";
1102 #endif /*]*/
1103 	    } else if (!high && underline) {
1104 		want_font = pstate.underscore_font;
1105 #if defined(GDI_DEBUG) /*[*/
1106 		want_font_name = "Underscore";
1107 #endif /*]*/
1108 	    } else {
1109 		want_font = pstate.bold_underscore_font;
1110 #if defined(GDI_DEBUG) /*[*/
1111 		want_font_name = "Underscore";
1112 #endif /*]*/
1113 	    }
1114 	    if (want_font != got_font) {
1115 		SelectObject(dc, want_font);
1116 		got_font = want_font;
1117 #if defined(GDI_DEBUG) /*[*/
1118 		vtrace("[gdi] selecting %s\n", want_font_name);
1119 #endif /*]*/
1120 	    }
1121 
1122 	    /*
1123 	     * Handle spaces and DBCS spaces (U+3000).
1124 	     * If not reverse or underline, just skip over them.
1125 	     * Otherwise, print a space or two spaces, using the
1126 	     * right font and modes.
1127 	     */
1128 	    if (uc == ' ' || uc == 0x3000) {
1129 		if (reverse || underline) {
1130 		    status = ExtTextOut(dc, pstate.hmargin_pixels +
1131 				(col * pstate.space_size.cx) -
1132 				pchar.poffX,
1133 			    pstate.vmargin_pixels +
1134 				((pstate.out_row + row + 1) *
1135 				 pstate.space_size.cy) -
1136 				pchar.poffY,
1137 			    0, NULL,
1138 			    "  ",
1139 			    (uc == 0x3000)? 2: 1,
1140 			    pstate.dx);
1141 		    if (status <= 0) {
1142 			*fail = "ExtTextOut(space) failed";
1143 			rc = -1;
1144 			goto done;
1145 		    }
1146 		}
1147 		continue;
1148 	    }
1149 
1150 	    /*
1151 	     * Emit one character at a time. This should be optimized to print
1152 	     * strings of characters with the same attributes.
1153 	     */
1154 #if defined(GDI_DEBUG) /*[*/
1155 	    if (uc != ' ') {
1156 		vtrace("[gdi] row %d col %d x=%ld y=%ld uc=%lx\n",
1157 			row, col,
1158 			pstate.hmargin_pixels + (col * pstate.space_size.cx) -
1159 			    pchar.poffX,
1160 			pstate.vmargin_pixels +
1161 			  ((pstate.out_row + row + 1) * pstate.space_size.cy) -
1162 			    pchar.poffY,
1163 			uc);
1164 	    }
1165 #endif /*]*/
1166 	    w = (wchar_t)uc;
1167 	    wdx = pstate.space_size.cx;
1168 	    status = ExtTextOutW(dc,
1169 		    pstate.hmargin_pixels + (col * pstate.space_size.cx) -
1170 			pchar.poffX,
1171 		    pstate.vmargin_pixels +
1172 			((pstate.out_row + row + 1) *
1173 			 pstate.space_size.cy) -
1174 			pchar.poffY,
1175 		    0, NULL,
1176 		    &w, 1, &wdx);
1177 	    if (status <= 0) {
1178 		*fail = "ExtTextOutW(image) failed";
1179 		rc = -1;
1180 		goto done;
1181 	    }
1182 	}
1183     }
1184 
1185     /* Tally the current screen and see if we need to go to a new page. */
1186     pstate.out_row += (row + 1); /* current screen plus a gap */
1187     pstate.screens++;
1188     if (pstate.out_row >= usable_rows || pstate.screens >= uparm.spp) {
1189 	if (EndPage(dc) <= 0) {
1190 	    *fail = "EndPage failed";
1191 	    rc = -1;
1192 	    goto done;
1193 	}
1194 	pstate.out_row = 0;
1195 	pstate.screens = 0;
1196     }
1197 
1198 done:
1199     GlobalUnlock(devmode);
1200     return rc;
1201 }
1202 
1203 /*
1204  * Finish the GDI print-out and clean up the data structures.
1205  */
1206 static int
gdi_done(const char ** fail)1207 gdi_done(const char **fail)
1208 {
1209     int rc = 0;
1210 
1211     if (pstate.out_row) {
1212 	if (EndPage(pstate.dlg.hDC) <= 0) {
1213 	    *fail = "EndPage failed";
1214 	    rc = -1;
1215 	}
1216 	pstate.out_row = 0;
1217     }
1218     if (EndDoc(pstate.dlg.hDC) <= 0) {
1219 	*fail = "EndDoc failed";
1220 	rc = -1;
1221     }
1222 
1223     cleanup_fonts();
1224 
1225     return rc;
1226 }
1227 
1228 /*
1229  * Clean up the GDI data structures without attempting any more printing.
1230  */
1231 static void
gdi_abort(void)1232 gdi_abort(void)
1233 {
1234     if (pstate.out_row) {
1235 	EndPage(pstate.dlg.hDC);
1236 	pstate.out_row = 0;
1237     }
1238     EndDoc(pstate.dlg.hDC);
1239 
1240     cleanup_fonts();
1241 }
1242 
1243 /*
1244  * Get a DEVMODE and DEVNAMES from a printer name.
1245  *
1246  * Returns TRUE for success, FALSE for failure.
1247  */
1248 static BOOL
get_printer_device(const char * printer_name,HGLOBAL * pdevnames,HGLOBAL * pdevmode)1249 get_printer_device(const char *printer_name, HGLOBAL *pdevnames,
1250 	HGLOBAL *pdevmode)
1251 {
1252     HANDLE h;
1253     DWORD len, len2;
1254     PRINTER_INFO_2 *pi;
1255     size_t dmsize;
1256     HGLOBAL gdm;
1257     char *dm;
1258     size_t ldn;
1259     size_t lpn;
1260     size_t ltn;
1261     HGLOBAL gdn;
1262     DEVNAMES *dn;
1263     size_t offset;
1264 
1265     /* Gotta have something to return the values in. */
1266     if (pdevmode == NULL || pdevnames == NULL) {
1267 	return FALSE;
1268     }
1269 
1270     /* Open the printer. */
1271     h = NULL;
1272     if (!OpenPrinter((char *)printer_name, &h, NULL)) {
1273 	return FALSE;
1274     }
1275 
1276     /* Get a PRINTER_INFO_2 structure for the printer. */
1277     GetPrinter(h, 2, NULL, 0, &len);
1278     pi = (PRINTER_INFO_2 *)malloc(len);
1279     if (!GetPrinter(h, 2, (LPBYTE)pi, len, &len2)) {
1280 	free(pi);
1281 	ClosePrinter(h);
1282 	return FALSE;
1283     }
1284     ClosePrinter(h);
1285     h = NULL;
1286 
1287     /* Copy the DEVMODE from the PRINTER_INFO_2 into a global handle. */
1288     dmsize = sizeof(*pi->pDevMode) + pi->pDevMode->dmDriverExtra;
1289     gdm = GlobalAlloc(GHND, dmsize);
1290     assert(gdm);
1291     dm = (char *)GlobalLock(gdm);
1292     assert(dm);
1293     memcpy(dm, pi->pDevMode, dmsize);
1294     GlobalUnlock(gdm);
1295 
1296     /*
1297      * Compute the size of the DEVNAMES structure from the fields in the
1298      * PRINTER_INFO_2.
1299      */
1300     ldn = strlen(pi->pDriverName)  + 1;
1301     lpn = strlen(pi->pPrinterName) + 1;
1302     ltn = strlen(pi->pPortName)    + 1;
1303 
1304     /*
1305      * Construct a DEVNAMES from the PRINTER_INFO_2, allocated as a global
1306      * handle.
1307      */
1308     gdn = GlobalAlloc(GHND, sizeof(DEVNAMES) + ldn + lpn + ltn);
1309     assert(gdn);
1310     dn = (DEVNAMES *)GlobalLock(gdn);
1311     assert(dn);
1312     memset(dn, '\0', sizeof(DEVNAMES));
1313     offset = sizeof(DEVNAMES);
1314     dn->wDriverOffset = (WORD)offset;
1315     memcpy((char *)dn + offset, pi->pDriverName, ldn);
1316     offset += ldn;
1317     dn->wDeviceOffset = (WORD)offset;
1318     memcpy((char *)dn + offset, pi->pPrinterName, lpn);
1319     offset += lpn;
1320     dn->wOutputOffset = (WORD)offset;
1321     memcpy((char *)dn + offset, pi->pPortName, ltn);
1322     dn->wDefault = 0;
1323 
1324     /* Done filling in dn. */
1325     GlobalUnlock(gdn);
1326 
1327     /* Done with the PRINTER_INFO_2. */
1328     free(pi);
1329     pi = NULL;
1330 
1331     /* Return the devmode and devnames. */
1332     *pdevmode = gdm;
1333     *pdevnames = gdn;
1334 
1335     /* Success. */
1336     return TRUE;
1337 }
1338