1
/* Copyright (C) 2001-2006 Artifex Software, Inc.
4
This software is provided AS-IS with no warranty, either express or
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.
13
/* $Id: gdevclj.c 8250 2007-09-25 13:31:24Z giles $ */
15
* H-P Color LaserJet 5/5M device; based on the PaintJet.
23
typedef struct gx_device_clj_s gx_device_clj;
24
struct gx_device_clj_s {
30
#define pclj ((gx_device_clj *)pdev)
33
* The HP Color LaserJet 5/5M provides a rather unexpected speed/performance
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
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).
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).
58
/* X_DPI and Y_DPI must be the same */
63
* Array of paper sizes, and the corresponding offsets.
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 */
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.
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 } }
85
* The supported set of resolutions.
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
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.
100
* All valuse are in dots per inch.
102
static const float supported_resolutions[] = { 75.0, 100.0, 150.0, 300.0 };
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)
111
* Determine a requested resolution pair is supported.
114
is_supported_resolution(
115
const float HWResolution[2]
120
for (i = 0; i < countof(supported_resolutions); i++) {
121
if (HWResolution[0] == supported_resolutions[i])
122
return HWResolution[0] == HWResolution[1];
127
/* ---------------- Standard driver ---------------- */
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
134
* A return value of 0 indicates the paper size is not supported.
136
* Note that for the standard driver, rotation is not allowed.
138
static const clj_paper_size *
140
const float MediaSize[2],
144
static const float tolerance = 5.0;
145
float width = MediaSize[0];
146
float height = MediaSize[1];
147
const clj_paper_size * psize = 0;
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) ) {
156
} else if ( (fabs(width - psize->height) <= tolerance) &&
157
(fabs(height - psize->width) <= tolerance) ) {
168
* Get the (PostScript style) default matrix for the current page size.
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.
176
clj_get_initial_matrix( gx_device *pdev, gs_matrix *pmat)
178
floatp fs_res = pdev->HWResolution[0] / 72.0;
179
floatp ss_res = pdev->HWResolution[1] / 72.0;
180
const clj_paper_size *psize;
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. */
192
pmat->ty = pdev->MediaSize[1] * ss_res;
201
pmat->tx = -psize->offsets.x * fs_res;
202
pmat->ty = -psize->offsets.y * ss_res;
208
pmat->tx = -psize->offsets.x * fs_res;
209
pmat->ty = pdev->height + psize->offsets.y * ss_res;
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.
219
clj_get_params(gx_device *pdev, gs_param_list *plist)
222
int code = gdev_prn_get_params(pdev, plist);
226
code = gdev_begin_input_media(plist, &mdict, countof(clj_paper_sizes));
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);
237
code = gdev_end_input_media(plist, &mdict);
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
250
clj_media_size(float mediasize[2], gs_param_list *plist)
252
gs_param_float_array fres;
253
gs_param_float_array fsize;
254
gs_param_int_array hwsize;
255
int have_pagesize = 0;
257
if ( (param_read_float_array(plist, "HWResolution", &fres) == 0) &&
258
!is_supported_resolution(fres.data) )
259
return_error(gs_error_rangecheck);
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];
268
if (param_read_int_array(plist, "HWSize", &hwsize) == 0) {
269
mediasize[0] = ((float)hwsize.data[0]) / fres.data[0];
270
mediasize[1] = ((float)hwsize.data[1]) / fres.data[1];
274
return have_pagesize;
278
* Special put_params routine, to make certain the desired MediaSize and
279
* HWResolution are supported.
284
gs_param_list * plist
289
int have_pagesize = clj_media_size(mediasize, plist);
291
if (have_pagesize < 0)
292
return have_pagesize;
294
if (get_paper_size(mediasize, &rotate) == 0 || rotate)
295
return_error(gs_error_rangecheck);
297
return gdev_prn_put_params(pdev, plist);
301
* Pack and then compress a scanline of data. Return the size of the compressed
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
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
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.
322
pack_and_compress_scanline(
330
( ((int)(CLJ_MAX_RES * CLJ_MAX_SCANLINE / 72.0) + sizeof(ulong) - 1) \
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);
338
byte c_val = 0, m_val = 0, y_val = 0;
342
/* pack the input for 4-bits per index */
343
for (i = 0; i < in_size; i++) {
347
if ((ival & 0x4) != 0)
349
if ((ival & 0x2) != 0)
351
if ((ival & 0x1) != 0)
355
if ((mask >>= 1) == 0) {
356
/* NB - write out in byte units */
367
/* NB - write out in byte units */
373
/* clear to up a longword boundary */
374
while ((((ulong)p_c) & (sizeof(ulong) - 1)) != 0) {
380
ptrs[0] = (ulong *)p_c;
381
ptrs[1] = (ulong *)p_m;
382
ptrs[2] = (ulong *)p_y;
384
for (i = 0; i < 3; i++) {
385
ulong * p_start = buff + i * BUFF_SIZE;
386
ulong * p_end = ptrs[i];
388
/* eleminate trailing 0's */
389
while ((p_end > p_start) && (p_end[-1] == 0))
392
if (p_start == p_end)
395
out_size[i] = gdev_pcl_mode2compress(p_start, p_end, pout[i]);
402
* Send the page to the printer. Compress each scan line.
406
gx_device_printer * pdev,
410
gs_memory_t *mem = pdev->memory;
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;
419
floatp fs_res = pdev->HWResolution[0] / 72.0;
420
floatp ss_res = pdev->HWResolution[1] / 72.0;
421
int imageable_width, imageable_height;
423
/* no paper size at this point is a serious error */
425
return_error(gs_error_unregistered);
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);
434
cdata[1] = cdata[0] + clsize;
435
cdata[2] = cdata[1] + clsize;
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. */
442
imageable_width = pdev->width - (2 * psize->offsets.x) * fs_res;
443
imageable_height = pdev->height - (2 * psize->offsets.y) * ss_res;
446
imageable_width = pdev->width - (2 * psize->offsets.y) * ss_res;
447
imageable_height = pdev->height - (2 * psize->offsets.x) * fs_res;
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. */
456
"\033E\033&u300D\033&l%da1x%dO\033*p0x0y+50x-100Y\033*t%dR"
460
"\033*v6W\001\002\003\001\001\001"
462
"\033*r0f%ds%dt1A\033*b2M",
465
(int)(pdev->HWResolution[0]),
470
/* process each scanline */
471
for (i = 0; i < imageable_height; i++) {
474
gdev_prn_copy_scan_lines(pdev, i, data, lsize);
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))
482
if (blank_lines != 0) {
483
fprintf(prn_stream, "\033*b%dY", blank_lines);
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);
495
/* PCL will take care of blank lines at the end */
496
fputs("\033*rC\f", prn_stream);
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)");
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 */
528
static gx_device_procs cljet5_procs = {
529
CLJ_PROCS(clj_get_params, clj_put_params)
532
/* CLJ device structure */
533
#define CLJ_DEVICE_BODY(procs, dname, rotated)\
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 */\
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 */\
549
rotated /* rotated - may be overridden subsequently */
551
gx_device_clj gs_cljet5_device = {
552
CLJ_DEVICE_BODY(cljet5_procs, "cljet5", 0 /*false*/)
555
/* ---------------- Driver with page rotation ---------------- */
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
570
* Special get_params routine, to fake MediaSize, width, and height if
571
* we were in a 'rotated' state.
574
clj_pr_get_params( gx_device *pdev, gs_param_list *plist )
578
/* First un-rotate the MediaSize, etc. if we were in a rotated mode */
583
ftmp = pdev->MediaSize[0];
584
pdev->MediaSize[0] = pdev->MediaSize[1];
585
pdev->MediaSize[1] = ftmp;
587
pdev->width = pdev->height;
591
/* process the parameter list */
592
code = gdev_prn_get_params(pdev, plist);
594
/* Now re-rotate the page size if needed */
599
ftmp = pdev->MediaSize[0];
600
pdev->MediaSize[0] = pdev->MediaSize[1];
601
pdev->MediaSize[1] = ftmp;
603
pdev->width = pdev->height;
611
* Special put_params routine, to intercept changes in the MediaSize, and to
612
* make certain the desired MediaSize and HWResolution are supported.
614
* This function will rotate MediaSize if it is needed by the device in
615
* order to print this size page.
620
gs_param_list * plist
626
int have_pagesize = clj_media_size(mediasize, plist);
628
if (have_pagesize < 0)
629
return have_pagesize;
631
if (get_paper_size(mediasize, &rotate) == 0)
632
return_error(gs_error_rangecheck);
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];
641
mediasize[0] = mediasize[1];
643
pf_array.data = mediasize;
645
pf_array.persistent = false;
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);
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);
657
if ((code = gdev_prn_put_params(pdev, plist)) >= 0)
658
pclj->rotated = false;
661
code = gdev_prn_put_params(pdev, plist);
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)
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*/)