Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
 
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
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
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
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
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
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
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
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
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
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
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
 
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
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
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
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
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
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
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
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
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
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
}