1 /* Copyright (C) 2001-2002 artofcode LLC. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: gdevijs.c,v 1.12 2005/05/27 05:43:24 dan Exp $ */
18 /*
19 * IJS device for Ghostscript.
20 * Intended to work with any IJS compliant inkjet driver, including
21 * hpijs 1.0 and later, an IJS-enhanced gimp-print driver, and
22 * the IJS Windows GDI server (ijsmswin.exe).
23 *
24 * DRAFT
25 *
26 * WARNING: The ijs server can be selected on the gs command line
27 * which is a security risk, since any program can be run.
28 * You should use -dSAFER which sets .LockSafetyParams to true
29 * before opening this device.
30 */
31
32 #include "unistd_.h" /* for dup() */
33 #include <stdlib.h>
34 #include "gdevprn.h"
35 #include "gp.h"
36 #include "ijs.h"
37 #include "ijs_client.h"
38
39 /* This should go into gdevprn.h, or, better yet, gdevprn should
40 acquire an API for changing resolution. */
41 int gdev_prn_maybe_realloc_memory(gx_device_printer *pdev,
42 gdev_prn_space_params *old_space,
43 int old_width, int old_height,
44 bool old_page_uses_transparency);
45
46 /* Device procedures */
47
48 /* See gxdevice.h for the definitions of the procedures. */
49 private dev_proc_open_device(gsijs_open);
50 private dev_proc_close_device(gsijs_close);
51 private dev_proc_output_page(gsijs_output_page);
52 private dev_proc_get_params(gsijs_get_params);
53 private dev_proc_put_params(gsijs_put_params);
54 private dev_proc_finish_copydevice(gsijs_finish_copydevice);
55
56 private const gx_device_procs gsijs_procs = {
57 gsijs_open,
58 NULL, /* get_initial_matrix */
59 NULL, /* sync_output */
60 gsijs_output_page,
61 gsijs_close,
62 gx_default_rgb_map_rgb_color,
63 gx_default_rgb_map_color_rgb,
64 NULL, /* fill_rectangle */
65 NULL, /* tile_rectangle */
66 NULL, /* copy_mono */
67 NULL, /* copy_color */
68 NULL, /* draw_line */
69 NULL, /* get_bits */
70 gsijs_get_params,
71 gsijs_put_params,
72 NULL, /* map_cmyk_color */
73 NULL, /* get_xfont_procs */
74 NULL, /* get_xfont_device */
75 NULL, /* map_rgb_alpha_color */
76 gx_page_device_get_page_device,
77 NULL, /* get_alpha_bits */
78 NULL, /* copy_alpha */
79 NULL, /* get_band */
80 NULL, /* copy_rop */
81 NULL, /* fill_path */
82 NULL, /* stroke_path */
83 NULL, /* fill_mask */
84 NULL, /* fill_trapezoid */
85 NULL, /* fill_parallelogram */
86 NULL, /* fill_triangle */
87 NULL, /* draw_thin_line */
88 NULL, /* begin_image */
89 NULL, /* image_data */
90 NULL, /* end_image */
91 NULL, /* strip_tile_rectangle */
92 NULL, /* strip_copy_rop, */
93 NULL, /* get_clipping_box */
94 NULL, /* begin_typed_image */
95 NULL, /* get_bits_rectangle */
96 NULL, /* map_color_rgb_alpha */
97 NULL, /* create_compositor */
98 NULL, /* get_hardware_params */
99 NULL, /* text_begin */
100 gsijs_finish_copydevice
101 };
102
103 typedef struct gx_device_ijs_s gx_device_ijs;
104
105 /* The device descriptor */
106 struct gx_device_ijs_s {
107 gx_device_common;
108 gx_prn_device_common;
109 bool IjsUseOutputFD;
110 char IjsServer[gp_file_name_sizeof]; /* name of executable ijs server */
111 char *ColorSpace;
112 int ColorSpace_size;
113 int BitsPerSample;
114 char *DeviceManufacturer;
115 int DeviceManufacturer_size;
116 char *DeviceModel;
117 int DeviceModel_size;
118 char *IjsParams;
119 int IjsParams_size;
120
121 /* Common setpagedevice parameters supported by ijs but not
122 currently parsed by gx_prn_device. We prefix these with Ijs to
123 avoid namespace collision if they do get added to gx_prn_device.
124 */
125 bool IjsTumble;
126 bool IjsTumble_set;
127
128 IjsClientCtx *ctx;
129 int ijs_version;
130 };
131
132 #define DEFAULT_DPI 74 /* See gsijs_set_resolution() below. */
133
134 gx_device_ijs gs_ijs_device =
135 {
136 prn_device_std_body(gx_device_ijs, gsijs_procs, "ijs",
137 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
138 DEFAULT_DPI, DEFAULT_DPI,
139 0, 0, 0, 0,
140 24 /* depth */, NULL /* print page */),
141 FALSE, /* IjsUseOutputFD */
142 "", /* IjsServer */
143 NULL, /* ColorSpace */
144 0, /* ColorSpace_size */
145 8, /* BitsPerSample */
146 NULL, /* DeviceManufacturer */
147 0, /* DeviceManufacturer_size */
148 NULL, /* DeviceModel */
149 0, /* DeviceModel_size */
150 NULL, /* IjsParams */
151 0, /* IjsParams_size */
152
153 FALSE, /* Tumble */
154 FALSE, /* Tumble_set */
155
156 NULL, /* IjsClient *ctx */
157 0 /* ijs_version */
158 };
159
160
161 private int gsijs_client_set_param(gx_device_ijs *ijsdev, const char *key,
162 const char *value);
163 private int gsijs_set_color_format(gx_device_ijs *ijsdev);
164 private int gsijs_read_int(gs_param_list *plist, gs_param_name pname,
165 int *pval, int min_value, int max_value, bool only_when_closed);
166 private int gsijs_read_bool(gs_param_list *plist, gs_param_name pname,
167 bool *pval, bool only_when_closed);
168 private int gsijs_read_string(gs_param_list * plist, gs_param_name pname,
169 char * str, uint size, bool safety, bool only_when_closed);
170
171 /**************************************************************************/
172
173 /* ------ Private definitions ------ */
174
175 /* Versions 1.0 through 1.0.2 of hpijs report IJS version 0.29, and
176 require some workarounds. When more up-to-date hpijs versions
177 become ubiquitous, all these workarounds should be removed. */
178 #define HPIJS_1_0_VERSION 29
179
180 private int
gsijs_parse_wxh(const char * val,int size,double * pw,double * ph)181 gsijs_parse_wxh (const char *val, int size, double *pw, double *ph)
182 {
183 char buf[256];
184 char *tail;
185 int i;
186
187 for (i = 0; i < size; i++)
188 if (val[i] == 'x')
189 break;
190
191 if (i + 1 >= size)
192 return IJS_ESYNTAX;
193
194 if (i >= sizeof(buf))
195 return IJS_EBUF;
196
197 memcpy (buf, val, i);
198 buf[i] = 0;
199 *pw = strtod (buf, &tail);
200 if (tail == buf)
201 return IJS_ESYNTAX;
202
203 if (size - i > sizeof(buf))
204 return IJS_EBUF;
205
206 memcpy (buf, val + i + 1, size - i - 1);
207 buf[size - i - 1] = 0;
208 *ph = strtod (buf, &tail);
209 if (tail == buf)
210 return IJS_ESYNTAX;
211
212 return 0;
213 }
214
215 /**
216 * gsijs_set_generic_params_hpijs: Set generic IJS parameters.
217 *
218 * This version is specialized for hpijs 1.0 through 1.0.2, and
219 * accommodates a number of quirks.
220 **/
221 private int
gsijs_set_generic_params_hpijs(gx_device_ijs * ijsdev)222 gsijs_set_generic_params_hpijs(gx_device_ijs *ijsdev)
223 {
224 char buf[256];
225 int code = 0;
226
227 /* IjsParams, Duplex, and Tumble get set at this point because
228 they may affect margins. */
229 if (ijsdev->IjsParams) {
230 code = gsijs_client_set_param(ijsdev, "IjsParams", ijsdev->IjsParams);
231 }
232
233 if (code == 0 && ijsdev->Duplex_set) {
234 int duplex_val;
235
236 duplex_val = ijsdev->Duplex ? (ijsdev->IjsTumble ? 1 : 2) : 0;
237 sprintf (buf, "%d", duplex_val);
238 code = gsijs_client_set_param(ijsdev, "Duplex", buf);
239 }
240 return code;
241 }
242
243 /**
244 * gsijs_set_generic_params: Set generic IJS parameters.
245 **/
246 private int
gsijs_set_generic_params(gx_device_ijs * ijsdev)247 gsijs_set_generic_params(gx_device_ijs *ijsdev)
248 {
249 char buf[256];
250 int code = 0;
251 int i, j;
252 char *value;
253
254 if (ijsdev->ijs_version == HPIJS_1_0_VERSION)
255 return gsijs_set_generic_params_hpijs(ijsdev);
256
257 /* Split IjsParams into separate parameters and send to ijs server */
258 value = NULL;
259 for (i=0, j=0; (j < ijsdev->IjsParams_size) && (i < sizeof(buf)-1); j++) {
260 char ch = ijsdev->IjsParams[j];
261 if (ch == '\\') {
262 j++;
263 buf[i++] = ijsdev->IjsParams[j];
264 }
265 else {
266 if (ch == '=') {
267 buf[i++] = '\0';
268 value = &buf[i];
269 }
270 else
271 buf[i++] = ch;
272 if (ch == ',') {
273 buf[i-1] = '\0';
274 if (value)
275 gsijs_client_set_param(ijsdev, buf, value);
276 i = 0;
277 value = NULL;
278 }
279 }
280 }
281 if (value)
282 code = gsijs_client_set_param(ijsdev, buf, value);
283
284 if (code == 0 && ijsdev->Duplex_set) {
285 code = gsijs_client_set_param(ijsdev, "PS:Duplex",
286 ijsdev->Duplex ? "true" : "false");
287 }
288 if (code == 0 && ijsdev->IjsTumble_set) {
289 code = gsijs_client_set_param(ijsdev, "PS:Tumble",
290 ijsdev->IjsTumble ? "true" :
291 "false");
292 }
293 return code;
294 }
295
296 /**
297 * gsijs_set_margin_params_hpijs: Do margin negotiation with IJS server.
298 *
299 * This version is specialized for hpijs 1.0 through 1.0.2, and
300 * accommodates a number of quirks.
301 **/
302 private int
gsijs_set_margin_params_hpijs(gx_device_ijs * ijsdev)303 gsijs_set_margin_params_hpijs(gx_device_ijs *ijsdev)
304 {
305 char buf[256];
306 int code = 0;
307
308 if (code == 0) {
309 sprintf(buf, "%d", ijsdev->width);
310 code = gsijs_client_set_param(ijsdev, "Width", buf);
311 }
312 if (code == 0) {
313 sprintf(buf, "%d", ijsdev->height);
314 code = gsijs_client_set_param(ijsdev, "Height", buf);
315 }
316
317 if (code == 0) {
318 double printable_width, printable_height;
319 double printable_left, printable_top;
320 float m[4];
321
322 code = ijs_client_get_param(ijsdev->ctx, 0, "PrintableArea",
323 buf, sizeof(buf));
324 if (code == IJS_EUNKPARAM)
325 /* IJS server doesn't support margin negotiations.
326 That's ok. */
327 return 0;
328 else if (code >= 0) {
329 code = gsijs_parse_wxh(buf, code,
330 &printable_width, &printable_height);
331 }
332
333 if (code == 0) {
334 code = ijs_client_get_param(ijsdev->ctx, 0, "PrintableTopLeft",
335 buf, sizeof(buf));
336 if (code == IJS_EUNKPARAM)
337 return 0;
338 else if (code >= 0) {
339 code = gsijs_parse_wxh(buf, code,
340 &printable_left, &printable_top);
341 }
342 }
343
344 if (code == 0) {
345 m[0] = printable_left;
346 m[1] = ijsdev->MediaSize[1] * (1.0 / 72) -
347 printable_top - printable_height;
348 m[2] = ijsdev->MediaSize[0] * (1.0 / 72) -
349 printable_left - printable_width;
350 m[3] = printable_top;
351 gx_device_set_margins((gx_device *)ijsdev, m, true);
352 }
353 }
354
355 return code;
356 }
357
358 /**
359 * gsijs_set_margin_params: Do margin negotiation with IJS server.
360 **/
361 private int
gsijs_set_margin_params(gx_device_ijs * ijsdev)362 gsijs_set_margin_params(gx_device_ijs *ijsdev)
363 {
364 char buf[256];
365 int code = 0;
366 int i, j;
367 char *value;
368
369 if (ijsdev->ijs_version == HPIJS_1_0_VERSION)
370 return gsijs_set_margin_params_hpijs(ijsdev);
371
372 /* Split IjsParams into separate parameters and send to ijs server */
373 value = NULL;
374 for (i=0, j=0; (j < ijsdev->IjsParams_size) && (i < sizeof(buf)-1); j++) {
375 char ch = ijsdev->IjsParams[j];
376 if (ch == '\\') {
377 j++;
378 buf[i++] = ijsdev->IjsParams[j];
379 }
380 else {
381 if (ch == '=') {
382 buf[i++] = '\0';
383 value = &buf[i];
384 }
385 else
386 buf[i++] = ch;
387 if (ch == ',') {
388 buf[i-1] = '\0';
389 if (value)
390 gsijs_client_set_param(ijsdev, buf, value);
391 i = 0;
392 value = NULL;
393 }
394 }
395 }
396 if (value)
397 code = gsijs_client_set_param(ijsdev, buf, value);
398
399 if (code == 0 && ijsdev->Duplex_set) {
400 code = gsijs_client_set_param(ijsdev, "Duplex",
401 ijsdev->Duplex ? "true" : "false");
402 }
403 if (code == 0 && ijsdev->IjsTumble_set) {
404 code = gsijs_client_set_param(ijsdev, "Tumble",
405 ijsdev->IjsTumble ? "true" :
406 "false");
407 }
408
409 if (code == 0) {
410 sprintf (buf, "%gx%g", ijsdev->MediaSize[0] * (1.0 / 72),
411 ijsdev->MediaSize[1] * (1.0 / 72));
412 code = ijs_client_set_param(ijsdev->ctx, 0, "PaperSize",
413 buf, strlen(buf));
414 }
415
416 if (code == 0) {
417 double printable_width, printable_height;
418 double printable_left, printable_top;
419 float m[4];
420
421 code = ijs_client_get_param(ijsdev->ctx, 0, "PrintableArea",
422 buf, sizeof(buf));
423 if (code == IJS_EUNKPARAM)
424 /* IJS server doesn't support margin negotiations.
425 That's ok. */
426 return 0;
427 else if (code >= 0) {
428 code = gsijs_parse_wxh (buf, code,
429 &printable_width, &printable_height);
430 }
431
432 if (code == 0) {
433 code = ijs_client_get_param(ijsdev->ctx, 0, "PrintableTopLeft",
434 buf, sizeof(buf));
435 if (code == IJS_EUNKPARAM)
436 return 0;
437 else if (code >= 0) {
438 code = gsijs_parse_wxh(buf, code,
439 &printable_left, &printable_top);
440 }
441 }
442
443 if (code == 0) {
444 m[0] = printable_left;
445 m[3] = printable_top;
446 m[2] = ijsdev->MediaSize[0] * (1.0 / 72) -
447 printable_left - printable_width;
448 m[1] = ijsdev->MediaSize[1] * (1.0 / 72) -
449 printable_top - printable_height;
450 gx_device_set_margins((gx_device *)ijsdev, m, true);
451 sprintf (buf, "%gx%g", printable_left, printable_top);
452 code = ijs_client_set_param(ijsdev->ctx, 0, "TopLeft",
453 buf, strlen(buf));
454 }
455 }
456
457 return code;
458 }
459
460 /**
461 * gsijs_set_resolution: Set resolution.
462 *
463 * The priority order, highest first, is: commandline -r switch,
464 * if specified; IJS get_param of Dpi; 72 dpi default.
465 *
466 * Because Ghostscript doesn't have a really good way to detect
467 * whether resolution was set on the command line, we set a
468 * low-probability resolution (DEFAULT_DPI) in the static
469 * initialization of the device, then detect whether it has been
470 * changed from that. This causes a minor infelicity: if DEFAULT_DPI
471 * is set on the command line, it is changed to the default here.
472 **/
473 private int
gsijs_set_resolution(gx_device_ijs * ijsdev)474 gsijs_set_resolution(gx_device_ijs *ijsdev)
475 {
476 char buf[256];
477 int code;
478 floatp x_dpi, y_dpi;
479 int width = ijsdev->width;
480 int height = ijsdev->height;
481 bool save_is_open = ijsdev->is_open;
482
483 if (ijsdev->HWResolution[0] != DEFAULT_DPI ||
484 ijsdev->HWResolution[1] != DEFAULT_DPI) {
485 /* Resolution has been set on command line. */
486 return 0;
487 }
488 code = ijs_client_get_param(ijsdev->ctx, 0, "Dpi",
489 buf, sizeof(buf));
490 if (code >= 0) {
491 int i;
492
493 for (i = 0; i < code; i++)
494 if (buf[i] == 'x')
495 break;
496 if (i == code) {
497 char *tail;
498
499 if (i == sizeof(buf))
500 code = IJS_EBUF;
501 buf[i] = 0;
502 x_dpi = y_dpi = strtod (buf, &tail);
503 if (tail == buf)
504 code = IJS_ESYNTAX;
505 } else {
506 double x, y;
507
508 code = gsijs_parse_wxh(buf, code, &x, &y);
509 x_dpi = x;
510 y_dpi = y;
511 }
512 }
513
514 if (code < 0) {
515 x_dpi = 72.0;
516 y_dpi = 72.0;
517 }
518
519 gx_device_set_resolution((gx_device *)ijsdev, x_dpi, y_dpi);
520
521 ijsdev->is_open = true;
522 code = gdev_prn_maybe_realloc_memory((gx_device_printer *)ijsdev,
523 &ijsdev->space_params, width, height,
524 ijsdev->page_uses_transparency);
525 ijsdev->is_open = save_is_open;
526 return code;
527 }
528
529 /* Open the gsijs driver */
530 private int
gsijs_open(gx_device * dev)531 gsijs_open(gx_device *dev)
532 {
533 gx_device_ijs *ijsdev = (gx_device_ijs *)dev;
534 int code;
535 char buf[256];
536 bool use_outputfd;
537 int fd = -1;
538
539 if (strlen(ijsdev->IjsServer) == 0) {
540 eprintf("ijs server not specified\n");
541 return gs_note_error(gs_error_ioerror);
542 }
543
544 /* Decide whether to use OutputFile or OutputFD. Note: how to
545 determine this is a tricky question, so we just allow the
546 user to set it.
547 */
548 use_outputfd = ijsdev->IjsUseOutputFD;
549
550 /* If using OutputFilename, we don't want to open the output file.
551 Leave that to the ijs server. */
552 ijsdev->OpenOutputFile = use_outputfd;
553
554 code = gdev_prn_open(dev);
555 if (code < 0)
556 return code;
557
558 if (use_outputfd) {
559 /* Note: dup() may not be portable to all interesting IJS
560 platforms. In that case, this branch should be #ifdef'ed out.
561 */
562 fd = dup(fileno(ijsdev->file));
563 }
564
565 /* WARNING: Ghostscript should be run with -dSAFER to stop
566 * someone changing the ijs server (e.g. running a shell).
567 */
568 ijsdev->ctx = ijs_invoke_server(ijsdev->IjsServer);
569 if (ijsdev->ctx == (IjsClientCtx *)NULL) {
570 eprintf1("Can't start ijs server \042%s\042\n", ijsdev->IjsServer);
571 return gs_note_error(gs_error_ioerror);
572 }
573
574 ijsdev->ijs_version = ijs_client_get_version (ijsdev->ctx);
575
576 if (ijs_client_open(ijsdev->ctx) < 0) {
577 eprintf("Can't open ijs\n");
578 return gs_note_error(gs_error_ioerror);
579 }
580 if (ijs_client_begin_job(ijsdev->ctx, 0) < 0) {
581 eprintf("Can't begin ijs job 0\n");
582 ijs_client_close(ijsdev->ctx);
583 return gs_note_error(gs_error_ioerror);
584 }
585
586 if (use_outputfd) {
587 /* Note: dup() may not be portable to all interesting IJS
588 platforms. In that case, this branch should be #ifdef'ed out.
589 */
590 sprintf(buf, "%d", fd);
591 ijs_client_set_param(ijsdev->ctx, 0, "OutputFD", buf, strlen(buf));
592 close(fd);
593 } else {
594 ijs_client_set_param(ijsdev->ctx, 0, "OutputFile",
595 ijsdev->fname, strlen(ijsdev->fname));
596 }
597
598 if (code >= 0 && ijsdev->DeviceManufacturer)
599 code = ijs_client_set_param(ijsdev->ctx, 0, "DeviceManufacturer",
600 ijsdev->DeviceManufacturer,
601 strlen(ijsdev->DeviceManufacturer));
602
603 if (code >= 0 && ijsdev->DeviceModel)
604 code = ijs_client_set_param(ijsdev->ctx, 0, "DeviceModel",
605 ijsdev->DeviceModel,
606 strlen(ijsdev->DeviceModel));
607
608 if (code >= 0)
609 code = gsijs_set_generic_params(ijsdev);
610
611 if (code >= 0)
612 code = gsijs_set_resolution(ijsdev);
613
614 if (code >= 0)
615 code = gsijs_set_margin_params(ijsdev);
616
617 return code;
618 }
619
620 /* Finish device initialization. */
621 private int
gsijs_finish_copydevice(gx_device * dev,const gx_device * from_dev)622 gsijs_finish_copydevice(gx_device *dev, const gx_device *from_dev)
623 {
624 int code;
625 static const char rgb[] = "DeviceRGB";
626 gx_device_ijs *ijsdev = (gx_device_ijs *)dev;
627
628 code = gx_default_finish_copydevice(dev, from_dev);
629 if(code < 0)
630 return code;
631
632 if (!ijsdev->ColorSpace) {
633 ijsdev->ColorSpace = gs_malloc(ijsdev->memory, sizeof(rgb), 1,
634 "gsijs_finish_copydevice");
635 if (!ijsdev->ColorSpace)
636 return gs_note_error(gs_error_VMerror);
637 ijsdev->ColorSpace_size = sizeof(rgb);
638 memcpy(ijsdev->ColorSpace, rgb, sizeof(rgb));
639 }
640 return code;
641 }
642
643 /* Close the gsijs driver */
644 private int
gsijs_close(gx_device * dev)645 gsijs_close(gx_device *dev)
646 {
647 gx_device_ijs *ijsdev = (gx_device_ijs *)dev;
648 int code;
649
650 /* ignore ijs errors on close */
651 ijs_client_end_job(ijsdev->ctx, 0);
652 ijs_client_close(ijsdev->ctx);
653 ijs_client_begin_cmd(ijsdev->ctx, IJS_CMD_EXIT);
654 ijs_client_send_cmd_wait(ijsdev->ctx);
655
656 code = gdev_prn_close(dev);
657 if (ijsdev->IjsParams)
658 gs_free(dev->memory, ijsdev->IjsParams,
659 ijsdev->IjsParams_size, 1, "gsijs_read_string_malloc");
660 if (ijsdev->ColorSpace)
661 gs_free(dev->memory, ijsdev->ColorSpace,
662 ijsdev->ColorSpace_size, 1, "gsijs_read_string_malloc");
663 if (ijsdev->DeviceManufacturer)
664 gs_free(dev->memory, ijsdev->DeviceManufacturer,
665 ijsdev->DeviceManufacturer_size, 1, "gsijs_read_string_malloc");
666 if (ijsdev->DeviceModel)
667 gs_free(dev->memory, ijsdev->DeviceModel,
668 ijsdev->DeviceModel_size, 1, "gsijs_read_string_malloc");
669 ijsdev->IjsParams = NULL;
670 ijsdev->IjsParams_size = 0;
671 ijsdev->DeviceManufacturer = NULL;
672 ijsdev->DeviceManufacturer_size = 0;
673 ijsdev->DeviceModel = NULL;
674 ijsdev->DeviceModel_size = 0;
675 return code;
676 }
677
678 /* This routine is entirely analagous to gdev_prn_print_scan_lines(),
679 but computes width instead of height. It is not specific to IJS,
680 and a strong case could be made for moving it into gdevprn.c. */
681 private int
gsijs_raster_width(gx_device * pdev)682 gsijs_raster_width(gx_device *pdev)
683 {
684 int width = pdev->width;
685 gs_matrix imat;
686 float xscale;
687 int right, offset, end;
688
689 (*dev_proc(pdev, get_initial_matrix)) (pdev, &imat);
690 xscale = imat.xx * 72.0;
691 right = (int)(dev_r_margin(pdev) * xscale);
692 offset = (int)(dev_x_offset(pdev) * xscale);
693 end = offset + width - right;
694 return min(width, end);
695 }
696
ijs_all_white(unsigned char * data,int size)697 private int ijs_all_white(unsigned char *data, int size)
698 {
699 int clean = 1;
700 int i;
701 for (i = 0; i < size; i++)
702 {
703 if (data[i] != 0xFF)
704 {
705 clean = 0;
706 break;
707 }
708 }
709 return clean;
710 }
711
712 /* Print a page. Don't use normal printer gdev_prn_output_page
713 * because it opens the output file.
714 */
715 private int
gsijs_output_page(gx_device * dev,int num_copies,int flush)716 gsijs_output_page(gx_device *dev, int num_copies, int flush)
717 {
718 gx_device_ijs *ijsdev = (gx_device_ijs *)dev;
719 gx_device_printer *pdev = (gx_device_printer *)dev;
720 int raster = gdev_prn_raster(pdev);
721 int ijs_width, ijs_height;
722 int row_bytes;
723 int n_chan = pdev->color_info.num_components;
724 unsigned char *data;
725 char buf[256];
726 double xres = pdev->HWResolution[0];
727 double yres = pdev->HWResolution[1];
728 int code = 0;
729 int endcode = 0;
730 int status = 0;
731 int i, y;
732
733 if ((data = gs_alloc_bytes(pdev->memory, raster, "gsijs_output_page"))
734 == (unsigned char *)NULL)
735 return gs_note_error(gs_error_VMerror);
736
737 /* Determine bitmap width and height */
738 ijs_height = gdev_prn_print_scan_lines(dev);
739 if (ijsdev->ijs_version == HPIJS_1_0_VERSION) {
740 ijs_width = pdev->width;
741 } else {
742 ijs_width = gsijs_raster_width(dev);
743 }
744 row_bytes = (ijs_width * pdev->color_info.depth + 7) >> 3;
745
746 /* Required page parameters */
747 sprintf(buf, "%d", n_chan);
748 gsijs_client_set_param(ijsdev, "NumChan", buf);
749 sprintf(buf, "%d", ijsdev->BitsPerSample);
750 gsijs_client_set_param(ijsdev, "BitsPerSample", buf);
751
752 /* This needs to become more sophisticated for DeviceN. */
753 strcpy(buf, (n_chan == 4) ? "DeviceCMYK" :
754 ((n_chan == 3) ? "DeviceRGB" : "DeviceGray"));
755 gsijs_client_set_param(ijsdev, "ColorSpace", buf);
756
757 /* If hpijs 1.0, don't set width and height here, because it
758 expects them to be the paper size. */
759 if (ijsdev->ijs_version != HPIJS_1_0_VERSION) {
760 sprintf(buf, "%d", ijs_width);
761 gsijs_client_set_param(ijsdev, "Width", buf);
762 sprintf(buf, "%d", ijs_height);
763 gsijs_client_set_param(ijsdev, "Height", buf);
764 }
765
766 sprintf(buf, "%gx%g", xres, yres);
767 gsijs_client_set_param(ijsdev, "Dpi", buf);
768
769 for (i=0; i<num_copies; i++) {
770 unsigned char *actual_data;
771 ijs_client_begin_cmd (ijsdev->ctx, IJS_CMD_BEGIN_PAGE);
772 status = ijs_client_send_cmd_wait(ijsdev->ctx);
773
774 for (y = 0; y < ijs_height; y++) {
775 code = gdev_prn_get_bits(pdev, y, data, &actual_data);
776 if (code < 0)
777 break;
778
779 if (ijsdev->ijs_version == HPIJS_1_0_VERSION &&
780 ijs_all_white(actual_data, row_bytes))
781 status = ijs_client_send_data_wait(ijsdev->ctx, 0, NULL, 0);
782 else
783 status = ijs_client_send_data_wait(ijsdev->ctx, 0,
784 (char *)actual_data, row_bytes);
785 if (status)
786 break;
787 }
788 ijs_client_begin_cmd(ijsdev->ctx, IJS_CMD_END_PAGE);
789 status = ijs_client_send_cmd_wait(ijsdev->ctx);
790 }
791
792 gs_free_object(pdev->memory, data, "gsijs_output_page");
793
794 endcode = (pdev->buffer_space && !pdev->is_async_renderer ?
795 clist_finish_page(dev, flush) : 0);
796
797
798 if (endcode < 0)
799 return endcode;
800
801 if (code < 0)
802 return endcode;
803
804 if (status < 0)
805 return gs_note_error(gs_error_ioerror);
806
807 code = gx_finish_output_page(dev, num_copies, flush);
808 return code;
809 }
810
811
812 /**************************************************************************/
813
814 /* Get device parameters */
815 private int
gsijs_get_params(gx_device * dev,gs_param_list * plist)816 gsijs_get_params(gx_device *dev, gs_param_list *plist)
817 {
818 gx_device_ijs *ijsdev = (gx_device_ijs *)dev;
819 gs_param_string gps;
820 int code = gdev_prn_get_params(dev, plist);
821
822 if (code >= 0) {
823 param_string_from_transient_string(gps, ijsdev->IjsServer);
824 code = param_write_string(plist, "IjsServer", &gps);
825 }
826
827 if (code >= 0) {
828 if (ijsdev->DeviceManufacturer) {
829 param_string_from_transient_string(gps,
830 ijsdev->DeviceManufacturer);
831 code = param_write_string(plist, "DeviceManufacturer", &gps);
832 } else {
833 code = param_write_null(plist, "DeviceManufacturer");
834 }
835 }
836
837 if (code >= 0) {
838 if (ijsdev->DeviceModel) {
839 param_string_from_transient_string(gps, ijsdev->DeviceModel);
840 code = param_write_string(plist, "DeviceModel", &gps);
841 } else {
842 code = param_write_null(plist, "DeviceModel");
843 }
844 }
845
846 if (code >= 0) {
847 if (ijsdev->IjsParams) {
848 param_string_from_transient_string(gps, ijsdev->IjsParams);
849 code = param_write_string(plist, "IjsParams", &gps);
850 } else {
851 code = param_write_null(plist, "IjsParams");
852 }
853 }
854
855 if (code >= 0)
856 code = param_write_int(plist, "BitsPerSample", &ijsdev->BitsPerSample);
857
858 if (code >= 0)
859 code = param_write_bool(plist, "IjsUseOutputFD",
860 &ijsdev->IjsUseOutputFD);
861
862 if (code >= 0) {
863 if (ijsdev->IjsTumble_set) {
864 code = param_write_bool(plist, "Tumble", &ijsdev->IjsTumble);
865 } else {
866 code = param_write_null(plist, "Tumble");
867 }
868 }
869
870 return code;
871 }
872
873 private int
gsijs_read_int(gs_param_list * plist,gs_param_name pname,int * pval,int min_value,int max_value,bool only_when_closed)874 gsijs_read_int(gs_param_list *plist, gs_param_name pname, int *pval,
875 int min_value, int max_value, bool only_when_closed)
876 {
877 int code = 0;
878 int new_value;
879
880 switch (code = param_read_int(plist, pname, &new_value)) {
881 case 0:
882 if (only_when_closed && (new_value != *pval)) {
883 code = gs_error_rangecheck;
884 goto e;
885 }
886 if ((new_value >= min_value) && (new_value <= max_value)) {
887 *pval = new_value;
888 break;
889 }
890 code = gs_note_error(gs_error_rangecheck);
891 goto e;
892 default:
893 if (param_read_null(plist, pname) == 0)
894 return 1;
895 e:param_signal_error(plist, pname, code);
896 case 1:
897 ;
898 }
899 return code;
900 }
901
902 private int
gsijs_read_bool(gs_param_list * plist,gs_param_name pname,bool * pval,bool only_when_closed)903 gsijs_read_bool(gs_param_list *plist, gs_param_name pname, bool *pval,
904 bool only_when_closed)
905 {
906 int code = 0;
907 bool new_value;
908
909 switch (code = param_read_bool(plist, pname, &new_value)) {
910 case 0:
911 if (only_when_closed && (new_value != *pval)) {
912 code = gs_error_rangecheck;
913 goto e;
914 }
915 *pval = new_value;
916 break;
917 default:
918 if (param_read_null(plist, pname) == 0) {
919 return 1;
920 }
921 e:param_signal_error(plist, pname, code);
922 case 1:
923 ;
924 }
925 return code;
926 }
927
928 private int
gsijs_read_string(gs_param_list * plist,gs_param_name pname,char * str,uint size,bool safety,bool only_when_closed)929 gsijs_read_string(gs_param_list *plist, gs_param_name pname, char *str,
930 uint size, bool safety, bool only_when_closed)
931 {
932 int code;
933 gs_param_string new_value;
934 int differs;
935
936 switch (code = param_read_string(plist, pname, &new_value)) {
937 case 0:
938 differs = bytes_compare(new_value.data, new_value.size,
939 (const byte *)str, strlen(str));
940 if (safety && differs) {
941 code = gs_error_invalidaccess;
942 goto e;
943 }
944 if (only_when_closed && differs) {
945 code = gs_error_rangecheck;
946 goto e;
947 }
948 if (new_value.size < size) {
949 strncpy(str, (const char *)new_value.data, new_value.size);
950 str[new_value.size+1] = '\0';
951 break;
952 }
953 code = gs_note_error(gs_error_rangecheck);
954 goto e;
955 default:
956 if (param_read_null(plist, pname) == 0)
957 return 1;
958 e:param_signal_error(plist, pname, code);
959 case 1:
960 ;
961 }
962 return code;
963 }
964
965 private int
gsijs_read_string_malloc(gs_param_list * plist,gs_param_name pname,char ** str,int * size,bool only_when_closed)966 gsijs_read_string_malloc(gs_param_list *plist, gs_param_name pname, char **str,
967 int *size, bool only_when_closed)
968 {
969 int code;
970 gs_param_string new_value;
971 int differs;
972
973 switch (code = param_read_string(plist, pname, &new_value)) {
974 case 0:
975 differs = bytes_compare(new_value.data, new_value.size,
976 (const byte *)(*str ? *str : ""),
977 *str ? strlen(*str) : 0);
978 if (only_when_closed && differs) {
979 code = gs_error_rangecheck;
980 goto e;
981 }
982 if (new_value.size + 1 != *size) {
983 if (*str)
984 gs_free(plist->memory, *str, *size, 1,
985 "gsijs_read_string_malloc");
986 *str = NULL;
987 *size = 0;
988 }
989 if (*str == NULL)
990 *str = gs_malloc(plist->memory, new_value.size + 1, 1,
991 "gsijs_read_string_malloc");
992 if (*str == NULL) {
993 code = gs_note_error(gs_error_VMerror);
994 goto e;
995 }
996 *size = new_value.size + 1;
997 strncpy(*str, (const char *)new_value.data, new_value.size);
998 (*str)[new_value.size] = '\0';
999 break;
1000 default:
1001 if (param_read_null(plist, pname) == 0)
1002 return 1;
1003 e:param_signal_error(plist, pname, code);
1004 case 1:
1005 ;
1006 }
1007 return code;
1008 }
1009
1010
1011 private int
gsijs_put_params(gx_device * dev,gs_param_list * plist)1012 gsijs_put_params(gx_device *dev, gs_param_list *plist)
1013 {
1014 gx_device_ijs *ijsdev = (gx_device_ijs *)dev;
1015 int code = 0;
1016 bool is_open = dev->is_open;
1017
1018 /* We allow duplex to be set in all cases. At some point, it may
1019 be worthwhile to query the device to see if it supports
1020 duplex. Note also that this code will get called even before
1021 the device has been opened, which is when the -DDuplex
1022 command line is processed. */
1023 if (ijsdev->Duplex_set < 0) {
1024 ijsdev->Duplex = 1;
1025 ijsdev->Duplex_set = 0;
1026 }
1027
1028 /* If a parameter must not be changed after the device is open,
1029 * the last parameter of gsijs_read_xxx() is is_open.
1030 * If a parameter may be changed at any time, it is false.
1031 */
1032 if (code >= 0)
1033 code = gsijs_read_string(plist, "IjsServer",
1034 ijsdev->IjsServer, sizeof(ijsdev->IjsServer),
1035 dev->LockSafetyParams, is_open);
1036
1037 if (code >= 0)
1038 code = gsijs_read_string_malloc(plist, "DeviceManufacturer",
1039 &ijsdev->DeviceManufacturer, &ijsdev->DeviceManufacturer_size,
1040 is_open);
1041
1042 if (code >= 0)
1043 code = gsijs_read_string_malloc(plist, "DeviceModel",
1044 &ijsdev->DeviceModel, &ijsdev->DeviceModel_size,
1045 is_open);
1046
1047 if (code >= 0)
1048 code = gsijs_read_string_malloc(plist, "IjsParams",
1049 &(ijsdev->IjsParams), &(ijsdev->IjsParams_size), is_open);
1050
1051 if (code >= 0)
1052 code = gsijs_read_int(plist, "BitsPerSample", &ijsdev->BitsPerSample,
1053 1, 16, is_open);
1054
1055 if (code >= 0)
1056 code = gsijs_read_bool(plist, "IjsUseOutputFD",
1057 &ijsdev->IjsUseOutputFD, is_open);
1058
1059 if (code >= 0) {
1060 code = gsijs_read_string_malloc(plist, "ProcessColorModel",
1061 &ijsdev->ColorSpace, &ijsdev->ColorSpace_size, is_open);
1062 }
1063
1064 if (code >= 0) {
1065 code = gsijs_read_bool(plist, "Tumble", &ijsdev->IjsTumble, false);
1066 if (code == 0)
1067 ijsdev->IjsTumble_set = true;
1068 }
1069
1070 if (code >= 0)
1071 code = gsijs_set_color_format(ijsdev);
1072
1073 if (code >= 0)
1074 code = gdev_prn_put_params(dev, plist);
1075
1076 if (code >= 0 && is_open) {
1077 code = gsijs_set_generic_params(ijsdev);
1078 if (code >= 0)
1079 code = gsijs_set_margin_params(ijsdev);
1080 if (code < 0)
1081 return gs_note_error(gs_error_ioerror);
1082 }
1083
1084 return code;
1085 }
1086
1087 private int
gsijs_client_set_param(gx_device_ijs * ijsdev,const char * key,const char * value)1088 gsijs_client_set_param(gx_device_ijs *ijsdev, const char *key,
1089 const char *value)
1090 {
1091 int code = ijs_client_set_param(ijsdev->ctx, 0 /* job id */,
1092 key, value, strlen(value));
1093 if (code < 0)
1094 dprintf2("ijs: Can't set parameter %s=%s\n", key, value);
1095 return code;
1096 }
1097
1098
1099 private int
gsijs_set_color_format(gx_device_ijs * ijsdev)1100 gsijs_set_color_format(gx_device_ijs *ijsdev)
1101 {
1102 gx_device_color_info dci = ijsdev->color_info;
1103 int components; /* 1=gray, 3=RGB, 4=CMYK */
1104 int bpc = ijsdev->BitsPerSample; /* bits per component */
1105 int maxvalue;
1106 const char *ColorSpace = ijsdev->ColorSpace;
1107
1108 if (ColorSpace == NULL)
1109 ColorSpace = "DeviceRGB";
1110
1111 if (!strcmp (ColorSpace, "DeviceGray")) {
1112 components = 1;
1113 if (bpc == 1) {
1114 ijsdev->procs.map_rgb_color = gx_default_w_b_map_rgb_color;
1115 ijsdev->procs.map_color_rgb = gx_default_w_b_map_color_rgb;
1116 } else {
1117 ijsdev->procs.map_rgb_color = gx_default_gray_map_rgb_color;
1118 ijsdev->procs.map_color_rgb = gx_default_gray_map_color_rgb;
1119 }
1120 ijsdev->procs.encode_color = gx_default_gray_fast_encode;
1121 ijsdev->procs.decode_color = gx_default_decode_color;
1122 dci.polarity = GX_CINFO_POLARITY_ADDITIVE;
1123 dci.gray_index = 0;
1124 } else if (!strcmp (ColorSpace, "DeviceRGB")) {
1125 components = 3;
1126 ijsdev->procs.map_rgb_color = gx_default_rgb_map_rgb_color;
1127 ijsdev->procs.map_color_rgb = gx_default_rgb_map_color_rgb;
1128 ijsdev->procs.encode_color = gx_default_rgb_map_rgb_color;
1129 ijsdev->procs.decode_color = gx_default_rgb_map_color_rgb;
1130 dci.polarity = GX_CINFO_POLARITY_ADDITIVE;
1131 dci.gray_index = GX_CINFO_COMP_NO_INDEX;
1132 } else if (!strcmp (ColorSpace, "DeviceCMYK")) {
1133 components = 4;
1134 ijsdev->procs.map_cmyk_color = cmyk_8bit_map_cmyk_color;
1135 ijsdev->procs.map_color_rgb = cmyk_8bit_map_color_rgb;
1136 ijsdev->procs.encode_color = cmyk_8bit_map_cmyk_color;
1137 ijsdev->procs.decode_color = gx_default_decode_color;
1138 dci.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
1139 dci.gray_index = 3;
1140 } else {
1141 return -1;
1142 }
1143
1144 maxvalue = (1 << bpc) - 1;
1145 dci.max_components = components;
1146 dci.num_components = components;
1147 dci.depth = bpc * components;
1148 dci.max_gray = maxvalue;
1149 dci.max_color = components > 1 ? maxvalue : 0;
1150 dci.dither_grays = maxvalue+1;
1151 dci.dither_colors = components > 1 ? maxvalue+1 : 0;
1152
1153 dci.separable_and_linear = GX_CINFO_SEP_LIN;
1154 dci.cm_name = ColorSpace;
1155
1156 ijsdev->color_info = dci;
1157
1158 set_linear_color_bits_mask_shift((gx_device *)ijsdev);
1159
1160 return 0;
1161 }
1162