/src/ghostpdl/contrib/pcl3/src/pclgen.c
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | File: $Id: pclgen.c,v 1.21 2001/04/29 10:37:08 Martin Rel $ |
3 | | Contents: PCL-generating routines |
4 | | Author: Martin Lottermoser, Greifswaldstrasse 28, 38124 Braunschweig, |
5 | | Germany. E-mail: Martin.Lottermoser@t-online.de. |
6 | | |
7 | | ******************************************************************************* |
8 | | * * |
9 | | * Copyright (C) 1999, 2000, 2001 by Martin Lottermoser * |
10 | | * All rights reserved * |
11 | | * * |
12 | | ******************************************************************************* |
13 | | |
14 | | In the implementation of these and other functions I have mainly used the |
15 | | following documents: |
16 | | |
17 | | - Hewlett-Packard |
18 | | "Technical Reference Guide for the HP DeskJet 500 Series Printers" |
19 | | First edition, October 1994 |
20 | | Manual Part Number: C2170-90099 |
21 | | (Quoted as "TRG500") |
22 | | - Hewlett-Packard |
23 | | "Hewlett-Packard 300 and 400 Series DeskJet Printers - Software Developer's |
24 | | Guide" |
25 | | January 1996 |
26 | | (Quoted as "DJ3/4") |
27 | | - Hewlett-Packard |
28 | | "HP DeskJet 600/800 Series Printers - Software Developer's PCL Guide" |
29 | | Fifth edition, October 1997 |
30 | | (Quoted as "DJ6/8") |
31 | | - Hewlett-Packard |
32 | | "DeskJet 1120C Printer - Software Developer's PCL Guide" |
33 | | First printing, December 1997. Version 1.0. |
34 | | (Quoted as "DJ1120C") |
35 | | - Hewlett-Packard |
36 | | "Printer Job Language Technical Reference Manual" |
37 | | Edition 10, October 1997. HP Part No. 5021-0380. |
38 | | (Quoted as "PJLTRM") |
39 | | - Lexmark |
40 | | "Printer Technical Reference, Version 1.1" |
41 | | First edition, February 1999 |
42 | | (Quoted as "Lexmark-PTR". It deals with PCL 5 and PCL 6.) |
43 | | |
44 | | In addition, some other documents are quoted in a form like "BPD02926". These |
45 | | were obtained from http://www.hp.com, usually from the directory |
46 | | cposupport/printers/support_doc. BPD02926, for example, (a short description |
47 | | of PCL commands for series-800 DeskJets) could be found as the file |
48 | | bpd02926.html in that directory. |
49 | | |
50 | | ******************************************************************************/ |
51 | | |
52 | | /*****************************************************************************/ |
53 | | |
54 | | #ifndef _XOPEN_SOURCE |
55 | | #define _XOPEN_SOURCE 500 |
56 | | #endif |
57 | | |
58 | | /* Standard headers */ |
59 | | #include <assert.h> |
60 | | #include <errno.h> |
61 | | #include <stdlib.h> |
62 | | #include <string.h> |
63 | | |
64 | | /* Special headers */ |
65 | | #include "pclgen.h" |
66 | | |
67 | | /*****************************************************************************/ |
68 | | |
69 | | /* Prefix for error messages */ |
70 | 0 | #define ERRPREF "? pclgen: " |
71 | | |
72 | | /* The usual array-size macro */ |
73 | | #define array_size(a) (sizeof(a)/sizeof(a[0])) |
74 | | |
75 | | /* Macro to check whether an octet is an ASCII letter. Note that we can't use |
76 | | the isalpha() function because we might be operating in an internationalized |
77 | | environment. */ |
78 | 0 | #define is_letter(c) ((0x41 <= (c) && (c) <= 0x5A) || (0x61 <= (c) && (c) <= 0x7A)) |
79 | | |
80 | | /* Same for digits */ |
81 | 0 | #define is_digit(c) (0x30 <= (c) && (c) <= 0x39) |
82 | | |
83 | | /****************************************************************************** |
84 | | |
85 | | Function: pcl3_levels_to_planes |
86 | | |
87 | | This function returns ceiling(ld(levels)), the number of bitplanes needed to |
88 | | store the specified number of intensity levels, or 0 if 'levels' is zero. |
89 | | 'levels' must be <= (ULONG_MAX+1)/2. |
90 | | |
91 | | ******************************************************************************/ |
92 | | |
93 | | unsigned int pcl3_levels_to_planes(unsigned int levels) |
94 | 0 | { |
95 | 0 | unsigned long |
96 | 0 | power = 1; |
97 | 0 | unsigned int |
98 | 0 | planes = 0; |
99 | | /* power == 2^planes */ |
100 | |
|
101 | 0 | while (power < levels) { |
102 | 0 | power *= 2; /* multiplication is faster than division */ |
103 | 0 | planes++; |
104 | 0 | } |
105 | | /* levels == 0 or 2^(planes-1) < levels <= 2^planes */ |
106 | |
|
107 | 0 | return planes; |
108 | 0 | } |
109 | | |
110 | | /****************************************************************************** |
111 | | |
112 | | Function: send_ERG |
113 | | |
114 | | This function sends an "End Raster Graphics" command to 'out', choosing its |
115 | | form according to 'level'. |
116 | | |
117 | | ******************************************************************************/ |
118 | | |
119 | | static void send_ERG(gp_file *out, pcl_Level level) |
120 | 0 | { |
121 | | /* PCL: End Raster Graphics/End Raster */ |
122 | 0 | gp_fputs("\033*r", out); |
123 | 0 | if (pcl_use_oldERG(level)) gp_fputc('B', out); |
124 | 0 | else if (level == pcl_level_3plus_ERG_both) gp_fputs("bC", out); |
125 | 0 | else gp_fputc('C', out); |
126 | |
|
127 | 0 | return; |
128 | 0 | } |
129 | | |
130 | | /****************************************************************************** |
131 | | |
132 | | Function: pcl3_init_file |
133 | | |
134 | | This function must be called to initialize the printer before the first page. |
135 | | The 'data' should remain constant within a page. Whenever one changes 'data' |
136 | | between pages, this function should be called again. |
137 | | |
138 | | The function follows the return code rules described in pclgen.h. It will |
139 | | perform a number of validity checks on 'data'. No checks will be performed on |
140 | | fields like 'print_quality' which can be passed to the printer without having |
141 | | to be interpreted by this component. |
142 | | |
143 | | The routine will configure the printer such that the top left corner of the |
144 | | logical page is the top left corner of the printable area in raster space. |
145 | | |
146 | | The file 'out' should be a binary file for this and all other output |
147 | | functions. |
148 | | |
149 | | ******************************************************************************/ |
150 | | |
151 | | int pcl3_init_file(gs_memory_t *mem, gp_file *out, pcl_FileData *data) |
152 | 0 | { |
153 | 0 | pcl_bool needs_CRD = (data && data->level == pcl_level_3plus_CRD_only); |
154 | | /* Do we need Configure Raster Data? */ |
155 | 0 | int j; |
156 | 0 | const pcl_ColorantState *colorant = NULL; |
157 | 0 | unsigned int maxhres = 0, maxvres = 0; /* maximal resolutions in ppi */ |
158 | | |
159 | | /* Check validity of the arguments */ |
160 | 0 | { |
161 | 0 | pcl_bool invalid; |
162 | |
|
163 | 0 | invalid = (out == NULL || data == NULL); |
164 | 0 | if (invalid) |
165 | 0 | errprintf(mem, ERRPREF "Null pointer passed to pcl3_init_file().\n"); |
166 | 0 | else { |
167 | | /* Palette und colorants */ |
168 | 0 | switch(data->palette) { |
169 | 0 | case pcl_no_palette: |
170 | | /*FALLTHROUGH*/ |
171 | 0 | case pcl_black: invalid = data->number_of_colorants != 1; break; |
172 | 0 | case pcl_CMY: invalid = data->number_of_colorants != 3; break; |
173 | 0 | case pcl_RGB: invalid = data->number_of_colorants != 3; break; |
174 | 0 | case pcl_CMYK: invalid = data->number_of_colorants != 4; break; |
175 | 0 | default: invalid = data->number_of_colorants <= 0; |
176 | 0 | } |
177 | 0 | if (invalid) |
178 | 0 | errprintf(mem, ERRPREF |
179 | 0 | "Palette specification and number of colorants are inconsistent.\n"); |
180 | 0 | else { |
181 | 0 | if (data->colorant == NULL) colorant = data->colorant_array; |
182 | 0 | else colorant = data->colorant; |
183 | | |
184 | | /* First pass over colorants: find minimal and maximal resolutions, |
185 | | check the number of intensity levels */ |
186 | 0 | data->minvres = colorant[0].vres; |
187 | 0 | for (j = 0; j < data->number_of_colorants; j++) { |
188 | 0 | if (colorant[j].hres <= 0 || colorant[j].vres <= 0) { |
189 | 0 | invalid = TRUE; |
190 | 0 | errprintf(mem, ERRPREF |
191 | 0 | "The resolution for colorant %d is not positive: %u x %u ppi.\n", |
192 | 0 | j, colorant[j].hres, colorant[j].vres); |
193 | 0 | } |
194 | 0 | else { |
195 | 0 | if (colorant[j].vres < data->minvres) |
196 | 0 | data->minvres = colorant[j].vres; |
197 | 0 | if (colorant[j].hres > maxhres) maxhres = colorant[j].hres; |
198 | 0 | if (colorant[j].vres > maxvres) maxvres = colorant[j].vres; |
199 | 0 | } |
200 | 0 | if (colorant[j].levels < 2 || 0xFFFF < colorant[j].levels) { |
201 | 0 | invalid = TRUE; |
202 | 0 | errprintf(mem, ERRPREF "The number of intensity levels for " |
203 | 0 | "colorant %d is %u instead of at least 2 and at most 65535.\n", |
204 | 0 | j, colorant[j].levels); |
205 | | /* Actually, DJ6/8 p. 68 requires the levels to be in the range |
206 | | 2-255, but as there are two octets available in the CRD format |
207 | | this seems unnecessarily strict. */ |
208 | 0 | } |
209 | 0 | } |
210 | | |
211 | | /* Check for relations between resolutions and the need for CRD */ |
212 | 0 | if (!invalid) /* this implies that all resolutions are positive */ |
213 | 0 | for (j = 0; j < data->number_of_colorants; j++) { |
214 | | /* If there is more than one resolution or more than 2 levels we |
215 | | need CRD. Note that we compare with 'maxhres' in both lines: */ |
216 | 0 | if (maxhres != colorant[j].hres || |
217 | 0 | maxhres != colorant[j].vres || colorant[j].levels > 2) |
218 | 0 | needs_CRD = TRUE; |
219 | | |
220 | | /* DJ6/8 states that the highest horizontal resolution must be a |
221 | | multiple of all lower horizontal resolutions and the same for |
222 | | the set of vertical resolutions. The reason is not given but |
223 | | these properties imply that it is possible to operate uniformly |
224 | | on a grid at the highest resolution. |
225 | | In contrast, the way in which raster data are transferred |
226 | | (strips with a height equal to the reciprocal of the lowest |
227 | | vertical resolution) logically requires that all vertical |
228 | | resolutions are multiples of the lowest vertical resolution. |
229 | | This condition is not stated in DJ6/8 but it is satisfied by |
230 | | all examples given in that document. This includes example 4 on |
231 | | page 72 which is meant to illustrate the maximum flexibility of |
232 | | format 2 and which describes a situation where the |
233 | | corresponding condition does not hold for the horizontal |
234 | | resolutions. |
235 | | */ |
236 | 0 | if (colorant[j].vres % data->minvres != 0) { |
237 | 0 | invalid = TRUE; |
238 | 0 | errprintf(mem, ERRPREF |
239 | 0 | "The vertical resolution for colorant %d (%u ppi) is not a " |
240 | 0 | "multiple of the lowest vertical resolution (%u ppi).\n", |
241 | 0 | j, colorant[j].vres, data->minvres); |
242 | 0 | } |
243 | 0 | if (maxhres % colorant[j].hres != 0) { |
244 | 0 | invalid = TRUE; |
245 | 0 | errprintf(mem, ERRPREF |
246 | 0 | "The highest horizontal resolution (%u ppi) is not a multiple " |
247 | 0 | "of the horizontal resolution for colorant %d (%u ppi).\n", |
248 | 0 | maxhres, j, colorant[j].hres); |
249 | 0 | } |
250 | 0 | if (maxvres % colorant[j].vres != 0) { |
251 | 0 | invalid = TRUE; |
252 | 0 | errprintf(mem, ERRPREF |
253 | 0 | "The highest vertical resolution (%u ppi) is not a multiple " |
254 | 0 | "of the vertical resolution for colorant %d (%u ppi).\n", |
255 | 0 | maxvres, j, colorant[j].vres); |
256 | 0 | } |
257 | 0 | } |
258 | 0 | } |
259 | |
|
260 | 0 | if (needs_CRD && data->palette == pcl_RGB) { |
261 | 0 | invalid = TRUE; |
262 | 0 | if (data->level == pcl_level_3plus_CRD_only) |
263 | 0 | errprintf(mem, ERRPREF |
264 | 0 | "You can't use an RGB palette at the requested PCL level.\n"); |
265 | 0 | else |
266 | 0 | errprintf(mem, ERRPREF "The specified structure of resolutions and intensity " |
267 | 0 | "levels is not possible with an RGB palette.\n"); |
268 | 0 | } |
269 | 0 | if (needs_CRD && !pcl_has_CRD(data->level)) { |
270 | 0 | invalid = TRUE; |
271 | 0 | errprintf(mem, ERRPREF "The specified structure of resolutions and intensity " |
272 | 0 | "levels is not possible at the requested PCL level.\n"); |
273 | 0 | } |
274 | 0 | if (data->palette == pcl_any_palette) { |
275 | 0 | needs_CRD = TRUE; |
276 | 0 | if (!pcl_has_CRD(data->level)) { |
277 | 0 | invalid = TRUE; |
278 | 0 | errprintf(mem, ERRPREF "The specified palette is not possible at the " |
279 | 0 | "requested PCL level.\n"); |
280 | 0 | } |
281 | 0 | } |
282 | 0 | if (needs_CRD && (maxhres > 0xFFFF || maxvres > 0xFFFF)) { |
283 | 0 | errprintf(mem, ERRPREF "Resolutions may be at most 65535 ppi when more than one " |
284 | 0 | "resolution or more than two intensity levels are requested.\n"); |
285 | 0 | invalid = TRUE; |
286 | 0 | } |
287 | 0 | if (data->order_CMYK && data->palette != pcl_CMYK) { |
288 | 0 | errprintf(mem, ERRPREF |
289 | 0 | "Ordering bit planes as CMYK instead of KCMY is only meaningful\n" |
290 | 0 | " for a CMYK palette.\n"); |
291 | 0 | invalid = TRUE; |
292 | 0 | } |
293 | | |
294 | | /* Check PJL job name */ |
295 | 0 | if (data->PJL_job != NULL) { |
296 | 0 | const unsigned char *s = (const unsigned char *)data->PJL_job; |
297 | | |
298 | | /* Permissible characters are HT and the octets 32-255 with the |
299 | | exception of '"' (PJLTRM, with some corrections). */ |
300 | 0 | while (*s != '\0' && (*s == '\t' || (32 <= *s && *s != '"'))) s++; |
301 | 0 | if (*s != '\0') { |
302 | 0 | errprintf(mem, |
303 | 0 | ERRPREF "Illegal character in PJL job name (code 0x%02X).\n", *s); |
304 | 0 | invalid = TRUE; |
305 | 0 | } |
306 | | |
307 | | /* There is a maximum of 80 "significant characters" (PJLTRM). |
308 | | According to PJLTRM, p. D-8, "String too long" is a parser warning |
309 | | and leads to a part of the command being ignored. I play it safe. |
310 | | There would also be a warning for an empty string but we treat that |
311 | | case differently anyway (see below). */ |
312 | 0 | if (strlen(data->PJL_job) > 80) { |
313 | 0 | errprintf(mem, ERRPREF "PJL job name is too long (more than 80 characters).\n"); |
314 | 0 | invalid = TRUE; |
315 | 0 | } |
316 | 0 | } |
317 | | |
318 | | /* Check PJL personality name */ |
319 | 0 | if (data->PJL_language != NULL) { |
320 | 0 | const char *s = data->PJL_language; |
321 | | |
322 | | /* PJLTRM does not give explicit lexical conventions for personality |
323 | | names but from general considerations it should be an "alphanumeric |
324 | | variable". The latter must start with a letter and may consist of |
325 | | letters and digits. */ |
326 | 0 | if (is_letter(*s)) do s++; while (is_letter(*s) || is_digit(*s)); |
327 | |
|
328 | 0 | if (*data->PJL_language == '\0') { |
329 | 0 | errprintf(mem, ERRPREF "Empty PJL language name.\n"); |
330 | 0 | invalid = TRUE; |
331 | 0 | } |
332 | 0 | else if (*s != '\0') { |
333 | 0 | errprintf(mem, |
334 | 0 | ERRPREF "Illegal character in PJL language name (code 0x%02X).\n", |
335 | 0 | *s); |
336 | 0 | invalid = TRUE; |
337 | 0 | } |
338 | 0 | } |
339 | 0 | } |
340 | | |
341 | 0 | if (invalid) return +1; |
342 | 0 | } |
343 | | |
344 | | /* Practically every output file from an HP driver I have seen starts with |
345 | | 600 and one even with 9600 NUL characters. This seems unnecessary |
346 | | and is undocumented, but just in case that there are situations where it |
347 | | might make a difference, this module provides the capability. */ |
348 | 0 | for (j = 0; j < data->NULs_to_send; j++) gp_fputc('\0', out); |
349 | | |
350 | | /* Issue PJL commands if requested. Most newer HP drivers for PCL-3 printers |
351 | | follow the NULs with a PJL statement to enter the PCL language |
352 | | interpreter. The language name varies (so far I've seen PCL3GUI and |
353 | | PCLSLEEK). Some drivers also generate JOB/EOJ statements. |
354 | | Interestingly enough, I have seen output from two HP drivers which, at |
355 | | the beginning of the job, generated first Printer Reset and then UEL. |
356 | | This is wrong (PJLTRM). |
357 | | */ |
358 | 0 | if (data->PJL_job != NULL || data->PJL_language != NULL) { |
359 | 0 | gp_fputs("\033%-12345X", out); /* Universal Exit Language (UEL) */ |
360 | | |
361 | | /* Start of job */ |
362 | 0 | if (data->PJL_job != NULL) { |
363 | 0 | gp_fputs("@PJL JOB", out); |
364 | 0 | if (*data->PJL_job != '\0') gp_fprintf(out, " NAME=\"%s\"", data->PJL_job); |
365 | 0 | gp_fputc('\n', out); |
366 | 0 | } |
367 | | |
368 | | /* Switch personality */ |
369 | 0 | if (data->PJL_language != NULL) |
370 | 0 | gp_fprintf(out, "@PJL ENTER LANGUAGE=%s\n", data->PJL_language); |
371 | 0 | } |
372 | | |
373 | | /* PCL: Printer Reset */ |
374 | 0 | gp_fputs("\033E", out); |
375 | | |
376 | | /* Additional initialization */ |
377 | 0 | if (data->init1.length > 0) |
378 | 0 | gp_fwrite(data->init1.str, sizeof(pcl_Octet), data->init1.length, out); |
379 | | |
380 | | /* Page layout initialization */ |
381 | 0 | gp_fprintf(out, |
382 | 0 | "\033&l%da" /* PCL: Page Size */ |
383 | 0 | "0o" /* PCL: Page Orientation/Orientation: portrait */ |
384 | 0 | "0L", /* PCL: Perforation Skip Mode: off. This also effectively sets |
385 | | the PCL top margin to zero. */ |
386 | 0 | (int) data->size |
387 | 0 | ); |
388 | | |
389 | | /* Media source */ |
390 | 0 | if (data->media_source != 0) |
391 | 0 | gp_fprintf(out, "\033&l%dH", data->media_source); /* PCL: Media Source */ |
392 | | /* Note that a value of zero for the Media Source command means |
393 | | "eject the current page". Hence we are losing no functionality by |
394 | | reserving 0 to mean "no Media Source request". */ |
395 | 0 | if (data->media_source != 2 && data->manual_feed) gp_fputs("\033&l2H", out); |
396 | | /* I am using two Media Source commands here in case the value 2 means |
397 | | "manual feed from the last selected media source" on some printer. */ |
398 | | |
399 | | /* Media destination. This is included here merely in the hope that, if a |
400 | | PCL-3 printer should support such a feature, the command used is the same |
401 | | as in PCL 5/6. At present, I know of no such printer. */ |
402 | 0 | if (data->media_destination != 0) |
403 | 0 | gp_fprintf(out, "\033&l%dG", data->media_destination); |
404 | | /* PCL: Paper Destination (BPL02705). A value of zero means "auto select". |
405 | | */ |
406 | | |
407 | | /* Duplex. Again, I have only PCL-5 documentation for this, but in this case |
408 | | I know that the DJ 970C uses the command. */ |
409 | 0 | if (data->duplex != -1) |
410 | 0 | gp_fprintf(out, "\033&l%dS", data->duplex); /* PCL: Simplex/Duplex Print */ |
411 | | |
412 | | /* Print quality */ |
413 | 0 | if (pcl_use_oldquality(data->level)) { |
414 | 0 | gp_fprintf(out, "\033*r%dQ", data->raster_graphics_quality); |
415 | | /* PCL: Raster Graphics Quality */ |
416 | 0 | if (data->level > pcl_level_3plus_DJ500) |
417 | 0 | gp_fprintf(out, "\033*o%dQ", data->shingling); |
418 | | /* PCL: Set Raster Graphics Shingling/Mechanical Print Quality */ |
419 | 0 | if (data->depletion != 0) |
420 | | /* According to TRG500 p. 6-32, depletion makes no sense for monochrome |
421 | | data. Besides, not all printers react to this command. Hence I'm |
422 | | handing the decision to the caller. Note that permitted values are 1-5. |
423 | | */ |
424 | 0 | gp_fprintf(out, "\033*o%dD", data->depletion); |
425 | | /* PCL: Set Raster Graphics Depletion */ |
426 | 0 | } |
427 | 0 | else |
428 | 0 | gp_fprintf(out, |
429 | 0 | "\033&l%dM" /* PCL: Media Type */ |
430 | 0 | "\033*o%dM", /* PCL: Print Quality */ |
431 | 0 | data->media_type, |
432 | 0 | data->print_quality |
433 | 0 | ); |
434 | | |
435 | | /* PCL: Set Dry Time/Dry Timer. This command is ignored by newer printers |
436 | | and is obsolete for every printer supporting the new Media Type and Print |
437 | | Quality commands. Because I am uncertain about what "current" means in |
438 | | the description of the value 0 ("default dry time for the current print |
439 | | quality", TRG500 p. 2-4), I'm putting the command here after the quality |
440 | | commands. */ |
441 | 0 | if (data->dry_time >= 0) gp_fprintf(out, "\033&b%dT", data->dry_time); |
442 | | |
443 | | /* End Raster Graphics. This provides a known graphics state, see |
444 | | TRG500 p. 6-25, but is probably superfluous here because of the Printer |
445 | | Reset command. */ |
446 | 0 | send_ERG(out, data->level); |
447 | |
|
448 | 0 | if (data->level != pcl_level_3plus_CRD_only) |
449 | | /* PCL: Set Raster Graphics Resolution/Raster Resolution */ |
450 | 0 | gp_fprintf(out, "\033*t%uR", maxhres < maxvres? maxvres: maxhres); |
451 | | /* If different x and y resolutions have been demanded but the printer does |
452 | | not support the combination, choosing the larger value here will prevent |
453 | | printing beyond the sheet---provided the printer accepts this resolution. |
454 | | */ |
455 | | |
456 | | /* Set PCL unit to reciprocal of largest resolution */ |
457 | 0 | if (data->level >= pcl_level_3plus_S68) |
458 | 0 | gp_fprintf(out, "\033&u%uD", maxhres < maxvres? maxvres: maxhres); |
459 | | /* PCL: Unit of Measure. This is not documented but merely mentioned in |
460 | | DJ6/8. All HP drivers for newer printers I've looked at (admittedly not |
461 | | many) generate this command, including a driver for the DJ 540. |
462 | | Actually, as the routines here send a Move CAP Horizontal/Vertical |
463 | | (PCL Units) command only for position 0, the units should be irrelevant. |
464 | | */ |
465 | | |
466 | | /* Colour planes */ |
467 | 0 | if (data->level != pcl_level_3plus_CRD_only && |
468 | 0 | data->palette != pcl_no_palette && data->palette != pcl_any_palette) |
469 | 0 | gp_fprintf(out, "\033*r%dU", |
470 | | /* PCL: Set Number of Planes per Row/Simple Color */ |
471 | 0 | data->palette == pcl_RGB? 3: /* RGB palette */ |
472 | 0 | -(int)data->number_of_colorants); /* (K)(CMY) palette */ |
473 | | |
474 | | /* Configure Raster Data */ |
475 | 0 | if (needs_CRD) { |
476 | 0 | gp_fprintf(out, "\033*g%uW" /* PCL: Configure Raster Data */ |
477 | 0 | "\002%c", /* Format 2: Complex Direct Planar */ |
478 | 0 | 2 + 6*data->number_of_colorants, data->number_of_colorants); |
479 | |
|
480 | 0 | for (j = 0; j < data->number_of_colorants; j++) |
481 | 0 | gp_fprintf(out, "%c%c%c%c%c%c", |
482 | | /* Note that %c expects an 'int' (and converts it to 'unsigned char'). |
483 | | */ |
484 | 0 | colorant[j].hres/256, colorant[j].hres%256, |
485 | 0 | colorant[j].vres/256, colorant[j].vres%256, |
486 | 0 | colorant[j].levels/256, colorant[j].levels%256); |
487 | 0 | } |
488 | |
|
489 | 0 | if (gp_ferror(out)) { |
490 | 0 | errprintf(mem, ERRPREF "Unidentified system error while writing the output file.\n"); |
491 | 0 | return -1; |
492 | 0 | } |
493 | | |
494 | | /* Additional initialization */ |
495 | 0 | if (data->init2.length > 0) |
496 | 0 | gp_fwrite(data->init2.str, sizeof(pcl_Octet), data->init2.length, out); |
497 | | |
498 | | /* Determine and set the number of bit planes */ |
499 | 0 | if (data->palette == pcl_CMY || data->palette == pcl_RGB) |
500 | 0 | data->black_planes = 0; |
501 | 0 | else data->black_planes = |
502 | 0 | pcl3_levels_to_planes(colorant[0].levels)*(colorant[0].vres/data->minvres); |
503 | 0 | data->number_of_bitplanes = 0; |
504 | 0 | for (j = 0; j < data->number_of_colorants; j++) |
505 | 0 | data->number_of_bitplanes += |
506 | 0 | pcl3_levels_to_planes(colorant[j].levels)* |
507 | 0 | (colorant[j].vres/data->minvres); |
508 | |
|
509 | 0 | return 0; |
510 | 0 | } |
511 | | |
512 | | /****************************************************************************** |
513 | | |
514 | | Function: pcl3_begin_page |
515 | | |
516 | | This function sets CAP on the top margin of the logical page. It may only |
517 | | be called after pcl3_init_file() and not while a page is still open. |
518 | | |
519 | | ******************************************************************************/ |
520 | | |
521 | | int pcl3_begin_page(gp_file *out, pcl_FileData *global) |
522 | 0 | { |
523 | 0 | gp_fputs("\033*p0Y", out); |
524 | | /* PCL: Vertical Cursor Positioning by Dots/Move CAP Vertical (PCL Units) */ |
525 | |
|
526 | 0 | return 0; |
527 | 0 | } |
528 | | |
529 | | /****************************************************************************** |
530 | | |
531 | | Function: pcl3_begin_raster |
532 | | |
533 | | This function starts raster mode at the left margin of the logical page |
534 | | and at the current height. |
535 | | |
536 | | The function may only be called within a page and not in raster mode. |
537 | | |
538 | | The raster data structure '*data' will be checked for validity and parts of |
539 | | the data may be modified. In particular, the caller should already have |
540 | | allocated appropriate storage in the 'next' and, for differential compression |
541 | | methods, 'previous' arrays. Until raster mode is terminated, the caller may |
542 | | only modify the storage currently pointed to by the pointers in 'next' and |
543 | | their corresponding length fields. |
544 | | |
545 | | ******************************************************************************/ |
546 | | |
547 | | int pcl3_begin_raster(gp_file *out, pcl_RasterData *data) |
548 | 0 | { |
549 | 0 | const pcl_FileData *global = NULL; |
550 | 0 | int j; |
551 | 0 | size_t seed_size = 0; |
552 | | |
553 | | /* Check 'data' for validity */ |
554 | 0 | { |
555 | 0 | pcl_bool invalid; |
556 | |
|
557 | 0 | invalid = (data == NULL || data->global == NULL || data->next == NULL || |
558 | 0 | data->workspace[0] == NULL || data->workspace_allocated <= 0); |
559 | 0 | if (!invalid) { |
560 | 0 | global = data->global; |
561 | |
|
562 | 0 | for (j = 0; |
563 | 0 | j < global->number_of_bitplanes && |
564 | 0 | (data->next[j].length == 0 || data->next[j].str != NULL); |
565 | 0 | j++); |
566 | 0 | invalid = j < global->number_of_bitplanes; |
567 | 0 | if (!invalid && pcl_cm_is_differential(global->compression)) { |
568 | 0 | invalid = (data->previous == NULL || |
569 | 0 | (global->compression == pcl_cm_delta && |
570 | 0 | data->workspace[1] == NULL)); |
571 | 0 | if (!invalid) { |
572 | 0 | for (j = 0; |
573 | 0 | j < global->number_of_bitplanes && |
574 | 0 | (data->previous[j].length == 0 || data->previous[j].str != NULL); |
575 | 0 | j++); |
576 | 0 | invalid = j < global->number_of_bitplanes; |
577 | 0 | } |
578 | 0 | } |
579 | 0 | } |
580 | |
|
581 | 0 | if (invalid) { |
582 | 0 | errprintf(out->memory, ERRPREF "Invalid data structure passed to pcl3_begin_raster().\n"); |
583 | 0 | return +1; |
584 | 0 | } |
585 | 0 | } |
586 | | |
587 | | /* Allocate the seed plane array */ |
588 | 0 | seed_size = (size_t)global->number_of_bitplanes*sizeof(pcl_OctetString *); |
589 | 0 | data->seed_plane = (pcl_OctetString **)malloc(seed_size); |
590 | 0 | if (data->seed_plane == NULL) { |
591 | 0 | errprintf(out->memory, ERRPREF "Memory allocation failure in pcl3_begin_raster().\n"); |
592 | 0 | return -1; |
593 | 0 | } |
594 | 0 | memset(data->seed_plane, 0, |
595 | 0 | seed_size); |
596 | | |
597 | | /* Use the seed plane array for differential compression methods */ |
598 | 0 | if (pcl_cm_is_differential(global->compression)) { |
599 | | /* HP's documentation is a bit obscure concerning what the seed plane for |
600 | | a particular bit plane is. Most of the documentation talks only about |
601 | | seed rows (and "rows" consist of planes), but then one suddenly comes |
602 | | across a statement like "a separate seed row is maintained for each |
603 | | graphic plane" (DJ6/8 p. 57). I've also never found a statement what |
604 | | the seed row/plane/whatever for a bitplane in a strip group with |
605 | | multiple lines or multiple planes per colorant is, except that DJ6/8 |
606 | | (p. 60) states explicitly that one cannot use Seed Row Source in that |
607 | | situation to select it. |
608 | | |
609 | | I therefore have to make a few assumptions. The following seem sensible: |
610 | | - The PCL interpreter maintains independent "seed lines" for each |
611 | | colorant. Each line consists of independent seed planes, one for each |
612 | | bit plane in a pixel line for that colorant (i.e., there are |
613 | | pcl3_levels_to_planes(levels) seed planes for a colorant accepting |
614 | | 'levels' intensity levels). |
615 | | - If the current compression method (this is a global property of the |
616 | | interpreter) is a differential one, a bit plane is interpreted as a |
617 | | delta plane with respect to the corresponding bit plane in the |
618 | | current colorant's preceding line. |
619 | | */ |
620 | |
|
621 | 0 | int strip; |
622 | 0 | const pcl_ColorantState *colorant = NULL; |
623 | 0 | int plane = 0; |
624 | |
|
625 | 0 | if (global->colorant == NULL) colorant = global->colorant_array; |
626 | 0 | else colorant = global->colorant; |
627 | |
|
628 | 0 | for (strip = 0; strip < global->number_of_colorants; strip++) { |
629 | 0 | int lines = colorant[strip].vres/global->minvres; |
630 | 0 | int planes = pcl3_levels_to_planes(colorant[strip].levels); |
631 | 0 | int l, p; |
632 | | |
633 | | /* The first line of the colorant strip refers to the last line in the |
634 | | preceding strip. I'm assuming Seed Row Source == 0 here. */ |
635 | 0 | for (p = 0; p < planes; p++, plane++) |
636 | 0 | data->seed_plane[plane] = data->previous + plane + (lines - 1)*planes; |
637 | | |
638 | | /* Subsequent lines refer to the preceding line in the current strip |
639 | | group */ |
640 | 0 | for (l = 1; l < lines; l++) |
641 | 0 | for (p = 0; p < planes; p++, plane++) |
642 | 0 | data->seed_plane[plane] = data->next + plane - planes; |
643 | 0 | } |
644 | 0 | } |
645 | | |
646 | | /* Start raster mode */ |
647 | 0 | if (data->width > 0) { |
648 | 0 | gp_fprintf(out, "\033*r%uS", data->width); |
649 | | /* PCL: Set Raster Graphics Width/Source Raster Width. The value is the |
650 | | number of pixels at the lowest horizontal resolution (see DJ6/8 p. 66). |
651 | | This is reset by End Raster Graphics. */ |
652 | 0 | } |
653 | 0 | gp_fputs("\033*p0X" /* PCL: Horizontal Cursor Positioning by Dots */ |
654 | 0 | "\033*r1A", out); |
655 | | /* PCL: Start Raster Graphics: at current position */ |
656 | | |
657 | | /* After Start Raster Graphics the seed row consists of all zeroes |
658 | | (DJ6/8 p. 50). */ |
659 | 0 | if (pcl_cm_is_differential(global->compression)) |
660 | 0 | for (j = 0; j < global->number_of_bitplanes; j++) |
661 | 0 | data->previous[j].length = 0; |
662 | |
|
663 | 0 | gp_fputs("\033*b", out); |
664 | | /* We use combined escape sequences, all with this prefix. */ |
665 | | |
666 | | /* The old End Raster Graphics command (with 'B') does not reset the |
667 | | compression method to 'pcl_cm_none'. We could keep track of the current |
668 | | compression method, but this would mean that we then have to track also |
669 | | the Printer Reset command (DJ6/8 p. 55). It's easier to set the method |
670 | | explicitly here. The worst which could happen is that in the case of |
671 | | old printers we generate two consecutive commands the first of which is |
672 | | superfluous. */ |
673 | 0 | if (pcl_use_oldERG(global->level)) { |
674 | | /* Raster Graphics Compression Method */ |
675 | 0 | gp_fprintf(out, "%dm", (int)global->compression); |
676 | 0 | data->current_compression = global->compression; |
677 | 0 | } |
678 | 0 | else data->current_compression = pcl_cm_none; |
679 | |
|
680 | 0 | return 0; |
681 | 0 | } |
682 | | |
683 | | /****************************************************************************** |
684 | | |
685 | | Function: pcl3_skip_groups |
686 | | |
687 | | Routine to skip a number of strip groups. 'count' is the number of strip |
688 | | groups (vertical distance in units of the reciprocal of the lowest vertical |
689 | | raster resolution). |
690 | | |
691 | | This function may only be called in raster mode. |
692 | | |
693 | | ******************************************************************************/ |
694 | | |
695 | | int pcl3_skip_groups(gp_file *out, pcl_RasterData *data, unsigned int count) |
696 | 0 | { |
697 | 0 | int j; |
698 | | |
699 | | /* I don't know what happens with the seed row when 'count' is zero, but as |
700 | | the command is superfluous in that case anyway I'm ignoring it. */ |
701 | 0 | if (count == 0) return 0; |
702 | | |
703 | 0 | gp_fprintf(out, "%uy", count); |
704 | | /* PCL: Relative Vertical Pixel Movement/Y Offset. |
705 | | The statement in DJ6/8, p. 52, that "this command zero-fills the offset |
706 | | area" is incorrect. This can be seen when printing with an RGB palette on |
707 | | a DJ 850C where it results in a white area, not a black one. Hence it |
708 | | really skips these groups. */ |
709 | | |
710 | | /* This command has zeroed the seed row (DJ6/8 p. 52). */ |
711 | 0 | if (pcl_cm_is_differential(data->global->compression)) |
712 | 0 | for (j = 0; j < data->global->number_of_bitplanes; j++) |
713 | 0 | data->previous[j].length = 0; |
714 | |
|
715 | 0 | return 0; |
716 | 0 | } |
717 | | |
718 | | /****************************************************************************** |
719 | | |
720 | | Function: send_plane |
721 | | |
722 | | This function sends a bit plane to the printer. It returns zero on success |
723 | | and a non-zero value otherwise. In the latter case, an error message will |
724 | | have been issued on stderr. |
725 | | |
726 | | 'final' indicates whether this is the last plane of the row or not. |
727 | | 'method_demanded' contains the PCL compression method desired, '*method_used' |
728 | | the one actually used in the last transmission. This last variable will be |
729 | | reset to the method used in this invocation. |
730 | | 'in' points to the octet string to be sent as plane data, 'prev' points to |
731 | | the string previously sent (or whatever the reference plane for a differential |
732 | | compression method is) and may be NULL if 'method_demanded' refers |
733 | | to a purely "horizontal" method. |
734 | | 'out' is the file to which the plane should be written. |
735 | | 'out_bf1' and possibly 'out_bf2' are pointers to storage areas of at least |
736 | | length 'out_bf_size' which can be used as scratch areas by this function. |
737 | | 'out_bf1' must be non-NULL. |
738 | | 'out_bf2' need only be non-NULL if 'method_demanded' is 'pcl_cm_delta'. |
739 | | 'out_bf_size' must be positive but need not be larger than 'in->length'+2. |
740 | | |
741 | | ******************************************************************************/ |
742 | | |
743 | | static int send_plane(pcl_bool final, |
744 | | pcl_Compression method_demanded, pcl_Compression *method_used, |
745 | | const pcl_OctetString *in, const pcl_OctetString *prev, gp_file *out, |
746 | | pcl_Octet *out_bf1, pcl_Octet *out_bf2, size_t out_bf_size) |
747 | 0 | { |
748 | 0 | int |
749 | 0 | rc = 0; /* Return code from commands */ |
750 | 0 | pcl_Compression |
751 | 0 | choice; /* Method chosen */ |
752 | 0 | pcl_OctetString |
753 | 0 | out1, |
754 | 0 | out2, |
755 | 0 | send; /* Octets to be sent to the printer */ |
756 | | |
757 | | /* Initialize 'out1' (no dynamic initializers for structs in ISO/ANSI C) */ |
758 | 0 | out1.str = out_bf1; |
759 | 0 | out1.length = in->length + (*method_used == pcl_cm_none? 0: 2); |
760 | | /* 2 is the cost of switching to 'pcl_cm_none'. */ |
761 | 0 | if (out1.length > out_bf_size) out1.length = out_bf_size; |
762 | | |
763 | | /* Set 'send' to a compressed row to be sent and 'choice' to the compression |
764 | | method employed. */ |
765 | 0 | if (method_demanded == pcl_cm_delta) { |
766 | | /* Method 3 (delta row compression) has a widely varying effectiveness, |
767 | | depending on the structure of the input. Hence it is best combined |
768 | | with a non-delta method like method 2, as is done here on a per-plane |
769 | | basis, or method 1, as inherent in method 9. |
770 | | The procedure here is simple: try both methods, and then take the one |
771 | | giving the shortest output. |
772 | | */ |
773 | 0 | int c1, c2; /* cost in octets */ |
774 | | |
775 | | /* Try delta row compression */ |
776 | 0 | rc = pcl_compress(pcl_cm_delta, in, prev, &out1); |
777 | 0 | if (rc == 0) c1 = out1.length; else c1 = -1; |
778 | 0 | if (*method_used != pcl_cm_delta && c1 >= 0) c1 += 2; |
779 | | /* cost of switching methods */ |
780 | | |
781 | | /* Try TIFF compression */ |
782 | 0 | if (0 == c1) c2 = -1; |
783 | 0 | else { |
784 | 0 | int bound = in->length + (*method_used == pcl_cm_none? 0: 2); |
785 | 0 | if (c1 >= 0 && c1 < bound) { |
786 | | /* We're interested in TIFF compression only if it results in an octet |
787 | | string shorter than the one produced by delta row compression. */ |
788 | 0 | bound = c1; |
789 | 0 | if (*method_used != pcl_cm_tiff && bound >= 2) bound -= 2; |
790 | 0 | } |
791 | 0 | out2.str = out_bf2; out2.length = bound; |
792 | 0 | rc = pcl_compress(pcl_cm_tiff, in, NULL, &out2); |
793 | 0 | if (rc == 0) c2 = out2.length; else c2 = -1; |
794 | 0 | if (*method_used != pcl_cm_tiff && c2 >= 0) c2 += 2; |
795 | 0 | } |
796 | | |
797 | | /* Select the better of the two, or no compression */ |
798 | 0 | if (c1 < 0) { |
799 | 0 | if (c2 < 0) choice = pcl_cm_none; |
800 | 0 | else choice = pcl_cm_tiff; |
801 | 0 | } |
802 | 0 | else { |
803 | 0 | if (c2 < 0 || c1 <= c2) choice = pcl_cm_delta; |
804 | 0 | else choice = pcl_cm_tiff; |
805 | 0 | } |
806 | 0 | switch (choice) { |
807 | 0 | case pcl_cm_tiff: |
808 | 0 | send = out2; break; |
809 | 0 | case pcl_cm_delta: |
810 | 0 | send = out1; break; |
811 | 0 | default: |
812 | 0 | send = *in; |
813 | 0 | } |
814 | 0 | } |
815 | 0 | else { |
816 | 0 | if (method_demanded != pcl_cm_none && |
817 | 0 | pcl_compress(method_demanded, in, prev, &out1) == 0) { |
818 | | /* Send compressed data */ |
819 | 0 | send = out1; |
820 | 0 | choice = method_demanded; |
821 | 0 | } |
822 | 0 | else { |
823 | | /* Send uncompressed data */ |
824 | 0 | send = *in; |
825 | 0 | choice = pcl_cm_none; |
826 | 0 | } |
827 | 0 | } |
828 | | |
829 | | /* Switch compression methods, if needed */ |
830 | 0 | if (*method_used != choice) { |
831 | | /* Raster Graphics Compression Method */ |
832 | 0 | if (gp_fprintf(out, "%dm", (int)choice) < 0) { |
833 | 0 | errprintf(out->memory, ERRPREF "Error from fprintf(): %s.\n", strerror(errno)); |
834 | 0 | return -1; |
835 | 0 | } |
836 | 0 | *method_used = choice; |
837 | 0 | } |
838 | | |
839 | | /* Transfer plane to the printer */ |
840 | 0 | if (send.length == 0) { |
841 | 0 | errno = 0; |
842 | 0 | if (final) |
843 | 0 | gp_fputc('w', out); |
844 | | /* PCL: Transfer Raster Graphics Data by Row/Transfer Raster by Row/Block |
845 | | */ |
846 | 0 | else |
847 | 0 | gp_fputc('v', out); |
848 | | /* PCL: Transfer Raster Graphics Data by Plane/Transfer Raster by Plane */ |
849 | 0 | if (errno != 0) { |
850 | 0 | errprintf(out->memory, ERRPREF "Error from fputc(): %s.\n", strerror(errno)); |
851 | 0 | return -1; |
852 | 0 | } |
853 | 0 | } |
854 | 0 | else { |
855 | | /* PCL: Transfer Raster Graphics Data by Row/Block/Plane */ |
856 | 0 | if (gp_fprintf(out, "%d%c", send.length, final? 'w': 'v') < 0) { |
857 | 0 | errprintf(out->memory, ERRPREF "Error from fprintf(): %s.\n", strerror(errno)); |
858 | 0 | return -1; |
859 | 0 | } |
860 | 0 | if (gp_fwrite(send.str, sizeof(pcl_Octet), send.length, out) != send.length) { |
861 | 0 | errprintf(out->memory, ERRPREF "Error in fwrite(): %s.\n", strerror(errno)); |
862 | 0 | return -1; |
863 | 0 | } |
864 | 0 | } |
865 | | |
866 | 0 | return 0; |
867 | 0 | } |
868 | | |
869 | | /****************************************************************************** |
870 | | |
871 | | Function: pcl3_transfer_group |
872 | | |
873 | | Routine to transmit a strip group given as a sequence of bitplanes. A strip |
874 | | group consists of one strip of raster rows for every colorant. Every strip |
875 | | has the same height (reciprocal of the lowest vertical resolution) and all |
876 | | the strips will be overlaid. For more details, see the description of |
877 | | 'pcl_RasterData' in the header file. |
878 | | |
879 | | The bit planes will be sent as specified without first trying to reduce their |
880 | | length by removing trailing null octets. |
881 | | |
882 | | This routine may only be called in raster mode. |
883 | | |
884 | | On success and if a differential compression method has been requested, this |
885 | | function will exchange the values of the 'next' and 'previous' arrays (not |
886 | | the storage areas pointed to by their 'str' fields). Hence the calling code |
887 | | need merely fill in the new sequence of bit planes into the areas currently |
888 | | pointed to from the 'next' array, set the length fields, call this function, |
889 | | and repeat the procedure for subsequent strip groups. On allocating the |
890 | | 'next' and 'previous' arrays it is therefore advisable to allocate storage of |
891 | | identical length for corresponding elements in 'next' and 'previous'. |
892 | | |
893 | | ******************************************************************************/ |
894 | | |
895 | | int pcl3_transfer_group(gp_file *out, pcl_RasterData *data) |
896 | 0 | { |
897 | 0 | const pcl_FileData *global = data->global; |
898 | 0 | int |
899 | 0 | final, |
900 | 0 | j; |
901 | | |
902 | | /* Send the bit planes in their proper order */ |
903 | 0 | if (global->palette == pcl_CMYK && global->order_CMYK) { |
904 | | /* First CMY */ |
905 | 0 | for (j = global->black_planes; j < global->number_of_bitplanes; j++) { |
906 | 0 | if (send_plane(FALSE, |
907 | 0 | global->compression, &data->current_compression, data->next + j, |
908 | 0 | data->seed_plane[j], |
909 | 0 | out, |
910 | 0 | data->workspace[0], data->workspace[1], |
911 | 0 | data->workspace_allocated) != 0) |
912 | 0 | return -1; |
913 | 0 | } |
914 | | /* Now black */ |
915 | 0 | final = global->black_planes - 1; |
916 | 0 | for (j = 0; j < global->black_planes; j++) { |
917 | 0 | if (send_plane(j == final, |
918 | 0 | global->compression, &data->current_compression, data->next + j, |
919 | 0 | data->seed_plane[j], |
920 | 0 | out, |
921 | 0 | data->workspace[0], data->workspace[1], |
922 | 0 | data->workspace_allocated) != 0) |
923 | 0 | return -1; |
924 | 0 | } |
925 | 0 | } |
926 | 0 | else { |
927 | | /* Output order is K, CMY, KCMY or RGB */ |
928 | 0 | final = global->number_of_bitplanes - 1; |
929 | 0 | for (j = 0; j < global->number_of_bitplanes; j++) { |
930 | 0 | if (send_plane(j == final, |
931 | 0 | global->compression, &data->current_compression, data->next + j, |
932 | 0 | data->seed_plane[j], |
933 | 0 | out, |
934 | 0 | data->workspace[0], data->workspace[1], |
935 | 0 | data->workspace_allocated) != 0) |
936 | 0 | return -1; |
937 | 0 | } |
938 | 0 | } |
939 | | |
940 | | /* Switch old and new planes in case of differential compression methods */ |
941 | 0 | if (pcl_cm_is_differential(data->global->compression)) |
942 | 0 | for (j = 0; j < global->number_of_bitplanes; j++) { |
943 | 0 | pcl_OctetString tmp; |
944 | 0 | tmp = data->previous[j]; |
945 | 0 | data->previous[j] = data->next[j]; |
946 | 0 | data->next[j] = tmp; |
947 | 0 | } |
948 | |
|
949 | 0 | return 0; |
950 | 0 | } |
951 | | |
952 | | /****************************************************************************** |
953 | | |
954 | | Function: pcl3_end_raster |
955 | | |
956 | | This function may only be called in raster mode which it terminates. |
957 | | |
958 | | ******************************************************************************/ |
959 | | |
960 | | int pcl3_end_raster(gp_file *out, pcl_RasterData *data) |
961 | 0 | { |
962 | 0 | gp_fputs("0Y", out); |
963 | | /* PCL: Relative Vertical Pixel Movement/Y Offset. This is a simple way |
964 | | to terminate the combined escape sequence started at the beginning of |
965 | | raster mode. */ |
966 | | |
967 | | /* End Raster Graphics */ |
968 | 0 | send_ERG(out, data->global->level); |
969 | 0 | if (!pcl_use_oldERG(data->global->level)) |
970 | 0 | data->current_compression = pcl_cm_none; |
971 | | /* TRG500 p. 6-40, Lexmark-PTR pp. 2-40/41 */ |
972 | |
|
973 | 0 | free(data->seed_plane); data->seed_plane = NULL; |
974 | |
|
975 | 0 | return 0; |
976 | 0 | } |
977 | | |
978 | | /****************************************************************************** |
979 | | |
980 | | Function: pcl3_end_page |
981 | | |
982 | | This function must be called to finish a page. It may only be called if the |
983 | | page has been opened and not in raster mode. |
984 | | |
985 | | ******************************************************************************/ |
986 | | |
987 | | int pcl3_end_page(gp_file *out, pcl_FileData *data) |
988 | 0 | { |
989 | | /* Eject the page */ |
990 | 0 | gp_fputc('\f', out); |
991 | |
|
992 | 0 | if (gp_ferror(out)) { |
993 | 0 | errprintf(out->memory, ERRPREF "Unidentified system error while writing the output file.\n"); |
994 | 0 | return -1; |
995 | 0 | } |
996 | | |
997 | 0 | return 0; |
998 | 0 | } |
999 | | |
1000 | | /****************************************************************************** |
1001 | | |
1002 | | Function: pcl3_end_file |
1003 | | |
1004 | | This function should be called at the end of each print job in order to leave |
1005 | | the printer in a known state. However, this is only a matter of courtesy for |
1006 | | print jobs which do not follow HP's recommendations and do not start with |
1007 | | Printer Reset as the initial command. |
1008 | | |
1009 | | ******************************************************************************/ |
1010 | | |
1011 | | int pcl3_end_file(gp_file *out, pcl_FileData *data) |
1012 | 0 | { |
1013 | | /* For banner printing, HP recommends to eject the page via Media Source. |
1014 | | The printer then enters a paper-unloading state. */ |
1015 | 0 | if (data->media_source == -1) |
1016 | 0 | gp_fputs("\033&l0H", out); /* PCL: Media Source: Eject Page */ |
1017 | | |
1018 | | /* PCL: Printer Reset */ |
1019 | 0 | gp_fputs("\033E", out); |
1020 | | |
1021 | | /* Terminate PJL */ |
1022 | 0 | if (data->PJL_job != NULL || data->PJL_language != NULL) { |
1023 | | /* PJL: UEL */ |
1024 | 0 | gp_fputs("\033%-12345X", out); |
1025 | |
|
1026 | 0 | if (data->PJL_job != NULL) { |
1027 | | /* PJL: End of Job. Some HP PCL-3 drivers using JOB omit this command. |
1028 | | According to PJLTRM, it is required in that case. */ |
1029 | 0 | gp_fputs("@PJL EOJ\n", out); |
1030 | | |
1031 | | /* PJL: UEL. All output I've seen from HP's PCL-3 drivers using EOJ |
1032 | | omits this final UEL. According to PJLTRM, it is required. In my |
1033 | | opinion it doesn't make any difference because in both cases the |
1034 | | printer expects PJL in preference to other data next and the rules |
1035 | | for deciding whether it's PJL or not are also the same. Note that the |
1036 | | command does not influence the printer's state. */ |
1037 | 0 | gp_fputs("\033%-12345X", out); |
1038 | 0 | } |
1039 | 0 | } |
1040 | |
|
1041 | 0 | if (gp_ferror(out)) { |
1042 | 0 | errprintf(out->memory, ERRPREF "Unidentified system error while writing the output file.\n"); |
1043 | 0 | return -1; |
1044 | 0 | } |
1045 | | |
1046 | 0 | return 0; |
1047 | 0 | } |
1048 | | |
1049 | | /****************************************************************************** |
1050 | | |
1051 | | Function: pcl3_set_oldquality |
1052 | | |
1053 | | This function determines and sets the old quality parameters based on the |
1054 | | current 'palette', 'print_quality' and 'media_type' values. This is mostly |
1055 | | done as recommended by HP on page 6-34 of the TRG500 but that information is |
1056 | | not complete. |
1057 | | |
1058 | | In particular, nothing is said about RGB palettes which I'm treating here |
1059 | | like CMY palettes. |
1060 | | |
1061 | | If the input values cannot be mapped, the function sets the old parameters as |
1062 | | if the offending value were 0 (normal quality or plain paper respectively) |
1063 | | and returns a non-zero exit code. |
1064 | | |
1065 | | ******************************************************************************/ |
1066 | | |
1067 | | int pcl3_set_oldquality(pcl_FileData *data) |
1068 | 0 | { |
1069 | | /* A media type of 3 means glossy paper, 4 is transparency film. */ |
1070 | |
|
1071 | 0 | switch (data->print_quality) { |
1072 | 0 | case -1 /* draft */: |
1073 | 0 | data->depletion = 3; /* 50 % */ |
1074 | 0 | data->raster_graphics_quality = 1; /* draft */ |
1075 | 0 | if (data->media_type == 4) data->shingling = 1; /* 2 passes */ |
1076 | 0 | else data->shingling = 0; /* no shingling */ |
1077 | 0 | break; |
1078 | 0 | case 1 /* presentation */: |
1079 | 0 | if (3 <= data->media_type && data->media_type <= 4) |
1080 | 0 | data->depletion = 1; /* 0 % */ |
1081 | 0 | else if (data->palette == pcl_CMY || data->palette == pcl_RGB) |
1082 | 0 | data->depletion = 2; /* 25 % */ |
1083 | 0 | else |
1084 | 0 | data->depletion = 3; /* 50 % */ |
1085 | | /* Actually, TRG500 recommends 5 (50 % with gamma correction), but we |
1086 | | assume that gamma correction will be handled externally. */ |
1087 | 0 | data->raster_graphics_quality = 2; /* high */ |
1088 | 0 | data->shingling = 2; /* 4 passes */ |
1089 | 0 | break; |
1090 | 0 | default: /* normal or an illegal value */ |
1091 | 0 | data->depletion = 2; /* 25 % */ |
1092 | 0 | data->raster_graphics_quality = 0; /* use current control panel setting */ |
1093 | 0 | if (data->media_type == 3 || |
1094 | 0 | (data->media_type == 4 && data->palette != pcl_CMY && |
1095 | 0 | data->palette != pcl_RGB)) |
1096 | 0 | data->shingling = 2; /* 4 passes (25 % each pass) */ |
1097 | 0 | else data->shingling = 1; /* 2 passes (50 % each pass) */ |
1098 | 0 | } |
1099 | | |
1100 | | /* No depletion for monochrome data */ |
1101 | 0 | if (data->palette <= pcl_black) data->depletion = 0; |
1102 | |
|
1103 | 0 | return -1 <= data->print_quality && data->print_quality <= 1 && |
1104 | 0 | 0 <= data->media_type && data->media_type <= 4? 0: 1; |
1105 | 0 | } |
1106 | | |
1107 | | /*****************************************************************************/ |
1108 | | |
1109 | | int pcl3_set_printquality(pcl_FileData *data, int quality) |
1110 | 0 | { |
1111 | 0 | data->print_quality = quality; |
1112 | 0 | if (pcl_use_oldquality(data->level)) return pcl3_set_oldquality(data); |
1113 | | |
1114 | 0 | return 0; |
1115 | 0 | } |
1116 | | |
1117 | | /*****************************************************************************/ |
1118 | | |
1119 | | int pcl3_set_mediatype(pcl_FileData *data, int mediatype) |
1120 | 0 | { |
1121 | 0 | data->media_type = mediatype; |
1122 | 0 | if (pcl_use_oldquality(data->level)) return pcl3_set_oldquality(data); |
1123 | | |
1124 | 0 | return 0; |
1125 | 0 | } |