xref: /plan9/sys/src/cmd/gs/src/gdevijs.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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