/*
 *   Function to apply IPP options to a CUPS/PWG Raster header.
 *
 *   Copyright 2013 by Till Kamppeter.
 *
 *   Distribution and use rights are outlined in the file "COPYING"
 *   which should have been included with this file.
 *
 * Contents:
 *
 *   cupsRasterParseIPPOptions() - Parse IPP options from the command line
 *                                 and apply them to the CUPS Raster header.
 */

#include <config.h>
#include <cups/cups.h>
#include <cups/ppd.h>
#if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 6)
#define HAVE_CUPS_1_7 1
#endif

/*
 * Include necessary headers.
 */

#include "driver.h"
#include <string.h>
#include <ctype.h>
#ifdef HAVE_CUPS_1_7
#include <cups/pwg.h>
#endif /* HAVE_CUPS_1_7 */

/*
 * '_strlcpy()' - Safely copy two strings.
 */

size_t					/* O - Length of string */
_strlcpy(char       *dst,		/* O - Destination string */
	 const char *src,		/* I - Source string */
	 size_t      size)		/* I - Size of destination string buffer */
{
  size_t	srclen;			/* Length of source string */


 /*
  * Figure out how much room is needed...
  */

  size --;

  srclen = strlen(src);

 /*
  * Copy the appropriate amount...
  */

  if (srclen > size)
    srclen = size;

  memcpy(dst, src, srclen);
  dst[srclen] = '\0';

  return (srclen);
}

/*
 * 'cupsRasterParseIPPOptions()' - Parse IPP options from the command line
 *                                 and apply them to the CUPS Raster header.
 */

int                                          /* O - -1 on error, 0 on success */
cupsRasterParseIPPOptions(cups_page_header2_t *h, /* I - Raster header */
			  int num_options,        /* I - Number of options */
			  cups_option_t *options, /* I - Options */
			  int pwg_raster,         /* I - 1 if PWG Raster */
			  int set_defaults)       /* I - If 1, se default values
						     for all fields for which
						     we did not get an option */
{
#ifdef HAVE_CUPS_1_7
  int		i;			/* Looping var */
  char		*ptr,			/* Pointer into string */
		s[255];			/* Temporary string */
  const char	*val,			/* Pointer into value */
                *media;			/* media option */
  char		*page_size,		/* PageSize option */
                *media_source,          /* Media source */
                *media_type;		/* Media type */
  pwg_media_t   *size_found;            /* page size found for given name */
  float         size;                   /* page size dimension */
  int           num_non_ppd_options = 0;/* Number of options which are not
                                           in the PPD */
  cups_option_t *non_ppd_options = NULL;/* Options not in the PPD */
  ppd_file_t    *ppd = NULL;

 /*
  * Range check input...
  */

  if (!h)
    return (-1);

 /*
  * If we have a PPD file (PPD environment variable), take it into
  * account, by not parsing options which are in the PPD file here.
  *
  * They should get parsed and applied separately via the
  * ppdRasterInterpretPPD() as that function parses the embedded
  * PostScript code. This way weird things like Gutenprint's
  * "Resolution" option (choice name is something odd, like
  * 301x300dpi, and actual resolution can be completely different)
  * will get treated correctly.
  *
  * We mark the option settings in the PPD and call
  * ppdRasterInterpretPPD() only when we are called with set_defaults
  * = 1. In any case we replace the option list we parse by a copy
  * without the options of the PPD file.
  */

  ptr = getenv("PPD");
  if (ptr && ptr[0] != '\0' && (ppd = ppdOpenFile(ptr)) != NULL)
  {
    if (set_defaults)
    {
      cupsMarkOptions(ppd, num_options, options);
      cupsRasterInterpretPPD(h, ppd, num_options, options, NULL);
    }
    for (i = 0; i < num_options; i ++)
      if (ppdFindOption(ppd, options[i].name) == NULL)
	num_non_ppd_options =
	  cupsAddOption(options[i].name, options[i].value,
			num_non_ppd_options, &non_ppd_options);
    num_options = num_non_ppd_options;
    options = non_ppd_options;
  }

 /*
  * Check if the supplied "media" option is a comma-separated list of any
  * combination of page size ("media"), media source ("media-position"),
  * and media type ("media-type") and if so, put these list elements into
  * their dedicated options.
  */

  page_size = NULL;
  media_source = NULL;
  media_type = NULL;
  if ((media = cupsGetOption("media", num_options, options)) != NULL)
  {
   /*
    * Loop through the option string, separating it at commas and marking each
    * individual option as long as the corresponding PPD option (PageSize,
    * InputSlot, etc.) is not also set.
    *
    * For PageSize, we also check for an empty option value since some versions
    * of MacOS X use it to specify auto-selection of the media based solely on
    * the size.
    */

    for (val = media; *val;)
    {
     /*
      * Extract the sub-option from the string...
      */

      for (ptr = s; *val && *val != ',' && (ptr - s) < (sizeof(s) - 1);)
	*ptr++ = *val++;
      *ptr++ = '\0';

      if (*val == ',')
	val ++;

     /*
      * Identify it...
      */

      size_found = NULL;
      if ((size_found = pwgMediaForPWG(s)) == NULL)
	if ((size_found = pwgMediaForPPD(s)) == NULL)
	  if ((size_found = pwgMediaForPPD(s)) == NULL)
	  {
	    if (strcasestr(s, "tray") ||
		strcasestr(s, "feed") ||
		strcasestr(s, "capacity") ||
		strcasestr(s, "upper") ||
		strcasestr(s, "top") ||
		strcasestr(s, "middle") ||
		strcasestr(s, "lower") ||
		strcasestr(s, "bottom") ||
		strcasestr(s, "left") ||
		strcasestr(s, "right") ||
		strcasestr(s, "side") ||
		strcasestr(s, "main"))
            { 
              if (media_source == NULL)
	        media_source = strdup(s);
            }
	    else
	      media_type = strdup(s);
	  }
      if (page_size == NULL && size_found)
	page_size = strdup(size_found->pwg);
    }
  }

  if (pwg_raster)
    strcpy(h->MediaClass, "PwgRaster");
  else if ((val = cupsGetOption("media-class", num_options, options)) != NULL ||
	   (val = cupsGetOption("MediaClass", num_options, options)) != NULL)
    _strlcpy(h->MediaClass, val, sizeof(h->MediaClass));
  else if (set_defaults)
    strcpy(h->MediaClass, "");

  if ((val = cupsGetOption("media-color", num_options, options)) != NULL ||
      (val = cupsGetOption("MediaColor", num_options, options)) != NULL)
    _strlcpy(h->MediaColor, val, sizeof(h->MediaColor));
  else if (set_defaults)
    h->MediaColor[0] = '\0';

  if ((val = cupsGetOption("media-type", num_options, options)) != NULL ||
      (val = cupsGetOption("MediaType", num_options, options)) != NULL ||
      (val = media_type) != NULL)
    _strlcpy(h->MediaType, val, sizeof(h->MediaType));
  else if (set_defaults)
    h->MediaType[0] = '\0';

  if ((val = cupsGetOption("print-content-optimize", num_options,
			   options)) != NULL ||
      (val = cupsGetOption("output-type", num_options, options)) != NULL ||
      (val = cupsGetOption("OutputType", num_options, options)) != NULL)
  {
    if (!strcasecmp(val, "automatic"))
      _strlcpy(h->OutputType, "Automatic",
	      sizeof(h->OutputType));
    else if (!strcasecmp(val, "graphics"))
      _strlcpy(h->OutputType, "Graphics", sizeof(h->OutputType));
    else if (!strcasecmp(val, "photo"))
      _strlcpy(h->OutputType, "Photo", sizeof(h->OutputType));
    else if (!strcasecmp(val, "text"))
      _strlcpy(h->OutputType, "Text", sizeof(h->OutputType));
    else if (!strcasecmp(val, "text-and-graphics") ||
	     !strcasecmp(val, "TextAndGraphics"))
      _strlcpy(h->OutputType, "TextAndGraphics",
	      sizeof(h->OutputType));
    else if (pwg_raster)
      fprintf(stderr, "DEBUG: Unsupported print-content-type \"%s\".\n", val);
    else
      _strlcpy(h->OutputType, val, sizeof(h->OutputType));
  }
  else if (set_defaults)
    _strlcpy(h->OutputType, "Automatic", sizeof(h->OutputType));

  if (pwg_raster)
  {
    /* Set "reserved" fields to 0 */
    h->AdvanceDistance = 0;
    h->AdvanceMedia = CUPS_ADVANCE_NONE;
    h->Collate = CUPS_FALSE;
  }
  else
  {
    /* TODO - Support for advance distance and advance media */
    if (set_defaults)
    {
      h->AdvanceDistance = 0;
      h->AdvanceMedia = CUPS_ADVANCE_NONE;
    }
    if ((val = cupsGetOption("Collate", num_options, options)) != NULL &&
	(!strcasecmp(val, "true") || !strcasecmp(val, "on") ||
	 !strcasecmp(val, "yes")))
      h->Collate = CUPS_TRUE;
    else if ((val = cupsGetOption("Collate", num_options, options)) != NULL &&
	     (!strcasecmp(val, "false") || !strcasecmp(val, "off") ||
	      !strcasecmp(val, "no")))
      h->Collate = CUPS_FALSE;
    else if (set_defaults)
      h->Collate = CUPS_FALSE;
  }

  if (set_defaults)
    h->CutMedia = CUPS_CUT_NONE;

  if (set_defaults)
    h->Tumble = CUPS_FALSE;
  if ((val = cupsGetOption("sides", num_options, options)) != NULL ||
      (val = cupsGetOption("Duplex", num_options, options)) != NULL)
  {
    if (!strcasecmp(val, "None") || !strcasecmp(val, "Off") ||
	!strcasecmp(val, "False") || !strcasecmp(val, "No") ||
	!strcasecmp(val, "one-sided") || !strcasecmp(val, "OneSided"))
      h->Duplex = CUPS_FALSE;
    else if (!strcasecmp(val, "On") ||
	     !strcasecmp(val, "True") || !strcasecmp(val, "Yes") ||
	     !strncasecmp(val, "two-sided", 9) ||
	     !strncasecmp(val, "TwoSided", 8) ||
	     !strncasecmp(val, "Duplex", 6))
    {
      h->Duplex = CUPS_TRUE;
      if (!strncasecmp(val, "DuplexTumble", 12))
	h->Tumble = CUPS_TRUE;
      if (!strncasecmp(val, "DuplexNoTumble", 12))
	h->Tumble = CUPS_FALSE;
    }
    else if (set_defaults)
      h->Duplex = CUPS_FALSE;
  }
  else if (set_defaults)
    h->Duplex = CUPS_FALSE;

  if ((val = cupsGetOption("printer-resolution", num_options,
			   options)) != NULL ||
      (val = cupsGetOption("Resolution", num_options, options)) != NULL)
  {
    int	        xres,		/* X resolution */
                yres;		/* Y resolution */
    char	*ptr;		/* Pointer into value */

    xres = yres = strtol(val, (char **)&ptr, 10);
    if (ptr > val && xres > 0)
    {
      if (*ptr == 'x')
	yres = strtol(ptr + 1, (char **)&ptr, 10);
    }

    if (ptr <= val || xres <= 0 || yres <= 0 || !ptr ||
	(*ptr != '\0' &&
	 strcasecmp(ptr, "dpi") &&
	 strcasecmp(ptr, "dpc") &&
	 strcasecmp(ptr, "dpcm")))
    {
      fprintf(stderr, "DEBUG: Bad resolution value \"%s\".\n", val);
      if (set_defaults)
      {
	h->HWResolution[0] = 600;
	h->HWResolution[1] = 600;
      }
    }
    else
    {
      if (!strcasecmp(ptr, "dpc") ||
	  !strcasecmp(ptr, "dpcm"))
      {
	xres = xres * 254 / 100;
	yres = yres * 254 / 100;
      }
      h->HWResolution[0] = xres;
      h->HWResolution[1] = yres;
    }
  }
  else if (set_defaults)
  {
    h->HWResolution[0] = 600;
    h->HWResolution[1] = 600;
  }
  
  if (set_defaults)
  {
    /* TODO - Support for insert sheets */
    h->InsertSheet = CUPS_FALSE;
  }

  if (set_defaults)
  {
    /* TODO - Support for jog */
    h->Jog = CUPS_JOG_NONE;
  }

  if ((val = cupsGetOption("feed-orientation", num_options,
			   options)) != NULL ||
      (val = cupsGetOption("feed-direction", num_options, options)) != NULL ||
      (val = cupsGetOption("LeadingEdge", num_options, options)) != NULL)
  {
    if (!strcasecmp(val, "ShortEdgeFirst"))
      h->LeadingEdge = CUPS_EDGE_TOP;
    else if (!strcasecmp(val, "LongEdgeFirst"))
      h->LeadingEdge = CUPS_EDGE_RIGHT;
    else
      fprintf(stderr, "DEBUG: Unsupported feed-orientation \"%s\".\n", val);
  }
  else if (set_defaults)
    h->LeadingEdge = CUPS_EDGE_TOP;

  if (pwg_raster || set_defaults)
  {
    /* TODO - Support for manual feed */
    h->ManualFeed = CUPS_FALSE;
  }

  if ((val = cupsGetOption("media-position", num_options, options)) != NULL ||
      (val = cupsGetOption("MediaPosition", num_options, options)) != NULL ||
      (val = cupsGetOption("media-source", num_options, options)) != NULL ||
      (val = cupsGetOption("MediaSource", num_options, options)) != NULL ||
      (val = cupsGetOption("InputSlot", num_options, options)) != NULL ||
      (val = media_source) != NULL)
  {
    if (!strncasecmp(val, "Auto", 4) ||
	!strncasecmp(val, "Default", 7))
      h->MediaPosition = 0;
    else if (!strcasecmp(val, "Main"))
      h->MediaPosition = 1;
    else if (!strcasecmp(val, "Alternate"))
      h->MediaPosition = 2;
    else if (!strcasecmp(val, "LargeCapacity"))
      h->MediaPosition = 3;
    else if (!strcasecmp(val, "Manual"))
      h->MediaPosition = 4;
    else if (!strcasecmp(val, "Envelope"))
      h->MediaPosition = 5;
    else if (!strcasecmp(val, "Disc"))
      h->MediaPosition = 6;
    else if (!strcasecmp(val, "Photo"))
      h->MediaPosition = 7;
    else if (!strcasecmp(val, "Hagaki"))
      h->MediaPosition = 8;
    else if (!strcasecmp(val, "MainRoll"))
      h->MediaPosition = 9;
    else if (!strcasecmp(val, "AlternateRoll"))
      h->MediaPosition = 10;
    else if (!strcasecmp(val, "Top"))
      h->MediaPosition = 11;
    else if (!strcasecmp(val, "Middle"))
      h->MediaPosition = 12;
    else if (!strcasecmp(val, "Bottom"))
      h->MediaPosition = 13;
    else if (!strcasecmp(val, "Side"))
      h->MediaPosition = 14;
    else if (!strcasecmp(val, "Left"))
      h->MediaPosition = 15;
    else if (!strcasecmp(val, "Right"))
      h->MediaPosition = 16;
    else if (!strcasecmp(val, "Center"))
      h->MediaPosition = 17;
    else if (!strcasecmp(val, "Rear"))
      h->MediaPosition = 18;
    else if (!strcasecmp(val, "ByPassTray"))
      h->MediaPosition = 19;
    else if (!strcasecmp(val, "Tray1"))
      h->MediaPosition = 20;
    else if (!strcasecmp(val, "Tray2"))
      h->MediaPosition = 21;
    else if (!strcasecmp(val, "Tray3"))
      h->MediaPosition = 22;
    else if (!strcasecmp(val, "Tray4"))
      h->MediaPosition = 23;
    else if (!strcasecmp(val, "Tray5"))
      h->MediaPosition = 24;
    else if (!strcasecmp(val, "Tray6"))
      h->MediaPosition = 25;
    else if (!strcasecmp(val, "Tray7"))
      h->MediaPosition = 26;
    else if (!strcasecmp(val, "Tray8"))
      h->MediaPosition = 27;
    else if (!strcasecmp(val, "Tray9"))
      h->MediaPosition = 28;
    else if (!strcasecmp(val, "Tray10"))
      h->MediaPosition = 29;
    else if (!strcasecmp(val, "Tray11"))
      h->MediaPosition = 30;
    else if (!strcasecmp(val, "Tray12"))
      h->MediaPosition = 31;
    else if (!strcasecmp(val, "Tray13"))
      h->MediaPosition = 32;
    else if (!strcasecmp(val, "Tray14"))
      h->MediaPosition = 33;
    else if (!strcasecmp(val, "Tray15"))
      h->MediaPosition = 34;
    else if (!strcasecmp(val, "Tray16"))
      h->MediaPosition = 35;
    else if (!strcasecmp(val, "Tray17"))
      h->MediaPosition = 36;
    else if (!strcasecmp(val, "Tray18"))
      h->MediaPosition = 37;
    else if (!strcasecmp(val, "Tray19"))
      h->MediaPosition = 38;
    else if (!strcasecmp(val, "Tray20"))
      h->MediaPosition = 39;
    else if (!strcasecmp(val, "Roll1"))
      h->MediaPosition = 40;
    else if (!strcasecmp(val, "Roll2"))
      h->MediaPosition = 41;
    else if (!strcasecmp(val, "Roll3"))
      h->MediaPosition = 42;
    else if (!strcasecmp(val, "Roll4"))
      h->MediaPosition = 43;
    else if (!strcasecmp(val, "Roll5"))
      h->MediaPosition = 44;
    else if (!strcasecmp(val, "Roll6"))
      h->MediaPosition = 45;
    else if (!strcasecmp(val, "Roll7"))
      h->MediaPosition = 46;
    else if (!strcasecmp(val, "Roll8"))
      h->MediaPosition = 47;
    else if (!strcasecmp(val, "Roll9"))
      h->MediaPosition = 48;
    else if (!strcasecmp(val, "Roll10"))
      h->MediaPosition = 49;
    else
      fprintf(stderr, "DEBUG: Unsupported media source \"%s\".\n", val);
  }
  else if (set_defaults)
    h->MediaPosition = 0; /* Auto */

  if ((val = cupsGetOption("media-weight", num_options, options)) != NULL ||
      (val = cupsGetOption("MediaWeight", num_options, options)) != NULL ||
      (val = cupsGetOption("media-weight-metric", num_options,
			   options)) != NULL ||
      (val = cupsGetOption("MediaWeightMetric", num_options, options)) != NULL)
    h->MediaWeight = atol(val);
  else if (set_defaults)
    h->MediaWeight = 0;

  if (pwg_raster)
  {
    /* Set "reserved" fields to 0 */
    h->MirrorPrint = CUPS_FALSE;
    h->NegativePrint = CUPS_FALSE;
  }
  else
  {
    if ((val = cupsGetOption("mirror-print", num_options, options)) != NULL ||
	(val = cupsGetOption("MirrorPrint", num_options, options)) != NULL)
    {
      if (!strcasecmp(val, "true") || !strcasecmp(val, "on") ||
	  !strcasecmp(val, "yes"))
	h->MirrorPrint = CUPS_TRUE;
      else if (!strcasecmp(val, "false") ||
	       !strcasecmp(val, "off") ||
	       !strcasecmp(val, "no"))
	h->MirrorPrint = CUPS_FALSE;
      else if (set_defaults)
	h->MirrorPrint = CUPS_FALSE;
    }
    if ((val = cupsGetOption("negative-print", num_options, options)) != NULL ||
	(val = cupsGetOption("NegativePrint", num_options, options)) != NULL)
    {
      if (!strcasecmp(val, "true") || !strcasecmp(val, "on") ||
	  !strcasecmp(val, "yes"))
	h->NegativePrint = CUPS_TRUE;
      else if (!strcasecmp(val, "false") ||
	       !strcasecmp(val, "off") ||
	       !strcasecmp(val, "no"))
	h->NegativePrint = CUPS_FALSE;
      else if (set_defaults)
	h->NegativePrint = CUPS_FALSE;
    }
  }

  if ((val = cupsGetOption("copies", num_options, options)) != NULL ||
      (val = cupsGetOption("Copies", num_options, options)) != NULL ||
      (val = cupsGetOption("num-copies", num_options, options)) != NULL ||
      (val = cupsGetOption("NumCopies", num_options, options)) != NULL)
    h->NumCopies = atol(val);
  else if (set_defaults)
    h->NumCopies = 1; /* 0 = Printer default */

  if ((val = cupsGetOption("orientation-requested", num_options,
			   options)) != NULL ||
      (val = cupsGetOption("OrientationRequested", num_options,
			   options)) != NULL ||
      (val = cupsGetOption("Orientation", num_options, options)) != NULL)
  {
    if (!strcasecmp(val, "Portrait") ||
	!strcasecmp(val, "3"))
      h->Orientation = CUPS_ORIENT_0;
    else if (!strcasecmp(val, "Landscape") ||
	     !strcasecmp(val, "4"))
      h->Orientation = CUPS_ORIENT_90;
    else if (!strcasecmp(val, "reverse-portrait") ||
	     !strcasecmp(val, "ReversePortrait") ||
	     !strcasecmp(val, "5"))
      h->Orientation = CUPS_ORIENT_180;
    else if (!strcasecmp(val, "reverse-landscape") ||
	     !strcasecmp(val, "ReverseLandscape") ||
	     !strcasecmp(val, "6"))
      h->Orientation = CUPS_ORIENT_270;
    else
      fprintf(stderr, "DEBUG: Unsupported Orientation \"%s\".\n", val);
  }
  else if (set_defaults)
    h->Orientation = CUPS_ORIENT_0;

  if (pwg_raster)
  {
    /* Set "reserved" fields to 0 */
    h->OutputFaceUp = CUPS_FALSE;
  }
  else
  {
    if ((val = cupsGetOption("OutputFaceUp", num_options, options)) != NULL ||
	(val = cupsGetOption("output-face-up", num_options, options)) != NULL)
    {
      if (!strcasecmp(val, "true") || !strcasecmp(val, "on") ||
	  !strcasecmp(val, "yes"))
	h->OutputFaceUp = CUPS_TRUE;
      else if (!strcasecmp(val, "false") ||
	       !strcasecmp(val, "off") ||
	       !strcasecmp(val, "no"))
	h->OutputFaceUp = CUPS_FALSE;
      else if (set_defaults)
	h->OutputFaceUp = CUPS_FALSE;
    }
  }

  if ((val = cupsGetOption("media-size", num_options, options)) != NULL ||
      (val = cupsGetOption("MediaSize", num_options, options)) != NULL ||
      (val = cupsGetOption("page-size", num_options, options)) != NULL ||
      (val = cupsGetOption("PageSize", num_options, options)) != NULL ||
      (val = page_size) != NULL)
  {
    size_found = NULL;
    if ((size_found = pwgMediaForPWG(val)) == NULL)
      if ((size_found = pwgMediaForPPD(val)) == NULL)
	size_found = pwgMediaForLegacy(val);
    if (size_found != NULL)
    {
      h->PageSize[0] = size_found->width * 72 / 2540;
      h->PageSize[1] = size_found->length * 72 / 2540;
      _strlcpy(h->cupsPageSizeName, size_found->pwg,
	      sizeof(h->cupsPageSizeName));
      if (pwg_raster)
      {
	h->cupsPageSize[0] = 0.0;
	h->cupsPageSize[1] = 0.0;
      }
      else
      {
	h->cupsPageSize[0] = size_found->width * 72.0 / 2540.0;
	h->cupsPageSize[1] = size_found->length * 72.0 / 2540.0;
      }
    }
    else
      fprintf(stderr, "DEBUG: Unsupported page size %s.\n", val);
  }
  else if (set_defaults)
  {
    /* TODO: Automatic A4/Letter, like in scheduler/conf.c in CUPS. */
    h->cupsPageSize[0] = 612.0f;
    h->cupsPageSize[1] = 792.0f;
    
    h->PageSize[0] = 612;
    h->PageSize[1] = 792;
    _strlcpy(h->cupsPageSizeName, "na_letter_8.5x11in",
	    sizeof(h->cupsPageSizeName));
    if (pwg_raster)
    {
      h->cupsPageSize[0] = 0.0;
      h->cupsPageSize[1] = 0.0;
    }
  }
  else if (pwg_raster)
  {
    h->cupsPageSize[0] = 0.0;
    h->cupsPageSize[1] = 0.0;
  }

  if (pwg_raster)
  {
    /* Set "reserved" fields to 0 */
    h->Margins[0] = 0;
    h->Margins[1] = 0;
    h->ImagingBoundingBox[0] = 0;
    h->ImagingBoundingBox[1] = 0;
    h->ImagingBoundingBox[2] = 0;
    h->ImagingBoundingBox[3] = 0;
    h->cupsImagingBBox[0] = 0.0;
    h->cupsImagingBBox[1] = 0.0;
    h->cupsImagingBBox[2] = 0.0;
    h->cupsImagingBBox[3] = 0.0;
  }
  else
  {
    if ((val = cupsGetOption("media-left-margin", num_options, options))
	!= NULL)
    {
      size = atol(val) * 72.0 / 2540.0; 
      h->Margins[0] = (int)size;
      h->ImagingBoundingBox[0] = (int)size;
      h->cupsImagingBBox[0] = size;
    }
    else if (set_defaults)
    {
      h->Margins[0] = 0;
      h->ImagingBoundingBox[0] = 0;
      h->cupsImagingBBox[0] = 18.0f;
    }
    if ((val = cupsGetOption("media-bottom-margin", num_options, options))
	!= NULL)
    {
      size = atol(val) * 72.0 / 2540.0; 
      h->Margins[1] = (int)size;
      h->ImagingBoundingBox[1] = (int)size;
      h->cupsImagingBBox[1] = size;
    }
    else if (set_defaults)
    {
      h->Margins[1] = 0;
      h->ImagingBoundingBox[1] = 0;
      h->cupsImagingBBox[1] = 36.0f;
    }
    if ((val = cupsGetOption("media-right-margin", num_options, options))
	!= NULL)
    {
      size = atol(val) * 72.0 / 2540.0; 
      h->ImagingBoundingBox[2] = h->PageSize[0] - (int)size;
      h->cupsImagingBBox[2] = h->cupsPageSize[0] - size;
    }
    else if (set_defaults)
    {
      h->ImagingBoundingBox[2] = h->PageSize[0];
      h->cupsImagingBBox[2] = 594.0f;
    }
    if ((val = cupsGetOption("media-top-margin", num_options, options))
	!= NULL)
    {
      size = atol(val) * 72.0 / 2540.0; 
      h->ImagingBoundingBox[3] = h->PageSize[1] - (int)size;
      h->cupsImagingBBox[3] = h->cupsPageSize[1] - size;
    }
    else if (set_defaults)
    {
      h->ImagingBoundingBox[3] = h->PageSize[1];
      h->cupsImagingBBox[3] = 756.0f;
    }
  }

  if (pwg_raster)
  {
    /* Set "reserved" fields to 0 */
    h->Separations = CUPS_FALSE;
    h->TraySwitch = CUPS_FALSE;
  }
  else
  {
    if ((val = cupsGetOption("separations", num_options, options)) != NULL ||
	(val = cupsGetOption("Separations", num_options, options)) != NULL)
    {
      if (!strcasecmp(val, "true") || !strcasecmp(val, "on") ||
	  !strcasecmp(val, "yes"))
	h->Separations = CUPS_TRUE;
      else if (!strcasecmp(val, "false") ||
	       !strcasecmp(val, "off") ||
	       !strcasecmp(val, "no"))
	h->Separations = CUPS_FALSE;
      else if (set_defaults)
	h->Separations = CUPS_FALSE;
    }
    if ((val = cupsGetOption("tray-switch", num_options, options)) != NULL ||
	(val = cupsGetOption("TraySwitch", num_options, options)) != NULL)
    {
      if (!strcasecmp(val, "true") || !strcasecmp(val, "on") ||
	  !strcasecmp(val, "yes"))
	h->TraySwitch = CUPS_TRUE;
      else if (!strcasecmp(val, "false") ||
	       !strcasecmp(val, "off") ||
	       !strcasecmp(val, "no"))
	h->TraySwitch = CUPS_FALSE;
      else if (set_defaults)
	h->TraySwitch = CUPS_FALSE;
    }
  }

  if ((val = cupsGetOption("sides", num_options, options)) != NULL ||
      (val = cupsGetOption("Tumble", num_options, options)) != NULL)
  {
    if (!strcasecmp(val, "None") || !strcasecmp(val, "Off") ||
	!strcasecmp(val, "False") || !strcasecmp(val, "No") ||
	!strcasecmp(val, "one-sided") || !strcasecmp(val, "OneSided") ||
	!strcasecmp(val, "two-sided-long-edge") ||
	!strcasecmp(val, "TwoSidedLongEdge") ||
	!strcasecmp(val, "DuplexNoTumble"))
      h->Tumble = CUPS_FALSE;
    else if (!strcasecmp(val, "On") ||
	     !strcasecmp(val, "True") || !strcasecmp(val, "Yes") ||
	     !strcasecmp(val, "two-sided-short-edge") ||
	     !strcasecmp(val, "TwoSidedShortEdge") ||
	     !strcasecmp(val, "DuplexTumble"))
      h->Tumble = CUPS_TRUE;
  }

  h->cupsWidth = h->HWResolution[0] * h->PageSize[0] / 72;
  h->cupsHeight = h->HWResolution[1] * h->PageSize[1] / 72;

  if (pwg_raster || set_defaults)
  {
    /* TODO - Support for MediaType number */
    h->cupsMediaType = 0;
  }

  if ((val = cupsGetOption("pwg-raster-document-type", num_options,
			   options)) != NULL ||
      (val = cupsGetOption("PwgRasterDocumentType", num_options,
			   options)) != NULL ||
      (val = cupsGetOption("print-color-mode", num_options,
			   options)) != NULL ||
      (val = cupsGetOption("PrintColorMode", num_options, options)) != NULL ||
      (val = cupsGetOption("color-space", num_options, options)) != NULL ||
      (val = cupsGetOption("ColorSpace", num_options, options)) != NULL ||
      (val = cupsGetOption("color-model", num_options, options)) != NULL ||
      (val = cupsGetOption("ColorModel", num_options, options)) != NULL)
  {
    int	        bitspercolor,	/* Bits per color */
                bitsperpixel,   /* Bits per pixel */
                colorspace,     /* CUPS/PWG raster color space */
                numcolors;	/* Number of colorants */
    const char	*ptr;		/* Pointer into value */

    ptr = NULL;
    numcolors = 0;
    bitspercolor = 8;
    if (!strncasecmp(val, "AdobeRgb", 8))
    {
      if (*(val + 8) == '_' || *(val + 8) == '-')
	ptr = val + 9;
      colorspace = 20;
      numcolors = 3;
    }
    else if (!strncasecmp(val, "adobe-rgb", 9))
    {
      if (*(val + 9) == '_' || *(val + 9) == '-')
	ptr = val + 10;
      colorspace = 20;
      numcolors = 3;
    }
    else if (!strcasecmp(val, "auto-monochrome"))
    {
      colorspace = 18;
      numcolors = 1;
    }
    else if (!strcasecmp(val, "bi-level") ||
	     !strcasecmp(val, "process-bi-level"))
    {
      bitspercolor = 1;
      colorspace = 3;
      numcolors = 1;
    }
    else if (!strncasecmp(val, "Black", 5))
    {
      if (*(val + 5) == '_' || *(val + 5) == '-')
	ptr = val + 6;
      bitspercolor = 1;
      colorspace = 3;
      numcolors = 1;
    }
    else if (!strcasecmp(val, "process-monochrome"))
    {
      colorspace = 18;
      numcolors = 1;
    }
    else if (!strncasecmp(val, "Monochrome", 10))
    {
      if (*(val + 10) == '_' || *(val + 10) == '-')
	ptr = val + 11;
      colorspace = 18;
      numcolors = 1;
    }
    else if (!strncasecmp(val, "Mono", 4))
    {
      if (*(val + 4) == '_' || *(val + 4) == '-')
	ptr = val + 5;
      colorspace = 18;
      numcolors = 1;
    }
    else if (!strcasecmp(val, "color"))
    {
      colorspace = 19;
      numcolors = 3;
    }
    else if (!strncasecmp(val, "Cmyk", 4))
    {
      if (*(val + 4) == '_' || *(val + 4) == '-')
	ptr = val + 5;
      colorspace = 6;
      numcolors = 4;
    }
    else if (!strncasecmp(val, "Cmy", 3))
    {
      if (*(val + 3) == '_' || *(val + 3) == '-')
	ptr = val + 4;
      colorspace = 4;
      numcolors = 3;
    }
    else if (!strncasecmp(val, "Device", 6))
    {
      ptr = val + 6;
      numcolors = strtol(ptr, (char **)&ptr, 10);
      if (*ptr == '_' || *ptr == '-')
      {
	ptr ++;
	colorspace = 47 + numcolors;
      }
      else
      {
	numcolors = 0;
	ptr = NULL;
      }
    }
    else if (!strncasecmp(val, "Sgray", 5))
    {
      if (*(val + 5) == '_' || *(val + 5) == '-')
	ptr = val + 6;
      colorspace = 18;
      numcolors = 1;
    }
    else if (!strncasecmp(val, "Gray", 4))
    {
      if (*(val + 4) == '_' || *(val + 4) == '-')
	ptr = val + 5;
      colorspace = 18;
      numcolors = 1;
    }
    else if (!strncasecmp(val, "Srgb", 4))
    {
      if (*(val + 4) == '_' || *(val + 4) == '-')
	ptr = val + 5;
      colorspace = 19;
      numcolors = 3;
    }
    else if (!strncasecmp(val, "Rgbw", 4))
    {
      if (*(val + 4) == '_' || *(val + 4) == '-')
	ptr = val + 5;
      colorspace = 17;
      numcolors = 4;
    }
    else if (!strncasecmp(val, "Rgb", 3))
    {
      if (*(val + 3) == '_' || *(val + 3) == '-')
	ptr = val + 4;
      colorspace = 1;
      numcolors = 3;
    }
    else if (!strcasecmp(val, "auto"))
    {
      /* Let "auto" not look like an error */
      if (set_defaults)
      {
	fprintf(stderr,
		"DEBUG: \"Auto\" mode, using default RGB color space.\n");
	colorspace = 19;
	numcolors = 3;
      }
    }
    if (numcolors > 0)
    {
      if (ptr)
	bitspercolor = strtol(ptr, (char **)&ptr, 10);
      bitsperpixel = bitspercolor * numcolors;
      /* In 1-bit-per-color RGB modes we add a forth bit to each pixel
	 to align the pixels with bytes */
      if (bitsperpixel == 3 &&
	  strcasestr(val, "Rgb"))
	bitsperpixel = 4;
      h->cupsBitsPerColor = bitspercolor;
      h->cupsBitsPerPixel = bitsperpixel;
      h->cupsColorSpace = colorspace;
      h->cupsNumColors = numcolors;
    }
    else
    {
      fprintf(stderr, "DEBUG: Bad color space value \"%s\".\n", val);
      if (set_defaults)
      {
	h->cupsBitsPerColor = 8;
	h->cupsBitsPerPixel = 24;
	h->cupsColorSpace = 19;
	h->cupsNumColors = 3;
      }
    }
  }
  else if (set_defaults)
  {
    h->cupsBitsPerColor = 8;
    h->cupsBitsPerPixel = 24;
    h->cupsColorSpace = 19;
    h->cupsNumColors = 3;
  }

  h->cupsBytesPerLine = (h->cupsWidth * h->cupsBitsPerPixel + 7) / 8;
  
  if (pwg_raster || set_defaults)
  {
    /* TODO - Support for color orders 1 (banded) and 2 (planar) */
    h->cupsColorOrder = 0;
  }
  
  if (pwg_raster || set_defaults)
  {
    /* TODO - Support for these parameters */
    h->cupsCompression = 0;
    h->cupsRowCount = 0;
    h->cupsRowFeed = 0;
    h->cupsRowStep = 0;
  }

  if (pwg_raster || set_defaults)
  {
    /* TODO - Support for cupsBorderlessScalingFactor */
    h->cupsBorderlessScalingFactor = 0.0;
  }

  if (pwg_raster || set_defaults)
  {
    /* TODO - Support for custom values in CUPS Raster mode */
    for (i = 0; i < 16; i ++)
    {
      h->cupsInteger[i] = 0;
      h->cupsReal[i] = 0.0;
      memset(h->cupsString[i], 0, 64);
    }
  }

  if (pwg_raster)
  {
    
    if ((val = cupsGetOption("job-impressions", num_options,
			     options)) != NULL ||
	(val = cupsGetOption("JobImpressions", num_options, options)) != NULL ||
	(val = cupsGetOption("Impressions", num_options, options)) != NULL)
    {
      int impressions = atoi(val);
      if (impressions >= 0)
	h->cupsInteger[0] = impressions;
    }

    if ((val = cupsGetOption("pwg-raster-document-sheet-back", num_options,
			     options)) != NULL ||
	(val = cupsGetOption("PwgRasterDocumentSheetBack", num_options,
			     options)) != NULL)
    {
      /* Set CrossFeedTransform and FeedTransform */
      if (h->Duplex == CUPS_FALSE)
      {
	h->cupsInteger[1] = 1;
	h->cupsInteger[2] = 1;
      }
      else if (h->Duplex == CUPS_TRUE)
      {
	if (h->Tumble == CUPS_FALSE)
	{
	  if (!strcasecmp(val, "Flipped"))
	  {
	    h->cupsInteger[1] =  1;
	    h->cupsInteger[2] = -1;
	  }
	  else if (!strncasecmp(val, "Manual", 6))
	  {
	    h->cupsInteger[1] =  1;
	    h->cupsInteger[2] =  1;
	  }
	  else if (!strcasecmp(val, "Normal"))
	  {
	    h->cupsInteger[1] =  1;
	    h->cupsInteger[2] =  1;
	  }
	  else if (!strcasecmp(val, "Rotated"))
	  {
	    h->cupsInteger[1] = -1;
	    h->cupsInteger[2] = -1;
	  }
	  else 
	  {
	    h->cupsInteger[1] =  1;
	    h->cupsInteger[2] =  1;
	  }
	}
	else
	{
	  if (!strcasecmp(val, "Flipped"))
	  {
	    h->cupsInteger[1] = -1;
	    h->cupsInteger[2] =  1;
	  }
	  else if (!strncasecmp(val, "Manual", 6))
	  {
	    h->cupsInteger[1] = -1;
	    h->cupsInteger[2] = -1;
	  }
	  else if (!strcasecmp(val, "Normal"))
	  {
	    h->cupsInteger[1] =  1;
	    h->cupsInteger[2] =  1;
	  }
	  else if (!strcasecmp(val, "Rotated"))
	  {
	    h->cupsInteger[1] =  1;
	    h->cupsInteger[2] =  1;
	  }
	  else 
	  {
	    h->cupsInteger[1] =  1;
	    h->cupsInteger[2] =  1;
	  }
	}
      }
      else
      {
	h->cupsInteger[1] = 1;
	h->cupsInteger[2] = 1;
      }
    }
    else
    {
      h->cupsInteger[1] = 1;
      h->cupsInteger[2] = 1;
    }

    /* TODO - Support for ImageBoxLeft, ImageBoxTop, ImageBoxRight, and
       ImageBoxBottom (h->cupsInteger[3..6]), leave on 0 for now */

    if ((val = cupsGetOption("alternate-primary", num_options,
			     options)) != NULL ||
	(val = cupsGetOption("AlternatePrimary", num_options,
			     options)) != NULL)
    {
      int alternateprimary = atoi(val);		/* SRGB value for black
						   pixels */
      h->cupsInteger[7] = alternateprimary;
    }

    if ((val = cupsGetOption("print-quality", num_options, options)) != NULL ||
	(val = cupsGetOption("PrintQuality", num_options, options)) != NULL ||
	(val = cupsGetOption("Quality", num_options, options)) != NULL)
    {
      int quality = atoi(val);		/* print-quality value */

      if (!quality ||
	  (quality >= IPP_QUALITY_DRAFT && quality <= IPP_QUALITY_HIGH))
	h->cupsInteger[8] = quality;
      else
	fprintf(stderr, "DEBUG: Unsupported print-quality %d.\n", quality);
    }

    /* Leave "reserved" fields (h->cupsInteger[9..13]) on 0 */

    if ((val = cupsGetOption("vendor-identifier", num_options,
			     options)) != NULL ||
	(val = cupsGetOption("VendorIdentifier", num_options,
			     options)) != NULL)
    {
      int vendorid = atoi(val);		/* USB ID of manufacturer */
      h->cupsInteger[14] = vendorid;
    }

    if ((val = cupsGetOption("vendor-length", num_options,
			     options)) != NULL ||
	(val = cupsGetOption("VendorLength", num_options,
			     options)) != NULL)
    {
      int vendorlength = atoi(val);		/* How many bytes of vendor
						   data? */
      if (vendorlength <= 1088)
      {
	h->cupsInteger[15] = vendorlength;
	if ((val = cupsGetOption("vendor-data", num_options,
				 options)) != NULL ||
	    (val = cupsGetOption("VendorData", num_options,
				 options)) != NULL)
	  /* TODO - How to enter binary data here? */
	  _strlcpy((char *)&(h->cupsReal[0]), val, 1088);
      }
    }
  }

  if (pwg_raster || set_defaults)
  {
    /* Set "reserved" fields to 0 */
    memset(h->cupsMarkerType, 0, 64);
  }

  if ((val = cupsGetOption("print-rendering-intent", num_options,
			   options)) != NULL ||
      (val = cupsGetOption("PrintRenderingIntent", num_options,
			   options)) != NULL ||
      (val = cupsGetOption("RenderingIntent", num_options,
			   options)) != NULL)
  {
    if (!strcmp(val, "absolute"))
      _strlcpy(h->cupsRenderingIntent, "Absolute",
	      sizeof(h->cupsRenderingIntent));
    else if (!strcmp(val, "automatic"))
      _strlcpy(h->cupsRenderingIntent, "Automatic",
	      sizeof(h->cupsRenderingIntent));
    else if (!strcmp(val, "perceptual"))
      _strlcpy(h->cupsRenderingIntent, "Perceptual",
	      sizeof(h->cupsRenderingIntent));
    else if (!strcmp(val, "relative"))
      _strlcpy(h->cupsRenderingIntent, "Relative",
	      sizeof(h->cupsRenderingIntent));
    else if (!strcmp(val, "relative-bpc"))
      _strlcpy(h->cupsRenderingIntent, "RelativeBpc",
	      sizeof(h->cupsRenderingIntent));
    else if (!strcmp(val, "saturation"))
      _strlcpy(h->cupsRenderingIntent, "Saturation",
	      sizeof(h->cupsRenderingIntent));
    else
      fprintf(stderr, "DEBUG: Unsupported print-rendering-intent \"%s\".\n",
	      val);
  }
  else if (set_defaults)
    h->cupsRenderingIntent[0] = '\0';

  if (media_source != NULL)
    free(media_source);
  if (media_type != NULL)
    free(media_type);
  if (page_size != NULL)
    free(page_size);
  if (num_non_ppd_options)
    cupsFreeOptions(num_non_ppd_options, non_ppd_options);
#endif /* HAVE_CUPS_1_7 */

  return (0);
}


/*
 * End
 */
