/***********************************************************************
 *                                                                     *
 * PS_LZWencode.c - output an image in PostScript format               *
 *                  (Lelpel-Ziv Welch encoding)                        *
 *                                                                     *
 * Author: Evgeni Chernyaev (chernaev@mx.ihep.su)                      *
 *                                                                     *
 * Copyright (C) 1993, 1994 by Evgeni Chernyaev.                       *
 *                                                                     *
 * Permission to use, copy, modify, and distribute this software and   *
 * its documentation for non-commercial purpose is hereby granted      *
 * without fee, provided that the above copyright notice appear in all *
 * copies and that both the copyright notice and this permission       *
 * notice appear in supporting documentation.                          *
 *                                                                     *
 * This software is provided "as is" without express or implied        *
 * warranty.                                                           *
 *                                                                     *
 ***********************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

#ifdef __STDC__
#define ARGS(alist) alist
#else 
#define ARGS(alist) ()
#endif

typedef unsigned char byte;

static int       Nbyte;
static char      s[80];
static void      (*put_b) ARGS((byte));
#define put_string nc=strlen(s); for(i=0;i<nc;i++) (*put_b)(s[i]); Nbyte += nc

                        
#define MAXWIDTH 4096
#define BLACK    1
#define WHITE    0

/***********************************************************************
 *                                                                     *
 * Name: PutPreview                                  Date:    02.11.93 *
 * Author: E.Chernyaev (IHEP/Protvino)               Revised:          *
 *                                                                     *
 * Function: Output device independent B&W preview                     *
 *                                                                     *
 * Input: Width      - image width                                     *
 *        Height     - image height                                    *
 *        R[]        - red components                                  *
 *        G[]        - green components                                *
 *        B[]        - blue components                                 *
 *        ScLine[]   - array for scan line (byte per pixel)            *
 *        get_scline - user routine to read scan line:                 *
 *                       get_scline(y, Width, ScLine)                  *
 *                                                                     *
 ***********************************************************************/
static void PutPreview(Width, Height, R, G, B, ScLine, get_scline)
                  int  Width, Height;
                  byte R[], G[], B[], ScLine[];
                  void (*get_scline) ARGS((int, int, byte *));
{
  int    i, k, nc, x, y;
  int    mask, hex, err, dp, dp1, dp2, ddd[MAXWIDTH];

  static int FSgamma[256] = {                   /* Floyd-Steinberg gamma */
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  1,  1,  2,  2,  3,  3,  4,  4,  5,  6,  6,  7,  8,
    9,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
   24, 26, 27, 28, 29, 30, 32, 33, 34, 36, 37, 38, 40, 41, 43, 44,
   45, 47, 48, 50, 51, 53, 54, 56, 58, 59, 61, 62, 64, 66, 67, 69,
   70, 72, 74, 76, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 95, 96,
   98,100,102,104,105,107,109,111,113,114,116,118,120,122,124,125,
  127,129,131,133,135,136,138,140,142,144,145,147,149,151,153,154,
  156,158,160,162,163,165,167,169,170,172,174,175,177,179,181,182,
  184,186,187,189,190,192,194,195,197,198,200,201,203,204,206,207,
  209,210,212,213,215,216,217,219,220,221,223,224,225,226,228,229,
  230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,
  246,246,247,248,248,249,250,250,251,251,252,252,253,253,254,254,
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 };

  k = (Width + 279) / 280;                      /* 70 HEX per line */
  sprintf(s,"%%%%BeginPreview: %d %d 1 %d\n", Width, Height, k*Height);
                                                   put_string;
  for (i=0; i<Width; i++)
    ddd[i] = 0;

  for (y=0; y<Height; y++) {
    (*get_scline)(y, Width, ScLine);
    nc    = 0; mask = 128; hex = 0; dp1 = 0;
    for (x=0; x<Width; x++) {
      i     = ScLine[x];
      i     = (11*R[i] + 16*G[i] + 5*B[i]) / 32;
      dp    = FSgamma[i] + dp1 + ddd[x];        /* Floyd-Steinberg dithering */
      if (dp<128) {                             /*     ---    x    7/16      */
        hex += BLACK*mask; err = dp;            /*     3/16  5/16  1/16      */
      }else{
        hex += WHITE*mask; err = dp - 255;
      }
      dp1   = (err*7)/16;            
      if (x == 0) {
        ddd[0]    = (err*5)/16;
        dp2       = err/16;
        }else{
        ddd[x-1] += (err*3)/16;
        ddd[x]    = dp2 + (err*5)/16;
        dp2       = err/16;
      }
      mask /= 2;
      if (mask == 0) {
        if (nc == 0) {
          sprintf(&s[nc],"%% "); nc = 2;
        }
        sprintf(&s[nc],"%02x",hex); nc += 2;
        if (nc == 72) {
          sprintf(&s[nc],"\n");                 put_string;
          nc = 0;
        }
        mask = 128; hex = 0;      
      }
    }
    if (mask == 128) {
      if (nc != 0) {
        sprintf(&s[nc],"\n");                   put_string;
      }
    }else{
      if (nc == 0) {
        sprintf(&s[nc],"%% "); nc = 2;
      }
      sprintf(&s[nc],"%02x\n",hex);             put_string;
    }
  }
  sprintf(s,"%%%%EndPreview\n");                put_string;
}

/***********************************************************************
 *                                                                     *
 * Name: ASCII85encode                               Date:    05.11.93 *
 * Author: E.Chernyaev (IHEP/Protvino)               Revised:          *
 *                                                                     *
 * Function: ASCII85 encode and output byte buffer                     *
 *                                                                     *
 * Input: k   - number of bytes                                        *
 *        Buf - byte buffer                                            *
 *                                                                     *
 ***********************************************************************/
static void ASCII85encode(k, Buf)
                     int  k;
                     byte Buf[];
{
  unsigned long Value;
  int    i,j, n, nc;

  if (k == 0) return;
  s[5] = '\0';
  for (j=0; j<k; j+=4) {
    Value = Buf[j]*256*256*256;
    if (j+1 < k) Value += Buf[j+1]*256*256;
    if (j+2 < k) Value += Buf[j+2]*256;
    if (j+3 < k) Value += Buf[j+3];
    for (n=4; n>=0; n--) {
      s[n]   = Value % 85 + 33;
      Value /= 85;
    }
    if (k-j < 4) s[k-j+1] = '\0'; 
    put_string;
  }
  sprintf(s,"\n");
  put_string;
}

#define BITS           12                       /* largest code size */
#define HSIZE          5003                     /* hash table size */
#define SHIFT          4                        /* shift for hashing */
#define CLEARCODE      256                      /* Clear Code */
#define EOD            257                      /* End Of Data code */
#define PIXS           170000                   /* largest # of pixels */

/***********************************************************************
 *                                                                     *
 * Name: PutCode                                     Date:    05.11.93 *
 * Author: E.Chernyaev (IHEP/Protvino)               Revised:          *
 *                                                                     *
 * Function: Put out code (LZW encoding)                               *
 *                                                                     *
 * Input: Code - code                                                  *
 *        CodeSize - codesize                                          *
 *                                                                     *
 ***********************************************************************/
static void PutCode(Code, CodeSize)
                int Code, CodeSize;
{
  static int k, PartA, PartB, SizeA, SizeB;
  static int mask[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
                                0x001F, 0x003F, 0x007F, 0x00FF,
                                0x01FF, 0x03FF, 0x07FF, 0x0FFF };
  static byte Accum[56];
  if (Code == -1) {
    k      = 0;
    PartA  = 0;
    SizeA  = 0;
    return;
  }

  PartB = Code;
  SizeB = CodeSize;

  while (SizeB >= 8) {
    SizeB  = SizeA + SizeB - 8;
    Accum[k++] = PartA | (PartB >> SizeB);
    if (k == 56) {
      ASCII85encode(k,Accum);
      k      = 0;
    }
    PartB &= mask[SizeB];
    SizeA  = 0;
    PartA  = 0;
  }

  SizeA = SizeB;
  PartA = PartB << (8-SizeB);
  if (Code == EOD) {
    if (SizeA != 0) Accum[k++] = PartA;
    ASCII85encode(k, Accum);
  }
}

/***********************************************************************
 *                                                                     *
 * Name: EncodeData                                  Date:    05.11.93 *
 * Author: E.Chernyaev (IHEP/Protvino)               Revised: 21.06.94 *
 *                                                            04.06.94 *
 * Function: Lelpel-Ziv Welch encoding of an image                     *
 *                                                                     *
 * Input: Width      - image width                                     *
 *        Height     - image height                                    *
 *        Ncol       - number of colors                                *
 *        R[]        - red components                                  *
 *        G[]        - green components                                *
 *        B[]        - blue components                                 *
 *        ScLine[]   - array for scan line (byte per pixel)            *
 *        get_scline - user routine to read scan line:                 *
 *                       get_scline(y, Width, ScLine)                  *
 *                                                                     *
 * Return: size of PS                                                  *
 *                                                                     *
 ***********************************************************************/
static void EncodeData(Width, Height, Ncol, R, G, B, ScLine, get_scline)
                  int  Width, Height, Ncol;
                  byte R[], G[], B[], ScLine[];
                  void (*get_scline) ARGS((int, int, byte *));
{
  int    i, k, nc, x, y, disp, Code, K;
  long   CodeK, Npix;
  char   **q;
  int    FreeCode, CurCodeSize, CurMaxCode;

  long   HashTab [HSIZE];                       /* hash table */
  int    CodeTab [HSIZE];                       /* code table */ 

  /*   O U T P U T   P A L E T T E   */

  for (k=0; k<Ncol; k++) {
    sprintf(s,"%02x%02x%02x",R[k],G[k],B[k]);   put_string; 
    if (k % 10 == 9) { 
      sprintf(s,"\n");                          put_string;
    }else{
      sprintf(s," ");                           put_string;
    }
  }
  for (k=Ncol; k<256; k++) {
    sprintf(s,"000000");                        put_string; 
    if (k % 10 == 9) { 
      sprintf(s,"\n");                          put_string;
    }else{
      sprintf(s," ");                           put_string;
    }
  }
  sprintf(s,"\n");                              put_string;

  /*   L W Z   C O M P R E S S I O N   */

  PutCode(-1, 0);
  FreeCode    = CLEARCODE + 2;
  CurCodeSize = 9;
  CurMaxCode  = 511;
  memset((char *) HashTab, -1, sizeof(HashTab));
  PutCode(CLEARCODE, CurCodeSize);              /* 1st - clear code */
  Npix = 0;
  for (y=0; y<Height; y++) {
    (*get_scline)(y, Width, ScLine);
    x     = 0;
    if (y == 0) Code  = ScLine[x++];
    while(x < Width) {
      K     = ScLine[x++];                      /* next symbol */
      Npix++;
      CodeK = ((long) K << BITS) + Code;        /* set full code */
      k     = (K << SHIFT) ^ Code;              /* xor hashing */

      if (HashTab[k] == CodeK) {                /* full code found */
        Code = CodeTab[k];
        continue;
      }
      else if (HashTab[k] < 0 )                 /* empty slot */
        goto NOMATCH;

      disp  = HSIZE - k;                        /* secondary hash */
      if (k == 0) disp = 1;

PROBE:
      if ((k -= disp) < 0)
        k  += HSIZE;

      if (HashTab[k] == CodeK) {                /* full code found */
        Code = CodeTab[k];
        continue;
      }

      if (HashTab[k] > 0)                       /* try again */
        goto PROBE;

NOMATCH:                                        /* full code not found */
      PutCode(Code, CurCodeSize);
      Code = K;
      if (FreeCode == CurMaxCode) {
        CurCodeSize++;
        CurMaxCode = CurMaxCode*2 + 1;
      }

      if (CurCodeSize <= BITS && Npix <= PIXS) {
        CodeTab[k] = FreeCode++;                /* code -> hashtable */
        HashTab[k] = CodeK;
      }else{
        if (CurCodeSize > BITS) CurCodeSize = BITS;
        PutCode(CLEARCODE, CurCodeSize);
        memset((char *) HashTab, -1, sizeof(HashTab));
        FreeCode    = CLEARCODE + 2;
        CurCodeSize = 9;
        CurMaxCode  = 511;
        Npix = 0;
      }
    }
  }
   /*   O U T P U T   T H E   R E S T  */

  PutCode(Code, CurCodeSize);
  if (FreeCode == CurMaxCode && CurCodeSize != BITS)
    CurCodeSize++;
  PutCode(EOD, CurCodeSize);
  sprintf(s,"~>\n");                            put_string;  
}

/***********************************************************************
 *                                                                     *
 * Name: PS_LZWencode                                Date:    02.02.93 *
 * Author: E.Chernyaev (IHEP/Protvino)               Revised: 02.11.93 *
 *                                                                     *
 * Function: Output image in PostScript format                         *
 *           (Lelpel-Ziv Welch encoding)                               *
 *                                                                     *
 * Input: iwhat      - 0 - PostScript                           (PS)   *
 *                     1 - encapsulated PostScript              (EPS)  *
 *                     2 - encapsulated PostScript with preview (EPSI) *
 *                    10 - PostScript (Gray)                    (PS)   *
 *                    11 - encapsulated PostScript (Gray)       (EPS)  *
 *                    12 - encapsulated PostScript with preview (EPSI) *
 *        Width      - image width                                     *
 *        Height     - image height                                    *
 *        Ncol       - number of colors                                *
 *        R[]        - red components                                  *
 *        G[]        - green components                                *
 *        B[]        - blue components                                 *
 *        ScLine[]   - array for scan line (byte per pixel)            *
 *        get_scline - user routine to read scan line:                 *
 *                       get_scline(y, Width, ScLine)                  *
 *        pb         - user routine for "put_byte": pb(b)              *
 *                                                                     *
 * Return: size of PS                                                  *
 *                                                                     *
 ***********************************************************************/
long PS_LZWencode(iwhat, Width, Height, Ncol, R, G, B, ScLine, get_scline, pb)
             int  iwhat, Width, Height, Ncol;
             byte R[], G[], B[], ScLine[];
             void (*get_scline) ARGS((int, int, byte *)), (*pb) ARGS((byte));
{
  extern char ProgName[];
  extern char ProgVers[];
  extern char FileName[];

  int        ifeps, ifgray, i, nc;
  char       **q;
  time_t     clock;

  static char *HeaderPS[] = {
    "%***********************************************************************",
    "%*                                                                     *",
    "%* Function: Display a Lempel-Ziv Welch (LZW) encoded color image.     *",
    "%*           This file is suitable for any kind of PostScript printer: *",
    "%*           Level I/II, color/grayscale, format A4/A3/...             *",
    "%*           By default the image will be centered, rotated and scaled *",
    "%*           to fill the maximum space on a page.                      *",
    "%*           If you want to use this file as an Encapsulated PostScript*",
    "%*           file (for example inside a LaTeX document), then just     *",
    "%*           uncomment line '/EPSneeded true def'.                     *",
    "%*           To optimize output on GrayScale printers you may uncomment*",
    "%*           line '/GRAYneeded true def'.                              *",
    "%*                                                                     *",
    "%* Author: Evgeni CHERNYAEV (chernaev@mx.ihep.su)                      *",
    "%*                                                                     *",
    "%***********************************************************************",
    "gsave",
    "userdict begin",
    "%/EPSneeded true def",
    NULL,
  };
  static char *HeaderEPS[] = {
    "%***********************************************************************",
    "%*                                                                     *",
    "%* Function: Display a Lempel-Ziv Welch (LZW) encoded color image.     *",
    "%*           This is an Encapsulated PostScript file suitable for any  *",
    "%            kind of PostScript printer: Level I/II, color/grayscale...*",
    "%*           To optimize output on GrayScale printers you may uncomment*",
    "%*           line '/GRAYneeded true def'.                              *",
    "%*                                                                     *",
    "%* Author: Evgeni CHERNYAEV (chernaev@vxcern.cern.ch)                  *",
    "%*                                                                     *",
    "%***********************************************************************",
    "gsave",
    "userdict begin",
    "/EPSneeded true def",
    NULL,
  };
  static char *HeaderCol[] = {
    "%/GRAYneeded true def",
    "end",
    "%***********************************************************************",
    "%*                              Image Description                      *",
    "%***********************************************************************",
    NULL,
  };
  static char *HeaderGray[] = {
    "/GRAYneeded true def",
    "end",
    "%***********************************************************************",
    "%*                              Image Description                      *",
    "%***********************************************************************",
    NULL,
  };
  static char *CommonPart[] = {
    "/mm {2.835 mul} def",
    "userdict /EPSneeded known {",
    "  /EPSneeded userdict /EPSneeded get def",
    "}{",
    "  /EPSneeded false def",
    "} ifelse",
    "userdict /GRAYneeded known {",
    "  /GRAYneeded userdict /GRAYneeded get def",
    "}{",
    "  /GRAYneeded false def",
    "} ifelse",
    "EPSneeded {",
    "  /IfRotate false def",
    "  /MarginX 0 def /MarginY 0 def",
    "  /Ymax rows def /Xmax colomns def /Ymin 0 def /Xmin 0 def",
    "}{",
    "  /IfRotate colomns rows gt {true} {false} ifelse def",
    "  /MarginX 5 mm def /MarginY 5 mm def",
    "  0 0 moveto clippath pathbbox",
    "  /Ymax exch def /Xmax exch def /Ymin exch def /Xmin exch def",
    "} ifelse",
    "/IfColor systemdict /colorimage known {true} {false} ifelse def",
    "GRAYneeded {/IfColor false def} if",
    "/IfLevel2 systemdict /filter known systemdict /setcolorspace known and",
    "  {true} {false} ifelse def",
    "%***********************************************************************",
    "%                               Procedures                             *",
    "%***********************************************************************",
    "/Table 4096 array def",
    "/StrArray 10 array def",
    "/InLine 70 string def",
    "/BinCodes 112 string def",
    "/CurBit 0 def /FreeCode 258 def /CurCodeSize 9 def /CurMask 511 def",
    "/incr IfColor {3} {1} ifelse def",
    "IfLevel2 not {/setcolorspace null def /filter null def} if",
    "%***********************************************************************",
    "/ReadLine {                     % Read Next Data Line (ASCII85 decode) *",
    "%***********************************************************************",
    "  BinCodes 0 BinCodes 56 56 getinterval putinterval",
    "  currentfile InLine readline pop pop",
    "  1 1 14 {",
    "    /i exch 1 sub def",
    "    /k i 5 mul def",
    "    /c1 InLine k get 33 sub 255 and def",
    "    /rest c1 1868977 mul",
    "      InLine k 1 add get 33 sub 255 and 614125 mul add",
    "      InLine k 2 add get 33 sub 255 and 7225 mul add",
    "      InLine k 3 add get 33 sub 255 and 85 mul add",
    "      InLine k 4 add get 33 sub 255 and add def",
    "    /k i 4 mul 56 add def",
    "    BinCodes k c1 3 mul rest 16777216 idiv add 255 and put",
    "    /rest rest 16777216 mod def",
    "    BinCodes k 1 add rest 65536 idiv put",
    "    BinCodes k 2 add rest 256 idiv 255 and put",
    "    BinCodes k 3 add rest 255 and put",
    "  } for",
    "} bind def",
    "%***********************************************************************",
    "/ReadCode {                     % Read next code                       *",
    "%***********************************************************************",
    "  /CurByte CurBit 8 idiv def",
    "  /CurCode",
    "    BinCodes CurByte get 8 bitshift",
    "    BinCodes CurByte 1 add get add 8 bitshift",
    "    BinCodes CurByte 2 add get add",
    "    CurCodeSize CurBit 7 and add 24 sub bitshift CurMask and def",
    "  /CurBit CurBit CurCodeSize add dup 448 ge {ReadLine 448 sub} if def",
    "} bind def",
    "%***********************************************************************",
    "/DecodeCode {                   % Decode CurCode                       *",
    "%***********************************************************************",
    "  ReadCode CurCode 256 eq {",
    "    /FreeCode 258 def /CurCodeSize 9 def /CurMask 511 def",
    "    /StrInd -1 def /Lrest 0 def ReadCode",
    "  }{",
    "    L Lrest gt {",
    "      /StrInd StrInd 1 add def /Lrest 65535 def",
    "      StrArray StrInd get null eq {StrArray StrInd 65535 string put} if",
    "      /CurStr StrArray StrInd get def",
    "    } if",
    "    Table FreeCode CurStr 65535 Lrest sub L getinterval",
    "    dup 0 Table OldCode get putinterval",
    "    dup L incr sub Table",
    "      CurCode FreeCode lt {CurCode} {OldCode} ifelse get",
    "    0 incr getinterval putinterval put",
    "    /Lrest Lrest L sub def /FreeCode FreeCode 1 add def",
    "    FreeCode CurMask ge CurCodeSize 12 lt and {",
    "      /CurCodeSize CurCodeSize 1 add def",
    "      /CurMask CurMask 1 bitshift 1 add def",
    "    } if",
    "  } ifelse",
    "  /OldCode CurCode def",
    "  Table CurCode get dup length incr add /L exch def",
    "} bind def",
    "%***********************************************************************",
    "/DisplayImage {          % Display a LZW-encoded color image           *",
    "%***********************************************************************",
    "  /DelX Xmax Xmin sub MarginX 2 mul sub def",
    "  /DelY Ymax Ymin sub MarginY 2 mul sub def",
    "  /SizeX IfRotate {rows} {colomns} ifelse def",
    "  /SizeY IfRotate {colomns} {rows} ifelse def",
    "  /FactorX DelX SizeX div def /FactorY DelY SizeY div def",
    "  /Factor FactorX FactorY le {FactorX} {FactorY} ifelse def",
    "  /ScaleX SizeX Factor mul def /ScaleY SizeY Factor mul def",
    "  DelX ScaleX sub 2 div MarginX add",
    "  DelY ScaleY sub 2 div MarginY add translate",
    "  IfRotate {ScaleY ScaleX} {ScaleX ScaleY} ifelse scale",
    "  /Palette currentfile 768 string readhexstring pop def",
    "  currentfile InLine readline pop pop",
    "  IfColor not {",
    "    0 1 255 {",
    "      Palette exch dup /i exch 3 mul def",
    "      Palette i 0 add get 0.299 mul",
    "      Palette i 1 add get 0.587 mul add",
    "      Palette i 2 add get 0.114 mul add cvi put",
    "    } for",
    "    /Palette Palette 0 256 getinterval def", 
    "  } if",
    "  /Matr IfRotate",
    "    {[0 colomns rows 0 0 0]} {[colomns 0 0 rows neg 0 rows]} ifelse def",
    "  IfLevel2 {",
    "    [/Indexed IfColor {/DeviceRGB} {/DeviceGray} ifelse 255 Palette]",
    "    setcolorspace",
    "    /infile currentfile /ASCII85Decode filter /LZWDecode filter def",
    "    8 dict",
    "    dup /ImageType 1 put", 
    "    dup /Width colomns put",
    "    dup /Height rows put",
    "    dup /BitsPerComponent 8 put",
    "    dup /ImageMatrix Matr put",
    "    dup /Interpolate false put",
    "    dup /Decode [0 255] put",
    "    dup /DataSource infile put image",
    "  }{",
    "    0 1 255 {",
    "      Table exch dup incr mul Palette exch incr getinterval put",
    "    } for",
    "    ReadLine ReadLine",
    "    colomns rows 8 Matr {DecodeCode}",
    "    IfColor {false 3 colorimage} {image} ifelse",
    "  } ifelse",
    "} bind def",
    "%***********************************************************************",
    "%*                              Image decoding                         *",
    "%***********************************************************************",
    "DisplayImage",
    NULL,
  };

  /*   C H E C K   P A R A M E T E R S   */
  
  if (Width <= 0 || Width > MAXWIDTH || Height <= 0 || Height > MAXWIDTH) {
    fprintf(stderr,
      "\n%s: incorrect image size: %d x %d\n", ProgName, Width, Height);
    return 0;
  }

  if (Ncol <= 0 || Ncol > 256) {
    fprintf(stderr,"\n%s: wrong number of colors: %d\n", ProgName, Ncol);
    return 0;
  }

  /*   I N I T I A L I S A T I O N   */

  if (iwhat < 10)
    ifeps = iwhat;
  else
    ifeps = iwhat - 10;         
  put_b  = pb;
  Nbyte  = 0;

  /*   O U T P U T   H E A D E R   */

  sprintf(s,"%%!PS-Adobe-2.0 EPSF-2.0\n");                      put_string;
  sprintf(s,"%%%%Title: %s\n", FileName);                       put_string;
  sprintf(s,"%%%%Creator: %s %s\n", ProgName, ProgVers);        put_string;
  sprintf(s,"%%%%CreationDate: %s",(time(&clock),ctime(&clock))); put_string;
  sprintf(s,"%%%%BoundingBox: 0 0 %d %d\n", Width, Height);     put_string;
  sprintf(s,"%%%%EndComments\n");                               put_string;

  /*   O U T P U T   P R E V I E W   */

  if (ifeps == 2)
    PutPreview(Width, Height, R, G, B, ScLine, get_scline);

  /*   O U T P U T   P O S T S C R I P T   P R O G R A M M   */

  if (ifeps == 0)
    q = HeaderPS;
  else
    q = HeaderEPS;
  for ( ; *q; q++) {
    sprintf(s,"%s\n",*q);                               put_string;
  }
  if (iwhat < 10)
    q = HeaderCol;
  else
    q = HeaderGray;
  for ( ; *q; q++) {
    sprintf(s,"%s\n",*q);                               put_string;
  }

  sprintf(s,"/colomns %d def\n",Width);                 put_string;
  sprintf(s,"/rows %d def\n",Height);                   put_string;
  for (q=CommonPart; *q; q++) {
    sprintf(s,"%s\n",*q);                               put_string;
  }

  /*   O U T P U T   E N C O D E D   D A T A   */

  EncodeData(Width, Height, Ncol, R, G, B, ScLine, get_scline);

  sprintf(s,"showpage grestore\n");                     put_string;
  sprintf(s,"%%%%Trailer\n");                           put_string;
  return (Nbyte);
}
