xref: /plan9/sys/src/cmd/gs/src/gdevpcl.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: gdevpcl.c,v 1.8 2002/08/22 07:12:28 henrys Exp $ */
18 /* Utilities for PCL printers */
19 #include "gdevprn.h"
20 #include "gdevpcl.h"
21 #include "math_.h"
22 
23 /* ------ Get paper size ------ */
24 
25 /* Get the paper size code, based on width and height. */
26 int
gdev_pcl_paper_size(gx_device * dev)27 gdev_pcl_paper_size(gx_device * dev)
28 {
29     float width_inches = dev->width / dev->x_pixels_per_inch;
30     float height_inches = dev->height / dev->y_pixels_per_inch;
31     /* The initial value for height_difference, and for code below, is
32        unnecessary and is here just to stop the compiler from
33        complaining about a possible uninitialized variable usage. */
34     float width_difference = -1.0, height_difference = -1.0;
35     float new_width_difference, new_height_difference;
36     int code = PAPER_SIZE_LETTER;
37 
38     /* Since we're telling the printer when to eject and start a new
39        page, the paper height doesn't matter a great deal, as long as
40        we ensure that it's at least as high as we want our pages to
41        be.  However, the paper width is important, because on printers
42        which center the paper in the input tray, having the wrong
43        width will cause the image to appear in the wrong place on the
44        paper (perhaps even partially missing the paper completely).
45        Therefore, we choose our paper size by finding one whose width
46        and height are both equal to or greater than the desired width
47        and height and whose width is the closest to what we want.  We
48        only pay close attention to the height when the widths of two
49        different paper sizes are equal.
50 
51        We use "foo - bar > -0.01" instead of "foo >= bar" to avoid
52        minor floating point and rounding errors.
53     */
54 #define CHECK_PAPER_SIZE(w,h,c)							\
55     new_width_difference = w - width_inches;					\
56     new_height_difference = h - height_inches;					\
57     if ((new_width_difference > -0.01) && (new_height_difference > -0.01) &&	\
58 	((width_difference == -1.0) ||						\
59 	 (new_width_difference < width_difference) ||				\
60 	 ((new_width_difference == width_difference) &&				\
61 	  (new_height_difference < height_difference)))) {			\
62       width_difference = new_width_difference;					\
63       height_difference = new_height_difference;				\
64       code = c;									\
65     }
66 
67     CHECK_PAPER_SIZE( 7.25, 10.5,  PAPER_SIZE_EXECUTIVE);
68     CHECK_PAPER_SIZE( 8.5 , 11.0,  PAPER_SIZE_LETTER);
69     CHECK_PAPER_SIZE( 8.5 , 14.0,  PAPER_SIZE_LEGAL);
70     CHECK_PAPER_SIZE(11.0 , 17.0,  PAPER_SIZE_LEDGER);
71     CHECK_PAPER_SIZE( 8.27, 11.69, PAPER_SIZE_A4);
72     CHECK_PAPER_SIZE(11.69, 16.54, PAPER_SIZE_A3);
73     CHECK_PAPER_SIZE(16.54, 23.39, PAPER_SIZE_A2);
74     CHECK_PAPER_SIZE(23.39, 33.11, PAPER_SIZE_A1);
75     CHECK_PAPER_SIZE(33.11, 46.81, PAPER_SIZE_A0);
76     CHECK_PAPER_SIZE( 7.16, 10.12, PAPER_SIZE_JIS_B5);
77     CHECK_PAPER_SIZE(10.12, 14.33, PAPER_SIZE_JIS_B4);
78     CHECK_PAPER_SIZE( 3.94,  5.83, PAPER_SIZE_JPOST);
79     CHECK_PAPER_SIZE( 5.83,  7.87, PAPER_SIZE_JPOSTD);
80     CHECK_PAPER_SIZE( 3.87,  7.5 , PAPER_SIZE_MONARCH);
81     CHECK_PAPER_SIZE( 4.12,  9.5 , PAPER_SIZE_COM10);
82     CHECK_PAPER_SIZE( 4.33,  8.66, PAPER_SIZE_DL);
83     CHECK_PAPER_SIZE( 6.38,  9.01, PAPER_SIZE_C5);
84     CHECK_PAPER_SIZE( 6.93,  9.84, PAPER_SIZE_B5);
85 
86 #undef CHECK_PAPER_SIZE
87 
88     return code;
89 }
90 
91 /* ------ Color mapping ------ */
92 
93 /* The PaintJet and DeskJet 500C use additive colors in separate planes. */
94 /* We only keep one bit of color, with 1 = R, 2 = G, 4 = B. */
95 /* Because the buffering routines assume 0 = white, */
96 /* we complement all the color components. */
97 #define cv_shift (sizeof(gx_color_value) * 8 - 1)
98 
99 /* Map an RGB color to a printer color. */
100 gx_color_index
gdev_pcl_3bit_map_rgb_color(gx_device * dev,const gx_color_value cv[])101 gdev_pcl_3bit_map_rgb_color(gx_device * dev, const gx_color_value cv[])
102 {
103     gx_color_value r, g, b;
104     r = cv[0]; g = cv[1]; b = cv[2];
105     return (((b >> cv_shift) << 2) + ((g >> cv_shift) << 1) + (r >> cv_shift)) ^ 7;
106 }
107 
108 /* Map the printer color back to RGB. */
109 int
gdev_pcl_3bit_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value prgb[3])110 gdev_pcl_3bit_map_color_rgb(gx_device * dev, gx_color_index color,
111 			    gx_color_value prgb[3])
112 {
113     ushort cc = (ushort) color ^ 7;
114 
115     prgb[0] = -(cc & 1);
116     prgb[1] = -((cc >> 1) & 1);
117     prgb[2] = -(cc >> 2);
118     return 0;
119 }
120 
121 /* ------ Compression ------ */
122 
123 /*
124  * Mode 2 Row compression routine for the HP DeskJet & LaserJet IIp.
125  * Compresses data from row up to end_row, storing the result
126  * starting at compressed.  Returns the number of bytes stored.
127  * Runs of K<=127 literal bytes are encoded as K-1 followed by
128  * the bytes; runs of 2<=K<=127 identical bytes are encoded as
129  * 257-K followed by the byte.
130  * In the worst case, the result is N+(N/127)+1 bytes long,
131  * where N is the original byte count (end_row - row).
132  * To speed up the search, we examine an entire word at a time.
133  * We will miss a few blocks of identical bytes; tant pis.
134  */
135 int
gdev_pcl_mode2compress_padded(const word * row,const word * end_row,byte * compressed,bool pad)136 gdev_pcl_mode2compress_padded(const word * row, const word * end_row,
137 			      byte * compressed, bool pad)
138 {
139     register const word *exam = row;	/* word being examined in the row to compress */
140     register byte *cptr = compressed;	/* output pointer into compressed bytes */
141 
142     while (exam < end_row) {	/* Search ahead in the input looking for a run */
143 	/* of at least 4 identical bytes. */
144 	const byte *compr = (const byte *)exam;
145 	const byte *end_dis;
146 	const word *next;
147 	register word test = *exam;
148 
149 	while (((test << 8) ^ test) > 0xff) {
150 	    if (++exam >= end_row)
151 		break;
152 	    test = *exam;
153 	}
154 
155 	/* Find out how long the run is */
156 	end_dis = (const byte *)exam;
157 	if (exam == end_row) {	/* no run */
158 	    /* See if any of the last 3 "dissimilar" bytes are 0. */
159 	    if (!pad && end_dis > compr && end_dis[-1] == 0) {
160 		if (end_dis[-2] != 0)
161 		    end_dis--;
162 		else if (end_dis[-3] != 0)
163 		    end_dis -= 2;
164 		else
165 		    end_dis -= 3;
166 	    }
167 	    next = --end_row;
168 	} else {
169 	    next = exam + 1;
170 	    while (next < end_row && *next == test)
171 		next++;
172 	    /* See if any of the last 3 "dissimilar" bytes */
173 	    /* are the same as the repeated byte. */
174 	    if (end_dis > compr && end_dis[-1] == (byte) test) {
175 		if (end_dis[-2] != (byte) test)
176 		    end_dis--;
177 		else if (end_dis[-3] != (byte) test)
178 		    end_dis -= 2;
179 		else
180 		    end_dis -= 3;
181 	    }
182 	}
183 
184 	/* Now [compr..end_dis) should be encoded as dissimilar, */
185 	/* and [end_dis..next) should be encoded as similar. */
186 	/* Note that either of these ranges may be empty. */
187 
188 	for (;;) {		/* Encode up to 127 dissimilar bytes */
189 	    uint count = end_dis - compr;	/* uint for faster switch */
190 
191 	    switch (count) {	/* Use memcpy only if it's worthwhile. */
192 		case 6:
193 		    cptr[6] = compr[5];
194 		case 5:
195 		    cptr[5] = compr[4];
196 		case 4:
197 		    cptr[4] = compr[3];
198 		case 3:
199 		    cptr[3] = compr[2];
200 		case 2:
201 		    cptr[2] = compr[1];
202 		case 1:
203 		    cptr[1] = compr[0];
204 		    *cptr = count - 1;
205 		    cptr += count + 1;
206 		case 0:	/* all done */
207 		    break;
208 		default:
209 		    if (count > 127)
210 			count = 127;
211 		    *cptr++ = count - 1;
212 		    memcpy(cptr, compr, count);
213 		    cptr += count, compr += count;
214 		    continue;
215 	    }
216 	    break;
217 	}
218 
219 	{			/* Encode up to 127 similar bytes. */
220 	    /* Note that count may be <0 at end of row. */
221 	    int count = (const byte *)next - end_dis;
222 
223 	    while (count > 0) {
224 		int this = (count > 127 ? 127 : count);
225 
226 		*cptr++ = 257 - this;
227 		*cptr++ = (byte) test;
228 		count -= this;
229 	    }
230 	    exam = next;
231 	}
232     }
233     return (cptr - compressed);
234 }
235 int
gdev_pcl_mode2compress(const word * row,const word * end_row,byte * compressed)236 gdev_pcl_mode2compress(const word * row, const word * end_row,
237 		       byte * compressed)
238 {
239     return gdev_pcl_mode2compress_padded(row, end_row, compressed, false);
240 }
241 
242 /*
243  * Mode 3 compression routine for the HP LaserJet III family.
244  * Compresses bytecount bytes starting at current, storing the result
245  * in compressed, comparing against and updating previous.
246  * Returns the number of bytes stored.  In the worst case,
247  * the number of bytes is bytecount+(bytecount/8)+1.
248  */
249 int
gdev_pcl_mode3compress(int bytecount,const byte * current,byte * previous,byte * compressed)250 gdev_pcl_mode3compress(int bytecount, const byte * current, byte * previous, byte * compressed)
251 {
252     register const byte *cur = current;
253     register byte *prev = previous;
254     register byte *out = compressed;
255     const byte *end = current + bytecount;
256 
257     while (cur < end) {		/* Detect a maximum run of unchanged bytes. */
258 	const byte *run = cur;
259 	register const byte *diff;
260 	const byte *stop;
261 	int offset, cbyte;
262 
263 	while (cur < end && *cur == *prev) {
264 	    cur++, prev++;
265 	}
266 	if (cur == end)
267 	    break;		/* rest of row is unchanged */
268 	/* Detect a run of up to 8 changed bytes. */
269 	/* We know that *cur != *prev. */
270 	diff = cur;
271 	stop = (end - cur > 8 ? cur + 8 : end);
272 	do {
273 	    *prev++ = *cur++;
274 	}
275 	while (cur < stop && *cur != *prev);
276 	/* Now [run..diff) are unchanged, and */
277 	/* [diff..cur) are changed. */
278 	/* Generate the command byte(s). */
279 	offset = diff - run;
280 	cbyte = (cur - diff - 1) << 5;
281 	if (offset < 31)
282 	    *out++ = cbyte + offset;
283 	else {
284 	    *out++ = cbyte + 31;
285 	    offset -= 31;
286 	    while (offset >= 255)
287 		*out++ = 255, offset -= 255;
288 	    *out++ = offset;
289 	}
290 	/* Copy the changed data. */
291 	while (diff < cur)
292 	    *out++ = *diff++;
293     }
294     return out - compressed;
295 }
296 
297 /*
298  * Mode 9 2D compression for the HP DeskJets . This mode can give
299  * very good compression ratios, especially if there are areas of flat
300  * colour (or blank areas), and so is 'highly recommended' for colour
301  * printing in particular because of the very large amounts of data which
302  * can be generated
303  */
304 int
gdev_pcl_mode9compress(int bytecount,const byte * current,const byte * previous,byte * compressed)305 gdev_pcl_mode9compress(int bytecount, const byte *current,
306 		       const byte *previous, byte *compressed)
307 {
308     register const byte *cur = current;
309     register const byte *prev = previous;
310     register byte *out = compressed;
311     const byte *end = current + bytecount;
312 
313     while (cur < end) {		/* Detect a run of unchanged bytes. */
314 	const byte *run = cur;
315 	register const byte *diff;
316 	int offset;
317 
318 	while (cur < end && *cur == *prev) {
319 	    cur++, prev++;
320 	}
321 	if (cur == end)
322 	    break;		/* rest of row is unchanged */
323 	/* Detect a run of changed bytes. */
324 	/* We know that *cur != *prev. */
325 	diff = cur;
326 	do {
327 	    prev++;
328 	    cur++;
329 	}
330 	while (cur < end && *cur != *prev);
331 	/* Now [run..diff) are unchanged, and */
332 	/* [diff..cur) are changed. */
333 	offset = diff - run;
334 	{
335 	    const byte *stop_test = cur - 4;
336 	    int dissimilar, similar;
337 
338 	    while (diff < cur) {
339 		const byte *compr = diff;
340 		const byte *next;	/* end of run */
341 		byte value = 0;
342 
343 		while (diff <= stop_test &&
344 		       ((value = *diff) != diff[1] ||
345 			value != diff[2] ||
346 			value != diff[3]))
347 		    diff++;
348 
349 		/* Find out how long the run is */
350 		if (diff > stop_test)	/* no run */
351 		    next = diff = cur;
352 		else {
353 		    next = diff + 4;
354 		    while (next < cur && *next == value)
355 			next++;
356 		}
357 
358 #define MAXOFFSETU 15
359 #define MAXCOUNTU 7
360 		/* output 'dissimilar' bytes, uncompressed */
361 		if ((dissimilar = diff - compr)) {
362 		    int temp, i;
363 
364 		    if ((temp = --dissimilar) > MAXCOUNTU)
365 			temp = MAXCOUNTU;
366 		    if (offset < MAXOFFSETU)
367 			*out++ = (offset << 3) | (byte) temp;
368 		    else {
369 			*out++ = (MAXOFFSETU << 3) | (byte) temp;
370 			offset -= MAXOFFSETU;
371 			while (offset >= 255) {
372 			    *out++ = 255;
373 			    offset -= 255;
374 			}
375 			*out++ = offset;
376 		    }
377 		    if (temp == MAXCOUNTU) {
378 			temp = dissimilar - MAXCOUNTU;
379 			while (temp >= 255) {
380 			    *out++ = 255;
381 			    temp -= 255;
382 			}
383 			*out++ = (byte) temp;
384 		    }
385 		    for (i = 0; i <= dissimilar; i++)
386 			*out++ = *compr++;
387 		    offset = 0;
388 		}		/* end uncompressed */
389 #undef MAXOFFSETU
390 #undef MAXCOUNTU
391 
392 #define MAXOFFSETC 3
393 #define MAXCOUNTC 31
394 		/* output 'similar' bytes, run-length encoded */
395 		if ((similar = next - diff)) {
396 		    int temp;
397 
398 		    if ((temp = (similar -= 2)) > MAXCOUNTC)
399 			temp = MAXCOUNTC;
400 		    if (offset < MAXOFFSETC)
401 			*out++ = 0x80 | (offset << 5) | (byte) temp;
402 		    else {
403 			*out++ = 0x80 | (MAXOFFSETC << 5) | (byte) temp;
404 			offset -= MAXOFFSETC;
405 			while (offset >= 255) {
406 			    *out++ = 255;
407 			    offset -= 255;
408 			}
409 			*out++ = offset;
410 		    }
411 		    if (temp == MAXCOUNTC) {
412 			temp = similar - MAXCOUNTC;
413 			while (temp >= 255) {
414 			    *out++ = 255;
415 			    temp -= 255;
416 			}
417 			*out++ = (byte) temp;
418 		    }
419 		    *out++ = value;
420 		    offset = 0;
421 		}		/* end compressed */
422 #undef MAXOFFSETC
423 #undef MAXCOUNTC
424 
425 		diff = next;
426 	    }
427 	}
428     }
429     return out - compressed;
430 }
431