xref: /plan9/sys/src/cmd/gs/src/gdevclj.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1998, 1999 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: gdevclj.c,v 1.4 2002/02/21 22:24:51 giles Exp $ */
18 /*
19  * H-P Color LaserJet 5/5M device; based on the PaintJet.
20  */
21 #include "math_.h"
22 #include "gx.h"
23 #include "gsparam.h"
24 #include "gdevprn.h"
25 #include "gdevpcl.h"
26 
27 typedef struct gx_device_clj_s gx_device_clj;
28 struct gx_device_clj_s {
29 	gx_device_common;
30 	gx_prn_device_common;
31 	bool rotated;
32 };
33 
34 #define pclj ((gx_device_clj *)pdev)
35 
36 /*
37  * The HP Color LaserJet 5/5M provides a rather unexpected speed/performance
38  * tradeoff.
39  *
40  * When generating rasters, only the fixed (simple) color spaces provide
41  * reasonable performance (in this case, reasonable != good). However, in
42  * these modes, certain of the fully-saturated primary colors (cyan, blue,
43  * green, and red) are rendered differently as rasters as opposed to colored
44  * geometric objects. Hence, the color of the output will be other than what
45  * is expected.
46  *
47  * Alternatively, the direct color, 1-bit per pixel scheme can be used. This
48  * will produce the expected colors, but performance will deteriorate
49  * significantly (observed printing time will be about 3 times longer than
50  * when using the simple color mode).
51  *
52  * Note that when using the latter mode to view output from the PCL
53  * interpreter, geometric objects and raster rendered with other than
54  * geometric color spaces will have the same appearance as if sent directly
55  * to the CLJ, but rasters generated from simple color spaces will have a
56  * different appearance. To make the latter rasters match in appearance, the
57  * faster printing mode must be used (in which the case the other objects
58  * will not have the same appearance).
59  */
60 #define USE_FAST_MODE
61 
62 /* X_DPI and Y_DPI must be the same */
63 #define X_DPI 300
64 #define Y_DPI 300
65 
66 /*
67  * Array of paper sizes, and the corresponding offsets.
68  */
69 typedef struct clj_paper_size_s {
70     uint        tag;                /* paper type tag */
71     int         orient;             /* logical page orientation to use */
72     float       width, height;      /* in pts; +- 5 pts */
73     gs_point    offsets;            /* offsets in the given orientation */
74 } clj_paper_size;
75 
76 /*
77  * The Color LaserJet prints page sizes up to 11.8" wide (A4 size) in
78  * long-edge-feed (landscape) orientation. Only executive, letter, and
79  * A4 size are supported for color, so we don't bother to list the others.
80  */
81 private const clj_paper_size    clj_paper_sizes[] = {
82     /* U.S. letter size comes first so it will be the default. */
83     {   2,  1, 11.00 * 72.0, 8.50 * 72.0, { .200 * 72.0, 0.0 } },
84     {   1,  1, 10.50 * 72.0, 7.25 * 72.0, { .200 * 72.0, 0.0 } },
85     {  26,  1, 11.69 * 72.0, 8.27 * 72.0, { .197 * 72.0, 0.0 } }
86 };
87 
88 /*
89  * The supported set of resolutions.
90  *
91  * The Color LaserJet 5/5M is actually a pseudo-contone device, with hardware
92  * capable of providing about 16 levels of intensity. The current code does
93  * not take advantage of this feature, because it is not readily controllable
94  * via PCL. Rather, the device is modeled as a bi-level device in each of
95  * three color planes. The maximum supported resolution for such an arrangement
96  * is 300 dpi.
97  *
98  * The CLJ does support raster scaling, but to invoke that scaling, even for
99  * integral factors, involves a large performance penalty. Hence, only those
100  * resolutions that can be supported without invoking raster scaling are
101  * included here. These resolutions are always the same in the fast and slow
102  * scan directions, so only a single value is listed here.
103  *
104  * All valuse are in dots per inch.
105  */
106 private const float supported_resolutions[] = { 75.0, 100.0, 150.0, 300.0 };
107 
108 
109 /* indicate the maximum supported resolution and scan-line length (pts) */
110 #define CLJ_MAX_RES        300.0
111 #define CLJ_MAX_SCANLINE   (12.0 * 72.0)
112 
113 
114 /*
115  * Determine a requested resolution pair is supported.
116  */
117   private bool
is_supported_resolution(const float HWResolution[2])118 is_supported_resolution(
119     const float HWResolution[2]
120 )
121 {
122     int     i;
123 
124     for (i = 0; i < countof(supported_resolutions); i++) {
125         if (HWResolution[0] == supported_resolutions[i])
126             return HWResolution[0] == HWResolution[1];
127     }
128     return false;
129 }
130 
131 /* ---------------- Standard driver ---------------- */
132 
133 /*
134  * Find the paper size information corresponding to a given pair of dimensions.
135  * If rotatep != 0, *rotatep is set to true if the page must be rotated 90
136  * degrees to fit.
137  *
138  * A return value of 0 indicates the paper size is not supported.
139  *
140  * Note that for the standard driver, rotation is not allowed.
141  */
142   private const clj_paper_size *
get_paper_size(const float MediaSize[2],bool * rotatep)143 get_paper_size(
144     const float             MediaSize[2],
145     bool *                  rotatep
146 )
147 {
148     static const float      tolerance = 5.0;
149     float                   width = MediaSize[0];
150     float                   height = MediaSize[1];
151     const clj_paper_size *  psize = 0;
152     int                     i;
153 
154     for (i = 0, psize = clj_paper_sizes; i < countof(clj_paper_sizes); i++, psize++) {
155         if ( (fabs(width - psize->width) <= tolerance)  &&
156              (fabs(height - psize->height) <= tolerance)  ) {
157             if (rotatep != 0)
158                 *rotatep = false;
159             return psize;
160         } else if ( (fabs(width - psize->height) <= tolerance) &&
161                     (fabs(height - psize->width) <= tolerance)   ) {
162             if (rotatep != 0)
163                 *rotatep = true;
164             return psize;
165         }
166     }
167 
168     return 0;
169 }
170 
171 /*
172  * Get the (PostScript style) default matrix for the current page size.
173  *
174  * For all of the supported sizes, the page will be printed with long-edge
175  * feed (the CLJ does support some additional sizes, but only for monochrome).
176  * As will all HP laser printers, the printable region marin is 12 pts. from
177  * the edge of the physical page.
178  */
179 private void
clj_get_initial_matrix(gx_device * pdev,gs_matrix * pmat)180 clj_get_initial_matrix( gx_device *pdev, gs_matrix *pmat)
181 {
182     floatp      	fs_res = pdev->HWResolution[0] / 72.0;
183     floatp      	ss_res = pdev->HWResolution[1] / 72.0;
184     const clj_paper_size *psize;
185 
186     psize = get_paper_size(pdev->MediaSize, NULL);
187     /* if the paper size is not recognized, not much can be done */
188     /* This shouldn't be possible since clj_put_params rejects   */
189     /* unknown media sizes.					 */
190     if (psize == 0) {
191 	pmat->xx = fs_res;
192 	pmat->xy = 0.0;
193 	pmat->yx = 0.0;
194 	pmat->yy = -ss_res;
195 	pmat->tx = 0.0;
196 	pmat->ty = pdev->MediaSize[1] * ss_res;
197 	return;
198     }
199 
200     if (pclj->rotated) {
201         pmat->xx = 0.0;
202         pmat->xy = ss_res;
203         pmat->yx = fs_res;
204         pmat->yy = 0.0;
205         pmat->tx = -psize->offsets.x * fs_res;
206         pmat->ty = -psize->offsets.y * ss_res;
207     } else {
208         pmat->xx = fs_res;
209         pmat->xy = 0.0;
210         pmat->yx = 0.0;
211         pmat->yy = -ss_res;
212         pmat->tx = -psize->offsets.x * fs_res;
213         pmat->ty = pdev->height + psize->offsets.y * ss_res;
214     }
215 }
216 
217 /*
218  * Get parameters, including InputAttributes for all supported page sizes.
219  * We associate each page size with a different "media source", since that
220  * is currently the only way to register multiple page sizes.
221  */
222 private int
clj_get_params(gx_device * pdev,gs_param_list * plist)223 clj_get_params(gx_device *pdev, gs_param_list *plist)
224 {
225     gs_param_dict mdict;
226     int code = gdev_prn_get_params(pdev, plist);
227     int ecode = code;
228     int i;
229 
230     code = gdev_begin_input_media(plist, &mdict, countof(clj_paper_sizes));
231     if (code < 0)
232 	ecode = code;
233     else {
234 	for (i = 0; i < countof(clj_paper_sizes); ++i) {
235 	    code = gdev_write_input_page_size(i, &mdict,
236 					      clj_paper_sizes[i].width,
237 					      clj_paper_sizes[i].height);
238 	    if (code < 0)
239 		ecode = code;
240 	}
241 	code = gdev_end_input_media(plist, &mdict);
242 	if (code < 0)
243 	    ecode = code;
244     }
245     return ecode;
246 }
247 
248 /*
249  * Get the media size being set by put_params, if any.  Return 0 if no media
250  * size is being set, 1 (and set mediasize[]) if the size is being set, <0
251  * on error.
252  */
253 private int
clj_media_size(float mediasize[2],gs_param_list * plist)254 clj_media_size(float mediasize[2], gs_param_list *plist)
255 {
256     gs_param_float_array fres;
257     gs_param_float_array fsize;
258     gs_param_int_array hwsize;
259     int have_pagesize = 0;
260 
261     if ( (param_read_float_array(plist, "HWResolution", &fres) == 0) &&
262           !is_supported_resolution(fres.data) )
263         return_error(gs_error_rangecheck);
264 
265     if ( (param_read_float_array(plist, "PageSize", &fsize) == 0) ||
266          (param_read_float_array(plist, ".MediaSize", &fsize) == 0) ) {
267 	mediasize[0] = fsize.data[0];
268 	mediasize[1] = fsize.data[1];
269 	have_pagesize = 1;
270     }
271 
272     if (param_read_int_array(plist, "HWSize", &hwsize) == 0) {
273         mediasize[0] = ((float)hwsize.data[0]) / fres.data[0];
274         mediasize[1] = ((float)hwsize.data[1]) / fres.data[1];
275 	have_pagesize = 1;
276     }
277 
278     return have_pagesize;
279 }
280 
281 /*
282  * Special put_params routine, to make certain the desired MediaSize and
283  * HWResolution are supported.
284  */
285   private int
clj_put_params(gx_device * pdev,gs_param_list * plist)286 clj_put_params(
287     gx_device *             pdev,
288     gs_param_list *         plist
289 )
290 {
291     float		    mediasize[2];
292     bool                    rotate = false;
293     int                     have_pagesize = clj_media_size(mediasize, plist);
294 
295     if (have_pagesize < 0)
296 	return have_pagesize;
297     if (have_pagesize) {
298 	if (get_paper_size(mediasize, &rotate) == 0 || rotate)
299 	    return_error(gs_error_rangecheck);
300     }
301     return gdev_prn_put_params(pdev, plist);
302 }
303 
304 /*
305  * Pack and then compress a scanline of data. Return the size of the compressed
306  * data produced.
307  *
308  * Input is arranged with one byte per pixel, but only the three low-order bits
309  * are used. These bits are in order ymc, with yellow being the highest order
310  * bit.
311  *
312  * Output is arranged in three planes, with one bit per pixel per plane. The
313  * Color LaserJet 5/5M does support more congenial pixel encodings, but use
314  * of anything other than the fixed palettes seems to result in very poor
315  * performance.
316  *
317  * Only compresion mode 2 is used. Compression mode 1 (pure run length) has
318  * an advantage over compression mode 2 only in cases in which very long runs
319  * occur (> 128 bytes). Since both methods provide good compression in that
320  * case, it is not worth worrying about, and compression mode 2 provides much
321  * better worst-case behavior. Compression mode 3 requires considerably more
322  * effort to generate, so it is useful only when it is known a prior that
323  * scanlines repeat frequently.
324  */
325   private void
pack_and_compress_scanline(const byte * pin,int in_size,byte * pout[3],int out_size[3])326 pack_and_compress_scanline(
327     const byte *        pin,
328     int                 in_size,
329     byte  *             pout[3],
330     int                 out_size[3]
331 )
332 {
333 #define BUFF_SIZE                                                           \
334     ( ((int)(CLJ_MAX_RES * CLJ_MAX_SCANLINE / 72.0) + sizeof(ulong) - 1)    \
335          / sizeof(ulong) )
336 
337     ulong               buff[3 * BUFF_SIZE];
338     byte *              p_c = (byte *)buff;
339     byte *              p_m = (byte *)(buff + BUFF_SIZE);
340     byte *              p_y = (byte *)(buff + 2 * BUFF_SIZE);
341     ulong *             ptrs[3];
342     byte                c_val = 0, m_val = 0, y_val = 0;
343     ulong               mask = 0x80;
344     int                 i;
345 
346     /* pack the input for 4-bits per index */
347     for (i = 0; i < in_size; i++) {
348         uint    ival = *pin++;
349 
350         if (ival != 0) {
351             if ((ival & 0x4) != 0)
352                 y_val |= mask;
353             if ((ival & 0x2) != 0)
354                 m_val |= mask;
355             if ((ival & 0x1) != 0)
356                 c_val |= mask;
357         }
358 
359         if ((mask >>= 1) == 0) {
360             /* NB - write out in byte units */
361             *p_c++ = c_val;
362             c_val = 0L;
363             *p_m++ = m_val;
364             m_val = 0L;
365             *p_y++ = y_val;
366             y_val = 0L;
367             mask = 0x80;
368         }
369     }
370     if (mask != 0x80) {
371         /* NB - write out in byte units */
372         *p_c++ = c_val;
373         *p_m++ = m_val;
374         *p_y++ = y_val;
375     }
376 
377     /* clear to up a longword boundary */
378     while ((((ulong)p_c) & (sizeof(ulong) - 1)) != 0) {
379         *p_c++ = 0;
380         *p_m++ = 0;
381         *p_y++ = 0;
382     }
383 
384     ptrs[0] = (ulong *)p_c;
385     ptrs[1] = (ulong *)p_m;
386     ptrs[2] = (ulong *)p_y;
387 
388     for (i = 0; i < 3; i++) {
389         ulong * p_start = buff + i * BUFF_SIZE;
390         ulong * p_end = ptrs[i];
391 
392         /* eleminate trailing 0's */
393         while ((p_end > p_start) && (p_end[-1] == 0))
394             p_end--;
395 
396         if (p_start == p_end)
397             out_size[i] = 0;
398         else
399             out_size[i] = gdev_pcl_mode2compress(p_start, p_end, pout[i]);
400     }
401 
402 #undef BUFF_SIZE
403 }
404 
405 /*
406  * Send the page to the printer.  Compress each scan line.
407  */
408   private int
clj_print_page(gx_device_printer * pdev,FILE * prn_stream)409 clj_print_page(
410     gx_device_printer *     pdev,
411     FILE *                  prn_stream
412 )
413 {
414     gs_memory_t *mem = pdev->memory;
415     bool                    rotate;
416     const clj_paper_size *  psize = get_paper_size(pdev->MediaSize, &rotate);
417     int                     lsize = pdev->width;
418     int                     clsize = (lsize + (lsize + 255) / 128) / 8;
419     byte *                  data = 0;
420     byte *                  cdata[3];
421     int                     blank_lines = 0;
422     int                     i;
423     floatp                  fs_res = pdev->HWResolution[0] / 72.0;
424     floatp                  ss_res = pdev->HWResolution[1] / 72.0;
425     int			    imageable_width, imageable_height;
426 
427     /* no paper size at this point is a serious error */
428     if (psize == 0)
429         return_error(gs_error_unregistered);
430 
431     /* allocate memory for the raw and compressed data */
432     if ((data = gs_alloc_bytes(mem, lsize, "clj_print_page(data)")) == 0)
433         return_error(gs_error_VMerror);
434     if ((cdata[0] = gs_alloc_bytes(mem, 3 * clsize, "clj_print_page(cdata)")) == 0) {
435         gs_free_object(mem, data, "clj_print_page(data)");
436         return_error(gs_error_VMerror);
437     }
438     cdata[1] = cdata[0] + clsize;
439     cdata[2] = cdata[1] + clsize;
440 
441 
442     /* Imageable area is without the margins. Note that the actual rotation
443      * of page size into pdev->width & height has been done. We just use
444      * rotate to access the correct offsets. */
445     if (pclj->rotated) {
446     	imageable_width = pdev->width - (2 * psize->offsets.x) * fs_res;
447     	imageable_height = pdev->height - (2 * psize->offsets.y) * ss_res;
448     }
449     else {
450     	imageable_width = pdev->width - (2 * psize->offsets.y) * ss_res;
451     	imageable_height = pdev->height - (2 * psize->offsets.x) * fs_res;
452     }
453 
454     /* start the page.  The pcl origin (0, 150 dots by default, y
455        increasing down the long edge side of the page) needs to be
456        offset such that it coincides with the offsets of the imageable
457        area.  This calculation should be independant of rotation but
458        only the rotated case has been tested with a real device. */
459     fprintf( prn_stream,
460              "\033E\033&u300D\033&l%da1x%dO\033*p0x0y+50x-100Y\033*t%dR"
461 #ifdef USE_FAST_MODE
462 	     "\033*r-3U"
463 #else
464              "\033*v6W\001\002\003\001\001\001"
465 #endif
466              "\033*r0f%ds%dt1A\033*b2M",
467              psize->tag,
468              pclj->rotated,
469              (int)(pdev->HWResolution[0]),
470              imageable_width,
471              imageable_height
472              );
473 
474     /* process each scanline */
475     for (i = 0; i < imageable_height; i++) {
476         int     clen[3];
477 
478         gdev_prn_copy_scan_lines(pdev, i, data, lsize);
479 
480 	/* The 'lsize' bytes of data have the blank margin area at the end due	*/
481 	/* to the 'initial_matrix' offsets that are applied.			*/
482         pack_and_compress_scanline(data, imageable_width, cdata, clen);
483         if ((clen[0] == 0) && (clen[1] == 0) && (clen[2] == 0))
484             ++blank_lines;
485         else {
486             if (blank_lines != 0) {
487                 fprintf(prn_stream, "\033*b%dY", blank_lines);
488                 blank_lines = 0;
489             }
490             fprintf(prn_stream, "\033*b%dV", clen[0]);
491             fwrite(cdata[0], sizeof(byte), clen[0], prn_stream);
492             fprintf(prn_stream, "\033*b%dV", clen[1]);
493             fwrite(cdata[1], sizeof(byte), clen[1], prn_stream);
494             fprintf(prn_stream, "\033*b%dW", clen[2]);
495             fwrite(cdata[2], sizeof(byte), clen[2], prn_stream);
496         }
497     }
498 
499     /* PCL will take care of blank lines at the end */
500     fputs("\033*rC\f", prn_stream);
501 
502     /* free the buffers used */
503     gs_free_object(mem, cdata[0], "clj_print_page(cdata)");
504     gs_free_object(mem, data, "clj_print_page(data)");
505 
506     return 0;
507 }
508 
509 /* CLJ device methods */
510 #define CLJ_PROCS(get_params, put_params)\
511     gdev_prn_open,                  /* open_device */\
512     clj_get_initial_matrix,         /* get_initial matrix */\
513     NULL,	                    /* sync_output */\
514     gdev_prn_output_page,           /* output_page */\
515     gdev_prn_close,                 /* close_device */\
516     gdev_pcl_3bit_map_rgb_color,    /* map_rgb_color */\
517     gdev_pcl_3bit_map_color_rgb,    /* map_color_rgb */\
518     NULL,	                    /* fill_rectangle */\
519     NULL,	                    /* tile_rectangle */\
520     NULL,	                    /* copy_mono */\
521     NULL,	                    /* copy_color */\
522     NULL,	                    /* obsolete draw_line */\
523     NULL,	                    /* get_bits */\
524     get_params, 	            /* get_params */\
525     put_params,                     /* put_params */\
526     NULL,	                    /* map_cmyk_color */\
527     NULL,	                    /* get_xfont_procs */\
528     NULL,	                    /* get_xfont_device */\
529     NULL,	                    /* map_rgb_alpha_color */\
530     gx_page_device_get_page_device  /* get_page_device */
531 
532 private gx_device_procs cljet5_procs = {
533     CLJ_PROCS(clj_get_params, clj_put_params)
534 };
535 
536 /* CLJ device structure */
537 #define CLJ_DEVICE_BODY(procs, dname, rotated)\
538   prn_device_body(\
539     gx_device_clj,\
540     procs,                  /* procedures */\
541     dname,                  /* device name */\
542     110,                    /* width - will be overridden subsequently */\
543     85,                     /* height - will be overridden subsequently */\
544     X_DPI, Y_DPI,           /* resolutions - current must be the same */\
545     0.167, 0.167,           /* margins (left, bottom, right, top */\
546     0.167, 0.167,\
547     3,                      /* num_components - 3 colors, 1 bit per pixel */\
548     8,			    /* depth - pack into bytes */\
549     1, 1, 		    /* max_gray=max_component=1 */\
550     2, 2,		    /* dithered_grays=dithered_components=2 */ \
551     clj_print_page          /* routine to output page */\
552 ),\
553     rotated		    /* rotated - may be overridden subsequently */
554 
555 gx_device_clj gs_cljet5_device = {
556     CLJ_DEVICE_BODY(cljet5_procs, "cljet5", 0 /*false*/)
557 };
558 
559 /* ---------------- Driver with page rotation ---------------- */
560 
561 /*
562  * For use with certain PCL interpreters, which don't implement
563  * setpagedevice, we provide a version of this driver that attempts to
564  * handle page rotation at the driver level.  This version breaks an
565  * invariant that all drivers must obey, namely, that drivers are not
566  * allowed to change the parameters passed by put_params (they can only
567  * accept or reject them).  Consequently, this driver must not be used in
568  * any context other than these specific PCL interpreters.  We support this
569  * hack only because these PCL interpreters can't be changed to handle page
570  * rotation properly.
571  */
572 
573 /*
574  * Special get_params routine, to fake MediaSize, width, and height if
575  * we were in a 'rotated' state.
576  */
577 private int
clj_pr_get_params(gx_device * pdev,gs_param_list * plist)578 clj_pr_get_params( gx_device *pdev, gs_param_list *plist )
579 {
580     int code;
581 
582     /* First un-rotate the MediaSize, etc. if we were in a rotated mode		*/
583     if (pclj->rotated) {
584         float ftmp;
585 	int   itmp;
586 
587 	ftmp = pdev->MediaSize[0];
588 	pdev->MediaSize[0] = pdev->MediaSize[1];
589 	pdev->MediaSize[1] = ftmp;
590 	itmp = pdev->width;
591 	pdev->width = pdev->height;
592 	pdev->height = itmp;
593     }
594 
595     /* process the parameter list */
596     code = gdev_prn_get_params(pdev, plist);
597 
598     /* Now re-rotate the page size if needed */
599     if (pclj->rotated) {
600         float ftmp;
601 	int   itmp;
602 
603 	ftmp = pdev->MediaSize[0];
604 	pdev->MediaSize[0] = pdev->MediaSize[1];
605 	pdev->MediaSize[1] = ftmp;
606 	itmp = pdev->width;
607 	pdev->width = pdev->height;
608 	pdev->height = itmp;
609     }
610 
611     return code;
612 }
613 
614 /*
615  * Special put_params routine, to intercept changes in the MediaSize, and to
616  * make certain the desired MediaSize and HWResolution are supported.
617  *
618  * This function will rotate MediaSize if it is needed by the device in
619  * order to print this size page.
620  */
621   private int
clj_pr_put_params(gx_device * pdev,gs_param_list * plist)622 clj_pr_put_params(
623     gx_device *             pdev,
624     gs_param_list *         plist
625 )
626 {
627     float		    mediasize[2];
628     int                     code = 0;
629     bool                    rotate = false;
630     int                     have_pagesize = clj_media_size(mediasize, plist);
631 
632     if (have_pagesize < 0)
633 	return have_pagesize;
634     if (have_pagesize) {
635 	if (get_paper_size(mediasize, &rotate) == 0)
636 	    return_error(gs_error_rangecheck);
637 	if (rotate) {
638 	    /* We need to rotate the requested page size, so synthesize a new	*/
639 	    /* parameter list in front of the requestor's list to force the	*/
640 	    /* rotated page size.						*/
641 	    gs_param_float_array	pf_array;
642 	    gs_c_param_list		alist;
643 	    float			ftmp = mediasize[0];
644 
645 	    mediasize[0] = mediasize[1];
646 	    mediasize[1] = ftmp;
647 	    pf_array.data = mediasize;
648 	    pf_array.size = 2;
649 	    pf_array.persistent = false;
650 
651 	    gs_c_param_list_write(&alist, pdev->memory);
652 	    code = param_write_float_array((gs_param_list *)&alist, ".MediaSize", &pf_array);
653 	    gs_c_param_list_read(&alist);
654 
655 	    /* stick this synthesized parameter on the front of the existing list */
656 	    gs_c_param_list_set_target(&alist, plist);
657 	    if ((code = gdev_prn_put_params(pdev, (gs_param_list *)&alist)) >= 0)
658 		pclj->rotated = true;
659 	    gs_c_param_list_release(&alist);
660 	} else {
661 	    if ((code = gdev_prn_put_params(pdev, plist)) >= 0)
662 		pclj->rotated = false;
663 	}
664     } else
665 	code = gdev_prn_put_params(pdev, plist);
666 
667     return code;
668 }
669 
670 /* CLJ device methods -- se above for CLJ_PROCS */
671 private gx_device_procs cljet5pr_procs = {
672     CLJ_PROCS(clj_pr_get_params, clj_pr_put_params)
673 };
674 
675 /* CLJ device structure -- see above for CLJ_DEVICE_BODY */
676 gx_device_clj gs_cljet5pr_device = {
677     CLJ_DEVICE_BODY(cljet5pr_procs, "cljet5pr", 1 /*true*/)
678 };
679