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