1 /* Copyright (C) 2001-2002 artofcode LLC.  All rights reserved.
2 
3   This program is free software; you can redistribute it and/or modify it
4   under the terms of the GNU General Public License as published by the
5   Free Software Foundation; either version 2 of the License, or (at your
6   option) any later version.
7 
8   This program is distributed in the hope that it will be useful, but
9   WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   General Public License for more details.
12 
13   You should have received a copy of the GNU General Public License along
14   with this program; if not, write to the Free Software Foundation, Inc.,
15   59 Temple Place, Suite 330, Boston, MA, 02111-1307.
16 
17 */
18 
19 /*$Id: gdevijs.c,v 1.1.2.3.2.1 2003/01/17 00:49:00 giles Exp $ */
20 /*
21  * IJS device for Ghostscript.
22  * Intended to work with any IJS compliant inkjet driver, including
23  * hpijs 1.0 and later, an IJS-enhanced gimp-print driver, and
24  * the IJS Windows GDI server (ijsmswin.exe).
25  *
26  * DRAFT
27  *
28  * WARNING: The ijs server can be selected on the gs command line
29  * which is a security risk, since any program can be run.
30  * You should use -dSAFER which sets .LockSafetyParams to true
31  * before opening this device.
32  *
33  * 11/26/03 David Suffield
34  * (c) 2003-2004 Copyright Hewlett-Packard Development Company, LP
35  *
36  * 1. Removed hpijs 1.0-1.0.2 workarounds, use hpijs 1.0.3 or higher.
37  * 2. Added krgb support.
38  *
39  * 02/21/05 David Suffield
40  * 1. Fixed segfault issue with 1-bit color space.
41  * 2. Fixed z-order issue with colored text on black rectangle.
42  *
43  */
44 
45 #include "unistd_.h"	/* for dup() */
46 #include <stdlib.h>
47 #include <fcntl.h>
48 #include "gdevprn.h"
49 #include "gp.h"
50 #include "ijs.h"
51 #include "ijs_client.h"
52 
53 //#define KRGB_DEBUG
54 
55 /* This should go into gdevprn.h, or, better yet, gdevprn should
56    acquire an API for changing resolution. */
57 int gdev_prn_maybe_realloc_memory(gx_device_printer *pdev,
58 				  gdev_prn_space_params *old_space,
59 				  int old_width, int old_height);
60 
61 /* Device procedures */
62 
63 /* See gxdevice.h for the definitions of the procedures. */
64 private dev_proc_open_device(gsijs_open);
65 private dev_proc_close_device(gsijs_close);
66 private dev_proc_output_page(gsijs_output_page);
67 private dev_proc_get_params(gsijs_get_params);
68 private dev_proc_put_params(gsijs_put_params);
69 
70 /* Following definitions are for krgb support. */
71 private dev_proc_create_buf_device(gsijs_create_buf_device);
72 private dev_proc_fill_rectangle(gsijs_fill_rectangle);
73 private dev_proc_copy_mono(gsijs_copy_mono);
74 private dev_proc_fill_mask(gsijs_fill_mask);
75 private dev_proc_fill_path(gsijs_fill_path);
76 private dev_proc_stroke_path(gsijs_stroke_path);
77 
78 private const gx_device_procs gsijs_procs =
79 prn_color_params_procs(gsijs_open, gsijs_output_page, gsijs_close,
80 		   gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb,
81 		   gsijs_get_params, gsijs_put_params);
82 
83 typedef struct gx_device_ijs_s gx_device_ijs;
84 
85 /* The device descriptor */
86 struct gx_device_ijs_s {
87     gx_device_common;
88     gx_prn_device_common;
89     bool IjsUseOutputFD;
90     char IjsServer[gp_file_name_sizeof]; /* name of executable ijs server */
91     char *ColorSpace;
92     int ColorSpace_size;
93     int BitsPerSample;
94     char *DeviceManufacturer;
95     int DeviceManufacturer_size;
96     char *DeviceModel;
97     int DeviceModel_size;
98     char *IjsParams;
99     int IjsParams_size;
100 
101     /* Common setpagedevice parameters supported by ijs but not
102        currently parsed by gx_prn_device. We prefix these with Ijs to
103        avoid namespace collision if they do get added to gx_prn_device.
104     */
105     bool IjsTumble;
106     bool IjsTumble_set;
107 
108     IjsClientCtx *ctx;
109     int ijs_version;
110 
111     /* Additional parameters for krgb support. */
112     int krgb_mode;     /* 0=false, 1=true */
113     int k_path;        /* k plane path, 0=false, 1=true */
114     int k_width;       /* k plane width in pixels */
115     int k_band_size;   /* k plane buffer size in bytes, byte aligned */
116     unsigned char *k_band;  /* k plane buffer */
117     gx_device_procs prn_procs;  /* banding playback procedures */
118 };
119 
120 #define DEFAULT_DPI 74   /* See gsijs_set_resolution() below. */
121 
122 gx_device_ijs gs_ijs_device =
123 {
124     prn_device_std_body(gx_device_ijs, gsijs_procs, "ijs",
125 			DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
126 			DEFAULT_DPI, DEFAULT_DPI,
127 			0, 0, 0, 0,
128 			24 /* depth */, NULL /* print page */),
129     FALSE,	/* IjsUseOutputFD */
130     "",		/* IjsServer */
131     NULL,	/* ColorSpace */
132     0,		/* ColorSpace_size */
133     8,		/* BitsPerSample */
134     NULL,	/* DeviceManufacturer */
135     0,		/* DeviceManufacturer_size */
136     NULL,	/* DeviceModel */
137     0,		/* DeviceModel_size */
138     NULL,	/* IjsParams */
139     0,		/* IjsParams_size */
140 
141     FALSE,	/* Tumble */
142     FALSE,	/* Tumble_set */
143 
144     NULL,	/* IjsClient *ctx */
145     0,		/* ijs_version */
146     0,          /* krgb_mode */
147     0,          /* k_path */
148     0,          /* k_width */
149     0,          /* k_band_size */
150     NULL        /* k_band buffer */
151 };
152 
153 
154 private int gsijs_client_set_param(gx_device_ijs *ijsdev, const char *key,
155     const char *value);
156 private int gsijs_set_color_format(gx_device_ijs *ijsdev);
157 private int gsijs_read_int(gs_param_list *plist, gs_param_name pname,
158    int *pval, int min_value, int max_value, bool only_when_closed);
159 private int gsijs_read_bool(gs_param_list *plist, gs_param_name pname,
160    bool *pval, bool only_when_closed);
161 private int gsijs_read_string(gs_param_list * plist, gs_param_name pname,
162    char * str, uint size, bool safety, bool only_when_closed);
163 
164 /**************************************************************************/
165 
166 /* ---------------- Low-level graphic procedures ---------------- */
167 
168 static unsigned char xmask[] =
169 {
170    0x80,    /* x=0 */
171    0x40,    /* 1 */
172    0x20,    /* 2 */
173    0x10,    /* 3 */
174    0x08,    /* 4 */
175    0x04,    /* 5 */
176    0x02,    /* 6 */
177    0x01     /* 7 */
178 };
179 
gsijs_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)180 private int gsijs_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
181            gx_color_index color)
182 {
183    gx_device_ijs *ijsdev = (gx_device_ijs *)((gx_device_forward *)dev)->target;
184 
185    if (ijsdev->krgb_mode && ijsdev->k_path && y >= 0 && x >= 0)
186    {
187       int raster = (ijsdev->k_width+7) >> 3;
188       register unsigned char *dest=ijsdev->k_band+(raster*y)+(x >> 3);
189       int dest_start_bit = x & 7;
190       int i,j,w1;
191 
192       if (h <= 0 || w <= 0)
193          return 0;
194 
195       if ((x+w) > ijsdev->k_width)
196          w1 = ijsdev->k_width - x;
197       else
198          w1 = w;
199 
200       /* Note x,y orgin 0,0 is stored first byte 0 left to right. */
201 
202       if (color==0x0)
203       {
204          /* Color is black, store in k plane band instead of regular band. */
205          for (j=0; j<h; j++)
206          {
207             for (i=0; i<w1; i++)
208                dest[(dest_start_bit+i)>>3] |= xmask[(dest_start_bit+i)&7];
209             dest+=raster;
210          }
211          return 0;
212       }
213       else
214       {
215          /* Color is not black, remove any k plane bits for z-order dependencies, store in regular band. */
216          for (j=0; j<h; j++)
217          {
218             for (i=0; i<w1; i++)
219                dest[(dest_start_bit+i)>>3] &= ~xmask[(dest_start_bit+i)&7];
220             dest+=raster;
221          }
222       }
223    }
224 
225    return (*ijsdev->prn_procs.fill_rectangle)(dev, x, y, w, h, color);
226 }
227 
gsijs_copy_mono(gx_device * dev,const byte * data,int dx,int draster,gx_bitmap_id id,int x,int y,int w,int height,gx_color_index zero,gx_color_index one)228 private int gsijs_copy_mono(gx_device * dev, const byte * data,
229       int dx, int draster, gx_bitmap_id id,
230       int x, int y, int w, int height, gx_color_index zero, gx_color_index one)
231 {
232    gx_device_ijs *ijsdev = (gx_device_ijs *)((gx_device_forward *)dev)->target;
233 
234    //   if (ijsdev->krgb_mode && ijsdev->k_path && one==0x0)
235    if (ijsdev->krgb_mode && ijsdev->k_path)
236    {
237       /* Store in k plane band instead of regular band. */
238       int raster = (ijsdev->k_width+7) >> 3;       /* raster width in bytes, byte aligned */
239       register unsigned char *dest=ijsdev->k_band+(raster*y)+(x >> 3);
240       register const unsigned char *scan=data+(dx >> 3);
241       int dest_start_bit = x & 7;
242       int scan_start_bit = dx & 7;
243       int i, h=height;
244 
245       if (h <= 0 || w <= 0)
246          return 0;
247 
248       if (one==0x0)
249       {
250          /* Color is black, store in k plane band instead of regular band. */
251          while (h-- > 0)
252          {
253             for (i=0; i<w; i++)
254             {
255                if (scan[(scan_start_bit+i)>>3] & xmask[(scan_start_bit+i)&7])
256                   dest[(dest_start_bit+i)>>3] |= xmask[(dest_start_bit+i)&7];
257             }
258             scan+=draster;
259             dest+=raster;
260          }
261          return 0;
262       }
263       else
264       {
265          /* Color is not black, remove any k plane bits for z-order dependencies, store in regular band. */
266          while (h-- > 0)
267          {
268             for (i=0; i<w; i++)
269             {
270                if (scan[(scan_start_bit+i)>>3] & xmask[(scan_start_bit+i)&7])
271                   dest[(dest_start_bit+i)>>3] &= ~xmask[(dest_start_bit+i)&7];
272             }
273             scan+=draster;
274             dest+=raster;
275          }
276       }
277    }
278 
279    return (*ijsdev->prn_procs.copy_mono)(dev, data, dx, draster, id, x, y, w, height, zero, one);
280 }
281 
282 /* ---------------- High-level graphic procedures ---------------- */
283 
gsijs_fill_mask(gx_device * dev,const byte * data,int dx,int raster,gx_bitmap_id id,int x,int y,int w,int h,const gx_drawing_color * pdcolor,int depth,gs_logical_operation_t lop,const gx_clip_path * pcpath)284 private int gsijs_fill_mask(gx_device * dev,
285       const byte * data, int dx, int raster, gx_bitmap_id id,
286       int x, int y, int w, int h,
287       const gx_drawing_color * pdcolor, int depth,
288       gs_logical_operation_t lop, const gx_clip_path * pcpath)
289 {
290    gx_device_ijs *ijsdev = (gx_device_ijs *)((gx_device_forward *)dev)->target;
291    int code;
292 
293    ijsdev->k_path = 1;
294 
295    code = (*ijsdev->prn_procs.fill_mask)(dev, data, dx, raster, id, x, y, w, h, pdcolor, depth, lop, pcpath);
296 
297    ijsdev->k_path = 0;
298 
299    return code;
300 }
301 
gsijs_fill_path(gx_device * dev,const gs_imager_state * pis,gx_path * ppath,const gx_fill_params * params,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath)302 private int gsijs_fill_path(gx_device * dev, const gs_imager_state * pis,
303       gx_path * ppath, const gx_fill_params * params,
304       const gx_drawing_color * pdcolor,
305       const gx_clip_path * pcpath)
306 {
307    gx_device_ijs *ijsdev = (gx_device_ijs *)((gx_device_forward *)dev)->target;
308    int code;
309 
310    ijsdev->k_path = 1;
311 
312    code = (*ijsdev->prn_procs.fill_path)(dev, pis, ppath, params, pdcolor, pcpath);
313 
314    ijsdev->k_path = 0;
315 
316    return 0;
317 }
318 
gsijs_stroke_path(gx_device * dev,const gs_imager_state * pis,gx_path * ppath,const gx_stroke_params * params,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath)319 private int gsijs_stroke_path(gx_device * dev, const gs_imager_state * pis,
320         gx_path * ppath, const gx_stroke_params * params,
321         const gx_drawing_color * pdcolor,
322         const gx_clip_path * pcpath)
323 {
324    gx_device_ijs *ijsdev = (gx_device_ijs *)((gx_device_forward *)dev)->target;
325    int code;
326 
327    ijsdev->k_path = 1;
328 
329    code = (*ijsdev->prn_procs.stroke_path)(dev, pis, ppath, params, pdcolor, pcpath);
330 
331    ijsdev->k_path = 0;
332 
333    return code;
334 }
335 
336 /* ---------------- krgb banding playback procedures ---------------- */
337 
gsijs_get_bits(gx_device_printer * pdev,int y,byte * str,byte ** actual_data)338 private int gsijs_get_bits(gx_device_printer * pdev, int y, byte * str, byte ** actual_data)
339 {
340    gx_device_ijs *ijsdev = (gx_device_ijs *)pdev;
341    gx_device_clist_common *cdev = (gx_device_clist_common *)pdev;
342    int band_height = cdev->page_info.band_params.BandHeight;
343    int band_number = y/band_height;
344    int raster = (ijsdev->k_width+7) >> 3;       /* raster width in bytes, byte aligned */
345    int y1=raster*(y-(band_height*band_number));
346 
347    if (y1 == 0)
348    {
349       /* First raster for band, clear k_band. Banding playback occurs on first raster. */
350       memset(ijsdev->k_band, 0, ijsdev->k_band_size);
351    }
352 
353    return gdev_prn_get_bits(pdev, y, str, actual_data);  /* get raster from regular band */
354 }
355 
gsijs_k_get_bits(gx_device_printer * pdev,int y,byte ** actual_data)356 private int gsijs_k_get_bits(gx_device_printer * pdev, int y, byte ** actual_data)
357 {
358    gx_device_ijs *ijsdev = (gx_device_ijs *)pdev;
359    gx_device_clist_common *cdev = (gx_device_clist_common *)pdev;
360    int band_height = cdev->page_info.band_params.BandHeight;
361    int band_number = y/band_height;
362    int raster = (ijsdev->k_width+7) >> 3;       /* raster width in bytes, byte aligned */
363    int y1=raster*(y-(band_height*band_number));
364 
365    *actual_data = ijsdev->k_band+y1;
366 
367    return 0;
368 }
369 
gsijs_create_buf_device(gx_device ** pbdev,gx_device * target,const gx_render_plane_t * render_plane,gs_memory_t * mem,bool for_band)370 private int gsijs_create_buf_device(gx_device **pbdev, gx_device *target,
371             const gx_render_plane_t *render_plane, gs_memory_t *mem, bool for_band)
372 {
373    gx_device_ijs *ijsdev = (gx_device_ijs *)target;
374    int n_chan = ijsdev->color_info.num_components;
375    int code = gx_default_create_buf_device(pbdev, target, render_plane, mem, for_band);
376    if (code < 0 || n_chan != 3)
377       return code;
378 
379    /* Save buffer (vector) procedures so that we can hook them during banding playback. */
380    ijsdev->prn_procs = (*pbdev)->procs;
381 
382    /* Replace buffer procedures with krgb procedures. */
383    set_dev_proc(*pbdev, fill_rectangle, gsijs_fill_rectangle);
384    set_dev_proc(*pbdev, copy_mono, gsijs_copy_mono);
385    set_dev_proc(*pbdev, fill_mask, gsijs_fill_mask);
386    set_dev_proc(*pbdev, fill_path, gsijs_fill_path);
387    set_dev_proc(*pbdev, stroke_path, gsijs_stroke_path);
388 
389    return code;
390 }
391 
392 /* See if IJS server supports krgb. Return value: 0=false, 1=true. */
393 private int
gsijs_set_krgb_mode(gx_device_ijs * ijsdev)394 gsijs_set_krgb_mode(gx_device_ijs *ijsdev)
395 {
396     char buf[256];
397     int n_chan = ijsdev->color_info.num_components;
398     int code;
399 
400     if (n_chan != 3)
401         return 0;    /* no krgb support, not RGB colorspace */
402 
403     buf[0] = 0;
404     code = ijs_client_enum_param(ijsdev->ctx, 0, "ColorSpace", buf, sizeof(buf)-1);
405     if (code >= 0)
406         buf[code] = 0;
407     if (strstr(buf, "KRGB") == NULL)
408         return 0;  /* no krgb support */
409 
410     return 1;  /* krgb is supported */
411 }
412 
413 /* ------ Private definitions ------ */
414 
415 private int
gsijs_parse_wxh(const char * val,int size,double * pw,double * ph)416 gsijs_parse_wxh (const char *val, int size, double *pw, double *ph)
417 {
418     char buf[256];
419     char *tail;
420     int i;
421 
422     for (i = 0; i < size; i++)
423 	if (val[i] == 'x')
424 	    break;
425 
426     if (i + 1 >= size)
427 	return IJS_ESYNTAX;
428 
429     if (i >= sizeof(buf))
430 	return IJS_EBUF;
431 
432     memcpy (buf, val, i);
433     buf[i] = 0;
434     *pw = strtod (buf, &tail);
435     if (tail == buf)
436 	return IJS_ESYNTAX;
437 
438     if (size - i > sizeof(buf))
439 	return IJS_EBUF;
440 
441     memcpy (buf, val + i + 1, size - i - 1);
442     buf[size - i - 1] = 0;
443     *ph = strtod (buf, &tail);
444     if (tail == buf)
445 	return IJS_ESYNTAX;
446 
447     return 0;
448 }
449 
450 /**
451  * gsijs_set_generic_params: Set generic IJS parameters.
452  **/
453 private int
gsijs_set_generic_params(gx_device_ijs * ijsdev)454 gsijs_set_generic_params(gx_device_ijs *ijsdev)
455 {
456     char buf[256];
457     int code = 0;
458     int i, j;
459     char *value;
460 
461     /* Split IjsParams into separate parameters and send to ijs server */
462     value = NULL;
463     for (i=0, j=0; (j < ijsdev->IjsParams_size) && (i < sizeof(buf)-1); j++) {
464 	char ch = ijsdev->IjsParams[j];
465 	if (ch == '\\') {
466 	    j++;
467 	    buf[i++] = ijsdev->IjsParams[j];
468 	}
469 	else {
470 	    if (ch == '=') {
471 		buf[i++] = '\0';
472 		value = &buf[i];
473 	    }
474 	    else
475 		buf[i++] = ch;
476 	    if (ch == ',') {
477 		buf[i-1] = '\0';
478 		if (value)
479 		    gsijs_client_set_param(ijsdev, buf, value);
480 		i = 0;
481 		value = NULL;
482 	    }
483 	}
484     }
485     if (value)
486 	code = gsijs_client_set_param(ijsdev, buf, value);
487 
488     if (code == 0 && ijsdev->Duplex_set) {
489 	code = gsijs_client_set_param(ijsdev, "PS:Duplex",
490 				      ijsdev->Duplex ? "true" : "false");
491     }
492     if (code == 0 && ijsdev->IjsTumble_set) {
493 	code = gsijs_client_set_param(ijsdev, "PS:Tumble",
494 				      ijsdev->IjsTumble ? "true" :
495 				      "false");
496     }
497     return code;
498 }
499 
500 /**
501  * gsijs_set_margin_params: Do margin negotiation with IJS server.
502  **/
503 private int
gsijs_set_margin_params(gx_device_ijs * ijsdev)504 gsijs_set_margin_params(gx_device_ijs *ijsdev)
505 {
506     char buf[256];
507     int code = 0;
508     int i, j;
509     char *value;
510 
511     /* Split IjsParams into separate parameters and send to ijs server */
512     value = NULL;
513     for (i=0, j=0; (j < ijsdev->IjsParams_size) && (i < sizeof(buf)-1); j++) {
514 	char ch = ijsdev->IjsParams[j];
515 	if (ch == '\\') {
516 	    j++;
517 	    buf[i++] = ijsdev->IjsParams[j];
518 	}
519 	else {
520 	    if (ch == '=') {
521 		buf[i++] = '\0';
522 		value = &buf[i];
523 	    }
524 	    else
525 		buf[i++] = ch;
526 	    if (ch == ',') {
527 		buf[i-1] = '\0';
528 		if (value)
529 		    gsijs_client_set_param(ijsdev, buf, value);
530 		i = 0;
531 		value = NULL;
532 	    }
533 	}
534     }
535     if (value)
536 	code = gsijs_client_set_param(ijsdev, buf, value);
537 
538     if (code == 0 && ijsdev->Duplex_set) {
539 	code = gsijs_client_set_param(ijsdev, "Duplex",
540 				      ijsdev->Duplex ? "true" : "false");
541     }
542     if (code == 0 && ijsdev->IjsTumble_set) {
543 	code = gsijs_client_set_param(ijsdev, "Tumble",
544 				      ijsdev->IjsTumble ? "true" :
545 				      "false");
546     }
547 
548     if (code == 0) {
549 	sprintf (buf, "%gx%g", ijsdev->MediaSize[0] * (1.0 / 72),
550 		 ijsdev->MediaSize[1] * (1.0 / 72));
551 	code = ijs_client_set_param(ijsdev->ctx, 0, "PaperSize",
552 				    buf, strlen(buf));
553     }
554 
555     if (code == 0) {
556 	double printable_width, printable_height;
557 	double printable_left, printable_top;
558 	float m[4];
559 
560 	code = ijs_client_get_param(ijsdev->ctx, 0, "PrintableArea",
561 				   buf, sizeof(buf));
562 	if (code == IJS_EUNKPARAM)
563 	    /* IJS server doesn't support margin negotiations.
564 	       That's ok. */
565 	    return 0;
566 	else if (code >= 0) {
567 	    code = gsijs_parse_wxh (buf, code,
568 				    &printable_width, &printable_height);
569 	}
570 
571 	if (code == 0) {
572 	    code = ijs_client_get_param(ijsdev->ctx, 0, "PrintableTopLeft",
573 					buf, sizeof(buf));
574 	    if (code == IJS_EUNKPARAM)
575 		return 0;
576 	    else if (code >= 0) {
577 		code = gsijs_parse_wxh(buf, code,
578 					&printable_left, &printable_top);
579 	    }
580 	}
581 
582 	if (code == 0) {
583 	    m[0] = printable_left;
584 	    m[3] = printable_top;
585 	    m[2] = ijsdev->MediaSize[0] * (1.0 / 72) -
586 		printable_left - printable_width;
587 	    m[1] = ijsdev->MediaSize[1] * (1.0 / 72) -
588 		printable_top - printable_height;
589 	    gx_device_set_margins((gx_device *)ijsdev, m, true);
590 	    sprintf (buf, "%gx%g", printable_left, printable_top);
591 	    code = ijs_client_set_param(ijsdev->ctx, 0, "TopLeft",
592 					buf, strlen(buf));
593 	}
594     }
595 
596     return code;
597 }
598 
599 /**
600  * gsijs_set_resolution: Set resolution.
601  *
602  * The priority order, highest first, is: commandline -r switch,
603  * if specified; IJS get_param of Dpi; 72 dpi default.
604  *
605  * Because Ghostscript doesn't have a really good way to detect
606  * whether resolution was set on the command line, we set a
607  * low-probability resolution (DEFAULT_DPI) in the static
608  * initialization of the device, then detect whether it has been
609  * changed from that.  This causes a minor infelicity: if DEFAULT_DPI
610  * is set on the command line, it is changed to the default here.
611  **/
612 private int
gsijs_set_resolution(gx_device_ijs * ijsdev)613 gsijs_set_resolution(gx_device_ijs *ijsdev)
614 {
615     char buf[256];
616     int code;
617     floatp x_dpi, y_dpi;
618     int width = ijsdev->width;
619     int height = ijsdev->height;
620     bool save_is_open = ijsdev->is_open;
621 
622     if (ijsdev->HWResolution[0] != DEFAULT_DPI ||
623 	ijsdev->HWResolution[1] != DEFAULT_DPI) {
624 	/* Resolution has been set on command line. */
625 	return 0;
626     }
627     code = ijs_client_get_param(ijsdev->ctx, 0, "Dpi",
628 				buf, sizeof(buf));
629     if (code >= 0) {
630 	int i;
631 
632 	for (i = 0; i < code; i++)
633 	    if (buf[i] == 'x')
634 		break;
635 	if (i == code) {
636 	    char *tail;
637 
638 	    if (i == sizeof(buf))
639 		code = IJS_EBUF;
640 	    buf[i] = 0;
641 	    x_dpi = y_dpi = strtod (buf, &tail);
642 	    if (tail == buf)
643 		code = IJS_ESYNTAX;
644 	} else {
645 	    double x, y;
646 
647 	    code = gsijs_parse_wxh(buf, code, &x, &y);
648 	    x_dpi = x;
649 	    y_dpi = y;
650 	}
651     }
652 
653     if (code < 0) {
654 	x_dpi = 72.0;
655 	y_dpi = 72.0;
656     }
657 
658     gx_device_set_resolution((gx_device *)ijsdev, x_dpi, y_dpi);
659 
660     ijsdev->is_open = true;
661     code = gdev_prn_maybe_realloc_memory((gx_device_printer *)ijsdev,
662 					 &ijsdev->space_params,
663 					 width, height);
664     ijsdev->is_open = save_is_open;
665     return code;
666 }
667 
668 /* Open the gsijs driver */
669 private int
gsijs_open(gx_device * dev)670 gsijs_open(gx_device *dev)
671 {
672     gx_device_ijs *ijsdev = (gx_device_ijs *)dev;
673     int code;
674     char buf[256];
675     bool use_outputfd;
676     int fd = -1;
677     long max_bitmap = ijsdev->space_params.MaxBitmap;
678 
679     if (strlen(ijsdev->IjsServer) == 0) {
680 	eprintf("ijs server not specified\n");
681 	return gs_note_error(gs_error_ioerror);
682     }
683 
684     ijsdev->space_params.MaxBitmap = 0;	/* force banding */
685 
686     /* Set create_buf_device in printer device, so that we can hook the banding playback procedures. */
687     ijsdev->printer_procs.buf_procs.create_buf_device = gsijs_create_buf_device;
688 
689     /* Decide whether to use OutputFile or OutputFD. Note: how to
690        determine this is a tricky question, so we just allow the
691        user to set it.
692     */
693     use_outputfd = ijsdev->IjsUseOutputFD;
694 
695     /* If using OutputFilename, we don't want to open the output file.
696        Leave that to the ijs server. */
697     ijsdev->OpenOutputFile = use_outputfd;
698 
699     code = gdev_prn_open(dev);
700     if (code < 0)
701 	return code;
702 
703     ijsdev->space_params.MaxBitmap = max_bitmap;
704 
705     if (use_outputfd) {
706 	/* Note: dup() may not be portable to all interesting IJS
707 	   platforms. In that case, this branch should be #ifdef'ed out.
708 	*/
709 	fd = dup(fileno(ijsdev->file));
710     }
711 
712     /* WARNING: Ghostscript should be run with -dSAFER to stop
713      * someone changing the ijs server (e.g. running a shell).
714      */
715     ijsdev->ctx = ijs_invoke_server(ijsdev->IjsServer);
716     if (ijsdev->ctx == (IjsClientCtx *)NULL) {
717 	eprintf1("Can't start ijs server \042%s\042\n", ijsdev->IjsServer);
718 	return gs_note_error(gs_error_ioerror);
719     }
720 
721     ijsdev->ijs_version = ijs_client_get_version (ijsdev->ctx);
722 
723     if (ijs_client_open(ijsdev->ctx) < 0) {
724 	eprintf("Can't open ijs\n");
725 	return gs_note_error(gs_error_ioerror);
726     }
727     if (ijs_client_begin_job(ijsdev->ctx, 0) < 0) {
728 	eprintf("Can't begin ijs job 0\n");
729 	ijs_client_close(ijsdev->ctx);
730 	return gs_note_error(gs_error_ioerror);
731     }
732 
733     if (use_outputfd) {
734 	/* Note: dup() may not be portable to all interesting IJS
735 	   platforms. In that case, this branch should be #ifdef'ed out.
736 	*/
737 	sprintf(buf, "%d", fd);
738 	ijs_client_set_param(ijsdev->ctx, 0, "OutputFD", buf, strlen(buf));
739 	close(fd);
740     } else {
741 	ijs_client_set_param(ijsdev->ctx, 0, "OutputFile",
742 			     ijsdev->fname, strlen(ijsdev->fname));
743     }
744 
745     if (code >= 0 && ijsdev->DeviceManufacturer)
746 	code = ijs_client_set_param(ijsdev->ctx, 0, "DeviceManufacturer",
747 			     ijsdev->DeviceManufacturer,
748 			     strlen(ijsdev->DeviceManufacturer));
749 
750     if (code >= 0 && ijsdev->DeviceModel)
751 	code = ijs_client_set_param(ijsdev->ctx, 0, "DeviceModel",
752 			     ijsdev->DeviceModel,
753 			     strlen(ijsdev->DeviceModel));
754 
755     if (code >= 0)
756 	code = gsijs_set_generic_params(ijsdev);
757 
758     if (code >= 0)
759 	code = gsijs_set_resolution(ijsdev);
760 
761     if (code >= 0)
762 	code = gsijs_set_margin_params(ijsdev);
763 
764     if (code >= 0)
765         ijsdev->krgb_mode = gsijs_set_krgb_mode(ijsdev);
766 
767     return code;
768 };
769 
770 /* Close the gsijs driver */
771 private int
gsijs_close(gx_device * dev)772 gsijs_close(gx_device *dev)
773 {
774     gx_device_ijs *ijsdev = (gx_device_ijs *)dev;
775     int code;
776 
777     /* ignore ijs errors on close */
778     ijs_client_end_job(ijsdev->ctx, 0);
779     ijs_client_close(ijsdev->ctx);
780     ijs_client_begin_cmd(ijsdev->ctx, IJS_CMD_EXIT);
781     ijs_client_send_cmd_wait(ijsdev->ctx);
782 
783     code = gdev_prn_close(dev);
784     if (ijsdev->IjsParams)
785 	gs_free(ijsdev->IjsParams, ijsdev->IjsParams_size, 1,
786 	    "gsijs_read_string_malloc");
787     if (ijsdev->ColorSpace)
788 	gs_free(ijsdev->ColorSpace,
789 		ijsdev->ColorSpace_size, 1,
790 		"gsijs_read_string_malloc");
791     if (ijsdev->DeviceManufacturer)
792 	gs_free(ijsdev->DeviceManufacturer,
793 		ijsdev->DeviceManufacturer_size, 1,
794 		"gsijs_read_string_malloc");
795     if (ijsdev->DeviceModel)
796 	gs_free(ijsdev->DeviceModel, ijsdev->DeviceModel_size, 1,
797 		"gsijs_read_string_malloc");
798     ijsdev->IjsParams = NULL;
799     ijsdev->IjsParams_size = 0;
800     ijsdev->DeviceManufacturer = NULL;
801     ijsdev->DeviceManufacturer_size = 0;
802     ijsdev->DeviceModel = NULL;
803     ijsdev->DeviceModel_size = 0;
804     return code;
805 }
806 
807 /* This routine is entirely analagous to gdev_prn_print_scan_lines(),
808    but computes width instead of height. It is not specific to IJS,
809    and a strong case could be made for moving it into gdevprn.c. */
810 private int
gsijs_raster_width(gx_device * pdev)811 gsijs_raster_width(gx_device *pdev)
812 {
813     int width = pdev->width;
814     gs_matrix imat;
815     float xscale;
816     int right, offset, end;
817 
818     (*dev_proc(pdev, get_initial_matrix)) (pdev, &imat);
819     xscale = imat.xx * 72.0;
820     right = (int)(dev_r_margin(pdev) * xscale);
821     offset = (int)(dev_x_offset(pdev) * xscale);
822     end = offset + width - right;
823     return min(width, end);
824 }
825 
826 /* Print a page.  Don't use normal printer gdev_prn_output_page
827  * because it opens the output file.
828  */
829 private int
gsijs_output_page(gx_device * dev,int num_copies,int flush)830 gsijs_output_page(gx_device *dev, int num_copies, int flush)
831 {
832     gx_device_ijs *ijsdev = (gx_device_ijs *)dev;
833     gx_device_printer *pdev = (gx_device_printer *)dev;
834     int raster = gdev_prn_raster(pdev);
835     int ijs_width, ijs_height;
836     int row_bytes, k_row_bytes=0;
837     int n_chan = pdev->color_info.num_components;
838     int krgb_mode = ijsdev->krgb_mode;
839     unsigned char *data;
840     char buf[256];
841     double xres = pdev->HWResolution[0];
842     double yres = pdev->HWResolution[1];
843     int code = 0;
844     int endcode = 0;
845     int status = 0;
846     int i, y;
847 
848     if ((data = gs_alloc_bytes(pdev->memory, raster, "gsijs_output_page"))
849 	== (unsigned char *)NULL)
850         return gs_note_error(gs_error_VMerror);
851 
852     /* Determine bitmap width and height */
853     ijs_height = gdev_prn_print_scan_lines(dev);
854 	ijs_width = gsijs_raster_width(dev);
855 
856     row_bytes = (ijs_width * pdev->color_info.depth + 7) >> 3;
857 
858     if (krgb_mode)
859     {
860         gx_device_clist_common *cdev = (gx_device_clist_common *)dev;
861         int band_height = cdev->page_info.band_params.BandHeight;
862         k_row_bytes = (ijs_width + 7) >> 3;
863 
864         /* Create banding buffer for k plane. */
865         ijsdev->k_width = ijs_width;
866         ijsdev->k_band_size = band_height * k_row_bytes;
867         if ((ijsdev->k_band = gs_malloc(ijsdev->k_band_size, 1, "gsijs_output_page")) == (unsigned char *)NULL)
868            return gs_note_error(gs_error_VMerror);
869     }
870 
871     /* Required page parameters */
872     sprintf(buf, "%d", n_chan);
873     gsijs_client_set_param(ijsdev, "NumChan", buf);
874     sprintf(buf, "%d", ijsdev->BitsPerSample);
875     gsijs_client_set_param(ijsdev, "BitsPerSample", buf);
876 
877     /* This needs to become more sophisticated for DeviceN. */
878     strcpy(buf, (n_chan == 4) ? "DeviceCMYK" :
879 	((n_chan == 3) ? (krgb_mode ? "KRGB" : "DeviceRGB") : "DeviceGray"));
880     gsijs_client_set_param(ijsdev, "ColorSpace", buf);
881 
882     sprintf(buf, "%d", ijs_width);
883     gsijs_client_set_param(ijsdev, "Width", buf);
884     sprintf(buf, "%d", ijs_height);
885     gsijs_client_set_param(ijsdev, "Height", buf);
886 
887     sprintf(buf, "%gx%g", xres, yres);
888     gsijs_client_set_param(ijsdev, "Dpi", buf);
889 
890 #ifdef KRGB_DEBUG
891     int kfd, rgbfd;
892     char sz[128];
893     kfd = open("/tmp/k.pbm", O_CREAT | O_TRUNC | O_RDWR, 0644);
894     rgbfd = open("/tmp/rgb.ppm", O_CREAT | O_TRUNC | O_RDWR, 0644);
895     snprintf(sz, sizeof(sz), "P4\n#gdevijs test\n%d\n%d\n", ijs_width, ijs_height);
896     write(kfd, sz, strlen(sz));
897     snprintf(sz, sizeof(sz), "P6\n#gdevijs test\n%d\n%d\n255\n", ijs_width, ijs_height);
898     write(rgbfd, sz, strlen(sz));
899 #endif
900 
901     for (i=0; i<num_copies; i++) {
902  	unsigned char *actual_data;
903 	ijs_client_begin_cmd (ijsdev->ctx, IJS_CMD_BEGIN_PAGE);
904 	status = ijs_client_send_cmd_wait(ijsdev->ctx);
905 
906 	for (y = 0; y < ijs_height; y++) {
907             if (krgb_mode)
908                 code = gsijs_get_bits(pdev, y, data, &actual_data);
909             else
910                 code = gdev_prn_get_bits(pdev, y, data, &actual_data);
911             if (code < 0)
912                 break;
913 #ifdef KRGB_DEBUG
914             write(rgbfd, actual_data, row_bytes);
915 #endif
916             status = ijs_client_send_data_wait(ijsdev->ctx, 0, (char *)actual_data, row_bytes);
917             if (status)
918                 break;
919 
920             if (krgb_mode) {
921                 code = gsijs_k_get_bits(pdev, y, &actual_data);
922                 if (code < 0)
923                     break;
924 #ifdef KRGB_DEBUG
925                 write(kfd, actual_data, k_row_bytes);
926 #endif
927                 status = ijs_client_send_data_wait(ijsdev->ctx, 0, (char *)actual_data, k_row_bytes);
928                 if (status)
929                     break;
930 	    }
931 	}
932 	ijs_client_begin_cmd(ijsdev->ctx, IJS_CMD_END_PAGE);
933 	status = ijs_client_send_cmd_wait(ijsdev->ctx);
934     }
935 
936 #ifdef KRGB_DEBUG
937     close(kfd);
938     close(rgbfd);
939 #endif
940 
941     if(krgb_mode)
942         gs_free(ijsdev->k_band, ijsdev->k_band_size, 1, "gsijs_output_page");
943 
944     gs_free_object(pdev->memory, data, "gsijs_output_page");
945 
946     endcode = (pdev->buffer_space && !pdev->is_async_renderer ?
947 	       clist_finish_page(dev, flush) : 0);
948 
949 
950     if (endcode < 0)
951 	return endcode;
952 
953     if (code < 0)
954 	return endcode;
955 
956     if (status < 0)
957 	return gs_note_error(gs_error_ioerror);
958 
959     code = gx_finish_output_page(dev, num_copies, flush);
960     return code;
961 }
962 
963 
964 /**************************************************************************/
965 
966 /* Get device parameters */
967 private int
gsijs_get_params(gx_device * dev,gs_param_list * plist)968 gsijs_get_params(gx_device *dev, gs_param_list *plist)
969 {
970     gx_device_ijs *ijsdev = (gx_device_ijs *)dev;
971     gs_param_string gps;
972     int code = gdev_prn_get_params(dev, plist);
973 
974     if (code >= 0) {
975 	param_string_from_transient_string(gps, ijsdev->IjsServer);
976 	code = param_write_string(plist, "IjsServer", &gps);
977     }
978 
979     if (code >= 0) {
980 	if (ijsdev->DeviceManufacturer) {
981 	    param_string_from_transient_string(gps,
982 					       ijsdev->DeviceManufacturer);
983 	    code = param_write_string(plist, "DeviceManufacturer", &gps);
984 	} else {
985 	    code = param_write_null(plist, "DeviceManufacturer");
986 	}
987     }
988 
989     if (code >= 0) {
990 	if (ijsdev->DeviceModel) {
991 	    param_string_from_transient_string(gps, ijsdev->DeviceModel);
992 	    code = param_write_string(plist, "DeviceModel", &gps);
993 	} else {
994 	    code = param_write_null(plist, "DeviceModel");
995 	}
996     }
997 
998     if (code >= 0) {
999 	if (ijsdev->IjsParams) {
1000 	    param_string_from_transient_string(gps, ijsdev->IjsParams);
1001 	    code = param_write_string(plist, "IjsParams", &gps);
1002 	} else {
1003 	    code = param_write_null(plist, "IjsParams");
1004 	}
1005     }
1006 
1007     if (code >= 0)
1008  	code = param_write_int(plist, "BitsPerSample", &ijsdev->BitsPerSample);
1009 
1010     if (code >= 0)
1011  	code = param_write_bool(plist, "IjsUseOutputFD",
1012 				&ijsdev->IjsUseOutputFD);
1013 
1014     if (code >= 0) {
1015 	if (ijsdev->IjsTumble_set) {
1016 	    code = param_write_bool(plist, "Tumble", &ijsdev->IjsTumble);
1017 	} else {
1018 	    code = param_write_null(plist, "Tumble");
1019 	}
1020     }
1021 
1022     return code;
1023 }
1024 
1025 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)1026 gsijs_read_int(gs_param_list *plist, gs_param_name pname, int *pval,
1027      int min_value, int max_value, bool only_when_closed)
1028 {
1029     int code = 0;
1030     int new_value;
1031 
1032     switch (code = param_read_int(plist, pname, &new_value)) {
1033 	case 0:
1034 	    if (only_when_closed && (new_value != *pval)) {
1035 		code = gs_error_rangecheck;
1036 		goto e;
1037 	    }
1038 	    if ((new_value >= min_value) && (new_value <= max_value)) {
1039 		*pval = new_value;
1040 		break;
1041 	    }
1042 	    code = gs_note_error(gs_error_rangecheck);
1043 	    goto e;
1044 	default:
1045 	    if (param_read_null(plist, pname) == 0)
1046 		return 1;
1047 	    e:param_signal_error(plist, pname, code);
1048 	case 1:
1049 	    ;
1050     }
1051     return code;
1052 }
1053 
1054 private int
gsijs_read_bool(gs_param_list * plist,gs_param_name pname,bool * pval,bool only_when_closed)1055 gsijs_read_bool(gs_param_list *plist, gs_param_name pname, bool *pval,
1056 		bool only_when_closed)
1057 {
1058     int code = 0;
1059     bool new_value;
1060 
1061     switch (code = param_read_bool(plist, pname, &new_value)) {
1062 	case 0:
1063 	    if (only_when_closed && (new_value != *pval)) {
1064 		code = gs_error_rangecheck;
1065 		goto e;
1066 	    }
1067 	    *pval = new_value;
1068 	    break;
1069 	default:
1070 	    if (param_read_null(plist, pname) == 0) {
1071 		return 1;
1072 	    }
1073 	    e:param_signal_error(plist, pname, code);
1074 	case 1:
1075 	    ;
1076     }
1077     return code;
1078 }
1079 
1080 private int
gsijs_read_string(gs_param_list * plist,gs_param_name pname,char * str,uint size,bool safety,bool only_when_closed)1081 gsijs_read_string(gs_param_list *plist, gs_param_name pname, char *str,
1082     uint size, bool safety, bool only_when_closed)
1083 {
1084     int code;
1085     gs_param_string new_value;
1086     int differs;
1087 
1088     switch (code = param_read_string(plist, pname, &new_value)) {
1089         case 0:
1090 	    differs = bytes_compare(new_value.data, new_value.size,
1091 			(const byte *)str, strlen(str));
1092 	    if (safety && differs) {
1093 		code = gs_error_invalidaccess;
1094 		goto e;
1095 	    }
1096 	    if (only_when_closed && differs) {
1097 		code = gs_error_rangecheck;
1098 		goto e;
1099 	    }
1100             if (new_value.size < size) {
1101 		strncpy(str, (const char *)new_value.data, new_value.size);
1102 		str[new_value.size+1] = '\0';
1103                 break;
1104 	    }
1105             code = gs_note_error(gs_error_rangecheck);
1106             goto e;
1107         default:
1108             if (param_read_null(plist, pname) == 0)
1109                 return 1;
1110           e:param_signal_error(plist, pname, code);
1111         case 1:
1112             ;
1113     }
1114     return code;
1115 }
1116 
1117 private int
gsijs_read_string_malloc(gs_param_list * plist,gs_param_name pname,char ** str,int * size,bool only_when_closed)1118 gsijs_read_string_malloc(gs_param_list *plist, gs_param_name pname, char **str,
1119     int *size, bool only_when_closed)
1120 {
1121     int code;
1122     gs_param_string new_value;
1123     int differs;
1124 
1125     switch (code = param_read_string(plist, pname, &new_value)) {
1126         case 0:
1127 	    differs = bytes_compare(new_value.data, new_value.size,
1128 			(const byte *)(*str ? *str : ""),
1129 		  	*str ? strlen(*str) : 0);
1130 	    if (only_when_closed && differs) {
1131 		code = gs_error_rangecheck;
1132 		goto e;
1133 	    }
1134 	    if (new_value.size >= *size) {
1135 	        if (*str)
1136 		    gs_free(str, *size, 1, "gsijs_read_string_malloc");
1137 		*str = NULL;
1138 		*size = 0;
1139 	    }
1140 	    *str = gs_malloc(new_value.size + 1, 1,
1141 		"gsijs_read_string_malloc");
1142 	    if (*str == NULL) {
1143                 code = gs_note_error(gs_error_VMerror);
1144                 goto e;
1145 	    }
1146 	    *size = new_value.size + 1;
1147 	    strncpy(*str, (const char *)new_value.data, new_value.size);
1148 	    (*str)[new_value.size] = '\0';
1149             break;
1150         default:
1151             if (param_read_null(plist, pname) == 0)
1152                 return 1;
1153           e:param_signal_error(plist, pname, code);
1154         case 1:
1155             ;
1156     }
1157     return code;
1158 }
1159 
1160 
1161 private int
gsijs_put_params(gx_device * dev,gs_param_list * plist)1162 gsijs_put_params(gx_device *dev, gs_param_list *plist)
1163 {
1164     gx_device_ijs *ijsdev = (gx_device_ijs *)dev;
1165     int code = 0;
1166     bool is_open = dev->is_open;
1167 
1168     /* We allow duplex to be set in all cases. At some point, it may
1169        be worthwhile to query the device to see if it supports
1170        duplex. Note also that this code will get called even before
1171        the device has been opened, which is when the -DDuplex
1172        command line is processed. */
1173     if (ijsdev->Duplex_set < 0) {
1174 	ijsdev->Duplex = 1;
1175 	ijsdev->Duplex_set = 0;
1176     }
1177 
1178     /* If a parameter must not be changed after the device is open,
1179      * the last parameter of gsijs_read_xxx() is is_open.
1180      * If a parameter may be changed at any time, it is false.
1181      */
1182     if (code >= 0)
1183 	code = gsijs_read_string(plist, "IjsServer",
1184 	    ijsdev->IjsServer, sizeof(ijsdev->IjsServer),
1185 	    dev->LockSafetyParams, is_open);
1186 
1187     if (code >= 0)
1188 	code = gsijs_read_string_malloc(plist, "DeviceManufacturer",
1189 	    &ijsdev->DeviceManufacturer, &ijsdev->DeviceManufacturer_size,
1190 	    is_open);
1191 
1192     if (code >= 0)
1193 	code = gsijs_read_string_malloc(plist, "DeviceModel",
1194 	    &ijsdev->DeviceModel, &ijsdev->DeviceModel_size,
1195 	    is_open);
1196 
1197     if (code >= 0)
1198 	code = gsijs_read_string_malloc(plist, "IjsParams",
1199 	    &(ijsdev->IjsParams), &(ijsdev->IjsParams_size), is_open);
1200 
1201     if (code >= 0)
1202 	code = gsijs_read_int(plist, "BitsPerSample", &ijsdev->BitsPerSample,
1203 		1, 16, is_open);
1204 
1205     if (code >= 0)
1206  	code = gsijs_read_bool(plist, "IjsUseOutputFD",
1207 			       &ijsdev->IjsUseOutputFD, is_open);
1208 
1209     if (code >= 0) {
1210 	code = gsijs_read_string_malloc(plist, "ProcessColorModel",
1211 	    &ijsdev->ColorSpace, &ijsdev->ColorSpace_size, is_open);
1212     }
1213 
1214     if (code >= 0) {
1215  	code = gsijs_read_bool(plist, "Tumble", &ijsdev->IjsTumble, false);
1216 	if (code == 0)
1217 	    ijsdev->IjsTumble_set = true;
1218     }
1219 
1220     if (code >= 0)
1221 	code = gsijs_set_color_format(ijsdev);
1222 
1223     if (code >= 0)
1224 	code = gdev_prn_put_params(dev, plist);
1225 
1226     if (code >= 0 && is_open) {
1227 	code = gsijs_set_generic_params(ijsdev);
1228 	if (code >= 0)
1229 	  code = gsijs_set_margin_params(ijsdev);
1230 	if (code < 0)
1231 	    return gs_note_error(gs_error_ioerror);
1232     }
1233 
1234     return code;
1235 }
1236 
1237 private int
gsijs_client_set_param(gx_device_ijs * ijsdev,const char * key,const char * value)1238 gsijs_client_set_param(gx_device_ijs *ijsdev, const char *key,
1239     const char *value)
1240 {
1241     int code = ijs_client_set_param(ijsdev->ctx, 0 /* job id */,
1242 	key, value, strlen(value));
1243     if (code < 0)
1244 	dprintf2("ijs: Can't set parameter %s=%s\n", key, value);
1245     return code;
1246 }
1247 
1248 private int
gsijs_set_color_format(gx_device_ijs * ijsdev)1249 gsijs_set_color_format(gx_device_ijs *ijsdev)
1250 {
1251     gx_device_color_info dci = ijsdev->color_info;
1252     int components;	/* 1=gray, 3=RGB, 4=CMYK */
1253     int bpc = ijsdev->BitsPerSample;		/* bits per component */
1254     int maxvalue;
1255     const char *ColorSpace = ijsdev->ColorSpace;
1256 
1257     if (ColorSpace == NULL)
1258 	ColorSpace = "DeviceRGB";
1259 
1260     if (!strcmp (ColorSpace, "DeviceGray")) {
1261 	components = 1;
1262 	if (bpc == 1) {
1263 	    ijsdev->procs.map_rgb_color = gx_default_w_b_map_rgb_color;
1264 	    ijsdev->procs.map_color_rgb = gx_default_w_b_map_color_rgb;
1265 	} else {
1266 	    ijsdev->procs.map_rgb_color = gx_default_gray_map_rgb_color;
1267 	    ijsdev->procs.map_color_rgb = gx_default_gray_map_color_rgb;
1268 	}
1269     } else if (!strcmp (ColorSpace, "DeviceRGB")) {
1270 	components = 3;
1271 	ijsdev->procs.map_rgb_color = gx_default_rgb_map_rgb_color;
1272 	ijsdev->procs.map_color_rgb = gx_default_rgb_map_color_rgb;
1273     } else if (!strcmp (ColorSpace, "DeviceCMYK")) {
1274 	components = 4;
1275 	ijsdev->procs.map_cmyk_color = cmyk_8bit_map_cmyk_color;
1276 	ijsdev->procs.map_color_rgb = cmyk_8bit_map_color_rgb;
1277     } else {
1278 	return -1;
1279     }
1280 
1281     maxvalue = (1 << bpc) - 1;
1282     dci.num_components = components;
1283     dci.depth = bpc * components;
1284     dci.max_gray = maxvalue;
1285     dci.max_color = components > 1 ? maxvalue : 0;
1286     dci.dither_grays = maxvalue+1;
1287     dci.dither_colors = components > 1 ? maxvalue+1 : 0;
1288 
1289     /* restore old anti_alias info */
1290     dci.anti_alias = ijsdev->color_info.anti_alias;
1291 
1292     ijsdev->color_info = dci;
1293 
1294     return 0;
1295 }
1296