/*===========================================================================
  Copyright (C) 1994-2010 European Southern Observatory (ESO)
 
  This program is free software; you can redistribute it and/or 
  modify it under the terms of the GNU General Public License as 
  published by the Free Software Foundation; either version 2 of 
  the License, or (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, 
  MA 02139, USA.
 
  Correspondence concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.LANGUAGE   C
.IDENT      pslibi.c
.AUTHOR     Preben Grosbol,  ESO/IPG
.KEYWORDS   hardcopy, PostScript, image, library
.ENVIRON    MIDAS
.PURPOSE    Library of C-routines to generate hardcopy of images in
            PostScript format.
.COMMENT    The following routines are available:
	        psopen(...)  : Open and initiate PS-file
	        psmode(...)  : Define and initiate page mode
	        pscolor(...) : Define graphics color mode
		psclose()    : Terminate and close PS-file 
		psitf(...)   : Define Intensity Transfer Functions
		psframe(...) : Define location and size of image frame
		psimage(...) : Define image parameters and do setup
		psline(...)  : Write one line of image data
		pscapt(...)  : Define location etc. of caption area
		pstext(...)  : Write text in caption area
		pslabel(...) : Write text lable at given position
		pswedge(...) : Write gray/color wedge
		psdraw(...)  : Draw different shapes: line, ...
.COMMENT    Routines use OSA-routines to write PostScript file to disk.
.VERSION    1.0   1990-Jul-18 : Creation, PJG
.VERSION    1.1   1990-Aug-09 : Correct bugs + change psopen,  PJG
.VERSION    1.2   1990-Aug-22 : Add pslabel/psmode + explicit fonts, PJG
.VERSION    1.3   1991-Jan-30 : Use pixel resolution, PJG
.VERSION    1.4   1991-Oct-04 : Correct spelling of 'gray', PJG
.VERSION    1.5   1992-Mar-17 : add pswedge function, PJG
.VERSION    1.55  1992-Apr-10 : use mode in psframe, KB
.VERSION    2.00  1992-Jul-29 : Change call sequence, add EPS, PJG
.VERSION    2.05  1993-Mar-02 : Omit 'setscreen' if resolution<=0, PJG
.VERSION    2.10  1993-Mar-23 : Change psline and psbwc, PJG
.VERSION    2.15  1993-Nov-11 : Correct pslabel, PJG
.VERSION    2.20  1994-May-03 : Add option for only box/no labels, PJG
.VERSION    2.25  1994-Sep-07 : Explicit cast for strlen, PJG
.VERSION    2.30  1998-Feb-27 : Add 'pfcolor()','pfdraw()' plus
                                  improve 'boundingbox' calculation, PJG
.VERSION    2.35  1998-Mar-04 : Add margin to boundingbox, PJG
.VERSION    2.40  1998-Sep-16 : Correct initiation of variable, PJG
.VERSION    2.45  2000-Jan-12 : Correct (atend) string, PJG
.VERSION    2.50  2002-Oct-02 : Update PS header/tailer section, PJG

 100118
------------------------------------------------------------------------*/

#include   <stdio.h>
#include   <string.h>                /* String library definitions      */
#include   <math.h>                  /* Standard math library           */
#include   <time.h>                  /* Standard time definitions       */
#include   <osparms.h>               /* OS-parameter definitions        */
#include   <midas_def.h>

#define     MXTEXT            128     /* Max. size of text and name str */
#define     MXCPL              72     /* Max. char. per PS-line         */
#define     PPCM           28.246     /* PS-points per cm               */
#define     MXITF             256     /* Max. size of ITF tables        */
#define     MXBUF            4096     /* Max. size of Image buffer      */

static int              psfid;  /* File-id for PostScript file          */
static int              pscxo;  /* Current X-origin                     */
static int              pscyo;  /* Current Y-origin                     */
static double           pscxs;  /* Current X scale factor               */
static double           pscys;  /* Current Y scale factor               */
static double           psang;  /* Rotation angle of paper              */
static int              psres;  /* PS Resolution in pixel per cm        */
static int              psisx;  /* Current Image size in X (pt)         */
static int              psisy;  /* Current Image size in Y (pt)         */
static int              psbin;  /* Binning factor in both X and Y       */
static int              psbyc;  /* Binning counter in Y                 */
static int              pstls;  /* PS Text line spacing                 */
static int              pstyl;  /* PS Text Y line position              */
static int              pstcs;  /* PS Text column spacing               */
static int              pstbc;  /* Text buffer countacing               */
static int               psnx;  /* No. of image pixels in X axis        */
static int               psny;  /* No. of image pixels in Y axis        */
static int              psbwc;  /* PSmode: 0)B/W, 1)pseudo, 2)true color*/
static int              psbp8;  /* Flag: True if 8bit per pixel else 4  */
static int              bflag;  /* Flag: true if boudingbox initiated   */
static int                llx;  /* Lower left corner of boudingbox in X */
static int                lly;  /* Lower left corner of boudingbox in Y */
static int                urx;  /* Upper right corner of boudingbox in X*/
static int                ury;  /* Upper right corner of boudingbox in Y*/
static int                mox;  /* Margin offset in X                   */
static int                moy;  /* Margin offset in Y                   */
static int       psitt[MXITF];  /* Intensity Transform Table            */
static int    psmap[3][MXITF];  /* Color Transform Table                */
static char      text[MXTEXT];  /* Text buffer for formating            */
static char     psfnt[MXTEXT];  /* Buffer for PostScript fonts used     */
static short     pbbuf[MXBUF];  /* Buffer for blue image pixel rebin    */
static short     pgbuf[MXBUF];  /* Buffer for green image pixel rebin   */
static short     prbuf[MXBUF];  /* Buffer for red image pixel rebin     */

void bbupdate(xll,yll,xur,yur)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE  Internal function to update boundingbox variables
.RETURN	  Status, none
------------------------------------------------------------------------*/
double    xll;		                 /* New lower left limit in X   */
double    yll;		                 /* New lower left limit in Y   */
double    xur;		                 /* New upper right limit in X  */
double    yur;		                 /* New upper right limit in Y  */
{
  if (bflag) {
    if (xll<llx) llx = (int) floor(xll);
    if (yll<lly) lly = (int) floor(yll);
    if (urx<xur) urx = (int) ceil(xur);
    if (ury<yur) ury = (int) ceil(yur);
  }
  else {
    llx = (int) floor(xll); lly = (int) floor(yll);
    urx = (int) ceil(xur); ury = (int) ceil(yur);
    bflag = 1;
  }
  return;
}

int addfont(font)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE  Internal function to add new font
.RETURN	  Status, 0: okay, -1: Not enough space in buffer
------------------------------------------------------------------------*/
char  *font;
{
  if (strstr(psfnt,font)==NULL) {
     if (strlen(psfnt)+strlen(font) < (size_t)(MXTEXT-2)) {
	(void) strcat(psfnt,font); (void) strcat(psfnt," ");
	/* 
	   (void) sprintf(text,"%%%%IncludeFont: %s\n", font);
	   (void) osawrite(psfid,text,(int)strlen(text));
	*/
     } else {
       return -1;
     }
  }

  return 0;
}

int psopen(name,ident,xsize,ysize,xoff,yoff,angle,res)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE  Create and initiate a PostScript output file
.RETURN	  Status, 0:OK else 'osaopen' error status
------------------------------------------------------------------------*/
char      *name;		/* Name of PostScript file to create    */
char     *ident;		/* Identifier for PostScript file       */
double    xsize;		/* Original X-size in cm of page        */
double    ysize;		/* Original Y-size in cm of page        */
double     xoff;                /* Offset/edge of active area in X (cm) */
double     yoff;                /* Offset/edge of active area in Y (cm) */
double    angle;		/* Angle in degrees to rotate page      */
double     *res;		/* psres + no. of pixels resolved pr cm 
				   (psres is used for rebinning the image
				   to avoid images too large for a printer) */

{
  char       c, psname[MXTEXT];
  int        n, i;
  double     x, y, xcen, ycen;
  struct tm  tms;

  /* create PostScript file or sdtout if name is "-"  */

  if (*name != '-') {
     for (n=0; n<MXTEXT && (c=name[n]) && c!=' ' && c!='.'; n++)
       psname[n] = c;
     for (i=0; n<MXTEXT && (psname[n++]=".ps"[i]);i++);
     psfid = osaopen(psname,WRITE);
     if (psfid<0) return psfid;               /* if error - return   */
   }
  else psfid = 1;

  /* compute transformation and initiate variables           */

  psang = -angle * atan(1.0)/45.0;
  xcen = 0.5*PPCM*xsize;
  ycen = 0.5*PPCM*ysize;
  xoff *= PPCM;
  yoff *= PPCM;
  mox = xoff; moy = yoff;
  xsize = xcen - xoff;
  ysize = ycen - yoff;
  x =  xsize*cos(psang) + ysize*sin(psang);
  y = -xsize*sin(psang) + ysize*cos(psang);
  if (x<0.0) x = -x; if (y<0.0) y = -y;
  pscxo = 0; pscyo = 0; pscxs = 1.0; pscys = 1.0;

  bflag = 0;                /* BoundingBox variables not initiated */
  llx = xoff; urx = 2.0*xcen - xoff;       /* Limits for EPSF      */
  lly = yoff; ury = 2.0*ycen - yoff;

  /* write prolog and comments into PostScript file  */

  (void) sprintf(text,"%%!PS-Adobe-3.0 EPSF-3.0\n");
  (void) osawrite(psfid,text,(int)strlen(text));
  (void) sprintf(text,"%%%%BoundingBox: (atend)\n");
  (void) osawrite(psfid,text,(int)strlen(text));
  (void) sprintf(text,"%%%%Creator: ESO-MIDAS\n");
  (void) osawrite(psfid,text,(int)strlen(text));
  (void) sprintf(text,"%%%%Title: Image >%-50.50s<\n",ident);
  (void) osawrite(psfid,text,(int)strlen(text));
  if (oshdate(psname,&tms)) psname[0] = '\0';
  (void) sprintf(text,"%%%%CreationDate: %s\n",psname);
  (void) osawrite(psfid,text,(int)strlen(text));
  (void) sprintf(text,"%%%%Pages: 1\n");
  (void) osawrite(psfid,text,(int)strlen(text));
  (void) sprintf(text,"%%%%DocumentNeededResources: (atend)\n");
  (void) osawrite(psfid,text,(int)strlen(text));
  (void) sprintf(text,"%%%%EndComments\n");
  (void) osawrite(psfid,text,(int)strlen(text));

  /* write coordinate transformation  */

  (void) sprintf(text,"save\n"); (void) osawrite(psfid,text,(int)strlen(text));
  (void) sprintf(text,"%f %f translate %f rotate\n",xcen,ycen,angle);
  (void) osawrite(psfid,text,(int)strlen(text));
  (void) sprintf(text,"%f %f translate\n",-x,-y);
  (void) osawrite(psfid,text,(int)strlen(text));

  /* define pixel frequence for halftone     */

  if (*res > 0.0)
     {
     psres = res[0];
     i = 2.54 * res[1];
     }
  else
     {
     psres = 60;
     i = 0;
     }

  psfnt[0] = '\0';
  if (0<i) {
     (void) sprintf(text,"%d currentscreen 3 -1 roll pop setscreen\n",i);
     (void) osawrite(psfid,text,(int)strlen(text));
   }

  /* initiate variables etc.                 */

  for (n=0; n<MXITF; n++) {
     psitt[n] = n;
     psmap[0][n] = n;
     psmap[1][n] = n;
     psmap[2][n] = n;
   }

  return 0;
}

int psmode(mode,rbgc,gbgc,bbgc)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE  Define and initiate page format/layout
.RETURN	  Status, 0:OK else -1:error
------------------------------------------------------------------------*/
int        mode;		/* Page mode                            */
double     rbgc;		/* Red background color for page        */
double     gbgc;		/* Green background color for page      */
double     bbgc;		/* Blue background color for page       */

{
  double   x;

  if (psfid<0) return -1;                 /* check if ps-file open      */

  /* write background color if required  */

  if (mode) {
     (void)sprintf(text,"clippath %f %f %f setrgbcolor fill\n",rbgc,gbgc,bbgc);
     (void) osawrite(psfid,text,(int)strlen(text));
     x = (2.5<(rbgc+gbgc+bbgc)) ? 0.0 : 1.0;
     (void) sprintf(text,"%f setgray\n",x);
     (void) osawrite(psfid,text,(int)strlen(text));
   }

  /* Start first and only page  */

  (void) sprintf(text,"%%%%Page: 1 1\n");
  (void) osawrite(psfid,text,(int)strlen(text));

  return 0;
}

int pscolor(mode,rc,gc,bc)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE  Define color/grey mode for graphics
.RETURN	  Status, 0:OK else -1:error
------------------------------------------------------------------------*/
int        mode;		/* Graphics color mode: 0)grey, 1)color */
double       rc;		/* Red color or grey level for graphics */
double       gc;		/* Green color for graphics             */
double       bc;		/* Blue color for graphics              */

{
  if (psfid<0) return -1;                 /* check if ps-file open      */

  /* write graphics color if required  */

  if (mode) {
     (void)sprintf(text,"%f %f %f setrgbcolor\n",rc,gc,bc);
     (void) osawrite(psfid,text,(int)strlen(text));
  }
  else {
     (void) sprintf(text,"%f setgray\n",rc);
     (void) osawrite(psfid,text,(int)strlen(text));
  }

  return 0;
}

int psclose()
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE   Write trailer and close PostScript file
.RETURN    Status, 0:OK else 'osaclose' error status
------------------------------------------------------------------------*/
{
  int  illx, illy, iurx, iury;

  if (psfid<0) return -1;                 /* check if ps-file open      */

  /* print page and write PostScript Trailer   */

  (void) sprintf(text,"showpage\n");
  (void) osawrite(psfid,text,(int)strlen(text));
  (void) sprintf(text,"%%%%Trailer\n");
  (void) osawrite(psfid,text,(int)strlen(text));
  illx =  llx*cos(psang) + lly*sin(psang);
  illy = -llx*sin(psang) + lly*cos(psang);
  if (illx<0) illx = -illx; if (illy<0) illy = -illy;
  iurx =  urx*cos(psang) + ury*sin(psang);
  iury = -urx*sin(psang) + ury*cos(psang);
  if (iurx<0) iurx = -iurx; if (iury<0) iury = -iury;
  illx += mox; illy += moy;                     /* correct for margin   */
  iurx += mox; iury += moy;
  (void) sprintf(text,"%%%%BoundingBox: %d %d %d %d\n",illx,illy,iurx,iury);
  (void) osawrite(psfid,text,(int)strlen(text));
  (void) sprintf(text,"%%%%DocumentNeededResources: font %s\n",psfnt);
  (void) osawrite(psfid,text,(int)strlen(text));
  (void) sprintf(text,"%%%%EOF\n");
  (void) osawrite(psfid,text,(int)strlen(text));

  if (1<psfid) osaclose(psfid);
  psfid = -1;

  return 0;
}

int psitf(mode,size,itt,red,green,blue)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE   Define Intensity Transfer Function for Image. The transform is
           initiated to a unity transform if the pointers is NULL, 
.RETURN    Status, 0:OK, -1:Error in ITF size
------------------------------------------------------------------------*/
int        mode;                /* Transform: 0)bw, 1)pseudo, 2)real    */
int        size;	        /* Size of ITT and Color tables         */
int        *itt;	       	/* Pointer to Intensity Transform Table */
int        *red;	       	/* Pointer to Red Color Table           */
int      *green;	       	/* Pointer to Green Color Table         */
int       *blue;	       	/* Pointer to Blue Color table          */
{

register int   nr;

  if (size<0 || MXITF<size) return (-1);
  if (psfid<0) return (-1);                 /* check if ps-file open      */

  if (itt)   for (nr=0; nr<size; nr++) psitt[nr] = *itt++;
  if (blue)  for (nr=0; nr<size; nr++) psmap[0][nr] = *blue++;
  if (green) for (nr=0; nr<size; nr++) psmap[1][nr] = *green++;
  if (red)   for (nr=0; nr<size; nr++) psmap[2][nr] = *red++;

  psbwc = (mode<0) ? 0 : (2<mode) ? 2 : mode;
  
  return 0;
}

int psframe(mode,xorg,yorg,xsize,ysize,font,fsize,xstr,ystr,xend,yend)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE    Define place and size of image frame
.RETURN     Status, 0:OK, -1:Error font-size
------------------------------------------------------------------------*/
int       mode;			/* Mode for drawing the frame           */
double    xorg;			/* Origin of frame in X axis            */
double    yorg;			/* Origin of frame in Y axis            */
double   xsize;			/* Size of frame in X axis              */
double   ysize;			/* Size of frame in Y axis              */
char     *font;                 /* Name of font used for axes           */
int      fsize;			/* Size of lable font                   */
double    xstr;			/* Start coordinate for X axis          */
double    ystr;			/* Start coordinate for Y axis          */
double    xend;			/* End coordinate for X axis            */
double    yend;			/* End coordinate for Y axis            */
{
  int      iox, ioy, isx, isy;
  double   sxi, syi, tsx, tsy;

  if (psfid<0) return -1;                 /* check if ps-file open      */

  iox = PPCM*xorg;  ioy = PPCM*yorg;
  isx = PPCM*xsize; isy = PPCM*ysize;
  sxi = isx; syi = isy;
  psisx = isx; psisy = isy;

  bbupdate(PPCM*xorg, PPCM*yorg, PPCM*(xorg+xsize), PPCM*(yorg+ysize));

  /* offset to image origin and scale to size  */

  (void) sprintf(text,"%f %f scale %d %d translate\n",
	  1.0/pscxs,1.0/pscys,-pscxo,-pscyo);
  (void) osawrite(psfid,text,(int)strlen(text));
  pscxo = 0; pscyo = 0; pscxs = 1.0; pscys = 1.0;

  /* draw frame around image  */

  if (mode) {                           /* draw frame box around image */
     (void) sprintf(text,"newpath %d %d  moveto %d %d lineto\n",
	     iox,ioy,iox,ioy+isy);
     (void) osawrite(psfid,text,(int)strlen(text));
     (void) sprintf(text,"%d %d lineto %d %d lineto closepath stroke\n",
           iox+isx,ioy+isy,iox+isx,ioy);
     (void) osawrite(psfid,text,(int)strlen(text));

     if (0<mode) {                          /* write labels on frame   */
	if (addfont(font)) return -1;
	(void) sprintf(text,"/%s findfont %d scalefont setfont\n",font,fsize);
	(void) osawrite(psfid,text,(int)strlen(text));
	(void) sprintf(text,"%d %d moveto ",iox,ioy-10);
	(void) osawrite(psfid,text,(int)strlen(text));
	(void) sprintf(text,"(%g) show\n",xstr);
	(void) osawrite(psfid,text,(int)strlen(text));
	(void) sprintf(text,"%d %d moveto ",iox+isx-20,ioy-10);
	(void) osawrite(psfid,text,(int)strlen(text));
	(void) sprintf(text,"(%g) show\n",xend);
	tsx = (double) (fsize*((int)strlen(text)-8) - 20);

	(void) osawrite(psfid,text,(int)strlen(text));
	(void) sprintf(text,"%d %d moveto 90 rotate ",iox-3,ioy);
	(void) osawrite(psfid,text,(int)strlen(text));
	(void) sprintf(text,"(%g) show -90 rotate\n",ystr);
	(void) osawrite(psfid,text,(int)strlen(text));
	(void) sprintf(text,"%d %d moveto 90 rotate ",iox-3,ioy+isy-20);
	(void) osawrite(psfid,text,(int)strlen(text));
	(void) sprintf(text,"(%g) show -90 rotate\n",yend);
	(void) osawrite(psfid,text,(int)strlen(text));
	tsy = (double)(fsize*((int)strlen(text)-20) - 20);

	bbupdate(PPCM*xorg-5-fsize, PPCM*yorg-12,
		 PPCM*(xorg+xsize)+tsx, PPCM*(yorg+ysize)+tsy);
      }
   }

  /* offset to image origin and scale to size  */

  (void) sprintf(text,"%d %d translate %f %f scale\n",
	  iox-pscxo,ioy-pscyo,sxi/pscxs,syi/pscys);
  (void) osawrite(psfid,text,(int)strlen(text));

  pscxo = iox; pscyo = ioy; pscxs = sxi; pscys = syi;
  return 0;
}

int psimage(nx,ny,fmt,bpp)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE    Define size and format of image
.RETURN     Status, 0:OK  else -1:Error input
------------------------------------------------------------------------*/
int    nx;			/* No. of image pixels in X axis        */
int    ny;			/* No. of image pixels in Y axis        */
int   fmt;			/* Data format of image                 */
int   bpp;			/* Bit per Pixel to use for display     */
{
  int ix, iy;

  if (psfid<0) return -1;                 /* check if ps-file open      */

  /* check size and format parameters   */

  if (nx<0 || ny<0) return -1;
  if (bpp!=4 && bpp!=8) return -1;
  psnx = nx; psny = ny;
  psbwc = (fmt) ? fmt : psbwc;

  /* define rebinning of image to match resolution  */

  ix = (PPCM*psnx)/(2.0*psres*psisx) + 1.0;
  iy = (PPCM*psny)/(2.0*psres*psisy) + 1.0;
  psbin = (ix<iy) ? ix : iy;
  psbyc = psbin;
  if (MXBUF<(psnx-1)/psbin+1) psbin = (psnx-1)/MXBUF + 1;
  nx = (psnx-1)/psbin + 1;
  ny = (psny-1)/psbin + 1;
  
  /* define image transformation and format  */

  psbp8 = (bpp==8);
  ix = nx;
  if (psbwc) ix *= 3;
  if (!psbp8) ix = (ix-1)/2 + 1;

  (void) sprintf(text,"/picstr %d string def\n",ix);
  (void) osawrite(psfid,text,(int)strlen(text));
  (void) sprintf(text,"%d %d %d ",nx,ny,bpp);
  (void) osawrite(psfid,text,(int)strlen(text));
  (void) sprintf(text,"[%d 0 0 %d 0 0]\n",nx,ny);
  (void) osawrite(psfid,text,(int)strlen(text));
  (void) sprintf(text,"{currentfile picstr readhexstring pop} ");
  (void) osawrite(psfid,text,(int)strlen(text));
  if (psbwc)
    (void) sprintf(text,"false 3 colorimage\n");
  else
    (void) sprintf(text,"image\n");
  (void) osawrite(psfid,text,(int)strlen(text));
  
  return 0;
}

int psline(pbi, pgi, pri)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE   Convert one line of image data to HEX-code in PostScript
           format and write it into file. Note, for B/W or pseudo color
           only the first pointer is used 'pbi'.
.RETURN    Status,  0:OK, -1: PS-file not open, -2: missing pointer
------------------------------------------------------------------------*/
unsigned char    *pbi;		/* Pointer to BLUE  image data          */
unsigned char    *pgi;		/* Pointer to GREEN image data          */
unsigned char    *pri;		/* Pointer to RED   image data          */
{
  register char  *pc;
  register short *psb, *psg, *psr;
  register int   nc, n1, n2;
  int            n, val, nv, np[3], nx;

  if (psfid<0) return -1;                 /* check if ps-file open      */
  if (!pbi && (psbyc==psbin)) return 0;   /* nothing to flush - return  */
  if (psbwc==2 && (!pbi || !pgi|| !pri)) return -2;

  nx = (psnx-1)/psbin;
  psb = pbbuf;
  psg = pgbuf;
  psr = prbuf;

  if (pbi) {
     if (1<psbin) {                          /* rebin if specified      */
	n2 = (psbyc==psbin);
	nv = nx;
	while (nv--) {
	   n1 = 0; nc = psbin;
	   while (nc--) n1 += *pbi++;
	   if (n2) *psb++ = n1; else *psb++ += n1;
	 }
	nv = psnx - psbin*nx; nc = psbin;
	while (nc--) {
	   *psb = (n2) ? *pbi : *psb + *pbi;
	   if (0<nv--) pbi++;
	 }
	if (psbwc==2) {              /* rebin green,red for true color */
	  nv = nx;
	  while (nv--) {
	    n1 = 0; nc = psbin;
	    while (nc--) n1 += *pgi++;
	    if (n2) *psg++ = n1; else *psg++ += n1;
	    n1 = 0; nc = psbin;
	    while (nc--) n1 += *pri++;
	    if (n2) *psr++ = n1; else *psr++ += n1;
	  }
	  nv = psnx - psbin*nx; nc = psbin;
	  while (nc--) {
	    *psb = (n2) ? *pbi : *psb + *pbi;
	    if (0<nv--) pbi++;
	    nv = psnx - psbin*nx;
	    *psr = (n2) ? *pri : *psr + *pri;
	    if (0<nv--) pri++;
	  }
	}
      }
     else {
	nc = psnx;
	while (nc--) *psb++ = *pbi++;
	if (psbwc==2) {              /* rebin green,red for true color */
	  nc = psnx;
	  while (nc--) *psg++ = *pgi++;
	  nc = psnx;
	  while (nc--) *psr++ = *pri++;
	}
      }
     if (--psbyc) return 0;
   }

  if (1<psbin) {                              /* take mean value       */
     n1  = psbin * (psbin-psbyc);
     n2  = n1/2;
     psb = pbbuf;
     nc  = nx + 1;
     while (nc--) { *psb = (*psb + n2)/n1; psb++; }
     if (psbwc==2) {                  /* mean green,red for true color */
       nc  = nx + 1;
       while (nc--) { *psg = (*psg + n2)/n1; psg++; }
       nc  = nx + 1;
       while (nc--) { *psr = (*psr + n2)/n1; psr++; }
     }
   }

  pc = text;
  if (psbwc!=2) {                        /* B/W or pseudo color mode    */
    psb = pbbuf;
    pstbc = 0;
    n = nx + 1;
    while (n--) {                             /* convert to hex         */
      val = *psb++;
      nv = psitt[val]; nc = (psbwc) ? 3 : 1;
      while (nc--) {
	n1 = psmap[nc][nv];
	n2 = n1 & 0x0F; n1 >>= 4;
	*pc++ = "0123456789abcdef"[n1]; pstbc++;
	if (psbp8) { *pc++ = "0123456789abcdef"[n2]; pstbc++; }
      }
      if (MXCPL<=pstbc) {             /* check if text line is full     */
	*pc++ = '\n'; *pc = '\0'; pc = text;
	(void) osawrite(psfid,pc,(int)strlen(pc));
	pstbc = 0;
      }
    }
  } 
  else {                                          /* true color mode    */
    psb = pbbuf;
    psg = pgbuf;
    psr = prbuf;
    pstbc = 0;
    n = nx + 1;
    while (n--) {                             /* convert to hex         */
      np[0] = psitt[*psb++];
      np[1] = psitt[*psg++];
      np[2] = psitt[*psr++];
      nc = 3;
      while (nc--) {
	n1 = psmap[nc][np[nc]];
	n2 = n1 & 0x0F; n1 >>= 4;
	*pc++ = "0123456789abcdef"[n1]; pstbc++;
	if (psbp8) { *pc++ = "0123456789abcdef"[n2]; pstbc++; }
      }
      if (MXCPL<=pstbc) {             /* check if text line is full     */
	*pc++ = '\n'; *pc = '\0'; pc = text;
	(void) osawrite(psfid,pc,(int)strlen(pc));
	pstbc = 0;
      }
    }
  }

  if (pstbc) {                         /* write remaining bytes to file */
     if (pstbc & 1) *pc++ = '0';
     *pc++ = '\n'; *pc = '\0'; pc = text;
     (void) osawrite(psfid,pc,(int)strlen(pc));
   }

  psbyc = psbin;
  return 0;
}

int pscapt(xstr,ystr,font,fsize,offset)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE    Define area and fonts for image captions
.RETURN	    Status, 0:OK -1:Error font-size
------------------------------------------------------------------------*/
double    xstr;			/* Start of Caption area in X axis      */
double    ystr;			/* Start of Caption area in Y axis      */
char     *font;                 /* Name of font used for captions       */
int      fsize;			/* Size of fonts used for captions      */
double  offset;			/* Column offset in cm for text field   */
{
  int   iox, ioy;

  if (psfid<0) return -1;                 /* check if ps-file open      */

  /* offset to image origin and scale to size  */

  iox = PPCM*xstr; ioy = PPCM*ystr;
  (void) sprintf(text,"%f %f scale %d %d translate\n",
          1.0/pscxs,1.0/pscys,iox-pscxo,ioy-pscyo);
  (void) osawrite(psfid,text,(int)strlen(text));
  pscxo = iox; pscyo = ioy; pscxs = 1.0; pscys = 1.0;

  /* define caption font and initiate offsets  */

  if (addfont(font)) return -1;
  (void) sprintf(text,"/%s findfont %d scalefont setfont\n",font,fsize);
  (void) osawrite(psfid,text,(int)strlen(text));
  pstcs = PPCM*offset; pstyl = 0;
  pstls = 1.2*fsize; if (pstls==fsize) pstls = fsize + 1;

  bbupdate(PPCM*xstr, PPCM*ystr, PPCM*xstr, PPCM*ystr);

  return 0;
}

int pstext(label,str)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE   Write a text line out in the caption area
.RETURN    Status,  0:OK
------------------------------------------------------------------------*/
char      *label;		/* Pointer to text in label column      */
char        *str;		/* Pointer to text string               */
{
  int  nt;

  if (psfid<0) return -1;                 /* check if ps-file open      */

  (void) sprintf(text,"0 %d moveto\n",pstyl);
  (void) osawrite(psfid,text,(int)strlen(text));
  (void) sprintf(text,"(%s) show\n",label);
  (void) osawrite(psfid,text,(int)strlen(text));

  (void) sprintf(text,"%d %d moveto\n",pstcs,pstyl);
  (void) osawrite(psfid,text,(int)strlen(text));
  (void) sprintf(text,"(: %s) show\n",str);
  (void) osawrite(psfid,text,(int)strlen(text));
  nt = strlen(text) - 10;
  pstyl -= pstls;

  bbupdate((double) pstcs, (double) pstyl,
	   (double) pstcs+pstls*nt/1.2, (double) pstyl+pstls);

  return 0;
}

int pslabel(xstr,ystr,font,fsize,label)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE    Write text label at given location with special font
.RETURN	    Status, 0:OK  -1:Error font-size
------------------------------------------------------------------------*/
double    xstr;			/* Start of text label in X axis        */
double    ystr;			/* Start of text label in Y axis        */
char     *font;                 /* Name of label font                   */
int      fsize;			/* Size of fonts used for text label    */
char    *label; 		/* Pointer to text                      */
{
  int   iox, ioy, nt;

  if (psfid<0) return -1;                 /* check if ps-file open      */

  /* reset offset and scale   */

  (void) sprintf(text,"%f %f scale %d %d translate\n",
          1.0/pscxs,1.0/pscys,-pscxo,-pscyo);
  (void) osawrite(psfid,text,(int)strlen(text));
  pscxo = 0; pscyo = 0; pscxs = 1.0; pscys = 1.0;

  /* define text font and initiate offsets  */

  if (addfont(font)) return -1;
  (void) sprintf(text,"/%s findfont %d scalefont setfont\n",font,fsize);
  (void) osawrite(psfid,text,(int)strlen(text));

  /* write text label   */

  iox = PPCM*xstr; ioy = PPCM*ystr;
  (void) sprintf(text,"%d %d moveto\n",iox,ioy);
  (void) osawrite(psfid,text,(int)strlen(text));
  (void) sprintf(text,"(%s) show\n",label);
  (void) osawrite(psfid,text,(int)strlen(text));
  nt = strlen(text) - 8;

  bbupdate(PPCM*xstr, PPCM*ystr, PPCM*xstr+nt*fsize, PPCM*ystr+fsize);

  return 0;
}

int pswedge(mode,xorg,yorg,xsize,ysize,zlow,zhigh)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE    write gray/color wedge at given location and size
.RETURN	    Status, 0:OK  -1:Error
------------------------------------------------------------------------*/
int       mode;			/* Mode for drawing the wedge           */
double    xorg;			/* Origin of wedge in X axis            */
double    yorg;			/* Origin of wedge in Y axis            */
double   xsize;			/* Size of wedge in X axis              */
double   ysize;			/* Size of wedge in Y axis              */
double    zlow;                 /* Start label value for wedge          */
double   zhigh;                 /* End label value for wedge            */
{
  char     *pc;
  int      iox, ioy, isx, isy;
  int      n, nx, ny, np, nv, nc, n1, n2, nn, bpp;
  double   sxi, syi;

  if (psfid<0) return -1;                /* check if ps-file open       */
  if (psbwc==2) return 0;                /* no wedge in real color mode */

  iox = PPCM*xorg;  ioy = PPCM*yorg;
  isx = PPCM*xsize; isy = PPCM*ysize;
  sxi = isx; syi = isy;

  bbupdate(PPCM*xorg, PPCM*yorg, PPCM*(xorg+xsize), PPCM*(yorg+ysize));

  /* offset to wedge origin and scale to size  */

  (void) sprintf(text,"%f %f scale %d %d translate\n",
	  1.0/pscxs,1.0/pscys,-pscxo,-pscyo);
  (void) osawrite(psfid,text,(int)strlen(text));
  pscxo = 0; pscyo = 0; pscxs = 1.0; pscys = 1.0;

  /* draw frame around wedge  */

  if (mode) {
     (void) sprintf(text,"%f setlinewidth\n",1.0/pscxs);
     (void) sprintf(text,"newpath %d %d  moveto %d %d lineto\n",
	     iox,ioy,iox,ioy+isy);
     (void) osawrite(psfid,text,(int)strlen(text));
     (void) sprintf(text,"%d %d lineto %d %d lineto closepath stroke\n",
	     iox+isx,ioy+isy,iox+isx,ioy);
     (void) osawrite(psfid,text,(int)strlen(text));
   }

  /* restore origin and scale  */

  (void) sprintf(text,"%d %d translate %f %f scale\n",
	  iox-pscxo,ioy-pscyo,sxi/pscxs,syi/pscys);
  (void) osawrite(psfid,text,(int)strlen(text));

  /* define image transformation and format  */

  psbp8 = 1;
  bpp = (psbp8) ? 8 : 4;
  np =  (psbp8) ? MXITF : MXITF/16;
  nx = (ysize<xsize) ? np : 1;
  ny = (ysize<xsize) ? 1 : np;

  if (psbwc) np *= 3;
  if (!psbp8) np = (np-1)/2 + 1;

  (void) sprintf(text,"/picstr %d string def\n",np);
  (void) osawrite(psfid,text,(int)strlen(text));
  (void) sprintf(text,"%d %d %d ",nx,ny,bpp);
  (void) osawrite(psfid,text,(int)strlen(text));
  (void) sprintf(text,"[%d 0 0 %d 0 0]\n",nx,ny);
  (void) osawrite(psfid,text,(int)strlen(text));
  (void) sprintf(text,"{currentfile picstr readhexstring pop} ");
  (void) osawrite(psfid,text,(int)strlen(text));
  if (psbwc) (void) sprintf(text,"false 3 colorimage\n");
     else (void) sprintf(text,"image\n");
  (void) osawrite(psfid,text,(int)strlen(text));

  /* write data ramp out for wedge image  */

  nn = 0; pc = text;
  for (n=0; n<MXITF; n++) {
     nv = psitt[n]; nc = (psbwc) ? 3 : 1;
     while (nc--) {
	n1 = psmap[nc][nv];
	n2 = n1 & 0x0F; n1 >>= 4;
	*pc++ = "0123456789abcdef"[n1]; nn++;
	if (psbp8) { *pc++ = "0123456789abcdef"[n2]; nn++; }
      }
     if (MXCPL<=nn) {                /* check if text line is full     */
	*pc++ = '\n'; *pc = '\0'; pc = text;
	(void) osawrite(psfid,pc,(int)strlen(pc));
	nn = 0;
      }
   }

  if (nn) {                           /* write remaining bytes to file */
     if (nn & 1) *pc++ = '0';
     *pc++ = '\n'; *pc = '\0'; pc = text;
     (void) osawrite(psfid,pc,(int)strlen(pc));
   }

  pscxo = iox; pscyo = ioy; pscxs = sxi; pscys = syi;
  return 0;
}

int psdraw(mode,xorg,yorg,width,parm1,parm2,parm3,parm4)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE    Draw shape depending on mode at origin xorg,yorg with line
            width 'width and the parameters parm1,parm2,parm3.  The
            supported shapes are:
                0) line   (parm1=length, parm2=angle)
                1) circle (parm1=radius, parm2=ang1, parm3=ang2)
.RETURN	    Status, 0:OK,  -1:Error font-size, -2: wrong mode
------------------------------------------------------------------------*/
int       mode;
double    xorg;			/* Origin of shape in X                 */
double    yorg;			/* Origin of shape in Y                 */
double   width;                 /* Width of line                        */
double   parm1;                 /* Shape parameter 1                    */
double   parm2;                 /* Shape parameter 2                    */
double   parm3;                 /* Shape parameter 3                    */
double   parm4;                 /* Shape parameter 3                    */
{
  int     iox, ioy, iex, iey;
  double  ang, ox, oy;

  if (psfid<0) return -1;                 /* check if ps-file open      */
  if (mode!=0 && mode!=1) return -2;      /* none supported shape       */

  /* reset offset and scale   */

  (void) sprintf(text,"%f %f scale %d %d translate\n",
          1.0/pscxs,1.0/pscys,-pscxo,-pscyo);
  (void) osawrite(psfid,text,(int)strlen(text));
  pscxo = 0; pscyo = 0; pscxs = 1.0; pscys = 1.0;

  (void) sprintf(text,"%f setlinewidth\n",PPCM*width);
  (void) osawrite(psfid,text,(int)strlen(text));

  switch (mode) {
    case  0:                                        /* line             */
      ang = (atan(1.0)/45.0) * parm2;
      iox = PPCM*xorg; ioy = PPCM*yorg;
      iex = iox + PPCM*parm1*cos(ang); iey = ioy + PPCM*parm1*sin(ang);
      (void) sprintf(text,"%d %d  moveto %d %d lineto stroke\n",
		     iox,ioy,iex,iey);
      (void) osawrite(psfid,text,(int)strlen(text));
      bbupdate((double) iox, (double) ioy, (double) iex, (double) iey);
      break;
    case 1:                                         /* circle arc       */
      ang = (atan(1.0)/45.0) * parm2;
      ox = PPCM*(xorg+parm1*cos(ang));
      oy = PPCM*(yorg+parm1*sin(ang));
      (void) sprintf(text,"%f %f moveto %f %f %f %f %f arc stroke\n",
		     ox,oy,PPCM*xorg,PPCM*yorg,PPCM*parm1,parm2,parm3);
      (void) osawrite(psfid,text,(int)strlen(text));
      bbupdate(PPCM*(xorg-parm1), PPCM*(yorg-parm1),
	       PPCM*(xorg+parm1), PPCM*(yorg+parm1));
      break;
  }
  
  return 0;
}
