[bug-gswin] GS and Epson

Dan Coby dan.coby@artifex.com
Wed, 22 Jan 2003 21:39:44 -0800


This is a multi-part message in MIME format.

------=_NextPart_000_0172_01C2C25E.C73DB6A0
Content-Type: multipart/alternative;
	boundary="----=_NextPart_001_0173_01C2C25E.C74298A0"


------=_NextPart_001_0173_01C2C25E.C74298A0
Content-Type: text/plain;
	charset="iso-8859-1"
Content-Transfer-Encoding: 8bit

GS and Epson
Stefan,

Try the following version of gdevupd.c and let us know if it solves your
problem.

Dan
  -----Original Message-----
  From: bug-gswin-admin@ghostscript.com
[mailto:bug-gswin-admin@ghostscript.com]On Behalf Of Radke, Stefan
  Sent: Monday, January 20, 2003 10:26 PM
  To: 'bug-gswin@ghostscript.com'
  Subject: [bug-gswin] GS and Epson


  Symptoms:
  After installing GV 4.3 and GS 8.0 I can't print correctly. I get yellow
pages with white letters instead of white/black.GV4.3 and GS7.04 work fine.

  Ghostscript version
  GS 8.0 for OS2

  Hardware
  Epson Stylus Color 880 (USB)
  .upp Files from http://murakami.best.vwh.net/uppfiles

  Operating system I'm using:
  OS2 eCS 1.02

  Do you have any ideas how to solve it?
  Will there be any OS2 solutions in the future?

    Mit freundlichen Grüssen/Kind regards

    Stefan Radke

    Kraft Foods Germany SCM-SN
    Tel  ++49 421 599 3995

          Fax ++49 421 599 8 3995
          Email: sradke@krafteurope.com





------=_NextPart_001_0173_01C2C25E.C74298A0
Content-Type: text/html;
	charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD><TITLE>GS and Epson</TITLE>
<META http-equiv=3DContent-Type content=3D"text/html; =
charset=3Diso-8859-1">
<META content=3D"MSHTML 6.00.2722.900" name=3DGENERATOR></HEAD>
<BODY>
<DIV><FONT face=3DArial color=3D#0000ff size=3D2></FONT>&nbsp;</DIV>
<DIV><SPAN class=3D147013705-23012003><FONT face=3DArial color=3D#0000ff =

size=3D2>Stefan,</FONT></SPAN></DIV>
<DIV><SPAN class=3D147013705-23012003><FONT face=3DArial color=3D#0000ff =

size=3D2></FONT></SPAN>&nbsp;</DIV>
<DIV><SPAN class=3D147013705-23012003><FONT face=3DArial color=3D#0000ff =
size=3D2>Try=20
the following version of gdevupd.c and let us know if it solves your=20
problem.</FONT></SPAN></DIV>
<DIV><SPAN class=3D147013705-23012003><FONT face=3DArial color=3D#0000ff =

size=3D2></FONT></SPAN>&nbsp;</DIV>
<DIV><SPAN class=3D147013705-23012003><FONT face=3DArial color=3D#0000ff =

size=3D2>Dan</FONT></SPAN></DIV>
<BLOCKQUOTE>
  <DIV class=3DOutlookMessageHeader dir=3Dltr align=3Dleft><FONT =
face=3DTahoma=20
  size=3D2>-----Original Message-----<BR><B>From:</B>=20
  bug-gswin-admin@ghostscript.com =
[mailto:bug-gswin-admin@ghostscript.com]<B>On=20
  Behalf Of </B>Radke, Stefan<BR><B>Sent:</B> Monday, January 20, 2003 =
10:26=20
  PM<BR><B>To:</B> 'bug-gswin@ghostscript.com'<BR><B>Subject:</B> =
[bug-gswin] GS=20
  and Epson<BR><BR></FONT></DIV>
  <P><FONT face=3D"Courier New" size=3D2>Symptoms:</FONT> <BR><FONT=20
  face=3D"Courier New" size=3D2>After installing GV 4.3 and GS 8.0 I =
can't print=20
  correctly. I get yellow pages with white letters instead of =
white/black.GV4.3=20
  and GS7.04 work fine. </FONT></P>
  <P><FONT face=3D"Courier New" size=3D2>Ghostscript version =
</FONT><BR><FONT=20
  face=3D"Courier New" size=3D2>GS 8.0 for OS2</FONT> </P>
  <P><FONT face=3D"Courier New" size=3D2>Hardware </FONT><BR><FONT=20
  face=3D"Courier New" size=3D2>Epson Stylus Color 880 (USB)</FONT> =
<BR><FONT=20
  face=3D"Courier New" size=3D2>.upp Files from </FONT><A=20
  href=3D"http://murakami.best.vwh.net/uppfiles"><U><FONT =
face=3D"Courier New"=20
  color=3D#0000ff =
size=3D2>http://murakami.best.vwh.net/uppfiles</FONT></U></A> </P>
  <P><FONT face=3D"Courier New" size=3D2>Operating system I'm using:=20
  </FONT><BR><FONT face=3D"Courier New" size=3D2>OS2 eCS 1.02&nbsp; =
</FONT></P>
  <P><FONT face=3D"Courier New" size=3D2>Do you have any ideas how to =
solve=20
  it?</FONT> <BR><FONT face=3D"Courier New" size=3D2>Will there be any =
OS2 solutions=20
  in the future?</FONT> </P>
  <UL>
    <P><FONT face=3DTahoma size=3D2>Mit freundlichen Gr=FCssen/Kind =
regards</FONT>=20
</P>
    <P><B><FONT face=3DTahoma size=3D2>Stefan Radke</FONT></B> </P>
    <P><FONT face=3DTahoma size=3D2>Kraft Foods Germany SCM-SN</FONT> =
<BR><FONT=20
    face=3DTahoma size=3D2>Tel&nbsp; ++49 421 599 3995</FONT> </P></UL>
  <P><FONT face=3DTahoma =
size=3D2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Fax=20
  ++49 421 599 8 3995</FONT> <BR><FONT face=3DArial=20
  size=3D1>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Email:=20
  sradke@krafteurope.com</FONT> </P><BR><BR></BLOCKQUOTE></BODY></HTML>

------=_NextPart_001_0173_01C2C25E.C74298A0--

------=_NextPart_000_0172_01C2C25E.C73DB6A0
Content-Type: application/octet-stream;
	name="gdevupd.c"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="gdevupd.c"

/* Copyright (C) 1997, 2000 Aladdin Enterprises.  All rights reserved.
 =20
  This software is provided AS-IS with no warranty, either express or
  implied.
 =20
  This software is distributed under license and may not be copied,
  modified or distributed except as expressly authorized under the terms
  of the license contained in the file LICENSE in this distribution.
 =20
  For more information about licensing, please refer to
  http://www.ghostscript.com/licensing/. For information on
  commercial licensing, go to http://www.artifex.com/licensing/ or
  contact Artifex Software, Inc., 101 Lucas Valley Road #110,
  San Rafael, CA  94903, U.S.A., +1(415)492-9861.
*/

/* $Id: gdevupd.c,v 1.11 2003/01/23 05:14:41 dan Exp $ */
/* gdevupd.c Revision: 1.88 */
/* "uniprint" -- Ugly Printer Driver by Gunther Hess (ghess@elmos.de) */

/* Revision-History:
   23-Mar-1997 -  1.43: First published version
   24-Mar-1997 -  1.44: gs4.03 compatible version on the web
   31-Mar-1997 -  1.53: First Version inside gs-fileset (limited)
   28-Apr-1997 -  1.54: Version intended for public gs-release
    4-May-1997 -  1.55: Deactivated an accidentially active Debug-Option
   14-Jun-1997 -  1.56: Bug-Workaround for White on White Printing =
(gs5.0)
   17-Jun-1997 -  1.57: More reasonable Fix for the above Bug
   ...
    7-Jul-1997 -  1.68: NULL-Param-BUG, HR's BJC, Pwidth/-height BUG, =
YFlip
   25-Jul-1997 -  1.69: Bug-Fix: incomplete Change of PHEIGHT-Treatment
    4-Aug-1997 -  1.70: Arrgh: still incomplete Change of =
PHEIGHT-Treatment
   17-AUG-1997 -  1.71: Fix of BSD-sprintf bug. (returns char * there)
   ...
   28-Sep-1997 -  1.77: Fixed the byte<>char and casted-lvalue Problems
   ...
   12-Mar-1998 -  1.80: Some PJL-Functions, Map-Bug-Fix (by =
Wonder-Wolfgang)
   21-Oct-1998 -  1.81: Added RGB2CMY[_]K Modi (Eric Domenjoud)
   ...
   27-Feb-2000 -  1.84: CMYKgenerate with forced K-Control [distributed]
    2-Apr-2000 -        Unofficial modifications for Epson Stylus Color =
300. GR
    5-Apr-2000 -        GR fixed last row not filled bug in wrtescnm
    7-May-2000 -  1.85: Always BOP/EOP-Massaging for RTL-Output (Dan =
Coby)
  ...
    7-May-2000 -  1.87: integrated stc300-code by Glenn Ramsey
       "       -  1.88: reduced "cast discards `const'" warnings to 1

*/

/* Canon BJC 610 additions from (hr)
      Helmut Riegler <helmut-riegler@net4you.co.at>

   The BJC-4000 can be supported very easily, only by creating the right =
.upp
   parameter file. If you have this printer and you are willing to do =
this,
   contact me, I'll give you the technical details (ESC codes).
*/

/* Epson Stylus Color 300 (FMT_ESCNMY) additions 2-Apr-2000.
   Glenn Ramsey <glennr@es.co.nz>
*/

/* ------------------------------------------------------------------- =
*/
/* Compile-Time-Options                                                =
*/
/* ------------------------------------------------------------------- =
*/

/**
There are two compile-time options for this driver:
   1. UPD_SIGNAL   enables interrupt detection, that aborts printing and
   2. UPD_MESSAGES controls the amount of messages generated by the =
driver
*/

#ifndef   UPD_SIGNAL
#ifdef      __unix__
#define       UPD_SIGNAL 1 /** Activated, if undefined, on UNIX-Systems =
*/
#else  /*  !__unix__ */
#define       UPD_SIGNAL 0 /** Inactive on others, by default */
#endif /*  ?__unix__ */
#endif /* UPD_SIGNAL */

#ifndef   UPD_MESSAGES
#define   UPD_MESSAGES UPD_M_ERROR /** Error-messages only, if not =
defined */
#endif /* UPD_MESSAGES */

/* ------------------------------------------------------------------- =
*/
/* Required Header-Files                                               =
*/
/* ------------------------------------------------------------------- =
*/

#ifndef   hess_test_INCLUDED /* A private test-Option */

#include "gdevprn.h" /** Printer-superclass header */
#include "gsparam.h" /** For the Parameter-Handling (optional) */

#include <stdlib.h>  /** for rand */
#include <limits.h>  /** for INT_MIN */
#include <ctype.h>   /** for isupper */

#endif /* hess_test_INCLUDED    A private test-Option */

#if       UPD_SIGNAL
#include <signal.h> /** Only included, if UPD_SIGNAL is active (true) */
#endif /* UPD_SIGNAL */

/* ------------------------------------------------------------------- =
*/
/* Device-Structure (including an additional Structure-Pointer-Type)   =
*/
/* ------------------------------------------------------------------- =
*/

typedef struct upd_s upd_t,*upd_p; /** Type & Pointer of =
device-specifics */
typedef const upd_t *upd_pc;       /** Pointer to constant =
device-specfics */

typedef struct upd_device_s {      /** The driver must typedef ... */
   gx_device_common;               /**    common fields for all devices =
*/
   gx_prn_device_common;           /**    common fields for =
printing-devices */
   gs_param_string upd_version;    /**    Source-Code Version */
   upd_p           upd;            /**    uniprint-specific extension */
} upd_device;                      /** some type usually  <name>_device> =
*/

/* ------------------------------------------------------------------- =
*/
/* Major Driver-Functions                                              =
*/
/* ------------------------------------------------------------------- =
*/

private dev_proc_print_page(upd_print_page); /** print a page (required) =
*/

private dev_proc_open_device(upd_open);      /** device-initialization =
(opt) */
private dev_proc_close_device(upd_close);    /** device-release (opt) */

private dev_proc_get_params(upd_get_params); /** export parameters (opt) =
*/
private dev_proc_put_params(upd_put_params); /** import parameters (opt) =
*/

/**
A `normal' Device-Driver wil only implement one of the following pairs
of functions for the colormapping. But "uniprint" is something special =
and
it really provides all four reasonable pairs and in addition to that
a fifth set of functions, that delivers better FS-Results with KCMY.

The first pair is for the mapping into a single stored component, that
usually represents a grayscale. But nevertheless GHOSTSCRIPT deals with
RGB-Values, but promises to deal with R=3D=3DG=3D=3DB-Values when asking =
to map.

The second pair deals with RGB-Values.
*/

private dev_proc_encode_color( upd_rgb_1color);  /** Gray-Gray->Index */
private dev_proc_decode_color( upd_1color_rgb);  /** Gray-Index->Gray */

private dev_proc_encode_color( upd_rgb_3color);  /** RGB->RGB-Index */
private dev_proc_decode_color( upd_3color_rgb);  /** RGB-Index->RGB */

/**
The third pair maps RGB-Values into four components, which one might
expect to be KCMY-Values, but they are not: "uniprint" considers this =
four
Values as White+RGB Values!
*/

private dev_proc_encode_color( upd_rgb_4color);  /** RGB->WRGB-Index */
private dev_proc_decode_color(upd_4color_rgb);   /** WRGB-Index->RGB */

/**
The fourth pair deals with KCMY-Values. The Mapping-Function
is of a different type, due to the additional argument, but the
inverse-Function is of the same type, and expects RGB-Values to be
deliverd into the receiving 3-Component-Array!
*/

private dev_proc_encode_color(upd_cmyk_icolor); /** KCMY->KCMY-Index */
private dev_proc_decode_color( upd_icolor_rgb);  /** KCMY->RGB-Index */

/**
The difference between the icolor-pair and the kcolor-pair is the =
enforced
black-generation in the forward-mapping. that is taken into account by =
the
reverse-mapping too.
*/

private dev_proc_encode_color(upd_cmyk_kcolor); /** adds black =
generation */
private dev_proc_decode_color( upd_kcolor_rgb);  /** watches black-gen =
*/

/**
"ovcolor" is CMYK with Black-Generation and Undercolor-Removal, which
is suitable for overprinting:
   CMY' =3D (CMY-K')/(1-K')
with
   K'   =3D min(C,M,Y)
*/

private dev_proc_encode_color(upd_rgb_ovcolor);  /** RGB->CMYK-Index */
#define upd_ovcolor_rgb upd_icolor_rgb            /** CMYK-Index->RGB */

/**
"novcolor" is CMYK with Black-Generation and Undercolor-Removal, which
is suitable for CMY / K - Printing:
   CMY' =3D CMY-K'
with
   K'   =3D min(C,M,Y)
*/

private dev_proc_encode_color(upd_rgb_novcolor); /** RGB->CMYK-Index */
#define upd_novcolor_rgb upd_icolor_rgb           /** CMYK-Index->RGB */

/**
For the sake of efficiency there is that bunch of functions and they
perform no validity checks, thus it has to be assured that they are
only active, if there is a valid device-structure for then.
upd_procs_map performs this task.
*/

private int             upd_procs_map( upd_device *udev);

/* ------------------------------------------------------------------- =
*/
/* Prototype of the Device-Structure (the only thing exported!)        =
*/
/* ------------------------------------------------------------------- =
*/

/**
"uniprint" needs a procedure-table of its own, since it provides several
optional procedures. Simpler-Drivers (e.g. non-color-drivers) may use
prn_std_procs instead of defining their own procedure-table.
*/

#define upd_set_dev_proc(dev, p, proc) \
   ((dev)->std_procs.p =3D (dev)->orig_procs.p =3D (proc))

private gx_device_procs upd_procs =3D {  /** Table of procedures */
   upd_open,                      /** open-function, upd-special */
   gx_default_get_initial_matrix, /** retrieve matrix */
   gx_default_sync_output,        /** sync display */
   gdev_prn_output_page,          /** superclass-print (calls back) */
   upd_close,                     /** close-function, upd-special */
   gx_default_map_rgb_color,      /** RGB-mapping */
   gx_default_map_color_rgb,      /** reverse mapping */
   NULL,                          /** fill_rectangle */
   NULL,                          /** tile_rectangle */
   NULL,                          /** copy_mono */
   NULL,                          /** copy_color */
   NULL,                          /** draw_line */
   gx_default_get_bits,           /** reads scanlines, e.g. for the =
driver */
   upd_get_params,                /** Export parameters, upd-special */
   upd_put_params,                /** Import parameters, upd-special */
   gx_default_map_cmyk_color      /** KCMY-mapping */
};                                     /** */

/**
The prototype-instance of the device-structure _must_ have the name
"gs_uniprint_device", where "uniprint" is the external name of the =
driver.
This notice is bluntly copied from drivers.txt, which a potential
driver-author should carefully read.

Just to mention: this prototype is quite similar to the one, that
"prn_device" produces and it identifies "uniprint" as a monochrome 1Bit
device to GHOSTSCRIPT. But during the lifetime of a driver-instance
this might change.

This is the end of the part of declarations, that are common for
color-drivers. The next sections address "uniprint"-specific data-types
and the reader might directly skip to the section titled

    upd_print_page: The main workhorse
*/

upd_device far_data gs_uniprint_device =3D { /** */
   prn_device_body(upd_device, upd_procs,  /** The Type and Procedures =
*/
      "uniprint",                          /** External name of the =
Device */
      DEFAULT_WIDTH_10THS,                 /** X-Size (1/10") */
      DEFAULT_HEIGHT_10THS,                /** Y-Size (1/10") */
      72, 72,                              /** X,Y-DpI */
      0.0, 0.0, 0.0, 0.0,                  /** L,B,R,T-Margin */
      1, /**  color_info.num_components 1/3/4 */
      1, /**  color_info.depth         1/2/4/8/16/24/32 */
      1, /**  color_info.max_gray      # of distinct gray levels -1 =
(255/1) */
      0, /**  color_info.max_color     # of distinct color levels -1 =
(255/1/0)*/
      1, /**  color_info.dither_grays  size of gray ramp for dithering =
(5/2) */
      0, /**  color_info.dither_colors size of color cube ditto (5/2/0) =
*/
      upd_print_page),                     /** Print-procedure */
      { NULL, 0, true },                   /** Driver-Version */
      NULL                                 /** upd-field: Initially none =
*/
};                                         /** */


/* ------------------------------------------------------------------- =
*/
/* UPD-Data- and Prototypes                                            =
*/
/* ------------------------------------------------------------------- =
*/

/*@ gdevupd.h < */
/* ------------------------------------------------------------------- =
*/
/* External names of the UPD-Parameters                                =
*/
/* ------------------------------------------------------------------- =
*/

/** UPD-Parameters

"uniprint" supports a hole bunch of external parameters. This Parameters
fall into the following categories:

 0. special-string the upd_version, readonly          upd_version
 1. choice         name-indices, stored in            upd->choice
 2. boolean        single bits, stored in             upd->flags
 3. integers       single numbers, stored in          upd->ints
 4. integer-Arrays arrays of numbers, stored in       upd->int_a
 5. string         device-commands, stored in         upd->strings
 6. string-Arrays  arrayed device-commands, stored in upd->string_a
 7. float-Arrays   arrays of floats, stored in        upd->float_a

Currently there is no need for single floats, but they may be introduced =
in
future versions. Since "uniprint" somtimes manipulates the contents of =
the
array-variables it dynamically allocates storage for all this =
parameters.

The following sections defines the names for this parameters in the =
order,
they are stored within the mentioned dynamic fields of the =
upd-structure.
A NULL-name means that the corresponding parameter is not externally =
visible.
Besides the name, there is always a symbolic index #defined, that MUST =
match
the Index-Number of the name.
Actually
*/

static const char *const upd_version =3D "upVersion"; /** Readonly =
Version */

/** Names for the multiple-choice-Parameters

Currently there are three Parameters, that are handled as named choices.
For each of them, there is an array of constant strings that consists of

1.       the Parameter-Name
2. - n-1 the available choices.
n.       A terminating NULL
*/

static const char *const upd_mapper[] =3D { "upColorModel",
#define MAP_GRAY        1   /** Monochrome & Grayscale Devices */
"DeviceGray",               /** Monochrome & Grayscale Devices */
#define MAP_RGBW        2   /** RGB with White-Generation */
"DeviceRGBW",               /** RGB with White-Generation */
#define MAP_RGB         3   /** RGB-Mapping */
"DeviceRGB",                /** RGB-Mapping */
#define MAP_CMYK        4   /** CMYK-Mapping */
"DeviceCMYK",               /** CMYK-Mapping */
#define MAP_CMYKGEN     5   /** CMYK-Mapping with Black-Generation */
"DeviceCMYKgenerate",       /** CMYK-Mapping with Black-Generation */
#define MAP_RGBOV       6   /** RGB->CMYK with BG and UCR for CMYK */
"DeviceRGB2CMYK",           /** RGB->CMYK with BG and UCR for CMYK */
#define MAP_RGBNOV      7   /** RGB->CMYK with BG and UCR for CMY + K */
"DeviceRGB2CMY_K",          /** RGB->CMYK with BG and UCR for CMY + K */
NULL
};

static const char *const upd_render[] =3D { "upRendering",
#define RND_FSCOMP      1   /** Componentwise Floyd-Steinberg */
"ErrorDiffusion",           /** Componentwise Floyd-Steinberg */
#define RND_FSCMYK      2   /** CMYK-specialized 32Bit Floyd-Steinberg =
*/
"FSCMYK32",                 /** CMYK-specialized 32Bit Floyd-Steinberg =
*/
#define RND_FSCMY_K     3   /** CMY_K Rendering */
"FSCMY_K",
NULL
};

static const char *const upd_format[] =3D { "upOutputFormat",
#define FMT_RAS         1   /** Generates SUN-Rasterfiles */
"SunRaster",                /** Generates SUN-Rasterfiles */
#define FMT_EPSON       2   /** Generates X+Y-Weaved ESC/P-Output */
"Epson",                    /** Generates X+Y-Weaved ESC/P-Output */
#define FMT_ESCP2Y      3   /** Generates Y-Weaved ESC/P2-Output */
"EscP2",                    /** Generates Y-Weaved ESC/P2-Output */
#define FMT_ESCP2XY     4   /** Generates X+Y-Weaved ESC/P2-Output */
"EscP2XY",                  /** Generates X+Y-Weaved ESC/P2-Output */
#define FMT_RTL         5   /** Generates HP-PCL/RTL-Output */
"Pcl",                      /** Generates HP-PCL/RTL-Output */
#define FMT_CANON       6   /** Generates Output for Canon extended mode =
(hr) */
"Canon",                    /** Generates Output for Canon extended mode =
(hr) */
#define FMT_ESCNMY      7   /** Generates Output for Epson Stylus Color =
300 (GR) */
"EscNozzleMap",             /** Generates Output for Epson Stylus Color =
300 (GR) */
NULL
};

static const char *const *const upd_choice[] =3D {
#define C_MAPPER        0   /** the selected Mapper */
   upd_mapper,
#define C_RENDER        1   /** the selected Rendering */
   upd_render,
#define C_FORMAT        2   /** the selected Choice */
   upd_format
};

/** Names for the flags (bool)
*/

static const char *const upd_flags[] =3D {      /** */
#define B_REVDIR            ((uint32) 1<<0)   /** FS-Dir-Flag */
"upFSReverseDirection",                       /** FS-Dir-Flag */
#define B_FIXDIR            ((uint32) 1<<1)   /** Do not alter =
FS-direction */
"upFSFixedDirection",                         /** Do not alter =
FS-direction */
#define B_FSWHITE           ((uint32) 1<<2)   /** Process white in FS */
"upFSProcessWhiteSpace",                      /** Process white in FS */
#define B_FSZERO            ((uint32) 1<<3)   /** Zero FS-Initialization =
*/
"upFSZeroInit",                               /** Zero FS-Initialization =
*/

#define B_PAGEWIDTH         ((uint32) 1<<4)   /** Adjust Width in BOP */
"upAdjustPageWidthCommand",                   /** Adjust Page-Width in =
BOP */
#define B_PAGELENGTH        ((uint32) 1<<5)   /** Adjust Length in BOP =
*/
"upAdjustPageLengthCommand",                  /** Adjust Page-Length in =
BOP */
#define B_TOPMARGIN         ((uint32) 1<<6)   /** Adjust Top-Margin in =
BOP */
"upAdjustTopMarginCommand",                   /** Adjust Top-Margin in =
BOP */
#define B_BOTTOMMARGIN      ((uint32) 1<<7)   /** Adjust Bottom-Margin =
in BOP */
"upAdjustBottomMarginCommand",                /** Adjust Bottom-Margin =
in BOP */
#define B_RESOLUTION        ((uint32) 1<<8)   /** Adjust Resolution in =
BOP */
"upAdjustResolutionCommand",                  /** Adjust Resolution in =
BOP */
#define B_MEDIASIZE         ((uint32) 1<<9)   /** Adjust Mediasize in =
BOP */
"upAdjustMediaSize",                          /** Adjust Mediasize in =
BOP */

#define B_XABS              ((uint32) 1<<10)  /** Use Absolute X-Values =
*/
"upFormatXabsolute",                          /** Use Absolute X-Values =
*/
#define B_YABS              ((uint32) 1<<11)  /** Use Absolute Y-Values =
*/
"upFormatYabsolute",                          /** Use Absolute Y-Values =
*/

#define B_MAP               ((uint32) 1<<12)  /** Mapping Initialized */
"upColorModelInitialized",                    /** Mapping Initialized */
#define B_BUF               ((uint32) 1<<13)  /** Raster-Buffer =
Initialized */
"upRasterBufferInitialized",                  /** Raster-Buffer =
Initialized */
#define B_RENDER            ((uint32) 1<<14)  /** Rendering Initialized =
*/
"upRenderingInitialized",                     /** Rendering Initialized =
*/
#define B_FORMAT            ((uint32) 1<<15)  /** Formatter Initialized =
*/
"upOutputFormatInitialized",                  /** Formatter Initialized =
*/
#define B_ABORT             ((uint32) 1<<16)  /** Abort on Interrupt */
"upOutputAborted",                            /** Abort on Interrupt */
#define B_ERROR             ((uint32) 1<<17)  /** Severe Error detected =
*/
"upErrorDetected",                            /** Severe Error detected =
*/

#define B_OPEN              ((uint32) 1<<18)  /** Open-Command written =
*/
"upWroteData",                                /** Open-Command written =
*/

#define B_YFLIP             ((uint32) 1<<19)  /** Mirrored printing (hr) =
*/
"upYFlip",                                    /** Mirrored printing (hr) =
*/

#define B_REDUCEK           ((uint32) 1<<20)  /** CMY->Black Reduction =
*/
"upFSReduceK"

};

/** B_OK4GO: Bits required to execute the print-loop */

#define B_OK4GO  (B_MAP | B_BUF | B_RENDER | B_FORMAT)

/** Names for the ints
*/

static const char *const upd_ints[] =3D {
#define I_PWIDTH            0                 /** Output-Width */
"upOutputWidth",
#define I_PHEIGHT           1                 /** Output-Height */
"upOutputHeight",
#define I_OCOMP             2                 /** Output-Components */
"upOutputComponents",
#define I_NSCNBUF           3                 /** Output-Buffers */
"upOutputBuffers",
#define I_XSTEP             4                 /** Unit-Step */
"upOutputXStep", /* > 0 -> divide Raster-X, < 0 muliply Raster-X */
#define I_XOFS              5                 /** abs. X-Offset */
"upOutputXOffset",
#define I_YSTEP             6                 /** Unit-Step */
"upOutputYStep", /* > 0 -> divide Raster-Y, < 0 muliply Raster-Y */
#define I_YOFS              7                 /** abs. Y-Offset */
"upOutputYOffset",
#define I_PINS2WRITE        8                 /** Number of Pins */
"upOutputPins",

#define I_NXPASS            9                 /** X-Passes */
"upWeaveXPasses",
#define I_NYPASS           10                 /** Y-Passes */
"upWeaveYPasses",
#define I_NPASS            11                 /** Total # Passes */
"upWeavePasses",
#define I_BEG_Y            12                 /** Start of normal =
Weaving */
"upWeaveInitialScan",
#define I_END_Y            13                 /** End of normal Weaving =
*/
"upWeaveFinalScan",
#define I_BEGSKIP          14                 /** A Scan-Offset */
"upWeaveYOffset",
#define I_ROWS             15                 /** Output rows per pass =
*/
"upNozzleMapRowsPerPass",
#define I_PATRPT           16                 /** mask pattern repeat =
interval */
"upNozzleMapPatternRepeat"
};

/** Names for the Integer-Arrays
*/

static const char *const upd_int_a[] =3D {      /** */
#define IA_COLOR_INFO       0                 /** external color_info */
"upColorInfo",                                /** external color_info */

#define IA_COMPBITS         1                 /** Bits stored per =
Component */
"upComponentBits",                            /** Bits stored per =
Component */
#define IA_COMPSHIFT        2                 /** Shift for the stored =
Bits */
"upComponentShift",                           /** Shift for the stored =
Bits */
#define IA_COMPORDER        3                 /** Order of =
Output-Components */
"upOutputComponentOrder",                     /** Order of =
Output-Components */

#define IA_STD_DY           4                 /** Standard-Weave Feeds =
*/
"upWeaveYFeeds",                              /** Standard-Weave Feeds =
*/
#define IA_STD_IX           5                 /** Standard-Weave =
X-Passes */
"upWeaveXStarts",                             /** Standard-Weave X-Start =
*/
#define IA_BEG_DY           6                 /** Initial-Weave Feeds */
"upWeaveInitialYFeeds",                       /** Initial-Weave Feeds */
#define IA_BEG_IX           7                 /** Initial-Weave X-Start =
*/
"upWeaveInitialXStarts",                      /** Initial-Weave X-Start =
*/
#define IA_BEGBOT           8                 /** Initial-Weave #Pins */
"upWeaveInitialPins",                         /** Initial-Weave #Pins */
#define IA_END_DY           9                 /** Final-Weave Feeds */
"upWeaveFinalYFeeds",                         /** Final-Weave Feeds */
#define IA_END_IX          10                 /** Final-Weave X-Start */
"upWeaveFinalXStarts",                        /** Final-Weave X-Start */
#define IA_ENDTOP          11                 /** Final-Weave #Pins */
"upWeaveFinalPins",                           /** Final-Weave #Pins */
#define IA_ROWMASK         12                 /** The nozzle to row map =
*/
"upNozzleMapRowMask",
#define IA_SCNOFS       13                 /** Mask to scan map */
"upNozzleMapMaskScanOffset"       =20
};

/** Names of the String-Parameters
*/

static const char *const upd_strings[] =3D { /** */
#define S_MODEL             0                 /** Name of the =
Printer-Model */
"upModel",                                    /** Name of the =
Printer-Model */
#define S_OPEN              1                 /** Printer-Begin-Job */
"upBeginJobCommand",                          /** Printer-Begin-Job */
#define S_CLOSE             2                 /** Printer-End-Job */
"upEndJobCommand",                            /** Printer-End-Job */
#define S_BEGIN             3                 /** Printer-Begin-Page */
"upBeginPageCommand",                         /** Printer-Begin-Page */
#define  S_END              4                 /** Printer-End-Page */
"upEndPageCommand",                           /** Printer-End-Page */
#define  S_ABORT            5                 /** Printer-Abort-Command =
*/
"upAbortCommand",                             /** Printer-Abort-Command =
*/

#define S_XMOVE             6                 /** X-Positioning-Command =
*/
"upXMoveCommand",                             /** X-Positioning-Command =
*/
#define S_XSTEP             7                 /** X-Step Command =
(1<I_XSTEP) */
"upXStepCommand",                             /** X-Step Command =
(1<I_XSTEP) */
#define S_SETLF             8                 /** Set-Linefeed-Command =
*/
"upSetLineFeedCommand",                       /** Set-Linefeed-Command =
*/
#define S_YMOVE             9                 /** Y-Positioning-Command =
*/
"upYMoveCommand",                             /** Y-Positioning-Command =
*/
#define S_YSTEP            10                 /** Y-Step Command =
(1<I_YSTEP) */
"upYStepCommand"                              /** Y-Step Command =
(1<I_YSTEP) */
}; /** */

/** Names for the String-Arrays
*/

static const char *const upd_string_a[] =3D {   /** */
#define SA_SETCOMP          0                 /** Select Components */
"upSelectComponentCommands",                  /** Select Components */
#define SA_WRITECOMP        1                 /** Write Component =
Comands */
"upWriteComponentCommands"                    /** Write Component =
Commands */
};                                            /** */

/** Names for the float-Arrays
*/
static const char *const upd_float_a[] =3D {    /** */
#define FA_WXFER            0                 /** White-Transfer */
"upWhiteTransfer",                            /** White-Transfer */
#define FA_RXFER            1                 /** Red-Transfer */
"upRedTransfer",                              /** Red-Transfer */
#define FA_GXFER            2                 /** Green-Transfer */
"upGreenTransfer",                            /** Green-Transfer */
#define FA_BXFER            3                 /** Blue-Transfer */
"upBlueTransfer",                             /** Blue-Transfer */
#define FA_KXFER            4                 /** Black-Transfer */
"upBlackTransfer",                            /** Black-Transfer */
#define FA_CXFER            5                 /** Cyan-Transfer */
"upCyanTransfer",                             /** Cyan-Transfer */
#define FA_MXFER            6                 /** Magenta-Transfer */
"upMagentaTransfer",                          /** Magenta-Transfer */
#define FA_YXFER            7                 /** Yellow-Transfer */
"upYellowTransfer",                           /** Yellow-Transfer */
#define FA_MARGINS          8                 /** private Margins */
"upMargins",                                  /** private Margins */
#define FA_MAP              9                 /** Color-Map       */
"upColorMap"                                  /** Color-Map       */
};                                            /** */

/* ------------------------------------------------------------------- =
*/
/* UPD-specific datatypes                                              =
*/
/* ------------------------------------------------------------------- =
*/

/**
int32 and uint32 are 32Bit-Integer-Types used in the
Floyd-Steinberg Algorithm and instead of gx_color_index. The
8-Byte long's on some 64Bit-Machines are apparently useless,
since gdevprn.c does (currently) support only 32-Bit Rasterdata.
*/

#if     arch_log2_sizeof_int < 2  /* int is too small */
   typedef          long  int32;
#define                   INT32_MIN  LONG_MIN
#define                   INT32_MAX  LONG_MAX
   typedef unsigned long uint32;
#define                  UINT32_MAX ULONG_MAX
#else                             /* int is sufficient */
   typedef          int   int32;
#define                   INT32_MIN   INT_MIN
#define                   INT32_MAX   INT_MAX
   typedef unsigned int  uint32;
#define                  UINT32_MAX  UINT_MAX
#endif                            /* use int or long ? */

/**
"updcmap" is used by the color-mapping functions of the driver.
there are four cmaps in the "uniprint"-structure, one for each =
component.
To be exact, it's not "4" but rather "UPD_CMAP_MAX", which is a synonym.
*/

typedef struct updcmap_s { /** */
   gx_color_value      *code;      /** Values related to codes */
   uint32               bitmsk;    /** Mask, right justified */
   int                  bitshf;    /** Shift to right-justify */
   int                  xfer;      /** Index to the Xfer-Array */
   int                  bits;      /** # of Bits */
   int                  comp;      /** Output-Number */
   bool                 rise;      /* Rising/Falling Curve */
} updcmap_t, *updcmap_p;  /** */
typedef const updcmap_t *updcmap_pc;


/**
"updcomp" holds similar informations, but is used for the rendering
*/

typedef struct updcomp_s {  /* Parameters for Floyd-Steinberg */
   int32                offset;    /* Offset added to scaled values */
   int32                scale;     /* Scale for the raw values */
   int32                threshold; /* Val must be larger than this to =
fire */
   int32                spotsize;  /* subtracted from Val when fired */
   uint32               bitmsk;    /* Mask */
   int                  bitshf;    /* shift */
   int                  bits;      /* # of Bits */
   int                  cmap;      /* Index for the Parameter-name */
} updcomp_t, *updcomp_p;    /* Parameters for Floyd-Steinberg */

/** updscan is the Element of the scan-buffer. */

typedef struct updscan_s { /* Single Scanline (1 Bit/Pixel) */
   byte   *bytes;      /* Buffer used w. 32-Bit Words */
   int    *xbegin;     /* 1st  Pixel set (or nbytes<<3 if none) */
   int    *xend;       /* last Pixel set (or -1, if none) */
} updscan_t, *updscan_p;   /* Single Scanline (1 Bit/Pixel) */


/** Main upd-Structure ***/

#define UPD_CMAP_MAX     4 /** Number of Colormaps provided */
#define UPD_VALPTR_MAX  32 /** Number of valbuf-Pointers */

#define upd_proc_pxlget(name) uint32 name(upd_p upd)
#define upd_proc_render(name) int name(upd_p upd)
#define upd_proc_writer(name) int name(upd_p upd,FILE *out)

struct upd_s { /* All upd-specific data */

   int                   *choice;     /** Named-Choices */
   int                   *ints;       /** Integers */
   gs_param_int_array    *int_a;      /** Integer-Arrays */
   gs_param_string       *strings;    /** Strings */
   gs_param_string_array *string_a;   /** String-Arrays */
   gs_param_float_array  *float_a;    /** Float-Arrays */

   updcmap_t              cmap[UPD_CMAP_MAX]; /** Mapping-Data */

   byte                  *gsbuf;      /* Storage for GS-Rasterdata */
   byte                  *gsscan;     /* Begin of GS-Rasterdata */

   byte                  *pxlptr;     /* Source for pxlget */
   upd_proc_pxlget(     (*pxlget));   /* The Pixel-Reader */
   upd_proc_render(     (*render));   /* Actual Rendering */
   upd_proc_writer(     (*writer));

   updscan_p             *scnbuf;     /* Output-Values */
   int32                 *valbuf;     /* Floyd-Steinberg-Buffer */
   void                  *valptr[UPD_VALPTR_MAX];

   byte                  *outbuf;     /* Output-Buffer */
   upd_proc_render(     (*start_render)); /* Setup for rendering */
   upd_proc_writer(     (*start_writer)); /* Setup for writilg */

   uint32                 flags;      /** Some flags */
   int                    pdwidth;    /** pdev-width upon open */
   int                    pdheight;   /** pdev-height upon open */

   uint                   ngsbuf;     /* Size of gsbuf */
   int                    gswidth;    /* Width in GS-Pixels */
   int                    gsheight;   /* Height in GS-Pixels */

   int                    rwidth;     /* Rendering-Width */

   int                    pwidth;     /* Printing-Width */
   int                    pheight;    /* # scanlines printed */

   int                    ncomp;      /* # Components in gsbuf */
   int                    nmap;       /* # Entries in color-map */

   uint                   nvalbuf;    /* Size of valbuf */
   int                    nscnbuf;    /* Number of entries in scnbuf. */

   int                    ocomp;      /* # Components written */
   int                    nbytes;     /* Size of scnbuf[][].words */
   int                    nlimits;    /* Size of scnbuf[][].xbegin/end =
*/
   int                    scnmsk;     /* Size of scanbuf - 1 */
   uint                   noutbuf;    /* Size of the Output-Buffer */

   int                    ixpass;     /* Current X-pass (0 ... nxpass-1) =
*/
   int                    ipass;      /* Current pass (0 ... npass-1) */
   int                    icomp;      /* Selected Component */
   int                    lf;         /* Selected Line-Space */

   int                    xprinter;   /* Actual X-Position */

   int                    yscan;      /* Top-Scan (page-vari) */
   int                    yprinter;   /* Actual Y-Position (page-vari) =
*/
   int                    yscnbuf;    /* Y not yet buffered */
};             /* All upd-specific data */


/* ------------------------------------------------------------------- =
*/
/* Various Message-Levels                                              =
*/
/* ------------------------------------------------------------------- =
*/

/**
UPD_MESSAGES, Is collection of Bits, that controls Messages
*/

#define UPD_M_NONE      0x0000 /** No Messages at all */
#define UPD_M_ERROR     0x0001 /** Errors */
#define UPD_M_WARNING   0x0002 /** Warnings */
#define UPD_M_TOPCALLS  0x0004 /** Log Calls to main Functions */
#define UPD_M_MAPCALLS  0x0008 /** Log Color-Mapping-Calls */
#define UPD_M_SETUP     0x0010 /** Log Setup-Activity */
#define UPD_M_FSBUF     0x0020 /** Error-Summary for valbuf */
#define UPD_M_FMTVARS   0x0040 /** (GR) Formatting variables */=20

/* ------------------------------------------------------------------- =
*/
/* The UPD-Routines                                                    =
*/
/* ------------------------------------------------------------------- =
*/

/**
Besides the main routines required for the color-mapping, that were
declared near the beginning, there are some auxillary functions.
Most prominent are "upd_open_map" and "upd_close_map", which
do the proper actions when opening and closing the device.
*/

private int             upd_open_map( upd_device *udev);
private int             upd_close_map(upd_device *udev);

/**
But "upd_truncate" and "upd_expand" are also mentionable. They are
the actual workhorses for the component-oriented mapping. When mapping
the 16Bit Component-Values to the indices, some truncation takes place
and this is what "upd_truncate" does, in the most general manner i can
think of and with O(log(n)) in time. "upd_expand" is required for the
reverse mapping-functions and is a constant-time `algorithm'.
*/
private uint32          upd_truncate(upd_pc,int,gx_color_value);
private gx_color_value  upd_expand(  upd_pc,int,uint32);

/**
The next group of internal functions adresses the rendering. Besides
the main-functions "upd_open_render" and "upd_close_render", there
are groups of up to 3 Functions, for each algorithm available with
UPD. Two routines are invoked during open and close and the third
is called for each scanline. Actually a fourth function is provided,
that is invoked at the beginning of each page to be printed, but the
current algorithms do not need it.
*/
private void            upd_open_render(   upd_device *udev);
private void            upd_close_render(  upd_device *udev);

private void            upd_open_fscomp(   upd_device *udev);
private int             upd_fscomp(        upd_p upd);
private void            upd_close_fscomp(  upd_device *udev);

private void            upd_open_fscmyk(   upd_device *udev);
private int             upd_fscmyk(        upd_p upd);

private void            upd_open_fscmy_k(  upd_device *udev);
private int             upd_fscmy_k(       upd_p upd);

/**
I hope that the formatting stuff can be kept simple and thus most
of the work is done inside the general open and close-functions.
During open, there is a call to a format-specific open-function, but
this is only for checking and determining the amount of of bytes =
required
for the output-buffer (and limit-values in the scan-buffer).
*/
private int             upd_open_writer(   upd_device *udev);
private void            upd_close_writer(  upd_device *udev);
#if UPD_SIGNAL
private void            upd_signal_handler(int sig);
#endif

/**
The first format are the uncompressed! SUN-Rasterfiles. The primary =
intention
of this format is testing, but it might turn out to be useful for other
purposes, even if the amount of generated data is huge. On the other =
hand
it is a violation of UPD's rules: the start-routine computes the =
Begin-Page
sequence (the Rasterfile header) since it would be a nuisance to provide
this code within each (test-)personalization in PostScript.
*/
private int             upd_open_rascomp(   upd_device *udev);
private int             upd_start_rascomp(  upd_p upd, FILE *out);
private int             upd_rascomp(        upd_p upd, FILE *out);

/**
The second format is ESC/P, the format introduced with the first Epson
impact printers. This format is used by a lot of other printers too.
It is also uncompressed. This formatter supports X- and Y-Weaving,
which makes it the most sophisticated one inside this driver.
*/

private void            upd_limits(        upd_p upd, bool check);
private int             upd_open_wrtescp(  upd_device *udev);
private int             upd_wrtescp(       upd_p upd, FILE *out);

/**
The third format is ESC/P2, the format use by the newer Epson-Printers.
It allows runlength-Compression similar to the RTL/PCL-Family of =
Printers.
This formatter does not allow for X-Weaving.

The fourth writer is a ESC/P2-Writer, that supports X-Weaving
*/
private int             upd_rle(byte *out,const byte *in,int nbytes);
private int             upd_open_wrtescp2( upd_device *udev);
private int             upd_wrtescp2(      upd_p upd, FILE *out);
private int             upd_wrtescp2x(     upd_p upd, FILE *out);

/**
The fifth writer is a HP-RTL/PCL-Writer
*/

private int             upd_open_wrtrtl(   upd_device *udev);
private int             upd_wrtrtl(        upd_p upd, FILE *out);

/**
The sixth writer is for Canon Extended Mode (currently BJC610) (hr)
*/

private int             upd_open_wrtcanon( upd_device *udev);
private int             upd_wrtcanon(      upd_p upd, FILE *out);

/**
The seventh writer is for ESC P/2 Nozzle Map Mode (currently Stylus =
Color 300) (GR)
*/

private int             upd_wrtescnm(      upd_p upd, FILE *out);


/**
Generalized Pixel Get & Read
*/
private uint32 upd_pxlfwd(upd_p upd);
private uint32 upd_pxlrev(upd_p upd);
#define upd_pxlget(UPD) (*UPD->pxlget)(UPD)

private void *upd_cast(const void *);

/* ------------------------------------------------------------------- =
*/
/* Macros to deal with the Parameter-Memory                            =
*/
/* ------------------------------------------------------------------- =
*/

/**
Usually the creation of copies of external parameters is not necessary,
at least with gs-versions > 4.03. But uniprint writes to the parameters
in some cases or creates some by itself, thus to get a unified interface
all parameter-data are copied and thus it is legal to manipulate them.

Here are several Macros, named "UPD_MM_*" to deal with that.
*/

/** UPD_MM_GET_ARRAY allocates & initializes an array of values */
#define UPD_MM_GET_ARRAY(Which,Nelts)                                 \
   Which =3D NULL;                                                      =
\
   if(0 < (Nelts)) {                                                  \
      byte *tmp =3D =
gs_malloc(Nelts,sizeof(Which[0]),"uniprint/params");\
      if(tmp) {                                                       \
         memset(tmp,0,(Nelts)*sizeof(Which[0]));                      \
         Which =3D (void *) tmp;                                        =
\
      } else {                                                        \
          return_error(gs_error_VMerror);                             \
      }                                                               \
   }

/** UPD_MM_DEL_ARRAY frees an array of values */
#define UPD_MM_DEL_ARRAY(Which,Nelts,Delete)                            =
\
   if(Which && 0 < (Nelts)) {                                           =
\
      uint ii;                                                          =
\
      for(ii =3D 0; (Nelts) > ii; ++ii) Delete(Which[ii]);               =
 \
      =
gs_free(upd_cast(Which),Nelts,sizeof(Which[0]),"uniprint/params");\
   }                                                                    =
\
   Which =3D 0

/** UPD_MM_DEL_VALUE deletes a value, does nothing */
#define UPD_MM_DEL_VALUE(Which) /* */

/** UPD_MM_DEL_PARAM deletes a single gs-array-parameter */
#define UPD_MM_DEL_PARAM(Which)  {                                  \
   if(Which.data && Which.size)                                     \
      gs_free(upd_cast(Which.data),Which.size,sizeof(Which.data[0]),\
         "uniprint/params");                                        \
}

/** UPD_MM_DEL_APARAM deletes a nested gs-array-parameter */
#define UPD_MM_DEL_APARAM(Which) {                                  \
   if(Which.data && Which.size) {                                   \
      uint iii;                                                     \
      for(iii =3D 0; iii < Which.size; ++iii)                         \
         UPD_MM_DEL_PARAM(Which.data[iii]);                         \
      gs_free(upd_cast(Which.data),Which.size,sizeof(Which.data[0]),\
         "uniprint/params");                                        \
   }                                                                \
}

/** UPD_MM_CPY_ARRAY creates a new copy of an array of values */
#define UPD_MM_CPY_ARRAY(To,From,Nelts,Copy)                \
   UPD_MM_GET_ARRAY(To,Nelts);                              \
   if(To && From) {                                         \
      uint ii;                                              \
      for(ii =3D 0; (Nelts) > ii; ++ii) Copy(To[ii],From[ii]);\
   }

/** UPD_MM_CPY_VALUE Copies a simple Value */
#define UPD_MM_CPY_VALUE(To,From)  To =3D From

/** UPD_MM_CPY_PARAM Creates a copy of a gs-parameter */
#define UPD_MM_CPY_PARAM(To,From)                                       =
\
   if(From.data && From.size) {                                         =
\
      UPD_MM_GET_ARRAY(To.data,From.size);                              =
\
      if(To.data) {                                                     =
\
         To.size =3D From.size;                                          =
 \
         =
memcpy(upd_cast(To.data),From.data,To.size*sizeof(To.data[0]));\
      }                                                                 =
\
   }

/** UPD_MM_CPY_APARAM Creates a copy of a nested gs-parameter */
#define UPD_MM_CPY_APARAM(To,From)                                     \
   if(From.data && From.size) {                                        \
      UPD_MM_GET_ARRAY(To.data,From.size);                             \
      if(To.data) {                                                    \
         gs_param_string *tmp2 =3D (gs_param_string *) =
upd_cast(To.data);\
         uint iii;                                                     \
         To.size =3D From.size;                                          =
\
         for(iii =3D 0; To.size > iii; ++iii)                            =
\
            UPD_MM_CPY_PARAM(tmp2[iii],From.data[iii]);                \
      }                                                                \
   }

/* ------------------------------------------------------------------- =
*/
/* UPD-Initialized-Data                                                =
*/
/* ------------------------------------------------------------------- =
*/

/** Version-String */

static const char rcsid[] =3D "$Revision: 1.11 $";

/** Default-Transfer-curve */

static const float upd_data_xfer[2] =3D { 0.0, 1.0 };

/*@ > */

/* ------------------------------------------------------------------- =
*/
/* upd_cast: keeps some compilers more happy [dangerous]               =
*/
/* ------------------------------------------------------------------- =
*/

private void *
upd_cast(const void *data)
{
  return (void *) data;
}

/* ------------------------------------------------------------------- =
*/
/* upd_signal_handler: Catch interrupts                                =
*/
/* ------------------------------------------------------------------- =
*/

#if UPD_SIGNAL
static upd_p sigupd =3D NULL;
private void
upd_signal_handler(int sig)
{
  if(sigupd) sigupd->flags |=3D B_ABORT;
}
#endif


/* ------------------------------------------------------------------- =
*/
/* upd_print_page: The main workhorse                                  =
*/
/* ------------------------------------------------------------------- =
*/

/**
Function: upd_print_page

This is the top-level printing routine. It works through this
steps:

 1. Once for each generated file, the "device-open-sequence" is written.
 2. The "page-begin-sequence" is written.

 3. The data are generated and written:
    3.1: Data are converted into a "printer-family"-specific format.
         This step includes the halftoning, if selected.
    3.2: Data are written with a printer-specific function.
         There is not much code-compression inside theese functions,
         since i observed to improvments in print-speed. Other
         drivers do a better job in this.

 4. The "page-end-sequence" is written.
 5. If a one-page-per-file mode is selected, the "device-close-sequence"
    is added to the output. For multi-page files, this writing is
    performed in "upd_close", the drivers close-function.

The routine is quite short, since all the allocation and checking
occur in upd_open and upd_putparams. The only test, that upd_print_page
does, is the verification wether the device is in a sane state. This
must be done here, since during the initialisation, the device is
usually opened several times, before obtaining a valid state.
*/

private int
upd_print_page(gx_device_printer *pdev, FILE *out)
{
   upd_device *const udev  =3D (upd_device *) pdev;
   const upd_p       upd   =3D udev->upd;
   const int *const  ints  =3D upd ? upd->ints : NULL;
   int error,need,yfill;

#if UPD_SIGNAL /* variables required for signal-handling only */
   void (*oldint )(int) =3D NULL;
   void (*oldterm)(int) =3D NULL;
   upd_p  oldupd            =3D sigupd;
#endif         /* variables required for signal-handling only */

/*
 * Refuse to work, if not explicitly enabled during open
 * (some/lot of allocated memory is required)
 */
   if(!upd || B_OK4GO !=3D (upd->flags & (B_OK4GO | B_ERROR))) {
#if UPD_MESSAGES & (UPD_M_ERROR | UPD_M_TOPCALLS)
         errprintf("CALL-REJECTED upd_print_page(0x%05lx,0x%05lx)\n",
             (long) udev,(long) out);
#endif
      return gs_error_undefined;
   }

#if UPD_MESSAGES & UPD_M_TOPCALLS
   errprintf("CALL: upd_print_page(0x%05lx,0x%05lx)\n",
      (long) udev,(long) out);
#endif

#if UPD_SIGNAL /* Setup of signal-handling */
   sigupd  =3D upd;
   oldint  =3D signal(SIGINT, upd_signal_handler);
   oldterm =3D signal(SIGTERM,upd_signal_handler);
#endif         /* Setup of signal-handling */

/*
 * If the OutputFile was just opened, transfer the Open-Sequence to it.
 */
   if(!(upd->flags & B_OPEN)) {

      if(0   <  upd->strings[S_OPEN].size)
         =
fwrite(upd->strings[S_OPEN].data,1,upd->strings[S_OPEN].size,out);
      upd->flags |=3D B_OPEN;
   }
/*
 * Always write the the Page-begin-sequence
 */
   if(0  <   upd->strings[S_BEGIN].size)
      =
fwrite(upd->strings[S_BEGIN].data,1,upd->strings[S_BEGIN].size,out);
/*
 * Establish page-variables
 */

/* Positions */
   upd->xprinter  =3D 0;
   upd->yscan     =3D 0; /* Position we are processing */
   upd->yprinter  =3D 0; /* Actual Printer-Positions */
   upd->yscnbuf   =3D 0; /* Next free scnbuf-Line */

/* Rendering & Writing Setup, if available */
   if(upd->start_render) (*upd->start_render)(upd);
   if(upd->start_writer) (*upd->start_writer)(upd,out);

/* How many scanlines do we need ? */
   need =3D ints[I_NYPASS] * ints[I_PINS2WRITE];
   if(0 >=3D need) need =3D 1;

/* The Weave-counters */
   upd->ipass  =3D  0;
   upd->ixpass =3D  0;
   upd->icomp  =3D -1; /* Enforces initial selection */
   upd->lf     =3D -1; /* Enforces initial selection */
/*
 * Main Loop
 */
   while(upd->pheight > upd->yscan) { /* Main-Loop */

/*
 *    Load as much data into the scan-buffer as possible
 *    (this is done in scan-sequence, the printing not necessarily.)
 */
      if(ints[I_BEGSKIP] > upd->yscan) yfill =3D 0;
      else                             yfill =3D upd->yscan - =
ints[I_BEGSKIP];

      for(yfill +=3D upd->nscnbuf; upd->yscnbuf < yfill; upd->yscnbuf++) =
{

         if(upd->gsheight > upd->yscnbuf)  {

            if(0 > (*dev_proc(udev,get_bits))((gx_device *) udev,
                                   =
upd->yscnbuf,upd->gsbuf,&upd->gsscan)) {
#if UPD_MESSAGES & UPD_M_WARNING
               errprintf("get_bits aborted with error, yscnbuf =3D =
%4d\n",
                  upd->yscnbuf);
#endif
               break;
            }
         } else {

            memset(upd->gsscan =3D upd->gsbuf,0,upd->ngsbuf);

         }

         if(0 > (*upd->render)(upd)) {
#if UPD_MESSAGES & UPD_M_WARNING
            errprintf("Rendering aborted with error, yscnbuf =3D %4d\n",
               upd->yscnbuf);
#endif
            break;
         }

      }
/*
 *    Did the buffering loop take an error exit ?
 */
      if((upd->yscnbuf ^ yfill) & upd->scnmsk) break;
/*
 *    Print as much as possible
 */
      while((upd->yscan - ints[I_BEGSKIP] + need) < upd->yscnbuf) {

/*        first write the scan(s) */
          (*upd->writer)(upd,out);

/*        Check for termination */
          if(upd->yscan >=3D upd->pheight) break;
          if(upd->flags  & B_ABORT ) {
#if UPD_MESSAGES & UPD_M_WARNING
             errprintf("Printing aborted upon interrupt, yscan =3D =
%4d\n",
                upd->yscan);
#endif
             break;
          }
      }
/*
 *    Did the print-Loop take an error exit ?
 */
      if((upd->yscan - ints[I_BEGSKIP] + need) < upd->yscnbuf) break;
   }                                  /* Main-Loop */

/*
 * If we aborted for some reason, use the dedicated sequence
 */

   if((upd->pheight > upd->yscan) &&
      (0  <  upd->strings[S_ABORT].size)) { /* Only This! */
      =
fwrite(upd->strings[S_ABORT].data,1,upd->strings[S_ABORT].size,out);

      upd->flags &=3D ~B_OPEN; /* Inhibit Close-Sequence ! */
/*
 * If there is no special sequence, or we came to normal end,
 * write the normal sequence, if any
 */

   } else if(0  <   upd->strings[S_END].size) {
      fwrite(upd->strings[S_END].data,1,upd->strings[S_END].size,out);
   }
/*
 * If necessary, write the close-sequence
 */
   if((NULL !=3D udev->fname  ) && strchr(udev->fname,'%')) {

      if(0  <   upd->strings[S_CLOSE].size)
         =
fwrite(upd->strings[S_CLOSE].data,1,upd->strings[S_CLOSE].size,out);

      upd->flags &=3D ~B_OPEN;
   }

/*
 * clean up, and return status
 */

   fflush(out); /* just to prepare for ferror */

   if(upd->pheight > upd->yscan) error =3D gs_error_interrupt;
   else if(ferror(out))          error =3D gs_error_ioerror;
   else                          error =3D 0;

#if UPD_MESSAGES & UPD_M_TOPCALLS
   errprintf("RETURN: %d =3D upd_print_page(0x%05lx,0x%05lx)\n",
      error,(long) udev,(long)out);
#endif

#if UPD_SIGNAL /* Restore Interrupt-state */
      sigupd =3D oldupd;
      (void) signal(SIGINT ,oldint);
      (void) signal(SIGTERM,oldterm);
#endif         /* Restore Interrupt-state */

   return error;
}

/* ------------------------------------------------------------------- =
*/
/* upd_open: Initialize everything for printing                        =
*/
/* ------------------------------------------------------------------- =
*/
/**
"upd_open" is -through the specified table of procedures- called instead
of the normal open-procedures for printer-devices, that performs quite
a complex job. Thus it is necessary to call this  `superclass-open=B4
here.

Besides that, this routine does quite a complex job too, in initializes
everything required to print a page. This might be time-consuming, the
alternative would be "upd_print_page", but i often print 100 pages or
more, but i never experienced more than 5-6 open-calls.
*/

private int
upd_open(gx_device *pdev)
{
   upd_device *const udev    =3D  (upd_device *) pdev;
   const upd_p       upd     =3D  udev->upd;
   int              error;

#if UPD_MESSAGES & UPD_M_TOPCALLS
      errprintf("CALL: upd_open(0x%05lx)\n",(long) pdev);
#endif

/** enforce the UPD-Margins */

   if((NULL !=3D upd) &&=20
      (NULL !=3D upd->float_a[FA_MARGINS].data) &&
      (4    =3D=3D upd->float_a[FA_MARGINS].size)    ) {
      static float m[4];
      m[1] =3D upd->float_a[FA_MARGINS].data[1] / 72.0;
      m[3] =3D upd->float_a[FA_MARGINS].data[3] / 72.0;
      if(B_YFLIP & upd->flags) {
         m[0] =3D upd->float_a[FA_MARGINS].data[2] / 72.0;
         m[2] =3D upd->float_a[FA_MARGINS].data[0] / 72.0;
      } else {
         m[0] =3D upd->float_a[FA_MARGINS].data[0] / 72.0;
         m[2] =3D upd->float_a[FA_MARGINS].data[2] / 72.0;
      }
      gx_device_set_margins((gx_device *) udev, m, true);
   }

/** call the super-class open **/
   error =3D gdev_prn_open(pdev);

/** invoke the subroutines, if an upd is present. */

   if(upd) {

      upd->flags &=3D ~B_OK4GO;

/**
The following initializations are run, even in case of an error in
the super-class open, just to bring our upd into a sane state.
*/
      if(0 > error) upd->flags |=3D B_ERROR;

      if(gs_error_VMerror =3D=3D upd_open_map(udev)) error =3D =
gs_error_VMerror;

/**
The following piece of code is here for demonstration-purposes:
It determines the size of the printed image and allocates the
buffer for the raw raster-data
*/
      upd->gswidth  =3D udev->width -
         =
(int)((dev_l_margin(udev)+dev_r_margin(udev))*udev->x_pixels_per_inch);

      upd->gsheight =3D udev->height -
         =
(int)((dev_t_margin(udev)+dev_b_margin(udev))*udev->y_pixels_per_inch);

      upd->ngsbuf =3D 0;    /* Ensure sane values */
      upd->gsbuf  =3D NULL; /* Ensure sane values */

      if(B_MAP & upd->flags) { /* Only if prerequisites were met */
         uint want  =3D gx_device_raster(pdev,true);
         upd->gsbuf =3D gs_malloc(want,1,"upd/gsbuf");

         if(upd->gsbuf) {
            upd->ngsbuf =3D want;
            upd->flags |=3D B_BUF;  /* Signal Success */
         } else {
            error =3D gs_error_VMerror; /* Signal Error */
            upd->flags |=3D B_ERROR;
         }

      }                            /* Only if prerequisites were met */

      upd_open_render(udev);  /* First subloop in printing */

      if(gs_error_VMerror =3D=3D upd_open_writer(udev)) error =3D =
gs_error_VMerror;

      udev->upd->pdwidth  =3D udev->width;
      udev->upd->pdheight =3D udev->height;

#if UPD_MESSAGES & UPD_M_SETUP
      if((upd->flags & (B_OK4GO | B_ERROR)) =3D=3D B_OK4GO) {
        int i,j,l,ln,lv;
        errprintf("\nupd->flags    =3D 0x%05lx\n",(unsigned =
long)upd->flags);
        errprintf(  "upd->pdwidth  =3D %5d\n",upd->pdwidth);
        errprintf(  "upd->pdheight =3D %5d\n",upd->pdheight);
        errprintf(  "upd->ngsbuf   =3D %5u\n",upd->ngsbuf);
        errprintf(  "upd->gswidth  =3D %5d\n",upd->gswidth);
        errprintf(  "upd->gsheight =3D %5d\n",upd->gsheight);
        errprintf(  "upd->rwidth   =3D %5d\n",upd->rwidth);
        errprintf(  "upd->pwidth   =3D %5d\n",upd->pwidth);
        errprintf(  "upd->pheight  =3D %5d\n",upd->pheight);
        errprintf(  "upd->nvalbuf  =3D %5u\n",upd->nvalbuf);
        errprintf(  "upd->nscnbuf  =3D %5d\n",upd->nscnbuf);
        errprintf(  "upd->ncomp    =3D %5d\n",upd->ncomp);
        errprintf(  "upd->ocomp    =3D %5d\n",upd->ocomp);
        errprintf(  "upd->nbytes   =3D %5d\n",upd->nbytes);
        errprintf(  "upd->nlimits  =3D %5d\n",upd->nlimits);
        errprintf(  "upd->scnmsk   =3D %5d\n",upd->scnmsk);
        errprintf(  "upd->noutbuf  =3D %5u\n",upd->noutbuf);
        errprintf(  "upd->ixpass   =3D %5d\n",upd->ixpass);
        errprintf(  "upd->ipass    =3D %5d\n",upd->ipass);
        errprintf(  "upd->icomp    =3D %5d\n",upd->icomp);
        errprintf(  "upd->lf       =3D %5d\n",upd->lf);
        errprintf(  "upd->xprinter =3D %5d\n",upd->xprinter);
        errprintf(  "upd->yscan    =3D %5d\n",upd->yscan);
        errprintf(  "upd->yprinter =3D %5d\n",upd->yprinter);
        errprintf(  "upd->yscnbuf  =3D %5d\n",upd->yscnbuf);

        ln =3D 13;
        lv =3D 5;
        for(i =3D 0; countof(upd_choice) > i; ++i) {
          if(!upd_choice[i]) continue;
          l =3D strlen(upd_choice[i][0]);
          if(ln < l) ln =3D l;=20
          for(j =3D 1; upd_choice[i][j]; ++j) {
            l =3D strlen(upd_choice[i][j]);
            if(lv < l) lv =3D l;
          }
        }

        for(i =3D 0; countof(upd_flags) > i; ++i) {
          if(upd_flags[i]) {
            l =3D strlen(upd_flags[i]);
            if(ln < l) ln =3D l;
          }
        }

        for(i =3D 0; countof(upd_ints) > i; ++i) {
          if(upd_ints[i]) {
            l =3D strlen(upd_ints[i]);
            if(ln < l) ln =3D l;
          }
        }

        for(i =3D 0; countof(upd_int_a) > i; ++i) {
          if(upd_int_a[i]) {
            l =3D strlen(upd_int_a[i]);
            if(ln < l) ln =3D l;
          }
        }

        for(i =3D 0; countof(upd_strings) > i; ++i) {
          if(upd_strings[i]) {
            l =3D strlen(upd_strings[i]);
            if(ln < l) ln =3D l;
          }
        }

        for(i =3D 0; countof(upd_string_a) > i; ++i) {
          if(upd_string_a[i]) {
            l =3D strlen(upd_string_a[i]);
            if(ln < l) ln =3D l;
          }
        }

        for(i =3D 0; countof(upd_float_a) > i; ++i) {
          if(upd_float_a[i]) {
            l =3D strlen(upd_float_a[i]);
            if(ln < l) ln =3D l;
          }
        }

        for(i =3D 0; countof(upd_choice) > i; ++i) {
          if(upd_choice[i]) {
            errprintf("%*s =3D %-*s (%2d)\n",ln,upd_choice[i][0],
               lv,upd_choice[i][upd->choice[i]],upd->choice[i]);
          } else {
            errprintf("%*s[%2d] =3D %2d\n",ln-4,"upd_choice",i,
               upd->choice[i]);
          }
        }

        for(i =3D 0; countof(upd_flags) > i; ++i) {
          if(upd_flags[i]) {
            errprintf("%*s =3D %s\n",ln,upd_flags[i],
               ((uint32) 1 << i) & upd->flags ? "true" : "false");
          } else {
            errprintf("%*s[%2d] =3D %s\n",ln-4,"upd_flags",i,
               ((uint32) 1 << i) & upd->flags ? "true" : "false");

          }
        }

        for(i =3D 0; countof(upd_ints) > i; ++i) {
          if(upd_ints[i]) {
            errprintf("%*s =3D %5d\n",ln,upd_ints[i],upd->ints[i]);
          } else {
            errprintf("%*s[%2d] =3D =
%5d\n",ln-4,"upd_ints",i,upd->ints[i]);
          }
        }

      }


      errprintf("\n%sready to print\n\n",
         B_OK4GO !=3D (upd->flags & (B_OK4GO | B_ERROR)) ?
         "NOT " : "");
#endif

   }

#if UPD_MESSAGES & UPD_M_TOPCALLS
      errprintf("RETURN: %d =3D upd_open(0x%05lx)\n",
         error,(long) pdev);
#endif

   return error;
}

/* ------------------------------------------------------------------- =
*/
/* upd_close: Release everything allocated in upd_open                 =
*/
/* ------------------------------------------------------------------- =
*/

private int
upd_close(gx_device *pdev)
{
   upd_device *const udev    =3D  (upd_device *) pdev;
   const upd_p       upd     =3D  udev->upd;
   int         error =3D 0;
   int         code;

#if UPD_MESSAGES & UPD_M_TOPCALLS
   errprintf("CALL: upd_close(0x%05lx)\n",(long)pdev);
#endif

/** If necessary, write the close-sequence **/

   if( upd && (( B_OPEN | B_OK4GO) =3D=3D
               ((B_OPEN | B_OK4GO | B_ERROR) & upd->flags))) {

      if(udev->file && upd->strings && 0 < upd->strings[S_CLOSE].size)
         fwrite(upd->strings[S_CLOSE].data,1,
                upd->strings[S_CLOSE].size,udev->file);

      upd->flags &=3D ~B_OPEN;
   }

/** Then release the open-allocated memory */
   if(upd) {

      upd_close_writer(udev);

      if(upd->gsbuf)
         gs_free(upd->gsbuf,upd->ngsbuf,1,"uniprint/gsbuf");
      upd->gsbuf  =3D NULL;
      upd->ngsbuf =3D 0;
      upd->flags &=3D ~B_BUF;

      upd_close_render(udev);
      upd_close_map(udev);

      UPD_MM_DEL_ARRAY(upd->choice,  countof(upd_choice),  =
UPD_MM_DEL_VALUE);
      UPD_MM_DEL_ARRAY(upd->ints,    countof(upd_ints),    =
UPD_MM_DEL_VALUE);
      UPD_MM_DEL_ARRAY(upd->int_a,   countof(upd_int_a),   =
UPD_MM_DEL_PARAM);
      UPD_MM_DEL_ARRAY(upd->strings, countof(upd_strings), =
UPD_MM_DEL_PARAM);
      =
UPD_MM_DEL_ARRAY(upd->string_a,countof(upd_string_a),UPD_MM_DEL_APARAM);
      UPD_MM_DEL_ARRAY(upd->float_a, countof(upd_float_a), =
UPD_MM_DEL_PARAM);

      gs_free(upd,sizeof(upd[0]),1,"uniprint");

      udev->upd =3D NULL;
   }

/** Then call the superclass close **/
   code =3D gdev_prn_close(pdev);
   error =3D error > code ? code : error;


#if UPD_MESSAGES & UPD_M_TOPCALLS
      errprintf("RETURN: %d =3D upd_close(0x%05lx)\n",
         error,(long) pdev);
#endif

   return error;
}

/* ------------------------------------------------------------------- =
*/
/* upd_get_params: Export Parameters to the Interpreter                =
*/
/* ------------------------------------------------------------------- =
*/

#if UPD_MESSAGES & UPD_M_TOPCALLS
#define UPD_EXIT_GET(Err,Dev,List)                                      =
\
   if(0 > Err) {                                                        =
\
      errprintf("RETURN-%d: %d upd_get_params(0x%05lx,0x%05lx)\n", \
         __LINE__,Err,(long) Dev,(long) List);                          =
\
      return_error(Err);                                                =
\
   }
#else
#define UPD_EXIT_GET(Err,Dev,List) if(0 > Err) return_error(Err);
#endif

private int
upd_get_params(gx_device *pdev, gs_param_list *plist)
{
   upd_device *const udev    =3D  (upd_device *) pdev;
   const upd_p       upd     =3D  udev->upd;
   int               error,i;

#if UPD_MESSAGES & UPD_M_TOPCALLS
      errprintf("CALL: upd_get_params(0x%05lx,0x%05lx)\n",
         (long) udev,(long) plist);
#endif

/** Call the SuperClass-get_params at the beginning */
   error =3D gdev_prn_get_params((gx_device *)udev,plist);
   UPD_EXIT_GET(error,udev,plist);

/** Export the version */
   if(upd_version) { /* Version-Export enabled */
      udev->upd_version.data       =3D (const byte *) rcsid;
      udev->upd_version.size       =3D strlen(rcsid);
      udev->upd_version.persistent =3D true;
      error =3D =
param_write_string(plist,upd_version,&udev->upd_version);
      UPD_EXIT_GET(error,udev,plist);
   }                 /* Version-Export enabled */

/** Export the Named choices */
   for(i =3D 0; i < countof(upd_choice); ++i) {
      if(!upd_choice[i]) continue; /* Choice-Export disabled */
      if(upd && upd->choice && upd->choice[i]) {
         gs_param_string name;
         name.data       =3D (const byte *) =
upd_choice[i][upd->choice[i]];
         name.size       =3D strlen((const char *) name.data);
         name.persistent =3D true;
         error =3D param_write_name(plist,upd_choice[i][0],&name);
      } else {
         error =3D param_write_null(plist,upd_choice[i][0]);
      }
      UPD_EXIT_GET(error,udev,plist);
   }

/** Export the flags (bool) */
   for(i =3D 0; i < countof(upd_flags); ++i) {
      if(!upd_flags[i]) continue; /* Flag-Export disabled */
      if(upd) {
         bool value =3D upd->flags & ((uint32) 1 << i);
         error =3D param_write_bool(plist,upd_flags[i],&value);
      } else {
         error =3D param_write_null(plist,upd_flags[i]);
      }
      UPD_EXIT_GET(error,udev,plist);
   }

/** Export the ints */
   for(i =3D 0; i < countof(upd_ints); ++i) {
      if(!upd_ints[i]) continue; /* int-Export disabled */
      if(upd && upd->ints && upd->ints[i]) {
         int value =3D upd->ints[i];
         error =3D param_write_int( plist,upd_ints[i],&value);
      } else {
         error =3D param_write_null(plist,upd_ints[i]);
      }
      UPD_EXIT_GET(error,udev,plist);
   }

/** Export the int-arrays */
   for(i =3D 0; i < countof(upd_int_a); ++i) {
      if(!upd_int_a[i]) continue; /* int-Array-Export disabled */
      if(upd && upd->int_a && upd->int_a[i].size) {
         error =3D param_write_int_array( =
plist,upd_int_a[i],(upd->int_a+i));
      } else {
         error =3D param_write_null(plist,upd_int_a[i]);
      }
      UPD_EXIT_GET(error,udev,plist);
   }

/** Export the strings */
   for(i =3D 0; i < countof(upd_strings); ++i) {
      if(!upd_strings[i]) continue; /* String-Export disabled */
      if(upd && upd->strings && upd->strings[i].size) {
         error =3D param_write_string( =
plist,upd_strings[i],(upd->strings+i));
      } else {
         error =3D param_write_null(plist,upd_strings[i]);
      }
      UPD_EXIT_GET(error,udev,plist);
   }

/** Export the string-Arrays */
   for(i =3D 0; i < countof(upd_string_a); ++i) {
      if(!upd_string_a[i]) continue; /* String-Array-Export disabled */
      if(upd && upd->string_a && upd->string_a[i].size) {
         error =3D
            param_write_string_array( =
plist,upd_string_a[i],(upd->string_a+i));
      } else {
         error =3D param_write_null(plist,upd_string_a[i]);
      }
      UPD_EXIT_GET(error,udev,plist);
   }

/** Export the float-Arrays */
   for(i =3D 0; i < countof(upd_float_a); ++i) {
      if(!upd_float_a[i]) continue; /* Float-Array-Export disabled */
      if(upd && upd->float_a && upd->float_a[i].size) {
         error =3D
            param_write_float_array( =
plist,upd_float_a[i],(upd->float_a+i));
      } else {
         error =3D param_write_null(plist,upd_float_a[i]);
      }
      UPD_EXIT_GET(error,udev,plist);
   }

#if UPD_MESSAGES & UPD_M_TOPCALLS
   errprintf("RETURN: %d =3D upd_get_params(0x%05lx,0x%05lx)\n",
       error,(long) udev,(long) plist);
#endif

   return error;
}

#undef UPD_EXIT_GET

/* ------------------------------------------------------------------- =
*/
/* upd_put_params: Load Parameters into the device-structure           =
*/
/* ------------------------------------------------------------------- =
*/

private int
upd_put_params(gx_device *pdev, gs_param_list *plist)
{
   upd_device *const      udev       =3D (upd_device *) pdev;
   upd_p                  upd        =3D udev->upd;
   int                    error      =3D 0, code,i;

   float                  MarginsHWResolution[2],Margins[2];
   gx_device_color_info   color_info;
   uint32                 flags      =3D 0;
   int                   *choice     =3D NULL;
   int                   *ints       =3D NULL;
   gs_param_int_array    *int_a      =3D NULL;
   gs_param_string       *strings    =3D NULL;
   gs_param_string_array *string_a   =3D NULL;
   gs_param_float_array  *float_a    =3D NULL, mfa;

/**
Error is used for two purposes: either it holds a negative error
code or it is used as a bitfield, that tells, which parameters
were actually loaded.  If any of the important parameters changed
upd_put_params closes the device, since the real parameter-evaluation
is carried out by upd_open.
*/

#define UPD_PUT_FLAGS       0x0002
#define UPD_PUT_CHOICE      0x0004
#define UPD_PUT_INTS        0x0008
#define UPD_PUT_INT_A       0x0010
#define UPD_PUT_STRINGS     0x0020
#define UPD_PUT_STRING_A    0x0040
#define UPD_PUT_FLOAT_A     0x0080
#define UPD_PUT_CHANGEDSIZE 0x0100

#if UPD_MESSAGES & UPD_M_TOPCALLS
      errprintf("CALL: upd_put_params(0x%05lx,0x%05lx)\n",
         (long)udev,(long)plist);
#endif


/**
I consider the following part of upd_put_params a bad-nasty-hack-hack
and i am uncertain, wether it really works in the intended way. I =
provide it
just for the case someone is performing nasty-parameter-changes on the
active device, especially switching the OutputFile. If this happens in
a situation, where data were written to the file, but the termination
sequence is required, the driver does it now. (If you want to know, why
i am writing bad-nasty-hack-hack, visit http://www.zark.com )
*/
   if(upd && (B_OPEN & udev->upd->flags) && (NULL !=3D udev->file)) {

      gs_param_string fname =3D { NULL, 0, false };

      code =3D param_read_string(plist,"OutputFile",&fname);
      if((1 !=3D code) && (0 !=3D code)) {
         code =3D param_read_null(plist,"OutputFile");
         if(0 =3D=3D code) {
            fname.data =3D (const byte *) "";
            fname.size =3D 0;
         }
      }

      if((0 =3D=3D code) &&=20
         strncmp((const char *)fname.data,udev->fname,fname.size)) {
         if(upd->strings && 0 < udev->upd->strings[S_CLOSE].size)
            fwrite(upd->strings[S_CLOSE].data,1,
                   upd->strings[S_CLOSE].size,udev->file);

         upd->flags &=3D ~B_OPEN;
      }
   }
/* Done with the bad-nasty-hack-hack */

/**
The next thing "upd_put_params" does, is a little strange too. It =
imports
a readonly-parameter, the version-string. I do not know wether it is =
still
required, but some versions of GHOSTSCRIPT disliked it very much, if an
existing parameter was not touched by the put-operation.

On the other hand it is the right time to show the basic-outline of the
parameter-importing flow. Basically the proper "param_read"-procedure
is called. If it indicated, that the parameter was present, but of the
wrong type, a read for the null-type is attempted, which is by =
convention
somehow an reset to default. This sequence is applied to all the =
parameters
and in case of the array-parameters, a succesful null-read is marked by
setting data and size to 0.
*/
#if UPD_MESSAGES & UPD_M_SETUP
#define UPD_PARAM_READ(Param_read,Name,Object)       \
   code =3D Param_read(plist,Name,&Object);            \
   if(0 > code) {                                    \
      code =3D param_read_null(plist,Name);            \
      if(0 =3D=3D code) memset(&Object,0,sizeof(Object));\
   }                                                 \
   if(!code) errprintf(                         \
      "upd_put_params: retrieved parameter \"%s\"\n",\
      Name);                                         \
   if(0 > code) {                                    \
      param_signal_error(plist,Name,code);           \
      if(error > code) error =3D code;                 \
   }
#else
#define UPD_PARAM_READ(Param_read,Name,Object)       \
   code =3D Param_read(plist,Name,&Object);            \
   if(0 > code) {                                    \
      code =3D param_read_null(plist,Name);            \
      if(0 =3D=3D code) memset(&Object,0,sizeof(Object));\
   }                                                 \
   if(0 > code) {                                    \
      param_signal_error(plist,Name,code);           \
      if(error > code) error =3D code;                 \
   }
#endif

   UPD_PARAM_READ(param_read_string,upd_version,udev->upd_version)


/**
upd_put_params begins it's normal work by creating a copy, of
the data, that it might change, except for color_info that might
be changed in the device-structure, all manipulations are carried
out on this copies.
*/
   MarginsHWResolution[0] =3D udev->MarginsHWResolution[0];
   MarginsHWResolution[1] =3D udev->MarginsHWResolution[1];
               Margins[0] =3D udev->Margins[0];
               Margins[1] =3D udev->Margins[1];

   color_info =3D udev->color_info;
   if(upd) {
     flags =3D upd->flags;
     UPD_MM_CPY_ARRAY(choice,  upd->choice,  countof(upd_choice),
        UPD_MM_CPY_VALUE);
     UPD_MM_CPY_ARRAY(ints,    upd->ints,    countof(upd_ints),
        UPD_MM_CPY_VALUE);
     UPD_MM_CPY_ARRAY(int_a,   upd->int_a,   countof(upd_int_a),
        UPD_MM_CPY_PARAM);
     UPD_MM_CPY_ARRAY(strings, upd->strings, countof(upd_strings),
        UPD_MM_CPY_PARAM);
     UPD_MM_CPY_ARRAY(string_a,upd->string_a,countof(upd_string_a),
        UPD_MM_CPY_APARAM);
     UPD_MM_CPY_ARRAY(float_a, upd->float_a, countof(upd_float_a),
        UPD_MM_CPY_PARAM);
   } else {
     flags =3D 0;
     UPD_MM_GET_ARRAY(choice,  countof(upd_choice));
     UPD_MM_GET_ARRAY(ints,    countof(upd_ints));
     UPD_MM_GET_ARRAY(int_a,   countof(upd_int_a));
     UPD_MM_GET_ARRAY(strings, countof(upd_strings));
     UPD_MM_GET_ARRAY(string_a,countof(upd_string_a));
     UPD_MM_GET_ARRAY(float_a, countof(upd_float_a));
   }

/** Import the Multiple-Choices */
   for(i =3D 0; countof(upd_choice) > i; ++i) {
      gs_param_string value =3D { NULL, 0, false};
      if(!upd_choice[i][0]) continue;
      UPD_PARAM_READ(param_read_name,upd_choice[i][0],value);
      if(0 =3D=3D code) {
         if(0 <=3D error) error |=3D UPD_PUT_CHOICE;
         choice[i] =3D 0;
         if(0 < value.size) {
            int j;
            for(j =3D 1; upd_choice[i][j]; ++j) {
               if((strlen(upd_choice[i][j]) =3D=3D value.size) &&
                  (0 =3D=3D strncmp(upd_choice[i][j],
                            (const char *) value.data,value.size))) {
                  choice[i] =3D j;
                  break;
               }
            }
         }
      }
   }

/** Import the Boolean Values */
   for(i =3D 0; countof(upd_flags) > i; ++i) {
      uint32 bit  =3D (uint32) 1 << i;
      bool   flag =3D flags & bit ? true : false;
      if(!upd_flags[i]) continue;
      UPD_PARAM_READ(param_read_bool,upd_flags[i],flag);
      if(0 =3D=3D code) {
         if(0 <=3D error) error |=3D UPD_PUT_FLAGS;
         if(flag) flags |=3D  bit;
         else     flags &=3D ~bit;
      }
   }

/** Import the Integer Values */
   for(i =3D 0; countof(upd_ints) > i; ++i) {
      int value =3D ints[i];
      if(!upd_ints[i]) continue;
      UPD_PARAM_READ(param_read_int,upd_ints[i],value);
      if(0 =3D=3D code) {
         if(0 <=3D error) error |=3D UPD_PUT_INTS;
         ints[i] =3D value;
      }
   }

/** Import the Integer Arrays */
   for(i =3D 0; countof(upd_int_a) > i; ++i) {
      gs_param_int_array value =3D int_a[i];
      if(!upd_int_a[i]) continue;
      UPD_PARAM_READ(param_read_int_array,upd_int_a[i],value);
      if(0 =3D=3D code) {
         if(0 <=3D error) error |=3D UPD_PUT_INT_A;
         UPD_MM_DEL_PARAM(int_a[i]);
         if(!value.size) {
            value.data =3D NULL;
            int_a[i]   =3D value;
         } else {
            UPD_MM_CPY_PARAM(int_a[i],value);
         }
      }
   }

/** Import the Strings */
   for(i =3D 0; countof(upd_strings) > i; ++i) {
      gs_param_string value =3D strings[i];
      if(!upd_strings[i]) continue;
      UPD_PARAM_READ(param_read_string,upd_strings[i],value);
      if(0 =3D=3D code) {
         if(0 <=3D error) error |=3D UPD_PUT_STRINGS;
         UPD_MM_DEL_PARAM(strings[i]);
         if(!value.size) {
            value.data =3D NULL;
            strings[i]   =3D value;
         } else {
            UPD_MM_CPY_PARAM(strings[i],value);
         }
      }
   }

/** Import the String Arrays */
   for(i =3D 0; countof(upd_string_a) > i; ++i) {
      gs_param_string_array value =3D string_a[i];
      if(!upd_string_a[i]) continue;
      UPD_PARAM_READ(param_read_string_array,upd_string_a[i],value);
      if(0 =3D=3D code) {
         if(0 <=3D error) error |=3D UPD_PUT_STRING_A;
         UPD_MM_DEL_APARAM(string_a[i]);
         if(!value.size) {
            value.data  =3D NULL;
            string_a[i] =3D value;
         } else {
            UPD_MM_CPY_APARAM(string_a[i],value);
         }
      }
   }

/** Import the Float Arrays */
   for(i =3D 0; countof(upd_float_a) > i; ++i) {
      gs_param_float_array value =3D float_a[i];
      if(!upd_float_a[i]) continue;
      UPD_PARAM_READ(param_read_float_array,upd_float_a[i],value);
      if(0 =3D=3D code) {
         if(0 <=3D error) error |=3D UPD_PUT_FLOAT_A;
         UPD_MM_DEL_PARAM(float_a[i]);
         if(!value.size) {
            value.data =3D NULL;
            float_a[i] =3D value;
         } else {
            UPD_MM_CPY_PARAM(float_a[i],value);
         }
      }
   }

/**
Prior to the call to the superclass-put_params, the memory-layout and
the color-model needs adjustment. This is performed here, if any =
parameters
were set.
In addition to that, Resolution & Margin-Parameters are tested & =
adjusted.
*/
   if(0 < error) {

      int *ip,*ip2,ncomp,nbits;

      if(6 > int_a[IA_COLOR_INFO].size) {
         UPD_MM_DEL_PARAM(int_a[IA_COLOR_INFO]);
         UPD_MM_GET_ARRAY(int_a[IA_COLOR_INFO].data,6);
         int_a[IA_COLOR_INFO].size =3D 6;
      }
      ip =3D (int *) upd_cast(int_a[IA_COLOR_INFO].data);

      if(0 =3D=3D ip[0]) { /* Try to obtain num_components */
         switch(choice[C_MAPPER]) {
            case MAP_GRAY:     ip[0] =3D 1; break;
            case MAP_RGBW:     ip[0] =3D 3; break;
            case MAP_RGB:      ip[0] =3D 3; break;
            case MAP_CMYK:     ip[0] =3D 4; break;
            case MAP_CMYKGEN:  ip[0] =3D 4; break;
            case MAP_RGBOV:    ip[0] =3D 3; break;
            case MAP_RGBNOV:   ip[0] =3D 3; break;
            default:           ip[0] =3D color_info.num_components; =
break;
         }
      }                /* Try to obtain num_components */

      switch(choice[C_MAPPER]) {
         case MAP_GRAY:     ncomp =3D 1; break;
         case MAP_RGBW:     ncomp =3D 4; break;
         case MAP_RGB:      ncomp =3D 3; break;
         case MAP_CMYK:     ncomp =3D 4; break;
         case MAP_CMYKGEN:  ncomp =3D 4; break;
         case MAP_RGBOV:    ncomp =3D 4; break;
         case MAP_RGBNOV:   ncomp =3D 4; break;
         default:           ncomp =3D ip[0]; break;
      }
      if(UPD_CMAP_MAX < ncomp) ncomp =3D UPD_CMAP_MAX;

      if(ncomp > int_a[IA_COMPBITS].size) { /* Default ComponentBits */
         UPD_MM_GET_ARRAY(ip2,ncomp);
         nbits =3D 32 / ncomp;
         if(8 < nbits) nbits =3D 8;
         for(i =3D 0; i < ncomp; ++i) ip2[i] =3D nbits;
         UPD_MM_DEL_PARAM(int_a[IA_COMPBITS]);
         int_a[IA_COMPBITS].data =3D ip2;
         int_a[IA_COMPBITS].size =3D ncomp;
      }                                     /* Default ComponentBits */

      if(ncomp > int_a[IA_COMPSHIFT].size) {  /* Default ComponentShift =
*/
         nbits =3D 0;
         for(i =3D 0; i < ncomp; ++i) nbits +=3D =
int_a[IA_COMPBITS].data[i];
         UPD_MM_GET_ARRAY(ip2,ncomp);
         for(i =3D 0; i < ncomp; ++i) {
            ip2[i] =3D nbits - int_a[IA_COMPBITS].data[i];
            nbits -=3D int_a[IA_COMPBITS].data[i];
         }
         UPD_MM_DEL_PARAM(int_a[IA_COMPSHIFT]);
         int_a[IA_COMPSHIFT].data =3D ip2;
         int_a[IA_COMPSHIFT].size =3D ncomp;
      }                                       /* Default ComponentShift =
*/

      if(0 =3D=3D ip[1]) { /* Try to compute the depth */
         nbits =3D 0;
         for(i =3D 0; i < ncomp; ++i) {
            if(nbits < (int_a[IA_COMPBITS].data[i] +
                        int_a[IA_COMPSHIFT].data[i]))
               nbits =3D  int_a[IA_COMPBITS].data[i] +
                        int_a[IA_COMPSHIFT].data[i];
         }
         if(      1 >=3D nbits) nbits =3D  1;
         else if( 2 >=3D nbits) nbits =3D  2;
         else if( 4 >=3D nbits) nbits =3D  4;
         else if( 8 >=3D nbits) nbits =3D  8;
         else if(16 >=3D nbits) nbits =3D 16;
         else if(24 >=3D nbits) nbits =3D 24;
         else                 nbits =3D 32;

         ip[1] =3D nbits;

      }                /* Try to compute the depth */

      if(0 =3D=3D ip[2]) { /* Number of Gray-Levels */
         nbits =3D 0;
         for(i =3D 0; i < ncomp; ++i) if(nbits < =
int_a[IA_COMPBITS].data[i])
            nbits =3D int_a[IA_COMPBITS].data[i];
         if(nbits > 8) nbits =3D 8;
         ip[2] =3D (1 << nbits) - 1;
      }                /* Number of Gray-Levels */

      if(0 =3D=3D ip[3] && 1 < ip[0]) { /* Number of Colors */
         nbits =3D 0;
         for(i =3D 0; i < ip[0]; ++i) nbits +=3D =
int_a[IA_COMPBITS].data[i];
         if(nbits > 8) nbits =3D 8;
         ip[3] =3D (1 << nbits) - 1;
      }                             /* Number of Colors */

      if(0 =3D=3D ip[4]) { /* Gray-Ramp */
         nbits =3D 0;
         for(i =3D 0; i < ncomp; ++i) if(nbits < =
int_a[IA_COMPBITS].data[i])
            nbits =3D int_a[IA_COMPBITS].data[i];
         if(2 < nbits) ip[4] =3D 5;
         else          ip[4] =3D 2;
      }                /* Gray-Ramp */

      if(0 =3D=3D ip[5] && 1 < ip[0]) { /* Color-Ramp */
         nbits =3D 0;
         for(i =3D 0; i < ncomp; ++i) if(nbits < =
int_a[IA_COMPBITS].data[i])
            nbits =3D int_a[IA_COMPBITS].data[i];
         if(2 < nbits) ip[5] =3D 5;
         else          ip[5] =3D 2;
      }                             /* Color-Ramp */

      udev->color_info.num_components =3D ip[0];
      udev->color_info.depth          =3D ip[1];
      udev->color_info.max_gray       =3D (gx_color_value) ip[2];
      udev->color_info.max_color      =3D (gx_color_value) ip[3];
      udev->color_info.dither_grays   =3D (gx_color_value) ip[4];
      udev->color_info.dither_colors  =3D (gx_color_value) ip[5];

/*
 * Now we're dealing with the Resolution- & Margin-Stuff
 * (This is close to be a bad-nasty-hack-hack)
 */
      if((0 =3D=3D param_read_float_array(plist,"HWResolution",&mfa)) &&
         (2 =3D=3D mfa.size) && (0 !=3D mfa.data)) {
         udev->MarginsHWResolution[0] =3D mfa.data[0];
         udev->MarginsHWResolution[1] =3D mfa.data[1];
      } else {
         udev->MarginsHWResolution[0] =3D udev->HWResolution[0];
         udev->MarginsHWResolution[1] =3D udev->HWResolution[1];
      }

      if((0 =3D=3D param_read_float_array(plist,".HWMargins",&mfa)) &&
         (4 =3D=3D mfa.size) && (0 !=3D mfa.data)) {
         udev->Margins[0] =3D -mfa.data[0] * =
udev->MarginsHWResolution[0] / 72.0;
         udev->Margins[1] =3D -mfa.data[3] * =
udev->MarginsHWResolution[1] / 72.0;
      }
   }                                       /* Change the color-Info */

/* Call the superclass-put_params now */
   code =3D gdev_prn_put_params((gx_device *)udev,plist);
   if(0 > code) error =3D code;

/**
If the superclass-"put_params" went o.k. too, then the new parameters =
are
transferred into the device-structure. In the case of "uniprint", this =
may

 1. Close the device, which might fail.
 2. Allocate new memory for the upd-specific structure, that might fail =
too.

*/
/* *HGS* recognize a changed device geometry */
   if( udev->upd &&                              /* HGS */
      ((udev->width  !=3D udev->upd->pdwidth) ||   /* HGS */
      (udev->height !=3D udev->upd->pdheight)  ))  /* HGS */
        error |=3D UPD_PUT_CHANGEDSIZE;            /* HGS */

   if(0 < error && udev->is_open) {
      code =3D gs_closedevice((gx_device *)udev);
      if(0 > code) error =3D code;
   }

   if(0 < error) { /* Actually something loaded without error */

      if(!(upd =3D udev->upd)) {
        UPD_MM_GET_ARRAY(udev->upd,1);
        upd =3D udev->upd;
      } else {
        UPD_MM_DEL_ARRAY(upd->choice,  countof(upd_choice),  =
UPD_MM_DEL_VALUE);
        UPD_MM_DEL_ARRAY(upd->ints,    countof(upd_ints),    =
UPD_MM_DEL_VALUE);
        UPD_MM_DEL_ARRAY(upd->int_a,   countof(upd_int_a),   =
UPD_MM_DEL_PARAM);
        UPD_MM_DEL_ARRAY(upd->strings, countof(upd_strings), =
UPD_MM_DEL_PARAM);
        =
UPD_MM_DEL_ARRAY(upd->string_a,countof(upd_string_a),UPD_MM_DEL_APARAM);
        UPD_MM_DEL_ARRAY(upd->float_a, countof(upd_float_a), =
UPD_MM_DEL_PARAM);
      }

      upd->choice   =3D choice;
      upd->flags    =3D flags;
      upd->ints     =3D ints;
      upd->int_a    =3D int_a;
      upd->strings  =3D strings;
      upd->string_a =3D string_a;
      upd->float_a  =3D float_a;

      if(0 < error) error =3D 0;

   } else {

                  udev->Margins[0] =3D             Margins[0];
                  udev->Margins[1] =3D             Margins[1];
      udev->MarginsHWResolution[0] =3D MarginsHWResolution[0];
      udev->MarginsHWResolution[1] =3D MarginsHWResolution[1];

      udev->color_info =3D color_info;
      UPD_MM_DEL_ARRAY(choice,  countof(upd_choice),  UPD_MM_DEL_VALUE);
      UPD_MM_DEL_ARRAY(ints,    countof(upd_ints),    UPD_MM_DEL_VALUE);
      UPD_MM_DEL_ARRAY(int_a,   countof(upd_int_a),   UPD_MM_DEL_PARAM);
      UPD_MM_DEL_ARRAY(strings, countof(upd_strings), UPD_MM_DEL_PARAM);
      =
UPD_MM_DEL_ARRAY(string_a,countof(upd_string_a),UPD_MM_DEL_APARAM);
      UPD_MM_DEL_ARRAY(float_a, countof(upd_float_a), UPD_MM_DEL_PARAM);

   }

/*
 * upd_put_params keeps the Procedures upd to date
 */

   upd_procs_map(udev);


#if UPD_MESSAGES & UPD_M_TOPCALLS
      errprintf("RETURN: %d =3D upd_put_params(0x%05lx,0x%05lx)\n",
         error,(long) udev, (long) plist);
#endif

   return error;
}

/* ------------------------------------------------------------------- =
*/
/* upd_cmyk_icolor: KCMY->KCMY-Index Mapping                           =
*/
/* ------------------------------------------------------------------- =
*/
/**
The next Routines, that follow, are the color-mapping routines.
GHOSTSCRIPT talks about "gx_color_values" and the driver has
to merge the 1, 3 or four values into up to 32 Bits, that means
it is necessary to do some truncation and shifting. For the truncation
"uniprint" uses the internal function "upd_truncate" and "upd_expand"
reverses this in the reverse-mapping procedures.
*/

private gx_color_index
upd_cmyk_icolor(gx_device *pdev, const gx_color_value cv[])
{
   const upd_p     upd =3D ((upd_device *)pdev)->upd;
   gx_color_index  rv;
   gx_color_value c, m, y, k;
   c =3D cv[0]; m =3D cv[1]; y =3D cv[2]; k =3D cv[3];

/**
All 4-Component-Modi have to deal with the Problem, that a value
with all bits set can be produced, which is treated as an error-return
from the mapping-functions. But with RGBW or KCMY, there is a neat
trick: Grayscale are transferred as RGB/CMY=3D0 and holding Data only
in the W- or K-Component.
*/

   if((c =3D=3D m) && (m =3D=3D y)) {

      rv =3D upd_truncate(upd,0,(gx_color_value)(c > k ? c : k));

   } else {

      rv  =3D upd_truncate(upd,0,k) | upd_truncate(upd,1,c)
          | upd_truncate(upd,2,m) | upd_truncate(upd,3,y);


/* It might still become a "gx_no_color_value" due to truncation, thus: =
*/

      if(rv =3D=3D gx_no_color_index) rv ^=3D 1;
   }


#if UPD_MESSAGES & UPD_M_MAPCALLS
  errprintf(
"cmyk_icolor: (%5.1f,%5.1f,%5.1f,%5.1f) : (%5.1f,%5.1f,%5.1f,%5.1f) : =
0x%0*lx\n",
   255.0 * (double) c / (double) gx_max_color_value,
   255.0 * (double) m / (double) gx_max_color_value,
   255.0 * (double) y / (double) gx_max_color_value,
   255.0 * (double) k / (double) gx_max_color_value,
   255.0 * (double) ((rv >> upd->cmap[1].bitshf) & upd->cmap[1].bitmsk)
                 / (double) upd->cmap[1].bitmsk,
   255.0 * (double) ((rv >> upd->cmap[2].bitshf) & upd->cmap[2].bitmsk)
                 / (double) upd->cmap[2].bitmsk,
   255.0 * (double) ((rv >> upd->cmap[3].bitshf) & upd->cmap[3].bitmsk)
                 / (double) upd->cmap[3].bitmsk,
   255.0 * (double) ((rv >> upd->cmap[0].bitshf) & upd->cmap[0].bitmsk)
                 / (double) upd->cmap[0].bitmsk,
   (pdev->color_info.depth + 3)>>2,rv);
#endif

   return rv;
}

/* ------------------------------------------------------------------- =
*/
/* upd_icolor_rgb: Stored KCMY back to a RGB                           =
*/
/* ------------------------------------------------------------------- =
*/

private int
upd_icolor_rgb(gx_device *pdev, gx_color_index color, gx_color_value =
prgb[3])
{
   const upd_p     upd =3D ((upd_device *)pdev)->upd;
   gx_color_value c,m,y,k;

/*
 * Expand to the Component-Values
 */
   k =3D upd_expand(upd,0,color);
   c =3D upd_expand(upd,1,color);
   m =3D upd_expand(upd,2,color);
   y =3D upd_expand(upd,3,color);

/*
 * Then Invert and subtract K from the colors
 */
   prgb[0] =3D gx_max_color_value - c;
   if(prgb[0] > k) prgb[0] -=3D k;
   else            prgb[0]  =3D 0;

   prgb[1] =3D gx_max_color_value - m;
   if(prgb[1] > k) prgb[1] -=3D k;
   else            prgb[1]  =3D 0;

   prgb[2] =3D gx_max_color_value - y;
   if(prgb[2] > k) prgb[2] -=3D k;
   else            prgb[2]  =3D 0;


#if UPD_MESSAGES & UPD_M_MAPCALLS
   errprintf(
    "icolor_rgb: 0x%0*lx -> (%5.1f,%5.1f,%5.1f,%5.1f) -> =
(%5.1f,%5.1f,%5.1f,%5.1f) -> (%5.1f,%5.1f,%5.1f)\n",
    (pdev->color_info.depth + 3)>>2,color,
    255.0 * (double) ((color >> upd->cmap[1].bitshf) & =
upd->cmap[1].bitmsk)
                     / (double) upd->cmap[1].bitmsk,
    255.0 * (double) ((color >> upd->cmap[2].bitshf) & =
upd->cmap[2].bitmsk)
                     / (double) upd->cmap[2].bitmsk,
    255.0 * (double) ((color >> upd->cmap[3].bitshf) & =
upd->cmap[3].bitmsk)
                     / (double) upd->cmap[3].bitmsk,
    255.0 * (double) ((color >> upd->cmap[0].bitshf) & =
upd->cmap[0].bitmsk)
                     / (double) upd->cmap[0].bitmsk,
    255.0 * (double)   c     / (double) gx_max_color_value,
    255.0 * (double)   m     / (double) gx_max_color_value,
    255.0 * (double)   y     / (double) gx_max_color_value,
    255.0 * (double)   k     / (double) gx_max_color_value,
    255.0 * (double) prgb[0] / (double) gx_max_color_value,
    255.0 * (double) prgb[1] / (double) gx_max_color_value,
    255.0 * (double) prgb[2] / (double) gx_max_color_value);
#endif

   return 0;
}

/* ------------------------------------------------------------------- =
*/
/* upd_rgb_1color: Grayscale->Grayscale-index-Mapping              */
/* ------------------------------------------------------------------- =
*/

private gx_color_index
upd_rgb_1color(gx_device *pdev, const gx_color_value cv[])
{
   const upd_p     upd =3D ((upd_device *)pdev)->upd;
   gx_color_index  rv;
   gx_color_value g;

   g =3D cv[0];
   rv =3D upd_truncate(upd,0,g);

#if UPD_MESSAGES & UPD_M_MAPCALLS
   errprintf(
      "rgb_1color: (%5.1f) : (%5.1f) : 0x%0*lx\n",
      255.0 * (double) g  / (double) gx_max_color_value,
      255.0 * (double) ((rv >> upd->cmap[0].bitshf) & =
upd->cmap[0].bitmsk)
                    / (double) upd->cmap[0].bitmsk,
      (pdev->color_info.depth + 3)>>2,rv);
#endif

   return rv;
}

/* ------------------------------------------------------------------- =
*/
/* upd_1color_rgb: reversal of the above                               =
*/
/* ------------------------------------------------------------------- =
*/

private int
upd_1color_rgb(gx_device *pdev, gx_color_index color, gx_color_value =
cv[1])
{
   const upd_p     upd =3D ((upd_device *)pdev)->upd;
/*
 * Actual task: expand to full range of gx_color_value
 */
   cv[0] =3D upd_expand(upd,0,color);

#if UPD_MESSAGES & UPD_M_MAPCALLS
   errprintf("1color_rgb: 0x%0*lx -> %5.1f -> (%5.1f,%5.1f,%5.1f)\n",
      (pdev->color_info.depth + 3)>>2,color,
      255.0 * (double) ((color >> upd->cmap[0].bitshf) & =
upd->cmap[0].bitmsk)
                       / (double) upd->cmap[0].bitmsk,
      255.0 * (double) prgb[0] / (double) gx_max_color_value,
      255.0 * (double) prgb[0] / (double) gx_max_color_value,
      255.0 * (double) prgb[0] / (double) gx_max_color_value);
#endif

   return 0;
}

/* ------------------------------------------------------------------- =
*/
/* upd_rgb_3color: component-wise RGB->RGB-Mapping                     =
*/
/* ------------------------------------------------------------------- =
*/

private gx_color_index
upd_rgb_3color(gx_device *pdev, const gx_color_value cv[])
{
   const upd_p     upd =3D ((upd_device *)pdev)->upd;
   gx_color_index  rv;
   gx_color_value r, g, b;
   r =3D cv[0]; g =3D cv[1]; b =3D cv[2];

   rv =3D upd_truncate(upd,0,r) | upd_truncate(upd,1,g) | =
upd_truncate(upd,2,b);
   if(rv =3D=3D gx_no_color_index) rv ^=3D 1;

#if UPD_MESSAGES & UPD_M_MAPCALLS
  errprintf(
   "rgb_3color: (%5.1f,%5.1f,%5.1f) : (%5.1f,%5.1f,%5.1f) : 0x%0*lx\n",
   255.0 * (double) r / (double) gx_max_color_value,
   255.0 * (double) g / (double) gx_max_color_value,
   255.0 * (double) b / (double) gx_max_color_value,
   255.0 * (double) ((rv >> upd->cmap[0].bitshf) & upd->cmap[0].bitmsk)
                 / (double) upd->cmap[0].bitmsk,
   255.0 * (double) ((rv >> upd->cmap[1].bitshf) & upd->cmap[1].bitmsk)
                 / (double) upd->cmap[1].bitmsk,
   255.0 * (double) ((rv >> upd->cmap[2].bitshf) & upd->cmap[2].bitmsk)
                 / (double) upd->cmap[2].bitmsk,
   (pdev->color_info.depth + 3)>>2,rv);
#endif

   return rv;
}

/* ------------------------------------------------------------------- =
*/
/* upd_3color_rgb: reversal of the above                               =
*/
/* ------------------------------------------------------------------- =
*/

private int
upd_3color_rgb(gx_device *pdev, gx_color_index color, gx_color_value =
prgb[3])
{
   const upd_p     upd =3D ((upd_device *)pdev)->upd;

   prgb[0] =3D upd_expand(upd,0,color);
   prgb[1] =3D upd_expand(upd,1,color);
   prgb[2] =3D upd_expand(upd,2,color);

#if UPD_MESSAGES & UPD_M_MAPCALLS
   errprintf(
     "3color_rgb: 0x%0*lx -> (%5.1f,%5.1f,%5.1f) -> =
(%5.1f,%5.1f,%5.1f)\n",
      (pdev->color_info.depth + 3)>>2,color,
      255.0 * (double) ((color >> upd->cmap[0].bitshf) & =
upd->cmap[0].bitmsk)
                       / (double) upd->cmap[0].bitmsk,
      255.0 * (double) ((color >> upd->cmap[1].bitshf) & =
upd->cmap[1].bitmsk)
                       / (double) upd->cmap[1].bitmsk,
      255.0 * (double) ((color >> upd->cmap[2].bitshf) & =
upd->cmap[2].bitmsk)
                       / (double) upd->cmap[2].bitmsk,

      255.0 * (double) prgb[0] / (double) gx_max_color_value,
      255.0 * (double) prgb[1] / (double) gx_max_color_value,
      255.0 * (double) prgb[2] / (double) gx_max_color_value);
#endif

   return 0;
}

/* ------------------------------------------------------------------- =
*/
/* upd_rgb_4color: Create an WRGB-Index from RGB                       =
*/
/* ------------------------------------------------------------------- =
*/

private gx_color_index
upd_rgb_4color(gx_device *pdev, const gx_color_value cv[])
{
   const upd_p     upd =3D ((upd_device *)pdev)->upd;
   gx_color_index  rv;
   gx_color_value r, g, b;

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

   if((r =3D=3D g) && (g =3D=3D b)) {

      rv =3D upd_truncate(upd,0,r);

   } else {

      gx_color_value w =3D g < r ? g : r; w =3D w < b ? w : b; /* =
Minimum */

      rv =3D upd_truncate(upd,0,w) | upd_truncate(upd,1,r) |
           upd_truncate(upd,2,g) | upd_truncate(upd,3,b);

/* It might still become a "gx_no_color_value" due to truncation, thus: =
*/

      if(rv =3D=3D gx_no_color_index) rv ^=3D 1;
   }

#if UPD_MESSAGES & UPD_M_MAPCALLS
  errprintf(
   "rgb_4color: (%5.1f,%5.1f,%5.1f) : (%5.1f,%5.1f,%5.1f,%5.1f) : =
0x%0*lx\n",
   255.0 * (double) r / (double) gx_max_color_value,
   255.0 * (double) g / (double) gx_max_color_value,
   255.0 * (double) b / (double) gx_max_color_value,
   255.0 * (double) ((rv >> upd->cmap[1].bitshf) & upd->cmap[1].bitmsk)
                 / (double) upd->cmap[1].bitmsk,
   255.0 * (double) ((rv >> upd->cmap[2].bitshf) & upd->cmap[2].bitmsk)
                 / (double) upd->cmap[2].bitmsk,
   255.0 * (double) ((rv >> upd->cmap[3].bitshf) & upd->cmap[3].bitmsk)
                 / (double) upd->cmap[3].bitmsk,
   255.0 * (double) ((rv >> upd->cmap[0].bitshf) & upd->cmap[0].bitmsk)
                 / (double) upd->cmap[0].bitmsk,
   (pdev->color_info.depth + 3)>>2,rv);
#endif

   return rv;
}

/* ------------------------------------------------------------------- =
*/
/* upd_4color_rgb: Stored WRGB-Index back to a RGB                     =
*/
/* ------------------------------------------------------------------- =
*/

private int
upd_4color_rgb(gx_device *pdev, gx_color_index color, gx_color_value =
prgb[3])
{
   const upd_p     upd =3D ((upd_device *)pdev)->upd;

/*
 * Expand to the Component-Values
 */
   prgb[0] =3D upd_expand(upd,1,color);
   prgb[1] =3D upd_expand(upd,2,color);
   prgb[2] =3D upd_expand(upd,3,color);

/* Revert our Grayscale-Trick: */
   if(!(prgb[0] || prgb[1] || prgb[2]))
      prgb[0] =3D prgb[1] =3D prgb[2] =3D upd_expand(upd,0,color);


#if UPD_MESSAGES & UPD_M_MAPCALLS
   errprintf(
    "4color_rgb: 0x%0*lx -> (%5.1f,%5.1f,%5.1f,%5.1f) -> =
(%5.1f,%5.1f,%5.1f)\n",
    (pdev->color_info.depth + 3)>>2,color,
    255.0 * (double) ((color >> upd->cmap[1].bitshf) & =
upd->cmap[1].bitmsk)
                     / (double) upd->cmap[1].bitmsk,
    255.0 * (double) ((color >> upd->cmap[2].bitshf) & =
upd->cmap[2].bitmsk)
                     / (double) upd->cmap[2].bitmsk,
    255.0 * (double) ((color >> upd->cmap[3].bitshf) & =
upd->cmap[3].bitmsk)
                     / (double) upd->cmap[3].bitmsk,
    255.0 * (double) ((color >> upd->cmap[0].bitshf) & =
upd->cmap[0].bitmsk)
                     / (double) upd->cmap[0].bitmsk,
    255.0 * (double) prgb[0] / (double) gx_max_color_value,
    255.0 * (double) prgb[1] / (double) gx_max_color_value,
    255.0 * (double) prgb[2] / (double) gx_max_color_value);
#endif

   return 0;
}

/* ------------------------------------------------------------------- =
*/
/* upd_cmyk_kcolor: KCMY->KCMY-Index Mapping with Black Generation     =
*/
/* ------------------------------------------------------------------- =
*/

private gx_color_index
upd_cmyk_kcolor(gx_device *pdev, const gx_color_value cv[])
{
   const upd_p     upd =3D ((upd_device *)pdev)->upd;
   gx_color_index  rv;
   gx_color_value  black;

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

   if((c =3D=3D m) && (m =3D=3D y)) {

      black =3D c > k ? c : k;
      rv =3D upd_truncate(upd,0,black);

   } else {

      if(k && !(c | m | y)) {
         black =3D k;
      } else {
         black =3D c     < m ? c     : m;
         black =3D black < y ? black : y;
      }

      rv  =3D upd_truncate(upd,0,black) | upd_truncate(upd,1,c)
          | upd_truncate(upd,2,m)     | upd_truncate(upd,3,y);

/* It might still become a "gx_no_color_value" due to truncation, thus: =
*/

      if(rv =3D=3D gx_no_color_index) rv ^=3D 1;
   }


#if UPD_MESSAGES & UPD_M_MAPCALLS
  errprintf(
"cmyk_kcolor: (%5.1f,%5.1f,%5.1f,%5.1f) : (%5.1f,%5.1f,%5.1f,%5.1f) : =
0x%0*lx\n",
   255.0 * (double) c / (double) gx_max_color_value,
   255.0 * (double) m / (double) gx_max_color_value,
   255.0 * (double) y / (double) gx_max_color_value,
   255.0 * (double) k / (double) gx_max_color_value,
   255.0 * (double) ((rv >> upd->cmap[1].bitshf) & upd->cmap[1].bitmsk)
                 / (double) upd->cmap[1].bitmsk,
   255.0 * (double) ((rv >> upd->cmap[2].bitshf) & upd->cmap[2].bitmsk)
                 / (double) upd->cmap[2].bitmsk,
   255.0 * (double) ((rv >> upd->cmap[3].bitshf) & upd->cmap[3].bitmsk)
                 / (double) upd->cmap[3].bitmsk,
   255.0 * (double) ((rv >> upd->cmap[0].bitshf) & upd->cmap[0].bitmsk)
                 / (double) upd->cmap[0].bitmsk,
   (pdev->color_info.depth + 3)>>2,rv);
#endif

   return rv;
}

/* ------------------------------------------------------------------- =
*/
/* upd_kcolor_rgb: Stored CMY+generated K back to a RGB                =
*/
/* ------------------------------------------------------------------- =
*/

private int
upd_kcolor_rgb(gx_device *pdev, gx_color_index color, gx_color_value =
prgb[3])
{
   const upd_p     upd =3D ((upd_device *)pdev)->upd;
   gx_color_value c,m,y,k;

/*
 * Expand to the Component-Values
 */
   k =3D upd_expand(upd,0,color);
   c =3D upd_expand(upd,1,color);
   m =3D upd_expand(upd,2,color);
   y =3D upd_expand(upd,3,color);

/*
 * Check for plain Gray-Values
 */
   if(!(c | m | y )) {

      prgb[2] =3D prgb[1] =3D prgb[0] =3D gx_max_color_value - k;

   } else {
      prgb[0] =3D gx_max_color_value - c;
      prgb[1] =3D gx_max_color_value - m;
      prgb[2] =3D gx_max_color_value - y;
   }

#if UPD_MESSAGES & UPD_M_MAPCALLS
   errprintf(
    "kcolor_rgb: 0x%0*lx -> (%5.1f,%5.1f,%5.1f,%5.1f) -> =
(%5.1f,%5.1f,%5.1f,%5.1f) -> (%5.1f,%5.1f,%5.1f)\n",
    (pdev->color_info.depth + 3)>>2,color,
    255.0 * (double) ((color >> upd->cmap[1].bitshf) & =
upd->cmap[1].bitmsk)
                     / (double) upd->cmap[1].bitmsk,
    255.0 * (double) ((color >> upd->cmap[2].bitshf) & =
upd->cmap[2].bitmsk)
                     / (double) upd->cmap[2].bitmsk,
    255.0 * (double) ((color >> upd->cmap[3].bitshf) & =
upd->cmap[3].bitmsk)
                     / (double) upd->cmap[3].bitmsk,
    255.0 * (double) ((color >> upd->cmap[0].bitshf) & =
upd->cmap[0].bitmsk)
                     / (double) upd->cmap[0].bitmsk,
    255.0 * (double)   c     / (double) gx_max_color_value,
    255.0 * (double)   m     / (double) gx_max_color_value,
    255.0 * (double)   y     / (double) gx_max_color_value,
    255.0 * (double)   k     / (double) gx_max_color_value,
    255.0 * (double) prgb[0] / (double) gx_max_color_value,
    255.0 * (double) prgb[1] / (double) gx_max_color_value,
    255.0 * (double) prgb[2] / (double) gx_max_color_value);
#endif

   return 0;
}

/* ------------------------------------------------------------------- =
*/
/* upd_rgb_ovcolor: Create an KCMY-Index from RGB                      =
*/
/* ------------------------------------------------------------------- =
*/

private gx_color_index
upd_rgb_ovcolor(gx_device *pdev, const gx_color_value cv[])
{
   const upd_p     upd =3D ((upd_device *)pdev)->upd;
   gx_color_index  rv;
   gx_color_value  c,m,y,black;
   gx_color_value r, g, b;
   r =3D cv[0]; g =3D cv[1]; b =3D cv[2];
   if((r =3D=3D g) && (g =3D=3D b)) {

      black  =3D gx_max_color_value - r;
      rv     =3D upd_truncate(upd,0,black);
      c =3D m =3D y =3D 0;

   } else {

      c =3D gx_max_color_value - r;
      m =3D gx_max_color_value - g;
      y =3D gx_max_color_value - b;

      black =3D c     < m ? c     : m;=20
      black =3D black < y ? black : y;

      if(black !=3D gx_max_color_value) {
        float tmp,d;
       =20
        d   =3D (float)(gx_max_color_value - black);

        tmp =3D (float) (c-black) / d;
        if(      0.0 > tmp) tmp =3D 0.0;
        else if( 1.0 < tmp) tmp =3D 1.0;
        c   =3D (gx_color_value)(tmp * gx_max_color_value + 0.499);

        tmp =3D (float) (m-black) / d;
        if(      0.0 > tmp) tmp =3D 0.0;
        else if( 1.0 < tmp) tmp =3D 1.0;
        m   =3D (gx_color_value)(tmp * gx_max_color_value + 0.499);

        tmp =3D (float) (y-black) / d;
        if(      0.0 > tmp) tmp =3D 0.0;
        else if( 1.0 < tmp) tmp =3D 1.0;
        y   =3D (gx_color_value)(tmp * gx_max_color_value + 0.499);

      } else {

        c =3D m =3D y =3D gx_max_color_value;

      }

      rv =3D upd_truncate(upd,0,black) | upd_truncate(upd,1,c) |
           upd_truncate(upd,2,m)     | upd_truncate(upd,3,y);

/* It might still become a "gx_no_color_value" due to truncation, thus: =
*/

      if(rv =3D=3D gx_no_color_index) rv ^=3D 1;
   }

#if UPD_MESSAGES & UPD_M_MAPCALLS
  errprintf(
   "rgb_ovcolor: (%5.1f,%5.1f,%5.1f) : (%5.1f,%5.1f,%5.1f,%5.1f) : =
0x%0*lx\n",
   255.0 * (double) r / (double) gx_max_color_value,
   255.0 * (double) g / (double) gx_max_color_value,
   255.0 * (double) b / (double) gx_max_color_value,
   255.0 * (double) ((rv >> upd->cmap[1].bitshf) & upd->cmap[1].bitmsk)
                 / (double) upd->cmap[1].bitmsk,
   255.0 * (double) ((rv >> upd->cmap[2].bitshf) & upd->cmap[2].bitmsk)
                 / (double) upd->cmap[2].bitmsk,
   255.0 * (double) ((rv >> upd->cmap[3].bitshf) & upd->cmap[3].bitmsk)
                 / (double) upd->cmap[3].bitmsk,
   255.0 * (double) ((rv >> upd->cmap[0].bitshf) & upd->cmap[0].bitmsk)
                 / (double) upd->cmap[0].bitmsk,
   (pdev->color_info.depth + 3)>>2,rv);
#endif

   return rv;
}

/* ------------------------------------------------------------------- =
*/
/* upd_rgb_novcolor: Create an KCMY-Index from RGB                      =
*/
/* ------------------------------------------------------------------- =
*/

private gx_color_index
upd_rgb_novcolor(gx_device *pdev, const gx_color_value cv[])
{
   const upd_p     upd =3D ((upd_device *)pdev)->upd;
   gx_color_index  rv;
   gx_color_value  c,m,y,black;
   gx_color_value r, g, b;
   r =3D cv[0]; g =3D cv[1]; b =3D cv[2];

   if((r =3D=3D g) && (g =3D=3D b)) {

      black  =3D gx_max_color_value - r;
      rv     =3D upd_truncate(upd,0,black);
      c =3D m =3D y =3D 0;

   } else {

      c =3D gx_max_color_value - r;
      m =3D gx_max_color_value - g;
      y =3D gx_max_color_value - b;

      black =3D c     < m ? c     : m;=20
      black =3D black < y ? black : y;
      c     =3D c - black;
      m     =3D m - black;
      y     =3D y - black;

      rv =3D upd_truncate(upd,0,black) | upd_truncate(upd,1,c) |
           upd_truncate(upd,2,m)     | upd_truncate(upd,3,y);

/* It might still become a "gx_no_color_value" due to truncation, thus: =
*/

      if(rv =3D=3D gx_no_color_index) rv ^=3D 1;
   }

#if UPD_MESSAGES & UPD_M_MAPCALLS
  errprintf(
   "rgb_ovcolor: (%5.1f,%5.1f,%5.1f) : (%5.1f,%5.1f,%5.1f,%5.1f) : =
0x%0*lx\n",
   255.0 * (double) r / (double) gx_max_color_value,
   255.0 * (double) g / (double) gx_max_color_value,
   255.0 * (double) b / (double) gx_max_color_value,
   255.0 * (double) ((rv >> upd->cmap[1].bitshf) & upd->cmap[1].bitmsk)
                 / (double) upd->cmap[1].bitmsk,
   255.0 * (double) ((rv >> upd->cmap[2].bitshf) & upd->cmap[2].bitmsk)
                 / (double) upd->cmap[2].bitmsk,
   255.0 * (double) ((rv >> upd->cmap[3].bitshf) & upd->cmap[3].bitmsk)
                 / (double) upd->cmap[3].bitmsk,
   255.0 * (double) ((rv >> upd->cmap[0].bitshf) & upd->cmap[0].bitmsk)
                 / (double) upd->cmap[0].bitmsk,
   (pdev->color_info.depth + 3)>>2,rv);
#endif

   return rv;
}

/* ------------------------------------------------------------------- =
*/
/* NOTE: Beyond this point only "uniprint"-special-items.              =
*/
/* ------------------------------------------------------------------- =
*/

/* ------------------------------------------------------------------- =
*/
/* Return the gx_color_value for a given component                     =
*/
/* ------------------------------------------------------------------- =
*/

private gx_color_value
upd_expand(upd_pc upd,int i,uint32 ci)
{
   const updcmap_pc cmap =3D upd->cmap + i;    /* Writing-Shortcut */

   ci =3D (ci >> cmap->bitshf) & cmap->bitmsk; /* Extract the component =
*/
   if(!cmap->rise) ci =3D cmap->bitmsk - ci;   /* Invert, if necessary =
*/
/* no Truncation/Expansion on full range */
   if(gx_color_value_bits > cmap->bits) return cmap->code[ci];
   else                                 return (gx_color_value) ci;
}
/* That's simple, isn't it? */

/* ------------------------------------------------------------------- =
*/
/* Truncate a gx_color_value to the desired number of bits.            =
*/
/* ------------------------------------------------------------------- =
*/

private uint32
upd_truncate(upd_pc upd,int i,gx_color_value v) {
   const updcmap_pc cmap =3D upd->cmap + i;
   int32           s; /* step size */
   gx_color_value *p; /* value-pointer */

   if(0 =3D=3D cmap->bits) {                          /* trivial case */

      v =3D 0;

   } else if(gx_color_value_bits > cmap->bits) { /* really truncate ? */

      p =3D cmap->code + ((cmap->bitmsk + 1) >> 1);
      s =3D              ((cmap->bitmsk + 1) >> 2);
/*
 * Perform search in monotonic code-array
 */
      while(s > 0) {
         if(v > *p) {           /* we're below */
            p +=3D s;
         } else if(v < p[-1]) { /* we're ahead for sure */
            p -=3D s;
         } else {
/* years ago, i knew what this was good for */
            if((v-p[-1]) < (p[0]-v)) p -=3D 1;
            break;
         }
         s >>=3D 1;
      }
      if((v-p[-1]) < (p[0]-v)) p -=3D 1;
      v =3D p - cmap->code;
   }

   if(!cmap->rise) v =3D cmap->bitmsk - v; /* Again reverse, if =
necessary */

   return ((uint32) v) << cmap->bitshf;
}

/* ------------------------------------------------------------------- =
*/
/* upd_open_map: install the color-mapping                             =
*/
/* ------------------------------------------------------------------- =
*/

private int
upd_open_map(upd_device *udev)
{
   const upd_p      upd   =3D udev->upd;
   int imap;

/** _always_ initialize crucial Values! */
   for(imap =3D 0; UPD_CMAP_MAX > imap; ++imap) upd->cmap[imap].code   =
=3D NULL;
   upd->ncomp =3D 0;

/** There should not be an error yet */
   if(B_ERROR & upd->flags)    imap =3D 0;

/** Establish the xfer-Indices */
   if(imap) {
      for(imap =3D 0; UPD_CMAP_MAX > imap; ++imap) {
         upd->cmap[imap].xfer =3D -1;
         upd->cmap[imap].bits =3D  0;
      }
      switch(upd->choice[C_MAPPER]) {
         case MAP_GRAY:
            upd->cmap[0].xfer =3D FA_WXFER;
         break;
         case MAP_RGBW:
            upd->cmap[0].xfer =3D FA_WXFER;
            upd->cmap[1].xfer =3D FA_RXFER;
            upd->cmap[2].xfer =3D FA_GXFER;
            upd->cmap[3].xfer =3D FA_BXFER;
         break;
         case MAP_RGB:
            upd->cmap[0].xfer =3D FA_RXFER;
            upd->cmap[1].xfer =3D FA_GXFER;
            upd->cmap[2].xfer =3D FA_BXFER;
         break;
         case MAP_CMYK:
            upd->cmap[0].xfer =3D FA_KXFER;
            upd->cmap[1].xfer =3D FA_CXFER;
            upd->cmap[2].xfer =3D FA_MXFER;
            upd->cmap[3].xfer =3D FA_YXFER;
         break;
         case MAP_CMYKGEN:
            upd->cmap[0].xfer =3D FA_KXFER;
            upd->cmap[1].xfer =3D FA_CXFER;
            upd->cmap[2].xfer =3D FA_MXFER;
            upd->cmap[3].xfer =3D FA_YXFER;
         break;
         case MAP_RGBOV:
            upd->cmap[0].xfer =3D FA_KXFER;
            upd->cmap[1].xfer =3D FA_CXFER;
            upd->cmap[2].xfer =3D FA_MXFER;
            upd->cmap[3].xfer =3D FA_YXFER;
         break;
         case MAP_RGBNOV:
            upd->cmap[0].xfer =3D FA_KXFER;
            upd->cmap[1].xfer =3D FA_CXFER;
            upd->cmap[2].xfer =3D FA_MXFER;
            upd->cmap[3].xfer =3D FA_YXFER;
         break;
         default:
#if         UPD_MESSAGES & UPD_M_WARNING
               if(upd_choice[C_MAPPER][0])
                  errprintf(
                     "upd_open_map: unsupported %s=3D%d\n",
                     upd_choice[C_MAPPER][0],upd->choice[C_MAPPER]);
               else
                  errprintf(
                     "upd_open_map: unsupported choce[%d]=3D%d\n",
                     C_MAPPER,upd->choice[C_MAPPER]);
#endif
            imap =3D 0;
         break;
      }
   }


/** The bit number sould be positive & fit into the storage */

   if(imap) { /* Check number of Bits & Shifts */

#if      UPD_MESSAGES & UPD_M_WARNING
      uint32 used =3D 0,bitmsk;
#endif
      bool success =3D true;

      for(imap =3D 0; UPD_CMAP_MAX > imap; ++imap) {
         if(0 > upd->cmap[imap].xfer) continue;

         if((0                     > upd->int_a[IA_COMPBITS].data[imap]) =
 ||
            (gx_color_value_bits   < upd->int_a[IA_COMPBITS].data[imap]) =
 ||
            (0                     > =
upd->int_a[IA_COMPSHIFT].data[imap]) ||
            (upd->int_a[IA_COMPBITS].data[imap] >
             (udev->color_info.depth - =
upd->int_a[IA_COMPSHIFT].data[imap]))) {
#if         UPD_MESSAGES & UPD_M_WARNING
               errprintf(
                  "upd_open_map: %d Bits << %d is illegal for %d. =
Component\n",
                  upd->int_a[IA_COMPBITS].data[imap],
                  upd->int_a[IA_COMPSHIFT].data[imap],imap+1);
#endif

            success =3D false;


         } else {

            int         n;
            const float *now;
            float       last;

            if((NULL =3D=3D upd->float_a[upd->cmap[imap].xfer].data) ||
               (2    >  upd->float_a[upd->cmap[imap].xfer].size)   ) {
               float *fp;
               UPD_MM_DEL_PARAM(upd->float_a[upd->cmap[imap].xfer]);
               UPD_MM_GET_ARRAY(fp,2);
               fp[0] =3D 0.0;
               fp[1] =3D 1.0;
               upd->float_a[upd->cmap[imap].xfer].data =3D fp;
               upd->float_a[upd->cmap[imap].xfer].size =3D 2;
            }
            n    =3D upd->float_a[upd->cmap[imap].xfer].size-1;
            now  =3D upd->float_a[upd->cmap[imap].xfer].data;
            last =3D now[n];

            if(     *now < last) { /* Rising */
               last =3D *now++;
               while(n--) {
                 if(last >=3D *now) break;
                 last =3D *now++;
               }
            } else if(*now > last) { /* Falling */
               last =3D *now++;
               while(n--) {
                 if(last <=3D *now) break;
                 last =3D *now++;
               }
            }                      /* Monotony-check */

            if(0 <=3D n) {
#if            UPD_MESSAGES & UPD_M_WARNING
               errprintf(
                  "upd_open_map: %d. Component has non monoton =
Xfer\n",imap+1);
#endif
               success =3D false;

            } else {

#if            UPD_MESSAGES & UPD_M_WARNING

               bitmsk   =3D ((uint32) 1 << =
upd->int_a[IA_COMPBITS].data[imap]) -1;
               bitmsk <<=3D upd->int_a[IA_COMPSHIFT].data[imap];

               if(used & bitmsk) errprintf(
                  "upd_open_map: %d. Component overlaps with =
others\n",imap+1);

               used |=3D bitmsk;
#endif
            }
         }
      }

      if(!success) imap =3D 0;

   }             /* Check number of Bits */

/** Do the allocation */

   if(imap) {

      for(imap =3D 0; UPD_CMAP_MAX > imap; ++imap) {
         if(0 > upd->cmap[imap].xfer) continue;

         upd->cmap[imap].bits     =3D =
upd->int_a[IA_COMPBITS].data[imap];
         upd->cmap[imap].bitshf   =3D =
upd->int_a[IA_COMPSHIFT].data[imap];
         upd->cmap[imap].bitmsk   =3D 1;
         upd->cmap[imap].bitmsk <<=3D upd->cmap[imap].bits;
         upd->cmap[imap].bitmsk  -=3D 1;
         upd->cmap[imap].rise     =3D
            upd->float_a[upd->cmap[imap].xfer].data[0] <
            upd->float_a[upd->cmap[imap].xfer].data[
               upd->float_a[upd->cmap[imap].xfer].size-1] ?
            true : false;
         upd->cmap[imap].code     =3D =
gs_malloc(upd->cmap[imap].bitmsk+1,
             sizeof(upd->cmap[imap].code[0]),"upd/code");
         if(!upd->cmap[imap].code) break;
      }

      if(UPD_CMAP_MAX > imap) {

         imap =3D 0;

#if      UPD_MESSAGES & UPD_M_ERROR
            errprintf("upd_open_map: could not allocate code-arrays\n");
#        endif

      }
   }

/** then fill the code-arrays */

   if(imap) {
/*
 * Try making things easier: (than with stcolor)
 *     normalize values to 0.0/1.0-Range
 *     X-Axis:   Color-Values (implied)
 *     Y-Values: Indices      (given)
 */

      for(imap =3D 0; UPD_CMAP_MAX > imap; ++imap) {

         const updcmap_p cmap =3D upd->cmap + imap;
         uint32 ly,iy;
         float ystep,xstep,fx,fy;

/*       Variables & Macro for Range-Normalization */
         double offset,scale;
#define  XFVAL(I) ((upd->float_a[cmap->xfer].data[I]-offset)*scale)

         if(0 > cmap->xfer) continue;

         cmap->code[cmap->bitmsk] =3D gx_max_color_value;

         if(!cmap->bits) continue;

         offset =3D upd->float_a[cmap->xfer].data[0];
         if(     0.0 > offset) offset =3D 0.0;
         else if(1.0 < offset) offset =3D 1.0;

         scale  =3D =
upd->float_a[cmap->xfer].data[upd->float_a[cmap->xfer].size-1];
         if(     0.0 > scale ) scale  =3D 0.0;
         else if(1.0 < scale ) scale  =3D 1.0;

         if(scale !=3D offset) scale =3D 1.0 /