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