1 /* Copyright (C) 1989, 1995, 1996, 1997, 1998, 2000 artofcode LLC.  All rights reserved.
2 
3   This program is free software; you can redistribute it and/or modify it
4   under the terms of the GNU General Public License as published by the
5   Free Software Foundation; either version 2 of the License, or (at your
6   option) any later version.
7 
8   This program is distributed in the hope that it will be useful, but
9   WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   General Public License for more details.
12 
13   You should have received a copy of the GNU General Public License along
14   with this program; if not, write to the Free Software Foundation, Inc.,
15   59 Temple Place, Suite 330, Boston, MA, 02111-1307.
16 
17 */
18 
19 /*$Id: gdevwprn.c,v 1.3.2.1.2.1 2003/01/17 00:49:02 giles Exp $ */
20 /*
21  * Microsoft Windows 3.n printer driver for Ghostscript.
22  *
23  * Original version by Russell Lang and
24  * L. Peter Deutsch, Aladdin Enterprises.
25  */
26 #include "gdevmswn.h"
27 #include "gp.h"
28 #include "gpcheck.h"
29 #include "commdlg.h"
30 
31 #define PARENT_WINDOW  HWND_DESKTOP
32 
33 
34 /*
35  ****** NOTE: this module and gdevwddb should be refactored.
36  * The drawing routines are almost identical.
37  * The differences are that the mswinprn doesn't use an extra
38  * palette (gdevwddb.c could probably be made to work with
39  * one palette also), mswinprn doesn't call win_update() because
40  * hwndimg doesn't exist, and the HDC is hdcmf not hdcbit.
41  ******/
42 
43 /* Make sure we cast to the correct structure type. */
44 typedef struct gx_device_win_prn_s gx_device_win_prn;
45 
46 #undef wdev
47 #define wdev ((gx_device_win_prn *)dev)
48 
49 /* Forward references */
50 private void near win_prn_addtool(P2(gx_device_win_prn *, int));
51 private void near win_prn_maketools(P2(gx_device_win_prn *, HDC));
52 private void near win_prn_destroytools(P1(gx_device_win_prn *));
53 BOOL CALLBACK _export AbortProc(HDC, int);
54 
55 /* Device procedures */
56 
57 /* See gxdevice.h for the definitions of the procedures. */
58 private dev_proc_open_device(win_prn_open);
59 private dev_proc_close_device(win_prn_close);
60 private dev_proc_sync_output(win_prn_sync_output);
61 private dev_proc_output_page(win_prn_output_page);
62 private dev_proc_map_rgb_color(win_prn_map_rgb_color);
63 private dev_proc_fill_rectangle(win_prn_fill_rectangle);
64 private dev_proc_tile_rectangle(win_prn_tile_rectangle);
65 private dev_proc_copy_mono(win_prn_copy_mono);
66 private dev_proc_copy_color(win_prn_copy_color);
67 private dev_proc_draw_line(win_prn_draw_line);
68 
69 /* The device descriptor */
70 struct gx_device_win_prn_s {
71     gx_device_common;
72     gx_device_win_common;
73 
74     /* Handles */
75 
76     HPEN hpen, *hpens;
77     uint hpensize;
78     HBRUSH hbrush, *hbrushs;
79     uint hbrushsize;
80 #define select_brush(color)\
81   if (wdev->hbrush != wdev->hbrushs[color])\
82    {	wdev->hbrush = wdev->hbrushs[color];\
83 	SelectObject(wdev->hdcmf,wdev->hbrush);\
84    }
85     /* A staging bitmap for copy_mono. */
86     /* We want one big enough to handle the standard 16x16 halftone; */
87     /* this is also big enough for ordinary-size characters. */
88 
89 #define bmWidthBytes 4		/* must be even */
90 #define bmWidthBits (bmWidthBytes * 8)
91 #define bmHeight 32
92     HBITMAP FAR hbmmono;
93     HDC FAR hdcmono;
94     gx_bitmap_id bm_id;
95 
96     HDC hdcprn;
97     HDC hdcmf;
98     char mfname[gp_file_name_sizeof];
99     DLGPROC lpfnAbortProc;
100 };
101 private const gx_device_procs win_prn_procs =
102 {
103     win_prn_open,
104     NULL,			/* get_initial_matrix */
105     win_prn_sync_output,
106     win_prn_output_page,
107     win_prn_close,
108     win_prn_map_rgb_color,
109     win_map_color_rgb,
110     win_prn_fill_rectangle,
111     win_prn_tile_rectangle,
112     win_prn_copy_mono,
113     win_prn_copy_color,
114     win_prn_draw_line,
115     NULL,			/* get_bits */
116     NULL,			/* get_params */
117     NULL,			/* put_params */
118     NULL,			/* map_cmyk_color */
119     win_get_xfont_procs
120 };
121 gx_device_win_prn far_data gs_mswinprn_device =
122 {
123     std_device_std_body(gx_device_win_prn, &win_prn_procs, "mswinprn",
124 			INITIAL_WIDTH, INITIAL_HEIGHT,	/* win_open() fills these in later */
125 			INITIAL_RESOLUTION, INITIAL_RESOLUTION	/* win_open() fills these in later */
126     ),
127     {0},			/* std_procs */
128     0,				/* BitsPerPixel */
129     2,				/* nColors */
130 };
131 
132 /* Open the win_prn driver */
133 private int
win_prn_open(gx_device * dev)134 win_prn_open(gx_device * dev)
135 {
136     int depth;
137     PRINTDLG pd;
138     FILE *f;
139     POINT offset;
140     POINT size;
141     float m[4];
142 
143     memset(&pd, 0, sizeof(PRINTDLG));
144     pd.lStructSize = sizeof(PRINTDLG);
145     pd.hwndOwner = PARENT_WINDOW;
146     pd.Flags = PD_PRINTSETUP | PD_RETURNDC;
147     if (!PrintDlg(&pd)) {
148 	/* device not opened - exit ghostscript */
149 	return gs_error_limitcheck;
150     }
151     GlobalFree(pd.hDevMode);
152     GlobalFree(pd.hDevNames);
153     pd.hDevMode = pd.hDevNames = NULL;
154     wdev->hdcprn = pd.hDC;
155     if (!(GetDeviceCaps(wdev->hdcprn, RASTERCAPS) != RC_BITBLT)) {
156 	DeleteDC(wdev->hdcprn);
157 	return gs_error_limitcheck;
158     }
159     wdev->lpfnAbortProc = (DLGPROC) AbortProc;
160     Escape(wdev->hdcprn, SETABORTPROC, 0, (LPSTR) wdev->lpfnAbortProc, NULL);
161     if (Escape(wdev->hdcprn, STARTDOC, strlen(szAppName), szAppName, NULL) <= 0) {
162 	DeleteDC(wdev->hdcprn);
163 	return gs_error_limitcheck;
164     }
165     f = gp_open_scratch_file(gp_scratch_file_name_prefix,
166 			     wdev->mfname, "wb");
167     if (f == (FILE *) NULL) {
168 	Escape(wdev->hdcprn, ENDDOC, 0, NULL, NULL);
169 	DeleteDC(wdev->hdcprn);
170 	return gs_error_limitcheck;
171     }
172     unlink(wdev->mfname);
173     wdev->hdcmf = CreateMetaFile(wdev->mfname);
174 
175     dev->x_pixels_per_inch = (float)GetDeviceCaps(wdev->hdcprn, LOGPIXELSX);
176     dev->y_pixels_per_inch = (float)GetDeviceCaps(wdev->hdcprn, LOGPIXELSY);
177     Escape(wdev->hdcprn, GETPHYSPAGESIZE, 0, NULL, (LPPOINT) & size);
178     dev->width = size.x;
179     dev->height = size.y;
180     Escape(wdev->hdcprn, GETPRINTINGOFFSET, 0, NULL, (LPPOINT) & offset);
181     m[0] /*left */  = offset.x / dev->x_pixels_per_inch;
182     m[3] /*top */  = offset.y / dev->y_pixels_per_inch;
183     m[2] /*right */  =
184 	(size.x - offset.x - GetDeviceCaps(wdev->hdcprn, HORZRES))
185 	/ dev->x_pixels_per_inch;
186     m[1] /*bottom */  =
187 	(size.y - offset.y - GetDeviceCaps(wdev->hdcprn, VERTRES))
188 	/ dev->y_pixels_per_inch
189 	+ 0.15;			/* hack to add a bit more margin for deskjet printer */
190     gx_device_set_margins(dev, m, true);
191 
192     /* Set parameters that were unknown before opening device */
193     /* Find out if the device supports color */
194     /* We recognize 2, 16 or 256 color devices */
195     depth = GetDeviceCaps(wdev->hdcprn, PLANES) * GetDeviceCaps(wdev->hdcprn, BITSPIXEL);
196     if (depth >= 8) {		/* use 64 static colors and 166 dynamic colors from 8 planes */
197 	static const gx_device_color_info win_256color = dci_color(8, 31, 4);
198 
199 	dev->color_info = win_256color;
200 	wdev->nColors = 64;
201     } else if (depth >= 4) {
202 	static const gx_device_color_info win_16ega_color = dci_color(4, 2, 3);
203 
204 	dev->color_info = win_16ega_color;
205 	wdev->nColors = 16;
206     } else {			/* default is black_and_white */
207 	wdev->nColors = 2;
208     }
209 
210     /* create palette for display */
211     if ((wdev->limgpalette = win_makepalette((gx_device_win *) dev))
212 	== (LPLOGPALETTE) NULL) {
213 	HMETAFILE hmf = CloseMetaFile(wdev->hdcmf);
214 
215 	DeleteMetaFile(hmf);
216 	unlink(wdev->mfname);
217 	Escape(wdev->hdcprn, ENDDOC, 0, NULL, NULL);
218 	DeleteDC(wdev->hdcprn);
219 	return win_nomemory();
220     }
221     wdev->himgpalette = CreatePalette(wdev->limgpalette);
222 
223     /* Create the bitmap and DC for copy_mono. */
224     wdev->hbmmono = CreateBitmap(bmWidthBits, bmHeight, 1, 1, NULL);
225     wdev->hdcmono = CreateCompatibleDC(wdev->hdcprn);
226     if (wdev->hbmmono == NULL || wdev->hdcmono == NULL) {
227 	HMETAFILE hmf = CloseMetaFile(wdev->hdcmf);
228 
229 	DeleteMetaFile(hmf);
230 	unlink(wdev->mfname);
231 	Escape(wdev->hdcprn, ENDDOC, 0, NULL, NULL);
232 	DeleteDC(wdev->hdcprn);
233 	gs_free((char *)(wdev->limgpalette), 1, sizeof(LOGPALETTE) +
234 		(1 << (wdev->color_info.depth)) * sizeof(PALETTEENTRY),
235 		"win_prn_open");
236 	return win_nomemory();
237     }
238     SetMapMode(wdev->hdcmono, GetMapMode(wdev->hdcprn));
239     SelectObject(wdev->hdcmono, wdev->hbmmono);
240     (void)SelectPalette(wdev->hdcmf, wdev->himgpalette, FALSE);
241     RealizePalette(wdev->hdcmf);
242     win_prn_maketools(wdev, wdev->hdcmf);
243     wdev->bm_id = gx_no_bitmap_id;
244 
245     return 0;
246 }
247 
248 
249 /* Close the win_prn driver */
250 private int
win_prn_close(gx_device * dev)251 win_prn_close(gx_device * dev)
252 {
253     HMETAFILE hmf;
254 
255     /* Free resources */
256     Escape(wdev->hdcprn, ENDDOC, 0, NULL, NULL);
257     DeleteDC(wdev->hdcprn);
258     hmf = CloseMetaFile(wdev->hdcmf);
259     DeleteMetaFile(hmf);
260     unlink(wdev->mfname);
261 
262     win_prn_destroytools(wdev);
263     DeleteDC(wdev->hdcmono);
264     DeleteObject(wdev->hbmmono);
265     DeleteObject(wdev->himgpalette);
266     gs_free((char *)(wdev->limgpalette), 1, sizeof(LOGPALETTE) +
267 	    (1 << (wdev->color_info.depth)) * sizeof(PALETTEENTRY),
268 	    "win_prn_close");
269     return (0);
270 }
271 
272 /* Do nothing */
273 int
win_prn_sync_output(gx_device * dev)274 win_prn_sync_output(gx_device * dev)
275 {
276     return 0;
277 }
278 
279 /* Write page to printer */
280 int
win_prn_output_page(gx_device * dev,int num_copies,int flush)281 win_prn_output_page(gx_device * dev, int num_copies, int flush)
282 {
283     RECT rect;
284     HMETAFILE hmf;
285 
286     hmf = CloseMetaFile(wdev->hdcmf);
287 
288     Escape(wdev->hdcprn, NEXTBAND, 0, NULL, (LPRECT) & rect);
289     while (!IsRectEmpty(&rect)) {
290 	PlayMetaFile(wdev->hdcprn, hmf);
291 	if (Escape(wdev->hdcprn, NEXTBAND, 0, NULL, (LPRECT) & rect) <= 0)
292 	    break;
293     }
294     DeleteMetaFile(hmf);
295     unlink(wdev->mfname);
296     wdev->hdcmf = CreateMetaFile(wdev->mfname);
297     (void)SelectPalette(wdev->hdcmf, wdev->himgpalette, FALSE);
298     RealizePalette(wdev->hdcmf);
299     SelectObject(wdev->hdcmf, wdev->hpen);
300     SelectObject(wdev->hdcmf, wdev->hbrush);
301 
302     return gx_finish_output_page(dev, num_copies, flush);
303 }
304 
305 
306 /* Map a r-g-b color to the colors available under Windows */
307 private gx_color_index
win_prn_map_rgb_color(gx_device * dev,gx_color_value r,gx_color_value g,gx_color_value b)308 win_prn_map_rgb_color(gx_device * dev, gx_color_value r, gx_color_value g,
309 		      gx_color_value b)
310 {
311     int i = wdev->nColors;
312     gx_color_index color = win_map_rgb_color(dev, r, g, b);
313 
314     if (color != i)
315 	return color;
316     (void)SelectPalette(wdev->hdcmf, wdev->himgpalette, FALSE);
317     RealizePalette(wdev->hdcmf);
318     win_prn_addtool(wdev, i);
319 
320     return color;
321 }
322 
323 
324 /* Macro for filling a rectangle with a color. */
325 /* Note that it starts with a declaration. */
326 #define fill_rect(x, y, w, h, color)\
327 RECT rect;\
328 rect.left = x, rect.top = y;\
329 rect.right = x + w, rect.bottom = y + h;\
330 FillRect(wdev->hdcmf, &rect, wdev->hbrushs[(int)color])
331 
332 
333 /* Fill a rectangle. */
334 private int
win_prn_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)335 win_prn_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
336 		       gx_color_index color)
337 {
338     fit_fill(dev, x, y, w, h);
339     /* Use PatBlt for filling.  Special-case black. */
340     if (color == 0)
341 	PatBlt(wdev->hdcmf, x, y, w, h, rop_write_0s);
342     else {
343 	select_brush((int)color);
344 	PatBlt(wdev->hdcmf, x, y, w, h, rop_write_pattern);
345     }
346 
347     return 0;
348 }
349 
350 /* Tile a rectangle.  If neither color is transparent, */
351 /* pre-clear the rectangle to color0 and just tile with color1. */
352 /* This is faster because of how win_copy_mono is implemented. */
353 /* Note that this also does the right thing for colored tiles. */
354 private int
win_prn_tile_rectangle(gx_device * dev,const gx_tile_bitmap * tile,int x,int y,int w,int h,gx_color_index czero,gx_color_index cone,int px,int py)355 win_prn_tile_rectangle(gx_device * dev, const gx_tile_bitmap * tile,
356       int x, int y, int w, int h, gx_color_index czero, gx_color_index cone,
357 		       int px, int py)
358 {
359     fit_fill(dev, x, y, w, h);
360     if (czero != gx_no_color_index && cone != gx_no_color_index) {
361 	fill_rect(x, y, w, h, czero);
362 	czero = gx_no_color_index;
363     }
364     if (tile->raster == bmWidthBytes && tile->size.y <= bmHeight &&
365 	(px | py) == 0 && cone != gx_no_color_index
366 	) {			/* We can do this much more efficiently */
367 	/* by using the internal algorithms of copy_mono */
368 	/* and gx_default_tile_rectangle. */
369 	int width = tile->size.x;
370 	int height = tile->size.y;
371 	int rwidth = tile->rep_width;
372 	int irx = ((rwidth & (rwidth - 1)) == 0 ?	/* power of 2 */
373 		   x & (rwidth - 1) :
374 		   x % rwidth);
375 	int ry = y % tile->rep_height;
376 	int icw = width - irx;
377 	int ch = height - ry;
378 	int ex = x + w, ey = y + h;
379 	int fex = ex - width, fey = ey - height;
380 	int cx, cy;
381 
382 	select_brush((int)cone);
383 
384 	if (tile->id != wdev->bm_id || tile->id == gx_no_bitmap_id) {
385 	    wdev->bm_id = tile->id;
386 	    SetBitmapBits(wdev->hbmmono,
387 			  (DWORD) (bmWidthBytes * tile->size.y),
388 			  (BYTE *) tile->data);
389 	}
390 #define copy_tile(srcx, srcy, tx, ty, tw, th)\
391   BitBlt(wdev->hdcmf, tx, ty, tw, th, wdev->hdcmono, srcx, srcy, rop_write_at_1s)
392 
393 	if (ch > h)
394 	    ch = h;
395 	for (cy = y;;) {
396 	    if (w <= icw)
397 		copy_tile(irx, ry, x, cy, w, ch);
398 	    else {
399 		copy_tile(irx, ry, x, cy, icw, ch);
400 		cx = x + icw;
401 		while (cx <= fex) {
402 		    copy_tile(0, ry, cx, cy, width, ch);
403 		    cx += width;
404 		}
405 		if (cx < ex) {
406 		    copy_tile(0, ry, cx, cy, ex - cx, ch);
407 		}
408 	    }
409 	    if ((cy += ch) >= ey)
410 		break;
411 	    ch = (cy > fey ? ey - cy : height);
412 	    ry = 0;
413 	}
414 
415 	return 0;
416     }
417     return gx_default_tile_rectangle(dev, tile, x, y, w, h, czero, cone, px, py);
418 }
419 
420 
421 /* Draw a line */
422 private int
win_prn_draw_line(gx_device * dev,int x0,int y0,int x1,int y1,gx_color_index color)423 win_prn_draw_line(gx_device * dev, int x0, int y0, int x1, int y1,
424 		  gx_color_index color)
425 {
426     if (wdev->hpen != wdev->hpens[(int)color]) {
427 	wdev->hpen = wdev->hpens[(int)color];
428 	SelectObject(wdev->hdcmf, wdev->hpen);
429     }
430     MoveToEx(wdev->hdcmf, x0, y0, NULL);
431     LineTo(wdev->hdcmf, x1, y1);
432     return 0;
433 }
434 
435 /* Copy a monochrome bitmap.  The colors are given explicitly. */
436 /* Color = gx_no_color_index means transparent (no effect on the image). */
437 private int
win_prn_copy_mono(gx_device * dev,const byte * base,int sourcex,int raster,gx_bitmap_id id,int x,int y,int w,int h,gx_color_index zero,gx_color_index one)438 win_prn_copy_mono(gx_device * dev,
439 		const byte * base, int sourcex, int raster, gx_bitmap_id id,
440 		  int x, int y, int w, int h,
441 		  gx_color_index zero, gx_color_index one)
442 {
443     int endx;
444     const byte *ptr_line;
445     int width_bytes, height;
446     DWORD rop = rop_write_at_1s;
447     int color;
448     BYTE aBit[bmWidthBytes * bmHeight];
449     BYTE *aptr = aBit;
450 
451     fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
452 
453     if (sourcex & ~7) {
454 	base += sourcex >> 3;
455 	sourcex &= 7;
456     }
457     /* Break up large transfers into smaller ones. */
458     while ((endx = sourcex + w) > bmWidthBits) {
459 	int lastx = (endx - 1) & -bmWidthBits;
460 	int subw = endx - lastx;
461 	int code = win_prn_copy_mono(dev, base, lastx,
462 				     raster, gx_no_bitmap_id,
463 				     x + lastx - sourcex, y,
464 				     subw, h, zero, one);
465 
466 	if (code < 0)
467 	    return code;
468 	w -= subw;
469     }
470     while (h > bmHeight) {
471 	int code;
472 
473 	h -= bmHeight;
474 	code = win_prn_copy_mono(dev, base + h * raster, sourcex,
475 				 raster, gx_no_bitmap_id,
476 				 x, y + h, w, bmHeight, zero, one);
477 	if (code < 0)
478 	    return code;
479     }
480 
481     width_bytes = (sourcex + w + 7) >> 3;
482     ptr_line = base;
483 
484     if (zero == gx_no_color_index) {
485 	if (one == gx_no_color_index)
486 	    return 0;
487 	color = (int)one;
488 	if (color == 0)
489 	    rop = rop_write_0_at_1s;
490 	else
491 	    select_brush(color);
492     } else {
493 	if (one == gx_no_color_index) {
494 	    color = (int)zero;
495 	    rop = rop_write_at_0s;
496 	} else {		/* Pre-clear the rectangle to zero */
497 	    fill_rect(x, y, w, h, zero);
498 	    color = (int)one;
499 	}
500 	select_brush(color);
501     }
502 
503     if (id != wdev->bm_id || id == gx_no_bitmap_id) {
504 	wdev->bm_id = id;
505 	if (raster == bmWidthBytes) {	/* We can do the whole thing in a single transfer! */
506 	    SetBitmapBits(wdev->hbmmono,
507 			  (DWORD) (bmWidthBytes * h),
508 			  (BYTE *) base);
509 	} else {
510 	    for (height = h; height--;
511 		 ptr_line += raster, aptr += bmWidthBytes
512 		) {		/* Pack the bits into the bitmap. */
513 		switch (width_bytes) {
514 		    default:
515 			memcpy(aptr, ptr_line, width_bytes);
516 			break;
517 		    case 4:
518 			aptr[3] = ptr_line[3];
519 		    case 3:
520 			aptr[2] = ptr_line[2];
521 		    case 2:
522 			aptr[1] = ptr_line[1];
523 		    case 1:
524 			aptr[0] = ptr_line[0];
525 		}
526 	    }
527 	    SetBitmapBits(wdev->hbmmono,
528 			  (DWORD) (bmWidthBytes * h),
529 			  &aBit[0]);
530 	}
531     }
532     BitBlt(wdev->hdcmf, x, y, w, h, wdev->hdcmono, sourcex, 0, rop);
533     return 0;
534 }
535 
536 
537 /* Copy a color pixel map.  This is just like a bitmap, except that */
538 /* each pixel takes 8 or 4 bits instead of 1 when device driver has color. */
539 private int
win_prn_copy_color(gx_device * dev,const byte * base,int sourcex,int raster,gx_bitmap_id id,int x,int y,int w,int h)540 win_prn_copy_color(gx_device * dev,
541 		const byte * base, int sourcex, int raster, gx_bitmap_id id,
542 		   int x, int y, int w, int h)
543 {
544     fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
545 
546     if (gx_device_has_color(dev)) {
547 	switch (dev->color_info.depth) {
548 	    case 8:
549 		{
550 		    int xi, yi;
551 		    int skip = raster - w;
552 		    const byte *sptr = base + sourcex;
553 
554 		    if (w <= 0)
555 			return 0;
556 		    if (x < 0 || x + w > dev->width)
557 			return_error(gs_error_rangecheck);
558 		    for (yi = y; yi - y < h; yi++) {
559 			for (xi = x; xi - x < w; xi++) {
560 			    int color = *sptr++;
561 
562 			    SetPixel(wdev->hdcmf, xi, yi, PALETTEINDEX(color));
563 			}
564 			sptr += skip;
565 		    }
566 		}
567 		break;
568 	    case 4:
569 		{		/* color device, four bits per pixel */
570 		    const byte *line = base + (sourcex >> 1);
571 		    int dest_y = y, end_x = x + w;
572 
573 		    if (w <= 0)
574 			return 0;
575 		    while (h--) {	/* for each line */
576 			const byte *source = line;
577 			register int dest_x = x;
578 
579 			if (sourcex & 1) {	/* odd nibble first */
580 			    int color = *source++ & 0xf;
581 
582 			    SetPixel(wdev->hdcmf, dest_x, dest_y, PALETTEINDEX(color));
583 			    dest_x++;
584 			}
585 			/* Now do full bytes */
586 			while (dest_x < end_x) {
587 			    int color = *source >> 4;
588 
589 			    SetPixel(wdev->hdcmf, dest_x, dest_y, PALETTEINDEX(color));
590 			    dest_x++;
591 			    if (dest_x < end_x) {
592 				color = *source++ & 0xf;
593 				SetPixel(wdev->hdcmf, dest_x, dest_y, PALETTEINDEX(color));
594 				dest_x++;
595 			    }
596 			}
597 			dest_y++;
598 			line += raster;
599 		    }
600 		}
601 		break;
602 	    default:
603 		return (-1);	/* panic */
604 	}
605     } else
606 	/* monochrome device: one bit per pixel */
607     {				/* bitmap is the same as win_copy_mono: one bit per pixel */
608 	win_prn_copy_mono(dev, base, sourcex, raster, id, x, y, w, h,
609 			  (gx_color_index) 0,
610 			  (gx_color_index) (dev->color_info.depth == 8 ? 63 : dev->color_info.max_gray));
611     }
612     return 0;
613 }
614 
615 
616 /* ------ Internal routines ------ */
617 
618 #undef wdev
619 
620 
621 private void near
win_prn_addtool(gx_device_win_prn * wdev,int i)622 win_prn_addtool(gx_device_win_prn * wdev, int i)
623 {
624     wdev->hpens[i] = CreatePen(PS_SOLID, 1, PALETTEINDEX(i));
625     wdev->hbrushs[i] = CreateSolidBrush(PALETTEINDEX(i));
626 }
627 
628 
629 private void near
win_prn_maketools(gx_device_win_prn * wdev,HDC hdc)630 win_prn_maketools(gx_device_win_prn * wdev, HDC hdc)
631 {
632     int i;
633 
634     wdev->hpensize = (1 << (wdev->color_info.depth)) * sizeof(HPEN);
635     wdev->hpens = (HPEN *) gs_malloc(1, wdev->hpensize,
636 				     "win_prn_maketools(pens)");
637     wdev->hbrushsize = (1 << (wdev->color_info.depth)) * sizeof(HBRUSH);
638     wdev->hbrushs = (HBRUSH *) gs_malloc(1, wdev->hbrushsize,
639 					 "win_prn_maketools(brushes)");
640     if (wdev->hpens && wdev->hbrushs) {
641 	for (i = 0; i < wdev->nColors; i++)
642 	    win_prn_addtool(wdev, i);
643 
644 	wdev->hpen = wdev->hpens[0];
645 	SelectObject(hdc, wdev->hpen);
646 
647 	wdev->hbrush = wdev->hbrushs[0];
648 	SelectObject(hdc, wdev->hbrush);
649     }
650 }
651 
652 
653 private void near
win_prn_destroytools(gx_device_win_prn * wdev)654 win_prn_destroytools(gx_device_win_prn * wdev)
655 {
656     int i;
657 
658     for (i = 0; i < wdev->nColors; i++) {
659 	DeleteObject(wdev->hpens[i]);
660 	DeleteObject(wdev->hbrushs[i]);
661     }
662     gs_free((char *)wdev->hbrushs, 1, wdev->hbrushsize,
663 	    "win_prn_destroytools(brushes)");
664     gs_free((char *)wdev->hpens, 1, wdev->hpensize,
665 	    "win_prn_destroytools(pens)");
666 }
667 
668 BOOL CALLBACK _export
AbortProc(HDC hdcPrn,int code)669 AbortProc(HDC hdcPrn, int code)
670 {
671     process_interrupts();
672     if (code == SP_OUTOFDISK)
673 	return (FALSE);		/* cancel job */
674     return (TRUE);
675 }
676 
677