/*
 *
 *   GNU Ghostscript raster output driver for the Common UNIX Printing
 *   System (CUPS).
 *
 *   Copyright 1993-2006 by Easy Software Products.
 *
 *   These coded instructions, statements, and computer programs are the
 *   property of Easy Software Products and are protected by Federal
 *   copyright law.  Distribution and use rights are outlined in the file
 *   "LICENSE.txt" which should have been included with this file.  If this
 *   file is missing or damaged please contact Easy Software Products
 *   at:
 *
 *       Attn: CUPS Licensing Information
 *       Easy Software Products
 *       44141 Airport View Drive, Suite 204
 *       Hollywood, Maryland 20636 USA
 *
 *       Voice: (301) 373-9600
 *       EMail: cups-info@cups.org
 *         WWW: http://www.cups.org/
 *
 *   This code and any derivative of it may be used and distributed
 *   freely under the terms of the GNU General Public License when
 *   used with GNU Ghostscript or its derivatives.  Use of the code
 *   (or any derivative of it) with software other than GNU
 *   GhostScript (or its derivatives) is governed by the CUPS license
 *   agreement.
 *
 * Contents:
 *
 *   cups_close()            - Close the output file.
 *   cups_decode_color()     - Decode a color value.
 *   cups_encode_color()     - Encode a color value.
 *   cups_get_color_comp_index()
 *                           - Color component to index
 *   cups_get_color_mapping_procs()
 *                           - Get the list of color mapping procedures.
 *   cups_get_matrix()       - Generate the default page matrix.
 *   cups_get_params()       - Get pagedevice parameters.
 *   cups_get_space_params() - Get space parameters from the RIP_CACHE env var.
 *   cups_map_cielab()       - Map CIE Lab transformation...
 *   cups_map_cmyk()         - Map a CMYK color value to device colors.
 *   cups_map_gray()         - Map a grayscale value to device colors.
 *   cups_map_rgb()          - Map a RGB color value to device colors.
 *   cups_map_cmyk_color()   - Map a CMYK color to a color index.
 *   cups_map_color_rgb()    - Map a color index to an RGB color.
 *   cups_map_rgb_color()    - Map an RGB color to a color index.  We map the
 *                             RGB color to the output colorspace & bits (we
 *                             figure out the format when we output a page).
 *   cups_open()             - Open the output file and initialize things.
 *   cups_print_pages()      - Send one or more pages to the output file.
 *   cups_put_params()       - Set pagedevice parameters.
 *   cups_set_color_info()   - Set the color information structure based on
 *                             the required output.
 *   cups_sync_output()      - Keep the user informed of our status...
 *   cups_print_chunked()    - Print a page of chunked pixels.
 *   cups_print_banded()     - Print a page of banded pixels.
 *   cups_print_planar()     - Print a page of planar pixels.
 */

/* prevent gp.h redefining fopen */
#define sprintf sprintf

/*
 * Include necessary headers...
 */

#include "std.h"                /* to stop stdlib.h redefining types */
#include "gdevprn.h"
#include "gsparam.h"
#include "gxdevsop.h"
#include "arch.h"
#include "gsicc_manage.h"

#include <stdlib.h>
#include <ctype.h>
#include <cups/raster.h>
#include <cups/ppd.h>
#include <math.h>

/* the extremely noisy DEBUG2 messages are now dependent on CUPS_DEBUG2 */
/* this can be enabled during the 'make' or by uncommenting the following */
/* #define CUPS_DEBUG2 */

#undef private
#define private

#ifdef WIN32
#define cbrt(arg) pow(arg, 1.0/3)
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#endif

/* This should go into gdevprn.h, or, better yet, gdevprn should
   acquire an API for changing resolution. */
int gdev_prn_maybe_realloc_memory(gx_device_printer *pdev,
                                  gdev_prn_space_params *old_space,
                                  int old_width, int old_height,
                                  bool old_page_uses_transparency);

/* Strings for cups_put/get_params */
static const char * const cups_Integer_strings[] =
{
  "cupsInteger0",
  "cupsInteger1",
  "cupsInteger2",
  "cupsInteger3",
  "cupsInteger4",
  "cupsInteger5",
  "cupsInteger6",
  "cupsInteger7",
  "cupsInteger8",
  "cupsInteger9",
  "cupsInteger10",
  "cupsInteger11",
  "cupsInteger12",
  "cupsInteger13",
  "cupsInteger14",
  "cupsInteger15",
  NULL
};

static const char * const cups_Real_strings[] =
{
  "cupsReal0",
  "cupsReal1",
  "cupsReal2",
  "cupsReal3",
  "cupsReal4",
  "cupsReal5",
  "cupsReal6",
  "cupsReal7",
  "cupsReal8",
  "cupsReal9",
  "cupsReal10",
  "cupsReal11",
  "cupsReal12",
  "cupsReal13",
  "cupsReal14",
  "cupsReal15",
  NULL
};

static const char * const cups_String_strings[] =
{
  "cupsString0",
  "cupsString1",
  "cupsString2",
  "cupsString3",
  "cupsString4",
  "cupsString5",
  "cupsString6",
  "cupsString7",
  "cupsString8",
  "cupsString9",
  "cupsString10",
  "cupsString11",
  "cupsString12",
  "cupsString13",
  "cupsString14",
  "cupsString15",
  NULL
};

/*
 * Check if we are compiling against CUPS 1.2.  If so, enable
 * certain extended attributes and use a different page header
 * structure and write function...
 */

#ifdef CUPS_RASTER_SYNCv1
#  define cups_page_header_t cups_page_header2_t
#  define cupsRasterWriteHeader cupsRasterWriteHeader2
#else
/* The RGBW, SW, SRGB, and ADOBERGB colorspaces is not defined until
   CUPS 1.2... */
#  define CUPS_CSPACE_RGBW 17
#  define CUPS_CSPACE_SW 18
#  define CUPS_CSPACE_SRGB 19
#  define CUPS_CSPACE_ADOBERGB 20
#endif /* CUPS_RASTER_SYNCv1 */

#if !defined(CUPS_RASTER_WRITE_PWG)
    #define CUPS_RASTER_WRITE_PWG 3
#endif

/*
 * CIE XYZ color constants...
 */

#define D65_X	(0.412453 + 0.357580 + 0.180423)
#define D65_Y	(0.212671 + 0.715160 + 0.072169)
#define D65_Z	(0.019334 + 0.119193 + 0.950227)


/*
 * Size of a tile in pixels...
 */

#define CUPS_TILE_SIZE	256


/*
 * Size of profile LUTs...
 */

#ifdef dev_t_proc_encode_color
#  define CUPS_MAX_VALUE	frac_1
#else
#  define CUPS_MAX_VALUE	gx_max_color_value
#endif /* dev_t_proc_encode_color */


/*
 * Macros...
 */

#define x_dpi		(pdev->HWResolution[0])
#define y_dpi		(pdev->HWResolution[1])
#define cups		((gx_device_cups *)pdev)

/*
 * Macros from <macros.h>; we can't include <macros.h> because it also
 * defines DEBUG, one of our flags to insert various debugging code.
 */

#ifndef max
#  define max(a,b)	((a)<(b) ? (b) : (a))
#endif /* !max */

#ifndef min
#  define min(a,b)	((a)>(b) ? (b) : (a))
#endif /* !min */

#ifndef abs
#  define abs(x)	((x)>=0 ? (x) : -(x))
#endif /* !abs */


/*
 * Procedures
 */

private dev_proc_close_device(cups_close);
private dev_proc_get_initial_matrix(cups_get_matrix);
private int cups_get_params(gx_device *, gs_param_list *);
private dev_proc_open_device(cups_open);
private dev_proc_output_page(cups_output_page);
private int cups_print_pages(gx_device_printer *, gp_file *, int);
private int cups_put_params(gx_device *, gs_param_list *);
private int cups_set_color_info(gx_device *);
private dev_proc_sync_output(cups_sync_output);
private prn_dev_proc_get_space_params(cups_get_space_params);
private int cups_spec_op(gx_device *dev_, int op, void *data, int datasize);

#ifdef dev_t_proc_encode_color
private cm_map_proc_gray(cups_map_gray);
private cm_map_proc_rgb(cups_map_rgb);
private cm_map_proc_cmyk(cups_map_cmyk);
private dev_proc_decode_color(cups_decode_color);
private dev_proc_encode_color(cups_encode_color);
private dev_proc_get_color_comp_index(cups_get_color_comp_index);
private dev_proc_get_color_mapping_procs(cups_get_color_mapping_procs);

static const gx_cm_color_map_procs cups_color_mapping_procs =
{
  cups_map_gray,
  cups_map_rgb,
  cups_map_cmyk
};
#else
private dev_proc_map_cmyk_color(cups_map_cmyk_color);
private dev_proc_map_color_rgb(cups_map_color_rgb);
private dev_proc_map_rgb_color(cups_map_rgb_color);
#endif /* dev_t_proc_encode_color */


/*
 * The device descriptors...
 */

typedef struct gx_device_cups_s
{
  gx_device_common;			/* Standard GhostScript device stuff */
  gx_prn_device_common;			/* Standard printer device stuff */
  int			page;		/* Page number */
  cups_raster_t		*stream;	/* Raster stream */
  cups_page_header_t	header;		/* PostScript page device info */
  int			landscape;	/* Non-zero if this is landscape */
  int			lastpage;
  int			HaveProfile;	/* Has a color profile been defined? */
  char			*Profile;	/* Current simple color profile string */
  ppd_file_t		*PPD;		/* PPD file for this device */
  unsigned char		RevLower1[16];	/* Lower 1-bit reversal table */
  unsigned char 	RevUpper1[16];	/* Upper 1-bit reversal table */
  unsigned char		RevLower2[16];	/* Lower 2-bit reversal table */
  unsigned char		RevUpper2[16];	/* Upper 2-bit reversal table */
#ifdef GX_COLOR_INDEX_TYPE
  gx_color_value	DecodeLUT[65536];/* Output color to RGB value LUT */
#else
  gx_color_value	DecodeLUT[256];	/* Output color to RGB value LUT */
#endif /* GX_COLOR_INDEX_TYPE */
  unsigned short	EncodeLUT[gx_max_color_value + 1];/* RGB value to output color LUT */
  int			Density[CUPS_MAX_VALUE + 1];/* Density LUT */
  int			Matrix[3][3][CUPS_MAX_VALUE + 1];/* Color transform matrix LUT */
  int                   user_icc;
  int                   cupsRasterVersion;
  char                  pageSizeRequested[64];

  /* Used by cups_put_params(): */
} gx_device_cups;

private gx_device_procs	cups_procs =
{
   cups_open,
   cups_get_matrix,
   cups_sync_output,
   cups_output_page,
   cups_close,
#ifdef dev_t_proc_encode_color
   NULL,				/* map_rgb_color */
   NULL,				/* map_color_rgb */
#else
   cups_map_rgb_color,
   cups_map_color_rgb,
#endif /* dev_t_proc_encode_color */
   NULL,				/* fill_rectangle */
   NULL,				/* tile_rectangle */
   NULL,				/* copy_mono */
   NULL,				/* copy_color */
   NULL,				/* draw_line */
   gx_default_get_bits,
   cups_get_params,
   cups_put_params,
#ifdef dev_t_proc_encode_color
   NULL,				/* map_cmyk_color */
#else
   cups_map_cmyk_color,
#endif /* dev_t_proc_encode_color */
   NULL,				/* get_xfont_procs */
   NULL,				/* get_xfont_device */
   NULL,				/* map_rgb_alpha_color */
   gx_page_device_get_page_device,
   NULL,				/* get_alpha_bits */
   NULL,				/* copy_alpha */
   NULL,				/* get_band */
   NULL,				/* copy_rop */
   NULL,				/* fill_path */
   NULL,				/* stroke_path */
   NULL,				/* fill_mask */
   NULL,				/* fill_trapezoid */
   NULL,				/* fill_parallelogram */
   NULL,				/* fill_triangle */
   NULL,				/* draw_thin_line */
   NULL,				/* begin_image */
   NULL,				/* image_data */
   NULL,				/* end_image */
   NULL,				/* strip_tile_rectangle */
   NULL,				/* strip_copy_rop */
   NULL,				/* get_clipping_box */
   NULL,				/* begin_typed_image */
   NULL,				/* get_bits_rectangle */
   NULL,				/* map_color_rgb_alpha */
   NULL,				/* create_compositor */
   NULL,				/* get_hardware_params */
   NULL,				/* text_begin */
   NULL,				/* finish_copydevice */
   NULL,				/* begin_transparency_group */
   NULL,				/* end_transparency_group */
   NULL,				/* begin_transparency_mask */
   NULL,				/* end_transparency_mask */
   NULL,				/* discard_transparency_layer */
#ifdef dev_t_proc_encode_color
   cups_get_color_mapping_procs,
   cups_get_color_comp_index,
   cups_encode_color,
   cups_decode_color,
#else
   NULL,                                /* get_color_mapping_procs */
   NULL,                                /* get_color_comp_index */
   NULL,                                /* encode_color */
   NULL,                                /* decode_color */
#endif /* dev_t_proc_encode_color */
   NULL,				/* pattern_manage */
   NULL,				/* fill_rectangle_hl_color */
   NULL,				/* include_color_space */
   NULL,				/* fill_linear_color_scanline */
   NULL,				/* fill_linear_color_trapezoid */
   NULL,				/* fill_linear_color_triangle */
   NULL,				/* update_spot_equivalent_colors */
   NULL,				/* ret_devn_params */
   NULL,				/* fillpage */
   NULL,				/* push_transparency_state */
   NULL,				/* pop_transparency_state */
   NULL,                                /* put_image */
   cups_spec_op
};

#define prn_device_body_copies(dtype, procs, dname, w10, h10, xdpi, ydpi, lo, to, lm, bm, rm, tm, ncomp, depth, mg, mc, dg, dc, print_pages)\
	std_device_full_body_type(dtype, &procs, dname, &st_device_printer,\
	  (int)((long)(w10) * (xdpi) / 10),\
	  (int)((long)(h10) * (ydpi) / 10),\
	  xdpi, ydpi,\
	  ncomp, depth, mg, mc, dg, dc,\
	  -(lo) * (xdpi), -(to) * (ydpi),\
	  (lm) * 72.0, (bm) * 72.0,\
	  (rm) * 72.0, (tm) * 72.0\
	),\
	prn_device_body_copies_rest_(print_pages)



#ifdef CUPS_RASTER_SYNCv1
#define RASTER_SYNCv1_ENTRIES \
    ,\
    1,                                  /* cupsNumColors */\
    1.0,                                /* cupsBorderlessScalingFactor */\
    { 612.0, 792.0 },                   /* cupsPageSize */\
    { 0.0, 0.0, 612.0, 792.0 },         /* cupsImagingBBox */\
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* cupsInteger */\
    { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\
      0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }, /* cupsReal */\
    { "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" },\
                                        /* cupsString */\
    "",                                 /* cupsMarkerType */\
    "",                                 /* cupsRenderingIntent */\
    ""                                  /* cupsPageSizeName */
#else
#define RASTER_SYNCv1_ENTRIES
#endif /* CUPS_RASTER_SYNCv1 */

#define gs_xxx_device(dname, mediaclass)\
  prn_device_body_copies(gx_device_cups,/* type */\
                         cups_procs,	/* procedures */\
			 dname,		/* device name */\
			 85,		/* initial width */\
			 110,		/* initial height */\
			 100,		/* initial x resolution */\
			 100,		/* initial y resolution */\
                         0,		/* initial left offset */\
			 0,		/* initial top offset */\
			 0,		/* initial left margin */\
			 0,		/* initial bottom margin */\
			 0,		/* initial right margin */\
			 0,		/* initial top margin */\
			 1,		/* number of color components */\
			 1,		/* number of color bits */\
			 1,		/* maximum gray value */\
			 0,		/* maximum color value */\
			 2,		/* number of gray values */\
			 0,		/* number of color values */\
			 cups_print_pages),\
					/* print procedure */\
  0,					/* page */\
  NULL,					/* stream */\
  {					/* header */\
    mediaclass,				/* MediaClass */\
    "",					/* MediaColor */\
    "",					/* MediaType */\
    "",					/* OutputType */\
    0,					/* AdvanceDistance */\
    CUPS_ADVANCE_NONE,			/* AdvanceMedia */\
    CUPS_FALSE,				/* Collate */\
    CUPS_CUT_NONE,			/* CutMedia */\
    CUPS_FALSE,				/* Duplex */\
    { 100, 100 },			/* HWResolution */\
    { 0, 0, 612, 792 },			/* ImagingBoundingBox */\
    CUPS_FALSE,				/* InsertSheet */\
    CUPS_JOG_NONE,			/* Jog */\
    CUPS_EDGE_TOP,			/* LeadingEdge */\
    { 0, 0 },				/* Margins */\
    CUPS_FALSE,				/* ManualFeed */\
    0,					/* MediaPosition */\
    0,					/* MediaWeight */\
    CUPS_FALSE,				/* MirrorPrint */\
    CUPS_FALSE,				/* NegativePrint */\
    1,					/* NumCopies */\
    CUPS_ORIENT_0,			/* Orientation */\
    CUPS_FALSE,				/* OutputFaceUp */\
    { 612, 792 },			/* PageSize */\
    CUPS_FALSE,				/* Separations */\
    CUPS_FALSE,				/* TraySwitch */\
    CUPS_FALSE,				/* Tumble */\
    850,				/* cupsWidth */\
    1100,				/* cupsHeight */\
    0,					/* cupsMediaType */\
    1,					/* cupsBitsPerColor */\
    1,					/* cupsBitsPerPixel */\
    107,				/* cupsBytesPerLine */\
    CUPS_ORDER_CHUNKED,			/* cupsColorOrder */\
    CUPS_CSPACE_K,			/* cupsColorSpace */\
    0,					/* cupsCompression */\
    0,					/* cupsRowCount */\
    0,					/* cupsRowFeed */\
    0					/* cupsRowStep */\
    RASTER_SYNCv1_ENTRIES, /* See above */\
  },\
  0,                                    /* landscape */\
  0,                                    /* lastpage */\
  0,                                    /* HaveProfile */\
  NULL,                                 /* Profile */\
  NULL,                                 /* PPD */\
  { 0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06, 0x0e,\
    0x01, 0x09, 0x05, 0x0d, 0x03, 0x0b, 0x07, 0x0f },/* RevLower1 */\
  { 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,\
    0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0 },/* RevUpper1 */\
  { 0x00, 0x04, 0x08, 0x0c, 0x01, 0x05, 0x09, 0x0d,\
    0x02, 0x06, 0x0a, 0x0e, 0x03, 0x07, 0x0b, 0x0f },/* RevLower2 */\
  { 0x00, 0x40, 0x80, 0xc0, 0x10, 0x50, 0x90, 0xd0,\
    0x20, 0x60, 0xa0, 0xe0, 0x30, 0x70, 0xb0, 0xf0 },/* RevUpper2 */\
  {0x00},                                  /* DecodeLUT */\
  {0x00},                                  /* EncodeLUT */\
  {0x00},                                  /* Density */\
  {{{0x00},{0x00},{0x00}},\
   {{0x00},{0x00},{0x00}},\
   {{0x00},{0x00},{0x00}}},                /* Matrix */\
  0,                                       /* user_icc */\
  3,                                     /* cupsRasterVersion */\
  ""                                     /* pageSizeRequested */

gx_device_cups	gs_cups_device = { gs_xxx_device("cups", "") };
gx_device_cups	gs_pwgraster_device = { gs_xxx_device("pwgraster",
						      "PwgRaster") };

/*
 * Local functions...
 */

static double	cups_map_cielab(double, double);
static int	cups_print_chunked(gx_device_printer *, unsigned char *,
		                   unsigned char *, int);
static int	cups_print_banded(gx_device_printer *, unsigned char *,
		                  unsigned char *, int);
static int	cups_print_planar(gx_device_printer *, unsigned char *,
		                  unsigned char *, int);

/*static void	cups_set_margins(gx_device *);*/


/*
 * 'cups_close()' - Close the output file.
 */

private int
cups_close(gx_device *pdev)		/* I - Device info */
{
#ifdef CUPS_DEBUG2
  dmprintf1(pdev->memory, "DEBUG2: cups_close(%p)\n", pdev);
#endif /* CUPS_DEBUG2 */

  dmprintf(pdev->memory, "INFO: Rendering completed\n");

  if (cups->stream != NULL)
  {
    cupsRasterClose(cups->stream);
    cups->stream = NULL;
  }

#if 0 /* Can't do this here because put_params() might close the device */
  if (cups->PPD != NULL)
  {
    ppdClose(cups->PPD);
    cups->PPD = NULL;
  }

  if (cups->Profile != NULL)
  {
    free(cups->Profile);
    cups->Profile = NULL;
  }
#endif /* 0 */

  return (gdev_prn_close(pdev));
}


#ifdef dev_t_proc_encode_color
/*
 * 'cups_decode_color()' - Decode a color value.
 */

private int				/* O - Status (0 = OK) */
cups_decode_color(gx_device      *pdev,	/* I - Device info */
                  gx_color_index ci,	/* I - Color index */
                  gx_color_value *cv)	/* O - Colors */
{
  int			i;		/* Looping var */
  int			shift;		/* Bits to shift */
  int			mask;		/* Bits to mask */


  if (cups->header.cupsColorSpace == CUPS_CSPACE_KCMYcm &&
      cups->header.cupsBitsPerColor == 1)
  {
   /*
    * KCMYcm data is represented internally by Ghostscript as CMYK...
    */

    cv[0] = (ci & 0x20) ? frac_1 : frac_0;
    cv[1] = (ci & 0x12) ? frac_1 : frac_0;
    cv[2] = (ci & 0x09) ? frac_1 : frac_0;
    cv[3] = (ci & 0x04) ? frac_1 : frac_0;
  }
  else
  {
    shift = cups->header.cupsBitsPerColor;
    mask  = (1 << shift) - 1;

    for (i = cups->color_info.num_components - 1; i > 0; i --, ci >>= shift)
      cv[i] = cups->DecodeLUT[ci & mask];

    cv[0] = cups->DecodeLUT[ci & mask];
  }

  return (0);
}


/*
 * 'cups_encode_color()' - Encode a color value.
 */

private gx_color_index			/* O - Color index */
cups_encode_color(gx_device            *pdev,
					/* I - Device info */
                  const gx_color_value *cv)
					/* I - Colors */
{
  int			i;		/* Looping var */
  gx_color_index	ci;		/* Color index */
  int			shift;		/* Bits to shift */


 /*
  * Encode the color index...
  */

  shift = cups->header.cupsBitsPerColor;

  for (ci = cups->EncodeLUT[cv[0]], i = 1;
       i < cups->color_info.num_components;
       i ++)
    ci = (ci << shift) | cups->EncodeLUT[cv[i]];

#ifdef CUPS_DEBUG2
  dmprintf2(pdev->memory, "DEBUG2: cv[0]=%d -> %llx\n", cv[0], ci);
#endif /* CUPS_DEBUG2 */

 /*
  * Handle 6-color output...
  */

  if (cups->header.cupsColorSpace == CUPS_CSPACE_KCMYcm &&
      cups->header.cupsBitsPerColor == 1)
  {
   /*
    * Welcome to hackville, where we map CMYK data to the
    * light inks in draft mode...  Map blue to light magenta and
    * cyan and green to light cyan and yellow...
    */

    ci <<= 2;				/* Leave room for light inks */

    if (ci == 0x18)			/* Blue */
      ci = 0x11;			/* == cyan + light magenta */
    else if (ci == 0x14)		/* Green */
      ci = 0x06;			/* == light cyan + yellow */
  }

  /* The entire manner that cups does its color mapping needs some serious
     rework.  In the case of the output RGBW color space, it takes a source
     CMYK value which gs maps to RGB, cups then maps the RGB to CMYK and then
     from there to RGBW and finally it does an encode.  Unfortunately, the
     number of color values for RGBW is 3 since it is using an RGB ICC profile
     this means that the W mapping value from cups is lost in cmap_rgb_direct
     So here we ensure that the W is always set to on (else we end up with a
     blue background cast).  The ideal way
     to fix this is to move some of these odd color spaces of cups to the
     separation device model ensuring that things are handled properly. */
  if (cups->header.cupsColorSpace == CUPS_CSPACE_RGBW) {
      ci = (ci << shift) | cups->EncodeLUT[gx_max_color_value];
  }

 /*
  * Range check the return value...
  */

  if (ci == gx_no_color_index)
    ci --;

 /*
  * Return the color index...
  */

  return (ci);
}

/*
 * 'cups_get_color_comp_index()' - Color component to index
 */

#define compare_color_names(pname, name_size, name_str) \
    (name_size == (int)strlen(name_str) && strncasecmp(pname, name_str, name_size) == 0)

int                                     /* O - Index of the named color in
					   the color space */
cups_get_color_comp_index(gx_device * pdev, const char * pname,
			  int name_size, int component_type)
{
  switch (cups->header.cupsColorSpace)
  {
    case CUPS_CSPACE_K :
        if (compare_color_names(pname, name_size, "Black") ||
	    compare_color_names(pname, name_size, "Gray") ||
	    compare_color_names(pname, name_size, "Grey"))
	    return 0;
	else
	    return -1; /* Indicate that the component name is "unknown" */
        break;
    case CUPS_CSPACE_W :
    case CUPS_CSPACE_SW :
    case CUPS_CSPACE_WHITE :
        if (compare_color_names(pname, name_size, "White") ||
	    compare_color_names(pname, name_size, "Luminance") ||
	    compare_color_names(pname, name_size, "Gray") ||
	    compare_color_names(pname, name_size, "Grey"))
	    return 0;
	else
	    return -1;
        break;
    case CUPS_CSPACE_RGBA :
        if (compare_color_names(pname, name_size, "Alpha") ||
	    compare_color_names(pname, name_size, "Transparent") ||
	    compare_color_names(pname, name_size, "Transparency"))
	    return 3;
        /* fall through */
    case CUPS_CSPACE_RGBW :
        if (compare_color_names(pname, name_size, "Red"))
	    return 0;
	if (compare_color_names(pname, name_size, "Green"))
	    return 1;
	if (compare_color_names(pname, name_size, "Blue"))
            return 2;
	if (compare_color_names(pname, name_size, "White"))
            return 3;
	else
	    return -1;
        break;
    case CUPS_CSPACE_RGB :
    case CUPS_CSPACE_SRGB :
    case CUPS_CSPACE_ADOBERGB :
        if (compare_color_names(pname, name_size, "Red"))
	    return 0;
	if (compare_color_names(pname, name_size, "Green"))
	    return 1;
	if (compare_color_names(pname, name_size, "Blue"))
            return 2;
        break;
    case CUPS_CSPACE_CMYK :
#  ifdef CUPS_RASTER_HAVE_COLORIMETRIC
    case CUPS_CSPACE_CIEXYZ :
    case CUPS_CSPACE_CIELab :
    case CUPS_CSPACE_ICC1 :
    case CUPS_CSPACE_ICC2 :
    case CUPS_CSPACE_ICC3 :
    case CUPS_CSPACE_ICC4 :
    case CUPS_CSPACE_ICC5 :
    case CUPS_CSPACE_ICC6 :
    case CUPS_CSPACE_ICC7 :
    case CUPS_CSPACE_ICC8 :
    case CUPS_CSPACE_ICC9 :
    case CUPS_CSPACE_ICCA :
    case CUPS_CSPACE_ICCB :
    case CUPS_CSPACE_ICCC :
    case CUPS_CSPACE_ICCD :
    case CUPS_CSPACE_ICCE :
    case CUPS_CSPACE_ICCF :
#  endif /* CUPS_RASTER_HAVE_COLORIMETRIC */
        if (compare_color_names(pname, name_size, "Black"))
	    return 3;
        /* fall through */
    case CUPS_CSPACE_CMY :
        if (compare_color_names(pname, name_size, "Cyan"))
	    return 0;
	if (compare_color_names(pname, name_size, "Magenta"))
	    return 1;
	if (compare_color_names(pname, name_size, "Yellow"))
	    return 2;
	else
	    return -1;
        break;
    case CUPS_CSPACE_GMCS :
        if (compare_color_names(pname, name_size, "Silver") ||
	    compare_color_names(pname, name_size, "Silver Foil"))
	    return 3;
        /* fall through */
    case CUPS_CSPACE_GMCK :
        if (compare_color_names(pname, name_size, "Gold") ||
	    compare_color_names(pname, name_size, "Gold Foil"))
	    return 0;
        /* fall through */
    case CUPS_CSPACE_YMCK :
        if (compare_color_names(pname, name_size, "Black"))
	    return 3;
        /* fall through */
    case CUPS_CSPACE_YMC :
	if (compare_color_names(pname, name_size, "Yellow"))
	    return 0;
	if (compare_color_names(pname, name_size, "Magenta"))
	    return 1;
        if (compare_color_names(pname, name_size, "Cyan"))
	    return 2;
	else
	    return -1;
        break;
    case CUPS_CSPACE_KCMYcm :
        if (compare_color_names(pname, name_size, "Light Cyan") ||
	    compare_color_names(pname, name_size, "Photo Cyan"))
	    return 4;
        if (compare_color_names(pname, name_size, "Light Magenta") ||
	    compare_color_names(pname, name_size, "Photo Magenta"))
	    return 5;
    case CUPS_CSPACE_KCMY :
        if (compare_color_names(pname, name_size, "Black"))
	    return 0;
        if (compare_color_names(pname, name_size, "Cyan"))
	    return 1;
	if (compare_color_names(pname, name_size, "Magenta"))
	    return 2;
	if (compare_color_names(pname, name_size, "Yellow"))
	    return 3;
	else
	    return -1;
        break;
    case CUPS_CSPACE_GOLD :
        if (compare_color_names(pname, name_size, "Gold") ||
	    compare_color_names(pname, name_size, "Gold Foil"))
	    return 0;
	else
	    return -1;
        break;
    case CUPS_CSPACE_SILVER :
        if (compare_color_names(pname, name_size, "Silver") ||
	    compare_color_names(pname, name_size, "Silver Foil"))
	    return 0;
	else
	    return -1;
        break;
    default:
        break;
  }
  return -1;
}

/*
 * 'cups_get_color_mapping_procs()' - Get the list of color mapping procedures.
 */

private const gx_cm_color_map_procs *	/* O - List of device procedures */
cups_get_color_mapping_procs(const gx_device *pdev)
					/* I - Device info */
{
  return (&cups_color_mapping_procs);
}
#endif /* dev_t_proc_encode_color */


/*
 * 'cups_get_matrix()' - Generate the default page matrix.
 */

private void
cups_get_matrix(gx_device *pdev,	/* I - Device info */
                gs_matrix *pmat)	/* O - Physical transform matrix */
{
#ifdef CUPS_DEBUG2
  dmprintf2(pdev->memory, "DEBUG2: cups_get_matrix(%p, %p)\n", pdev, pmat);
#endif /* CUPS_DEBUG2 */

 /*
  * Set the raster width and height...
  */

  cups->header.cupsWidth  = cups->width;
  cups->header.cupsHeight = cups->height;

 /*
  * Set the transform matrix...
  */

  if (cups->landscape)
  {
   /*
    * Do landscape orientation...
    */
#ifdef CUPS_DEBUG2
    dprintf("DEBUG2: Landscape matrix: XX=0 XY=+1 YX=+1 YY=0\n");
#endif /* CUPS_DEBUG2 */
    pmat->xx = 0.0;
    pmat->xy = (float)cups->header.HWResolution[1] / 72.0;
    pmat->yx = (float)cups->header.HWResolution[0] / 72.0;
    pmat->yy = 0.0;
    pmat->tx = -(float)cups->header.HWResolution[0] * pdev->HWMargins[1] / 72.0;
    pmat->ty = -(float)cups->header.HWResolution[1] * pdev->HWMargins[0] / 72.0;
  }
  else
  {
   /*
    * Do portrait orientation...
    */
#ifdef CUPS_DEBUG2
    dmprintf(pdev->memory, "DEBUG2: Portrait matrix: XX=+1 XY=0 YX=0 YY=-1\n");
#endif /* CUPS_DEBUG2 */
    pmat->xx = (float)cups->header.HWResolution[0] / 72.0;
    pmat->xy = 0.0;
    pmat->yx = 0.0;
    pmat->yy = -(float)cups->header.HWResolution[1] / 72.0;
    pmat->tx = -(float)cups->header.HWResolution[0] * pdev->HWMargins[0] / 72.0;
    pmat->ty = (float)cups->header.HWResolution[1] *
               ((float)cups->header.PageSize[1] - pdev->HWMargins[3]) / 72.0;
  }

#ifdef CUPS_RASTER_SYNCv1
  if (cups->header.cupsBorderlessScalingFactor > 1.0)
  {
    pmat->xx *= cups->header.cupsBorderlessScalingFactor;
    pmat->xy *= cups->header.cupsBorderlessScalingFactor;
    pmat->yx *= cups->header.cupsBorderlessScalingFactor;
    pmat->yy *= cups->header.cupsBorderlessScalingFactor;
    pmat->tx *= cups->header.cupsBorderlessScalingFactor;
    pmat->ty *= cups->header.cupsBorderlessScalingFactor;
  }
#endif /* CUPS_RASTER_SYNCv1 */

#ifdef CUPS_DEBUG2
  dmprintf2(pdev->memory, "DEBUG2: width = %d, height = %d\n", cups->header.cupsWidth,
            cups->header.cupsHeight);
  dmprintf4(pdev->memory, "DEBUG2: PageSize = [ %d %d ], HWResolution = [ %d %d ]\n",
            cups->header.PageSize[0], cups->header.PageSize[1],
            cups->header.HWResolution[0], cups->header.HWResolution[1]);
  dmprintf4(pdev->memory, "DEBUG2: HWMargins = [ %.3f %.3f %.3f %.3f ]\n",
            pdev->HWMargins[0], pdev->HWMargins[1], pdev->HWMargins[2],
            pdev->HWMargins[3]);
  dmprintf6(pdev->memory, "DEBUG2: matrix = [ %.3f %.3f %.3f %.3f %.3f %.3f ]\n",
            pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty);
#endif /* CUPS_DEBUG2 */
}


/*
 * 'cups_get_params()' - Get pagedevice parameters.
 */

private int				/* O - Error status */
cups_get_params(gx_device     *pdev,	/* I - Device info */
                gs_param_list *plist)	/* I - Parameter list */
{
  int			code;		/* Return code */
  gs_param_string	s;		/* Temporary string value */
  bool			b;		/* Temporary boolean value */
#ifdef CUPS_RASTER_SYNCv1
  int			i;		/* Looping var */
#endif /* CUPS_RASTER_SYNCv1 */


#ifdef CUPS_DEBUG2
  dmprintf2(pdev->memory, "DEBUG2: cups_get_params(%p, %p)\n", pdev, plist);
#endif /* CUPS_DEBUG2 */

 /*
  * First process the "standard" page device parameters...
  */

#ifdef CUPS_DEBUG2
  dmprintf(pdev->memory, "DEBUG2: before gdev_prn_get_params()\n");
#endif /* CUPS_DEBUG2 */

  if ((code = gdev_prn_get_params(pdev, plist)) < 0)
    goto done;

#ifdef CUPS_DEBUG2
  dmprintf(pdev->memory, "DEBUG2: after gdev_prn_get_params()\n");
#endif /* CUPS_DEBUG2 */

 /*
  * Then write the CUPS parameters...
  */

  param_string_from_transient_string(s, cups->header.MediaClass);
  if ((code = param_write_string(plist, "MediaClass", &s)) < 0)
    goto done;

  param_string_from_transient_string(s, cups->header.MediaColor);
  if ((code = param_write_string(plist, "MediaColor", &s)) < 0)
    goto done;

  param_string_from_transient_string(s, cups->header.MediaType);
  if ((code = param_write_string(plist, "MediaType", &s)) < 0)
    goto done;

  param_string_from_transient_string(s, cups->header.OutputType);
  if ((code = param_write_string(plist, "OutputType", &s)) < 0)
    goto done;

  if ((code = param_write_int(plist, "AdvanceDistance",
                              (int *)&(cups->header.AdvanceDistance))) < 0)
    goto done;

  if ((code = param_write_int(plist, "AdvanceMedia",
                              (int *)&(cups->header.AdvanceMedia))) < 0)
    goto done;

  b = cups->header.Collate;
  if ((code = param_write_bool(plist, "Collate", &b)) < 0)
    goto done;

  if ((code = param_write_int(plist, "CutMedia",
                              (int *)&(cups->header.CutMedia))) < 0)
    goto done;

  b = cups->header.Duplex;
  if ((code = param_write_bool(plist, "Duplex", &b)) < 0)
    goto done;

  b = cups->header.InsertSheet;
  if ((code = param_write_bool(plist, "InsertSheet", &b)) < 0)
    goto done;

  if ((code = param_write_int(plist, "Jog",
                              (int *)&(cups->header.Jog))) < 0)
    goto done;

  b = cups->header.ManualFeed;
  if ((code = param_write_bool(plist, "ManualFeed", &b)) < 0)
    goto done;

  if ((code = param_write_int(plist, "MediaPosition",
                              (int *)&(cups->header.MediaPosition))) < 0)
    goto done;

  if ((code = param_write_int(plist, "MediaWeight",
                              (int *)&(cups->header.MediaWeight))) < 0)
    goto done;

  b = cups->header.MirrorPrint;
  if ((code = param_write_bool(plist, "MirrorPrint", &b)) < 0)
    goto done;

  b = cups->header.NegativePrint;
  if ((code = param_write_bool(plist, "NegativePrint", &b)) < 0)
    goto done;

  b = cups->header.OutputFaceUp;
  if ((code = param_write_bool(plist, "OutputFaceUp", &b)) < 0)
    goto done;

  b = cups->header.Separations;
  if ((code = param_write_bool(plist, "Separations", &b)) < 0)
    goto done;

  b = cups->header.TraySwitch;
  if ((code = param_write_bool(plist, "TraySwitch", &b)) < 0)
    goto done;

  b = cups->header.Tumble;
  if ((code = param_write_bool(plist, "Tumble", &b)) < 0)
    goto done;

#if 0 /* Don't include read-only parameters... */
  if ((code = param_write_int(plist, "cupsWidth",
                              (int *)&(cups->header.cupsWidth))) < 0)
    goto done;

  if ((code = param_write_int(plist, "cupsHeight",
                              (int *)&(cups->header.cupsHeight))) < 0)
    goto done;

  if ((code = param_write_int(plist, "cupsBitsPerPixel",
                              (int *)&(cups->header.cupsBitsPerPixel))) < 0)
    goto done;

  if ((code = param_write_int(plist, "cupsBytesPerLine",
                              (int *)&(cups->header.cupsBytesPerLine))) < 0)
    goto done;
#endif /* 0 */

  if ((code = param_write_int(plist, "cupsMediaType",
                              (int *)&(cups->header.cupsMediaType))) < 0)
    goto done;

  if ((code = param_write_int(plist, "cupsBitsPerColor",
                              (int *)&(cups->header.cupsBitsPerColor))) < 0)
    goto done;

  if ((code = param_write_int(plist, "cupsColorOrder",
                              (int *)&(cups->header.cupsColorOrder))) < 0)
    goto done;

  if ((code = param_write_int(plist, "cupsColorSpace",
                              (int *)&(cups->header.cupsColorSpace))) < 0)
    goto done;

  if ((code = param_write_int(plist, "cupsCompression",
                              (int *)&(cups->header.cupsCompression))) < 0)
    goto done;

  if ((code = param_write_int(plist, "cupsRowCount",
                              (int *)&(cups->header.cupsRowCount))) < 0)
    goto done;

  if ((code = param_write_int(plist, "cupsRowFeed",
                              (int *)&(cups->header.cupsRowFeed))) < 0)
    goto done;

  if ((code = param_write_int(plist, "cupsRowStep",
                              (int *)&(cups->header.cupsRowStep))) < 0)
    goto done;

#ifdef CUPS_RASTER_SYNCv1
#if 0 /* Don't include read-only parameters... */
  if ((code = param_write_int(plist, "cupsNumColors",
                              (int *)&(cups->header.cupsNumColors))) < 0)
    goto done;
#endif /* 0 */

  if ((code = param_write_float(plist, "cupsBorderlessScalingFactor",
                        	&(cups->header.cupsBorderlessScalingFactor))) < 0)
    goto done;

  for (i = 0; cups_Integer_strings[i] != NULL; i ++)
  {
    if ((code = param_write_int(plist, cups_Integer_strings[i],
                        	(int *)(cups->header.cupsInteger + i))) < 0)
      goto done;
  }

  for (i = 0; cups_Real_strings[i] != NULL; i ++)
  {
    if ((code = param_write_float(plist, cups_Real_strings[i],
                        	  cups->header.cupsReal + i)) < 0)
      goto done;
  }

  for (i = 0; cups_String_strings[i] != NULL; i ++)
  {
    param_string_from_transient_string(s, cups->header.cupsString[i]);
    if ((code = param_write_string(plist, cups_String_strings[i], &s)) < 0)
      goto done;
  }

  param_string_from_transient_string(s, cups->header.cupsMarkerType);
  if ((code = param_write_string(plist, "cupsMarkerType", &s)) < 0)
    goto done;

  param_string_from_transient_string(s, cups->header.cupsRenderingIntent);
  if ((code = param_write_string(plist, "cupsRenderingIntent", &s)) < 0)
    goto done;

  param_string_from_transient_string(s, cups->header.cupsPageSizeName);
  if ((code = param_write_string(plist, "cupsPageSizeName", &s)) < 0)
    goto done;
#endif /* CUPS_RASTER_SYNCv1 */

done:

#ifdef CUPS_DEBUG2
  dmprintf(pdev->memory, "DEBUG2: Leaving cups_get_params()\n");
#endif /* CUPS_DEBUG2 */

  return code;
}


/*
 * 'cups_get_space_params()' - Get space parameters from the RIP_CACHE env var.
 */

void
cups_get_space_params(const gx_device_printer *pdev,
					/* I - Printer device */
                      gdev_prn_space_params   *space_params)
					/* O - Space parameters */
{
  float	cache_size;			/* Size of tile cache in bytes */
  char	*cache_env,			/* Cache size environment variable */
	cache_units[255];		/* Cache size units */


#ifdef CUPS_DEBUG2
  dmprintf2(pdev->memory, "DEBUG2: cups_get_space_params(%p, %p)\n", pdev, space_params);
#endif /* CUPS_DEBUG2 */

  if ((cache_env = getenv("RIP_MAX_CACHE")) != NULL)
  {
    switch (sscanf(cache_env, "%f%254s", &cache_size, cache_units))
    {
      case 0 :
          return;
      case 1 :
          cache_size *= 4 * CUPS_TILE_SIZE * CUPS_TILE_SIZE;
	  break;
      case 2 :
          if (tolower(cache_units[0]) == 'g')
	    cache_size *= 1024 * 1024 * 1024;
          else if (tolower(cache_units[0]) == 'm')
	    cache_size *= 1024 * 1024;
	  else if (tolower(cache_units[0]) == 'k')
	    cache_size *= 1024;
	  else if (tolower(cache_units[0]) == 't')
	    cache_size *= 4 * CUPS_TILE_SIZE * CUPS_TILE_SIZE;
	  break;
    }
  }
  else
    return;

  if (cache_size == 0)
    return;

#ifdef CUPS_DEBUG2
  dmprintf1(pdev->memory, "DEBUG2: cache_size = %.0f\n", cache_size);
#endif /* CUPS_DEBUG2 */

  space_params->MaxBitmap   = (long)cache_size;
  space_params->BufferSpace = (long)cache_size;
}


/*
 * 'cups_map_cielab()' - Map CIE Lab transformation...
 */

static double				/* O - Adjusted color value */
cups_map_cielab(double x,		/* I - Raw color value */
                double xn)		/* I - Whitepoint color value */
{
  double x_xn;				/* Fraction of whitepoint */


  x_xn = x / xn;

  if (x_xn > 0.008856)
    return (cbrt(x_xn));
  else
    return (7.787 * x_xn + 16.0 / 116.0);
}


#ifdef dev_t_proc_encode_color
/*
 * 'cups_map_cmyk()' - Map a CMYK color value to device colors.
 */

private void
cups_map_cmyk(gx_device *pdev,		/* I - Device info */
              frac      c,		/* I - Cyan value */
	      frac      m,		/* I - Magenta value */
	      frac      y,		/* I - Yellow value */
	      frac      k,		/* I - Black value */
	      frac      *out)		/* O - Device colors */
{
  int	c0 = 0, c1 = 0,
        c2 = 0, c3 = 0;			/* Temporary color values */
  float	rr, rg, rb,			/* Real RGB colors */
	ciex, ciey, ciez,		/* CIE XYZ colors */
	ciey_yn,			/* Normalized luminance */
	ciel, ciea, cieb;		/* CIE Lab colors */


#ifdef CUPS_DEBUG2
  dmprintf6(pdev->memory, "DEBUG2: cups_map_cmyk(%p, %d, %d, %d, %d, %p)\n",
            pdev, c, m, y, k, out);
#endif /* CUPS_DEBUG2 */

 /*
  * Convert the CMYK color to the destination colorspace...
  */

  switch (cups->header.cupsColorSpace)
  {
    case CUPS_CSPACE_W :
    case CUPS_CSPACE_SW :
        c0 = (c * 31 + m * 61 + y * 8) / 100 + k;

	if (c0 < 0)
	  c0 = 0;
	else if (c0 > frac_1)
	  c0 = frac_1;
	out[0] = frac_1 - (frac)cups->Density[c0];
        break;

    case CUPS_CSPACE_RGBA :
        out[3] = frac_1;

    case CUPS_CSPACE_RGB :
    case CUPS_CSPACE_SRGB :
    case CUPS_CSPACE_ADOBERGB :
    case CUPS_CSPACE_RGBW :
        c0 = c + k;
        c1 = m + k;
        c2 = y + k;
        if (cups->header.cupsColorSpace == CUPS_CSPACE_RGBW) {
	  if ((k >= frac_1 - 1) ||
	      ((c0 >= frac_1) && (c1 >= frac_1) && (c2 >= frac_1))) {
	    c0 = frac_1;
	    c1 = frac_1;
	    c2 = frac_1;
	    c3 = frac_1;
	  } else
	    c3 = 0;
	}

        if (c0 < 0)
	  c0 = 0;
	else if (c0 > frac_1)
	  c0 = frac_1;
	out[0] = frac_1 - (frac)cups->Density[c0];

        if (c1 < 0)
	  c1 = 0;
	else if (c1 > frac_1)
	  c1 = frac_1;
	out[1] = frac_1 - (frac)cups->Density[c1];

        if (c2 < 0)
	  c2 = 0;
	else if (c2 > frac_1)
	  c2 = frac_1;
	out[2] = frac_1 - (frac)cups->Density[c2];

        if (cups->header.cupsColorSpace == CUPS_CSPACE_RGBW) {
	  if (c3 == 0)
	    out[3] = frac_1;
	  else if (c3 == frac_1)
	    out[3] = 0;
	}
        break;

    default :
    case CUPS_CSPACE_K :
        c0 = (c * 31 + m * 61 + y * 8) / 100 + k;

	if (c0 < 0)
	  out[0] = 0;
	else if (c0 > frac_1)
	  out[0] = (frac)cups->Density[frac_1];
	else
	  out[0] = (frac)cups->Density[c0];
        break;

    case CUPS_CSPACE_CMY :
        c0 = c + k;
	c1 = m + k;
	c2 = y + k;

        if (c0 < 0)
	  out[0] = 0;
	else if (c0 > frac_1)
	  out[0] = (frac)cups->Density[frac_1];
	else
	  out[0] = (frac)cups->Density[c0];

        if (c1 < 0)
	  out[1] = 0;
	else if (c1 > frac_1)
	  out[1] = (frac)cups->Density[frac_1];
	else
	  out[1] = (frac)cups->Density[c1];

        if (c2 < 0)
	  out[2] = 0;
	else if (c2 > frac_1)
	  out[2] = (frac)cups->Density[frac_1];
	else
	  out[2] = (frac)cups->Density[c2];
        break;

    case CUPS_CSPACE_YMC :
        c0 = y + k;
	c1 = m + k;
	c2 = c + k;

        if (c0 < 0)
	  out[0] = 0;
	else if (c0 > frac_1)
	  out[0] = (frac)cups->Density[frac_1];
	else
	  out[0] = (frac)cups->Density[c0];

        if (c1 < 0)
	  out[1] = 0;
	else if (c1 > frac_1)
	  out[1] = (frac)cups->Density[frac_1];
	else
	  out[1] = (frac)cups->Density[c1];

        if (c2 < 0)
	  out[2] = 0;
	else if (c2 > frac_1)
	  out[2] = (frac)cups->Density[frac_1];
	else
	  out[2] = (frac)cups->Density[c2];
        break;

    case CUPS_CSPACE_CMYK :
        if (c < 0)
	  out[0] = 0;
	else if (c > frac_1)
	  out[0] = (frac)cups->Density[frac_1];
	else
	  out[0] = (frac)cups->Density[c];

        if (m < 0)
	  out[1] = 0;
	else if (m > frac_1)
	  out[1] = (frac)cups->Density[frac_1];
	else
	  out[1] = (frac)cups->Density[m];

        if (y < 0)
	  out[2] = 0;
	else if (y > frac_1)
	  out[2] = (frac)cups->Density[frac_1];
	else
	  out[2] = (frac)cups->Density[y];

        if (k < 0)
	  out[3] = 0;
	else if (k > frac_1)
	  out[3] = (frac)cups->Density[frac_1];
	else
	  out[3] = (frac)cups->Density[k];
        break;

    case CUPS_CSPACE_YMCK :
    case CUPS_CSPACE_GMCK :
    case CUPS_CSPACE_GMCS :
        if (y < 0)
	  out[0] = 0;
	else if (y > frac_1)
	  out[0] = (frac)cups->Density[frac_1];
	else
	  out[0] = (frac)cups->Density[y];

        if (m < 0)
	  out[1] = 0;
	else if (m > frac_1)
	  out[1] = (frac)cups->Density[frac_1];
	else
	  out[1] = (frac)cups->Density[m];

        if (c < 0)
	  out[2] = 0;
	else if (c > frac_1)
	  out[2] = (frac)cups->Density[frac_1];
	else
	  out[2] = (frac)cups->Density[c];

        if (k < 0)
	  out[3] = 0;
	else if (k > frac_1)
	  out[3] = (frac)cups->Density[frac_1];
	else
	  out[3] = (frac)cups->Density[k];
        break;

    case CUPS_CSPACE_KCMYcm :
    case CUPS_CSPACE_KCMY :
        if (k < 0)
	  out[0] = 0;
	else if (k > frac_1)
	  out[0] = (frac)cups->Density[frac_1];
	else
	  out[0] = (frac)cups->Density[k];

        if (c < 0)
	  out[1] = 0;
	else if (c > frac_1)
	  out[1] = (frac)cups->Density[frac_1];
	else
	  out[1] = (frac)cups->Density[c];

        if (m < 0)
	  out[2] = 0;
	else if (m > frac_1)
	  out[2] = (frac)cups->Density[frac_1];
	else
	  out[2] = (frac)cups->Density[m];

        if (y < 0)
	  out[3] = 0;
	else if (y > frac_1)
	  out[3] = (frac)cups->Density[frac_1];
	else
	  out[3] = (frac)cups->Density[y];
        break;

#  ifdef CUPS_RASTER_HAVE_COLORIMETRIC
    case CUPS_CSPACE_CIEXYZ :
    case CUPS_CSPACE_CIELab :
    case CUPS_CSPACE_ICC1 :
    case CUPS_CSPACE_ICC2 :
    case CUPS_CSPACE_ICC3 :
    case CUPS_CSPACE_ICC4 :
    case CUPS_CSPACE_ICC5 :
    case CUPS_CSPACE_ICC6 :
    case CUPS_CSPACE_ICC7 :
    case CUPS_CSPACE_ICC8 :
    case CUPS_CSPACE_ICC9 :
    case CUPS_CSPACE_ICCA :
    case CUPS_CSPACE_ICCB :
    case CUPS_CSPACE_ICCC :
    case CUPS_CSPACE_ICCD :
    case CUPS_CSPACE_ICCE :
    case CUPS_CSPACE_ICCF :
       /*
        * Convert CMYK to sRGB...
	*/

        c0 = frac_1 - c - k;
	c1 = frac_1 - m - k;
	c2 = frac_1 - y - k;

        if (c0 < 0)
	  c0 = 0;

        if (c1 < 0)
	  c1 = 0;

        if (c2 < 0)
	  c2 = 0;

       /*
        * Convert sRGB to linear RGB...
	*/

	rr = pow(((double)c0 / (double)frac_1 + 0.055) / 1.055, 2.4);
	rg = pow(((double)c1 / (double)frac_1 + 0.055) / 1.055, 2.4);
	rb = pow(((double)c2 / (double)frac_1 + 0.055) / 1.055, 2.4);

       /*
        * Convert to CIE XYZ...
	*/

	ciex = 0.412453 * rr + 0.357580 * rg + 0.180423 * rb;
	ciey = 0.212671 * rr + 0.715160 * rg + 0.072169 * rb;
	ciez = 0.019334 * rr + 0.119193 * rg + 0.950227 * rb;

        if (cups->header.cupsColorSpace == CUPS_CSPACE_CIEXYZ)
	{
	 /*
	  * Convert to an integer XYZ color value...
	  */

          if (cups->header.cupsBitsPerColor == 8)
	  {
	    if (ciex <= 0.0f)
	      c0 = 0;
	    else if (ciex < 1.1)
	      c0 = (int)(ciex * 231.8181 + 0.5);
	    else
	      c0 = 255;

	    if (ciey <= 0.0f)
	      c1 = 0;
	    else if (ciey < 1.1)
	      c1 = (int)(ciey * 231.8181 + 0.5);
	    else
	      c1 = 255;

	    if (ciez <= 0.0f)
	      c2 = 0;
	    else if (ciez < 1.1)
	      c2 = (int)(ciez * 231.8181 + 0.5);
	    else
	      c2 = 255;
	  }
	  else
	  {
	    if (ciex <= 0.0f)
	      c0 = 0;
	    else if (ciex < 1.1)
	      c0 = (int)(ciex * 59577.2727 + 0.5);
	    else
	      c0 = 65535;

	    if (ciey <= 0.0f)
	      c1 = 0;
	    else if (ciey < 1.1)
	      c1 = (int)(ciey * 59577.2727 + 0.5);
	    else
	      c1 = 65535;

	    if (ciez <= 0.0f)
	      c2 = 0;
	    else if (ciez < 1.1)
	      c2 = (int)(ciez * 59577.2727 + 0.5);
	    else
	      c2 = 65535;
	  }
	}
	else
	{
	 /*
	  * Convert CIE XYZ to Lab...
	  */

	  ciey_yn = ciey / D65_Y;

	  if (ciey_yn > 0.008856)
	    ciel = 116 * cbrt(ciey_yn) - 16;
	  else
	    ciel = 903.3 * ciey_yn;

	  ciea = 500 * (cups_map_cielab(ciex, D65_X) -
	                cups_map_cielab(ciey, D65_Y));
	  cieb = 200 * (cups_map_cielab(ciey, D65_Y) -
	                cups_map_cielab(ciez, D65_Z));

          if (cups->header.cupsBitsPerColor == 8)
	  {
           /*
	    * Scale the L value and bias the a and b values by 128
	    * so that all values are in the range of 0 to 255.
	    */

	    ciel = ciel * 2.55 + 0.5;
	    ciea += 128.5;
	    cieb += 128.5;

	    if (ciel <= 0.0)
	      c0 = 0;
	    else if (ciel < 255.0)
	      c0 = (int)ciel;
	    else
	      c0 = 255;

	    if (ciea <= 0.0)
	      c1 = 0;
	    else if (ciea < 255.0)
	      c1 = (int)ciea;
	    else
	      c1 = 255;

	    if (cieb <= 0.0)
	      c2 = 0;
	    else if (cieb < 255.0)
	      c2 = (int)cieb;
	    else
	      c2 = 255;
          }
	  else
	  {
	   /*
	    * Scale the L value and bias the a and b values by 128 so that all
	    * numbers are from 0 to 65535.
	    */

	    ciel = ciel * 655.35 + 0.5;
	    ciea = (ciea + 128.0) * 256.0 + 0.5;
	    cieb = (cieb + 128.0) * 256.0 + 0.5;

	   /*
	    * Output 16-bit values...
	    */

	    if (ciel <= 0.0)
	      c0 = 0;
	    else if (ciel < 65535.0)
	      c0 = (int)ciel;
	    else
	      c0 = 65535;

	    if (ciea <= 0.0)
	      c1 = 0;
	    else if (ciea < 65535.0)
	      c1 = (int)ciea;
	    else
	      c1 = 65535;

	    if (cieb <= 0.0)
	      c2 = 0;
	    else if (cieb < 65535.0)
	      c2 = (int)cieb;
	    else
	      c2 = 65535;
	  }
	}

        out[0] = cups->DecodeLUT[c0];
        out[1] = cups->DecodeLUT[c1];
        out[2] = cups->DecodeLUT[c2];
        break;
#  endif /* CUPS_RASTER_HAVE_COLORIMETRIC */
  }

#ifdef CUPS_DEBUG2
  switch (cups->color_info.num_components)
  {
    default :
    case 1 :
        dmprintf1(pdev->memory, "DEBUG2:   \\=== COLOR %d\n", out[0]);
	break;

    case 3 :
        dmprintf3(pdev->memory, "DEBUG2:   \\=== COLOR %d, %d, %d\n",
                  out[0], out[1], out[2]);
	break;

    case 4 :
        dmprintf4(pdev->memory, "DEBUG2:   \\=== COLOR %d, %d, %d, %d\n",
                  out[0], out[1], out[2], out[3]);
	break;
  }
#endif /* CUPS_DEBUG2 */
}


/*
 * 'cups_map_gray()' - Map a grayscale value to device colors.
 */

private void
cups_map_gray(gx_device *pdev,		/* I - Device info */
              frac      g,		/* I - Grayscale value */
	      frac      *out)		/* O - Device colors */
{
#ifdef CUPS_DEBUG22
  dmprintf3(pdev->memory, "DEBUG2: cups_map_gray(%p, %d, %p)\n",
            pdev, g, out);
#endif /* CUPS_DEBUG22 */

 /*
  * Just use the CMYK mapper...
  */

  cups_map_cmyk(pdev, 0, 0, 0, frac_1 - g, out);
}


/*
 * 'cups_map_rgb()' - Map a RGB color value to device colors.
 */

private void
cups_map_rgb(gx_device             *pdev,
					/* I - Device info */
             const gs_gstate        *pgs,/* I - Device state */
             frac                  r,	/* I - Red value */
	     frac                  g,	/* I - Green value */
	     frac                  b,	/* I - Blue value */
	     frac                  *out)/* O - Device colors */
{
  frac		c, m, y, k;		/* CMYK values */
  frac		mk;			/* Maximum K value */
  int		tc, tm, ty;		/* Temporary color values */


#ifdef CUPS_DEBUG2
  dmprintf6(pdev->memory, "DEBUG2: cups_map_rgb(%p, %p, %d, %d, %d, %p)\n",
            pdev, pgs, r, g, b, out);
#endif /* CUPS_DEBUG2 */

 /*
  * Compute CMYK values...
  */

  c = frac_1 - r;
  m = frac_1 - g;
  y = frac_1 - b;
  k = min(c, min(m, y));

  if ((mk = max(c, max(m, y))) > k)
    k = (int)((float)k * (float)k * (float)k / ((float)mk * (float)mk));

  c -= k;
  m -= k;
  y -= k;

 /*
  * Do color correction as needed...
  */

  if (cups->HaveProfile)
  {
   /*
    * Color correct CMY...
    */

    tc = cups->Matrix[0][0][c] +
         cups->Matrix[0][1][m] +
	 cups->Matrix[0][2][y];
    tm = cups->Matrix[1][0][c] +
         cups->Matrix[1][1][m] +
	 cups->Matrix[1][2][y];
    ty = cups->Matrix[2][0][c] +
         cups->Matrix[2][1][m] +
	 cups->Matrix[2][2][y];

    if (tc < 0)
      c = 0;
    else if (tc > frac_1)
      c = frac_1;
    else
      c = (frac)tc;

    if (tm < 0)
      m = 0;
    else if (tm > frac_1)
      m = frac_1;
    else
      m = (frac)tm;

    if (ty < 0)
      y = 0;
    else if (ty > frac_1)
      y = frac_1;
    else
      y = (frac)ty;
  }

 /*
  * Use the CMYK mapping function to produce the device colors...
  */

  cups_map_cmyk(pdev, c, m, y, k, out);
}
#else
/*
 * 'cups_map_cmyk_color()' - Map a CMYK color to a color index.
 *
 * This function is only called when a 4 or 6 color colorspace is
 * selected for output.  CMYK colors are *not* corrected but *are*
 * density adjusted.
 */

private gx_color_index			/* O - Color index */
cups_map_cmyk_color(gx_device      *pdev,
					/* I - Device info */
                    const gx_color_value cv[4])/* I - CMYK color values */
{
  gx_color_index	i;		/* Temporary index */
  gx_color_value	c, m, y, k;
  gx_color_value	ic, im, iy, ik;	/* Integral CMYK values */

  c = cv[0];
  m = cv[1];
  y = cv[2];
  k = cv[3];

#ifdef CUPS_DEBUG2
  dmprintf5(pdev->memory, "DEBUG2: cups_map_cmyk_color(%p, %d, %d, %d, %d)\n",
            pdev, c, m, y, k);
#endif /* CUPS_DEBUG2 */

 /*
  * Setup the color info data as needed...
  */

  if (pdev->color_info.num_components == 0) {
    if (cups_set_color_info(pdev) < 0)
      return(gx_no_color_index);
  }

 /*
  * Density correct...
  */

  if (cups->HaveProfile)
  {
    c = cups->Density[c];
    m = cups->Density[m];
    y = cups->Density[y];
    k = cups->Density[k];
  }

  ic = cups->EncodeLUT[c];
  im = cups->EncodeLUT[m];
  iy = cups->EncodeLUT[y];
  ik = cups->EncodeLUT[k];

 /*
  * Convert the CMYK color to a color index...
  */

  switch (cups->header.cupsColorSpace)
  {
    default :
        switch (cups->header.cupsBitsPerColor)
        {
          default :
              i = (((((ic << 1) | im) << 1) | iy) << 1) | ik;
              break;
          case 2 :
              i = (((((ic << 2) | im) << 2) | iy) << 2) | ik;
              break;
          case 4 :
              i = (((((ic << 4) | im) << 4) | iy) << 4) | ik;
              break;
          case 8 :
              i = (((((ic << 8) | im) << 8) | iy) << 8) | ik;
              break;
#ifdef GX_COLOR_INDEX_TYPE
	  case 16 :
	      i = (((((ic << 16) | im) << 16) | iy) << 16) | ik;
	      break;
#endif /* GX_COLOR_INDEX_TYPE */
        }
        break;

    case CUPS_CSPACE_YMCK :
    case CUPS_CSPACE_GMCK :
    case CUPS_CSPACE_GMCS :
        switch (cups->header.cupsBitsPerColor)
        {
          default :
              i = (((((iy << 1) | im) << 1) | ic) << 1) | ik;
              break;
          case 2 :
              i = (((((iy << 2) | im) << 2) | ic) << 2) | ik;
              break;
          case 4 :
              i = (((((iy << 4) | im) << 4) | ic) << 4) | ik;
              break;
          case 8 :
              i = (((((iy << 8) | im) << 8) | ic) << 8) | ik;
              break;
#ifdef GX_COLOR_INDEX_TYPE
	  case 16 :
	      i = (((((iy << 16) | im) << 16) | ic) << 16) | ik;
	      break;
#endif /* GX_COLOR_INDEX_TYPE */
        }
        break;

    case CUPS_CSPACE_KCMYcm :
        if (cups->header.cupsBitsPerColor == 1)
	{
	  if (ik)
	    i = 32;
	  else
	    i = 0;

	  if (ic && im)
	    i |= 17;
	  else if (ic && iy)
	    i |= 6;
	  else if (im && iy)
	    i |= 12;
	  else if (ic)
	    i |= 16;
	  else if (im)
	    i |= 8;
	  else if (iy)
	    i |= 4;
	  break;
	}

    case CUPS_CSPACE_KCMY :
        switch (cups->header.cupsBitsPerColor)
        {
          default :
              i = (((((ik << 1) | ic) << 1) | im) << 1) | iy;
              break;
          case 2 :
              i = (((((ik << 2) | ic) << 2) | im) << 2) | iy;
              break;
          case 4 :
              i = (((((ik << 4) | ic) << 4) | im) << 4) | iy;
              break;
          case 8 :
              i = (((((ik << 8) | ic) << 8) | im) << 8) | iy;
              break;
#ifdef GX_COLOR_INDEX_TYPE
	  case 16 :
	      i = (((((ik << 16) | ic) << 16) | im) << 16) | iy;
	      break;
#endif /* GX_COLOR_INDEX_TYPE */
        }
        break;
  }

#ifdef CUPS_DEBUG2
  dmprintf9(pdev->memory, "DEBUG2: CMYK (%d,%d,%d,%d) -> CMYK %08x (%d,%d,%d,%d)\n",
            c, m, y, k, (unsigned)i, ic, im, iy, ik);
#endif /* CUPS_DEBUG2 */

 /*
  * Make sure we don't get a CMYK color of 255, 255, 255, 255...
  */

  if (i == gx_no_color_index)
    i --;

  return (i);
}


/*
 * 'cups_map_color_rgb()' - Map a color index to an RGB color.
 */

private int
cups_map_color_rgb(gx_device      *pdev,/* I - Device info */
                   gx_color_index color,/* I - Color index */
		   gx_color_value prgb[3])
					/* O - RGB values */
{
  unsigned char		c0, c1, c2, c3;	/* Color index components */
  gx_color_value	c, m, y, k, divk; /* Colors, Black & divisor */


#ifdef CUPS_DEBUG2
  dmprintf3(pdev->memory, "DEBUG2: cups_map_color_rgb(%p, %d, %p)\n", pdev,
            (unsigned)color, prgb);
#endif /* CUPS_DEBUG2 */

 /*
  * Setup the color info data as needed...
  */

  if (pdev->color_info.num_components == 0) {
    if (cups_set_color_info(pdev) < 0)
      return(gx_no_color_index);
  }

#ifdef CUPS_DEBUG2
  dmprintf1(pdev->memory, "DEBUG2: COLOR %08x = ", (unsigned)color);
#endif /* CUPS_DEBUG2 */

 /*
  * Extract the color components from the color index...
  */

  switch (cups->header.cupsBitsPerColor)
  {
    default :
        c3 = color & 1;
        color >>= 1;
        c2 = color & 1;
        color >>= 1;
        c1 = color & 1;
        color >>= 1;
        c0 = color;
        break;
    case 2 :
        c3 = color & 3;
        color >>= 2;
        c2 = color & 3;
        color >>= 2;
        c1 = color & 3;
        color >>= 2;
        c0 = color;
        break;
    case 4 :
        c3 = color & 15;
        color >>= 4;
        c2 = color & 15;
        color >>= 4;
        c1 = color & 15;
        color >>= 4;
        c0 = color;
        break;
    case 8 :
        c3 = color & 255;
        color >>= 8;
        c2 = color & 255;
        color >>= 8;
        c1 = color & 255;
        color >>= 8;
        c0 = color;
        break;
#ifdef GX_COLOR_INDEX_TYPE
    case 16 :
        c3 = color & 0xffff;
        color >>= 16;
        c2 = color & 0xffff;
        color >>= 16;
        c1 = color & 0xffff;
        color >>= 16;
        c0 = color;
        break;
#endif /* GX_COLOR_INDEX_TYPE */
  }

 /*
  * Convert the color components to RGB...
  */

  switch (cups->header.cupsColorSpace)
  {
    case CUPS_CSPACE_K :
    case CUPS_CSPACE_WHITE :
    case CUPS_CSPACE_GOLD :
    case CUPS_CSPACE_SILVER :
        prgb[0] =
        prgb[1] =
        prgb[2] = cups->DecodeLUT[c3];
        break;

    case CUPS_CSPACE_W :
    case CUPS_CSPACE_SW :
        prgb[0] =
        prgb[1] =
        prgb[2] = cups->DecodeLUT[c3];
        break;

    case CUPS_CSPACE_RGB :
    case CUPS_CSPACE_SRGB :
    case CUPS_CSPACE_ADOBERGB :
        prgb[0] = cups->DecodeLUT[c1];
        prgb[1] = cups->DecodeLUT[c2];
        prgb[2] = cups->DecodeLUT[c3];
        break;

    case CUPS_CSPACE_RGBA :
        prgb[0] = cups->DecodeLUT[c0];
        prgb[1] = cups->DecodeLUT[c1];
        prgb[2] = cups->DecodeLUT[c2];
        break;

    case CUPS_CSPACE_CMY :
        prgb[0] = cups->DecodeLUT[c1];
        prgb[1] = cups->DecodeLUT[c2];
        prgb[2] = cups->DecodeLUT[c3];
        break;

    case CUPS_CSPACE_YMC :
        prgb[0] = cups->DecodeLUT[c3];
        prgb[1] = cups->DecodeLUT[c2];
        prgb[2] = cups->DecodeLUT[c1];
        break;

    case CUPS_CSPACE_KCMY :
    case CUPS_CSPACE_KCMYcm :
        k    = cups->DecodeLUT[c0];
        divk = gx_max_color_value - k;
        if (divk == 0)
        {
          prgb[0] = 0;
          prgb[1] = 0;
          prgb[2] = 0;
        }
        else
        {
          prgb[0] = gx_max_color_value + divk -
                    gx_max_color_value * c1 / divk;
          prgb[1] = gx_max_color_value + divk -
                    gx_max_color_value * c2 / divk;
          prgb[2] = gx_max_color_value + divk -
                    gx_max_color_value * c3 / divk;
        }
        break;

    case CUPS_CSPACE_RGBW :
       /*
        * cups->DecodeLUT actually maps to RGBW, not CMYK...
	*/

        if (c3 == 0) {
	  c = 0;
	  m = 0;
	  y = 0;
	} else {
	  c = cups->DecodeLUT[c0];
	  m = cups->DecodeLUT[c1];
	  y = cups->DecodeLUT[c2];
	}

        if (c > gx_max_color_value)
	  prgb[0] = gx_max_color_value;
	else if (c < 0)
          prgb[0] = 0;
        else
	  prgb[0] = c;

        if (m > gx_max_color_value)
	  prgb[1] = gx_max_color_value;
        else if (m < 0)
          prgb[1] = 0;
	else
	  prgb[1] = m;

        if (y > gx_max_color_value)
	  prgb[2] = gx_max_color_value;
	else if (y < 0)
          prgb[2] = 0;
        else
	  prgb[2] = y;
        break;

    case CUPS_CSPACE_CMYK :
        k    = cups->DecodeLUT[c3];
        divk = gx_max_color_value - k;
        if (divk == 0)
        {
          prgb[0] = 0;
          prgb[1] = 0;
          prgb[2] = 0;
        }
        else
        {
          prgb[0] = gx_max_color_value + divk -
                    gx_max_color_value * c0 / divk;
          prgb[1] = gx_max_color_value + divk -
                    gx_max_color_value * c1 / divk;
          prgb[2] = gx_max_color_value + divk -
                    gx_max_color_value * c2 / divk;
        }
        break;

    case CUPS_CSPACE_YMCK :
    case CUPS_CSPACE_GMCK :
    case CUPS_CSPACE_GMCS :
        k    = cups->DecodeLUT[c3];
        divk = gx_max_color_value - k;
        if (divk == 0)
        {
          prgb[0] = 0;
          prgb[1] = 0;
          prgb[2] = 0;
        }
        else
        {
          prgb[0] = gx_max_color_value + divk -
                    gx_max_color_value * c2 / divk;
          prgb[1] = gx_max_color_value + divk -
                    gx_max_color_value * c1 / divk;
          prgb[2] = gx_max_color_value + divk -
                    gx_max_color_value * c0 / divk;
        }
        break;

#  ifdef CUPS_RASTER_HAVE_COLORIMETRIC
    case CUPS_CSPACE_CIEXYZ :
    case CUPS_CSPACE_CIELab :
    case CUPS_CSPACE_ICC1 :
    case CUPS_CSPACE_ICC2 :
    case CUPS_CSPACE_ICC3 :
    case CUPS_CSPACE_ICC4 :
    case CUPS_CSPACE_ICC5 :
    case CUPS_CSPACE_ICC6 :
    case CUPS_CSPACE_ICC7 :
    case CUPS_CSPACE_ICC8 :
    case CUPS_CSPACE_ICC9 :
    case CUPS_CSPACE_ICCA :
    case CUPS_CSPACE_ICCB :
    case CUPS_CSPACE_ICCC :
    case CUPS_CSPACE_ICCD :
    case CUPS_CSPACE_ICCE :
    case CUPS_CSPACE_ICCF :
        break;
#  endif /* CUPS_RASTER_HAVE_COLORIMETRIC */
  }

#ifdef CUPS_DEBUG2
  dmprintf3(pdev->memory, "DEBUG2: RGB values: %d,%d,%d\n",
            prgb[0], prgb[1], prgb[2]);
#endif /* CUPS_DEBUG2 */

  return (0);
}


/*
 * 'cups_map_rgb_color()' - Map an RGB color to a color index.  We map the
 *                          RGB color to the output colorspace & bits (we
 *                          figure out the format when we output a page).
 */

private gx_color_index			/* O - Color index */
cups_map_rgb_color(gx_device      *pdev,/* I - Device info */
                   const gx_color_value cv[3])/* I - RGB color values */
{
  gx_color_index	i;		/* Temporary index */
  gx_color_value        r, g, b;
  gx_color_value	ic, im, iy, ik;	/* Integral CMYK values */
  gx_color_value	mk;		/* Maximum K value */
  int			tc, tm, ty;	/* Temporary color values */
  float			rr, rg, rb,	/* Real RGB colors */
			ciex, ciey, ciez,
					/* CIE XYZ colors */
			ciey_yn,	/* Normalized luminance */
			ciel, ciea, cieb;
					/* CIE Lab colors */

  r = cv[0];
  g = cv[1];
  b = cv[2];

#ifdef CUPS_DEBUG2
  dmprintf4(pdev->memory, "DEBUG2: cups_map_rgb_color(%p, %d, %d, %d)\n",
            pdev, r, g, b);
#endif /* CUPS_DEBUG2 */

 /*
  * Setup the color info data as needed...
  */

  if (pdev->color_info.num_components == 0) {
    if (cups_set_color_info(pdev) < 0)
        return(gx_no_color_index);
  }

 /*
  * Do color correction as needed...
  */

  if (cups->HaveProfile)
  {
   /*
    * Compute CMYK values...
    */

    ic = gx_max_color_value - r;
    im = gx_max_color_value - g;
    iy = gx_max_color_value - b;
    ik = min(ic, min(im, iy));

    if ((mk = max(ic, max(im, iy))) > ik)
      ik = (int)((float)ik * (float)ik * (float)ik / ((float)mk * (float)mk));

    ic -= ik;
    im -= ik;
    iy -= ik;

   /*
    * Color correct CMY...
    */

    tc = cups->Matrix[0][0][ic] +
         cups->Matrix[0][1][im] +
	 cups->Matrix[0][2][iy] +
	 ik;
    tm = cups->Matrix[1][0][ic] +
         cups->Matrix[1][1][im] +
	 cups->Matrix[1][2][iy] +
	 ik;
    ty = cups->Matrix[2][0][ic] +
         cups->Matrix[2][1][im] +
	 cups->Matrix[2][2][iy] +
	 ik;

   /*
    * Density correct combined CMYK...
    */

    if (tc < 0)
      r = gx_max_color_value;
    else if (tc > gx_max_color_value)
      r = gx_max_color_value - cups->Density[gx_max_color_value];
    else
      r = gx_max_color_value - cups->Density[tc];

    if (tm < 0)
      g = gx_max_color_value;
    else if (tm > gx_max_color_value)
      g = gx_max_color_value - cups->Density[gx_max_color_value];
    else
      g = gx_max_color_value - cups->Density[tm];

    if (ty < 0)
      b = gx_max_color_value;
    else if (ty > gx_max_color_value)
      b = gx_max_color_value - cups->Density[gx_max_color_value];
    else
      b = gx_max_color_value - cups->Density[ty];
  }

 /*
  * Convert the RGB color to a color index...
  */

  switch (cups->header.cupsColorSpace)
  {
    case CUPS_CSPACE_W :
    case CUPS_CSPACE_SW :
        i = cups->EncodeLUT[(r * 31 + g * 61 + b * 8) / 100];
        break;

    case CUPS_CSPACE_RGB :
    case CUPS_CSPACE_SRGB :
    case CUPS_CSPACE_ADOBERGB :
        ic = cups->EncodeLUT[r];
        im = cups->EncodeLUT[g];
        iy = cups->EncodeLUT[b];

        switch (cups->header.cupsBitsPerColor)
        {
          default :
              i = (((ic << 1) | im) << 1) | iy;
              break;
          case 2 :
              i = (((ic << 2) | im) << 2) | iy;
              break;
          case 4 :
              i = (((ic << 4) | im) << 4) | iy;
              break;
          case 8 :
              i = (((ic << 8) | im) << 8) | iy;
              break;
#ifdef GX_COLOR_INDEX_TYPE
	  case 16 :
	      i = (((ic << 16) | im) << 16) | iy;
	      break;
#endif /* GX_COLOR_INDEX_TYPE */
        }
        break;

    case CUPS_CSPACE_RGBW :
        if (!r && !g && !b)
	{
	 /*
	  * Map black to W...
	  */

          switch (cups->header.cupsBitsPerColor)
          {
            default :
        	i = 0x00;
        	break;
            case 2 :
        	i = 0x00;
        	break;
            case 4 :
        	i = 0x0000;
        	break;
            case 8 :
        	i = 0x00000000;
        	break;
#ifdef GX_COLOR_INDEX_TYPE
	    case 16 :
		i = 0x0000000000000000;
		break;
#endif /* GX_COLOR_INDEX_TYPE */
          }
	  break;
	}

    case CUPS_CSPACE_RGBA :
        ic = cups->EncodeLUT[r];
        im = cups->EncodeLUT[g];
        iy = cups->EncodeLUT[b];

        switch (cups->header.cupsBitsPerColor)
        {
          default :
              i = (((((ic << 1) | im) << 1) | iy) << 1) | 0x01;
              break;
          case 2 :
              i = (((((ic << 2) | im) << 2) | iy) << 2) | 0x03;
              break;
          case 4 :
              i = (((((ic << 4) | im) << 4) | iy) << 4) | 0x0f;
              break;
          case 8 :
              i = (((((ic << 8) | im) << 8) | iy) << 8) | 0xff;
              break;
#ifdef GX_COLOR_INDEX_TYPE
	  case 16 :
	      i = (((((ic << 16) | im) << 16) | iy) << 16) | 0xffff;
	      break;
#endif /* GX_COLOR_INDEX_TYPE */
        }
        break;

    default :
        i = cups->EncodeLUT[gx_max_color_value - (r * 31 + g * 61 + b * 8) / 100];
        break;

    case CUPS_CSPACE_CMY :
        ic = cups->EncodeLUT[gx_max_color_value - r];
        im = cups->EncodeLUT[gx_max_color_value - g];
        iy = cups->EncodeLUT[gx_max_color_value - b];

        switch (cups->header.cupsBitsPerColor)
        {
          default :
              i = (((ic << 1) | im) << 1) | iy;
              break;
          case 2 :
              i = (((ic << 2) | im) << 2) | iy;
              break;
          case 4 :
              i = (((ic << 4) | im) << 4) | iy;
              break;
          case 8 :
              i = (((ic << 8) | im) << 8) | iy;
              break;
#ifdef GX_COLOR_INDEX_TYPE
	  case 16 :
	      i = (((ic << 16) | im) << 16) | iy;
	      break;
#endif /* GX_COLOR_INDEX_TYPE */
        }
        break;

    case CUPS_CSPACE_YMC :
        ic = cups->EncodeLUT[gx_max_color_value - r];
        im = cups->EncodeLUT[gx_max_color_value - g];
        iy = cups->EncodeLUT[gx_max_color_value - b];

        switch (cups->header.cupsBitsPerColor)
        {
          default :
              i = (((iy << 1) | im) << 1) | ic;
              break;
          case 2 :
              i = (((iy << 2) | im) << 2) | ic;
              break;
          case 4 :
              i = (((iy << 4) | im) << 4) | ic;
              break;
          case 8 :
              i = (((iy << 8) | im) << 8) | ic;
              break;
        }
        break;

    case CUPS_CSPACE_CMYK :
	ic = gx_max_color_value - r;
	im = gx_max_color_value - g;
	iy = gx_max_color_value - b;
        ik = min(ic, min(im, iy));

	if ((mk = max(ic, max(im, iy))) > ik)
	  ik = (int)((float)ik * (float)ik * (float)ik /
	             ((float)mk * (float)mk));

        ic = cups->EncodeLUT[ic - ik];
        im = cups->EncodeLUT[im - ik];
        iy = cups->EncodeLUT[iy - ik];
        ik = cups->EncodeLUT[ik];

        switch (cups->header.cupsBitsPerColor)
        {
          default :
              i = (((((ic << 1) | im) << 1) | iy) << 1) | ik;
              break;
          case 2 :
              i = (((((ic << 2) | im) << 2) | iy) << 2) | ik;
              break;
          case 4 :
              i = (((((ic << 4) | im) << 4) | iy) << 4) | ik;
              break;
          case 8 :
              i = (((((ic << 8) | im) << 8) | iy) << 8) | ik;
              break;
#ifdef GX_COLOR_INDEX_TYPE
	  case 16 :
	      i = (((((ic << 16) | im) << 16) | iy) << 16) | ik;
	      break;
#endif /* GX_COLOR_INDEX_TYPE */
        }

#ifdef CUPS_DEBUG2
        dmprintf8(pdev->memory, "DEBUG2: CMY (%d,%d,%d) -> CMYK %08x (%d,%d,%d,%d)\n",
                  r, g, b, (unsigned)i, ic, im, iy, ik);
#endif /* CUPS_DEBUG2 */
        break;

    case CUPS_CSPACE_YMCK :
    case CUPS_CSPACE_GMCK :
    case CUPS_CSPACE_GMCS :
	ic = gx_max_color_value - r;
	im = gx_max_color_value - g;
	iy = gx_max_color_value - b;
        ik = min(ic, min(im, iy));

	if ((mk = max(ic, max(im, iy))) > ik)
	  ik = (int)((float)ik * (float)ik * (float)ik /
	             ((float)mk * (float)mk));

        ic = cups->EncodeLUT[ic - ik];
        im = cups->EncodeLUT[im - ik];
        iy = cups->EncodeLUT[iy - ik];
        ik = cups->EncodeLUT[ik];

        switch (cups->header.cupsBitsPerColor)
        {
          default :
              i = (((((iy << 1) | im) << 1) | ic) << 1) | ik;
              break;
          case 2 :
              i = (((((iy << 2) | im) << 2) | ic) << 2) | ik;
              break;
          case 4 :
              i = (((((iy << 4) | im) << 4) | ic) << 4) | ik;
              break;
          case 8 :
              i = (((((iy << 8) | im) << 8) | ic) << 8) | ik;
              break;
#ifdef GX_COLOR_INDEX_TYPE
	  case 16 :
	      i = (((((iy << 16) | im) << 16) | ic) << 16) | ik;
	      break;
#endif /* GX_COLOR_INDEX_TYPE */
        }
        break;

    case CUPS_CSPACE_KCMYcm :
        if (cups->header.cupsBitsPerColor == 1)
	{
	  ic = gx_max_color_value - r;
	  im = gx_max_color_value - g;
	  iy = gx_max_color_value - b;
          ik = min(ic, min(im, iy));

	  if ((mk = max(ic, max(im, iy))) > ik)
	    ik = (int)((float)ik * (float)ik * (float)ik /
	               ((float)mk * (float)mk));

          ic = cups->EncodeLUT[ic - ik];
          im = cups->EncodeLUT[im - ik];
          iy = cups->EncodeLUT[iy - ik];
          ik = cups->EncodeLUT[ik];
	  if (ik)
	    i = 32;
	  else if (ic && im)
	    i = 17;
	  else if (ic && iy)
	    i = 6;
	  else if (im && iy)
	    i = 12;
	  else if (ic)
	    i = 16;
	  else if (im)
	    i = 8;
	  else if (iy)
	    i = 4;
	  else
	    i = 0;
	  break;
	}

    case CUPS_CSPACE_KCMY :
	ic = gx_max_color_value - r;
	im = gx_max_color_value - g;
	iy = gx_max_color_value - b;
        ik = min(ic, min(im, iy));

	if ((mk = max(ic, max(im, iy))) > ik)
	  ik = (int)((float)ik * (float)ik * (float)ik /
	             ((float)mk * (float)mk));

        ic = cups->EncodeLUT[ic - ik];
        im = cups->EncodeLUT[im - ik];
        iy = cups->EncodeLUT[iy - ik];
        ik = cups->EncodeLUT[ik];

        switch (cups->header.cupsBitsPerColor)
        {
          default :
              i = (((((ik << 1) | ic) << 1) | im) << 1) | iy;
              break;
          case 2 :
              i = (((((ik << 2) | ic) << 2) | im) << 2) | iy;
              break;
          case 4 :
              i = (((((ik << 4) | ic) << 4) | im) << 4) | iy;
              break;
          case 8 :
              i = (((((ik << 8) | ic) << 8) | im) << 8) | iy;
              break;
#ifdef GX_COLOR_INDEX_TYPE
	  case 16 :
	      i = (((((ik << 16) | ic) << 16) | im) << 16) | iy;
	      break;
#endif /* GX_COLOR_INDEX_TYPE */
        }
        break;

#  ifdef CUPS_RASTER_HAVE_COLORIMETRIC
    case CUPS_CSPACE_CIEXYZ :
    case CUPS_CSPACE_CIELab :
    case CUPS_CSPACE_ICC1 :
    case CUPS_CSPACE_ICC2 :
    case CUPS_CSPACE_ICC3 :
    case CUPS_CSPACE_ICC4 :
    case CUPS_CSPACE_ICC5 :
    case CUPS_CSPACE_ICC6 :
    case CUPS_CSPACE_ICC7 :
    case CUPS_CSPACE_ICC8 :
    case CUPS_CSPACE_ICC9 :
    case CUPS_CSPACE_ICCA :
    case CUPS_CSPACE_ICCB :
    case CUPS_CSPACE_ICCC :
    case CUPS_CSPACE_ICCD :
    case CUPS_CSPACE_ICCE :
    case CUPS_CSPACE_ICCF :
       /*
        * Convert sRGB to linear RGB...
	*/

	rr = pow(((double)r / (double)gx_max_color_value + 0.055) / 1.055, 2.4);
	rg = pow(((double)g / (double)gx_max_color_value + 0.055) / 1.055, 2.4);
	rb = pow(((double)b / (double)gx_max_color_value + 0.055) / 1.055, 2.4);

       /*
        * Convert to CIE XYZ...
	*/

	ciex = 0.412453 * rr + 0.357580 * rg + 0.180423 * rb;
	ciey = 0.212671 * rr + 0.715160 * rg + 0.072169 * rb;
	ciez = 0.019334 * rr + 0.119193 * rg + 0.950227 * rb;

        if (cups->header.cupsColorSpace == CUPS_CSPACE_CIEXYZ)
	{
	 /*
	  * Convert to an integer XYZ color value...
	  */

          if (ciex > 1.1)
	    ic = 255;
	  else if (ciex > 0.0)
	    ic = (int)(ciex / 1.1 * 255.0 + 0.5);
	  else
	    ic = 0;

          if (ciey > 1.1)
	    im = 255;
	  else if (ciey > 0.0)
	    im = (int)(ciey / 1.1 * 255.0 + 0.5);
	  else
	    im = 0;

          if (ciez > 1.1)
	    iy = 255;
	  else if (ciez > 0.0)
	    iy = (int)(ciez / 1.1 * 255.0 + 0.5);
	  else
	    iy = 0;
	}
	else
	{
	 /*
	  * Convert CIE XYZ to Lab...
	  */

	  ciey_yn = ciey / D65_Y;

	  if (ciey_yn > 0.008856)
	    ciel = 116 * cbrt(ciey_yn) - 16;
	  else
	    ciel = 903.3 * ciey_yn;

	  ciea = 500 * (cups_map_cielab(ciex, D65_X) -
	                cups_map_cielab(ciey, D65_Y));
	  cieb = 200 * (cups_map_cielab(ciey, D65_Y) -
	                cups_map_cielab(ciez, D65_Z));

         /*
	  * Scale the L value and bias the a and b values by 128
	  * so that all values are in the range of 0 to 255.
	  */

	  ciel = ciel * 2.55 + 0.5;
	  ciea += 128.5;
	  cieb += 128.5;

         /*
	  * Convert to 8-bit values...
	  */

          if (ciel < 0.0)
	    ic = 0;
	  else if (ciel < 255.0)
	    ic = (int)ciel;
	  else
	    ic = 255;

          if (ciea < 0.0)
	    im = 0;
	  else if (ciea < 255.0)
	    im = (int)ciea;
	  else
	    im = 255;

          if (cieb < 0.0)
	    iy = 0;
	  else if (cieb < 255.0)
	    iy = (int)cieb;
	  else
	    iy = 255;
	}

       /*
        * Put the final color value together...
	*/

        switch (cups->header.cupsBitsPerColor)
        {
          default :
              i = (((ic << 1) | im) << 1) | iy;
              break;
          case 2 :
              i = (((ic << 2) | im) << 2) | iy;
              break;
          case 4 :
              i = (((ic << 4) | im) << 4) | iy;
              break;
          case 8 :
              i = (((ic << 8) | im) << 8) | iy;
              break;
#ifdef GX_COLOR_INDEX_TYPE
	  case 16 :
	      i = (((ic << 16) | im) << 16) | iy;
	      break;
#endif /* GX_COLOR_INDEX_TYPE */
        }
        break;
#  endif /* CUPS_RASTER_HAVE_COLORIMETRIC */
  }

#ifdef CUPS_DEBUG2
  dmprintf4(pdev->memory, "DEBUG2: RGB %d,%d,%d = %08x\n",
            r, g, b, (unsigned)i);
#endif /* CUPS_DEBUG2 */

  return (i);
}
#endif /* dev_t_proc_encode_color */


/*
 * 'cups_open()' - Open the output file and initialize things.
 */

private int				/* O - Error status */
cups_open(gx_device *pdev)		/* I - Device info */
{
  int	code;				/* Return status */

#ifdef CUPS_DEBUG2
  dmprintf1(pdev->memory, "DEBUG2: cups_open(%p)\n", pdev);
#endif /* CUPS_DEBUG2 */

  dmprintf(pdev->memory, "INFO: Start rendering...\n");
  cups->printer_procs.get_space_params = cups_get_space_params;

  if (cups->page == 0)
  {
    dmprintf(pdev->memory, "INFO: Processing page 1...\n");
    cups->page = 1;
  }

  if ((code = cups_set_color_info(pdev)) < 0) {
    return(code);
  }

  /* Establish the default LeadingEdge in the cups header */
  cups->header.LeadingEdge = (cups_edge_t)(pdev->LeadingEdge & LEADINGEDGE_MASK);

  if ((code = gdev_prn_open(pdev)) != 0)
    return(code);

  if (cups->PPD == NULL)
    cups->PPD = ppdOpenFile(getenv("PPD"));

  if (cups->pageSizeRequested[0] == '\0') {
    (void) snprintf(cups->pageSizeRequested, sizeof(cups->pageSizeRequested), "%s", cups->header.cupsPageSizeName);
#ifdef CUPS_DEBUG
    dmprintf1(pdev->memory, "DEBUG: Page size requested: %s\n",
	      cups->header.cupsPageSizeName);
#endif /* CUPS_DEBUG */
  }

  return (0);
}


/*
 * 'cups_output_page()' - Send one or more pages to the output file.
 * The changes to the cups->page are done here for background printing
 * but testing shows some regressions, so BGPrint is not used for now.
 */

private int				/* O - 0 if everything is OK */
cups_output_page(gx_device *pdev, int num_copies, int flush)
{
  int		code = 0;		/* Error code */

  /* FIXME: We would like to support BGPrint=true and call gdev_prn_bg_output_page */
  /* but there must still be other things that prevent this. */
  if ((code = gdev_prn_output_page(pdev, num_copies, flush)) < 0)
      return code;

  cups->page ++;
  dmprintf1(pdev->memory, "INFO: Processing page %d...\n", cups->page);

  return (0);
}


/*
 * 'cups_print_pages()' - Send one or more pages to the output file.
 */

private int				/* O - 0 if everything is OK */
cups_print_pages(gx_device_printer *pdev,
					/* I - Device info */
                 gp_file           *fp,	/* I - Output file */
		 int               num_copies)
					/* I - Number of copies */
{
  int		code = 0;		/* Error code */
  int		copy;			/* Copy number */
  int		srcbytes;		/* Byte width of scanline */
  unsigned char	*src,			/* Scanline data */
		*dst;			/* Bitmap data */
  ppd_attr_t    *RasterVersion = NULL;  /* CUPS Raster version read from PPD
					   file */

  (void)fp; /* reference unused file pointer to prevent compiler warning */

#ifdef CUPS_DEBUG2
  dmprintf3(pdev->memory, "DEBUG2: cups_print_pages(%p, %p, %d)\n", pdev, fp,
            num_copies);
#endif /* CUPS_DEBUG2 */

 /*
  * Figure out the number of bytes per line...
  */

  switch (cups->header.cupsColorOrder)
  {
    case CUPS_ORDER_CHUNKED :
        cups->header.cupsBytesPerLine = (cups->header.cupsBitsPerPixel *
	                                 cups->header.cupsWidth + 7) / 8;
        break;

    case CUPS_ORDER_BANDED :
        if (cups->header.cupsColorSpace == CUPS_CSPACE_KCMYcm &&
	    cups->header.cupsBitsPerColor == 1)
          cups->header.cupsBytesPerLine = (cups->header.cupsBitsPerColor *
                                           cups->header.cupsWidth + 7) / 8 * 6;
        else
          cups->header.cupsBytesPerLine = (cups->header.cupsBitsPerColor *
                                           cups->header.cupsWidth + 7) / 8 *
				          cups->color_info.num_components;
        break;

    case CUPS_ORDER_PLANAR :
        cups->header.cupsBytesPerLine = (cups->header.cupsBitsPerColor *
	                                 cups->header.cupsWidth + 7) / 8;
        break;
  }

 /*
  * Compute the width of a scanline and allocate input/output buffers...
  */

  srcbytes = gdev_prn_raster(pdev);

#ifdef CUPS_DEBUG2
  dmprintf4(pdev->memory, "DEBUG2: cupsBitsPerPixel = %d, cupsWidth = %d, cupsBytesPerLine = %d, srcbytes = %d\n",
            cups->header.cupsBitsPerPixel, cups->header.cupsWidth,
            cups->header.cupsBytesPerLine, srcbytes);
#endif /* CUPS_DEBUG2 */

  src = (unsigned char *)gs_malloc(pdev->memory->non_gc_memory, srcbytes, 1, "cups_print_pages");

  if (src == NULL)	/* can't allocate input buffer */
    return_error(gs_error_VMerror);

  memset(src, 0, srcbytes);

 /*
  * Need an output buffer, too...
  */

  dst = (unsigned char *)gs_malloc(pdev->memory->non_gc_memory, cups->header.cupsBytesPerLine, 2,
                                   "cups_print_pages");

  if (dst == NULL)	/* can't allocate working area */
    return_error(gs_error_VMerror);

  memset(dst, 0, 2 * cups->header.cupsBytesPerLine);

 /*
  * See if the stream has been initialized yet...
  */

  if (cups->stream == NULL)
  {
    RasterVersion = ppdFindAttr(cups->PPD, "cupsRasterVersion", NULL);
    if (RasterVersion) {
#ifdef CUPS_DEBUG2
      dmprintf1(pdev->memory, "DEBUG2: cupsRasterVersion = %s\n",
                RasterVersion->value);
#endif /* CUPS_DEBUG2 */
      cups->cupsRasterVersion = atoi(RasterVersion->value);
      if ((cups->cupsRasterVersion != 2) &&
	  (cups->cupsRasterVersion != 3)) {
        dmprintf1(pdev->memory, "ERROR: Unsupported CUPS Raster Version: %s",
                  RasterVersion->value);
	return_error(gs_error_unknownerror);
      }
    }
    /* NOTE: PWG Raster output is only available with shared CUPS CUPS and
       CUPS image libraries as the built-in libraries of Ghostscript do not
       contain the new code needed for PWG Raster output. This conditional
       is a temporary workaround for the time being until up-to-date CUPS
       libraries get included. */
    if ((cups->stream = cupsRasterOpen(fileno(gp_get_file(cups->file)),
#if defined(CUPS_RASTER_HAVE_PWGRASTER)
                                       (strcasecmp(cups->header.MediaClass,
						   "PwgRaster") == 0 ?
					CUPS_RASTER_WRITE_PWG :
					(cups->cupsRasterVersion == 3 ?
					 CUPS_RASTER_WRITE :
					 CUPS_RASTER_WRITE_COMPRESSED)))) ==
	NULL)
#else
                                       (cups->cupsRasterVersion == 3 ?
					CUPS_RASTER_WRITE :
					CUPS_RASTER_WRITE_COMPRESSED))) == NULL)
#endif
    {
      perror("ERROR: Unable to open raster stream - ");
      return_error(gs_error_ioerror);
    }
  }

 /*
  * Output a page of graphics...
  */

  if (num_copies < 1)
    num_copies = 1;

  if (cups->PPD != NULL && !cups->PPD->manual_copies)
  {
    cups->header.NumCopies = num_copies;
    num_copies = 1;
  }

#ifdef CUPS_DEBUG
  dmprintf3(pdev->memory, "DEBUG2: cupsWidth = %d, cupsHeight = %d, cupsBytesPerLine = %d\n",
            cups->header.cupsWidth, cups->header.cupsHeight,
            cups->header.cupsBytesPerLine);
#endif /* CUPS_DEBUG */

  for (copy = num_copies; copy > 0; copy --)
  {
    cupsRasterWriteHeader(cups->stream, &(cups->header));

    if (pdev->color_info.num_components == 1)
      code = cups_print_chunked(pdev, src, dst, srcbytes);
    else
      switch (cups->header.cupsColorOrder)
      {
	case CUPS_ORDER_CHUNKED :
            code = cups_print_chunked(pdev, src, dst, srcbytes);
	    break;
	case CUPS_ORDER_BANDED :
            code = cups_print_banded(pdev, src, dst, srcbytes);
	    break;
	case CUPS_ORDER_PLANAR :
            code = cups_print_planar(pdev, src, dst, srcbytes);
	    break;
      }
    if (code < 0)
      break;
  }

 /*
  * Free temporary storage and return...
  */

  gs_free(pdev->memory->non_gc_memory, (char *)src, srcbytes, 1, "cups_print_pages");
  gs_free(pdev->memory->non_gc_memory, (char *)dst, cups->header.cupsBytesPerLine, 1, "cups_print_pages");

 return (code);
}


/*
 * 'cups_put_params()' - Set pagedevice parameters.
 */

private int				/* O - Error status */
cups_put_params(gx_device     *pdev,	/* I - Device info */
                gs_param_list *plist)	/* I - Parameter list */
{
  int			i;		/* Looping var */
  float			mediasize[2];	/* Physical size of print */
  float			margins[4];	/* Physical margins of print */
  float			cups_mediasize[2]; /* Media size to use in Raster */
  float			cups_margins[4]; /* Margins to use in Raster */
  ppd_size_t		*size;		/* Page size */
  int			code;		/* Error code */
  int			intval;		/* Integer value */
  bool			boolval;	/* Boolean value */
  float			floatval;	/* Floating point value */
  gs_param_string	stringval;	/* String value */
  gs_param_float_array	arrayval;	/* Float array value */
  int			margins_set;	/* Were the margins set? */
  int			size_set;	/* Was the size set? */
  int			color_set;	/* Were the color attrs set? */
  gdev_prn_space_params	sp_old;	        /* Space parameter data */
  int			width,		/* New width of page */
                        height,		/* New height of page */
                        width_old = 0,  /* Previous width of page */
                        height_old = 0; /* Previous height of page */
  bool                  transp_old = 0; /* Previous transparency usage state */
  ppd_attr_t            *backside = NULL,
                        *backsiderequiresflippedmargins = NULL;
  float                 swap;
  int                   xflip = 0,
                        yflip = 0;
  int                   found = 0;
  long                  best_score = -1,
                        score = 0;
  ppd_size_t            *best_size = NULL;
  int                   size_matched = 0,
                        margins_matched = 0,
                        imageable_area_matched = 0,
                        name_requested_matched = 0;
  float long_edge_mismatch, short_edge_mismatch;
  gs_param_string icc_pro_dummy;
  int old_cmps = cups->color_info.num_components;
  int old_depth = cups->color_info.depth;
#ifdef CUPS_RASTER_SYNCv1
  float			sf;		/* cupsBorderlessScalingFactor */
#endif /* CUPS_RASTER_SYNCv1 */

#ifdef CUPS_DEBUG
  dmprintf2(pdev->memory, "DEBUG2: cups_put_params(%p, %p)\n", pdev, plist);
#endif /* CUPS_DEBUG */

 /*
  * Process other options for CUPS...
  */

#define stringoption(name, sname) \
  if ((code = param_read_string(plist, sname, &stringval)) < 0) \
  { \
    dmprintf1(pdev->memory, "ERROR: Error setting %s...\n", sname);	      \
    param_signal_error(plist, sname, code); \
    goto done; \
  } \
  else if (code == 0) \
  { \
    strncpy(cups->header.name, (const char *)(stringval.data),	\
            stringval.size); \
    cups->header.name[stringval.size] = '\0'; \
  }

#define intoption(name, sname, type) \
  if ((code = param_read_int(plist, sname, &intval)) < 0) \
  { \
    dmprintf1(pdev->memory, "ERROR: Error setting %s ...\n", sname); \
    param_signal_error(plist, sname, code); \
    goto done; \
  } \
  else if (code == 0) \
  { \
    cups->header.name = (type)intval; \
  }

#define floatoption(name, sname) \
  if ((code = param_read_float(plist, sname, &floatval)) < 0) \
  { \
    dmprintf1(pdev->memory, "ERROR: Error setting %s ...\n", sname); \
    param_signal_error(plist, sname, code); \
    goto done; \
  } \
  else if (code == 0) \
  { \
    cups->header.name = (float)floatval; \
  }

#define booloption(name, sname) \
  if ((code = param_read_bool(plist, sname, &boolval)) < 0) \
  { \
    if ((code = param_read_null(plist, sname)) < 0) \
    { \
      dmprintf1(pdev->memory, "ERROR: Error setting %s ...\n", sname); \
      param_signal_error(plist, sname, code); \
      goto done; \
    } \
    if (code == 0) \
      cups->header.name = CUPS_FALSE; \
  } \
  else if (code == 0) \
  { \
    cups->header.name = (cups_bool_t)boolval; \
  }

#define arrayoption(name, sname, count) \
  if ((code = param_read_float_array(plist, sname, &arrayval)) < 0) \
  { \
    if ((code = param_read_null(plist, sname)) < 0) \
    { \
      dmprintf1(pdev->memory, "ERROR: Error setting %s...\n", sname); \
      param_signal_error(plist, sname, code); \
      goto done; \
    } \
    if (code == 0) \
    { \
      for (i = 0; i < count; i ++) \
	cups->header.name[i] = 0; \
    } \
  } \
  else if (code == 0) \
  { \
    for (i = 0; i < count; i ++) { \
      cups->header.name[i] = (unsigned)(arrayval.data[i]); \
    } \
  }

  sp_old = ((gx_device_printer *)pdev)->space_params;
  width_old = pdev->width;
  height_old = pdev->height;
  transp_old = cups->page_uses_transparency;
  size_set    = param_read_float_array(plist, ".MediaSize", &arrayval) == 0 ||
                param_read_float_array(plist, "PageSize", &arrayval) == 0;
  margins_set = param_read_float_array(plist, "Margins", &arrayval) == 0;
  color_set   = param_read_int(plist, "cupsColorSpace", &intval) == 0 ||
                param_read_int(plist, "cupsBitsPerColor", &intval) == 0;

  if (!cups->user_icc) {
      cups->user_icc = param_read_string(plist, "OutputICCProfile", &icc_pro_dummy) == 0;
  }

  /* We also recompute page size and margins if we simply get onto a new
     page without necessarily having a page size change in the PostScript
     code, as for some printers margins have to be flipped on the back sides of
     the sheets (even pages) when printing duplex */
  if (cups->page != cups->lastpage) {
    size_set = 1;
    cups->lastpage = cups->page;
  }

  stringoption(MediaClass, "MediaClass")
  stringoption(MediaColor, "MediaColor")
  stringoption(MediaType, "MediaType")
  stringoption(OutputType, "OutputType")
  intoption(AdvanceDistance, "AdvanceDistance", unsigned)
  intoption(AdvanceMedia, "AdvanceMedia", cups_adv_t)
  booloption(Collate, "Collate")
  intoption(CutMedia, "CutMedia", cups_cut_t)
  booloption(Duplex, "Duplex")
  arrayoption(ImagingBoundingBox, "ImagingBoundingBox", 4)
  booloption(InsertSheet, "InsertSheet")
  intoption(Jog, "Jog", cups_jog_t)
  arrayoption(Margins, "Margins", 2)
  booloption(ManualFeed, "ManualFeed")
  intoption(MediaPosition, "cupsMediaPosition", unsigned) /* Compatibility */
  intoption(MediaPosition, "MediaPosition", unsigned)
  intoption(MediaWeight, "MediaWeight", unsigned)
  booloption(MirrorPrint, "MirrorPrint")
  booloption(NegativePrint, "NegativePrint")
  intoption(Orientation, "Orientation", cups_orient_t)
  booloption(OutputFaceUp, "OutputFaceUp")
  booloption(Separations, "Separations")
  booloption(TraySwitch, "TraySwitch")
  booloption(Tumble, "Tumble")
  intoption(cupsMediaType, "cupsMediaType", unsigned)
  intoption(cupsBitsPerColor, "cupsBitsPerColor", unsigned)
  intoption(cupsColorOrder, "cupsColorOrder", cups_order_t)
  intoption(cupsColorSpace, "cupsColorSpace", cups_cspace_t)
  intoption(cupsCompression, "cupsCompression", unsigned)
  intoption(cupsRowCount, "cupsRowCount", unsigned)
  intoption(cupsRowFeed, "cupsRowFeed", unsigned)
  intoption(cupsRowStep, "cupsRowStep", unsigned)

#ifdef GX_COLOR_INDEX_TYPE
 /*
  * Support cupsPreferredBitsPerColor - basically, allows you to
  * request 16-bits per color in a backwards-compatible way...
  */

  if (!param_read_int(plist, "cupsPreferredBitsPerColor", &intval))
    if (intval > cups->header.cupsBitsPerColor && intval <= 16)
      cups->header.cupsBitsPerColor = intval;
#endif /* GX_COLOR_INDEX_TYPE */

#ifdef CUPS_RASTER_SYNCv1
  floatoption(cupsBorderlessScalingFactor, "cupsBorderlessScalingFactor");

  for (i = 0; cups_Integer_strings[i] != NULL; i ++)
  {
    intoption(cupsInteger[i], cups_Integer_strings[i], unsigned)
  }

  for (i = 0; cups_Real_strings[i] != NULL; i ++)
  {
    floatoption(cupsReal[i], cups_Real_strings[i])
  }

  for (i = 0; cups_String_strings[i] != NULL; i ++)
  {
    stringoption(cupsString[i], cups_String_strings[i])
  }

  stringoption(cupsMarkerType, "cupsMarkerType");
  stringoption(cupsRenderingIntent, "cupsRenderingIntent");
  stringoption(cupsPageSizeName, "cupsPageSizeName");
#endif /* CUPS_RASTER_SYNCv1 */

  if ((code = param_read_string(plist, "cupsProfile", &stringval)) < 0)
  {
    param_signal_error(plist, "cupsProfile", code);
    goto done;
  }
  else if (code == 0)
  {
    if (cups->Profile != NULL)
      free(cups->Profile);

    cups->Profile = strdup((char *)stringval.data);
  }

  if ((code = cups_set_color_info(pdev)) < 0) {
      goto done;
  }

  /*
  * Then process standard page device options...
  */

  if ((code = gdev_prn_put_params(pdev, plist)) < 0)
    goto done;

  cups->header.LeadingEdge = (cups_edge_t)(pdev->LeadingEdge & LEADINGEDGE_MASK);

  /* If cups_set_color_info() changed the color model of the device we want to
   * force the raster memory to be recreated/reinitialized
   */
  if (cups->color_info.num_components != old_cmps || cups->color_info.depth != old_depth) {
      width_old = 0;
      height_old = 0;
  }
  else {
  /* pdev->width/height may have been changed by the call to
   * gdev_prn_put_params()
   */
     width_old = pdev->width;
     height_old = pdev->height;
  }
 /*
  * Update margins/sizes as needed...
  */

  if (size_set)
  {
   /*
    * Compute the page margins...
    */

#ifdef CUPS_DEBUG
    dmprintf2(pdev->memory, "DEBUG: Updating PageSize to [%.0f %.0f]...\n",
              cups->MediaSize[0], cups->MediaSize[1]);
#endif /* CUPS_DEBUG */

    memset(margins, 0, sizeof(margins));

    cups->landscape = 0;

    if (cups->PPD != NULL)
    {
#ifdef CUPS_DEBUG
      dmprintf1(pdev->memory, "DEBUG2: cups->header.Duplex = %d\n", cups->header.Duplex);
      dmprintf1(pdev->memory, "DEBUG2: cups->header.Tumble = %d\n", cups->header.Tumble);
      dmprintf1(pdev->memory, "DEBUG2: cups->page = %d\n", cups->page);
      dmprintf1(pdev->memory, "DEBUG2: cups->PPD = %p\n", cups->PPD);
#endif /* CUPS_DEBUG */

      backside = ppdFindAttr(cups->PPD, "cupsBackSide", NULL);
      if (backside) {
#ifdef CUPS_DEBUG
        dmprintf1(pdev->memory, "DEBUG2: cupsBackSide = %s\n", backside->value);
#endif /* CUPS_DEBUG */
	cups->PPD->flip_duplex = 0;
      }
#ifdef CUPS_DEBUG
      dmprintf1(pdev->memory, "DEBUG2: cups->PPD->flip_duplex = %d\n", cups->PPD->flip_duplex);
#endif /* CUPS_DEBUG */

      backsiderequiresflippedmargins =
	ppdFindAttr(cups->PPD, "APDuplexRequiresFlippedMargin", NULL);
#ifdef CUPS_DEBUG
      if (backsiderequiresflippedmargins)
        dmprintf1(pdev->memory, "DEBUG2: APDuplexRequiresFlippedMargin = %s\n",
                  backsiderequiresflippedmargins->value);
#endif /* CUPS_DEBUG */

      if (cups->header.Duplex &&
	  (cups->header.Tumble &&
	   (backside && !strcasecmp(backside->value, "Flipped"))) &&
	  !(cups->page & 1))
      {
	xflip = 1;
	if (backsiderequiresflippedmargins &&
	    !strcasecmp(backsiderequiresflippedmargins->value, "False")) {
#ifdef CUPS_DEBUG
          dmprintf(pdev->memory, "DEBUG2: (1) Flip: X=1 Y=0\n");
#endif /* CUPS_DEBUG */
	  yflip = 0;
	} else {
#ifdef CUPS_DEBUG
          dmprintf(pdev->memory, "DEBUG2: (1) Flip: X=1 Y=1\n");
#endif /* CUPS_DEBUG */
	  yflip = 1;
	}
      }
      else if (cups->header.Duplex &&
	       (!cups->header.Tumble &&
		(backside && !strcasecmp(backside->value, "Flipped"))) &&
	       !(cups->page & 1))
      {
	xflip = 0;
	if (backsiderequiresflippedmargins &&
	    !strcasecmp(backsiderequiresflippedmargins->value, "False")) {
#ifdef CUPS_DEBUG
          dmprintf(pdev->memory, "DEBUG2: (2) Flip: X=0 Y=1\n");
#endif /* CUPS_DEBUG */
	  yflip = 1;
	} else {
#ifdef CUPS_DEBUG
          dmprintf(pdev->memory, "DEBUG2: (2) Flip: X=0 Y=0\n");
#endif /* CUPS_DEBUG */
	  yflip = 0;
	}
      }
      else if (cups->header.Duplex &&
	       ((!cups->header.Tumble &&
		 (cups->PPD->flip_duplex ||
		  (backside && !strcasecmp(backside->value, "Rotated")))) ||
		(cups->header.Tumble &&
		 (backside && !strcasecmp(backside->value, "ManualTumble")))) &&
	       !(cups->page & 1))
      {
	xflip = 1;
	if (backsiderequiresflippedmargins &&
	    !strcasecmp(backsiderequiresflippedmargins->value, "True")) {
#ifdef CUPS_DEBUG
          dmprintf(pdev->memory, "DEBUG2: (3) Flip: X=1 Y=0\n");
#endif /* CUPS_DEBUG */
	  yflip = 0;
	} else {
#ifdef CUPS_DEBUG
          dmprintf(pdev->memory, "DEBUG2: (3) Flip: X=1 Y=1\n");
#endif /* CUPS_DEBUG */
	  yflip = 1;
	}
      }
      else
      {
#ifdef CUPS_DEBUG
        dmprintf(pdev->memory, "DEBUG2: (4) Flip: X=0 Y=0\n");
#endif /* CUPS_DEBUG */
	xflip = 0;
	yflip = 0;
      }

#ifdef CUPS_RASTER_SYNCv1
     /*
      * Chack whether cupsPageSizeName has a valid value
      */

      if (strlen(cups->header.cupsPageSizeName) != 0) {
	found = 0;
	for (i = cups->PPD->num_sizes, size = cups->PPD->sizes;
	     i > 0;
	     i --, size ++)
	  if (strcasecmp(cups->header.cupsPageSizeName, size->name) == 0) {
	    found = 1;
	    break;
	  }
	if (found == 0) cups->header.cupsPageSizeName[0] = '\0';
      }
#ifdef CUPS_DEBUG
      dmprintf1(pdev->memory, "DEBUG2: cups->header.cupsPageSizeName = %s\n",
                cups->header.cupsPageSizeName);
#endif /* CUPS_DEBUG */
#endif /* CUPS_RASTER_SYNCv1 */

     /*
      * Find the matching page size...
      */

#define LONG_EDGE_LENGTH_MATCH_LIMIT  0.01
#define SHORT_EDGE_LENGTH_MATCH_LIMIT 0.01
#define PAGESIZE_SCORE_SIZE_MARGINS   2
#define PAGESIZE_SCORE_SIZE           1

      best_score = -1;
      best_size = NULL;
      for (i = cups->PPD->num_sizes, size = cups->PPD->sizes;
	   i > 0;
	   i --, size ++)
      {
	if (size->length == 0 || size->width == 0) continue;

	score = 0;
	size_matched = 0;
	margins_matched = 0;
	imageable_area_matched = 0;
	name_requested_matched = 0;

	long_edge_mismatch =
	  fabs(cups->MediaSize[1] - size->length)/size->length +
	  LONG_EDGE_LENGTH_MATCH_LIMIT / 100;
	short_edge_mismatch =
	  fabs(cups->MediaSize[0] - size->width)/size->width +
	  SHORT_EDGE_LENGTH_MATCH_LIMIT / 100;
	if (size->length < size->width)
	{
	  swap = long_edge_mismatch;
	  long_edge_mismatch = short_edge_mismatch;
	  short_edge_mismatch = swap;
	}

	if (long_edge_mismatch < LONG_EDGE_LENGTH_MATCH_LIMIT &&
	    short_edge_mismatch < SHORT_EDGE_LENGTH_MATCH_LIMIT)
	{
	  size_matched = 1;
	  /* If two sizes match within the limits, take the one with less
	     mismatch */
	  score = (long)(9999.0 -
			 long_edge_mismatch * short_edge_mismatch * 9999.0 /
			 LONG_EDGE_LENGTH_MATCH_LIMIT /
			 SHORT_EDGE_LENGTH_MATCH_LIMIT);
	  if (score < 0) score = 0;
	  /* We check whether all 4 margins match with the margin info
	     of the page size in the PPD. Here we check also for swapped
	     left/right and top/bottom margins as the cups->HWMargins
	     info can be from the previous page and there the margins
	     can be swapped due to duplex printing requirements */
	  if (!margins_set ||
	      (((fabs(cups->HWMargins[0] - size->left) < 1.0 &&
		 fabs(cups->HWMargins[2] - size->width + size->right) < 1.0) ||
		(fabs(cups->HWMargins[0] - size->width + size->right) < 1.0 &&
		 fabs(cups->HWMargins[2] - size->left) < 1.0)) &&
	       ((fabs(cups->HWMargins[1] - size->bottom) < 1.0 &&
		 fabs(cups->HWMargins[3] - size->length + size->top) < 1.0) ||
		(fabs(cups->HWMargins[1] - size->length + size->top) < 1.0 &&
		 fabs(cups->HWMargins[3] - size->bottom) < 1.0))))
	    margins_matched = 1;
	} else {
	  /* Compare the dimensions of the imageable area against the
	     the input page size */
	  long_edge_mismatch =
	    fabs(cups->MediaSize[1] - size->top + size->bottom)/size->length +
	    LONG_EDGE_LENGTH_MATCH_LIMIT / 100;
	  short_edge_mismatch =
	    fabs(cups->MediaSize[0] - size->right + size->left)/size->width +
	    SHORT_EDGE_LENGTH_MATCH_LIMIT / 100;
	  if (size->length < size->width)
	  {
	    swap = long_edge_mismatch;
	    long_edge_mismatch = short_edge_mismatch;
	    short_edge_mismatch = swap;
	  }

	  if (long_edge_mismatch < LONG_EDGE_LENGTH_MATCH_LIMIT &&
	      short_edge_mismatch < SHORT_EDGE_LENGTH_MATCH_LIMIT)
	  {
	    imageable_area_matched = 1;
	    /* If two sizes match within the limits, take the one with less
	       mismatch */
	    score = (long)(4999.0 -
			   long_edge_mismatch * short_edge_mismatch * 4999.0 /
			   LONG_EDGE_LENGTH_MATCH_LIMIT /
			   SHORT_EDGE_LENGTH_MATCH_LIMIT);
	    if (score < 0) score = 0;
	  }
	}

        if (margins_matched)
	  score += PAGESIZE_SCORE_SIZE_MARGINS * 10000;
	else if (size_matched)
	  score += PAGESIZE_SCORE_SIZE * 10000;

	if (size_matched || imageable_area_matched) {
	  if (!strcasecmp(cups->pageSizeRequested, size->name))
	    name_requested_matched = 1;
	  else
	    score -= 1000;
	}

	if (score > best_score)
	{
	  best_score = score;
	  if (score > 0)
	    best_size = size;
	}
#ifdef CUPS_DEBUG
	dmprintf1(pdev->memory, "DEBUG2: Checking against PPD page size (portrait): %s\n",
		  size->name);
	dmprintf2(pdev->memory, "DEBUG2:    Width: %.2f; Height: %.2f\n",
		  size->width, size->length);
	dmprintf4(pdev->memory, "DEBUG2:    Margins: Left: %.2f; Right: %.2f; Top: %.2f; Bottom: %.2f\n",
		  size->left, size->right, size->top, size->bottom);
	dmprintf4(pdev->memory, "DEBUG2:    Size mismatch: Long Edge (%.3f): %.5f; Short Edge (%.3f): %.5f\n",
		  LONG_EDGE_LENGTH_MATCH_LIMIT, long_edge_mismatch,
		  SHORT_EDGE_LENGTH_MATCH_LIMIT, short_edge_mismatch);
	dmprintf4(pdev->memory, "DEBUG2:    Match: Size: %d; Margins: %d; Imageable Area: %d; Name requested: %d\n",
		  size_matched, margins_matched, imageable_area_matched,
		  name_requested_matched);
	dmprintf2(pdev->memory, "DEBUG2:    Score: %ld; Best Score: %ld\n",
		  score, best_score);
#endif /* CUPS_DEBUG */
      }

      if (best_size)
      {
       /*
	* Standard size...
	*/

#ifdef CUPS_DEBUG
	dmprintf1(pdev->memory, "DEBUG: size = %s\n", best_size->name);
#endif /* CUPS_DEBUG */

	mediasize[0] = best_size->width;
	mediasize[1] = best_size->length;

	cups->landscape = 0;

#ifdef CUPS_RASTER_SYNCv1
	strncpy(cups->header.cupsPageSizeName, best_size->name,
		sizeof(cups->header.cupsPageSizeName));
	cups->header.cupsPageSizeName[sizeof(cups->header.cupsPageSizeName) - 1] =
	  '\0';

	if (strcasecmp(cups->header.MediaClass, "PwgRaster") != 0)
	{
#endif
	  margins[0] = best_size->left / 72.0;
	  margins[1] = best_size->bottom / 72.0;
	  margins[2] = (best_size->width - best_size->right) / 72.0;
	  margins[3] = (best_size->length - best_size->top) / 72.0;
	  if (xflip == 1)
	  {
	    swap = margins[0]; margins[0] = margins[2]; margins[2] = swap;
	  }
	  if (yflip == 1)
	  {
	    swap = margins[1]; margins[1] = margins[3]; margins[3] = swap;
	  }
#ifdef CUPS_RASTER_SYNCv1
	}
	else
	{
	  margins[0] = 0.0;
	  margins[1] = 0.0;
	  margins[2] = 0.0;
	  margins[3] = 0.0;
	}
#endif
      }
      else
      {
       /*
	* No matching portrait size; look for a matching size in
	* landscape orientation...
	*/

	best_score = -1;
	best_size = NULL;
	for (i = cups->PPD->num_sizes, size = cups->PPD->sizes;
	     i > 0;
	     i --, size ++)
	{
	  if (size->length == 0 || size->width == 0) continue;

	  score = 0;
	  size_matched = 0;
	  margins_matched = 0;
	  imageable_area_matched = 0;
	  name_requested_matched = 0;

	  long_edge_mismatch =
	    fabs(cups->MediaSize[0] - size->length)/size->length +
	    LONG_EDGE_LENGTH_MATCH_LIMIT / 100;
	  short_edge_mismatch =
	    fabs(cups->MediaSize[1] - size->width)/size->width +
	    SHORT_EDGE_LENGTH_MATCH_LIMIT / 100;
	  if (size->length < size->width)
	  {
	    swap = long_edge_mismatch;
	    long_edge_mismatch = short_edge_mismatch;
	    short_edge_mismatch = swap;
	  }

	  if (long_edge_mismatch < LONG_EDGE_LENGTH_MATCH_LIMIT &&
	      short_edge_mismatch < SHORT_EDGE_LENGTH_MATCH_LIMIT)
	  {
	    size_matched = 1;
	    /* If two sizes match within the limits, take the one with less
	       mismatch */
	    score = (long)(9999.0 -
			   long_edge_mismatch * short_edge_mismatch * 9999.0 /
			   LONG_EDGE_LENGTH_MATCH_LIMIT /
			   SHORT_EDGE_LENGTH_MATCH_LIMIT);
	    if (score < 0) score = 0;
	    /* We check whether all 4 margins match with the margin info
	       of the page size in the PPD. Here we check also for swapped
	       left/right and top/bottom margins as the cups->HWMargins
	       info can be from the previous page and there the margins
	       can be swapped due to duplex printing requirements */
	    if (!margins_set ||
		(((fabs(cups->HWMargins[1] - size->left) < 1.0 &&
		   fabs(cups->HWMargins[3] - size->width + size->right) < 1.0)||
		  (fabs(cups->HWMargins[1] - size->width + size->right) < 1.0 &&
		   fabs(cups->HWMargins[3] - size->left) < 1.0)) &&
		 ((fabs(cups->HWMargins[0] - size->bottom) < 1.0 &&
		   fabs(cups->HWMargins[2] - size->length + size->top) < 1.0) ||
		  (fabs(cups->HWMargins[0] - size->length + size->top) < 1.0 &&
		   fabs(cups->HWMargins[2] - size->bottom) < 1.0))))
	      margins_matched = 1;
	  } else {
	    /* Compare the dimensions of the imageable area against the
	       the input page size */
	    long_edge_mismatch =
	      fabs(cups->MediaSize[0] - size->top + size->bottom)/size->length +
	      LONG_EDGE_LENGTH_MATCH_LIMIT / 100;
	    short_edge_mismatch =
	      fabs(cups->MediaSize[1] - size->right + size->left)/size->width +
	      SHORT_EDGE_LENGTH_MATCH_LIMIT / 100;
	    if (size->length < size->width)
	    {
	      swap = long_edge_mismatch;
	      long_edge_mismatch = short_edge_mismatch;
	      short_edge_mismatch = swap;
	    }

	    if (long_edge_mismatch < LONG_EDGE_LENGTH_MATCH_LIMIT &&
		short_edge_mismatch < SHORT_EDGE_LENGTH_MATCH_LIMIT)
	    {
	      imageable_area_matched = 1;
	      /* If two sizes match within the limits, take the one with less
		 mismatch */
	      score = (long)(4999.0 -
			     long_edge_mismatch * short_edge_mismatch * 4999.0 /
			     LONG_EDGE_LENGTH_MATCH_LIMIT /
			     SHORT_EDGE_LENGTH_MATCH_LIMIT);
	      if (score < 0) score = 0;
	    }
	  }

	  if (margins_matched)
	    score += PAGESIZE_SCORE_SIZE_MARGINS * 10000;
	  else if (size_matched)
	    score += PAGESIZE_SCORE_SIZE * 10000;

	  if (size_matched || imageable_area_matched) {
	    if (!strcasecmp(cups->pageSizeRequested, size->name))
	      name_requested_matched = 1;
	    else
	      score -= 1000;
	  }

	  if (score > best_score)
	  {
	    best_score = score;
	    if (score > 0)
	      best_size = size;
	  }
#ifdef CUPS_DEBUG
	  dmprintf1(pdev->memory, "DEBUG2: Checking against PPD page size (landscape): %s\n",
		    size->name);
	  dmprintf2(pdev->memory, "DEBUG2:    Width: %.2f; Height: %.2f\n",
		    size->width, size->length);
	  dmprintf4(pdev->memory, "DEBUG2:    Margins: Left: %.2f; Right: %.2f; Top: %.2f; Bottom: %.2f\n",
		    size->left, size->right, size->top, size->bottom);
	  dmprintf4(pdev->memory, "DEBUG2:    Size mismatch: Long Edge (%.3f): %.5f; Short Edge (%.3f): %.5f\n",
		    LONG_EDGE_LENGTH_MATCH_LIMIT, long_edge_mismatch,
		    SHORT_EDGE_LENGTH_MATCH_LIMIT, short_edge_mismatch);
	  dmprintf4(pdev->memory, "DEBUG2:    Match: Size: %d; Margins: %d; Imageable Area: %d; Name requested: %d\n",
		    size_matched, margins_matched, imageable_area_matched,
		    name_requested_matched);
	  dmprintf2(pdev->memory, "DEBUG2:    Score: %ld; Best Score: %ld\n",
		    score, best_score);
#endif /* CUPS_DEBUG */
	}

	if (best_size)
	{
	 /*
	  * Standard size in landscape orientation...
	  */

#ifdef CUPS_DEBUG
	  dmprintf1(pdev->memory, "DEBUG: landscape size = %s\n", best_size->name);
#endif /* CUPS_DEBUG */

	  mediasize[0] = best_size->length;
	  mediasize[1] = best_size->width;

          cups->landscape = 1;

#ifdef CUPS_RASTER_SYNCv1
	  strncpy(cups->header.cupsPageSizeName, best_size->name,
		  sizeof(cups->header.cupsPageSizeName));
	  cups->header.cupsPageSizeName[sizeof(cups->header.cupsPageSizeName) - 1] =
	    '\0';

	  if (strcasecmp(cups->header.MediaClass, "PwgRaster") != 0)
	  {
#endif
	    margins[0] = (best_size->length - best_size->top) / 72.0;
	    margins[1] = best_size->left / 72.0;
	    margins[2] = best_size->bottom / 72.0;
	    margins[3] = (best_size->width - best_size->right) / 72.0;
	    if (xflip == 1)
	    {
	      swap = margins[1]; margins[1] = margins[3]; margins[3] = swap;
	    }
	    if (yflip == 1)
	    {
	      swap = margins[0]; margins[0] = margins[2]; margins[2] = swap;
	    }
#ifdef CUPS_RASTER_SYNCv1
	  }
	  else
	  {
	    margins[0] = 0.0;
	    margins[1] = 0.0;
	    margins[2] = 0.0;
	    margins[3] = 0.0;
	  }
#endif
	}
	else
	{
	 /*
	  * Custom size...
	  */

#ifdef CUPS_DEBUG
          dmprintf(pdev->memory, "DEBUG: size = Custom\n");
#endif /* CUPS_DEBUG */

#ifdef CUPS_RASTER_SYNCv1
	  snprintf(cups->header.cupsPageSizeName,
		   sizeof(cups->header.cupsPageSizeName),
		   "Custom.%.2fx%.2f",
		   cups->MediaSize[0], cups->MediaSize[1]);
#endif

	  /* Rotate page if it only fits into the printer's dimensions
	     when rotated */
	  if (((cups->MediaSize[0] > cups->PPD->custom_max[0]) ||
	       (cups->MediaSize[1] > cups->PPD->custom_max[1])) &&
	      ((cups->MediaSize[0] <= cups->PPD->custom_max[1]) &&
	      (cups->MediaSize[1] <= cups->PPD->custom_max[0]))) {
	    /* Rotate */
	    mediasize[0] = cups->MediaSize[1];
	    mediasize[1] = cups->MediaSize[0];

	    cups->landscape = 1;

#ifdef CUPS_RASTER_SYNCv1
	    if (strcasecmp(cups->header.MediaClass, "PwgRaster") != 0)
	    {
#endif
	      margins[0] = cups->PPD->custom_margins[3] / 72.0;
	      margins[1] = cups->PPD->custom_margins[0] / 72.0;
	      margins[2] = cups->PPD->custom_margins[1] / 72.0;
	      margins[3] = cups->PPD->custom_margins[2] / 72.0;
	      if (xflip == 1)
	      {
		swap = margins[1]; margins[1] = margins[3]; margins[3] = swap;
	      }
	      if (yflip == 1)
	      {
		swap = margins[0]; margins[0] = margins[2]; margins[2] = swap;
	      }
#ifdef CUPS_RASTER_SYNCv1
	    }
	    else
	    {
	      margins[0] = 0.0;
	      margins[1] = 0.0;
	      margins[2] = 0.0;
	      margins[3] = 0.0;
	    }
#endif
	  }
	  else
	  {
	    /* Do not rotate */
	    mediasize[0] = cups->MediaSize[0];
	    mediasize[1] = cups->MediaSize[1];

	    cups->landscape = 0;

#ifdef CUPS_RASTER_SYNCv1
	    if (strcasecmp(cups->header.MediaClass, "PwgRaster") != 0)
	    {
#endif
	      for (i = 0; i < 4; i ++)
		margins[i] = cups->PPD->custom_margins[i] / 72.0;
	      if (xflip == 1)
	      {
		swap = margins[0]; margins[0] = margins[2]; margins[2] = swap;
	      }
	      if (yflip == 1)
	      {
		swap = margins[1]; margins[1] = margins[3]; margins[3] = swap;
	      }
#ifdef CUPS_RASTER_SYNCv1
	    }
	    else
	    {
	      margins[0] = 0.0;
	      margins[1] = 0.0;
	      margins[2] = 0.0;
	      margins[3] = 0.0;
	    }
#endif
	  }
	}
      }

#ifdef CUPS_DEBUG
      dmprintf4(pdev->memory, "DEBUG: margins[] = [ %f %f %f %f ]\n",
                margins[0], margins[1], margins[2], margins[3]);
#endif /* CUPS_DEBUG */
    }
    else
    {
      /* No PPD file available */
      mediasize[0] = cups->MediaSize[0];
      mediasize[1] = cups->MediaSize[1];
#ifdef CUPS_RASTER_SYNCv1
      if (strcasecmp(cups->header.MediaClass, "PwgRaster") != 0)
      {
#endif
	/* If we do not have a PPD file, make sure that margins given via the
	   input file or via something like
	   "-c '<</.HWMargins[12 12 12 12] /Margins[0 0]>>setpagedevice'"
	   on the command line are conserved */
	margins[0] = pdev->HWMargins[0] / 72.0;
	margins[1] = pdev->HWMargins[1] / 72.0;
	margins[2] = pdev->HWMargins[2] / 72.0;
	margins[3] = pdev->HWMargins[3] / 72.0;
#ifdef CUPS_RASTER_SYNCv1
      }
      else
      {
	margins[0] = 0.0;
	margins[1] = 0.0;
	margins[2] = 0.0;
	margins[3] = 0.0;
      }
#endif
    }

   /*
    * Set the media size and margins to update the bitmap size...
    */

    for (i = 0; i < 2; i ++)
      cups_mediasize[i] = mediasize[i];
    for (i = 0; i < 4; i ++)
      cups_margins[i] = margins[i] * 72.0;
    if (best_score > 0 && best_score < 5000) {
#ifdef CUPS_DEBUG
      dmputs(pdev->memory, "DEBUG: Imageable area fit!\n");
#endif /* CUPS_DEBUG */
      /* Page size matched by imageable area */
      for (i = 0; i < 2; i ++)
	mediasize[i] = cups->MediaSize[i];
      for (i = 0; i < 4; i ++)
	margins[i] = 0.0;
    }
    gx_device_set_media_size(pdev, mediasize[0], mediasize[1]);
    gx_device_set_margins(pdev, margins, false);
  } else {
    /* No size change, use the current size in CUPS Raster header */
    for (i = 0; i < 2; i ++)
      cups_mediasize[i] = pdev->MediaSize[i];
    for (i = 0; i < 4; i ++)
      cups_margins[i] = pdev->HWMargins[i];
  }

 /*
  * Reallocate memory if the size or color depth was changed...
  */

  if (color_set || size_set)
  {
   /*
    * Make sure the page image is the correct size - current Ghostscript
    * does not keep track of the margins in the bitmap size...
    */

    if (cups->landscape)
    {
      width  = (pdev->MediaSize[1] - pdev->HWMargins[1] - pdev->HWMargins[3]) *
               pdev->HWResolution[0] / 72.0f + 0.499f;
      height = (pdev->MediaSize[0] - pdev->HWMargins[0] - pdev->HWMargins[2]) *
               pdev->HWResolution[1] / 72.0f + 0.499f;
    }
    else
    {
      width  = (pdev->MediaSize[0] - pdev->HWMargins[0] - pdev->HWMargins[2]) *
               pdev->HWResolution[0] / 72.0f + 0.499f;
      height = (pdev->MediaSize[1] - pdev->HWMargins[1] - pdev->HWMargins[3]) *
               pdev->HWResolution[1] / 72.0f + 0.499f;
    }

    if (width <= 0 || height <= 0) {
      dmprintf(pdev->memory, "ERROR: page margins overlap\n");
      return_error(gs_error_rangecheck);
    }

#ifdef CUPS_RASTER_SYNCv1
    if (cups->header.cupsBorderlessScalingFactor > 1.0)
    {
      width  *= cups->header.cupsBorderlessScalingFactor;
      height *= cups->header.cupsBorderlessScalingFactor;
    }
#endif /* CUPS_RASTER_SYNCv1 */

    pdev->width  = width;
    pdev->height = height;

   /*
    * Don't reallocate memory unless the device has been opened...
    * Also reallocate only if the size has actually changed...
    */

    if (pdev->is_open)
    {

     /*
      * Device is open and size has changed, so reallocate...
      */

#ifdef CUPS_DEBUG
      dmprintf4(pdev->memory, "DEBUG2: Reallocating memory, [%.0f %.0f] = %dx%d pixels...\n",
                pdev->MediaSize[0], pdev->MediaSize[1], width, height);
#endif /* CUPS_DEBUG */

      if ((code = gdev_prn_maybe_realloc_memory((gx_device_printer *)pdev,
                                                &sp_old,
						width_old, height_old,
						transp_old))
	  < 0)
        goto done;
#ifdef CUPS_DEBUG
      dmprintf4(pdev->memory, "DEBUG2: Reallocated memory, [%.0f %.0f] = %dx%d pixels...\n",
                pdev->MediaSize[0], pdev->MediaSize[1], width, height);
#endif /* CUPS_DEBUG */
    }
    else
    {
     /*
      * Device isn't yet open, so just save the new width and height...
      */

#ifdef CUPS_DEBUG
      dmprintf4(pdev->memory, "DEBUG: Setting initial media size, [%.0f %.0f] = %dx%d pixels...\n",
                pdev->MediaSize[0], pdev->MediaSize[1], width, height);
#endif /* CUPS_DEBUG */

      pdev->width  = width;
      pdev->height = height;
    }
  }

 /*
  * Set CUPS raster header values...
  */

  cups->header.HWResolution[0] = pdev->HWResolution[0];
  cups->header.HWResolution[1] = pdev->HWResolution[1];

#ifdef CUPS_RASTER_SYNCv1

  if (cups->landscape)
  {
    cups->header.cupsPageSize[0] = cups_mediasize[1];
    cups->header.cupsPageSize[1] = cups_mediasize[0];

    if ((sf = cups->header.cupsBorderlessScalingFactor) < 1.0)
      sf = 1.0;

    cups->header.PageSize[0] = (cups_mediasize[1] * sf) + 0.5;
    cups->header.PageSize[1] = (cups_mediasize[0] * sf) + 0.5;

    if (strcasecmp(cups->header.MediaClass, "PwgRaster") != 0)
    {
      cups->header.Margins[0] = (cups_margins[1] * sf) + 0.5;
      cups->header.Margins[1] = (cups_margins[2] * sf) + 0.5;
      cups->header.ImagingBoundingBox[0] = (cups_margins[1] * sf) + 0.5;
      cups->header.ImagingBoundingBox[1] = (cups_margins[2] * sf) + 0.5;
      cups->header.ImagingBoundingBox[2] = ((cups_mediasize[1] -
					     cups_margins[3]) * sf) + 0.5;
      cups->header.ImagingBoundingBox[3] = ((cups_mediasize[0] -
					     cups_margins[0]) * sf) + 0.5;
      cups->header.cupsImagingBBox[0] = cups_margins[1];
      cups->header.cupsImagingBBox[1] = cups_margins[2];
      cups->header.cupsImagingBBox[2] = cups_mediasize[1] - cups_margins[3];
      cups->header.cupsImagingBBox[3] = cups_mediasize[0] - cups_margins[0];
    }
    else
    {
      for (i = 0; i < 2; i ++)
	cups->header.Margins[i] = 0;
      for (i = 0; i < 4; i ++)
      {
	cups->header.ImagingBoundingBox[i] = 0;
	cups->header.cupsImagingBBox[i] = 0.0;
      }
    }
  }
  else
  {
    cups->header.cupsPageSize[0] = cups_mediasize[0];
    cups->header.cupsPageSize[1] = cups_mediasize[1];

    if ((sf = cups->header.cupsBorderlessScalingFactor) < 1.0)
      sf = 1.0;

    cups->header.PageSize[0] = (cups_mediasize[0] * sf) + 0.5;
    cups->header.PageSize[1] = (cups_mediasize[1] * sf) + 0.5;

    if (strcasecmp(cups->header.MediaClass, "PwgRaster") != 0)
    {
      cups->header.Margins[0] = (cups_margins[0] * sf) + 0.5;
      cups->header.Margins[1] = (cups_margins[1] * sf) + 0.5;
      cups->header.ImagingBoundingBox[0] = (cups_margins[0] * sf) + 0.5;
      cups->header.ImagingBoundingBox[1] = (cups_margins[1] * sf) + 0.5;
      cups->header.ImagingBoundingBox[2] = ((cups_mediasize[0] -
					     cups_margins[2]) * sf) + 0.5;
      cups->header.ImagingBoundingBox[3] = ((cups_mediasize[1] -
					     cups_margins[3]) * sf) + 0.5;
      cups->header.cupsImagingBBox[0] = cups_margins[0];
      cups->header.cupsImagingBBox[1] = cups_margins[1];
      cups->header.cupsImagingBBox[2] = cups_mediasize[0] - cups_margins[2];
      cups->header.cupsImagingBBox[3] = cups_mediasize[1] - cups_margins[3];
    }
    else
    {
      for (i = 0; i < 2; i ++)
	cups->header.Margins[i] = 0;
      for (i = 0; i < 4; i ++)
      {
	cups->header.ImagingBoundingBox[i] = 0;
	cups->header.cupsImagingBBox[i] = 0.0;
      }
    }
  }

#else

  if (cups->landscape)
  {
    cups->header.PageSize[0] = cups_mediasize[1] + 0.5;
    cups->header.PageSize[1] = cups_mediasize[0] + 0.5;

    if (strcasecmp(cups->header.MediaClass, "PwgRaster") != 0)
    {
      cups->header.Margins[0] = (cups_margins[1]) + 0.5;
      cups->header.Margins[1] = (cups_margins[2]) + 0.5;
      cups->header.ImagingBoundingBox[0] = (cups_margins[1]) + 0.5;
      cups->header.ImagingBoundingBox[1] = (cups_margins[0]) + 0.5;
      cups->header.ImagingBoundingBox[2] = (cups_mediasize[1] -
					    cups_margins[3]) + 0.5;
      cups->header.ImagingBoundingBox[3] = (cups_mediasize[0] -
					    cups_margins[2]) + 0.5;
    }
    else
    {
      for (i = 0; i < 2; i ++)
	cups->header.Margins[i] = 0;
      for (i = 0; i < 4; i ++)
	cups->header.ImagingBoundingBox[i] = 0;
    }
  }
  else
  {
    cups->header.PageSize[0] = cups_mediasize[0] + 0.5;
    cups->header.PageSize[1] = cups_mediasize[1] + 0.5;

    if (strcasecmp(cups->header.MediaClass, "PwgRaster") != 0)
    {
      cups->header.Margins[0] = (cups_margins[0]) + 0.5;
      cups->header.Margins[1] = (cups_margins[1]) + 0.5;
      cups->header.ImagingBoundingBox[0] = (cups_margins[0]) + 0.5;
      cups->header.ImagingBoundingBox[1] = (cups_margins[3]) + 0.5;
      cups->header.ImagingBoundingBox[2] = (cups_mediasize[0] -
					    cups_margins[2]) + 0.5;
      cups->header.ImagingBoundingBox[3] = (cups_mediasize[1] -
					    cups_margins[1]) + 0.5;
    }
    else
    {
      for (i = 0; i < 2; i ++)
	cups->header.Margins[i] = 0;
      for (i = 0; i < 4; i ++)
	cups->header.ImagingBoundingBox[i] = 0;
    }
  }

#endif /* CUPS_RASTER_SYNCv1 */
  cups->header.cupsWidth  = cups->width;
  cups->header.cupsHeight = cups->height;

#ifdef CUPS_DEBUG
  if (size_set) {
    dmprintf2(pdev->memory, "DEBUG2: mediasize = [ %.3f %.3f ]\n",
              mediasize[0], mediasize[1]);
    dmprintf4(pdev->memory, "DEBUG2: margins = [ %.3f %.3f %.3f %.3f ]\n",
              margins[0], margins[1], margins[2], margins[3]);
  }
  dmprintf2(pdev->memory, "DEBUG2: cups_mediasize = [ %.3f %.3f ]\n",
	    cups_mediasize[0], cups_mediasize[1]);
  dmprintf4(pdev->memory, "DEBUG2: cups_margins = [ %.3f %.3f %.3f %.3f ]\n",
	    cups_margins[0], cups_margins[1], cups_margins[2], cups_margins[3]);
  dmprintf1(pdev->memory, "DEBUG2: ppd = %p\n", cups->PPD);
  dmprintf2(pdev->memory, "DEBUG2: PageSize = [ %.3f %.3f ]\n",
            pdev->MediaSize[0], pdev->MediaSize[1]);
  dmprintf2(pdev->memory, "DEBUG2: HWResolution = [ %.3f %.3f ]\n",
            pdev->HWResolution[0], pdev->HWResolution[1]);
  dmprintf2(pdev->memory, "DEBUG2: width = %d, height = %d\n",
            pdev->width, pdev->height);
  dmprintf4(pdev->memory, "DEBUG2: HWMargins = [ %.3f %.3f %.3f %.3f ]\n",
            pdev->HWMargins[0], pdev->HWMargins[1],
            pdev->HWMargins[2], pdev->HWMargins[3]);
  dmprintf2(pdev->memory, "DEBUG2: cups->header.cupsWidth = %d, cups->header.cupsHeight = %d\n",
            cups->header.cupsWidth, cups->header.cupsHeight);
  dmprintf2(pdev->memory, "DEBUG2: cups->header.PageSize[0] = %d, cups->header.PageSize[1] = %d\n",
            cups->header.PageSize[0], cups->header.PageSize[1]);
  dmprintf4(pdev->memory, "DEBUG2: cups->header.ImagingBoundingBox = [ %d %d %d %d ]\n",
            cups->header.ImagingBoundingBox[0],
	    cups->header.ImagingBoundingBox[1],
            cups->header.ImagingBoundingBox[2],
	    cups->header.ImagingBoundingBox[3]);
#ifdef CUPS_RASTER_SYNCv1
  dmprintf2(pdev->memory, "DEBUG2: cups->header.cupsPageSize[0] = %.3f, cups->header.cupsPageSize[1] = %.3f\n",
            cups->header.cupsPageSize[0], cups->header.cupsPageSize[1]);
  dmprintf4(pdev->memory, "DEBUG2: cups->header.cupsImagingBBox = [ %.3f %.3f %.3f %.3f ]\n",
            cups->header.cupsImagingBBox[0], cups->header.cupsImagingBBox[1],
            cups->header.cupsImagingBBox[2], cups->header.cupsImagingBBox[3]);
  dmprintf1(pdev->memory, "DEBUG2: cups->header.cupsBorderlessScalingFactor = %.3f\n",
            cups->header.cupsBorderlessScalingFactor);
#endif /* CUPS_RASTER_SYNCv1 */
#endif /* CUPS_DEBUG */

done:
  return code;
}

/*
 * 'cups_set_color_info()' - Set the color information structure based on
 *                           the required output.
 */

private int
cups_set_color_info(gx_device *pdev)	/* I - Device info */
{
  int		i, j, k;		/* Looping vars */
  int		max_lut;		/* Maximum LUT value */
  float		d, g;			/* Density and gamma correction */
  float		m[3][3];		/* Color correction matrix */
  char		resolution[41];		/* Resolution string */
  ppd_profile_t	*profile;		/* Color profile information */
  int           code = 0;

#ifdef CUPS_DEBUG
  dmprintf1(pdev->memory, "DEBUG2: cups_set_color_info(%p)\n", pdev);
#endif /* CUPS_DEBUG */

#ifndef GX_COLOR_INDEX_TYPE
  if (cups->header.cupsBitsPerColor > 8)
    cups->header.cupsBitsPerColor = 8;
#endif /* !GX_COLOR_INDEX_TYPE */

  switch (cups->header.cupsColorSpace)
  {
    default :
    case CUPS_CSPACE_W :
    case CUPS_CSPACE_SW :
    case CUPS_CSPACE_K :
    case CUPS_CSPACE_WHITE :
    case CUPS_CSPACE_GOLD :
    case CUPS_CSPACE_SILVER :
#ifdef CUPS_RASTER_SYNCv1
	cups->header.cupsNumColors      = 1;
#endif /* CUPS_RASTER_SYNCv1 */
        cups->header.cupsBitsPerPixel   = cups->header.cupsBitsPerColor;
        cups->color_info.depth          = cups->header.cupsBitsPerPixel;
        cups->color_info.num_components = 1;
        cups->color_info.dither_grays = 1L << cups->header.cupsBitsPerColor;
        cups->color_info.dither_colors = 1L << cups->header.cupsBitsPerColor;
        cups->color_info.max_gray = cups->color_info.dither_grays - 1;
        cups->color_info.max_color = cups->color_info.dither_grays - 1;
        break;

    case CUPS_CSPACE_CMY :
    case CUPS_CSPACE_YMC :
    case CUPS_CSPACE_RGB :
    case CUPS_CSPACE_SRGB :
    case CUPS_CSPACE_ADOBERGB :
#ifdef CUPS_RASTER_SYNCv1
	cups->header.cupsNumColors      = 3;
#endif /* CUPS_RASTER_SYNCv1 */
        if (cups->header.cupsColorOrder != CUPS_ORDER_CHUNKED)
          cups->header.cupsBitsPerPixel = cups->header.cupsBitsPerColor;
	else if (cups->header.cupsBitsPerColor < 8)
	  cups->header.cupsBitsPerPixel = 4 * cups->header.cupsBitsPerColor;
	else
	  cups->header.cupsBitsPerPixel = 3 * cups->header.cupsBitsPerColor;

	if (cups->header.cupsBitsPerColor < 8)
	  cups->color_info.depth = 4 * cups->header.cupsBitsPerColor;
	else
	  cups->color_info.depth = 3 * cups->header.cupsBitsPerColor;

        cups->color_info.num_components = 3;
        break;

    case CUPS_CSPACE_KCMYcm :
        if (cups->header.cupsBitsPerColor == 1)
	{
#ifdef CUPS_RASTER_SYNCv1
	  cups->header.cupsNumColors      = 6;
#endif /* CUPS_RASTER_SYNCv1 */
	  cups->header.cupsBitsPerPixel   = 8;
	  cups->color_info.depth          = 8;
	  cups->color_info.num_components = 4;
	  break;
	}

    case CUPS_CSPACE_RGBA :
    case CUPS_CSPACE_RGBW :
#ifdef CUPS_RASTER_SYNCv1
        cups->header.cupsNumColors = 4;
#endif /* CUPS_RASTER_SYNCv1 */
        if (cups->header.cupsColorOrder != CUPS_ORDER_CHUNKED)
            cups->header.cupsBitsPerPixel = cups->header.cupsBitsPerColor;
        else
            cups->header.cupsBitsPerPixel = 4 * cups->header.cupsBitsPerColor;

        cups->color_info.depth = 4 * cups->header.cupsBitsPerColor;
        cups->color_info.num_components = 3;
        break;

    case CUPS_CSPACE_CMYK :
    case CUPS_CSPACE_YMCK :
    case CUPS_CSPACE_KCMY :
    case CUPS_CSPACE_GMCK :
    case CUPS_CSPACE_GMCS :
#ifdef CUPS_RASTER_SYNCv1
	cups->header.cupsNumColors = 4;
#endif /* CUPS_RASTER_SYNCv1 */
        if (cups->header.cupsColorOrder != CUPS_ORDER_CHUNKED)
            cups->header.cupsBitsPerPixel = cups->header.cupsBitsPerColor;
	    else
	      cups->header.cupsBitsPerPixel = 4 * cups->header.cupsBitsPerColor;

        cups->color_info.depth          = 4 * cups->header.cupsBitsPerColor;
        cups->color_info.num_components = 4;
        break;

#ifdef CUPS_RASTER_HAVE_COLORIMETRIC
    case CUPS_CSPACE_CIEXYZ :
    case CUPS_CSPACE_CIELab :
    case CUPS_CSPACE_ICC1 :
    case CUPS_CSPACE_ICC2 :
    case CUPS_CSPACE_ICC3 :
    case CUPS_CSPACE_ICC4 :
    case CUPS_CSPACE_ICC5 :
    case CUPS_CSPACE_ICC6 :
    case CUPS_CSPACE_ICC7 :
    case CUPS_CSPACE_ICC8 :
    case CUPS_CSPACE_ICC9 :
    case CUPS_CSPACE_ICCA :
    case CUPS_CSPACE_ICCB :
    case CUPS_CSPACE_ICCC :
    case CUPS_CSPACE_ICCD :
    case CUPS_CSPACE_ICCE :
    case CUPS_CSPACE_ICCF :
       /*
	* Colorimetric color spaces currently are implemented as 24-bit
	* mapping to XYZ or Lab, which are then converted as needed to
	* the final representation...
	*
	* This code enforces a minimum output depth of 8 bits per
	* component...
	*/

#ifdef CUPS_RASTER_SYNCv1
	cups->header.cupsNumColors = 3;
#endif /* CUPS_RASTER_SYNCv1 */

	if (cups->header.cupsBitsPerColor < 8)
          cups->header.cupsBitsPerColor = 8;

	if (cups->header.cupsColorOrder != CUPS_ORDER_CHUNKED)
          cups->header.cupsBitsPerPixel = cups->header.cupsBitsPerColor;
	else
          cups->header.cupsBitsPerPixel = 3 * cups->header.cupsBitsPerColor;

	cups->color_info.depth          = 24;
	cups->color_info.num_components = 3;
	break;
#endif /* CUPS_RASTER_HAVE_COLORIMETRIC */
  }

#ifdef dev_t_proc_encode_color
  switch (cups->header.cupsColorSpace)
  {
    default :
        cups->color_info.gray_index = GX_CINFO_COMP_NO_INDEX;
	break;

    case CUPS_CSPACE_W :
    case CUPS_CSPACE_SW :
    case CUPS_CSPACE_WHITE :
    case CUPS_CSPACE_K :
    case CUPS_CSPACE_GOLD :
    case CUPS_CSPACE_SILVER :
    case CUPS_CSPACE_KCMYcm :
    case CUPS_CSPACE_KCMY :
        cups->color_info.gray_index = 0;
	break;

    case CUPS_CSPACE_CMYK :
    case CUPS_CSPACE_YMCK :
    case CUPS_CSPACE_GMCK :
    case CUPS_CSPACE_GMCS :
        cups->color_info.gray_index = 3;
	break;
  }

  switch (cups->header.cupsColorSpace)
  {
    default :
    case CUPS_CSPACE_RGBW :
    case CUPS_CSPACE_W :
    case CUPS_CSPACE_SW :
    case CUPS_CSPACE_WHITE :
    case CUPS_CSPACE_RGB :
    case CUPS_CSPACE_CMY:
    case CUPS_CSPACE_YMC:
    case CUPS_CSPACE_SRGB :
    case CUPS_CSPACE_ADOBERGB :
    case CUPS_CSPACE_RGBA :
#  ifdef CUPS_RASTER_HAVE_COLORIMETRIC
    case CUPS_CSPACE_CIEXYZ :
    case CUPS_CSPACE_CIELab :
    case CUPS_CSPACE_ICC1 :
    case CUPS_CSPACE_ICC2 :
    case CUPS_CSPACE_ICC3 :
    case CUPS_CSPACE_ICC4 :
    case CUPS_CSPACE_ICC5 :
    case CUPS_CSPACE_ICC6 :
    case CUPS_CSPACE_ICC7 :
    case CUPS_CSPACE_ICC8 :
    case CUPS_CSPACE_ICC9 :
    case CUPS_CSPACE_ICCA :
    case CUPS_CSPACE_ICCB :
    case CUPS_CSPACE_ICCC :
    case CUPS_CSPACE_ICCD :
    case CUPS_CSPACE_ICCE :
    case CUPS_CSPACE_ICCF :
#  endif /* CUPS_RASTER_HAVE_COLORIMETRIC */
        cups->color_info.polarity = GX_CINFO_POLARITY_ADDITIVE;
        break;

    case CUPS_CSPACE_K :
    case CUPS_CSPACE_GOLD :
    case CUPS_CSPACE_SILVER :
    case CUPS_CSPACE_KCMYcm :
    case CUPS_CSPACE_CMYK :
    case CUPS_CSPACE_YMCK :
    case CUPS_CSPACE_KCMY :
    case CUPS_CSPACE_GMCK :
    case CUPS_CSPACE_GMCS :
        cups->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
        break;
  }

  cups->color_info.separable_and_linear = GX_CINFO_SEP_LIN_NONE;
#endif /* dev_t_proc_encode_color */

  i       = cups->header.cupsBitsPerColor;
  max_lut = (1 << i) - 1;

  switch (cups->color_info.num_components)
  {
    default :
    case 1 :
	cups->color_info.max_gray      = max_lut;
	cups->color_info.max_color     = 0;
	cups->color_info.dither_grays  = max_lut + 1;
	cups->color_info.dither_colors = 0;
        break;

    case 3 :
	cups->color_info.max_gray      = 0;
	cups->color_info.max_color     = max_lut;
	cups->color_info.dither_grays  = 0;
	cups->color_info.dither_colors = max_lut + 1;
	break;

    case 4 :
	cups->color_info.max_gray      = max_lut;
	cups->color_info.max_color     = max_lut;
	cups->color_info.dither_grays  = max_lut + 1;
	cups->color_info.dither_colors = max_lut + 1;
	break;
  }

 /*
  * Enable/disable CMYK color support...
  */

#ifdef dev_t_proc_encode_color
  cups->color_info.max_components = cups->color_info.num_components;
#endif /* dev_t_proc_encode_color */

 /*
  * Tell Ghostscript to forget any colors it has cached...
  */

  gx_device_decache_colors(pdev);

 /*
  * Compute the lookup tables...
  */

  for (i = 0; i <= gx_max_color_value; i ++)
  {
    j = (max_lut * i + gx_max_color_value / 2) / gx_max_color_value;

#if !ARCH_IS_BIG_ENDIAN
    if (max_lut > 255)
      j = ((j & 255) << 8) | ((j >> 8) & 255);
#endif /* !ARCH_IS_BIG_ENDIAN */

    cups->EncodeLUT[i] = j;

#ifdef CUPS_DEBUG2
    if (i == 0 || cups->EncodeLUT[i] != cups->EncodeLUT[i - 1])
      dmprintf2(pdev->memory, "DEBUG2: cups->EncodeLUT[%d] = %d\n", i,
                (int)cups->EncodeLUT[i]);
#endif /* CUPS_DEBUG2 */
  }

#ifdef CUPS_DEBUG2
  dmprintf1(pdev->memory, "DEBUG2: cups->EncodeLUT[0] = %d\n", (int)cups->EncodeLUT[0]);
  dmprintf2(pdev->memory, "DEBUG2: cups->EncodeLUT[%d] = %d\n", gx_max_color_value,
            (int)cups->EncodeLUT[gx_max_color_value]);
#endif /* CUPS_DEBUG2 */

  for (i = 0; i < cups->color_info.dither_grays; i ++) {
    j = i;
#if !ARCH_IS_BIG_ENDIAN
    if (max_lut > 255)
      j = ((j & 255) << 8) | ((j >> 8) & 255);
#endif /* !ARCH_IS_BIG_ENDIAN */
    cups->DecodeLUT[i] = gx_max_color_value * j / max_lut;
  }

#ifdef CUPS_DEBUG
  dmprintf2(pdev->memory, "DEBUG: num_components = %d, depth = %d\n",
            cups->color_info.num_components, cups->color_info.depth);
  dmprintf2(pdev->memory, "DEBUG: cupsColorSpace = %d, cupsColorOrder = %d\n",
            cups->header.cupsColorSpace, cups->header.cupsColorOrder);
  dmprintf2(pdev->memory, "DEBUG: cupsBitsPerPixel = %d, cupsBitsPerColor = %d\n",
            cups->header.cupsBitsPerPixel, cups->header.cupsBitsPerColor);
  dmprintf2(pdev->memory, "DEBUG: max_gray = %d, dither_grays = %d\n",
            cups->color_info.max_gray, cups->color_info.dither_grays);
  dmprintf2(pdev->memory, "DEBUG: max_color = %d, dither_colors = %d\n",
            cups->color_info.max_color, cups->color_info.dither_colors);
#endif /* CUPS_DEBUG */

 /*
  * Set the color profile as needed...
  */

  cups->HaveProfile = 0;

#ifdef dev_t_proc_encode_color
  if (cups->Profile)
#else
  if (cups->Profile && cups->header.cupsBitsPerColor == 8)
#endif /* dev_t_proc_encode_color */
  {
#ifdef CUPS_DEBUG
    dmprintf1(pdev->memory, "DEBUG: Using user-defined profile \"%s\"...\n", cups->Profile);
#endif /* CUPS_DEBUG */

    if (sscanf(cups->Profile, "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f", &d, &g,
               m[0] + 0, m[0] + 1, m[0] + 2,
               m[1] + 0, m[1] + 1, m[1] + 2,
               m[2] + 0, m[2] + 1, m[2] + 2) != 11)
      dmprintf(pdev->memory, "ERROR: User-defined profile does not contain 11 integers!\n");
    else
    {
      cups->HaveProfile = 1;

      d       *= 0.001f;
      g       *= 0.001f;
      m[0][0] *= 0.001f;
      m[0][1] *= 0.001f;
      m[0][2] *= 0.001f;
      m[1][0] *= 0.001f;
      m[1][1] *= 0.001f;
      m[1][2] *= 0.001f;
      m[2][0] *= 0.001f;
      m[2][1] *= 0.001f;
      m[2][2] *= 0.001f;
    }
  }
#ifdef dev_t_proc_encode_color
  else if (cups->PPD)
#else
  else if (cups->PPD && cups->header.cupsBitsPerColor == 8)
#endif /* dev_t_proc_encode_color */
  {
   /*
    * Find the appropriate color profile...
    */

    if (pdev->HWResolution[0] != pdev->HWResolution[1])
      sprintf(resolution, "%.0fx%.0fdpi", pdev->HWResolution[0],
              pdev->HWResolution[1]);
    else
      sprintf(resolution, "%.0fdpi", pdev->HWResolution[0]);

    for (i = 0, profile = cups->PPD->profiles;
         i < cups->PPD->num_profiles;
	 i ++, profile ++)
      if ((strcmp(profile->resolution, resolution) == 0 ||
           profile->resolution[0] == '-') &&
          (strcmp(profile->media_type, cups->header.MediaType) == 0 ||
           profile->media_type[0] == '-'))
	break;

   /*
    * If we found a color profile, use it!
    */

    if (i < cups->PPD->num_profiles)
    {
#ifdef CUPS_DEBUG
      dmprintf(pdev->memory, "DEBUG: Using color profile in PPD file!\n");
#endif /* CUPS_DEBUG */

      cups->HaveProfile = 1;

      d = profile->density;
      g = profile->gamma;

      memcpy(m, profile->matrix, sizeof(m));
    }
  }

  if (cups->HaveProfile)
  {
    for (i = 0; i < 3; i ++)
      for (j = 0; j < 3; j ++)
	for (k = 0; k <= CUPS_MAX_VALUE; k ++)
	{
          cups->Matrix[i][j][k] = (int)((float)k * m[i][j] + 0.5);

#ifdef CUPS_DEBUG
          if ((k & 4095) == 0)
            dmprintf4(pdev->memory, "DEBUG2: cups->Matrix[%d][%d][%d] = %d\n",
                      i, j, k, cups->Matrix[i][j][k]);
#endif /* CUPS_DEBUG */
        }


    for (k = 0; k <= CUPS_MAX_VALUE; k ++)
    {
      cups->Density[k] = (int)((float)CUPS_MAX_VALUE * d *
	                     pow((float)k / (float)CUPS_MAX_VALUE, g) +
			     0.5);

#ifdef CUPS_DEBUG
      if ((k & 4095) == 0)
        dmprintf2(pdev->memory, "DEBUG2: cups->Density[%d] = %d\n", k, cups->Density[k]);
#endif /* CUPS_DEBUG */
    }
  }
  else
  {
    for (k = 0; k <= CUPS_MAX_VALUE; k ++)
      cups->Density[k] = k;
  }
  if (!cups->user_icc) {
    /* Set up the ICC profile for ghostscript to use based upon the color space.
       This is different than the PPD profile above which appears to be some sort
       of matrix based TRC profile */
    switch (cups->header.cupsColorSpace)
    {
      default :
      case CUPS_CSPACE_RGBW :
      case CUPS_CSPACE_RGB :
      case CUPS_CSPACE_SRGB :
      case CUPS_CSPACE_ADOBERGB :
      case CUPS_CSPACE_RGBA :
      case CUPS_CSPACE_CMY :
      case CUPS_CSPACE_YMC :
#    ifdef CUPS_RASTER_HAVE_COLORIMETRIC
      case CUPS_CSPACE_CIELab :
      case CUPS_CSPACE_ICC1 :
      case CUPS_CSPACE_ICC2 :
      case CUPS_CSPACE_ICC3 :
      case CUPS_CSPACE_ICC4 :
      case CUPS_CSPACE_ICC5 :
      case CUPS_CSPACE_ICC6 :
      case CUPS_CSPACE_ICC7 :
      case CUPS_CSPACE_ICC8 :
      case CUPS_CSPACE_ICC9 :
      case CUPS_CSPACE_ICCA :
      case CUPS_CSPACE_ICCB :
      case CUPS_CSPACE_ICCC :
      case CUPS_CSPACE_ICCD :
      case CUPS_CSPACE_ICCE :
      case CUPS_CSPACE_ICCF :
#    endif /* CUPS_RASTER_HAVE_COLORIMETRIC */
        if (!pdev->icc_struct || (pdev->icc_struct &&
             pdev->icc_struct->device_profile[gsDEFAULTPROFILE]->data_cs != gsRGB)) {

          if (pdev->icc_struct) {
              rc_decrement(pdev->icc_struct, "cups_set_color_info");
          }
          pdev->icc_struct = gsicc_new_device_profile_array(pdev->memory);

          code = gsicc_set_device_profile(pdev, pdev->memory,
              (char *)DEFAULT_RGB_ICC, gsDEFAULTPROFILE);
          }
        break;

      case CUPS_CSPACE_W :
      case CUPS_CSPACE_SW :
      case CUPS_CSPACE_WHITE :
      case CUPS_CSPACE_K :
      case CUPS_CSPACE_GOLD :
      case CUPS_CSPACE_SILVER :
        if (!pdev->icc_struct || (pdev->icc_struct &&
             pdev->icc_struct->device_profile[gsDEFAULTPROFILE]->data_cs != gsGRAY)) {

          if (pdev->icc_struct) {
              rc_decrement(pdev->icc_struct, "cups_set_color_info");
          }
          pdev->icc_struct = gsicc_new_device_profile_array(pdev->memory);

          code = gsicc_set_device_profile(pdev, pdev->memory->non_gc_memory,
              (char *)DEFAULT_GRAY_ICC, gsDEFAULTPROFILE);
        }
        break;
      case CUPS_CSPACE_KCMYcm :
#    ifdef CUPS_RASTER_HAVE_COLORIMETRIC
      case CUPS_CSPACE_CIEXYZ :
#endif
      case CUPS_CSPACE_CMYK :
      case CUPS_CSPACE_YMCK :
      case CUPS_CSPACE_KCMY :
      case CUPS_CSPACE_GMCK :
      case CUPS_CSPACE_GMCS :
        if (!pdev->icc_struct || (pdev->icc_struct &&
             pdev->icc_struct->device_profile[gsDEFAULTPROFILE]->data_cs != gsCMYK)) {

          if (pdev->icc_struct) {
              rc_decrement(pdev->icc_struct, "cups_set_color_info");
          }
          pdev->icc_struct = gsicc_new_device_profile_array(pdev->memory);

          code = gsicc_set_device_profile(pdev, pdev->memory,
              (char *)DEFAULT_CMYK_ICC, gsDEFAULTPROFILE);
          }
        break;
    }
  }
  return(code);
}

/*
 * 'cups_sync_output()' - Keep the user informed of our status...
 */

private int				/* O - Error status */
cups_sync_output(gx_device *pdev)	/* I - Device info */
{
  dmprintf1(pdev->memory, "INFO: Processing page %d...\n", cups->page);

  return (0);
}


/*
 * 'cups_print_chunked()' - Print a page of chunked pixels.
 */

static int
cups_print_chunked(gx_device_printer *pdev,
					/* I - Printer device */
                   unsigned char     *src,
					/* I - Scanline buffer */
		   unsigned char     *dst,
					/* I - Bitmap buffer */
		   int               srcbytes)
					/* I - Number of bytes in src */
{
  int		y;			/* Looping var */
  unsigned char	*srcptr,		/* Pointer to data */
		*dstptr;		/* Pointer to bits */
  int		count;			/* Count for loop */
  int		xflip,			/* Flip scanline? */
#ifdef CUPS_DEBUG
                yflip,			/* Reverse scanline order? */
#endif
                ystart, yend, ystep;    /* Loop control for scanline order */
  ppd_attr_t    *backside = NULL;

#ifdef CUPS_DEBUG
  dmprintf1(pdev->memory, "DEBUG2: cups->header.Duplex = %d\n", cups->header.Duplex);
  dmprintf1(pdev->memory, "DEBUG2: cups->header.Tumble = %d\n", cups->header.Tumble);
  dmprintf1(pdev->memory, "DEBUG2: cups->page = %d\n", cups->page);
  dmprintf1(pdev->memory, "DEBUG2: cups->PPD = %p\n", cups->PPD);
#endif /* CUPS_DEBUG */

  if (cups->PPD) {
    backside = ppdFindAttr(cups->PPD, "cupsBackSide", NULL);
    if (backside) {
#ifdef CUPS_DEBUG
      dmprintf1(pdev->memory, "DEBUG2: cupsBackSide = %s\n", backside->value);
#endif /* CUPS_DEBUG */
      cups->PPD->flip_duplex = 0;
    }
  }
  if (cups->header.Duplex && cups->PPD &&
      ((!cups->header.Tumble &&
	(cups->PPD->flip_duplex ||
	 (backside && !strcasecmp(backside->value, "Rotated")))) ||
       (cups->header.Tumble &&
	(backside && (!strcasecmp(backside->value, "Flipped") ||
		      !strcasecmp(backside->value, "ManualTumble"))))) &&
      !(cups->page & 1))
    xflip = 1;
  else
    xflip = 0;
  if (cups->header.Duplex && cups->PPD &&
      ((!cups->header.Tumble &&
	(cups->PPD->flip_duplex ||
	 (backside && (!strcasecmp(backside->value, "Flipped") ||
		       !strcasecmp(backside->value, "Rotated"))))) ||
       (cups->header.Tumble &&
	(backside && !strcasecmp(backside->value, "ManualTumble")))) &&
      !(cups->page & 1)) {
#ifdef CUPS_DEBUG
    yflip = 1;
#endif
    ystart = cups->height - 1;
    yend = -1;
    ystep = -1;
  } else {
#ifdef CUPS_DEBUG
    yflip = 0;
#endif
    ystart = 0;
    yend = cups->height;
    ystep = 1;
  }

#ifdef CUPS_DEBUG
  dmprintf3(pdev->memory, "DEBUG: cups_print_chunked: xflip = %d, yflip = %d, height = %d\n",
            xflip, yflip, cups->height);
#endif /* CUPS_DEBUG */

 /*
  * Loop through the page bitmap and write chunked pixels, reversing as
  * needed...
  */
  for (y = ystart; y != yend; y += ystep)
  {
   /*
    * Grab the scanline data...
    */

    if (gdev_prn_get_bits((gx_device_printer *)pdev, y, src, &srcptr) < 0)
    {
      dmprintf1(pdev->memory, "ERROR: Unable to get scanline %d!\n", y);
      return_error(gs_error_unknownerror);
    }

    if (xflip)
    {
     /*
      * Flip the raster data before writing it...
      */

      if (srcptr[0] == 0 && memcmp(srcptr, srcptr + 1, srcbytes - 1) == 0)
	memset(dst, 0, cups->header.cupsBytesPerLine);
      else
      {
        dstptr = dst;
	count  = srcbytes;

	switch (cups->color_info.depth)
	{
          case 1 : /* B&W bitmap */
	      for (srcptr += srcbytes - 1;
	           count > 0;
		   count --, srcptr --, dstptr ++)
	      {
	        *dstptr = cups->RevUpper1[*srcptr & 15] |
		          cups->RevLower1[*srcptr >> 4];
              }
	      break;

	  case 2 : /* 2-bit W/K image */
	      for (srcptr += srcbytes - 1;
	           count > 0;
		   count --, srcptr --, dstptr ++)
	      {
	        *dstptr = cups->RevUpper2[*srcptr & 15] |
		          cups->RevLower2[*srcptr >> 4];
              }
	      break;

	  case 4 : /* 1-bit RGB/CMY/CMYK bitmap or 4-bit W/K image */
	      for (srcptr += srcbytes - 1;
	           count > 0;
		   count --, srcptr --, dstptr ++)
	        *dstptr = (*srcptr >> 4) | (*srcptr << 4);
	      break;

          case 8 : /* 2-bit RGB/CMY/CMYK or 8-bit W/K image */
	      for (srcptr += srcbytes - 1;
	           count > 0;
		   count --, srcptr --, dstptr ++)
	        *dstptr = *srcptr;
	      break;

          case 16 : /* 4-bit RGB/CMY/CMYK or 16-bit W/K image */
	      for (srcptr += srcbytes - 2;
	           count > 0;
		   count -= 2, srcptr -= 2, dstptr += 2)
	      {
	        dstptr[0] = srcptr[0];
	        dstptr[1] = srcptr[1];
              }
	      break;

          case 24 : /* 8-bit RGB or CMY image */
	      for (srcptr += srcbytes - 3;
	           count > 0;
		   count -= 3, srcptr -= 3, dstptr += 3)
	      {
	        dstptr[0] = srcptr[0];
	        dstptr[1] = srcptr[1];
	        dstptr[2] = srcptr[2];
              }
	      break;

          case 32 : /* 8-bit CMYK image */
	      for (srcptr += srcbytes - 4;
	           count > 0;
		   count -= 4, srcptr -= 4, dstptr += 4)
	      {
	        dstptr[0] = srcptr[0];
	        dstptr[1] = srcptr[1];
	        dstptr[2] = srcptr[2];
	        dstptr[3] = srcptr[3];
              }
	      break;

          case 48 : /* 16-bit RGB or CMY image */
	      for (srcptr += srcbytes - 6;
	           count > 0;
		   count -= 6, srcptr -= 6, dstptr += 6)
	      {
	        dstptr[0] = srcptr[0];
	        dstptr[1] = srcptr[1];
	        dstptr[2] = srcptr[2];
	        dstptr[3] = srcptr[3];
	        dstptr[4] = srcptr[4];
	        dstptr[5] = srcptr[5];
              }
	      break;

          case 64 : /* 16-bit CMYK image */
	      for (srcptr += srcbytes - 8;
	           count > 0;
		   count -= 8, srcptr -= 8, dstptr += 8)
	      {
	        dstptr[0] = srcptr[0];
	        dstptr[1] = srcptr[1];
	        dstptr[2] = srcptr[2];
	        dstptr[3] = srcptr[3];
	        dstptr[4] = srcptr[4];
	        dstptr[5] = srcptr[5];
	        dstptr[6] = srcptr[6];
	        dstptr[7] = srcptr[7];
              }
	      break;
        }
      }

     /*
      * Write the bitmap data to the raster stream...
      */

      cupsRasterWritePixels(cups->stream, dst, cups->header.cupsBytesPerLine);
    }
    else
    {
     /*
      * Write the scanline data to the raster stream...
      */

      cupsRasterWritePixels(cups->stream, srcptr, cups->header.cupsBytesPerLine);
    }
  }
  return (0);
}


/*
 * 'cups_print_banded()' - Print a page of banded pixels.
 */

static int
cups_print_banded(gx_device_printer *pdev,
					/* I - Printer device */
                  unsigned char     *src,
					/* I - Scanline buffer */
		  unsigned char     *dst,
					/* I - Bitmap buffer */
		  int               srcbytes)
					/* I - Number of bytes in src */
{
  int		x;			/* Looping var */
  int		y;			/* Looping var */
  int		bandbytes;		/* Bytes per band */
  unsigned char	bit;			/* Current bit */
  unsigned char	temp;			/* Temporary variable */
  unsigned char	*srcptr;		/* Pointer to data */
  unsigned char	*cptr, *mptr, *yptr,	/* Pointer to components */
		*kptr, *lcptr, *lmptr;	/* ... */
  int		xflip,			/* Flip scanline? */
#ifdef CUPS_DEBUG
                yflip,			/* Reverse scanline order? */
#endif
                ystart, yend, ystep;    /* Loop control for scanline order */
  ppd_attr_t    *backside = NULL;

#ifdef CUPS_DEBUG
  dmprintf1(pdev->memory, "DEBUG2: cups->header.Duplex = %d\n", cups->header.Duplex);
  dmprintf1(pdev->memory, "DEBUG2: cups->header.Tumble = %d\n", cups->header.Tumble);
  dmprintf1(pdev->memory, "DEBUG2: cups->page = %d\n", cups->page);
  dmprintf1(pdev->memory, "DEBUG2: cups->PPD = %p\n", cups->PPD);
#endif /* CUPS_DEBUG */

  if (cups->PPD) {
    backside = ppdFindAttr(cups->PPD, "cupsBackSide", NULL);
    if (backside) {
#ifdef CUPS_DEBUG
      dmprintf1(pdev->memory, "DEBUG2: cupsBackSide = %s\n", backside->value);
#endif /* CUPS_DEBUG */
      cups->PPD->flip_duplex = 0;
    }
  }
  if (cups->header.Duplex && cups->PPD &&
      ((!cups->header.Tumble &&
	(cups->PPD->flip_duplex ||
	 (backside && !strcasecmp(backside->value, "Rotated")))) ||
       (cups->header.Tumble &&
	(backside && (!strcasecmp(backside->value, "Flipped") ||
		      !strcasecmp(backside->value, "ManualTumble"))))) &&
      !(cups->page & 1))
    xflip = 1;
  else
    xflip = 0;
  if (cups->header.Duplex && cups->PPD &&
      ((!cups->header.Tumble &&
	(cups->PPD->flip_duplex ||
	 (backside && (!strcasecmp(backside->value, "Flipped") ||
		       !strcasecmp(backside->value, "Rotated"))))) ||
       (cups->header.Tumble &&
	(backside && !strcasecmp(backside->value, "ManualTumble")))) &&
      !(cups->page & 1)) {
#ifdef CUPS_DEBUG
    yflip = 1;
#endif
    ystart = cups->height - 1;
    yend = -1;
    ystep = -1;
  } else {
#ifdef CUPS_DEBUG
    yflip = 0;
#endif
    ystart = 0;
    yend = cups->height;
    ystep = 1;
  }

#ifdef CUPS_DEBUG
  dmprintf3(pdev->memory, "DEBUG: cups_print_chunked: xflip = %d, yflip = %d, height = %d\n",
            xflip, yflip, cups->height);
#endif /* CUPS_DEBUG */

 /*
  * Loop through the page bitmap and write banded pixels...  We have
  * to separate each chunked color as needed...
  */

#ifdef CUPS_RASTER_SYNCv1
  bandbytes = cups->header.cupsBytesPerLine / cups->header.cupsNumColors;
#else
  if (cups->header.cupsColorSpace == CUPS_CSPACE_KCMYcm &&
      cups->header.cupsBitsPerColor == 1)
    bandbytes = cups->header.cupsBytesPerLine / 6;
  else
    bandbytes = cups->header.cupsBytesPerLine / cups->color_info.num_components;
#endif /* CUPS_RASTER_SYNCv1 */

  for (y = ystart; y != yend; y += ystep)
  {
   /*
    * Grab the scanline data...
    */

    if (gdev_prn_get_bits((gx_device_printer *)pdev, y, src, &srcptr) < 0)
    {
      dmprintf1(pdev->memory, "ERROR: Unable to get scanline %d!\n", y);
      return_error(gs_error_unknownerror);
    }

   /*
    * Separate the chunked colors into their components...
    */

    if (srcptr[0] == 0 && memcmp(srcptr, srcptr + 1, srcbytes - 1) == 0)
      memset(dst, 0, cups->header.cupsBytesPerLine);
    else
    {
      if (xflip)
        cptr = dst + bandbytes - 1;
      else
        cptr = dst;

      mptr  = cptr + bandbytes;
      yptr  = mptr + bandbytes;
      kptr  = yptr + bandbytes;
      lcptr = kptr + bandbytes;
      lmptr = lcptr + bandbytes;

      switch (cups->header.cupsBitsPerColor)
      {
	default :
            memset(dst, 0, cups->header.cupsBytesPerLine);

            switch (cups->header.cupsColorSpace)
	    {
	      default :
	          for (x = cups->width, bit = xflip ? 1 << (x & 7) : 128;
		       x > 0;
		       x --, srcptr ++)
		  {
		    if (*srcptr & 0x40)
		      *cptr |= bit;
		    if (*srcptr & 0x20)
		      *mptr |= bit;
		    if (*srcptr & 0x10)
		      *yptr |= bit;

                    if (xflip)
		    {
		      if (bit < 128)
			bit <<= 1;
		      else
		      {
			cptr --;
			mptr --;
			yptr --;
			bit = 1;
		      }
		    }
		    else
		      bit >>= 1;

		    x --;
		    if (x == 0)
		      break;

		    if (*srcptr & 0x4)
		      *cptr |= bit;
		    if (*srcptr & 0x2)
		      *mptr |= bit;
		    if (*srcptr & 0x1)
		      *yptr |= bit;

                    if (xflip)
		    {
		      if (bit < 128)
			bit <<= 1;
		      else
		      {
			cptr --;
			mptr --;
			yptr --;
			bit = 1;
		      }
		    }
		    else if (bit > 1)
		      bit >>= 1;
		    else
		    {
		      cptr ++;
		      mptr ++;
		      yptr ++;
		      bit = 128;
		    }
		  }
	          break;
	      case CUPS_CSPACE_GMCK :
	      case CUPS_CSPACE_GMCS :
	      case CUPS_CSPACE_RGBA :
	      case CUPS_CSPACE_RGBW :
	      case CUPS_CSPACE_CMYK :
	      case CUPS_CSPACE_YMCK :
	      case CUPS_CSPACE_KCMY :
	          for (x = cups->width, bit = xflip ? 1 << (x & 7) : 128;
		       x > 0;
		       x --, srcptr ++)
		  {
		    if (*srcptr & 0x80)
		      *cptr |= bit;
		    if (*srcptr & 0x40)
		      *mptr |= bit;
		    if (*srcptr & 0x20)
		      *yptr |= bit;
		    if (*srcptr & 0x10)
		      *kptr |= bit;

                    if (xflip)
		    {
		      if (bit < 128)
			bit <<= 1;
		      else
		      {
			cptr --;
			mptr --;
			yptr --;
			kptr --;
			bit = 1;
		      }
		    }
		    else
		      bit >>= 1;

		    x --;
		    if (x == 0)
		      break;

		    if (*srcptr & 0x8)
		      *cptr |= bit;
		    if (*srcptr & 0x4)
		      *mptr |= bit;
		    if (*srcptr & 0x2)
		      *yptr |= bit;
		    if (*srcptr & 0x1)
		      *kptr |= bit;

                    if (xflip)
		    {
		      if (bit < 128)
			bit <<= 1;
		      else
		      {
			cptr --;
			mptr --;
			yptr --;
			kptr --;
			bit = 1;
		      }
		    }
		    else if (bit > 1)
		      bit >>= 1;
		    else
		    {
		      cptr ++;
		      mptr ++;
		      yptr ++;
		      kptr ++;
		      bit = 128;
		    }
		  }
	          break;
	      case CUPS_CSPACE_KCMYcm :
	          for (x = cups->width, bit = xflip ? 1 << (x & 7) : 128;
		       x > 0;
		       x --, srcptr ++)
		  {
                   /*
                    * Note: Because of the way the pointers are setup,
                    *       the following code is correct even though
                    *       the names don't match...
                    */

		    if (*srcptr & 0x20)
		      *cptr |= bit;
		    if (*srcptr & 0x10)
		      *mptr |= bit;
		    if (*srcptr & 0x08)
		      *yptr |= bit;
		    if (*srcptr & 0x04)
		      *kptr |= bit;
		    if (*srcptr & 0x02)
		      *lcptr |= bit;
		    if (*srcptr & 0x01)
		      *lmptr |= bit;

                    if (xflip)
		    {
		      if (bit < 128)
			bit <<= 1;
		      else
		      {
			cptr --;
			mptr --;
			yptr --;
			kptr --;
			lcptr --;
			lmptr --;
			bit = 1;
		      }
		    }
		    else if (bit > 1)
		      bit >>= 1;
		    else
		    {
		      cptr ++;
		      mptr ++;
		      yptr ++;
		      kptr ++;
		      lcptr ++;
		      lmptr ++;
		      bit = 128;
		    }
		  }
	          break;
	    }
            break;

	case 2 :
            memset(dst, 0, cups->header.cupsBytesPerLine);

            switch (cups->header.cupsColorSpace)
	    {
	      default :
	          for (x = cups->width, bit = xflip ? 3 << (2 * (x & 3)) : 0xc0;
		       x > 0;
		       x --, srcptr ++)
		    switch (bit)
		    {
		      case 0xc0 :
			  if ((temp = *srcptr & 0x30) != 0)
			    *cptr |= temp << 2;
			  if ((temp = *srcptr & 0x0c) != 0)
			    *mptr |= temp << 4;
			  if ((temp = *srcptr & 0x03) != 0)
			    *yptr |= temp << 6;

                          if (xflip)
			  {
			    bit = 0x03;
			    cptr --;
			    mptr --;
			    yptr --;
			  }
			  else
			    bit = 0x30;
			  break;
		      case 0x30 :
			  if ((temp = *srcptr & 0x30) != 0)
			    *cptr |= temp;
			  if ((temp = *srcptr & 0x0c) != 0)
			    *mptr |= temp << 2;
			  if ((temp = *srcptr & 0x03) != 0)
			    *yptr |= temp << 4;

			  if (xflip)
			    bit = 0xc0;
			  else
			    bit = 0x0c;
			  break;
		      case 0x0c :
			  if ((temp = *srcptr & 0x30) != 0)
			    *cptr |= temp >> 2;
			  if ((temp = *srcptr & 0x0c) != 0)
			    *mptr |= temp;
			  if ((temp = *srcptr & 0x03) != 0)
			    *yptr |= temp << 2;

			  if (xflip)
			    bit = 0x30;
			  else
			    bit = 0x03;
			  break;
		      case 0x03 :
			  if ((temp = *srcptr & 0x30) != 0)
			    *cptr |= temp >> 4;
			  if ((temp = *srcptr & 0x0c) != 0)
			    *mptr |= temp >> 2;
			  if ((temp = *srcptr & 0x03) != 0)
			    *yptr |= temp;

			  if (xflip)
			    bit = 0x0c;
			  else
			  {
			    bit = 0xc0;
			    cptr ++;
			    mptr ++;
			    yptr ++;
			  }
			  break;
                    }
	          break;
	      case CUPS_CSPACE_GMCK :
	      case CUPS_CSPACE_GMCS :
	      case CUPS_CSPACE_RGBA :
	      case CUPS_CSPACE_RGBW :
	      case CUPS_CSPACE_CMYK :
	      case CUPS_CSPACE_YMCK :
	      case CUPS_CSPACE_KCMY :
	      case CUPS_CSPACE_KCMYcm :
	          for (x = cups->width, bit = xflip ? 3 << (2 * (x & 3)) : 0xc0;
		       x > 0;
		       x --, srcptr ++)
		    switch (bit)
		    {
		      case 0xc0 :
		          if ((temp = *srcptr & 0xc0) != 0)
			    *cptr |= temp;
			  if ((temp = *srcptr & 0x30) != 0)
			    *mptr |= temp << 2;
			  if ((temp = *srcptr & 0x0c) != 0)
			    *yptr |= temp << 4;
			  if ((temp = *srcptr & 0x03) != 0)
			    *kptr |= temp << 6;

                          if (xflip)
			  {
			    bit = 0x03;
			    cptr --;
			    mptr --;
			    yptr --;
			    kptr --;
			  }
			  else
			    bit = 0x30;
			  break;
		      case 0x30 :
		          if ((temp = *srcptr & 0xc0) != 0)
			    *cptr |= temp >> 2;
			  if ((temp = *srcptr & 0x30) != 0)
			    *mptr |= temp;
			  if ((temp = *srcptr & 0x0c) != 0)
			    *yptr |= temp << 2;
			  if ((temp = *srcptr & 0x03) != 0)
			    *kptr |= temp << 4;

			  if (xflip)
			    bit = 0xc0;
			  else
			    bit = 0x0c;
			  break;
		      case 0x0c :
		          if ((temp = *srcptr & 0xc0) != 0)
			    *cptr |= temp >> 4;
			  if ((temp = *srcptr & 0x30) != 0)
			    *mptr |= temp >> 2;
			  if ((temp = *srcptr & 0x0c) != 0)
			    *yptr |= temp;
			  if ((temp = *srcptr & 0x03) != 0)
			    *kptr |= temp << 2;

			  if (xflip)
			    bit = 0x30;
			  else
			    bit = 0x03;
			  break;
		      case 0x03 :
		          if ((temp = *srcptr & 0xc0) != 0)
			    *cptr |= temp >> 6;
			  if ((temp = *srcptr & 0x30) != 0)
			    *mptr |= temp >> 4;
			  if ((temp = *srcptr & 0x0c) != 0)
			    *yptr |= temp >> 2;
			  if ((temp = *srcptr & 0x03) != 0)
			    *kptr |= temp;

			  if (xflip)
			    bit = 0x0c;
			  else
			  {
			    bit = 0xc0;
			    cptr ++;
			    mptr ++;
			    yptr ++;
			    kptr ++;
			  }
			  break;
                    }
	          break;
	    }
            break;

	case 4 :
            memset(dst, 0, cups->header.cupsBytesPerLine);

            switch (cups->header.cupsColorSpace)
	    {
	      default :
	          for (x = cups->width, bit = xflip && (x & 1) ? 0xf0 : 0x0f;
		       x > 0;
		       x --, srcptr += 2)
		    switch (bit)
		    {
		      case 0xf0 :
			  if ((temp = srcptr[0] & 0x0f) != 0)
			    *cptr |= temp << 4;
			  if ((temp = srcptr[1] & 0xf0) != 0)
			    *mptr |= temp;
			  if ((temp = srcptr[1] & 0x0f) != 0)
			    *yptr |= temp << 4;

			  bit = 0x0f;

                          if (xflip)
			  {
			    cptr --;
			    mptr --;
			    yptr --;
			  }
			  break;
		      case 0x0f :
			  if ((temp = srcptr[0] & 0x0f) != 0)
			    *cptr |= temp;
			  if ((temp = srcptr[1] & 0xf0) != 0)
			    *mptr |= temp >> 4;
			  if ((temp = srcptr[1] & 0x0f) != 0)
			    *yptr |= temp;

			  bit = 0xf0;

                          if (!xflip)
			  {
			    cptr ++;
			    mptr ++;
			    yptr ++;
			  }
			  break;
                    }
	          break;
	      case CUPS_CSPACE_GMCK :
	      case CUPS_CSPACE_GMCS :
	      case CUPS_CSPACE_RGBA :
	      case CUPS_CSPACE_RGBW :
	      case CUPS_CSPACE_CMYK :
	      case CUPS_CSPACE_YMCK :
	      case CUPS_CSPACE_KCMY :
	      case CUPS_CSPACE_KCMYcm :
	          for (x = cups->width, bit = xflip && (x & 1) ? 0xf0 : 0x0f;
		       x > 0;
		       x --, srcptr += 2)
		    switch (bit)
		    {
		      case 0xf0 :
		          if ((temp = srcptr[0] & 0xf0) != 0)
			    *cptr |= temp;
			  if ((temp = srcptr[0] & 0x0f) != 0)
			    *mptr |= temp << 4;
			  if ((temp = srcptr[1] & 0xf0) != 0)
			    *yptr |= temp;
			  if ((temp = srcptr[1] & 0x0f) != 0)
			    *kptr |= temp << 4;

			  bit = 0x0f;

                          if (xflip)
			  {
			    cptr --;
			    mptr --;
			    yptr --;
			    kptr --;
			  }
			  break;
		      case 0x0f :
		          if ((temp = srcptr[0] & 0xf0) != 0)
			    *cptr |= temp >> 4;
			  if ((temp = srcptr[0] & 0x0f) != 0)
			    *mptr |= temp;
			  if ((temp = srcptr[1] & 0xf0) != 0)
			    *yptr |= temp >> 4;
			  if ((temp = srcptr[1] & 0x0f) != 0)
			    *kptr |= temp;

			  bit = 0xf0;

                          if (!xflip)
			  {
			    cptr ++;
			    mptr ++;
			    yptr ++;
			    kptr ++;
			  }
			  break;
                    }
	          break;
	    }
            break;

	case 8 :
            switch (cups->header.cupsColorSpace)
	    {
	      default :
	          if (xflip)
	            for (x = cups->width; x > 0; x --)
		    {
		      *cptr-- = *srcptr++;
		      *mptr-- = *srcptr++;
		      *yptr-- = *srcptr++;
		    }
		  else
	            for (x = cups->width; x > 0; x --)
		    {
		      *cptr++ = *srcptr++;
		      *mptr++ = *srcptr++;
		      *yptr++ = *srcptr++;
		    }
	          break;
	      case CUPS_CSPACE_GMCK :
	      case CUPS_CSPACE_GMCS :
	      case CUPS_CSPACE_RGBA :
	      case CUPS_CSPACE_RGBW :
	      case CUPS_CSPACE_CMYK :
	      case CUPS_CSPACE_YMCK :
	      case CUPS_CSPACE_KCMY :
	      case CUPS_CSPACE_KCMYcm :
	          if (xflip)
	            for (x = cups->width; x > 0; x --)
		    {
		      *cptr-- = *srcptr++;
		      *mptr-- = *srcptr++;
		      *yptr-- = *srcptr++;
		      *kptr-- = *srcptr++;
		    }
		  else
	            for (x = cups->width; x > 0; x --)
		    {
		      *cptr++ = *srcptr++;
		      *mptr++ = *srcptr++;
		      *yptr++ = *srcptr++;
		      *kptr++ = *srcptr++;
		    }
	          break;
	    }
            break;

	case 16 :
            switch (cups->header.cupsColorSpace)
	    {
	      default :
	          if (xflip)
	            for (x = cups->width; x > 0; x --, srcptr += 6)
		    {
		      *cptr-- = srcptr[1];
		      *cptr-- = srcptr[0];
		      *mptr-- = srcptr[3];
		      *mptr-- = srcptr[2];
		      *yptr-- = srcptr[5];
		      *yptr-- = srcptr[4];
		    }
		  else
	            for (x = cups->width; x > 0; x --)
		    {
		      *cptr++ = *srcptr++;
		      *cptr++ = *srcptr++;
		      *mptr++ = *srcptr++;
		      *mptr++ = *srcptr++;
		      *yptr++ = *srcptr++;
		      *yptr++ = *srcptr++;
		    }
	          break;
	      case CUPS_CSPACE_GMCK :
	      case CUPS_CSPACE_GMCS :
	      case CUPS_CSPACE_RGBA :
	      case CUPS_CSPACE_RGBW :
	      case CUPS_CSPACE_CMYK :
	      case CUPS_CSPACE_YMCK :
	      case CUPS_CSPACE_KCMY :
	      case CUPS_CSPACE_KCMYcm :
	          if (xflip)
	            for (x = cups->width; x > 0; x --, srcptr += 8)
		    {
		      *cptr-- = srcptr[1];
		      *cptr-- = srcptr[0];
		      *mptr-- = srcptr[3];
		      *mptr-- = srcptr[2];
		      *yptr-- = srcptr[5];
		      *yptr-- = srcptr[4];
		      *kptr-- = srcptr[7];
		      *kptr-- = srcptr[6];
		    }
		  else
	            for (x = cups->width; x > 0; x --)
		    {
		      *cptr++ = *srcptr++;
		      *cptr++ = *srcptr++;
		      *mptr++ = *srcptr++;
		      *mptr++ = *srcptr++;
		      *yptr++ = *srcptr++;
		      *yptr++ = *srcptr++;
		      *kptr++ = *srcptr++;
		      *kptr++ = *srcptr++;
		    }
	          break;
	    }
            break;
      }
    }

   /*
    * Write the bitmap data to the raster stream...
    */

    cupsRasterWritePixels(cups->stream, dst, cups->header.cupsBytesPerLine);
  }
  return (0);
}


/*
 * 'cups_print_planar()' - Print a page of planar pixels.
 */

static int
cups_print_planar(gx_device_printer *pdev,
					/* I - Printer device */
                  unsigned char     *src,
					/* I - Scanline buffer */
		  unsigned char     *dst,
					/* I - Bitmap buffer */
		  int               srcbytes)
					/* I - Number of bytes in src */
{
  int		x;			/* Looping var */
  int		y;			/* Looping var */
  unsigned char z;			/* Looping var */
  unsigned char	srcbit;			/* Current source bit */
  unsigned char	dstbit;			/* Current destination bit */
  unsigned char	temp;			/* Temporary variable */
  unsigned char	*srcptr;		/* Pointer to data */
  unsigned char	*dstptr;		/* Pointer to bitmap */


 /**** NOTE: Currently planar output doesn't support flipped duplex!!! ****/

 /*
  * Loop through the page bitmap and write planar pixels...  We have
  * to separate each chunked color as needed...
  */

  for (z = 0; z < pdev->color_info.num_components; z ++)
    for (y = 0; y < cups->height; y ++)
    {
     /*
      * Grab the scanline data...
      */

      if (gdev_prn_get_bits((gx_device_printer *)pdev, y, src, &srcptr) < 0)
      {
        dmprintf1(pdev->memory, "ERROR: Unable to get scanline %d!\n", y);
	return_error(gs_error_unknownerror);
      }

     /*
      * Pull the individual color planes out of the pixels...
      */

      if (srcptr[0] == 0 && memcmp(srcptr, srcptr + 1, srcbytes - 1) == 0)
	memset(dst, 0, cups->header.cupsBytesPerLine);
      else
	switch (cups->header.cupsBitsPerColor)
	{
          default :
	      memset(dst, 0, cups->header.cupsBytesPerLine);

	      switch (cups->header.cupsColorSpace)
	      {
		default :
	            for (dstptr = dst, x = cups->width, srcbit = 64 >> z,
		             dstbit = 128;
			 x > 0;
			 x --)
		    {
		      if (*srcptr & srcbit)
			*dstptr |= dstbit;

                      if (srcbit >= 16)
			srcbit >>= 4;
		      else
		      {
			srcbit = 64 >> z;
			srcptr ++;
		      }

                      if (dstbit > 1)
			dstbit >>= 1;
		      else
		      {
			dstbit = 128;
			dstptr ++;
		      }
		    }
	            break;
		case CUPS_CSPACE_GMCK :
		case CUPS_CSPACE_GMCS :
		case CUPS_CSPACE_RGBA :
		case CUPS_CSPACE_RGBW :
		case CUPS_CSPACE_CMYK :
		case CUPS_CSPACE_YMCK :
		case CUPS_CSPACE_KCMY :
	            for (dstptr = dst, x = cups->width, srcbit = 128 >> z,
		             dstbit = 128;
			 x > 0;
			 x --)
		    {
		      if (*srcptr & srcbit)
			*dstptr |= dstbit;

                      if (srcbit >= 16)
			srcbit >>= 4;
		      else
		      {
			srcbit = 128 >> z;
			srcptr ++;
		      }

                      if (dstbit > 1)
			dstbit >>= 1;
		      else
		      {
			dstbit = 128;
			dstptr ++;
		      }
		    }
	            break;
		case CUPS_CSPACE_KCMYcm :
	            for (dstptr = dst, x = cups->width, srcbit = 32 >> z,
		             dstbit = 128;
			 x > 0;
			 x --, srcptr ++)
		    {
		      if (*srcptr & srcbit)
			*dstptr |= dstbit;

                      if (dstbit > 1)
			dstbit >>= 1;
		      else
		      {
			dstbit = 128;
			dstptr ++;
		      }
		    }
	            break;
              }
	      break;

	  case 2 :
	      memset(dst, 0, cups->header.cupsBytesPerLine);

	      switch (cups->header.cupsColorSpace)
	      {
		default :
	            for (dstptr = dst, x = cups->width, srcbit = 48 >> (z * 2),
		             dstbit = 0xc0;
			 x > 0;
			 x --, srcptr ++)
		    {
		      if ((temp = *srcptr & srcbit) != 0)
		      {
			if (srcbit == dstbit)
		          *dstptr |= temp;
	        	else
			{
		          switch (srcbit)
			  {
			    case 0x30 :
				temp >>= 4;
				break;
			    case 0x0c :
				temp >>= 2;
				break;
                          }

		          switch (dstbit)
			  {
			    case 0xc0 :
				*dstptr |= temp << 6;
				break;
			    case 0x30 :
				*dstptr |= temp << 4;
				break;
			    case 0x0c :
				*dstptr |= temp << 2;
				break;
			    case 0x03 :
				*dstptr |= temp;
				break;
                          }
			}
		      }

		      if (dstbit > 0x03)
			dstbit >>= 2;
		      else
		      {
			dstbit = 0xc0;
			dstptr ++;
		      }
		    }
	            break;
		case CUPS_CSPACE_GMCK :
		case CUPS_CSPACE_GMCS :
		case CUPS_CSPACE_RGBA :
		case CUPS_CSPACE_RGBW :
		case CUPS_CSPACE_CMYK :
		case CUPS_CSPACE_YMCK :
		case CUPS_CSPACE_KCMY :
		case CUPS_CSPACE_KCMYcm :
	            for (dstptr = dst, x = cups->width, srcbit = 192 >> (z * 2),
		             dstbit = 0xc0;
			 x > 0;
			 x --, srcptr ++)
		    {
		      if ((temp = *srcptr & srcbit) != 0)
		      {
			if (srcbit == dstbit)
		          *dstptr |= temp;
	        	else
			{
		          switch (srcbit)
			  {
			    case 0xc0 :
				temp >>= 6;
				break;
			    case 0x30 :
				temp >>= 4;
				break;
			    case 0x0c :
				temp >>= 2;
				break;
                          }

		          switch (dstbit)
			  {
			    case 0xc0 :
				*dstptr |= temp << 6;
				break;
			    case 0x30 :
				*dstptr |= temp << 4;
				break;
			    case 0x0c :
				*dstptr |= temp << 2;
				break;
			    case 0x03 :
				*dstptr |= temp;
				break;
                          }
			}
		      }

		      if (dstbit > 0x03)
			dstbit >>= 2;
		      else
		      {
			dstbit = 0xc0;
			dstptr ++;
		      }
		    }
	            break;
              }
	      break;

	  case 4 :
	      memset(dst, 0, cups->header.cupsBytesPerLine);

	      switch (cups->header.cupsColorSpace)
	      {
		default :
	            if (z > 0)
		      srcptr ++;

		    if (z == 1)
		      srcbit = 0xf0;
		    else
		      srcbit = 0x0f;

	            for (dstptr = dst, x = cups->width, dstbit = 0xf0;
			 x > 0;
			 x --, srcptr += 2)
		    {
		      if ((temp = *srcptr & srcbit) != 0)
		      {
			if (srcbit == dstbit)
		          *dstptr |= temp;
	        	else
			{
		          if (srcbit == 0xf0)
	                    temp >>= 4;

		          if (dstbit == 0xf0)
   			    *dstptr |= temp << 4;
			  else
			    *dstptr |= temp;
			}
		      }

		      if (dstbit == 0xf0)
			dstbit = 0x0f;
		      else
		      {
			dstbit = 0xf0;
			dstptr ++;
		      }
		    }
	            break;
		case CUPS_CSPACE_GMCK :
		case CUPS_CSPACE_GMCS :
		case CUPS_CSPACE_RGBA :
		case CUPS_CSPACE_RGBW :
		case CUPS_CSPACE_CMYK :
		case CUPS_CSPACE_YMCK :
		case CUPS_CSPACE_KCMY :
		case CUPS_CSPACE_KCMYcm :
	            if (z > 1)
		      srcptr ++;

		    if (z & 1)
		      srcbit = 0x0f;
		    else
		      srcbit = 0xf0;

	            for (dstptr = dst, x = cups->width, dstbit = 0xf0;
			 x > 0;
			 x --, srcptr += 2)
		    {
		      if ((temp = *srcptr & srcbit) != 0)
		      {
			if (srcbit == dstbit)
		          *dstptr |= temp;
	        	else
			{
		          if (srcbit == 0xf0)
	                    temp >>= 4;

		          if (dstbit == 0xf0)
   			    *dstptr |= temp << 4;
			  else
			    *dstptr |= temp;
			}
		      }

		      if (dstbit == 0xf0)
			dstbit = 0x0f;
		      else
		      {
			dstbit = 0xf0;
			dstptr ++;
		      }
		    }
	            break;
              }
	      break;

	  case 8 :
	      for (srcptr += z, dstptr = dst, x = cups->header.cupsBytesPerLine;
	           x > 0;
		   srcptr += pdev->color_info.num_components, x --)
		*dstptr++ = *srcptr;
	      break;

	  case 16 :
	      for (srcptr += 2 * z, dstptr = dst, x = cups->header.cupsBytesPerLine;
	           x > 0;
		   srcptr += 2 * pdev->color_info.num_components, x --)
              {
		*dstptr++ = srcptr[0];
		*dstptr++ = srcptr[1];
	      }
	      break;
	}

     /*
      * Write the bitmap data to the raster stream...
      */

      cupsRasterWritePixels(cups->stream, dst, cups->header.cupsBytesPerLine);
    }
  return (0);
}

private int
cups_spec_op(gx_device *dev_, int op, void *data, int datasize)
{
    return gx_default_dev_spec_op(dev_, op, data, datasize);
}

/*
 */
