/src/ghostpdl/contrib/pcl3/src/gdevpcl3.c
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | File: $Id: gdevpcl3.c,v 1.32 2001/08/14 15:22:35 Martin Rel $ |
3 | | Contents: Ghostscript device 'pcl3' for PCL-3+ printers |
4 | | Author: Martin Lottermoser, Greifswaldstrasse 28, 38124 Braunschweig, |
5 | | Germany. E-mail: Martin.Lottermoser@t-online.de. |
6 | | |
7 | | ******************************************************************************* |
8 | | * * |
9 | | * Copyright (C) 2000, 2001 by Martin Lottermoser * |
10 | | * All rights reserved * |
11 | | * * |
12 | | ******************************************************************************* |
13 | | |
14 | | Preprocessor symbols: |
15 | | |
16 | | GS_REVISION (integer) |
17 | | If defined, this must be the ghostscript version number, e.g., 601 for |
18 | | ghostscript 6.01. |
19 | | |
20 | | PCL3_MEDIA_FILE (const char *) |
21 | | Define this to set a media configuration file for the "unspec" device |
22 | | unless the user overrides it. |
23 | | |
24 | | ******************************************************************************/ |
25 | | |
26 | | /*****************************************************************************/ |
27 | | |
28 | | #ifndef _XOPEN_SOURCE |
29 | | #define _XOPEN_SOURCE 500 |
30 | | #endif |
31 | | |
32 | | /* Special Aladdin header, must be included before <sys/types.h> on some |
33 | | platforms (e.g., FreeBSD). */ |
34 | | #include "std.h" |
35 | | |
36 | | /* Standard headers */ |
37 | | #include <assert.h> |
38 | | #include <ctype.h> |
39 | | #include <errno.h> |
40 | | #include <stdio.h> |
41 | | #include <stdlib.h> |
42 | | #include <string.h> |
43 | | |
44 | | /* Ghostscript headers */ |
45 | | #ifdef EPRN_TRACE |
46 | | #include "gdebug.h" |
47 | | #endif /* EPRN_TRACE */ |
48 | | |
49 | | /* Driver-specific headers */ |
50 | | #include "gdeveprn.h" |
51 | | #include "pclcap.h" |
52 | | #include "pclgen.h" |
53 | | #include "pclsize.h" |
54 | | |
55 | | /*****************************************************************************/ |
56 | | |
57 | | /* Does the argument point to an instance of the generic (pcl3) device? */ |
58 | 0 | #define is_generic_device(dev) (strcmp(dev->dname, "pcl3") == 0) |
59 | | |
60 | | /*****************************************************************************/ |
61 | | |
62 | | /* Combined type with a range of bool plus null */ |
63 | | typedef enum {bn_null, bn_true, bn_false} bool_or_null; |
64 | | |
65 | | /* Type for duplex capabilities */ |
66 | | typedef enum {Duplex_none, Duplex_sameLeadingEdge, Duplex_oppositeLeadingEdge, |
67 | | Duplex_both} DuplexCapabilities; |
68 | | |
69 | | /* Device structure */ |
70 | | typedef struct { |
71 | | gx_eprn_device_common; /* eprn part including base types */ |
72 | | |
73 | | /* Printer selection and other data not directly mappable to PCL */ |
74 | | pcl_Printer printer; |
75 | | bool_or_null use_card; |
76 | | DuplexCapabilities duplex_capability; |
77 | | bool tumble; /* only relevant if 'Duplex' is 'true' */ |
78 | | |
79 | | /* PCL generation */ |
80 | | bool |
81 | | initialized, /* Has init() been run on this device instance? */ |
82 | | configured, /* Has the output file been configured? */ |
83 | | configure_every_page; /* Repeat the configuration for every page? */ |
84 | | pcl_FileData file_data; |
85 | | pcl3_sizetable table; |
86 | | } pcl3_Device; |
87 | | |
88 | | /*****************************************************************************/ |
89 | | |
90 | | /* Device procedures */ |
91 | | static dev_proc_open_device(pcl3_open_device); |
92 | | static dev_proc_close_device(pcl3_close_device); |
93 | | static dev_proc_get_params(pcl3_get_params); |
94 | | static dev_proc_put_params(pcl3_put_params); |
95 | | |
96 | | /* Device procedures */ |
97 | | static void |
98 | | eprn_initialize_device_procs(gx_device *dev) |
99 | 0 | { |
100 | 0 | gdev_prn_initialize_device_procs(dev); |
101 | |
|
102 | 0 | set_dev_proc(dev, open_device, pcl3_open_device); |
103 | 0 | set_dev_proc(dev, get_initial_matrix, eprn_get_initial_matrix); |
104 | 0 | set_dev_proc(dev, close_device, pcl3_close_device); |
105 | 0 | set_dev_proc(dev, map_rgb_color, eprn_map_rgb_color_for_CMY_or_K); |
106 | 0 | set_dev_proc(dev, map_color_rgb, eprn_map_color_rgb); |
107 | 0 | set_dev_proc(dev, map_cmyk_color, eprn_map_cmyk_color_glob); |
108 | 0 | set_dev_proc(dev, get_params, pcl3_get_params); |
109 | 0 | set_dev_proc(dev, put_params, pcl3_put_params); |
110 | 0 | set_dev_proc(dev, fillpage, eprn_fillpage); |
111 | 0 | }; |
112 | | |
113 | | /* prn procedure implementations */ |
114 | | #if !defined(GS_REVISION) || GS_REVISION >= 550 |
115 | | static prn_dev_proc_print_page(pcl3_print_page); |
116 | | #else |
117 | | static dev_proc_print_page(pcl3_print_page); |
118 | | #endif |
119 | | |
120 | | /*****************************************************************************/ |
121 | | |
122 | | /* Media flags known to this device in addition to the standard ones */ |
123 | | static const ms_Flag |
124 | | flag_description[] = { |
125 | | {PCL_CARD_FLAG, PCL_CARD_STRING}, |
126 | | {ms_none, NULL} |
127 | | }; |
128 | | |
129 | | /* List of possible optional media flags */ |
130 | | static const ms_MediaCode |
131 | | card_is_optional[] = {PCL_CARD_FLAG, ms_none}; |
132 | | |
133 | | /*****************************************************************************/ |
134 | | |
135 | | /* Forward declaration */ |
136 | | static void pcl3_flag_mismatch_reporter( |
137 | | const struct s_eprn_Device *eprn, bool no_match); |
138 | | |
139 | | /* Macro for creating device structure instances */ |
140 | | #define pcl3_device_instance(dname, printer) \ |
141 | | pcl3_Device gs_##dname##_device = { \ |
142 | | eprn_device_initdata( \ |
143 | | pcl3_Device, /* device type */ \ |
144 | | eprn_initialize_device_procs, /* initialize dev_procs */ \ |
145 | | #dname, /* device name */ \ |
146 | | 300.0, 300.0, /* horizontal and vertical resolution */\ |
147 | | pcl3_print_page, /* print page routine */ \ |
148 | | &pcl3_printers[printer].desc, /* printer capability description */ \ |
149 | | flag_description, /* flag descriptions */ \ |
150 | | ms_none, /* desired media flags */ \ |
151 | | card_is_optional, /* list of optional flags */ \ |
152 | | &pcl3_flag_mismatch_reporter), /* reporting function */\ |
153 | | printer, /* printer */ \ |
154 | | bn_null, /* use_card */ \ |
155 | | Duplex_none, /* duplex_capability */ \ |
156 | | false, /* tumble */ \ |
157 | | false /* initialized */ \ |
158 | | /* The remaining fields will be set in init(). */ \ |
159 | | } |
160 | | |
161 | | /* Generic and flexible device structure instance */ |
162 | | pcl3_device_instance(pcl3, pcl3_generic_new); |
163 | | |
164 | | /* Printer-specific and fixed device structure instances */ |
165 | | /* At present there is no entry for the HP DeskJet because its natural name |
166 | | collides with the hpdj driver. */ |
167 | | pcl3_device_instance(hpdjplus, HPDeskJetPlus); |
168 | | pcl3_device_instance(hpdjportable, HPDJPortable); |
169 | | pcl3_device_instance(hpdj310, HPDJ310); |
170 | | pcl3_device_instance(hpdj320, HPDJ320); |
171 | | pcl3_device_instance(hpdj340, HPDJ340); |
172 | | pcl3_device_instance(hpdj400, HPDJ400); |
173 | | pcl3_device_instance(hpdj500, HPDJ500); |
174 | | pcl3_device_instance(hpdj500c, HPDJ500C); |
175 | | pcl3_device_instance(hpdj510, HPDJ510); |
176 | | pcl3_device_instance(hpdj520, HPDJ520); |
177 | | pcl3_device_instance(hpdj540, HPDJ540); |
178 | | pcl3_device_instance(hpdj550c, HPDJ550C); |
179 | | pcl3_device_instance(hpdj560c, HPDJ560C); |
180 | | pcl3_device_instance(hpdj600, HPDJ600); |
181 | | pcl3_device_instance(hpdj660c, HPDJ660C); |
182 | | pcl3_device_instance(hpdj670c, HPDJ670C); |
183 | | pcl3_device_instance(hpdj680c, HPDJ680C); |
184 | | pcl3_device_instance(hpdj690c, HPDJ690C); |
185 | | pcl3_device_instance(hpdj850c, HPDJ850C); |
186 | | pcl3_device_instance(hpdj855c, HPDJ855C); |
187 | | pcl3_device_instance(hpdj870c, HPDJ870C); |
188 | | pcl3_device_instance(hpdj890c, HPDJ890C); |
189 | | pcl3_device_instance(hpdj1120c, HPDJ1120C); |
190 | | |
191 | | /*****************************************************************************/ |
192 | | |
193 | | #define ERRPREF "? pcl3: " |
194 | | #define WARNPREF "?-W pcl3: " |
195 | | |
196 | 0 | #define array_size(a) (sizeof(a)/sizeof(a[0])) |
197 | | |
198 | | /*****************************************************************************/ |
199 | | |
200 | | static const eprn_StringAndInt |
201 | | /* Names for duplex capabilities */ |
202 | | duplex_capabilities_list[] = { |
203 | | { "none", Duplex_none }, |
204 | | { "sameLeadingEdge", Duplex_sameLeadingEdge }, |
205 | | { "oppositeLeadingEdge", Duplex_oppositeLeadingEdge }, |
206 | | { "both", Duplex_both }, |
207 | | { NULL, 0 } |
208 | | }, |
209 | | /* Names for PCL Media Type values */ |
210 | | media_type_list[] = { |
211 | | /* Canonical names */ |
212 | | { "plain paper", 0 }, |
213 | | { "bond paper", 1 }, |
214 | | { "HP Premium paper", 2 }, |
215 | | { "glossy paper", 3 }, |
216 | | { "transparency film", 4 }, |
217 | | { "quick dry glossy", 5 }, |
218 | | { "quick dry transparency", 6 }, |
219 | | /* Shortened names */ |
220 | | { "plain", 0 }, |
221 | | { "bond", 1 }, |
222 | | { "Premium", 2 }, |
223 | | { "glossy", 3 }, |
224 | | { "transparency", 4 }, |
225 | | { NULL, 0 } |
226 | | }, |
227 | | /* Print Quality */ |
228 | | print_quality_list[] = { |
229 | | { "draft", -1 }, |
230 | | { "normal", 0 }, |
231 | | { "presentation", 1 }, |
232 | | /* Start of synonyms */ |
233 | | { "econo", -1 }, |
234 | | { "best", 1 }, |
235 | | { NULL, 0 } |
236 | | }, |
237 | | /* Subdevice names. They must be ordered by 'value' except for the last |
238 | | (NULL) entry. At present, there are 26 non-NULL entries here. */ |
239 | | subdevice_list[] = { |
240 | | { "hpdj", HPDeskJet }, |
241 | | { "hpdjplus", HPDeskJetPlus }, |
242 | | { "hpdjportable", HPDJPortable }, |
243 | | { "hpdj310", HPDJ310 }, |
244 | | { "hpdj320", HPDJ320 }, |
245 | | { "hpdj340", HPDJ340 }, |
246 | | { "hpdj400", HPDJ400 }, |
247 | | { "hpdj500", HPDJ500 }, |
248 | | { "hpdj500c", HPDJ500C }, |
249 | | { "hpdj510", HPDJ510 }, |
250 | | { "hpdj520", HPDJ520 }, |
251 | | { "hpdj540", HPDJ540 }, |
252 | | { "hpdj550c", HPDJ550C }, |
253 | | { "hpdj560c", HPDJ560C }, |
254 | | { "unspecold", pcl3_generic_old }, |
255 | | { "hpdj600", HPDJ600 }, |
256 | | { "hpdj660c", HPDJ660C }, |
257 | | { "hpdj670c", HPDJ670C }, |
258 | | { "hpdj680c", HPDJ680C }, |
259 | | { "hpdj690c", HPDJ690C }, |
260 | | { "hpdj850c", HPDJ850C }, |
261 | | { "hpdj855c", HPDJ855C }, |
262 | | { "hpdj870c", HPDJ870C }, |
263 | | { "hpdj890c", HPDJ890C }, |
264 | | { "hpdj1120c", HPDJ1120C }, |
265 | | { "unspec", pcl3_generic_new }, |
266 | | { NULL, 0 } |
267 | | }; |
268 | | |
269 | | /****************************************************************************** |
270 | | |
271 | | Function: cmp_by_value |
272 | | |
273 | | This function compares two 'eprn_StringAndInt' instances by their 'value' |
274 | | fields. |
275 | | |
276 | | ******************************************************************************/ |
277 | | |
278 | | static int cmp_by_value(const void *a, const void *b) |
279 | 0 | { |
280 | 0 | return ((const eprn_StringAndInt *)a)->value - |
281 | 0 | ((const eprn_StringAndInt *)b)->value; |
282 | 0 | } |
283 | | |
284 | | /****************************************************************************** |
285 | | |
286 | | Function: get_string_for_int |
287 | | |
288 | | This function returns a string representation of 'in_value' in '*out_value', |
289 | | based on 'table'. 'table' must be an array terminated with an entry having |
290 | | NULL as the 'name' value and must be permanently allocated and constant. |
291 | | If 'in_value' cannot be found in 'table', the function returns a decimal |
292 | | representation of 'in_value'. |
293 | | |
294 | | The string buffer in '*out_value' will be a permanently allocated area which |
295 | | must not be modified. |
296 | | |
297 | | ******************************************************************************/ |
298 | | |
299 | | static void get_string_for_int(int in_value, const eprn_StringAndInt *table, |
300 | | gs_param_string *out_value) |
301 | 0 | { |
302 | 0 | while (table->name != NULL && table->value != in_value) table++; |
303 | 0 | if (table->name != NULL) { |
304 | 0 | out_value->data = (const byte *)table->name; |
305 | 0 | out_value->size = strlen(table->name); |
306 | 0 | out_value->persistent = true; |
307 | 0 | } |
308 | 0 | else { |
309 | 0 | char buffer[22]; /* Must be sufficient for an 'int' */ |
310 | |
|
311 | 0 | gs_snprintf(buffer, sizeof(buffer), "%d", in_value); |
312 | 0 | assert(strlen(buffer) < sizeof(buffer)); |
313 | 0 | out_value->data = (const byte *)buffer; |
314 | 0 | out_value->size = strlen(buffer); |
315 | 0 | out_value->persistent = false; |
316 | 0 | } |
317 | |
|
318 | 0 | return; |
319 | 0 | } |
320 | | |
321 | | /****************************************************************************** |
322 | | |
323 | | Function: get_int_for_string |
324 | | |
325 | | This function parses 'in_value' based on 'table' and returns the result in |
326 | | '*out_value'. 'table' must be an array, terminated with an entry having NULL |
327 | | as the value for 'name'. |
328 | | |
329 | | 'in_value' must either be a decimal representation of an integer or must be |
330 | | a string present in 'table'. In these cases, the function returns 0, |
331 | | otherwise a non-zero ghostscript error value. |
332 | | |
333 | | On returning 'gs_error_VMerror', the function will have issued an error |
334 | | message. |
335 | | |
336 | | ******************************************************************************/ |
337 | | |
338 | | static int get_int_for_string(const gs_param_string *in_value, |
339 | | const eprn_StringAndInt *table, int *out_value) |
340 | 0 | { |
341 | 0 | char *s; |
342 | 0 | int read; /* counter */ |
343 | | |
344 | | /* First we construct a properly NUL-terminated string */ |
345 | 0 | s = (char *) malloc(in_value->size + 1); |
346 | 0 | if (s == NULL) { |
347 | 0 | eprintf1(ERRPREF |
348 | 0 | "Memory allocation failure in get_int_for_string(): %s.\n", |
349 | 0 | strerror(errno)); |
350 | 0 | return_error(gs_error_VMerror); |
351 | 0 | } |
352 | 0 | strncpy(s, (const char *)in_value->data, in_value->size); |
353 | 0 | s[in_value->size] = '\0'; |
354 | | |
355 | | /* To foil bugs in sscanf() on Windows (see gdeveprn.c in eprn) I'm removing |
356 | | trailing white space here instead of skipping it with a format. */ |
357 | 0 | { |
358 | 0 | char *t = strchr(s, '\0'); |
359 | |
|
360 | 0 | while (s < t && isspace(*(t-1))) t--; |
361 | 0 | *t = '\0'; |
362 | 0 | } |
363 | | |
364 | | /* Check for a numerical value */ |
365 | 0 | if (sscanf(s, "%d%n", out_value, &read) != 1 || s[read] != '\0') { |
366 | | /* What the user specified is not a valid numerical value */ |
367 | 0 | while (table->name != NULL && strcmp(table->name, s) != 0) table++; |
368 | 0 | if (table->name == NULL) { |
369 | 0 | free(s); s = NULL; |
370 | 0 | return_error(gs_error_rangecheck); |
371 | 0 | } |
372 | 0 | *out_value = table->value; |
373 | 0 | } |
374 | | |
375 | 0 | free(s); s = NULL; |
376 | |
|
377 | 0 | return 0; |
378 | 0 | } |
379 | | |
380 | | /****************************************************************************** |
381 | | |
382 | | Function: init |
383 | | |
384 | | This function does that part of initialization which cannot be performed at |
385 | | compile time or which must be repeated whenever the subdevice is changed. |
386 | | It must be called if 'initialized' is false and after the subdevice is |
387 | | changed. |
388 | | |
389 | | When this function is called, 'dev->printer' must have been set correctly. |
390 | | |
391 | | This function must not and does not depend on the state of initialization of |
392 | | its base devices. |
393 | | |
394 | | ******************************************************************************/ |
395 | | |
396 | | static void init(pcl3_Device *dev) |
397 | 0 | { |
398 | | #ifndef NDEBUG |
399 | | /* Check that 'subdevice_list' is sorted by 'value' */ |
400 | | { |
401 | | int j; |
402 | | for (j = 1; j < array_size(subdevice_list) - 1; j++) |
403 | | assert(cmp_by_value(subdevice_list + j - 1, subdevice_list + j) <= 0); |
404 | | } |
405 | | #endif /* !NDEBUG */ |
406 | | |
407 | | /* Base class fields */ |
408 | 0 | if (is_generic_device(dev)) dev->Duplex_set = 0; |
409 | | /* "Duplex" is null. See remarks on the "Duplex" page device parameter in |
410 | | pcl3_put_params(). */ |
411 | | |
412 | | /* pcl3 fields */ |
413 | 0 | dev->use_card = bn_null; |
414 | 0 | dev->duplex_capability = Duplex_none; |
415 | 0 | dev->tumble = false; |
416 | 0 | dev->configured = false; |
417 | 0 | dev->configure_every_page = false; |
418 | | |
419 | | /* Initialize 'file_data' */ |
420 | 0 | pcl3_fill_defaults(dev->printer, &dev->file_data); |
421 | |
|
422 | 0 | dev->initialized = true; |
423 | |
|
424 | 0 | return; |
425 | 0 | } |
426 | | |
427 | | /****************************************************************************** |
428 | | |
429 | | Function: pcl3_flag_mismatch_reporter |
430 | | |
431 | | Flag mismatch reporting function for the pcl3 device. |
432 | | |
433 | | The 'desired_flags' field can only contain MS_BIG_FLAG and PCL_CARD_FLAG. |
434 | | |
435 | | ******************************************************************************/ |
436 | | |
437 | | static void pcl3_flag_mismatch_reporter( |
438 | | const struct s_eprn_Device *eprn, bool no_match) |
439 | 0 | { |
440 | 0 | const char *epref = eprn->CUPS_messages? CUPS_ERRPREF: ""; |
441 | |
|
442 | 0 | if (eprn->desired_flags == 0) { |
443 | 0 | eprintf2( |
444 | 0 | "%s" ERRPREF "The %s does not support the requested media properties.\n", |
445 | 0 | epref, eprn->cap->name); |
446 | 0 | } |
447 | 0 | else if (eprn->desired_flags == MS_BIG_FLAG) { |
448 | 0 | eprintf2("%s" ERRPREF "The %s does not support banner printing", |
449 | 0 | epref, eprn->cap->name); |
450 | 0 | if (!no_match) eprintf(" for this size"); |
451 | 0 | eprintf(".\n"); |
452 | 0 | } |
453 | 0 | else if (eprn->desired_flags == PCL_CARD_FLAG) { |
454 | 0 | eprintf2("%s" ERRPREF |
455 | 0 | "The %s does not support a `Card' variant for ", |
456 | 0 | epref, eprn->cap->name); |
457 | 0 | if (no_match) eprintf("any"); else eprintf("this"); |
458 | 0 | eprintf(" size.\n"); |
459 | 0 | } |
460 | 0 | else { |
461 | 0 | eprintf1( |
462 | 0 | "%s" ERRPREF "Banner printing on postcards?? You must be joking!\n", |
463 | 0 | epref); |
464 | 0 | } |
465 | |
|
466 | 0 | return; |
467 | 0 | } |
468 | | |
469 | | /****************************************************************************** |
470 | | |
471 | | Function: find_subdevice_name |
472 | | |
473 | | This function returns a pointer to a static storage location containing |
474 | | a NUL-terminated string with the name of the subdevice for 'subdev'. |
475 | | |
476 | | It must not be called for invalid 'subdev' values. |
477 | | |
478 | | ******************************************************************************/ |
479 | | |
480 | | static const char *find_subdevice_name(int subdev) |
481 | 0 | { |
482 | 0 | eprn_StringAndInt |
483 | 0 | key = {NULL, 0}; |
484 | 0 | const eprn_StringAndInt |
485 | 0 | *found; |
486 | |
|
487 | 0 | key.value = subdev; |
488 | |
|
489 | 0 | found = (const eprn_StringAndInt *)bsearch(&key, subdevice_list, |
490 | 0 | array_size(subdevice_list) - 1, sizeof(eprn_StringAndInt), cmp_by_value); |
491 | 0 | assert(found != NULL); |
492 | |
|
493 | 0 | return found->name; |
494 | 0 | } |
495 | | |
496 | | /****************************************************************************** |
497 | | |
498 | | Function: pcl3_get_params |
499 | | |
500 | | This function returns to the caller information about the values of |
501 | | parameters defined for the device. This includes parameters defined in base |
502 | | classes. |
503 | | |
504 | | The function returns zero on success and a negative value on error. |
505 | | |
506 | | ******************************************************************************/ |
507 | | |
508 | | static int pcl3_get_params(gx_device *device, gs_param_list *plist) |
509 | 0 | { |
510 | 0 | gs_param_string string_value; |
511 | 0 | pcl3_Device *dev = (pcl3_Device *)device; |
512 | 0 | const pcl_FileData *data = &dev->file_data; |
513 | 0 | int |
514 | 0 | temp, /* Used as an intermediate for various reasons */ |
515 | 0 | rc; |
516 | | |
517 | | /* Constructor */ |
518 | 0 | if (!dev->initialized) init(dev); |
519 | | |
520 | | /* Base class parameters */ |
521 | 0 | rc = eprn_get_params(device, plist); |
522 | 0 | if (rc < 0) return rc; |
523 | | |
524 | | /* Compression method */ |
525 | 0 | temp = data->compression; |
526 | 0 | if ((rc = param_write_int(plist, "CompressionMethod", &temp)) < 0) return rc; |
527 | | |
528 | | /* Configure every page */ |
529 | 0 | if ((rc = param_write_bool(plist, "ConfigureEveryPage", |
530 | 0 | &dev->configure_every_page)) < 0) return rc; |
531 | | |
532 | | /* Dry time */ |
533 | 0 | if (data->dry_time < 0) { |
534 | 0 | if ((rc = param_write_null(plist, "DryTime")) < 0) return rc; |
535 | 0 | } |
536 | 0 | else if ((rc = param_write_int(plist, "DryTime", &data->dry_time)) < 0) |
537 | 0 | return rc; |
538 | | |
539 | | /* Duplex capability */ |
540 | 0 | if (is_generic_device(dev)) { |
541 | 0 | eprn_get_string(dev->duplex_capability, duplex_capabilities_list, |
542 | 0 | &string_value); |
543 | 0 | if ((rc = param_write_string(plist, "DuplexCapability", &string_value)) < 0) |
544 | 0 | return rc; |
545 | 0 | } |
546 | | |
547 | | /* Manual feed */ |
548 | 0 | { |
549 | 0 | bool temp = dev->file_data.manual_feed; |
550 | 0 | if ((rc = param_write_bool(plist, "ManualFeed", &temp)) < 0) return rc; |
551 | 0 | } |
552 | | |
553 | | /* PCL media type */ |
554 | 0 | get_string_for_int(data->media_type, media_type_list, &string_value); |
555 | 0 | if ((rc = param_write_string(plist, "Medium", &string_value)) < 0) |
556 | 0 | return rc; |
557 | | |
558 | | /* Media destination */ |
559 | 0 | if ((rc = param_write_int(plist, "%MediaDestination", |
560 | 0 | &data->media_destination)) < 0) return rc; |
561 | | |
562 | | /* Media source */ |
563 | 0 | if ((rc = param_write_int(plist, "%MediaSource", &data->media_source)) < 0) |
564 | 0 | return rc; |
565 | | |
566 | | /* Use of Configure Raster Data */ |
567 | 0 | if (is_generic_device(dev) || pcl_has_CRD(data->level)) { |
568 | 0 | bool temp = (data->level == pcl_level_3plus_CRD_only); |
569 | 0 | if ((rc = param_write_bool(plist, "OnlyCRD", &temp)) < 0) return rc; |
570 | 0 | } |
571 | | |
572 | | /* PCL initilization strings */ |
573 | 0 | if (data->init1.length == 0) { |
574 | 0 | if ((rc = param_write_null(plist, "PCLInit1")) < 0) return rc; |
575 | 0 | } |
576 | 0 | else { |
577 | 0 | string_value.data = (const byte *)data->init1.str; |
578 | 0 | string_value.size = data->init1.length; |
579 | 0 | string_value.persistent = false; |
580 | 0 | if ((rc = param_write_string(plist, "PCLInit1", &string_value)) < 0) |
581 | 0 | return rc; |
582 | 0 | } |
583 | 0 | if (data->init2.length == 0) { |
584 | 0 | if ((rc = param_write_null(plist, "PCLInit2")) < 0) return rc; |
585 | 0 | } |
586 | 0 | else { |
587 | 0 | string_value.data = (const byte *)data->init2.str; |
588 | 0 | string_value.size = data->init2.length; |
589 | 0 | string_value.persistent = false; |
590 | 0 | if ((rc = param_write_string(plist, "PCLInit2", &string_value)) < 0) |
591 | 0 | return rc; |
592 | 0 | } |
593 | | |
594 | | /* PJL job name */ |
595 | 0 | if (data->PJL_job == NULL) { |
596 | 0 | if ((rc = param_write_null(plist, "PJLJob")) < 0) return rc; |
597 | 0 | } |
598 | 0 | else { |
599 | 0 | string_value.data = (const byte *)data->PJL_job; |
600 | 0 | string_value.size = strlen(data->PJL_job); |
601 | 0 | string_value.persistent = false; |
602 | 0 | if ((rc = param_write_string(plist, "PJLJob", &string_value)) < 0) |
603 | 0 | return rc; |
604 | 0 | } |
605 | | |
606 | | /* PJL language */ |
607 | 0 | if (data->PJL_language == NULL) { |
608 | 0 | if ((rc = param_write_null(plist, "PJLLanguage")) < 0) return rc; |
609 | 0 | } |
610 | 0 | else { |
611 | 0 | string_value.data = (const byte *)data->PJL_language; |
612 | 0 | string_value.size = strlen(data->PJL_language); |
613 | 0 | string_value.persistent = false; |
614 | 0 | if ((rc = param_write_string(plist, "PJLLanguage", &string_value)) < 0) |
615 | 0 | return rc; |
616 | 0 | } |
617 | | |
618 | | /* Print quality */ |
619 | 0 | get_string_for_int(data->print_quality, print_quality_list, &string_value); |
620 | 0 | if ((rc = param_write_string(plist, "PrintQuality", &string_value)) < 0) |
621 | 0 | return rc; |
622 | | |
623 | | /* Black bit planes first (standard PCL) or last */ |
624 | 0 | { |
625 | 0 | bool temp = (data->order_CMYK == TRUE); |
626 | 0 | if ((rc = param_write_bool(plist, "SendBlackLast", &temp)) < 0) return rc; |
627 | 0 | } |
628 | | |
629 | | /* NUL header */ |
630 | 0 | if ((rc = param_write_int(plist, "SendNULs", &data->NULs_to_send)) < 0) |
631 | 0 | return rc; |
632 | | |
633 | | /* Subdevice name, but only for the generic device */ |
634 | 0 | if (is_generic_device(dev)) { |
635 | 0 | const char *name = find_subdevice_name(dev->printer); |
636 | 0 | string_value.data = (const byte *)name; |
637 | 0 | string_value.size = strlen(name); |
638 | 0 | string_value.persistent = true; |
639 | 0 | if ((rc = param_write_string(plist, "Subdevice", &string_value)) < 0) |
640 | 0 | return rc; |
641 | 0 | } |
642 | | |
643 | | /* Tumble */ |
644 | 0 | if (is_generic_device(dev)) |
645 | 0 | if ((rc = param_write_bool(plist, "Tumble", &dev->tumble)) < 0) return rc; |
646 | | |
647 | | /* UseCard */ |
648 | 0 | if (dev->use_card == bn_null) { |
649 | 0 | if ((rc = param_write_null(plist, "UseCard")) < 0) return rc; |
650 | 0 | } |
651 | 0 | else { |
652 | 0 | bool temp = (dev->use_card == bn_true); |
653 | 0 | if ((rc = param_write_bool(plist, "UseCard", &temp)) < 0) return rc; |
654 | 0 | } |
655 | | |
656 | | /* The old quality commands if they have meaning for this device */ |
657 | 0 | if (pcl_use_oldquality(data->level)) { |
658 | | /* Depletion */ |
659 | 0 | if (data->depletion == 0) { |
660 | 0 | if ((rc = param_write_null(plist, "Depletion")) < 0) return rc; |
661 | 0 | } |
662 | 0 | else if ((rc = param_write_int(plist, "Depletion", &data->depletion)) < 0) |
663 | 0 | return rc; |
664 | | |
665 | | /* Raster Graphics Quality */ |
666 | 0 | if ((rc = param_write_int(plist, "RasterGraphicsQuality", |
667 | 0 | &data->raster_graphics_quality)) < 0) return rc; |
668 | | |
669 | | /* Shingling */ |
670 | 0 | if ((rc = param_write_int(plist, "Shingling", &data->shingling)) < 0) |
671 | 0 | return rc; |
672 | 0 | } |
673 | 0 | else if (is_generic_device(dev)) { |
674 | | /* It is logically wrong for these parameters to be visible if we are |
675 | | dealing with a group-3 device, but I haven't yet found a way to get |
676 | | around ghostscript's undefined-parameter-update problem. */ |
677 | 0 | if ((rc = param_write_null(plist, "Depletion")) < 0) return rc; |
678 | 0 | if ((rc = param_write_null(plist, "RasterGraphicsQuality")) < 0) return rc; |
679 | 0 | if ((rc = param_write_null(plist, "Shingling")) < 0) return rc; |
680 | 0 | } |
681 | | |
682 | | #ifdef EPRN_TRACE |
683 | | if (gs_debug_c(EPRN_TRACE_CHAR)) { |
684 | | dmlprintf(dev->memory, "! pcl3_get_params() returns the following parameters:\n"); |
685 | | eprn_dump_parameter_list(plist); |
686 | | } |
687 | | #endif |
688 | | |
689 | 0 | return 0; |
690 | 0 | } |
691 | | |
692 | | /****************************************************************************** |
693 | | |
694 | | Function: fetch_octets |
695 | | |
696 | | This function checks whether 'plist' contains a string entry for the parameter |
697 | | 'pname' and returns the value via '*s' if it does. |
698 | | |
699 | | Neither 'plist' nor 'pname' may be NULL. On entry, '*s' must be a valid octet |
700 | | string; if it is non-zero, 's->str' must point to a gs_malloc()-allocated |
701 | | storage area of length 's->length'. |
702 | | |
703 | | If the parameter exists in 'plist', its type must be null or string. If it is |
704 | | null, '*s' will become zero. If the parameter type is string, the value will |
705 | | be copied to '*s'. |
706 | | |
707 | | The function returns a negative ghostscript error code on error and zero |
708 | | otherwise. In the former case an error message will have been issued, |
709 | | using 'epref' as a prefix for the message. |
710 | | |
711 | | ******************************************************************************/ |
712 | | |
713 | | static int fetch_octets(const char *epref, |
714 | | gs_param_list *plist, const char *pname, pcl_OctetString *s) |
715 | 0 | { |
716 | 0 | gs_param_string string_value; |
717 | 0 | int rc; |
718 | |
|
719 | 0 | if ((rc = param_read_null(plist, pname)) == 0) { |
720 | 0 | if (s->length != 0) |
721 | 0 | gs_free(plist->memory->non_gc_memory, s->str, s->length, sizeof(pcl_Octet), "fetch_octets"); |
722 | 0 | s->str = NULL; |
723 | 0 | s->length = 0; |
724 | 0 | } |
725 | 0 | else if (rc < 0 && |
726 | 0 | (rc = param_read_string(plist, pname, &string_value)) == 0) { |
727 | | /* Free old storage */ |
728 | 0 | if (s->length != 0) |
729 | 0 | gs_free(plist->memory->non_gc_memory, s->str, s->length, sizeof(pcl_Octet), "fetch_octets"); |
730 | | |
731 | | /* Allocate new */ |
732 | 0 | s->str = (pcl_Octet *)gs_malloc(plist->memory->non_gc_memory, string_value.size, sizeof(pcl_Octet), |
733 | 0 | "fetch_octets"); |
734 | |
|
735 | 0 | if (s->str == NULL) { |
736 | 0 | s->length = 0; |
737 | 0 | eprintf1("%s" ERRPREF |
738 | 0 | "Memory allocation failure from gs_malloc().\n", epref); |
739 | 0 | rc = gs_error_VMerror; |
740 | 0 | param_signal_error(plist, pname, rc); |
741 | 0 | } |
742 | 0 | else { |
743 | 0 | memcpy(s->str, string_value.data, string_value.size); |
744 | 0 | s->length = string_value.size; |
745 | 0 | } |
746 | 0 | } |
747 | 0 | else if (rc > 0) rc = 0; |
748 | |
|
749 | 0 | return rc; |
750 | 0 | } |
751 | | |
752 | | /****************************************************************************** |
753 | | |
754 | | Function: fetch_cstring |
755 | | |
756 | | This function checks whether 'plist' contains a string entry for the parameter |
757 | | 'pname' and returns the value via '*s' if it does. |
758 | | |
759 | | Neither 'plist' nor 'pname' may be NULL. On entry, '*s' must be NULL or point |
760 | | to a NUL-terminated string in a gs_malloc()-allocated storage area. |
761 | | |
762 | | If the parameter exists in 'plist', its type must be null or string. If it is |
763 | | null, '*s' will be gs_free()d and set to NULL. If it is a string, '*s' will |
764 | | be reallocated to contain the NUL-terminated value of the string. Should the |
765 | | string already contain a NUL value, only the part up to the NUL will be |
766 | | copied. |
767 | | |
768 | | The function returns a negative ghostscript error code on error and zero |
769 | | otherwise. In the former case an error message will have been issued. |
770 | | |
771 | | ******************************************************************************/ |
772 | | |
773 | | static int fetch_cstring(const char *epref, |
774 | | gs_param_list *plist, const char *pname, char **s) |
775 | 0 | { |
776 | 0 | gs_param_string string_value; |
777 | 0 | int rc; |
778 | |
|
779 | 0 | if ((rc = param_read_null(plist, pname)) == 0) { |
780 | 0 | if (*s != NULL) gs_free(plist->memory->non_gc_memory, *s, strlen(*s) + 1, sizeof(char), "fetch_cstring"); |
781 | 0 | *s = NULL; |
782 | 0 | } |
783 | 0 | else if (rc < 0 && |
784 | 0 | (rc = param_read_string(plist, pname, &string_value)) == 0) { |
785 | | /* Free old storage */ |
786 | 0 | if (*s != NULL) gs_free(plist->memory->non_gc_memory, *s, strlen(*s) + 1, sizeof(char), "fetch_cstring"); |
787 | | |
788 | | /* Allocate new */ |
789 | 0 | *s = (char *)gs_malloc(plist->memory->non_gc_memory, string_value.size + 1, sizeof(char), |
790 | 0 | "fetch_cstring"); |
791 | |
|
792 | 0 | if (*s == NULL) { |
793 | 0 | eprintf1("%s" ERRPREF |
794 | 0 | "Memory allocation failure from gs_malloc().\n", epref); |
795 | 0 | rc = gs_error_VMerror; |
796 | 0 | param_signal_error(plist, pname, rc); |
797 | 0 | } |
798 | 0 | else { |
799 | 0 | strncpy(*s, (const char*)string_value.data, string_value.size); |
800 | 0 | (*s)[string_value.size] = '\0'; |
801 | 0 | } |
802 | 0 | } |
803 | 0 | else if (rc > 0) rc = 0; |
804 | |
|
805 | 0 | return rc; |
806 | 0 | } |
807 | | |
808 | | /****************************************************************************** |
809 | | |
810 | | Function: set_palette |
811 | | |
812 | | This function sets 'dev->file_data.palette' and some other fields in |
813 | | agreement with 'dev->eprn.colour_model'. |
814 | | |
815 | | ******************************************************************************/ |
816 | | |
817 | | static void set_palette(pcl3_Device *dev) |
818 | 0 | { |
819 | 0 | pcl_FileData *data = &dev->file_data; |
820 | |
|
821 | 0 | switch(dev->eprn.colour_model) { |
822 | 0 | case eprn_DeviceGray: |
823 | 0 | { |
824 | 0 | const eprn_ColourInfo *ci = dev->eprn.cap->colour_info; |
825 | | |
826 | | /* Can this printer switch palettes? */ |
827 | 0 | while (ci->info[0] != NULL && ci->colour_model == eprn_DeviceGray) ci++; |
828 | 0 | if (ci->info[0] != NULL) data->palette = pcl_black; |
829 | 0 | else data->palette = pcl_no_palette; |
830 | 0 | } |
831 | 0 | data->number_of_colorants = 1; |
832 | 0 | data->depletion = 0; /* Depletion is only meaningful for colour. */ |
833 | 0 | break; |
834 | 0 | case eprn_DeviceCMY: |
835 | 0 | data->palette = pcl_CMY; |
836 | 0 | data->number_of_colorants = 3; |
837 | 0 | break; |
838 | 0 | case eprn_DeviceRGB: |
839 | 0 | data->palette = pcl_RGB; |
840 | 0 | data->number_of_colorants = 3; |
841 | 0 | break; |
842 | 0 | case eprn_DeviceCMY_plus_K: |
843 | | /*FALLTHROUGH*/ |
844 | 0 | case eprn_DeviceCMYK: |
845 | 0 | data->palette = pcl_CMYK; |
846 | 0 | data->number_of_colorants = 4; |
847 | 0 | break; |
848 | 0 | default: |
849 | 0 | assert(0); |
850 | 0 | } |
851 | | |
852 | 0 | return; |
853 | 0 | } |
854 | | |
855 | | /****************************************************************************** |
856 | | |
857 | | Function: pcl3_put_params |
858 | | |
859 | | This function reads a parameter list, extracts the parameters known to the |
860 | | device, and configures the device appropriately. This includes parameters |
861 | | defined by base classes. |
862 | | |
863 | | If an error occurs in the processing of parameters, the function will |
864 | | return a negative value, otherwise zero. |
865 | | |
866 | | This function does *not* exhibit transactional behaviour as requested in |
867 | | gsparam.h, i.e. on error the parameter values in the device structure |
868 | | might have changed. However, all values will be individually valid. |
869 | | |
870 | | Some of the parameters determine derived data in base classes or are relevant |
871 | | for device initialization. Setting any of these parameters closes the |
872 | | device if it is open. |
873 | | |
874 | | ******************************************************************************/ |
875 | | |
876 | | static int pcl3_put_params(gx_device *device, gs_param_list *plist) |
877 | 0 | { |
878 | 0 | bool new_quality = false; /* has someone requested the new variables? */ |
879 | 0 | gs_param_name pname; |
880 | 0 | gs_param_string string_value; |
881 | 0 | pcl3_Device *dev = (pcl3_Device *)device; |
882 | 0 | const char |
883 | 0 | *epref = dev->eprn.CUPS_messages? CUPS_ERRPREF: "", |
884 | 0 | *wpref = dev->eprn.CUPS_messages? CUPS_WARNPREF: ""; |
885 | 0 | eprn_ColourModel previous_colour_model = dev->eprn.colour_model; |
886 | 0 | pcl_FileData *data = &dev->file_data; |
887 | 0 | int |
888 | 0 | last_error = 0, |
889 | 0 | temp, |
890 | 0 | rc; |
891 | 0 | struct { |
892 | 0 | int depletion, quality, shingling; |
893 | 0 | } requested = {-1, -1, -1}; |
894 | | /* old quality parameters. -1 means "not specified". */ |
895 | | |
896 | | /* Check for subdevice selection */ |
897 | 0 | if (is_generic_device(dev)) { |
898 | 0 | if ((rc = param_read_string(plist, (pname = "Subdevice"), &string_value)) |
899 | 0 | == 0) { |
900 | | /* This property must be a known string. */ |
901 | 0 | int j = 0; |
902 | 0 | while (subdevice_list[j].name != NULL && |
903 | 0 | (string_value.size != strlen(subdevice_list[j].name) || |
904 | 0 | strncmp((const char *)string_value.data, subdevice_list[j].name, |
905 | 0 | string_value.size) != 0)) |
906 | 0 | j++; |
907 | | /* param_read_string() does not return NUL-terminated strings. */ |
908 | 0 | if (subdevice_list[j].name != NULL) { |
909 | 0 | if (dev->is_open) gs_closedevice(device); |
910 | 0 | dev->printer = subdevice_list[j].value; |
911 | 0 | dev->initialized = false; |
912 | 0 | rc = eprn_init_device((eprn_Device *)dev, &pcl3_printers[dev->printer].desc); |
913 | 0 | if (rc < 0) { |
914 | 0 | last_error = rc; |
915 | 0 | param_signal_error(plist, pname, last_error); |
916 | 0 | } |
917 | 0 | } |
918 | 0 | else { |
919 | 0 | eprintf1("%s" ERRPREF "Unknown subdevice name: `", epref); |
920 | 0 | errwrite(dev->memory, |
921 | 0 | (const char *)string_value.data, |
922 | 0 | sizeof(char)*string_value.size); |
923 | 0 | eprintf("'.\n"); |
924 | 0 | last_error = gs_error_rangecheck; |
925 | 0 | param_signal_error(plist, pname, last_error); |
926 | 0 | } |
927 | 0 | } |
928 | 0 | else if (rc < 0) last_error = rc; |
929 | 0 | } |
930 | | |
931 | | /* Constructor */ |
932 | 0 | if (!dev->initialized) init(dev); |
933 | | |
934 | | /* Compression method */ |
935 | 0 | if ((rc = param_read_int(plist, (pname = "CompressionMethod"), &temp)) |
936 | 0 | == 0) { |
937 | 0 | if (temp != pcl_cm_none && temp != pcl_cm_rl && temp != pcl_cm_tiff && |
938 | 0 | temp != pcl_cm_delta && temp != pcl_cm_crdr) { |
939 | 0 | eprintf2("%s" ERRPREF "Unsupported compression method: %d.\n", |
940 | 0 | epref, temp); |
941 | 0 | last_error = gs_error_rangecheck; |
942 | 0 | param_signal_error(plist, pname, last_error); |
943 | 0 | } |
944 | 0 | else { |
945 | 0 | if (temp == pcl_cm_crdr && (dev->printer == HPDeskJet || |
946 | 0 | dev->printer == HPDeskJetPlus || dev->printer == HPDJ500)) { |
947 | | /* This I know to be the case for the DJ 500. The others are guessed. */ |
948 | 0 | eprintf2( |
949 | 0 | "%s" ERRPREF "The %s does not support compression method 9.\n", |
950 | 0 | epref, dev->eprn.cap->name); |
951 | 0 | last_error = gs_error_rangecheck; |
952 | 0 | param_signal_error(plist, pname, last_error); |
953 | 0 | } |
954 | 0 | else data->compression= temp; |
955 | 0 | } |
956 | 0 | } |
957 | 0 | else if (rc < 0) last_error = rc; |
958 | | |
959 | | /* Configure every page */ |
960 | 0 | if ((rc = param_read_bool(plist, "ConfigureEveryPage", |
961 | 0 | &dev->configure_every_page)) < 0) last_error = rc; |
962 | | |
963 | | /* Depletion */ |
964 | 0 | if ((rc = param_read_null(plist, (pname = "Depletion"))) == 0) |
965 | 0 | requested.depletion = 0; |
966 | 0 | else if (rc < 0 && (rc = param_read_int(plist, pname, &temp)) == 0) { |
967 | 0 | if (1 <= temp && temp <= 5 && (dev->printer != HPDJ500C || temp <= 3)) |
968 | 0 | requested.depletion = temp; |
969 | 0 | else { |
970 | 0 | eprintf2("%s" ERRPREF "Invalid value for depletion: %d.\n", |
971 | 0 | epref, temp); |
972 | 0 | last_error = gs_error_rangecheck; |
973 | 0 | param_signal_error(plist, pname, last_error); |
974 | 0 | } |
975 | 0 | } |
976 | 0 | else if (rc < 0) last_error = rc; |
977 | | |
978 | | /* Dry time */ |
979 | 0 | if ((rc = param_read_null(plist, (pname = "DryTime"))) == 0) |
980 | 0 | data->dry_time = -1; |
981 | 0 | else if (rc < 0 && |
982 | 0 | (rc = param_read_int(plist, pname, &temp)) == 0) { |
983 | 0 | if (0 <= temp && temp <= 1200) { |
984 | 0 | if (dev->printer == HPDJ500 || dev->printer == HPDJ500C) { |
985 | | /* According to HP (DJ6/8 p. 18), only some of the series 600 and 800 |
986 | | DeskJets respond to this command. I also suspect that the same is |
987 | | true for pre-DeskJet-500 printers. This should not matter, though, |
988 | | because the PCL interpreter should merely ignore the command. |
989 | | Hence I'm giving an error message only in those cases where HP |
990 | | explicitly states that the printer does not support the command. |
991 | | */ |
992 | 0 | eprintf2( |
993 | 0 | "%s" ERRPREF "The %s does not support setting a dry time.\n", |
994 | 0 | epref, dev->eprn.cap->name); |
995 | 0 | last_error = gs_error_rangecheck; |
996 | 0 | param_signal_error(plist, pname, last_error); |
997 | 0 | } |
998 | 0 | else data->dry_time = temp; |
999 | 0 | } |
1000 | 0 | else { |
1001 | 0 | eprintf2("%s" ERRPREF "Invalid value for the dry time: %d.\n", |
1002 | 0 | epref, temp); |
1003 | 0 | last_error = gs_error_rangecheck; |
1004 | 0 | param_signal_error(plist, pname, last_error); |
1005 | 0 | } |
1006 | 0 | } |
1007 | 0 | else if (rc < 0) last_error = rc; |
1008 | | |
1009 | | /* Duplex capability */ |
1010 | 0 | if (is_generic_device(dev)) { |
1011 | 0 | if ((rc = param_read_string(plist, (pname = "DuplexCapability"), |
1012 | 0 | &string_value)) == 0) { |
1013 | 0 | rc = eprn_get_int(&string_value, duplex_capabilities_list, &temp); |
1014 | 0 | if (rc == 0) { |
1015 | 0 | if (dev->printer == pcl3_generic_new || |
1016 | 0 | dev->printer == pcl3_generic_old || temp == Duplex_none) { |
1017 | 0 | dev->duplex_capability = temp; |
1018 | 0 | if (dev->duplex_capability == Duplex_none) |
1019 | 0 | dev->Duplex_set = 0; /* force to "null" */ |
1020 | 0 | } |
1021 | 0 | else { |
1022 | 0 | eprintf2("%s" ERRPREF |
1023 | 0 | "You can use a non-trivial value for DuplexCapability\n" |
1024 | 0 | "%s only for unspec and unspecold.\n", epref, epref); |
1025 | 0 | last_error = gs_error_rangecheck; |
1026 | 0 | param_signal_error(plist, pname, last_error); |
1027 | 0 | } |
1028 | 0 | } |
1029 | 0 | else { |
1030 | 0 | eprintf1("%s" ERRPREF "Invalid duplex capability: `", epref); |
1031 | 0 | errwrite(dev->memory, |
1032 | 0 | (const char *)string_value.data, |
1033 | 0 | sizeof(char)*string_value.size); |
1034 | 0 | eprintf("'.\n"); |
1035 | 0 | last_error = gs_error_rangecheck; |
1036 | 0 | param_signal_error(plist, pname, last_error); |
1037 | 0 | } |
1038 | 0 | } |
1039 | 0 | else if (rc < 0) last_error = rc; |
1040 | 0 | } |
1041 | | |
1042 | | /* Check on "Duplex". This parameter is really read in gdev_prn_put_params(), |
1043 | | but we check here to prevent it being set unless the printer really |
1044 | | supports duplex printing. I would prefer to use the 'Duplex_set' variable |
1045 | | for controlling the appearance of this page device parameter, but if I do, |
1046 | | I can set the parameter only from PostScript and not from the command line. |
1047 | | */ |
1048 | 0 | { |
1049 | 0 | bool temp; |
1050 | 0 | if ((rc = param_read_bool(plist, (pname = "Duplex"), &temp)) == 0 && |
1051 | 0 | temp && dev->duplex_capability == Duplex_none) { |
1052 | 0 | if (dev->printer == pcl3_generic_new || dev->printer == pcl3_generic_old) |
1053 | 0 | eprintf3("%s" ERRPREF |
1054 | 0 | "The '%s' device does not support duplex printing unless\n" |
1055 | 0 | "%s 'DuplexCapability' is not 'none'.\n", |
1056 | 0 | epref, find_subdevice_name(dev->printer), epref); |
1057 | 0 | else |
1058 | 0 | eprintf2("%s" ERRPREF |
1059 | 0 | "The %s does not support duplex printing.\n", |
1060 | 0 | epref, dev->eprn.cap->name); |
1061 | 0 | last_error = gs_error_rangecheck; |
1062 | 0 | param_signal_error(plist, pname, last_error); |
1063 | 0 | } |
1064 | | /* NO check of rc because "null" is legal. */ |
1065 | 0 | } |
1066 | | |
1067 | | /* Manual feed */ |
1068 | 0 | { |
1069 | 0 | bool temp; |
1070 | 0 | if ((rc = param_read_bool(plist, (pname = "ManualFeed"), &temp)) == 0) |
1071 | 0 | dev->file_data.manual_feed = temp; |
1072 | 0 | else if (rc < 0) last_error = rc; |
1073 | 0 | } |
1074 | | |
1075 | | /* PCL media type */ |
1076 | 0 | if ((rc = param_read_string(plist, (pname = "Medium"), &string_value)) == 0) { |
1077 | | /* We accept numerical and string values. Numerical values at present |
1078 | | officially defined are 0-6, but not all printers know all these values. |
1079 | | We give the user the benefit of the doubt, though, because the |
1080 | | value is simply passed through to the printer, except for the older |
1081 | | DeskJets where we map illegal values to "plain paper". |
1082 | | If the user specifies a string, however, it must be a known one. |
1083 | | */ |
1084 | 0 | rc = get_int_for_string(&string_value, media_type_list, &temp); |
1085 | 0 | if (rc != 0) { |
1086 | 0 | if (rc != gs_error_VMerror) { |
1087 | 0 | eprintf1("%s" ERRPREF "Unknown medium: `", epref); |
1088 | 0 | errwrite(dev->memory, |
1089 | 0 | (const char *)string_value.data, |
1090 | 0 | sizeof(char)*string_value.size); |
1091 | 0 | eprintf("'.\n"); |
1092 | 0 | } |
1093 | 0 | last_error = rc; |
1094 | 0 | param_signal_error(plist, pname, last_error); |
1095 | 0 | } |
1096 | 0 | else { |
1097 | 0 | new_quality = true; |
1098 | 0 | if (temp < 0 || 6 < temp) |
1099 | 0 | eprintf2("%s" WARNPREF "Unknown media type code: %d.\n", |
1100 | 0 | wpref, temp); |
1101 | 0 | pcl3_set_mediatype(data, temp); |
1102 | 0 | } |
1103 | 0 | } |
1104 | 0 | else if (rc < 0) last_error = rc; |
1105 | | |
1106 | | /* Media destination */ |
1107 | 0 | if ((rc = param_read_int(plist, (pname = "%MediaDestination"), |
1108 | 0 | &data->media_destination)) < 0) last_error = rc; |
1109 | | |
1110 | | /* Media source */ |
1111 | 0 | if ((rc = param_read_int(plist, (pname = "%MediaSource"), |
1112 | 0 | &data->media_source)) < 0) last_error = rc; |
1113 | 0 | else if (rc == 0 && dev->is_open) gs_closedevice(device); |
1114 | | /* In pcl3, %MediaSource is relevant for banner printing and hence page |
1115 | | layout which is determined in the open routine. */ |
1116 | | |
1117 | | /* Use of Configure Raster Data */ |
1118 | 0 | if (is_generic_device(dev) || pcl_has_CRD(data->level)) { |
1119 | 0 | bool temp; |
1120 | |
|
1121 | 0 | if ((rc = param_read_bool(plist, (pname = "OnlyCRD"), &temp)) == 0) { |
1122 | 0 | if (pcl_has_CRD(data->level)) |
1123 | 0 | data->level = (temp? pcl_level_3plus_CRD_only: pcl_level_3plus_S68); |
1124 | 0 | else if (temp == true) { |
1125 | 0 | eprintf1("%s" ERRPREF |
1126 | 0 | "OnlyCRD may be set only for group-3 devices.\n", epref); |
1127 | 0 | last_error = gs_error_rangecheck; |
1128 | 0 | param_signal_error(plist, pname, last_error); |
1129 | 0 | } |
1130 | 0 | } |
1131 | 0 | else if (rc < 0) last_error = rc; |
1132 | 0 | } |
1133 | | |
1134 | | /* PCL initialization string 1 */ |
1135 | 0 | rc = fetch_octets(epref, plist, "PCLInit1", &data->init1); |
1136 | 0 | if (rc < 0) last_error = rc; |
1137 | | |
1138 | | /* PCL initialization string 2 */ |
1139 | 0 | rc = fetch_octets(epref, plist, "PCLInit2", &data->init2); |
1140 | 0 | if (rc < 0) last_error = rc; |
1141 | | |
1142 | | /* PJL job name */ |
1143 | 0 | rc = fetch_cstring(epref, plist, "PJLJob", &data->PJL_job); |
1144 | 0 | if (rc < 0) last_error = rc; |
1145 | | |
1146 | | /* PJL language */ |
1147 | 0 | rc = fetch_cstring(epref, plist, "PJLLanguage", &data->PJL_language); |
1148 | 0 | if (rc < 0) last_error = rc; |
1149 | | |
1150 | | /* Print Quality */ |
1151 | 0 | if ((rc = param_read_string(plist, (pname = "PrintQuality"), &string_value)) |
1152 | 0 | == 0) { |
1153 | | /* The only known values are -1, 0 and 1. Again, however, we assume the |
1154 | | user knows what s/he is doing if another value is given. */ |
1155 | 0 | rc = get_int_for_string(&string_value, print_quality_list, &temp); |
1156 | 0 | if (rc != 0) { |
1157 | 0 | if (rc != gs_error_VMerror) { |
1158 | 0 | eprintf1("%s" ERRPREF "Unknown print quality: `", epref); |
1159 | 0 | errwrite(dev->memory, |
1160 | 0 | (const char *)string_value.data, |
1161 | 0 | sizeof(char)*string_value.size); |
1162 | 0 | eprintf("'.\n"); |
1163 | 0 | } |
1164 | 0 | last_error = rc; |
1165 | 0 | param_signal_error(plist, pname, last_error); |
1166 | 0 | } |
1167 | 0 | else { |
1168 | 0 | new_quality = true; |
1169 | 0 | if (temp < -1 || 1 < temp) |
1170 | 0 | eprintf2("%s" WARNPREF "Unknown print quality: %d.\n", |
1171 | 0 | wpref, temp); |
1172 | 0 | pcl3_set_printquality(data, temp); |
1173 | 0 | } |
1174 | 0 | } |
1175 | 0 | else if (rc < 0) last_error = rc; |
1176 | | |
1177 | | /* Raster Graphics Quality */ |
1178 | 0 | if ((rc = param_read_null(plist, (pname = "RasterGraphicsQuality"))) == 0) |
1179 | 0 | ; /* ignore */ |
1180 | 0 | else if (rc < 0 && |
1181 | 0 | (rc = param_read_int(plist, (pname = "RasterGraphicsQuality"), &temp)) |
1182 | 0 | == 0) { |
1183 | 0 | if (0 <= temp && temp <= 2) requested.quality = temp; |
1184 | 0 | else { |
1185 | 0 | eprintf2( |
1186 | 0 | "%s" ERRPREF "Invalid value for raster graphics quality: %d.\n", |
1187 | 0 | epref, temp); |
1188 | 0 | last_error = gs_error_rangecheck; |
1189 | 0 | param_signal_error(plist, pname, last_error); |
1190 | 0 | } |
1191 | 0 | } |
1192 | 0 | else if (rc < 0) last_error = rc; |
1193 | | |
1194 | | /* Colorant order */ |
1195 | 0 | { |
1196 | 0 | bool temp; |
1197 | 0 | if ((rc = param_read_bool(plist, (pname = "SendBlackLast"), &temp)) == 0) |
1198 | 0 | data->order_CMYK = temp; |
1199 | 0 | else if (rc < 0 ) last_error = rc; |
1200 | 0 | } |
1201 | | |
1202 | | /* Sending of NULs */ |
1203 | 0 | if ((rc = param_read_int(plist, (pname = "SendNULs"), &temp)) == 0) { |
1204 | 0 | if (data->NULs_to_send >= 0) data->NULs_to_send = temp; |
1205 | 0 | else { |
1206 | 0 | eprintf2( |
1207 | 0 | "%s" ERRPREF "Invalid value for SendNULs parameter: %d.\n", |
1208 | 0 | epref, temp); |
1209 | 0 | last_error = gs_error_rangecheck; |
1210 | 0 | param_signal_error(plist, pname, last_error); |
1211 | 0 | } |
1212 | 0 | } |
1213 | 0 | else if (rc < 0 ) last_error = rc; |
1214 | | |
1215 | | /* Shingling */ |
1216 | 0 | if ((rc = param_read_null(plist, (pname = "Shingling"))) == 0) |
1217 | 0 | ; /* ignore */ |
1218 | 0 | else if (rc < 0 && |
1219 | 0 | (rc = param_read_int(plist, pname, &temp)) == 0) { |
1220 | 0 | if (0 <= temp && temp <= 2) requested.shingling = temp; |
1221 | 0 | else { |
1222 | 0 | eprintf2("%s" ERRPREF "Invalid value for shingling: %d.\n", |
1223 | 0 | epref, temp); |
1224 | 0 | last_error = gs_error_rangecheck; |
1225 | 0 | param_signal_error(plist, pname, last_error); |
1226 | 0 | } |
1227 | 0 | } |
1228 | 0 | else if (rc < 0) last_error = rc; |
1229 | | |
1230 | | /* Tumble */ |
1231 | 0 | if (is_generic_device(dev)) |
1232 | 0 | if ((rc = param_read_bool(plist, (pname = "Tumble"), &dev->tumble)) < 0) |
1233 | 0 | last_error = rc; |
1234 | | |
1235 | | /* UseCard */ |
1236 | 0 | { |
1237 | 0 | bool temp; |
1238 | |
|
1239 | 0 | if ((rc = param_read_null(plist, (pname = "UseCard"))) == 0) |
1240 | 0 | dev->use_card = bn_null; |
1241 | 0 | else if (rc < 0 && |
1242 | 0 | (rc = param_read_bool(plist, pname, &temp)) == 0) |
1243 | 0 | dev->use_card = (temp? bn_true: bn_false); |
1244 | 0 | else if (rc < 0 ) last_error = rc; |
1245 | 0 | } |
1246 | | |
1247 | | /* Process parameters defined by base classes (should occur after treating |
1248 | | parameters defined for the derived class, see gsparam.h) */ |
1249 | 0 | rc = eprn_put_params(device, plist); |
1250 | 0 | if (rc < 0 || (rc > 0 && last_error >= 0)) |
1251 | 0 | last_error = rc; |
1252 | | |
1253 | | /* Act if the colour model was changed */ |
1254 | 0 | if (previous_colour_model != dev->eprn.colour_model) set_palette(dev); |
1255 | |
|
1256 | 0 | if (last_error < 0) return_error(last_error); |
1257 | | |
1258 | | /* If we have seen new quality parameters, derive the old ones from them |
1259 | | based on the current and possibly new value of the palette. */ |
1260 | 0 | if (new_quality) pcl3_set_oldquality(data); |
1261 | | |
1262 | | /* If we have seen old quality parameters, store them */ |
1263 | 0 | if (pcl_use_oldquality(data->level)) { |
1264 | 0 | if (requested.depletion >= 0) data->depletion = requested.depletion; |
1265 | 0 | if (requested.quality >= 0) |
1266 | 0 | data->raster_graphics_quality = requested.quality; |
1267 | 0 | if (requested.shingling >= 0) data->shingling = requested.shingling; |
1268 | 0 | } |
1269 | |
|
1270 | 0 | return 0; |
1271 | 0 | } |
1272 | | |
1273 | | /****************************************************************************** |
1274 | | |
1275 | | Function: pcl3_open_device |
1276 | | |
1277 | | ******************************************************************************/ |
1278 | | |
1279 | | static int pcl3_open_device(gx_device *device) |
1280 | 0 | { |
1281 | 0 | pcl3_Device *dev = (pcl3_Device *)device; |
1282 | 0 | const char |
1283 | 0 | *epref = dev->eprn.CUPS_messages? CUPS_ERRPREF: "", |
1284 | 0 | *wpref = dev->eprn.CUPS_messages? CUPS_WARNPREF: ""; |
1285 | 0 | int rc; |
1286 | | |
1287 | | /* Constructor */ |
1288 | 0 | if (!dev->initialized) init(dev); |
1289 | |
|
1290 | | #ifdef PCL3_MEDIA_FILE |
1291 | | /* Change default media descriptions for 'unspec' */ |
1292 | | if (dev->eprn.media_file == NULL && dev->printer == pcl3_generic_new) { |
1293 | | if ((rc = eprn_set_media_data(device, PCL3_MEDIA_FILE, 0)) != 0) |
1294 | | return rc; |
1295 | | } |
1296 | | #endif |
1297 | | |
1298 | | /* Check on rendering parameters */ |
1299 | 0 | if ((dev->eprn.black_levels > 2 || dev->eprn.non_black_levels > 2) && |
1300 | 0 | dev->file_data.print_quality == -1) |
1301 | 0 | eprintf2( |
1302 | 0 | "%s" WARNPREF "More than 2 intensity levels and draft quality\n" |
1303 | 0 | "%s are unlikely to work in combination.\n", wpref, wpref); |
1304 | | |
1305 | | /* Ensure correct media request flags */ |
1306 | 0 | eprn_set_media_flags((eprn_Device *)dev, |
1307 | 0 | (dev->file_data.media_source == -1? MS_BIG_FLAG: ms_none) | |
1308 | 0 | (dev->use_card == bn_true? PCL_CARD_FLAG: ms_none), |
1309 | 0 | (dev->use_card == bn_null? card_is_optional: NULL)); |
1310 | |
|
1311 | 0 | dev->eprn.soft_tumble = false; |
1312 | | |
1313 | | /* Open the "eprn" device part */ |
1314 | 0 | if ((rc = eprn_open_device(device)) != 0) return rc; |
1315 | | |
1316 | | /* if device has been subclassed (FirstPage/LastPage device) then make sure we use |
1317 | | * the subclassed device. |
1318 | | */ |
1319 | 0 | while (device->child) |
1320 | 0 | device = device->child; |
1321 | |
|
1322 | 0 | dev = (pcl3_Device *)device; |
1323 | | |
1324 | | /* Fill the still unassigned parts of 'file_data' from the other data */ |
1325 | 0 | { |
1326 | 0 | pcl_FileData *data = &dev->file_data; |
1327 | 0 | unsigned int j; |
1328 | | |
1329 | | /* Media handling */ |
1330 | 0 | data->size = pcl3_page_size(&dev->table, dev->eprn.code); |
1331 | 0 | if (data->size == pcl_ps_default) { |
1332 | | /* This is due to a media description using a media size code for which |
1333 | | there is no PCL Page Size code. This is either an error in a builtin |
1334 | | description or the user specified it in a media configuration file. |
1335 | | Note that there might be a "Card" flag, hence we should not talk |
1336 | | about "size" only. |
1337 | | */ |
1338 | 0 | char buffer[50]; |
1339 | |
|
1340 | 0 | eprintf2("%s" ERRPREF |
1341 | 0 | "The current configuration for this driver has identified the\n" |
1342 | 0 | "%s page setup requested by the document as being for `", |
1343 | 0 | epref, epref); |
1344 | 0 | if (ms_find_name_from_code(buffer, sizeof(buffer), |
1345 | 0 | dev->eprn.code, flag_description) == 0) eprintf1("%s", buffer); |
1346 | 0 | else eprintf("UNKNOWN"); /* should never happen */ |
1347 | 0 | eprintf3("' (%.0f x %.0f bp).\n" |
1348 | 0 | "%s The driver does not know how to do this in PCL.\n", |
1349 | 0 | dev->MediaSize[0], dev->MediaSize[1], epref); |
1350 | 0 | if (dev->eprn.media_file != NULL) |
1351 | 0 | eprintf2( |
1352 | 0 | "%s You should therefore not include such an entry in the\n" |
1353 | 0 | "%s media configuration file.\n", epref, epref); |
1354 | 0 | return_error(gs_error_rangecheck); |
1355 | 0 | } |
1356 | 0 | data->duplex = -1; |
1357 | 0 | if (dev->Duplex_set > 0) { /* Duplex is not null */ |
1358 | 0 | if (dev->Duplex) { |
1359 | 0 | bool same_leading_edge; |
1360 | | |
1361 | | /* Find direction of default user space y axis in device space */ |
1362 | 0 | int orient = dev->eprn.default_orientation; |
1363 | 0 | if (dev->MediaSize[1] < dev->MediaSize[0]) /* landscape */ |
1364 | 0 | orient++; /* rotate +90 degrees */ |
1365 | |
|
1366 | 0 | same_leading_edge = (orient % 2 == 0 /* y axis is vertical */) != |
1367 | 0 | (dev->tumble != false); |
1368 | | /* If there were a native 'bool' type in C, the last parenthesis |
1369 | | could be reliably replaced by "dev->tumble". This is safer and |
1370 | | just as fast, provided the compiler is sufficiently intelligent. */ |
1371 | |
|
1372 | 0 | dev->eprn.soft_tumble = dev->duplex_capability != Duplex_both && |
1373 | 0 | ((same_leading_edge && |
1374 | 0 | dev->duplex_capability != Duplex_sameLeadingEdge) || |
1375 | 0 | (!same_leading_edge && |
1376 | 0 | dev->duplex_capability != Duplex_oppositeLeadingEdge)); |
1377 | 0 | if (dev->eprn.soft_tumble) same_leading_edge = !same_leading_edge; |
1378 | | |
1379 | | /* I am assuming here that the values 1 and 2, specified by HP in |
1380 | | BPL02705 as meaning "Long-Edge Binding" and "Short-Edge Binding", |
1381 | | respectively, in fact mean what I've called the "same leading edge" |
1382 | | and "opposite leading edge" settings for the second pass. */ |
1383 | 0 | if (same_leading_edge) data->duplex = 1; |
1384 | 0 | else data->duplex = 2; |
1385 | 0 | } |
1386 | 0 | else data->duplex = 0; /* simplex */ |
1387 | 0 | } |
1388 | | |
1389 | | /* It is almost not necessary to set the palette here because the default |
1390 | | settings of eprn and pcl3 agree and all other calls are routed through |
1391 | | the put_params routines. But there is a special case: I want to use |
1392 | | 'pcl_no_palette' if the printer cannot switch palettes. */ |
1393 | 0 | set_palette(dev); |
1394 | | |
1395 | | /* Per-colorant information */ |
1396 | 0 | for (j = 0; j < data->number_of_colorants; j++) { |
1397 | 0 | data->colorant_array[j].hres = (int)(dev->HWResolution[0] + 0.5); |
1398 | 0 | data->colorant_array[j].vres = (int)(dev->HWResolution[1] + 0.5); |
1399 | 0 | } |
1400 | 0 | if (data->palette == pcl_CMY || data->palette == pcl_RGB) |
1401 | 0 | for (j = 0; j < 3; j++) |
1402 | 0 | data->colorant_array[j].levels = dev->eprn.non_black_levels; |
1403 | 0 | else { |
1404 | 0 | data->colorant_array[0].levels = dev->eprn.black_levels; |
1405 | 0 | for (j = 1; j < data->number_of_colorants; j++) |
1406 | 0 | data->colorant_array[j].levels = dev->eprn.non_black_levels; |
1407 | 0 | } |
1408 | 0 | } |
1409 | | |
1410 | 0 | return rc; |
1411 | 0 | } |
1412 | | |
1413 | | /****************************************************************************** |
1414 | | |
1415 | | Function: pcl3_close_device |
1416 | | |
1417 | | ******************************************************************************/ |
1418 | | |
1419 | | static int pcl3_close_device(gx_device *device) |
1420 | 0 | { |
1421 | 0 | pcl3_Device *dev = (pcl3_Device *)device; |
1422 | | |
1423 | | /* HP recommends that a driver should send the Printer Reset command at the |
1424 | | end of each print job in order to leave the printer in its default state. |
1425 | | This is a matter of courtesy for the next print job which could otherwise |
1426 | | inherit some of the properties set for the present job unless it starts |
1427 | | with a Printer Reset command itself (every job generated with this driver |
1428 | | does). |
1429 | | |
1430 | | Unfortunately, ghostscript does not have a corresponding device procedure. |
1431 | | In particular, the 'close_device' procedure may be called multiple times |
1432 | | during a job and for multi-file output it is even only called at the end |
1433 | | of the sequence of files and then when 'dev->file' is already NULL. |
1434 | | Hence this routine tries to get close by checking the 'configured' field: |
1435 | | it is set if the pcl3_init_file() function has been called and therefore |
1436 | | indicates that the driver has sent configuration commands to the printer. |
1437 | | That part we can and should take back. |
1438 | | |
1439 | | Of course one might reset the printer at the end of every page, but this |
1440 | | would entail having to repeat the initialization at the beginning of |
1441 | | every page. I regard this as logically inappropriate. |
1442 | | */ |
1443 | |
|
1444 | 0 | if (dev->configured && dev->file != NULL) { |
1445 | 0 | pcl3_end_file(dev->file, &dev->file_data); |
1446 | 0 | dev->configured = false; |
1447 | 0 | } |
1448 | |
|
1449 | 0 | return eprn_close_device(device); |
1450 | 0 | } |
1451 | | |
1452 | | /****************************************************************************** |
1453 | | |
1454 | | Function: pcl3_print_page |
1455 | | |
1456 | | This is the implementation of prn's print_page() method for this device. |
1457 | | |
1458 | | It initializes the printer if necessary and prints the page. |
1459 | | |
1460 | | ******************************************************************************/ |
1461 | | |
1462 | | /* Function to convert return codes from calls to pclgen routines. */ |
1463 | | static int convert(int code) |
1464 | 0 | { |
1465 | 0 | if (code > 0) return gs_error_Fatal; |
1466 | 0 | if (code < 0) return gs_error_ioerror; |
1467 | 0 | return 0; |
1468 | 0 | } |
1469 | | |
1470 | | static int pcl3_print_page(gx_device_printer *device, gp_file *out) |
1471 | 0 | { |
1472 | 0 | int |
1473 | 0 | blank_lines, |
1474 | 0 | rc = 0; |
1475 | 0 | pcl3_Device *dev = (pcl3_Device *)device; |
1476 | 0 | const char *epref = dev->eprn.CUPS_messages? CUPS_ERRPREF: ""; |
1477 | 0 | pcl_RasterData rd; |
1478 | 0 | unsigned int |
1479 | 0 | j, |
1480 | 0 | *lengths, |
1481 | 0 | planes; |
1482 | 0 | size_t alloc_size = 0; |
1483 | | |
1484 | | /* Make sure out cleanup code will cope with partially-initialised data. */ |
1485 | 0 | memset(&rd, 0, sizeof(pcl_RasterData)); /* Belt and braces. */ |
1486 | 0 | planes = 0; |
1487 | 0 | lengths = NULL; |
1488 | 0 | rd.next = NULL; |
1489 | 0 | rd.previous = NULL; |
1490 | 0 | rd.workspace[0] = NULL; |
1491 | 0 | rd.workspace[1] = NULL; |
1492 | | |
1493 | | /* If this is a new file or we've decided to re-configure, initialize the |
1494 | | printer first */ |
1495 | 0 | if (gdev_prn_file_is_new(device) || !dev->configured || |
1496 | 0 | dev->configure_every_page) { |
1497 | 0 | rc = convert(pcl3_init_file(device->memory, out, &dev->file_data)); |
1498 | 0 | if (rc) { |
1499 | 0 | (void) gs_note_error(rc); |
1500 | 0 | goto end; |
1501 | 0 | } |
1502 | 0 | dev->configured = true; |
1503 | 0 | } |
1504 | | |
1505 | | /* Initialize raster data structure */ |
1506 | 0 | rd.global = &dev->file_data; |
1507 | 0 | planes = eprn_number_of_bitplanes((eprn_Device *)dev); |
1508 | 0 | alloc_size = planes*sizeof(unsigned int); |
1509 | 0 | lengths = (unsigned int *)malloc(alloc_size); |
1510 | 0 | if (!lengths) { |
1511 | 0 | rc = gs_note_error(gs_error_VMerror); |
1512 | 0 | goto end; |
1513 | 0 | } |
1514 | 0 | eprn_number_of_octets((eprn_Device *)dev, lengths); |
1515 | 0 | alloc_size = planes*sizeof(pcl_OctetString); |
1516 | 0 | rd.next = (pcl_OctetString *)malloc(alloc_size); |
1517 | 0 | if (!rd.next) { |
1518 | 0 | rc = gs_note_error(gs_error_VMerror); |
1519 | 0 | goto end; |
1520 | 0 | } |
1521 | 0 | for (j=0; j<planes; j++) { /* Make sure we can free at any point. */ |
1522 | 0 | rd.next[j].str = NULL; |
1523 | 0 | } |
1524 | 0 | if (pcl_cm_is_differential(dev->file_data.compression)) { |
1525 | 0 | rd.previous = (pcl_OctetString *)malloc(alloc_size); |
1526 | 0 | if (!rd.previous) { |
1527 | 0 | rc = gs_note_error(gs_error_VMerror); |
1528 | 0 | goto end; |
1529 | 0 | } |
1530 | 0 | for (j=0; j<planes; j++) { /* Make sure we can free at any point. */ |
1531 | 0 | rd.previous[j].str = NULL; |
1532 | 0 | } |
1533 | 0 | for (j = 0; j < planes; j++) { |
1534 | 0 | alloc_size = lengths[j]*sizeof(eprn_Octet); |
1535 | 0 | rd.previous[j].str = (pcl_Octet *)malloc(alloc_size); |
1536 | 0 | if (!rd.previous[j].str) { |
1537 | 0 | rc = gs_note_error(gs_error_VMerror); |
1538 | 0 | goto end; |
1539 | 0 | } |
1540 | 0 | } |
1541 | 0 | } |
1542 | 0 | rd.width = 8*lengths[0]; /* all colorants have equal resolution */ |
1543 | 0 | for (j = 0; j < planes; j++) { |
1544 | 0 | alloc_size = lengths[j]*sizeof(eprn_Octet); |
1545 | 0 | rd.next[j].str = (pcl_Octet *)malloc(alloc_size); |
1546 | 0 | if (!rd.next[j].str) { |
1547 | 0 | rc = gs_note_error(gs_error_VMerror); |
1548 | 0 | goto end; |
1549 | 0 | } |
1550 | 0 | } |
1551 | | /* Note: 'pcl_Octet' must be identical with 'eprn_Octet'. */ |
1552 | 0 | rd.workspace_allocated = lengths[0]; |
1553 | 0 | for (j = 1; j < planes; j++) |
1554 | 0 | if (lengths[j] > rd.workspace_allocated) |
1555 | 0 | rd.workspace_allocated = lengths[j]; |
1556 | 0 | for (j = 0; |
1557 | 0 | j < 2 && (j != 1 || dev->file_data.compression == pcl_cm_delta); j++) { |
1558 | 0 | alloc_size = rd.workspace_allocated*sizeof(pcl_Octet); |
1559 | 0 | rd.workspace[j] = |
1560 | 0 | (pcl_Octet *)malloc(alloc_size); |
1561 | 0 | if (!rd.workspace[j]) { |
1562 | 0 | rc = gs_note_error(gs_error_VMerror); |
1563 | 0 | goto end; |
1564 | 0 | } |
1565 | 0 | } |
1566 | | |
1567 | | /* Open the page and start raster mode */ |
1568 | 0 | rc = convert(pcl3_begin_page(out, &dev->file_data)); |
1569 | 0 | if (rc) { |
1570 | 0 | (void) gs_note_error(rc); |
1571 | 0 | goto end; |
1572 | 0 | } |
1573 | 0 | rc = convert(pcl3_begin_raster(out, &rd)); |
1574 | 0 | if (rc) { |
1575 | 0 | (void) gs_note_error(rc); |
1576 | 0 | goto end; |
1577 | 0 | } |
1578 | | |
1579 | | /* Loop over scan lines */ |
1580 | 0 | blank_lines = 0; |
1581 | 0 | while (eprn_get_planes((eprn_Device *)dev, (eprn_OctetString *)rd.next) == 0){ |
1582 | | /* Is this a blank (white) line? */ |
1583 | 0 | if (dev->eprn.colour_model == eprn_DeviceRGB) { |
1584 | | /* White results if all three colorants use their highest intensity. |
1585 | | Fortunately, PCL-3+ can only support two intensity levels for all |
1586 | | colorants in an RGB palette, hence this intensity must be one for all |
1587 | | colorants simultaneously. |
1588 | | Because the planes returned by eprn_get_planes() are guaranteed to |
1589 | | have no trailing zero octets, we can easily check that they are of |
1590 | | equal length before proceeding further. |
1591 | | */ |
1592 | 0 | for (j = 1; j < planes && rd.next[j].length == rd.next[0].length; j++); |
1593 | 0 | if (j >= planes && rd.next[0].length == lengths[0]) { |
1594 | 0 | int k; |
1595 | | /* All planes have the same length and cover the whole width of the |
1596 | | page. Check that they all contain 0xFF. */ |
1597 | 0 | j = 0; |
1598 | 0 | do { |
1599 | 0 | k = rd.next[j].length - 1; |
1600 | 0 | while (k > 0 && rd.next[j].str[k] == 0xFF) k--; |
1601 | 0 | } while (k == 0 && rd.next[j].str[0] == 0xFF && ++j < planes); |
1602 | 0 | } |
1603 | 0 | } |
1604 | 0 | else |
1605 | | /* White is zero */ |
1606 | 0 | for (j = 0; j < planes && rd.next[j].length == 0; j++); |
1607 | |
|
1608 | 0 | if (j == planes) blank_lines++; |
1609 | 0 | else { |
1610 | 0 | if (blank_lines > 0) { |
1611 | 0 | rc = convert(pcl3_skip_groups(out, &rd, blank_lines)); |
1612 | 0 | if (rc) { |
1613 | 0 | (void) gs_note_error(rc); |
1614 | 0 | goto end; |
1615 | 0 | } |
1616 | 0 | blank_lines = 0; |
1617 | 0 | } |
1618 | 0 | rc = convert(pcl3_transfer_group(out, &rd)); |
1619 | 0 | if (rc) { |
1620 | 0 | (void) gs_note_error(rc); |
1621 | 0 | goto end; |
1622 | 0 | } |
1623 | 0 | } |
1624 | 0 | } |
1625 | | |
1626 | | /* Terminate raster mode and close the page */ |
1627 | 0 | rc = convert(pcl3_end_raster(out, &rd)); |
1628 | 0 | if (rc) { |
1629 | 0 | (void) gs_note_error(rc); |
1630 | 0 | goto end; |
1631 | 0 | } |
1632 | 0 | rc = convert(pcl3_end_page(out, &dev->file_data)); |
1633 | 0 | if (rc) { |
1634 | 0 | (void) gs_note_error(rc); |
1635 | 0 | goto end; |
1636 | 0 | } |
1637 | | |
1638 | 0 | end: |
1639 | | /* Free dynamic storage */ |
1640 | 0 | if (rd.next) { |
1641 | 0 | for (j = 0; j < planes; j++) { |
1642 | 0 | free(rd.next[j].str); |
1643 | 0 | } |
1644 | 0 | } |
1645 | 0 | if (rd.previous) { |
1646 | 0 | for (j = 0; j < planes; j++) { |
1647 | 0 | free(rd.previous[j].str); |
1648 | 0 | } |
1649 | 0 | } |
1650 | 0 | for (j = 0; j < 2; j++) { |
1651 | 0 | free(rd.workspace[j]); |
1652 | 0 | } |
1653 | 0 | free(lengths); |
1654 | 0 | free(rd.next); |
1655 | 0 | free(rd.previous); |
1656 | |
|
1657 | 0 | if (rc == gs_error_VMerror) { |
1658 | 0 | eprintf1("%s" ERRPREF "Memory allocation failure from malloc().\n", |
1659 | 0 | epref); |
1660 | 0 | } |
1661 | |
|
1662 | 0 | return rc; |
1663 | 0 | } |