1/*
2 *  $Id: ijsgutenprint.c,v 1.23 2010/07/19 11:14:04 rlk Exp $
3 *
4 *   IJS server for Gutenprint.
5 *
6 *   Copyright 2001 Robert Krawitz (rlk@alum.mit.edu)
7 *
8 *   Originally written by Russell Lang, copyright assigned to Robert Krawitz.
9 *
10 *   This program is free software; you can redistribute it and/or modify it
11 *   under the terms of the GNU General Public License as published by the Free
12 *   Software Foundation; either version 2 of the License, or (at your option)
13 *   any later version.
14 *
15 *   This program is distributed in the hope that it will be useful, but
16 *   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17 *   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
18 *   for more details.
19 *
20 *   You should have received a copy of the GNU General Public License
21 *   along with this program; if not, write to the Free Software
22 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 *
24 * Revision History:
25 *
26 *   See ChangeLog
27 */
28
29#ifdef HAVE_CONFIG_H
30#include <config.h>
31#endif
32#include <gutenprint/gutenprint.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <unistd.h>
37#include <locale.h>
38#include <ijs.h>
39#include <ijs_server.h>
40#include <errno.h>
41#include <gutenprint/gutenprint-intl-internal.h>
42
43
44static int suppress_messages = 0;
45volatile int SDEBUG = 1;
46static int job_aborted = 0;
47
48#define STP_DEBUG(x)				\
49do						\
50{						\
51  if (!suppress_messages)			\
52    fprintf(stderr, "DEBUG: ");			\
53  if (!suppress_messages)			\
54    x;						\
55} while (0)
56
57typedef struct _GutenprintParamList GutenprintParamList;
58
59struct _GutenprintParamList {
60  GutenprintParamList *next;
61  char *key;
62  char *value;
63  int value_size;
64};
65
66typedef struct _IMAGE
67{
68  IjsServerCtx *ctx;
69  stp_vars_t *v;
70  char *filename;	/* OutputFile */
71  int fd;		/* OutputFD + 1 (so that 0 is invalid) */
72  int width;		/* pixels */
73  int height;		/* pixels */
74  int bps;		/* bytes per sample */
75  int n_chan;		/* number of channels */
76  int xres;		/* dpi */
77  int yres;
78  int output_type;
79  int left_margin;
80  int right_margin;
81  int top_margin;
82  int bottom_margin;
83  int monochrome_flag;	/* for monochrome output */
84  int row;		/* row number in buffer */
85  int row_width;	/* length of a row */
86  char *row_buf;	/* buffer for raster */
87  double total_bytes;	/* total size of raster */
88  double bytes_left;	/* bytes remaining to be read */
89  GutenprintParamList *params;
90} IMAGE;
91
92static const char DeviceGray[] = "DeviceGray";
93static const char DeviceRGB[] = "DeviceRGB";
94static const char DeviceCMYK[] = "DeviceCMYK";
95
96static const char *version_id;
97static int version_is_ok = 1;
98#define VERSION_MISMATCH "\
99ERROR: ijsgutenprint: the version of Gutenprint software installed (%s)\n\
100ERROR: ijsgutenprint: does not match the PPD file (%s).  If you have upgraded your version\n\
101ERROR: ijsgutenprint: of Gutenprint recently, you must reinstall all printer queues.\n\
102ERROR: ijsgutenprint: Please refer to your vendor's documentation or the ``foomatic-ppdfile''\n\
103ERROR: ijsgutenprint: command for instructions.\n\
104ERROR: ijsgutenprint: the version of Gutenprint software installed (%s) does not match the PPD file (%s).\n"
105
106const char *gutenprint_ppd_version = NULL;
107static int ppd_mode = 0;	/* Use PPD-style margins */
108
109static stp_string_list_t *option_remap_list = NULL;
110static int print_messages_as_errors = 0;
111
112static double page_bytes_printed = 0;
113static double total_bytes_printed = 0;
114
115static char *
116c_strdup(const char *s)
117{
118  char *ret = stp_malloc(strlen(s) + 1);
119  strcpy(ret, s);
120  return ret;
121}
122
123static int
124image_init(IMAGE *img, IjsPageHeader *ph)
125{
126  img->width = ph->width;
127  img->height = ph->height;
128  img->bps = ph->bps;
129  img->n_chan = ph->n_chan;
130  img->xres = ph->xres;
131  img->yres = ph->yres;
132
133  img->row = -1;
134  img->row_width = (ph->n_chan * ph->bps * ph->width + 7) >> 3;
135  if (img->row_buf)
136    stp_free(img->row_buf);
137  img->row_buf = (char *)stp_malloc(img->row_width);
138  STP_DEBUG(fprintf(stderr, "ijsgutenprint: image_init\n"));
139  STP_DEBUG(fprintf(stderr,
140		    "ijsgutenprint: ph width %d height %d bps %d n_chan %d xres %f yres %f\n",
141		    ph->width, ph->height, ph->bps, ph->n_chan, ph->xres,
142		    ph->yres));
143
144  stp_set_string_parameter(img->v, "ChannelBitDepth", "8");
145  if ((img->bps == 1) && (img->n_chan == 1) &&
146      (strncmp(ph->cs, DeviceGray, strlen(DeviceGray)) == 0))
147    {
148      STP_DEBUG(fprintf(stderr, "ijsgutenprint: output monochrome\n"));
149      stp_set_string_parameter(img->v, "InputImageType", "Whitescale");
150      stp_set_string_parameter(img->v, "PrintingMode", "BW");
151      stp_set_string_parameter(img->v, "ColorCorrection", "Threshold");
152      img->monochrome_flag = 1;
153      /* 8-bit greyscale */
154    }
155  else if (img->bps == 8 || img->bps == 16)
156    {
157      if (img->bps == 8)
158	stp_set_string_parameter(img->v, "ChannelBitDepth", "8");
159      else
160	stp_set_string_parameter(img->v, "ChannelBitDepth", "16");
161      if ((img->n_chan == 1) &&
162	  (strncmp(ph->cs, DeviceGray, strlen(DeviceGray)) == 0))
163	{
164	  STP_DEBUG(fprintf(stderr, "ijsgutenprint: output gray\n"));
165	  stp_set_string_parameter(img->v, "InputImageType", "Whitescale");
166	  stp_set_string_parameter(img->v, "PrintingMode", "BW");
167	  img->monochrome_flag = 0;
168	  /* 8/16-bit greyscale */
169	}
170      else if ((img->n_chan == 3) &&
171	       (strncmp(ph->cs, DeviceRGB, strlen(DeviceRGB)) == 0))
172	{
173	  STP_DEBUG(fprintf(stderr, "ijsgutenprint: output color\n"));
174	  stp_set_string_parameter(img->v, "InputImageType", "RGB");
175	  stp_set_string_parameter(img->v, "PrintingMode", "Color");
176	  img->monochrome_flag = 0;
177	  /* 24/48-bit RGB colour */
178	}
179      else if ((img->n_chan == 4) &&
180	       (strncmp(ph->cs, DeviceCMYK, strlen(DeviceCMYK)) == 0))
181	{
182	  STP_DEBUG(fprintf(stderr, "ijsgutenprint: output CMYK\n"));
183	  stp_set_string_parameter(img->v, "InputImageType", "CMYK");
184	  stp_set_string_parameter(img->v, "PrintingMode", "Color");
185	  img->monochrome_flag = 0;
186	  /* 32/64-bit CMYK colour */
187	}
188    }
189  else
190    {
191      fprintf(stderr, "ERROR: ijsgutenprint: Bad color space: bps %d channels %d space %s\n",
192		img->bps, img->n_chan, ph->cs);
193      /* unsupported */
194      return -1;
195    }
196
197  if (img->row_buf == NULL)
198    {
199      fprintf(stderr, "ERROR: ijsgutenprint: No row buffer\n");
200      return -1;
201    }
202
203  return 0;
204}
205
206static void
207image_finish(IMAGE *img)
208{
209  if (img->row_buf)
210    stp_free(img->row_buf);
211  img->row_buf = NULL;
212}
213
214static double
215get_float(const char *str, const char *name, double *pval)
216{
217  float new_value;
218  int status = 0;
219  /* Force locale to "C", because decimal numbers coming from the IJS
220     client are always with a decimal point, nver with a decimal comma */
221  setlocale(LC_ALL, "C");
222  if (sscanf(str, "%f", &new_value) == 1)
223    *pval = new_value;
224  else
225    {
226      fprintf(stderr, "ERROR: ijsgutenprint: Unable to parse parameter %s=%s (expect a number)\n",
227	      name, str);
228      status = -1;
229    }
230  setlocale(LC_ALL, "");
231  return status;
232}
233
234static int
235get_int(const char *str, const char *name, int *pval)
236{
237  int new_value;
238  int status = 0;
239  /* Force locale to "C", because decimal numbers sent to the IJS
240     client must have a decimal point, nver a decimal comma */
241  setlocale(LC_ALL, "C");
242  if (sscanf(str, "%d", &new_value) == 1)
243    *pval = new_value;
244  else
245    {
246      fprintf(stderr, "ERROR: ijsgutenprint: Unable to parse parameter %s=%s (expect a number)\n",
247	      name, str);
248      status = -1;
249    }
250  setlocale(LC_ALL, "");
251  return status;
252}
253
254static int
255parse_wxh_internal(const char *val, int size, double *pw, double *ph)
256{
257  char buf[256];
258  char *tail;
259  int i;
260
261  for (i = 0; i < size; i++)
262    if (val[i] == 'x')
263      break;
264
265  if (i + 1 >= size)
266    return IJS_ESYNTAX;
267
268  if (i >= sizeof(buf))
269    return IJS_EBUF;
270
271  memcpy (buf, val, i);
272  buf[i] = 0;
273  *pw = strtod (buf, &tail);
274  if (tail == buf)
275    return IJS_ESYNTAX;
276
277  if (size - i > sizeof(buf))
278    return IJS_EBUF;
279
280  memcpy (buf, val + i + 1, size - i - 1);
281  buf[size - i - 1] = 0;
282  *ph = strtod (buf, &tail);
283  if (tail == buf)
284    return IJS_ESYNTAX;
285
286  return 0;
287}
288
289/* A C implementation of /^(\d\.+\-eE)+x(\d\.+\-eE)+$/ */
290static int
291gutenprint_parse_wxh (const char *val, int size, double *pw, double *ph)
292{
293  /* Force locale to "C", because decimal numbers coming from the IJS
294     client are always with a decimal point, nver with a decimal comma */
295  int status;
296  setlocale(LC_ALL, "C");
297  status = parse_wxh_internal(val, size, pw, ph);
298  setlocale(LC_ALL, "");
299  return status;
300}
301
302/**
303 * gutenprint_find_key: Search parameter list for key.
304 *
305 * @key: key to look up
306 *
307 * Return value: GutenprintParamList entry matching @key, or NULL.
308 **/
309static GutenprintParamList *
310gutenprint_find_key (GutenprintParamList *pl, const char *key)
311{
312  GutenprintParamList *curs;
313
314  for (curs = pl; curs != NULL; curs = curs->next)
315    {
316      if (!strcmp (curs->key, key))
317	return curs;
318    }
319  return NULL;
320}
321
322static int
323gutenprint_status_cb (void *status_cb_data,
324		      IjsServerCtx *ctx,
325		      IjsJobId job_id)
326{
327  return 0;
328}
329
330static const char *
331list_all_parameters(void)
332{
333  static char *param_string = NULL;
334  size_t param_length = 0;
335  size_t offset = 0;
336  if (param_length == 0)
337    {
338      stp_string_list_t *sl = stp_string_list_create();
339      int printer_count = stp_printer_model_count();
340      int i;
341      stp_string_list_add_string(sl, "PrintableArea", NULL);
342      stp_string_list_add_string(sl, "Dpi", NULL);
343      stp_string_list_add_string(sl, "PrintableTopLeft", NULL);
344      stp_string_list_add_string(sl, "DeviceManufacturer", NULL);
345      stp_string_list_add_string(sl, "DeviceModel", NULL);
346      stp_string_list_add_string(sl, "PageImageFormat", NULL);
347      stp_string_list_add_string(sl, "OutputFile", NULL);
348      stp_string_list_add_string(sl, "OutputFd", NULL);
349      stp_string_list_add_string(sl, "PaperSize", NULL);
350      stp_string_list_add_string(sl, "MediaName", NULL);
351      stp_string_list_add_string(sl, "STP_VERSION", NULL);
352      for (i = 0; i < printer_count; i++)
353	{
354	  const stp_printer_t *printer = stp_get_printer_by_index(i);
355	  stp_parameter_list_t params =
356	    stp_get_parameter_list(stp_printer_get_defaults(printer));
357	  size_t count = stp_parameter_list_count(params);
358	  int j;
359	  if (strcmp(stp_printer_get_family(printer), "ps") == 0 ||
360	      strcmp(stp_printer_get_family(printer), "raw") == 0)
361	    continue;
362	  for (j = 0; j < count; j++)
363	    {
364	      const stp_parameter_t *param =
365		stp_parameter_list_param(params, j);
366	      char *tmp =
367		stp_malloc(strlen(param->name) + strlen("STP_") + 1);
368	      sprintf(tmp, "STP_%s", param->name);
369	      if ((param->p_level < STP_PARAMETER_LEVEL_ADVANCED4) &&
370		  (param->p_type != STP_PARAMETER_TYPE_RAW) &&
371		  (param->p_type != STP_PARAMETER_TYPE_FILE) &&
372		  (!param->read_only) &&
373		  (strcmp(param->name, "Resolution") != 0) &&
374		  (strcmp(param->name, "PageSize") != 0) &&
375		  (!stp_string_list_is_present(sl, tmp)))
376		{
377		  sprintf(tmp, "STP_%s", param->name);
378		  stp_string_list_add_string(sl, tmp, NULL);
379		  if ((param->p_type == STP_PARAMETER_TYPE_DOUBLE ||
380		       param->p_type == STP_PARAMETER_TYPE_DIMENSION) &&
381		      !param->read_only && param->is_active &&
382		      !param->is_mandatory)
383		    {
384		      char *tmp1 =
385			stp_malloc(strlen(param->name) + strlen("STP_Enable") + 1);
386		      sprintf(tmp1, "STP_Enable%s", param->name);
387		      stp_string_list_add_string(sl, tmp1, NULL);
388		      stp_free(tmp1);
389		    }
390		}
391	      stp_free(tmp);
392	    }
393	  stp_parameter_list_destroy(params);
394	}
395      for (i = 0; i < stp_string_list_count(sl); i++)
396	param_length += strlen(stp_string_list_param(sl, i)->name) + 1;
397      param_string = stp_malloc(param_length);
398      for (i = 0; i < stp_string_list_count(sl); i++)
399	{
400	  stp_param_string_t *param = stp_string_list_param(sl, i);
401	  strcpy(param_string + offset, param->name);
402	  offset += strlen(param->name) + 1;
403	  param_string[offset - 1] = ',';
404	}
405      if (offset != param_length)
406	{
407	  fprintf(stderr, "ERROR: ijsgutenprint: Bad string length %lu != %lu!\n",
408		  (unsigned long) offset,
409		  (unsigned long) param_length);
410	  exit(1);
411	}
412      param_string[param_length - 1] = '\0';
413    }
414  return param_string;
415}
416
417
418static int
419gutenprint_list_cb (void *list_cb_data,
420	      IjsServerCtx *ctx,
421	      IjsJobId job_id,
422	      char *val_buf,
423	      int val_size)
424{
425  const char *param_list = list_all_parameters();
426  int size = strlen (param_list);
427  STP_DEBUG(fprintf(stderr, "ijsgutenprint: gutenprint_list_cb: %s\n", param_list));
428
429  if (size > val_size)
430    return IJS_EBUF;
431
432  memcpy (val_buf, param_list, size);
433  return size;
434}
435
436static int
437gutenprint_enum_cb (void *enum_cb_data,
438	      IjsServerCtx *ctx,
439	      IjsJobId job_id,
440	      const char *key,
441	      char *val_buf,
442	      int val_size)
443{
444  const char *val = NULL;
445  STP_DEBUG(fprintf(stderr, "ijsgutenprint: gutenprint_enum_cb: key=%s\n", key));
446  if (!strcmp (key, "ColorSpace"))
447    val = "DeviceRGB,DeviceGray,DeviceCMYK";
448  else if (!strcmp (key, "DeviceManufacturer"))
449    val = "Gutenprint";
450  else if (!strcmp (key, "DeviceModel"))
451    val = "gutenprint";
452  else if (!strcmp (key, "PageImageFormat"))
453    val = "Raster";
454  else if (!strcmp (key, "BitsPerSample"))
455    val = "8,16";
456  else if (!strcmp (key, "ByteSex"))
457    {
458#if __BYTE_ORDER == __LITTLE_ENDIAN
459      val="little-endian";
460#else
461      val="big-endian";
462#endif
463    }
464
465  if (val == NULL)
466    return IJS_EUNKPARAM;
467  else
468    {
469      int size = strlen (val);
470
471      if (size > val_size)
472	return IJS_EBUF;
473      memcpy (val_buf, val, size);
474      return size;
475    }
476}
477
478static int
479gutenprint_get_cb (void *get_cb_data,
480	     IjsServerCtx *ctx,
481	     IjsJobId job_id,
482	     const char *key,
483	     char *val_buf,
484	     int val_size)
485{
486  IMAGE *img = (IMAGE *)get_cb_data;
487  stp_vars_t *v = img->v;
488  const stp_printer_t *printer = stp_get_printer(v);
489  GutenprintParamList *pl = img->params;
490  GutenprintParamList *curs;
491  const char *val = NULL;
492  char buf[256];
493
494  STP_DEBUG(fprintf(stderr, "ijsgutenprint: gutenprint_get_cb: %s\n", key));
495  if (!printer)
496    {
497      if (strlen(stp_get_driver(v)) == 0)
498	fprintf(stderr, "ERROR: ijsgutenprint: Printer must be specified with -sDeviceModel\n");
499      else
500	fprintf(stderr, "ERROR: ijsgutenprint: Printer %s is not a known model\n",
501		stp_get_driver(v));
502      return IJS_EUNKPARAM;
503    }
504  curs = gutenprint_find_key (pl, key);
505  if (curs != NULL)
506    {
507      if (curs->value_size > val_size)
508	return IJS_EBUF;
509      memcpy (val_buf, curs->value, curs->value_size);
510      return curs->value_size;
511    }
512
513  if (!strcmp(key, "PrintableArea"))
514    {
515      int l, r, b, t;
516      int h, w;
517      if (ppd_mode)
518	{
519	  stp_get_media_size(v, &w, &h);
520	  stp_get_maximum_imageable_area(v, &l, &r, &b, &t);
521	  if (l < 0)
522	    l = 0;
523	  if (r > w)
524	    r = w;
525	  if (t < 0)
526	    t = 0;
527	  if (b > h)
528	    b = h;
529	}
530      else
531	stp_get_imageable_area(v, &l, &r, &b, &t);
532
533
534      h = b - t;
535      w = r - l;
536      /* Force locale to "C", because decimal numbers sent to the IJS
537	 client must have a decimal point, nver a decimal comma */
538      setlocale(LC_ALL, "C");
539      sprintf(buf, "%gx%g", (double) w / 72.0, (double) h / 72.0);
540      setlocale(LC_ALL, "");
541      STP_DEBUG(fprintf(stderr, "ijsgutenprint: PrintableArea %d %d %s\n", h, w, buf));
542      val = buf;
543    }
544  else if (!strcmp(key, "Dpi"))
545    {
546      int x, y;
547      stp_describe_resolution(v, &x, &y);
548      /* Force locale to "C", because decimal numbers sent to the IJS
549	 client must have a decimal point, nver a decimal comma */
550      setlocale(LC_ALL, "C");
551      sprintf(buf, "%d", x);
552      setlocale(LC_ALL, "");
553      STP_DEBUG(fprintf(stderr, "ijsgutenprint: Dpi %d %d (%d) %s\n", x, y, x, buf));
554      val = buf;
555    }
556  else if (!strcmp(key, "PrintableTopLeft"))
557    {
558      int l, r, b, t;
559      int h, w;
560      stp_get_media_size(v, &w, &h);
561      if (ppd_mode)
562	{
563	  stp_get_maximum_imageable_area(v, &l, &r, &b, &t);
564	  if (l < 0)
565	    l = 0;
566	  if (r > w)
567	    r = w;
568	  if (t < 0)
569	    t = 0;
570	  if (b > h)
571	    b = h;
572	}
573      else
574	stp_get_imageable_area(v, &l, &r, &b, &t);
575      /* Force locale to "C", because decimal numbers sent to the IJS
576	 client must have a decimal point, nver a decimal comma */
577      setlocale(LC_ALL, "C");
578      sprintf(buf, "%gx%g", (double) l / 72.0, (double) t / 72.0);
579      setlocale(LC_ALL, "");
580      STP_DEBUG(fprintf(stderr, "ijsgutenprint: PrintableTopLeft %d %d %s\n", t, l, buf));
581      val = buf;
582    }
583  else if (!strcmp (key, "DeviceManufacturer"))
584    val = "Gutenprint";
585  else if (!strcmp (key, "DeviceModel"))
586    val = stp_get_driver(img->v);
587  else if (!strcmp (key, "PageImageFormat"))
588    val = "Raster";
589
590  if (val == NULL)
591    return IJS_EUNKPARAM;
592  else
593    {
594      int size = strlen (val);
595
596      if (size > val_size)
597	return IJS_EBUF;
598      memcpy (val_buf, val, size);
599      return size;
600    }
601}
602
603static void
604print_debug_setcb(const char *key, const char *value, int value_size)
605{
606  fprintf (stderr, "ijsgutenprint: gutenprint_set_cb: %s='", key);
607  fwrite (value, 1, value_size, stderr);
608  fputs ("'\n", stderr);
609}
610
611static int
612gutenprint_set_cb (void *set_cb_data, IjsServerCtx *ctx, IjsJobId jobid,
613	     const char *key, const char *value, int value_size)
614{
615  int code = 0;
616  char vbuf[256];
617  int i;
618  double z = 0;
619  IMAGE *img = (IMAGE *)set_cb_data;
620  STP_DEBUG(print_debug_setcb(key, value, value_size));
621  if (value_size > sizeof(vbuf)-1)
622    return -1;
623  memset(vbuf, 0, sizeof(vbuf));
624  memcpy(vbuf, value, value_size);
625
626  if (strcmp(key, "OutputFile") == 0)
627    {
628      if (img->filename)
629	stp_free(img->filename);
630      img->filename = c_strdup(vbuf);
631    }
632  else if (strcmp(key, "OutputFD") == 0)
633    {
634      /* Force locale to "C", because decimal numbers sent to the IJS
635	 client must have a decimal point, nver a decimal comma */
636      setlocale(LC_ALL, "C");
637      img->fd = atoi(vbuf) + 1;
638      setlocale(LC_ALL, "");
639    }
640  else if (strcmp(key, "DeviceManufacturer") == 0)
641    ;				/* We don't care who makes it */
642  else if (strcmp(key, "DeviceModel") == 0)
643    {
644      const stp_printer_t *printer = stp_get_printer_by_driver(vbuf);
645      stp_set_driver(img->v, vbuf);
646      if (printer &&
647	  strcmp(stp_printer_get_family(printer), "ps") != 0 &&
648	  strcmp(stp_printer_get_family(printer), "raw") != 0)
649        {
650	  stp_set_printer_defaults(img->v, printer);
651          /* Reset JobMode to "Job" */
652          stp_set_string_parameter(img->v, "JobMode", "Job");
653        }
654      else
655	{
656	  fprintf(stderr, "ERROR: ijsgutenprint: unknown DeviceModel %s\n", vbuf);
657	  code = IJS_ERANGE;
658	}
659    }
660  else if (strcmp(key, "TopLeft") == 0)
661    {
662      int l, r, b, t, pw, ph;
663      double w, h;
664      stp_get_media_size(img->v, &pw, &ph);
665      if (ppd_mode)
666	{
667	  stp_get_maximum_imageable_area(img->v, &l, &r, &b, &t);
668	  STP_DEBUG(fprintf(stderr, "ijsgutenprint: l %d r %d t %d b %d pw %d ph %d\n",
669			    l, r, t, b, pw, ph));
670	  if (l < 0)
671	    l = 0;
672	  if (r > pw)
673	    r = pw;
674	  if (t < 0)
675	    t = 0;
676	  if (b > ph)
677	    b = ph;
678	}
679      else
680	stp_get_imageable_area(img->v, &l, &r, &b, &t);
681      STP_DEBUG(fprintf(stderr, "ijsgutenprint ppd_mode %d top left: %s\n",
682			ppd_mode, vbuf));
683      STP_DEBUG(fprintf(stderr, "ijsgutenprint: l %d r %d t %d b %d pw %d ph %d\n",
684			l, r, t, b, pw, ph));
685      code = gutenprint_parse_wxh(vbuf, strlen(vbuf), &w, &h);
686      if (code == 0)
687	{
688	  int al = (w * 72) + .5;
689	  int ah = (h * 72) + .5;
690	  STP_DEBUG(fprintf(stderr, "ijsgutenprint: left top %f %f %d %d %s\n",
691			    w * 72, h * 72, al, ah, vbuf));
692	  if (al >= 0)
693	    stp_set_left(img->v, al);
694	  if (ah >= 0)
695	    stp_set_top(img->v, ah);
696	  stp_set_width(img->v, r - l);
697	  stp_set_height(img->v, b - t);
698	}
699      else
700	fprintf(stderr, "ERROR: ijsgutenprint: cannot parse TopLeft %s\n", vbuf);
701    }
702  else if (strcmp(key, "PaperSize") == 0)
703    {
704      double w, h;
705      code = gutenprint_parse_wxh(vbuf, strlen(vbuf), &w, &h);
706      if (code == 0)
707	{
708	  const stp_papersize_t *p;
709	  w *= 72;
710	  h *= 72;
711	  STP_DEBUG(fprintf(stderr, "ijsgutenprint: paper size %f %f %s\n", w, h, vbuf));
712	  stp_set_page_width(img->v, w);
713	  stp_set_page_height(img->v, h);
714	  if ((p = stp_get_papersize_by_size_exact(h, w)) != NULL)
715	    {
716	      STP_DEBUG(fprintf(stderr, "ijsgutenprint: Found page size %s\n", p->name));
717	      stp_set_string_parameter(img->v, "PageSize", p->name);
718	    }
719	  else
720	    STP_DEBUG(fprintf(stderr, "ijsgutenprint: No matching paper size found\n"));
721	}
722      else
723	fprintf(stderr, "ERROR: ijsgutenprint: cannot parse PaperSize %s\n", vbuf);
724    }
725
726/*
727 * Duplex & Tumble. The PS: values come from the PostScript document, the
728 * others come from the command line. However, the PS: values seem to get
729 * fed back again as non PS: values after the command line is processed.
730 * The net effect is that the command line is always overridden by the
731 * values from the document.
732 */
733
734  else if ((strcmp (key, "Duplex") == 0) || (strcmp (key, "PS:Duplex") == 0))
735    {
736      stp_set_string_parameter(img->v, "x_Duplex", vbuf);
737    }
738  else if ((strcmp (key, "Tumble") == 0) || (strcmp (key, "PS:Tumble") == 0))
739    {
740       stp_set_string_parameter(img->v, "x_Tumble", vbuf);
741    }
742  else if (strcmp(key, "STP_VERSION") == 0)
743    {
744      ppd_mode = 1;
745      if (strcmp(vbuf, version_id) != 0)
746	{
747	  fprintf(stderr, VERSION_MISMATCH,
748		  version_id, vbuf, version_id, vbuf);
749	  version_is_ok = 0;
750	  gutenprint_ppd_version = c_strdup(vbuf);
751	  code = IJS_ERANGE;
752	}
753    }
754  else if (strncmp(key, "STP_OPT_REMAP_", strlen("STP_OPT_REMAP_")) == 0)
755    {
756      const char *xkey = key + strlen("STP_OPT_REMAP_");
757      char *buf1 = stp_malloc(strlen("STP_OPT_") + strlen(xkey) + 1);
758      char *buf2 = c_strdup(vbuf);
759      strcpy(buf1, "STP_OPT_");
760      strcpy(buf1 + strlen("STP_OPT_"), xkey);
761      stp_string_list_add_string(option_remap_list, buf1, buf2);
762      stp_free(buf1);
763      stp_free(buf2);
764    }
765  else if (strncmp(key, "STP_", 4) == 0)
766    {
767      stp_curve_t *curve;
768      stp_parameter_t desc;
769      const char *xkey = key + 4;
770      stp_param_string_t *pstr = stp_string_list_find(option_remap_list, key);
771      if (pstr)
772	{
773	  xkey = pstr->text;
774	  STP_DEBUG(fprintf(stderr, "ijsgutenprint: remapping %s to %s\n",
775			    key, xkey));
776	}
777
778      stp_describe_parameter(img->v, xkey, &desc);
779      switch (desc.p_type)
780	{
781	case STP_PARAMETER_TYPE_STRING_LIST:
782	  stp_set_string_parameter(img->v, xkey, vbuf);
783	  break;
784	case STP_PARAMETER_TYPE_FILE:
785	  stp_set_file_parameter(img->v, xkey, vbuf);
786	  break;
787	case STP_PARAMETER_TYPE_CURVE:
788	  curve = stp_curve_create_from_string(vbuf);
789	  if (curve)
790	    {
791	      stp_set_curve_parameter(img->v, xkey, curve);
792	      stp_curve_destroy(curve);
793	    }
794	  else
795	    fprintf(stderr, "ERROR: ijsgutenprint: cannot parse curve %s\n", vbuf);
796	  break;
797	case STP_PARAMETER_TYPE_DOUBLE:
798	  code = get_float(vbuf, xkey, &z);
799	  if (code == 0)
800	    stp_set_float_parameter(img->v, xkey, z);
801	  else
802	    fprintf(stderr, "ERROR: ijsgutenprint: cannot parse %s float %s\n", xkey, vbuf);
803	  break;
804	case STP_PARAMETER_TYPE_INT:
805	  code = get_int(vbuf, xkey, &i);
806	  if (code == 0)
807	    stp_set_int_parameter(img->v, xkey, i);
808	  else
809	    fprintf(stderr, "ERROR: ijsgutenprint: cannot parse %s int %s\n", xkey, vbuf);
810	  break;
811	case STP_PARAMETER_TYPE_DIMENSION:
812	  code = get_int(vbuf, xkey, &i);
813	  if (code == 0)
814	    stp_set_dimension_parameter(img->v, xkey, i);
815	  else
816	    fprintf(stderr, "ERROR: ijsgutenprint: cannot parse %s dimension %s\n", xkey, vbuf);
817	  break;
818	case STP_PARAMETER_TYPE_BOOLEAN:
819	  if (strcmp(vbuf, "False") == 0 ||
820	      strcmp(vbuf, "false") == 0 ||
821	      strcmp(vbuf, "FALSE") == 0 ||
822	      strcmp(vbuf, "0") == 0)
823	    stp_set_boolean_parameter(img->v, xkey, 0);
824	  else if (strcmp(vbuf, "True") == 0 ||
825		   strcmp(vbuf, "true") == 0 ||
826		   strcmp(vbuf, "TRUE") == 0 ||
827		   strcmp(vbuf, "1") == 0)
828	    stp_set_boolean_parameter(img->v, xkey, 1);
829	  else
830	    fprintf(stderr, "ERROR: ijsgutenprint: cannot parse %s boolean %s\n", xkey, vbuf);
831	  break;
832	default:
833	  if (strncmp(xkey, "Enable", strlen("Enable")) == 0)
834	    {
835	      STP_DEBUG(fprintf(stderr,
836				"ijsgutenprint: Setting dummy enable parameter %s %s\n",
837				xkey, vbuf));
838	      stp_set_string_parameter(img->v, xkey, vbuf);
839	    }
840	  else
841	    fprintf(stderr, "ERROR: ijsgutenprint: Bad parameter %s %d\n", key, desc.p_type);
842	}
843      stp_parameter_description_destroy(&desc);
844    }
845
846  if (code == 0)
847    {
848      GutenprintParamList *pl = gutenprint_find_key (img->params, key);
849
850      if (pl == NULL)
851	{
852	  pl = (GutenprintParamList *)stp_malloc (sizeof (GutenprintParamList));
853	  pl->next = img->params;
854	  pl->key = stp_malloc (strlen(key) + 1);
855	  memcpy (pl->key, key, strlen(key) + 1);
856	  img->params = pl;
857	}
858      else
859	{
860	  stp_free (pl->value);
861	}
862      pl->value = stp_malloc (value_size);
863      memcpy (pl->value, value, value_size);
864      pl->value_size = value_size;
865    }
866  else
867    fprintf(stderr, "ERROR: ijsgutenprint: bad key code %d\n", code);
868
869  return code;
870}
871
872/**********************************************************/
873
874static void
875gutenprint_errfunc(void *file, const char *buf, size_t bytes)
876{
877  size_t next_nl = 0;
878  size_t where = 0;
879  FILE *prn = (FILE *)file;
880  while (where < bytes)
881    {
882      if (print_messages_as_errors)
883	fputs("ERROR: Gutenprint: ", prn);
884      else
885	fputs("DEBUG: Gutenprint internal: ", prn);
886      while (next_nl < bytes)
887	{
888	  if (buf[next_nl++] == '\n')
889	    break;
890	}
891      fwrite(buf + where, 1, next_nl - where, prn);
892      where = next_nl;
893    }
894}
895
896static void
897gutenprint_outfunc(void *data, const char *buffer, size_t bytes)
898{
899  page_bytes_printed += bytes;
900  total_bytes_printed += bytes;
901  if ((data != NULL) && (buffer != NULL) && (bytes != 0))
902    fwrite(buffer, 1, bytes, (FILE *)data);
903}
904
905/**********************************************************/
906/* stp_image_t functions */
907
908static int
909gutenprint_image_width(stp_image_t *image)
910{
911  IMAGE *img = (IMAGE *)(image->rep);
912  STP_DEBUG(fprintf(stderr, "ijsgutenprint: image width %d\n", img->width));
913  return img->width;
914}
915
916static int
917gutenprint_image_height(stp_image_t *image)
918{
919  IMAGE *img = (IMAGE *)(image->rep);
920  STP_DEBUG(fprintf(stderr, "ijsgutenprint: image height %d (%d)\n",
921		    img->height, img->height * img->xres / img->yres));
922  return img->height * img->xres / img->yres;
923}
924
925static void
926throwaway_data(int amount, IMAGE *img)
927{
928  char trash[4096];	/* Throwaway */
929  int block_count = amount / 4096;
930  int leftover = amount % 4096;
931  while (block_count > 0)
932    {
933      ijs_server_get_data(img->ctx, trash, 4096);
934      block_count--;
935    }
936  if (leftover)
937    ijs_server_get_data(img->ctx, trash, leftover);
938}
939
940static int
941image_next_row(IMAGE *img)
942{
943  int status = 0;
944  double n_bytes = img->bytes_left;
945  if (img->bytes_left)
946    {
947
948      if (n_bytes > img->row_width)
949	n_bytes = img->row_width;
950#ifdef VERBOSE
951      STP_DEBUG(fprintf(stderr, "ijsgutenprint: %.0f bytes left, reading %.d, on row %d\n",
952			img->bytes_left, (int) n_bytes, img->row));
953#endif
954      throwaway_data(img->left_margin, img);
955      status = ijs_server_get_data(img->ctx, img->row_buf, (int) n_bytes);
956      if (status)
957	{
958	  STP_DEBUG(fprintf(stderr, "ERROR: ijsgutenprint: page aborted (%d) at line %d!\n",
959			    status, img->row));
960	  job_aborted = 1;
961	  return status;
962	}
963      else
964	{
965	  img->row++;
966	  img->bytes_left -= (n_bytes + img->right_margin + img->left_margin);
967	}
968      throwaway_data(img->right_margin, img);
969    }
970  else
971    return 1;	/* Done */
972  return status;
973}
974
975static stp_image_status_t
976gutenprint_image_get_row(stp_image_t *image, unsigned char *data, size_t byte_limit,
977		   int row)
978{
979  IMAGE *img = (IMAGE *)(image->rep);
980  int physical_row = row * img->yres / img->xres;
981
982  if ((physical_row < 0) || (physical_row >= img->height))
983    return STP_IMAGE_STATUS_ABORT;
984
985  /* Read until we reach the requested row. */
986  while (physical_row > img->row)
987    {
988      if (image_next_row(img))
989	return STP_IMAGE_STATUS_ABORT;
990    }
991
992  if (physical_row == img->row)
993    {
994      unsigned i, j, length;
995      switch (img->bps)
996	{
997	case 16:
998	case 8:
999	  memcpy(data, img->row_buf, img->row_width);
1000	  break;
1001	case 1:
1002	  length = img->width / 8;
1003	  for (i = 0; i < length; i++)
1004	    for (j = 128; j > 0; j >>= 1)
1005	      {
1006		if (img->row_buf[i] & j)
1007		  data[0] = 255;
1008		else
1009		  data[0] = 0;
1010		data++;
1011	      }
1012	  length = img->width % 8;
1013	  for (j = 128; j > 1 << (7 - length); j >>= 1)
1014	    {
1015	      if (img->row_buf[i] & j)
1016		data[0] = 255;
1017	      else
1018		data[0] = 0;
1019	      data++;
1020	    }
1021	  break;
1022	default:
1023	  return STP_IMAGE_STATUS_ABORT;
1024	}
1025    }
1026  else
1027    return STP_IMAGE_STATUS_ABORT;
1028  return STP_IMAGE_STATUS_OK;
1029}
1030
1031
1032static const char *
1033gutenprint_image_get_appname(stp_image_t *image)
1034{
1035  return "ijsgutenprint";
1036}
1037
1038/**********************************************************/
1039
1040static const char *
1041safe_get_string_parameter(const stp_vars_t *v, const char *param)
1042{
1043  const char *val = stp_get_string_parameter(v, param);
1044  if (val)
1045    return val;
1046  else
1047    return "NULL";
1048}
1049
1050static void
1051stp_dbg(const char *msg, const stp_vars_t *v)
1052{
1053  stp_parameter_list_t params = stp_get_parameter_list(v);
1054  int count = stp_parameter_list_count(params);
1055  int i;
1056  if (suppress_messages)
1057    return;
1058  fprintf(stderr, "DEBUG: %s\n", msg);
1059  fprintf(stderr, "DEBUG: ijsgutenprint: Settings: Model %s\n", stp_get_driver(v));
1060  for (i = 0; i < count; i++)
1061    {
1062      const stp_parameter_t *p = stp_parameter_list_param(params, i);
1063      switch (p->p_type)
1064	{
1065	case STP_PARAMETER_TYPE_DOUBLE:
1066	  if (stp_check_float_parameter(v, p->name, STP_PARAMETER_DEFAULTED))
1067	    fprintf(stderr, "DEBUG: ijsgutenprint: Settings: %s %f\n",
1068		    p->name, stp_get_float_parameter(v, p->name));
1069	  break;
1070	case STP_PARAMETER_TYPE_INT:
1071	  if (stp_check_int_parameter(v, p->name, STP_PARAMETER_DEFAULTED))
1072	    fprintf(stderr, "DEBUG: ijsgutenprint: Settings: %s %d\n",
1073		    p->name, stp_get_int_parameter(v, p->name));
1074	  break;
1075	case STP_PARAMETER_TYPE_DIMENSION:
1076	  if (stp_check_dimension_parameter(v, p->name, STP_PARAMETER_DEFAULTED))
1077	    fprintf(stderr, "DEBUG: ijsgutenprint: Settings: %s %d\n",
1078		    p->name, stp_get_dimension_parameter(v, p->name));
1079	  break;
1080	case STP_PARAMETER_TYPE_BOOLEAN:
1081	  if (stp_check_boolean_parameter(v, p->name, STP_PARAMETER_DEFAULTED))
1082	    fprintf(stderr, "DEBUG: ijsgutenprint: Settings: %s %s\n",
1083		    p->name,
1084		    stp_get_boolean_parameter(v, p->name) ? "true" : "false");
1085	  break;
1086	case STP_PARAMETER_TYPE_STRING_LIST:
1087	  if (stp_check_string_parameter(v, p->name, STP_PARAMETER_DEFAULTED))
1088	    fprintf(stderr, "DEBUG: ijsgutenprint: Settings: %s %s\n",
1089		    p->name, safe_get_string_parameter(v, p->name));
1090	  break;
1091	case STP_PARAMETER_TYPE_CURVE:
1092	  if (stp_check_curve_parameter(v, p->name, STP_PARAMETER_DEFAULTED))
1093	    {
1094	      char *curve =
1095		stp_curve_write_string(stp_get_curve_parameter(v, p->name));
1096	      fprintf(stderr, "DEBUG: ijsgutenprint: Settings: %s %s\n",
1097		      p->name, curve);
1098	      stp_free(curve);
1099	    }
1100	  break;
1101	default:
1102	  break;
1103	}
1104    }
1105  stp_parameter_list_destroy(params);
1106}
1107
1108static void
1109purge_unused_float_parameters(stp_vars_t *v)
1110{
1111  int i;
1112  stp_parameter_list_t params = stp_get_parameter_list(v);
1113  size_t count = stp_parameter_list_count(params);
1114  STP_DEBUG(fprintf(stderr, "ijsgutenprint: Purging unused floating point parameters\n"));
1115  for (i = 0; i < count; i++)
1116    {
1117      const stp_parameter_t *param = stp_parameter_list_param(params, i);
1118      if (param->p_type == STP_PARAMETER_TYPE_DOUBLE &&
1119	  !param->read_only && param->is_active && !param->is_mandatory)
1120	{
1121	  size_t bytes = strlen(param->name) + strlen("Enable") + 1;
1122	  char *tmp = stp_malloc(bytes);
1123	  const char *value;
1124	  sprintf(tmp, "Enable%s", param->name);
1125	  STP_DEBUG(fprintf(stderr, "ijsgutenprint:   Looking for parameter %s\n", tmp));
1126	  value = stp_get_string_parameter(v, tmp);
1127	  if (value)
1128	    {
1129	      STP_DEBUG(fprintf(stderr, "ijsgutenprint:   Found %s: %s\n", tmp, value));
1130	      if (strcmp(value, "Disabled") == 0)
1131		{
1132		  STP_DEBUG(fprintf(stderr, "ijsgutenprint:     Clearing %s\n", param->name));
1133		  stp_clear_float_parameter(v, param->name);
1134		}
1135	    }
1136	  stp_free(tmp);
1137	}
1138      if (param->p_type == STP_PARAMETER_TYPE_DIMENSION &&
1139	  !param->read_only && param->is_active && !param->is_mandatory)
1140	{
1141	  size_t bytes = strlen(param->name) + strlen("Enable") + 1;
1142	  char *tmp = stp_malloc(bytes);
1143	  const char *value;
1144	  sprintf(tmp, "Enable%s", param->name);
1145	  STP_DEBUG(fprintf(stderr, "ijsgutenprint:   Looking for parameter %s\n", tmp));
1146	  value = stp_get_string_parameter(v, tmp);
1147	  if (value)
1148	    {
1149	      STP_DEBUG(fprintf(stderr, "ijsgutenprint:     Found %s: %s\n", tmp, value));
1150	      if (strcmp(value, "Disabled") == 0)
1151		{
1152		  STP_DEBUG(fprintf(stderr, "ijsgutenprint:     Clearing %s\n", param->name));
1153		  stp_clear_dimension_parameter(v, param->name);
1154		}
1155	    }
1156	  stp_free(tmp);
1157	}
1158    }
1159  stp_parameter_list_destroy(params);
1160}
1161
1162static void
1163validate_options(stp_image_t *image)
1164{
1165  IMAGE *im = (IMAGE *) (image->rep);
1166  stp_vars_t *v = im->v;
1167  stp_parameter_list_t params = stp_get_parameter_list(v);
1168  int nparams = stp_parameter_list_count(params);
1169  int i;
1170  for (i = 0; i < nparams; i++)
1171    {
1172      const stp_parameter_t *param = stp_parameter_list_param(params, i);
1173      stp_parameter_t desc;
1174      stp_describe_parameter(v, param->name, &desc);
1175      if (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST)
1176	{
1177	  if (!stp_string_list_is_present
1178	      (desc.bounds.str, stp_get_string_parameter(v, desc.name)))
1179	    {
1180	      STP_DEBUG(fprintf(stderr, "ijsgutenprint: clearing string %s (%s)\n",
1181				desc.name, safe_get_string_parameter(v, desc.name)));
1182	      stp_clear_string_parameter(v, desc.name);
1183	      if (!desc.read_only && desc.is_mandatory && desc.is_active)
1184		{
1185		  STP_DEBUG(fprintf(stderr, "ijsgutenprint: setting default string %s to %s\n",
1186				    desc.name, desc.deflt.str ? desc.deflt.str : "(null)"));
1187		  stp_set_string_parameter(v, desc.name, desc.deflt.str);
1188		  if (strcmp(desc.name, "PageSize") == 0)
1189		    {
1190		      const stp_papersize_t *ps =
1191			stp_get_papersize_by_name(desc.deflt.str);
1192		      if (ps->width > 0)
1193			{
1194			  STP_DEBUG(fprintf(stderr, "ijsgutenprint: setting page width to %d\n",
1195					    ps->width));
1196			  if (ps->width < stp_get_page_width(v))
1197			    stp_set_page_width(v, ps->width);
1198			  if (ps->width < stp_get_left(v) + stp_get_width(v))
1199			    {
1200#if 0
1201			      if (im->width < ps->width)
1202				im->width = ps->width;
1203#endif
1204			      STP_DEBUG(fprintf(stderr, "ijsgutenprint: setting width to %d\n",
1205						ps->width - stp_get_left(v)));
1206			      stp_set_width(v, ps->width - stp_get_left(v));
1207			    }
1208			}
1209		      if (ps->height > 0)
1210			{
1211			  STP_DEBUG(fprintf(stderr, "ijsgutenprint: setting page height to %d\n",
1212					    ps->height));
1213			  if (ps->height < stp_get_page_height(v))
1214			    stp_set_page_height(v, ps->height);
1215			  if (ps->height < stp_get_top(v) + stp_get_height(v))
1216			    {
1217#if 0
1218			      if (im->height < ps->height)
1219				im->height = ps->height;
1220#endif
1221			      STP_DEBUG(fprintf(stderr, "ijsgutenprint: setting height to %d\n",
1222						ps->height - stp_get_top(v)));
1223			      stp_set_height(v, ps->height - stp_get_top(v));
1224			    }
1225			}
1226		    }
1227		}
1228	    }
1229	}
1230      stp_parameter_description_destroy(&desc);
1231    }
1232  stp_parameter_list_destroy(params);
1233}
1234
1235
1236
1237int
1238main (int argc, char **argv)
1239{
1240  IjsPageHeader ph;
1241  int status;
1242  int page = 0;
1243  IMAGE img;
1244  stp_image_t si;
1245  const stp_printer_t *printer = NULL;
1246  FILE *f = NULL;
1247  int l, t, r, b, w, h;
1248  int width, height;
1249
1250  if (getenv("STP_SUPPRESS_MESSAGES"))
1251    suppress_messages = 1;
1252
1253  if (getenv("STP_DEBUG_STARTUP"))
1254    while (SDEBUG)
1255      ;
1256
1257  memset(&img, 0, sizeof(img));
1258
1259  stp_init();
1260  version_id = stp_get_version();
1261  option_remap_list = stp_string_list_create();
1262
1263  img.ctx = ijs_server_init();
1264  if (img.ctx == NULL)
1265    return 1;
1266
1267  img.v = stp_vars_create();
1268  if (img.v == NULL)
1269    {
1270      ijs_server_done(img.ctx);
1271      return 1;
1272    }
1273  stp_set_top(img.v, 0);
1274  stp_set_left(img.v, 0);
1275
1276  /* Error messages to stderr. */
1277  stp_set_errfunc(img.v, gutenprint_errfunc);
1278  stp_set_errdata(img.v, stderr);
1279
1280  /* Printer data goes to file f, but we haven't opened it yet. */
1281  stp_set_outfunc(img.v, gutenprint_outfunc);
1282  stp_set_outdata(img.v, NULL);
1283
1284  memset(&si, 0, sizeof(si));
1285  si.width = gutenprint_image_width;
1286  si.height = gutenprint_image_height;
1287  si.get_row = gutenprint_image_get_row;
1288  si.get_appname = gutenprint_image_get_appname;
1289  si.rep = &img;
1290
1291  ijs_server_install_status_cb (img.ctx, gutenprint_status_cb, &img);
1292  ijs_server_install_list_cb (img.ctx, gutenprint_list_cb, &img);
1293  ijs_server_install_enum_cb (img.ctx, gutenprint_enum_cb, &img);
1294  ijs_server_install_get_cb (img.ctx, gutenprint_get_cb, &img);
1295  ijs_server_install_set_cb(img.ctx, gutenprint_set_cb, &img);
1296
1297  stp_dbg("ijsgutenprint: about to start\n", img.v);
1298
1299  STP_DEBUG(fprintf(stderr, "ijsgutenprint: About to get page header\n"));
1300  status = ijs_server_get_page_header(img.ctx, &ph);
1301  while (status == 0)
1302    {
1303      stp_vars_t *old_v = NULL;
1304      STP_DEBUG(fprintf(stderr, "ijsgutenprint: got page header, %d x %d\n",
1305			ph.width, ph.height));
1306      stp_dbg("ijsgutenprint: have page header\n", img.v);
1307
1308      status = image_init(&img, &ph);
1309      if (status)
1310	{
1311	  fprintf(stderr, "ERROR: ijsgutenprint: image_init failed %d\n", status);
1312	  break;
1313	}
1314
1315      if (page == 0)
1316	{
1317	  if (img.fd)
1318	    {
1319	      f = fdopen(img.fd - 1, "wb");
1320	      if (!f)
1321		{
1322		  fprintf(stderr, "ERROR: ijsgutenprint: Unable to open file descriptor: %s\n",
1323			  strerror(errno));
1324		  status = -1;
1325		  break;
1326		}
1327	    }
1328	  else if (img.filename && strlen(img.filename) > 0)
1329	    {
1330	      f = fopen(img.filename, "wb");
1331	      if (!f)
1332		{
1333		  status = -1;
1334		  fprintf(stderr, "ERROR: ijsgutenprint: Unable to open %s: %s\n", img.filename,
1335			  strerror(errno));
1336		  break;
1337		}
1338	    }
1339
1340	  /* Printer data to file */
1341	  stp_set_outdata(img.v, f);
1342	}
1343
1344      printer = stp_get_printer(img.v);
1345      if (printer == NULL)
1346	{
1347	  fprintf(stderr, "ERROR: ijsgutenprint: Unknown printer %s\n",
1348		  stp_get_driver(img.v));
1349	  status = -1;
1350	  break;
1351	}
1352      purge_unused_float_parameters(img.v);
1353      stp_merge_printvars(img.v, stp_printer_get_defaults(printer));
1354
1355
1356      img.total_bytes = (double) ((ph.n_chan * ph.bps * ph.width + 7) >> 3)
1357	* (double) ph.height;
1358      img.bytes_left = img.total_bytes;
1359
1360      stp_set_float_parameter(img.v, "AppGamma", 1.0);
1361      stp_get_media_size(img.v, &w, &h);
1362      stp_get_imageable_area(img.v, &l, &r, &b, &t);
1363      STP_DEBUG(fprintf(stderr, "ijsgutenprint: chan %d bps %d image w %d %d h %d %d\n",
1364			ph.n_chan, ph.bps, stp_get_width(img.v), img.width,
1365			stp_get_height(img.v), img.height));
1366      if (ppd_mode)
1367	{
1368	  int lt, rt, bt, tt;
1369
1370	  stp_get_maximum_imageable_area(img.v, &lt, &rt, &bt, &tt);
1371	  STP_DEBUG(fprintf(stderr, "ijsgutenprint: w %d h %d l %d %d t %d %d r %d %d b %d %d\n",
1372			    w, h, l, lt, t, tt, r, rt, b, bt));
1373	  if (lt < 0)
1374	    lt = 0;
1375	  if (tt < 0)
1376	    tt = 0;
1377	  if (rt > w)
1378	    rt = w;
1379	  if (bt > h)
1380	    bt = h;
1381	  if (l < 0)
1382	    l = 0;
1383	  if (t < 0)
1384	    t = 0;
1385	  if (r > w + l)
1386	    r = w + l;
1387	  if (b > h + t)
1388	    b = h + t;
1389	  STP_DEBUG(fprintf(stderr, "ijsgutenprint: w %d h %d l %d %d t %d %d r %d %d b %d %d\n",
1390			    w, h, l, lt, t, tt, r, rt, b, bt));
1391	  if (lt < l)
1392	    {
1393	      STP_DEBUG(fprintf(stderr, "ijsgutenprint: l %d, lt %d\n", l, lt));
1394	      img.left_margin = (l - lt) * ph.xres * ph.n_chan * ph.bps / 8 / 72;
1395	      img.width -= (l - lt) * ph.xres / 72;
1396	      STP_DEBUG(fprintf(stderr, "ijsgutenprint: chan %d bps %d image w %d %d h %d %d\n",
1397				ph.n_chan, ph.bps, stp_get_width(img.v), img.width,
1398				stp_get_height(img.v), img.height));
1399	    }
1400	  else
1401	    img.left_margin = 0;
1402	  stp_set_left(img.v, l);
1403	  if (tt < t)
1404	    {
1405	      STP_DEBUG(fprintf(stderr, "ijsgutenprint: t %d, tt %d\n", t, tt));
1406	      img.top_margin = (t - tt) * ph.yres * ph.n_chan * ph.bps / 8 / 72;
1407	      img.height -= (t - tt) * ph.yres / 72;
1408	      STP_DEBUG(fprintf(stderr, "ijsgutenprint: chan %d bps %d image w %d %d h %d %d\n",
1409				ph.n_chan, ph.bps, stp_get_width(img.v), img.width,
1410				stp_get_height(img.v), img.height));
1411	    }
1412	  else
1413	    img.top_margin = 0;
1414	  stp_set_top(img.v, t);
1415	  if (rt > r)
1416	    {
1417	      STP_DEBUG(fprintf(stderr, "ijsgutenprint: r %d, rt %d\n", r, rt));
1418	      img.right_margin = (rt - r) * ph.xres * ph.n_chan * ph.bps / 8 / 72;
1419	      img.width -= (rt - r) * ph.xres / 72;
1420	      STP_DEBUG(fprintf(stderr, "ijsgutenprint: chan %d bps %d image w %d %d h %d %d\n",
1421				ph.n_chan, ph.bps, stp_get_width(img.v), img.width,
1422				stp_get_height(img.v), img.height));
1423	    }
1424	  else
1425	    img.right_margin = 0;
1426	  if (bt > b)
1427	    {
1428	      STP_DEBUG(fprintf(stderr, "ijsgutenprint: b %d, bt %d\n", b, bt));
1429	      img.bottom_margin = (bt - b) * ph.yres * ph.n_chan * ph.bps / 8 / 72;
1430	      img.height -= (bt - b) * ph.yres / 72;
1431	      STP_DEBUG(fprintf(stderr, "ijsgutenprint: chan %d bps %d image w %d %d h %d %d\n",
1432				ph.n_chan, ph.bps, stp_get_width(img.v), img.width,
1433				stp_get_height(img.v), img.height));
1434	    }
1435	  else
1436	    img.bottom_margin = 0;
1437	}
1438      if (l < 0)
1439	width = r;
1440      else
1441	width = r - l;
1442      stp_set_width(img.v, width);
1443      if (t < 0)
1444	height = b;
1445      else
1446	height = b - t;
1447      img.row_width -= img.left_margin;
1448      img.row_width -= img.right_margin;
1449      stp_set_height(img.v, height);
1450      stp_set_int_parameter(img.v, "PageNumber", page);
1451      STP_DEBUG(fprintf(stderr, "ijsgutenprint: w %d h %d l %d r %d t %d b %d\n",
1452			width, height, l, r, t, b));
1453      STP_DEBUG(fprintf(stderr, "ijsgutenprint: chan %d bps %d image w %d %d h %d %d\n",
1454			ph.n_chan, ph.bps, stp_get_width(img.v), img.width,
1455			stp_get_height(img.v), img.height));
1456      STP_DEBUG(fprintf(stderr, "ijsgutenprint: margins l %d r %d t %d b %d row width %d\n",
1457			img.left_margin, img.right_margin,
1458			img.top_margin, img.bottom_margin,
1459			img.row_width));
1460
1461/*
1462 * Fix up the duplex/tumble settings stored in the "x_" parameters
1463 * If Duplex is "true" then look at "Tumble". If duplex is not "true" or "false"
1464 * then just take it (e.g. Duplex=DuplexNoTumble).
1465 */
1466      STP_DEBUG(fprintf(stderr, "ijsgutenprint: x_Duplex=%s\n", safe_get_string_parameter(img.v, "x_Duplex")));
1467      STP_DEBUG(fprintf(stderr, "ijsgutenprint: x_Tumble=%s\n", safe_get_string_parameter(img.v, "x_Tumble")));
1468
1469      if (stp_get_string_parameter(img.v, "x_Duplex"))
1470        {
1471          if (strcmp(stp_get_string_parameter(img.v, "x_Duplex"), "false") == 0)
1472            stp_set_string_parameter(img.v, "Duplex", "None");
1473          else if (strcmp(stp_get_string_parameter(img.v, "x_Duplex"), "true") == 0)
1474            {
1475              if (stp_get_string_parameter(img.v, "x_Tumble"))
1476              {
1477                if (strcmp(stp_get_string_parameter(img.v, "x_Tumble"), "false") == 0)
1478                  stp_set_string_parameter(img.v, "Duplex", "DuplexNoTumble");
1479                else
1480                  stp_set_string_parameter(img.v, "Duplex", "DuplexTumble");
1481              }
1482            else	/* Tumble missing, assume false */
1483              stp_set_string_parameter(img.v, "Duplex", "DuplexNoTumble");
1484            }
1485          else	/* Not true or false */
1486            stp_set_string_parameter(img.v, "Duplex", stp_get_string_parameter(img.v, "x_Duplex"));
1487        }
1488
1489/* can I destroy the unused parameters? */
1490
1491      STP_DEBUG(fprintf(stderr, "ijsgutenprint: Duplex=%s\n", safe_get_string_parameter(img.v, "Duplex")));
1492
1493      validate_options(&si);
1494      stp_dbg("ijsgutenprint: about to print", img.v);
1495      STP_DEBUG(fprintf(stderr, "ijsgutenprint: w %d h %d l %d t %d\n",
1496			stp_get_width(img.v), stp_get_height(img.v),
1497			stp_get_left(img.v), stp_get_top(img.v)));
1498      STP_DEBUG(fprintf(stderr, "ijsgutenprint: start printing page %d\n", page));
1499      print_messages_as_errors = 1;
1500      if (!version_is_ok)
1501	{
1502	  fprintf(stderr, VERSION_MISMATCH, version_id,
1503		  gutenprint_ppd_version, version_id, gutenprint_ppd_version);
1504	  status = IJS_ERANGE;
1505	  break;
1506	}
1507      else if (stp_verify(img.v))
1508	{
1509	  page_bytes_printed = 0;
1510	  if (page == 0)
1511	    stp_start_job(img.v, &si);
1512	  stp_print(img.v, &si);
1513	  STP_DEBUG(fprintf(stderr, "ijsgutenprint: printed page %d, %.0f bytes\n",
1514			    page, page_bytes_printed));
1515	  old_v = stp_vars_create_copy(img.v);
1516	}
1517      else
1518	{
1519	  fprintf(stderr, "ERROR: ijsgutenprint: Bad parameters; cannot continue!\n");
1520	  status = IJS_ERANGE;
1521	  break;
1522	}
1523      if (job_aborted)
1524	{
1525	  STP_DEBUG(fprintf(stderr, "ijsgutenprint: aborting job\n"));
1526	  status = 1;
1527	}
1528      else
1529	{
1530	  STP_DEBUG(fprintf(stderr, "ijsgutenprint: done printing page %d\n", page));
1531
1532	  while (img.bytes_left)
1533	    {
1534	      status = image_next_row(&img);
1535	      if (status)
1536		{
1537		  fprintf(stderr, "ERROR: ijsgutenprint: Get next row failed at %.0f\n",
1538			  img.bytes_left);
1539		  break;
1540		}
1541	    }
1542
1543	  image_finish(&img);
1544	  status = ijs_server_get_page_header(img.ctx, &ph);
1545	}
1546      if (status > 0)
1547	{
1548	  fprintf(stderr, "INFO: ijsgutenprint Ready to print.\n");
1549	  stp_end_job(old_v, &si);
1550	}
1551      else
1552	{
1553	  stp_vars_destroy(old_v);
1554	  page++;
1555	}
1556    }
1557  if (f)
1558    {
1559      fclose(f);
1560    }
1561
1562  if (status > 0)
1563    status = 0; /* normal exit */
1564
1565  ijs_server_done(img.ctx);
1566
1567  STP_DEBUG(fprintf (stderr, "ijsgutenprint: printed total %.0f bytes\n",
1568		     total_bytes_printed));
1569  STP_DEBUG(fprintf (stderr, "ijsgutenprint: server exiting with status %d\n", status));
1570  return status;
1571}
1572