xref: /plan9/sys/src/cmd/gs/src/gdevpbm.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1992, 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: gdevpbm.c,v 1.11 2004/05/26 04:10:58 dan Exp $ */
18 /* Portable Bit/Gray/PixMap drivers */
19 #include "gdevprn.h"
20 #include "gscdefs.h"
21 #include "gscspace.h" /* For pnm_begin_typed_image(..) */
22 #include "gxgetbit.h"
23 #include "gxlum.h"
24 #include "gxiparam.h" /* For pnm_begin_typed_image(..) */
25 #include "gdevmpla.h"
26 #include "gdevplnx.h"
27 #include "gdevppla.h"
28 
29 /*
30  * Thanks are due to Jos Vos (jos@bull.nl) for an earlier P*M driver,
31  * on which this one is based; to Nigel Roles (ngr@cotswold.demon.co.uk),
32  * for the plan9bm changes; and to Leon Bottou (leonb@research.att.com)
33  * for the color detection code in pnm_begin_typed_image.
34  */
35 
36 /*
37  * There are 8 (families of) drivers here, plus one less related one:
38  *      pbm[raw] - outputs PBM (black and white).
39  *      pgm[raw] - outputs PGM (gray-scale).
40  *      pgnm[raw] - outputs PBM if the page contains only black and white,
41  *        otherwise PGM.
42  *      ppm[raw] - outputs PPM (RGB).
43  *      pnm[raw] - outputs PBM if the page contains only black and white,
44  *        otherwise PGM if the page contains only gray shades,
45  *        otherwise PPM.
46  *      pkm[raw] - computes internally in CMYK, outputs PPM (RGB).
47  *      pksm[raw] - computes internally in CMYK, outputs 4 PBM pages.
48  *      pam - outputs CMYK as PAM
49  *      plan9bm - outputs Plan 9 bitmap format.
50  */
51 
52 /*
53  * The code here is designed to work with variable depths for PGM and PPM.
54  * The code will work with any of the values in brackets, but the
55  * Ghostscript imager requires that depth be a power of 2 or be 24,
56  * so the actual allowed values are more limited.
57  *      pgm, pgnm: 1, 2, 4, 8, 16.  [1-16]
58  *      pgmraw, pgnmraw: 1, 2, 4, 8.  [1-8]
59  *      ppm, pnm: 4(3x1), 8(3x2), 16(3x5), 24(3x8), 32(3x10).  [3-32]
60  *      ppmraw, pnmraw: 4(3x1), 8(3x2), 16(3x5), 24(3x8).  [3-24]
61  *      pkm, pkmraw: 4(4x1), 8(4x2), 16(4x4), 32(4x8).  [4-32]
62  *	pksm, pksmraw: ibid.
63  *      pam: 32 (CMYK)
64  */
65 
66 /* Structure for P*M devices, which extend the generic printer device. */
67 
68 #define MAX_COMMENT 70		/* max user-supplied comment */
69 struct gx_device_pbm_s {
70     gx_device_common;
71     gx_prn_device_common;
72     /* Additional state for P*M devices */
73     char magic;			/* n for "Pn" */
74     char comment[MAX_COMMENT + 1];	/* comment for head of file */
75     byte is_raw;		/* 1 if raw format, 0 if plain */
76     byte optimize;		/* 1 if optimization OK, 0 if not */
77     byte uses_color;		/* 0 if image is black and white, */
78 				/* 1 if gray (PGM or PPM only), */
79 				/* 2 or 3 if colored (PPM only) */
80     bool UsePlanarBuffer;	/* 0 if chunky buffer, 1 if planar */
81     dev_proc_copy_alpha((*save_copy_alpha));
82     dev_proc_begin_typed_image((*save_begin_typed_image));
83 };
84 typedef struct gx_device_pbm_s gx_device_pbm;
85 
86 /* ------ The device descriptors ------ */
87 
88 /*
89  * Default X and Y resolution.
90  */
91 #define X_DPI 72
92 #define Y_DPI 72
93 
94 /* Macro for generating P*M device descriptors. */
95 #define pbm_prn_device(procs, dev_name, magic, is_raw, num_comp, depth, max_gray, max_rgb, optimize, x_dpi, y_dpi, print_page)\
96 {	prn_device_body(gx_device_pbm, procs, dev_name,\
97 	  DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, x_dpi, y_dpi,\
98 	  0, 0, 0, 0,\
99 	  num_comp, depth, max_gray, max_rgb, max_gray + 1, max_rgb + 1,\
100 	  print_page),\
101 	magic,\
102 	 { 0 },\
103 	is_raw,\
104 	optimize,\
105 	0, 0, 0\
106 }
107 
108 /* For all but PBM, we need our own color mapping and alpha procedures. */
109 private dev_proc_map_rgb_color(pgm_map_rgb_color);
110 private dev_proc_map_rgb_color(ppm_map_rgb_color);
111 private dev_proc_map_color_rgb(pgm_map_color_rgb);
112 private dev_proc_map_color_rgb(ppm_map_color_rgb);
113 private dev_proc_map_cmyk_color(pkm_map_cmyk_color);
114 private dev_proc_map_color_rgb(pkm_map_color_rgb);
115 private dev_proc_get_params(ppm_get_params);
116 private dev_proc_put_params(ppm_put_params);
117 private dev_proc_copy_alpha(pnm_copy_alpha);
118 private dev_proc_begin_typed_image(pnm_begin_typed_image);
119 
120 /* We need to initialize uses_color when opening the device, */
121 /* and after each showpage. */
122 private dev_proc_open_device(ppm_open);
123 private dev_proc_output_page(ppm_output_page);
124 
125 /* And of course we need our own print-page routines. */
126 private dev_proc_print_page(pbm_print_page);
127 private dev_proc_print_page(pgm_print_page);
128 private dev_proc_print_page(ppm_print_page);
129 private dev_proc_print_page(pkm_print_page);
130 private dev_proc_print_page(psm_print_page);
131 private dev_proc_print_page(psm_print_page);
132 private dev_proc_print_page(pam_print_page);
133 
134 private int pam_print_row(gx_device_printer * pdev, byte * data, int depth,
135 	       FILE * pstream);
136 private int pam_print_page(gx_device_printer * pdev, FILE * pstream);
137 
138 /* The device procedures */
139 
140 /* See gdevprn.h for the template for the following. */
141 #define pgpm_procs(p_open, p_get_params, p_map_rgb_color, p_map_color_rgb, p_map_cmyk_color) {\
142 	p_open, NULL, NULL, ppm_output_page, gdev_prn_close,\
143 	p_map_rgb_color, p_map_color_rgb, NULL, NULL, NULL, NULL, NULL, NULL,\
144 	p_get_params, ppm_put_params,\
145 	p_map_cmyk_color, NULL, NULL, NULL, gx_page_device_get_page_device\
146 }
147 
148 private const gx_device_procs pbm_procs =
149     pgpm_procs(gdev_prn_open, gdev_prn_get_params,
150 	       gdev_prn_map_rgb_color, gdev_prn_map_color_rgb, NULL);
151 private const gx_device_procs pgm_procs =
152     pgpm_procs(ppm_open, gdev_prn_get_params,
153 	       pgm_map_rgb_color, pgm_map_color_rgb, NULL);
154 private const gx_device_procs ppm_procs =
155     pgpm_procs(ppm_open, ppm_get_params,
156 	       gx_default_rgb_map_rgb_color, ppm_map_color_rgb, NULL);
157 private const gx_device_procs pnm_procs =
158     pgpm_procs(ppm_open, ppm_get_params,
159 	       ppm_map_rgb_color, ppm_map_color_rgb, NULL);
160 private const gx_device_procs pkm_procs =
161     pgpm_procs(ppm_open, ppm_get_params,
162 	       NULL, cmyk_1bit_map_color_rgb, cmyk_1bit_map_cmyk_color);
163 private const gx_device_procs pam_procs =
164     pgpm_procs(ppm_open, ppm_get_params,
165 	       NULL, cmyk_8bit_map_color_rgb, cmyk_8bit_map_cmyk_color);
166 
167 /* The device descriptors themselves */
168 const gx_device_pbm gs_pbm_device =
169 pbm_prn_device(pbm_procs, "pbm", '1', 0, 1, 1, 1, 0, 0,
170 	       X_DPI, Y_DPI, pbm_print_page);
171 const gx_device_pbm gs_pbmraw_device =
172 pbm_prn_device(pbm_procs, "pbmraw", '4', 1, 1, 1, 1, 1, 0,
173 	       X_DPI, Y_DPI, pbm_print_page);
174 const gx_device_pbm gs_pgm_device =
175 pbm_prn_device(pgm_procs, "pgm", '2', 0, 1, 8, 255, 0, 0,
176 	       X_DPI, Y_DPI, pgm_print_page);
177 const gx_device_pbm gs_pgmraw_device =
178 pbm_prn_device(pgm_procs, "pgmraw", '5', 1, 1, 8, 255, 0, 0,
179 	       X_DPI, Y_DPI, pgm_print_page);
180 const gx_device_pbm gs_pgnm_device =
181 pbm_prn_device(pgm_procs, "pgnm", '2', 0, 1, 8, 255, 0, 1,
182 	       X_DPI, Y_DPI, pgm_print_page);
183 const gx_device_pbm gs_pgnmraw_device =
184 pbm_prn_device(pgm_procs, "pgnmraw", '5', 1, 1, 8, 255, 0, 1,
185 	       X_DPI, Y_DPI, pgm_print_page);
186 const gx_device_pbm gs_ppm_device =
187 pbm_prn_device(ppm_procs, "ppm", '3', 0, 3, 24, 255, 255, 0,
188 	       X_DPI, Y_DPI, ppm_print_page);
189 const gx_device_pbm gs_ppmraw_device =
190 pbm_prn_device(ppm_procs, "ppmraw", '6', 1, 3, 24, 255, 255, 0,
191 	       X_DPI, Y_DPI, ppm_print_page);
192 const gx_device_pbm gs_pnm_device =
193 pbm_prn_device(pnm_procs, "pnm", '3', 0, 3, 24, 255, 255, 1,
194 	       X_DPI, Y_DPI, ppm_print_page);
195 const gx_device_pbm gs_pnmraw_device =
196 pbm_prn_device(pnm_procs, "pnmraw", '6', 1, 3, 24, 255, 255, 1,
197 	       X_DPI, Y_DPI, ppm_print_page);
198 const gx_device_pbm gs_pkm_device =
199 pbm_prn_device(pkm_procs, "pkm", '3', 0, 4, 4, 1, 1, 0,
200 	       X_DPI, Y_DPI, pkm_print_page);
201 const gx_device_pbm gs_pkmraw_device =
202 pbm_prn_device(pkm_procs, "pkmraw", '6', 1, 4, 4, 1, 1, 0,
203 	       X_DPI, Y_DPI, pkm_print_page);
204 const gx_device_pbm gs_pksm_device =
205 pbm_prn_device(pkm_procs, "pksm", '1', 0, 4, 4, 1, 1, 0,
206 	       X_DPI, Y_DPI, psm_print_page);
207 const gx_device_pbm gs_pksmraw_device =
208 pbm_prn_device(pkm_procs, "pksmraw", '4', 1, 4, 4, 1, 1, 0,
209 	       X_DPI, Y_DPI, psm_print_page);
210 const gx_device_pbm gs_pam_device =
211 pbm_prn_device(pam_procs, "pam", '7', 1, 4, 32, 255, 255, 0,
212 	       X_DPI, Y_DPI, pam_print_page);
213 
214 /* Plan 9 bitmaps default to 100 dpi. */
215 const gx_device_pbm gs_plan9bm_device =
216 pbm_prn_device(pbm_procs, "plan9bm", '9', 1, 1, 1, 1, 1, 1,
217 	       100, 100, pbm_print_page);
218 
219 /* ------ Initialization ------ */
220 
221 /* Set the copy_alpha and color mapping procedures if necessary. */
222 private void
ppm_set_dev_procs(gx_device * pdev)223 ppm_set_dev_procs(gx_device * pdev)
224 {
225     gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
226 
227     if (dev_proc(pdev, copy_alpha) != pnm_copy_alpha) {
228 	bdev->save_copy_alpha = dev_proc(pdev, copy_alpha);
229 	if (pdev->color_info.depth > 4)
230 	    set_dev_proc(pdev, copy_alpha, pnm_copy_alpha);
231     }
232     if (dev_proc(pdev, begin_typed_image) != pnm_begin_typed_image) {
233         bdev->save_begin_typed_image = dev_proc(pdev, begin_typed_image);
234 	set_dev_proc(pdev, begin_typed_image, pnm_begin_typed_image);
235     }
236     if (bdev->color_info.num_components == 4) {
237 	if (bdev->magic == 7) {
238 	    set_dev_proc(pdev, map_color_rgb, cmyk_8bit_map_color_rgb);
239 	    set_dev_proc(pdev, map_cmyk_color, cmyk_8bit_map_cmyk_color);
240 	} else if (bdev->color_info.depth == 4) {
241 	    set_dev_proc(pdev, map_color_rgb, cmyk_1bit_map_color_rgb);
242 	    set_dev_proc(pdev, map_cmyk_color, cmyk_1bit_map_cmyk_color);
243 	} else {
244 	    set_dev_proc(pdev, map_color_rgb, pkm_map_color_rgb);
245 	    set_dev_proc(pdev, map_cmyk_color, pkm_map_cmyk_color);
246 	}
247     }
248 }
249 
250 /*
251  * Define a special open procedure that changes create_buf_device to use
252  * a planar device.
253  */
254 
255 private int
ppm_open(gx_device * pdev)256 ppm_open(gx_device * pdev)
257 {
258     gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
259     int code = gdev_prn_open_planar(pdev, bdev->UsePlanarBuffer);
260 
261     if (code < 0)
262 	return code;
263     pdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN;
264     set_linear_color_bits_mask_shift(pdev);
265     bdev->uses_color = 0;
266     ppm_set_dev_procs(pdev);
267     return code;
268 }
269 
270 /* Print a page, and reset uses_color if this is a showpage. */
271 private int
ppm_output_page(gx_device * pdev,int num_copies,int flush)272 ppm_output_page(gx_device * pdev, int num_copies, int flush)
273 {
274     int code = gdev_prn_output_page(pdev, num_copies, flush);
275     gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
276 
277     if (code < 0)
278 	return code;
279     if (flush)
280 	bdev->uses_color = 0;
281     return code;
282 }
283 
284 /* ------ Color mapping routines ------ */
285 
286 /* Map an RGB color to a PGM gray value. */
287 /* Keep track of whether the image is black-and-white or gray. */
288 private gx_color_index
pgm_map_rgb_color(gx_device * pdev,const gx_color_value cv[])289 pgm_map_rgb_color(gx_device * pdev, const gx_color_value cv[])
290 {				/* We round the value rather than truncating it. */
291     gx_color_value gray;
292     /* TO_DO_DEVICEN  - Kludge to emulate pre DeviceN math errors */
293 #if 1
294     gx_color_value r, g, b;
295 
296     r = cv[0]; g = cv[0]; b = cv[0];
297     gray = ((r * (ulong) lum_red_weight) +
298      (g * (ulong) lum_green_weight) +
299      (b * (ulong) lum_blue_weight) +
300      (lum_all_weights / 2)) / lum_all_weights
301     * pdev->color_info.max_gray / gx_max_color_value;
302 #else	    /* Should be ... */
303     gray = cv[0] * pdev->color_info.max_gray / gx_max_color_value;
304 #endif
305 
306     if (!(gray == 0 || gray == pdev->color_info.max_gray)) {
307 	gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
308 
309 	bdev->uses_color = 1;
310     }
311     return gray;
312 }
313 
314 /* Map a PGM gray value back to an RGB color. */
315 private int
pgm_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value prgb[3])316 pgm_map_color_rgb(gx_device * dev, gx_color_index color,
317 		  gx_color_value prgb[3])
318 {
319     gx_color_value gray =
320     color * gx_max_color_value / dev->color_info.max_gray;
321 
322     prgb[0] = gray;
323     prgb[1] = gray;
324     prgb[2] = gray;
325     return 0;
326 }
327 
328 /*
329  * Pre gs8.00 version of RGB mapping for 24-bit true (RGB) color devices
330  * It is kept here for backwards comparibility since the gs8.00 version
331  * has changed in functionality.  The new one requires that the device be
332  * 'separable'.  This routine is logically separable but does not require
333  * the various color_info fields associated with separability (comp_shift,
334  * comp_bits, and comp_mask) be setup.
335  */
336 
337 private gx_color_index
gx_old_default_rgb_map_rgb_color(gx_device * dev,gx_color_value r,gx_color_value g,gx_color_value b)338 gx_old_default_rgb_map_rgb_color(gx_device * dev,
339 		       gx_color_value r, gx_color_value g, gx_color_value b)
340 {
341     if (dev->color_info.depth == 24)
342 	return gx_color_value_to_byte(b) +
343 	    ((uint) gx_color_value_to_byte(g) << 8) +
344 	    ((ulong) gx_color_value_to_byte(r) << 16);
345     else {
346 	int bpc = dev->color_info.depth / 3;
347 	int drop = sizeof(gx_color_value) * 8 - bpc;
348 
349 	return ((((r >> drop) << bpc) + (g >> drop)) << bpc) + (b >> drop);
350     }
351 }
352 
353 /* Map an RGB color to a PPM color tuple. */
354 /* Keep track of whether the image is black-and-white, gray, or colored. */
355 private gx_color_index
ppm_map_rgb_color(gx_device * pdev,const gx_color_value cv[])356 ppm_map_rgb_color(gx_device * pdev, const gx_color_value cv[])
357 {
358     gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
359     gx_color_index color =
360 	    gx_old_default_rgb_map_rgb_color(pdev, cv[0], cv[1], cv[2]);
361     uint bpc = pdev->color_info.depth / 3;
362     gx_color_index mask =
363 	((gx_color_index)1 << (pdev->color_info.depth - bpc)) - 1;
364     if (!(((color >> bpc) ^ color) & mask)) { /* gray shade */
365 	if (color != 0 && (~color & mask))
366 	    bdev->uses_color |= 1;
367     } else			/* color */
368 	bdev->uses_color = 2;
369     return color;
370 }
371 
372 /* Map a PPM color tuple back to an RGB color. */
373 private int
ppm_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value prgb[3])374 ppm_map_color_rgb(gx_device * dev, gx_color_index color,
375 		  gx_color_value prgb[3])
376 {
377     uint bitspercolor = dev->color_info.depth / 3;
378     uint colormask = (1 << bitspercolor) - 1;
379     uint max_rgb = dev->color_info.max_color;
380 
381     prgb[0] = ((color >> (bitspercolor * 2)) & colormask) *
382 	(ulong) gx_max_color_value / max_rgb;
383     prgb[1] = ((color >> bitspercolor) & colormask) *
384 	(ulong) gx_max_color_value / max_rgb;
385     prgb[2] = (color & colormask) *
386 	(ulong) gx_max_color_value / max_rgb;
387     return 0;
388 }
389 
390 /* Map a CMYK color to a pixel value. */
391 private gx_color_index
pkm_map_cmyk_color(gx_device * pdev,const gx_color_value cv[])392 pkm_map_cmyk_color(gx_device * pdev, const gx_color_value cv[])
393 {
394     uint bpc = pdev->color_info.depth >> 2;
395     uint max_value = pdev->color_info.max_color;
396     uint cc = cv[0] * max_value / gx_max_color_value;
397     uint mc = cv[1] * max_value / gx_max_color_value;
398     uint yc = cv[2] * max_value / gx_max_color_value;
399     uint kc = cv[3] * max_value / gx_max_color_value;
400     gx_color_index color =
401 	(((((cc << bpc) + mc) << bpc) + yc) << bpc) + kc;
402 
403     return (color == gx_no_color_index ? color ^ 1 : color);
404 }
405 
406 /* Map a CMYK pixel value to RGB. */
407 private int
pkm_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value rgb[3])408 pkm_map_color_rgb(gx_device * dev, gx_color_index color, gx_color_value rgb[3])
409 {
410     int bpc = dev->color_info.depth >> 2;
411     gx_color_index cshift = color;
412     uint mask = (1 << bpc) - 1;
413     uint k = cshift & mask;
414     uint y = (cshift >>= bpc) & mask;
415     uint m = (cshift >>= bpc) & mask;
416     uint c = cshift >> bpc;
417     uint max_value = dev->color_info.max_color;
418     uint not_k = max_value - k;
419 
420 #define CVALUE(c)\
421   ((gx_color_value)((ulong)(c) * gx_max_color_value / max_value))
422 	/* We use our improved conversion rule.... */
423 	rgb[0] = CVALUE((max_value - c) * not_k / max_value);
424     rgb[1] = CVALUE((max_value - m) * not_k / max_value);
425     rgb[2] = CVALUE((max_value - y) * not_k / max_value);
426 #undef CVALUE
427     return 0;
428 }
429 
430 /* Augment get/put_params to add UsePlanarBuffer */
431 
432 private int
ppm_get_params(gx_device * pdev,gs_param_list * plist)433 ppm_get_params(gx_device * pdev, gs_param_list * plist)
434 {
435     gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
436 
437     return gdev_prn_get_params_planar(pdev, plist, &bdev->UsePlanarBuffer);
438 }
439 
440 private int
ppm_put_params(gx_device * pdev,gs_param_list * plist)441 ppm_put_params(gx_device * pdev, gs_param_list * plist)
442 {
443     gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
444     gx_device_color_info save_info;
445     int ncomps = pdev->color_info.num_components;
446     int bpc = pdev->color_info.depth / ncomps;
447     int ecode = 0;
448     int code;
449     long v;
450     const char *vname;
451 
452     save_info = pdev->color_info;
453     if ((code = param_read_long(plist, (vname = "GrayValues"), &v)) != 1 ||
454 	(code = param_read_long(plist, (vname = "RedValues"), &v)) != 1 ||
455 	(code = param_read_long(plist, (vname = "GreenValues"), &v)) != 1 ||
456 	(code = param_read_long(plist, (vname = "BlueValues"), &v)) != 1
457 	) {
458 	if (code < 0)
459 	    ecode = code;
460 	else if (v < 2 || v > (bdev->is_raw || ncomps > 1 ? 256 : 65536L))
461 	    param_signal_error(plist, vname,
462 			       ecode = gs_error_rangecheck);
463 	else if (v == 2)
464 	    bpc = 1;
465 	else if (v <= 4)
466 	    bpc = 2;
467 	else if (v <= 16)
468 	    bpc = 4;
469 	else if (v <= 32 && ncomps == 3)
470 	    bpc = 5;
471 	else if (v <= 256)
472 	    bpc = 8;
473 	else
474 	    bpc = 16;
475 	if (ecode >= 0) {
476 	    static const byte depths[4][16] =
477 	    {
478 		{1, 2, 0, 4, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 16},
479 		{0},
480 		{4, 8, 0, 16, 16, 0, 0, 24},
481 		{4, 8, 0, 16, 0, 0, 0, 32},
482 	    };
483 
484 	    pdev->color_info.depth = depths[ncomps - 1][bpc - 1];
485 	    pdev->color_info.max_gray = pdev->color_info.max_color =
486 		(pdev->color_info.dither_grays =
487 		 pdev->color_info.dither_colors = (int)v) - 1;
488 	}
489     }
490     if ((code = ecode) < 0 ||
491 	(code = gdev_prn_put_params_planar(pdev, plist, &bdev->UsePlanarBuffer)) < 0
492 	)
493 	pdev->color_info = save_info;
494     ppm_set_dev_procs(pdev);
495     return code;
496 }
497 
498 /* Copy an alpha map, noting whether we may generate some non-black/white */
499 /* colors through blending. */
500 private int
pnm_copy_alpha(gx_device * pdev,const byte * data,int data_x,int raster,gx_bitmap_id id,int x,int y,int width,int height,gx_color_index color,int depth)501 pnm_copy_alpha(gx_device * pdev, const byte * data, int data_x,
502 	   int raster, gx_bitmap_id id, int x, int y, int width, int height,
503 	       gx_color_index color, int depth)
504 {
505     gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
506 
507     if (pdev->color_info.depth < 24 ||
508 	(color >> 8) == (color & 0xffff)
509 	)
510 	bdev->uses_color |= 1;
511     else
512 	bdev->uses_color |= 2;
513     return (*bdev->save_copy_alpha) (pdev, data, data_x, raster, id,
514 				     x, y, width, height, color, depth);
515 }
516 
517 /* Begin processing an image, noting whether we may generate some */
518 /* non-black/white colors in the process. */
519 private int
pnm_begin_typed_image(gx_device * dev,const gs_imager_state * pis,const gs_matrix * pmat,const gs_image_common_t * pim,const gs_int_rect * prect,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath,gs_memory_t * memory,gx_image_enum_common_t ** pinfo)520 pnm_begin_typed_image(gx_device *dev,
521                       const gs_imager_state *pis, const gs_matrix *pmat,
522                       const gs_image_common_t *pim, const gs_int_rect *prect,
523                       const gx_drawing_color *pdcolor,
524 		      const gx_clip_path *pcpath,
525                       gs_memory_t *memory, gx_image_enum_common_t **pinfo)
526 {
527     gx_device_pbm * const bdev = (gx_device_pbm *)dev;
528 
529     /* Conservatively guesses whether this operation causes color usage
530        that might not be otherwise captured by ppm_map_color_rgb. */
531     if (pim && pim->type) {
532 	switch (pim->type->index) {
533 	case 1: case 3: case 4: {
534 	    /* Use colorspace to handle image types 1,3,4 */
535 	    const gs_pixel_image_t *pim1 = (const gs_pixel_image_t *)pim;
536 
537 	    if (pim1->ColorSpace) {
538 		if (gs_color_space_get_index(pim1->ColorSpace) == gs_color_space_index_DeviceGray) {
539 		    if (pim1->BitsPerComponent > 1)
540 			bdev->uses_color |= 1;
541 		} else
542 		    bdev->uses_color = 2;
543 	    }
544 	    break;
545 	}
546 	default:
547 	    /* Conservatively handles other image types */
548 	    bdev->uses_color = 2;
549 	}
550     }
551     /* Forward to saved routine */
552     return (*bdev->save_begin_typed_image)(dev, pis, pmat, pim, prect,
553 					   pdcolor, pcpath, memory, pinfo);
554 }
555 
556 
557 /* ------ Internal routines ------ */
558 
559 /* Print a page using a given row printing routine. */
560 private int
pbm_print_page_loop(gx_device_printer * pdev,char magic,FILE * pstream,int (* row_proc)(gx_device_printer *,byte *,int,FILE *))561 pbm_print_page_loop(gx_device_printer * pdev, char magic, FILE * pstream,
562 	     int (*row_proc) (gx_device_printer *, byte *, int, FILE *))
563 {
564     gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
565     uint raster = gdev_prn_raster(pdev);
566     byte *data = gs_alloc_bytes(pdev->memory, raster, "pbm_print_page_loop");
567     int lnum = 0;
568     int code = 0;
569 
570     if (data == 0)
571 	return_error(gs_error_VMerror);
572     /* Hack.  This should be done in the callers.  */
573     if (magic == '9')
574 	fprintf(pstream, "%11d %11d %11d %11d %11d ",
575 		0, 0, 0, pdev->width, pdev->height);
576     else if (magic == '7') {
577 	int ncomps = pdev->color_info.num_components;
578 	fprintf(pstream, "P%c\n", magic);
579 	fprintf(pstream, "WIDTH %d\n", pdev->width);
580 	fprintf(pstream, "HEIGHT %d\n", pdev->height);
581 	fprintf(pstream, "DEPTH %d\n", ncomps);
582 	fprintf(pstream, "MAXVAL %d\n", pdev->color_info.max_gray);
583 	fprintf(pstream, "TUPLTYPE %s\n",
584 	    (ncomps == 4) ? "CMYK" :
585 	    ((ncomps == 3) ? "RGB" : "GRAYSCALE"));
586 	if (bdev->comment[0])
587 	    fprintf(pstream, "# %s\n", bdev->comment);
588 	else
589 	    fprintf(pstream, "# Image generated by %s (device=%s)\n",
590 		    gs_product, pdev->dname);
591 	fprintf(pstream, "ENDHDR\n");
592     }
593     else {
594 	fprintf(pstream, "P%c\n", magic);
595 	if (bdev->comment[0])
596 	    fprintf(pstream, "# %s\n", bdev->comment);
597 	else
598 	    fprintf(pstream, "# Image generated by %s (device=%s)\n",
599 		    gs_product, pdev->dname);
600 	fprintf(pstream, "%d %d\n", pdev->width, pdev->height);
601     }
602     switch (magic) {
603 	case '1':		/* pbm */
604 	case '4':		/* pbmraw */
605 	case '7':		/* pam */
606 	case '9':		/* plan9bm */
607 	    break;
608 	default:
609 	    fprintf(pstream, "%d\n", pdev->color_info.max_gray);
610     }
611     for (; lnum < pdev->height; lnum++) {
612 	byte *row;
613 
614 	code = gdev_prn_get_bits(pdev, lnum, data, &row);
615 	if (code < 0)
616 	    break;
617 	code = (*row_proc) (pdev, row, pdev->color_info.depth, pstream);
618 	if (code < 0)
619 	    break;
620     }
621     gs_free_object(pdev->memory, data, "pbm_print_page_loop");
622     return (code < 0 ? code : 0);
623 }
624 
625 /* ------ Individual page printing routines ------ */
626 
627 /* Print a monobit page. */
628 private int
pbm_print_row(gx_device_printer * pdev,byte * data,int depth,FILE * pstream)629 pbm_print_row(gx_device_printer * pdev, byte * data, int depth,
630 	      FILE * pstream)
631 {
632     gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
633 
634     if (bdev->is_raw)
635 	fwrite(data, 1, (pdev->width + 7) >> 3, pstream);
636     else {
637 	byte *bp;
638 	uint x, mask;
639 
640 	for (bp = data, x = 0, mask = 0x80; x < pdev->width;) {
641 	    putc((*bp & mask ? '1' : '0'), pstream);
642 	    if (++x == pdev->width || !(x & 63))
643 		putc('\n', pstream);
644 	    if ((mask >>= 1) == 0)
645 		bp++, mask = 0x80;
646 	}
647     }
648     return 0;
649 }
650 private int
pbm_print_page(gx_device_printer * pdev,FILE * pstream)651 pbm_print_page(gx_device_printer * pdev, FILE * pstream)
652 {
653     gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
654 
655     return pbm_print_page_loop(pdev, bdev->magic, pstream, pbm_print_row);
656 }
657 
658 /* Print a gray-mapped page. */
659 private int
pgm_print_row(gx_device_printer * pdev,byte * data,int depth,FILE * pstream)660 pgm_print_row(gx_device_printer * pdev, byte * data, int depth,
661 	      FILE * pstream)
662 {				/* Note that bpp <= 8 for raw format, bpp <= 16 for plain. */
663     gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
664     uint mask = (1 << depth) - 1;
665     /*
666      * If we're writing planes for a CMYK device, we have 0 = white,
667      * mask = black, which is the opposite of the pgm convention.
668      */
669     uint invert = (pdev->color_info.polarity == GX_CINFO_POLARITY_SUBTRACTIVE);
670     byte *bp;
671     uint x;
672     int shift;
673 
674     if (bdev->is_raw && depth == 8) {
675 	if (invert) {
676 	    for (bp = data, x = 0; x < pdev->width; bp++, x++)
677 		putc((byte)~*bp, pstream);
678 	} else
679 	    fwrite(data, 1, pdev->width, pstream);
680     } else
681 	for (bp = data, x = 0, shift = 8 - depth; x < pdev->width;) {
682 	    uint pixel;
683 
684 	    if (shift < 0) {	/* bpp = 16 */
685 		pixel = ((uint) * bp << 8) + bp[1];
686 		bp += 2;
687 	    } else {
688 		pixel = (*bp >> shift) & mask;
689 		if ((shift -= depth) < 0)
690 		    bp++, shift += 8;
691 	    }
692 	    ++x;
693 	    pixel ^= invert;
694 	    if (bdev->is_raw)
695 		putc(pixel, pstream);
696 	    else
697 		fprintf(pstream, "%d%c", pixel,
698 			(x == pdev->width || !(x & 15) ? '\n' : ' '));
699 	}
700     return 0;
701 }
702 private int
pxm_pbm_print_row(gx_device_printer * pdev,byte * data,int depth,FILE * pstream)703 pxm_pbm_print_row(gx_device_printer * pdev, byte * data, int depth,
704 		  FILE * pstream)
705 {				/* Compress a PGM or PPM row to a PBM row. */
706     /* This doesn't have to be very fast. */
707     /* Note that we have to invert the data as well. */
708     int delta = (depth + 7) >> 3;
709     byte *src = data + delta - 1;	/* always big-endian */
710     byte *dest = data;
711     int x;
712     byte out_mask = 0x80;
713     byte out = 0;
714 
715     if (depth >= 8) {		/* One or more bytes per source pixel. */
716 	for (x = 0; x < pdev->width; x++, src += delta) {
717 	    if (!(*src & 1))
718 		out |= out_mask;
719 	    out_mask >>= 1;
720 	    if (!out_mask)
721 		out_mask = 0x80,
722 		    *dest++ = out,
723 		    out = 0;
724 	}
725     } else {			/* Multiple source pixels per byte. */
726 	byte in_mask = 0x100 >> depth;
727 
728 	for (x = 0; x < pdev->width; x++) {
729 	    if (!(*src & in_mask))
730 		out |= out_mask;
731 	    in_mask >>= depth;
732 	    if (!in_mask)
733 		in_mask = 0x100 >> depth,
734 		    src++;
735 	    out_mask >>= 1;
736 	    if (!out_mask)
737 		out_mask = 0x80,
738 		    *dest++ = out,
739 		    out = 0;
740 	}
741     }
742     if (out_mask != 0x80)
743 	*dest = out;
744     return pbm_print_row(pdev, data, 1, pstream);
745 }
746 private int
pgm_print_page(gx_device_printer * pdev,FILE * pstream)747 pgm_print_page(gx_device_printer * pdev, FILE * pstream)
748 {
749     gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
750 
751     return (bdev->uses_color == 0 && bdev->optimize ?
752 	    pbm_print_page_loop(pdev, (char)((int)bdev->magic - 1), pstream,
753 				pxm_pbm_print_row) :
754 	    pbm_print_page_loop(pdev, bdev->magic, pstream,
755 				pgm_print_row));
756 }
757 
758 /* Print a color-mapped page. */
759 private int
ppgm_print_row(gx_device_printer * pdev,byte * data,int depth,FILE * pstream,bool color)760 ppgm_print_row(gx_device_printer * pdev, byte * data, int depth,
761 	       FILE * pstream, bool color)
762 {				/* If color=false, write only one value per pixel; */
763     /* if color=true, write 3 values per pixel. */
764     /* Note that depth <= 24 for raw format, depth <= 32 for plain. */
765     gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
766     uint bpe = depth / 3;	/* bits per r/g/b element */
767     uint mask = (1 << bpe) - 1;
768     byte *bp;
769     uint x;
770     uint eol_mask = (color ? 7 : 15);
771     int shift;
772 
773     if (bdev->is_raw && depth == 24 && color)
774 	fwrite(data, 1, pdev->width * (depth / 8), pstream);
775     else
776 	for (bp = data, x = 0, shift = 8 - depth; x < pdev->width;) {
777 	    bits32 pixel = 0;
778 	    uint r, g, b;
779 
780 	    switch (depth >> 3) {
781 		case 4:
782 		    pixel = (bits32) * bp << 24;
783 		    bp++;
784 		    /* falls through */
785 		case 3:
786 		    pixel += (bits32) * bp << 16;
787 		    bp++;
788 		    /* falls through */
789 		case 2:
790 		    pixel += (uint) * bp << 8;
791 		    bp++;
792 		    /* falls through */
793 		case 1:
794 		    pixel += *bp;
795 		    bp++;
796 		    break;
797 		case 0:	/* bpp == 4, bpe == 1 */
798 		    pixel = *bp >> shift;
799 		    if ((shift -= depth) < 0)
800 			bp++, shift += 8;
801 		    break;
802 	    }
803 	    ++x;
804 	    b = pixel & mask;
805 	    pixel >>= bpe;
806 	    g = pixel & mask;
807 	    pixel >>= bpe;
808 	    r = pixel & mask;
809 	    if (bdev->is_raw) {
810 		if (color) {
811 		    putc(r, pstream);
812 		    putc(g, pstream);
813 		}
814 		putc(b, pstream);
815 	    } else {
816 		if (color)
817 		    fprintf(pstream, "%d %d ", r, g);
818 		fprintf(pstream, "%d%c", b,
819 			(x == pdev->width || !(x & eol_mask) ?
820 			 '\n' : ' '));
821 	    }
822 	}
823     return 0;
824 }
825 private int
ppm_print_row(gx_device_printer * pdev,byte * data,int depth,FILE * pstream)826 ppm_print_row(gx_device_printer * pdev, byte * data, int depth,
827 	      FILE * pstream)
828 {
829     return ppgm_print_row(pdev, data, depth, pstream, true);
830 }
831 private int
ppm_pgm_print_row(gx_device_printer * pdev,byte * data,int depth,FILE * pstream)832 ppm_pgm_print_row(gx_device_printer * pdev, byte * data, int depth,
833 		  FILE * pstream)
834 {
835     return ppgm_print_row(pdev, data, depth, pstream, false);
836 }
837 private int
ppm_print_page(gx_device_printer * pdev,FILE * pstream)838 ppm_print_page(gx_device_printer * pdev, FILE * pstream)
839 {
840     gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
841 
842     return (bdev->uses_color >= 2 || !bdev->optimize ?
843 	    pbm_print_page_loop(pdev, bdev->magic, pstream,
844 				ppm_print_row) :
845 	    bdev->uses_color == 1 ?
846 	    pbm_print_page_loop(pdev, (char)((int)bdev->magic - 1), pstream,
847 				ppm_pgm_print_row) :
848 	    pbm_print_page_loop(pdev, (char)((int)bdev->magic - 2), pstream,
849 				pxm_pbm_print_row));
850 }
851 
852 private int
pam_print_row(gx_device_printer * pdev,byte * data,int depth,FILE * pstream)853 pam_print_row(gx_device_printer * pdev, byte * data, int depth,
854 	       FILE * pstream)
855 {
856     if (depth == 32)
857 	fwrite(data, 1, pdev->width * (depth / 8), pstream);
858     return 0;
859 }
860 
861 private int
pam_print_page(gx_device_printer * pdev,FILE * pstream)862 pam_print_page(gx_device_printer * pdev, FILE * pstream)
863 {
864     gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
865 
866     return pbm_print_page_loop(pdev, bdev->magic, pstream,
867 				pam_print_row);
868 }
869 
870 /* Print a faux CMYK page. */
871 /* Print a row where each pixel occupies 4 bits (depth == 4). */
872 /* In this case, we also know pdev->color_info.max_color == 1. */
873 private int
pkm_print_row_4(gx_device_printer * pdev,byte * data,int depth,FILE * pstream)874 pkm_print_row_4(gx_device_printer * pdev, byte * data, int depth,
875 		FILE * pstream)
876 {
877     gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
878     byte *bp;
879     uint x;
880     byte rv[16], gv[16], bv[16], i;
881 
882     /* Precompute all the possible pixel values. */
883     for (i = 0; i < 16; ++i) {
884 	gx_color_value rgb[3];
885 
886 	cmyk_1bit_map_color_rgb((gx_device *)pdev, (gx_color_index)i, rgb);
887 	rv[i] = rgb[0] / gx_max_color_value;
888 	gv[i] = rgb[1] / gx_max_color_value;
889 	bv[i] = rgb[2] / gx_max_color_value;
890     }
891     /*
892      * Contrary to what the documentation implies, gcc compiles putc
893      * as a procedure call.  This is ridiculous, but since we can't
894      * change it, we buffer groups of pixels ourselves and use fwrite.
895      */
896     if (bdev->is_raw) {
897 	for (bp = data, x = 0; x < pdev->width;) {
898 	    byte raw[50 * 3];	/* 50 is arbitrary, but must be even */
899 	    int end = min(x + sizeof(raw) / 3, pdev->width);
900 	    byte *outp = raw;
901 
902 	    for (; x < end; bp++, outp += 6, x += 2) {
903 		uint b = *bp;
904 		uint pixel = b >> 4;
905 
906 		outp[0] = rv[pixel], outp[1] = gv[pixel], outp[2] = bv[pixel];
907 		pixel = b & 0xf;
908 		outp[3] = rv[pixel], outp[4] = gv[pixel], outp[5] = bv[pixel];
909 	    }
910 	    /* x might overshoot the width by 1 pixel. */
911 	    if (x > end)
912 		outp -= 3;
913 	    fwrite(raw, 1, outp - raw, pstream);
914 	}
915     } else {
916 	int shift;
917 
918 	for (bp = data, x = 0, shift = 4; x < pdev->width;) {
919 	    int pixel = (*bp >> shift) & 0xf;
920 
921 	    shift ^= 4;
922 	    bp += shift >> 2;
923 	    ++x;
924 	    fprintf(pstream, "%d %d %d%c", rv[pixel], gv[pixel], bv[pixel],
925 		    (x == pdev->width || !(x & 7) ?
926 		     '\n' : ' '));
927 	}
928     }
929     return 0;
930 }
931 /* Print a row where each pixel occupies 1 or more bytes (depth >= 8). */
932 private int
pkm_print_row(gx_device_printer * pdev,byte * data,int depth,FILE * pstream)933 pkm_print_row(gx_device_printer * pdev, byte * data, int depth,
934 	      FILE * pstream)
935 {
936     gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
937     byte *bp;
938     uint x;
939     ulong max_value = pdev->color_info.max_color;
940 
941     for (bp = data, x = 0; x < pdev->width;) {
942 	bits32 pixel = 0;
943 	gx_color_value rgb[3];
944 	uint r, g, b;
945 
946 	switch (depth >> 3) {
947 	    case 4:
948 		pixel = (bits32) * bp << 24;
949 		bp++;
950 		/* falls through */
951 	    case 3:
952 		pixel += (bits32) * bp << 16;
953 		bp++;
954 		/* falls through */
955 	    case 2:
956 		pixel += (uint) * bp << 8;
957 		bp++;
958 		/* falls through */
959 	    case 1:
960 		pixel += *bp;
961 		bp++;
962 	}
963 	++x;
964 	pkm_map_color_rgb((gx_device *) pdev, pixel, rgb);
965 	r = rgb[0] * max_value / gx_max_color_value;
966 	g = rgb[1] * max_value / gx_max_color_value;
967 	b = rgb[2] * max_value / gx_max_color_value;
968 	if (bdev->is_raw) {
969 	    putc(r, pstream);
970 	    putc(g, pstream);
971 	    putc(b, pstream);
972 	} else {
973 	    fprintf(pstream, "%d %d %d%c", r, g, b,
974 		    (x == pdev->width || !(x & 7) ?
975 		     '\n' : ' '));
976 	}
977     }
978     return 0;
979 }
980 private int
pkm_print_page(gx_device_printer * pdev,FILE * pstream)981 pkm_print_page(gx_device_printer * pdev, FILE * pstream)
982 {
983     gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
984 
985     return pbm_print_page_loop(pdev, bdev->magic, pstream,
986 			       (pdev->color_info.depth < 8 ?
987 				pkm_print_row_4 :
988 				pkm_print_row));
989 }
990 
991 /* Print individual separations on a single file. */
992 private int
psm_print_page(gx_device_printer * pdev,FILE * pstream)993 psm_print_page(gx_device_printer * pdev, FILE * pstream)
994 {
995     gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
996     /*
997      * Allocate a large enough buffer for full pixels, on the theory that we
998      * don't know how many bits will be allocated to each component.  (This
999      * is for didactic purposes only: we know perfectly well that each
1000      * component will have 1/N of the bits.)
1001      */
1002     uint max_raster = bitmap_raster(pdev->width * pdev->color_info.depth);
1003     byte *data = gs_alloc_bytes(pdev->memory, max_raster, "pksm_print_page");
1004     int code = 0;
1005     int plane;
1006 
1007     if (data == 0)
1008 	return_error(gs_error_VMerror);
1009     for (plane = 0; plane < pdev->color_info.num_components; ++plane) {
1010 	int lnum, band_end;
1011 	/*
1012 	 * The following initialization is unnecessary: lnum == band_end on
1013 	 * the first pass through the loop below, so marked will always be
1014 	 * set before it is used.  We initialize marked solely to suppress
1015 	 * bogus warning messages from certain compilers.
1016 	 */
1017 	gx_color_index marked = 0;
1018 	gx_render_plane_t render_plane;
1019 	int plane_depth;
1020 	int plane_shift;
1021 	gx_color_index plane_mask;
1022 	int raster;
1023 
1024 	gx_render_plane_init(&render_plane, (gx_device *)pdev, plane);
1025 	plane_depth = render_plane.depth;
1026 	plane_shift = render_plane.shift;
1027 	plane_mask = (1 << plane_depth) - 1;
1028 	raster = bitmap_raster(pdev->width * plane_depth);
1029 	fprintf(pstream, "P%c\n", bdev->magic + (plane_depth > 1));
1030 	if (bdev->comment[0])
1031 	    fprintf(pstream, "# %s\n", bdev->comment);
1032 	else
1033 	    fprintf(pstream, "# Image generated by %s (device=%s)\n",
1034 		    gs_product, pdev->dname);
1035 	fprintf(pstream, "%d %d\n", pdev->width, pdev->height);
1036 	if (plane_depth > 1)
1037 	    fprintf(pstream, "%d\n", pdev->color_info.max_gray);
1038 	for (lnum = band_end = 0; lnum < pdev->height; lnum++) {
1039 	    byte *row;
1040 
1041 	    if (lnum == band_end) {
1042 		gx_colors_used_t colors_used;
1043 		int band_start;
1044 		int band_height =
1045 		    gdev_prn_colors_used((gx_device *)pdev, lnum, 1,
1046 					 &colors_used, &band_start);
1047 
1048 		band_end = band_start + band_height;
1049 		marked = colors_used.or & (plane_mask << plane_shift);
1050 		if (!marked)
1051 		    memset(data, 0, raster);
1052 #ifdef DEBUG
1053 		if (plane == 0)
1054 		    if_debug4(':',
1055 			      "[:]%4d - %4d mask = 0x%lx, slow_rop = %d\n",
1056 			      lnum, band_end - 1, (ulong)colors_used.or,
1057 			      colors_used.slow_rop);
1058 #endif
1059 	    }
1060 	    if (marked) {
1061 		gx_render_plane_t render_plane;
1062 		uint actual_raster;
1063 
1064 		render_plane.index = plane;
1065 		code = gdev_prn_get_lines(pdev, lnum, 1, data, raster,
1066 					  &row, &actual_raster,
1067 					  &render_plane);
1068 		if (code < 0)
1069 		    break;
1070 	    } else
1071 		row = data;
1072 	    code =
1073 		(plane_depth == 1 ?
1074 		 pbm_print_row(pdev, row, plane_depth, pstream) :
1075 		 pgm_print_row(pdev, row, plane_depth, pstream));
1076 	    if (code < 0)
1077 		break;
1078 	}
1079     }
1080     gs_free_object(pdev->memory, data, "pksm_print_page");
1081     return (code < 0 ? code : 0);
1082 }
1083