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