Logo Search packages:      
Sourcecode: c2esp version File versions  Download package

c2esp.c

/* 
 *
 *   Kodak ESP 5xxx (OPL?) Control Language filter for the Common UNIX
 *   Printing System (CUPS).
 *
 *  copyright Paul Newall May 2010 - Mar2011. VERSION 1.8 (c2esp18) WITH marker levels and proper termination of the jbig data
 *
 *  Params: job-id user title copies options [file]
 *  options = "noback" disables all calls to the back channel for testing.
 *
 
This filter is based on:
the structure of the rastertohp filter supplied with cups
the JBIG library by Markus Kuhn

 */
#define DEBUGFILES 0
#define TESTING 0

//#include <cups/string.h> //appears to not be needed
//#include <cups/cups.h> //appears to not be needed
//#include <stdlib.h> //appears to not be needed
//#include <unistd.h> //appears to not be needed

#include <cups/raster.h>
#include <cups/sidechannel.h> //FlushBackChannel, and the side channel functions and constants
#include <fcntl.h> //files
#include <sys/stat.h>
#include <signal.h>
#include <errno.h>
#include "jbig85.h" //the reduced jbig library
#include <cups/driver.h> //has the dither functions
//#include "string.h" //for memcpy
#include <time.h> //time funtions used for debugging

//constants
unsigned char ESC = 255;
int StripeHeightMax = 1280; //the max height of a stripe. (Windows 300x1200 files have 1920)

/*
 * Globals...
 */
char  *Version = "c2esp17";
unsigned char     *RasSpace;        /* Output buffer with space before start for previous lines */
unsigned char     *RasForComp;            /* Output buffer */
unsigned char     *CupsLineBuffer;  //buffer for one line of the cups raster
unsigned char     *DitherOutputBuffer;    //buffer for output of the dither
short       *DitherInputBuffer;     //buffer for input to the dither 
unsigned char     *CompStripeBuffer;      //buffer for one compressed stripe
char        KodakPaperSize[50];     /* String that the printer expects for paper size */
int         CompStripeBufferFree,   //free pointer for buffer
//          NumPlanes,        /* Number of color planes */
            OutBitsPerPixel,  /* Number of bits per color per pixel for printer*/
            RasForCompWidth,        //width of raster that is compessed and sent to printer
//          Feed,             /* Number of lines to skip */
            Duplex,                 /* Current duplex mode */
            Page,             /* Current page number */
            Canceled,         /* Has the current job been canceled? */
            DoBack;                 /* Enables the back channel comms */ 
//int             SkipStripe[32]; //=1 for each blank stripe that will be skipped
int         SkipStripe; //=1 for each blank stripe that will be skipped
int         MbUsed = 0; //tracks memory use
long        BytesOutCountSingle;//tracks total no of compressed bytes output by jbig library for single stripe system
char        BackBuf[32000]; //for the back channel replies from the printer
int         BackBufLen=sizeof(BackBuf)-1;
time_t            TimeStart; //to record the start of a section
FILE        *PrintFile = NULL; //file descriptor for debug file
FILE        *dfp = NULL; //file descriptor for composite raster file
FILE        *Cyanfp = NULL; //file descriptor for cyan only raster file
FILE        *Magentafp = NULL; //file descriptor for magenta only raster file
FILE        *Yellowfp = NULL; //file descriptor for yellow only raster file
FILE        *Blackfp = NULL; //file descriptor for black only raster file
FILE        *LogFile = NULL; //file descriptor for log file
FILE        *RawColourFile = NULL; //file descriptor for cyan input to dither
FILE        *DitheredColourFile = NULL; //file descriptor for cyan input to dither
const float default_lut[3] = {0.0, 0.7, 1.0}; //default {0.0, 0.5, 1.0}, {0.0, 0.7, 1.0} SEEMS TO WORK NICELY
FILE        *JobFile;

time_t            StartTime;
time_t            KeepAwakeStart;
struct jbg85_enc_state JbigState;

/* DoLog used during development */
void DoLog(char *PrintFormat, int I1, int I2)
{
      //prints a line with 2 integers to the log file
char LogFormat[100];
      strcpy(LogFormat, "%d : ");
      strcat(LogFormat,PrintFormat);
      fprintf(LogFile, LogFormat, time(NULL)-StartTime, I1, I2);
}
void DoLogString(char *PrintFormat, char *String)
{
      //prints a line with a string to the log file
char LogFormat[100];
      strcpy(LogFormat, "%d : ");
      strcat(LogFormat,PrintFormat);
      fprintf(LogFile, LogFormat, time(NULL)-StartTime, String);
}

/* DoOutJob used to enable one call to send to the job file */
void DoOutJob(FILE *OutFile, char *PrintFormat, int I1, int I2)
{
      fprintf(OutFile, PrintFormat, I1, I2); //and to the specified file
      fprintf(stdout, PrintFormat, I1, I2); //and to the output
      KeepAwakeStart = time(NULL); // reset timer
}

/* DoOut used to enable one call to send to output and print file */
void DoOut(FILE *OutFile, char *PrintFormat, int I1, int I2)
{
      if(PrintFile) fprintf(PrintFile, PrintFormat, I1, I2); //to the global print file
#if TESTING == 0
      fprintf(OutFile, PrintFormat, I1, I2); //and to the specified file
#endif
}

/* DoOutSend used to enable one call to send to output and print file (forces a packet to send)*/
void DoOutSend(FILE *OutFile, char *PrintFormat, int I1, int I2)
{
      if(PrintFile) fprintf(PrintFile, PrintFormat, I1, I2); //to the global print file
#if TESTING == 0
      fprintf(OutFile, PrintFormat, I1, I2); //and to the specified file
      fflush(OutFile);
      sleep(2); //try shortening
#endif
}

/* FlushBackChannel gets rid of any previous reply that could cause confusion */
int FlushBackChannel(char *IdString, float DrainTime)
{
//returns 1 if sucessful
      cups_sc_status_t status;
      char BackBuf[2]; //useless buffer to satisfy cupsSideChannelDoRequest
      int BackBufLen=sizeof(BackBuf)-1;
      if(DoBack)
      {
            status = cupsSideChannelDoRequest(CUPS_SC_CMD_DRAIN_OUTPUT, BackBuf, &BackBufLen, DrainTime);
            if(status == CUPS_SC_STATUS_OK) 
            {
                  fprintf(stderr, "DEBUG: c2esp:<did DRAIN_OUTPUT %s>\n", IdString);
                  return(1);
            }
            else
            {
                  if(status == CUPS_SC_STATUS_TIMEOUT) fprintf(stderr, "DEBUG: c2esp:<Failed DRAIN_OUTPUT %s = Timeout>\n", IdString);
                  else if(status == CUPS_SC_STATUS_IO_ERROR) fprintf(stderr, "DEBUG: c2esp:<Failed DRAIN_OUTPUT %s = IO error>\n", IdString);
                  else if(status == CUPS_SC_STATUS_NOT_IMPLEMENTED) fprintf(stderr, "DEBUG: c2esp:<Failed DRAIN_OUTPUT %s = not implemented>\n", IdString);
                  else  fprintf(stderr, "DEBUG: c2esp:<Failed DRAIN_OUTPUT %s = unknown reason>\n", IdString);
                  return(0);
            }
      }
      else return(0);
}

/* GoodExchange sends a command gets reply from the printer on the back channel and compares it with the expected reply
      It returns the number of bytes read if the reply was the one expected, 
      otherwise -(the number of bytes read) if the reply did not match Expect, or 0 if there was no reply */
int GoodExchange(char *Command, char *Expect, int DoBack, char *BackBuf, int BackBufLen, unsigned int SleepTime, float ReplyTime)
{
      int BytesRead = 0; //int because cupsBackChannel can return -1
      char Display[60];
      int i;

      fprintf(stderr, "c2esp: Sent command = %s\n", Command);
      DoLogString("Sent command = %s\n", Command);
      if(PrintFile) fprintf(PrintFile, "%s", Command); //to the global print file
#if TESTING == 0
      fprintf(stdout, "%s", Command); //printer command
#endif
      fflush(stdout); //force a packet to the printer so it can reply
      sleep(SleepTime); //give it a chance to reply before trying to read the reply (may not be needed)

      if(DoBack)
      {
      BytesRead = cupsBackChannelRead(BackBuf, BackBufLen, ReplyTime); //read the reply from printer
      if(BytesRead >= 1) BackBuf[BytesRead]=0; //add null terminator NB BytesRead==-1 if nothing read
      for(i=0;i<59;++i) Display[i] = BackBuf[i]; //copy the first 59 chars to Display
      Display[59] = 0; //add null terminator
      fprintf(stderr, "DEBUG: c2esp: Got %d byte reply = %s\n", BytesRead, Display);
      DoLog("Got %d byte reply\n", BytesRead, 0);
            if(strncmp(BackBuf, Expect, strlen(Expect)) != 0) 
            {
                  fprintf(stderr, "DEBUG: c2esp: wrong reply = %s\n", Display);
                  DoLogString("Unexpected reply = %s\n", Display);
                  return(-1*BytesRead);
            }
            else DoLogString("Reply = %s\n", Display);
      }
      return(BytesRead);
}

int
MarkerPercent(char *Buf, int GetColour) /* GetColour = 1 for "Color" or 0 for "Black" */
{
      char *MarkerLevelString;
      
            if(GetColour) MarkerLevelString = strstr(Buf, "DeviceStatus.Printer.InkLevelPercent.Color=");
            else MarkerLevelString = strstr(Buf, "DeviceStatus.Printer.InkLevelPercent.Black=");
            MarkerLevelString = strstr(MarkerLevelString, "=")+1;
            if(strncmp(MarkerLevelString,"F",1)==0) return (100);
            else return (atoi(MarkerLevelString));
}

void
SetupPrinter()
{
//gets the printer ready to start the job
      int BlackPercent, ColourPercent, i;
      int StatusLength;
//    char *ColourLevelString;
//    char *BlackLevelString;

      if(DoBack) FlushBackChannel("previous", 3.0);
      for(i=0; i<4; ++i)
      {
            if(GoodExchange("LockPrinter?", "0002, OK, Locked for printing;", DoBack, BackBuf,  BackBufLen, 1,  3.0)) break;
      }
      DoOutSend(stdout, "Event=StartOfJob;",0,0); //printer command

      // color levels might get sent here, but not always
      FlushBackChannel("colours", 3.0);

      StatusLength=abs(GoodExchange("DeviceStatus?", "0101,DeviceStatus.ImageDevice", DoBack, BackBuf,  BackBufLen,  1,  1.0));
      DoLog("StatusLength=%d\n",StatusLength,0);
/* you can get unexpected reply if there is an ink low warning then GoodExchange will be -ve */
//aquire ink levels here? DeviceStatus.Printer.InkLevelPercent.Colour=nn%&DeviceStatus.Printer.InkLevelPercent.Black=nn%
//note & used as separator
      if(StatusLength>0)
      {
            /* search for the ink data in the buffer using char *strstr(char *string2, char string*1);
MAKE THIS INTO A GENERAL FUNCTION FOR EITHER CARTRIDGE LATER */
/*
            ColourLevelString = strstr(BackBuf, "DeviceStatus.Printer.InkLevelPercent.Color=");
            BlackLevelString = strstr(BackBuf, "DeviceStatus.Printer.InkLevelPercent.Black=");
            //DoLogString("ColourLevelString=%s\n",ColourLevelString);
            //DoLogString("BlackLevelString=%s\n",BlackLevelString);
            ColourLevelString = strstr(ColourLevelString, "=")+1;
            BlackLevelString = strstr(BlackLevelString, "=")+1;
            if(strncmp(ColourLevelString,"F",1)==0) ColourPercent = 100;
            else
            {
            ColourPercent = atoi(ColourLevelString);
            }
            if(strncmp(BlackLevelString,"F",1)==0) BlackPercent = 100;
            else
            {
            BlackPercent = atoi(BlackLevelString);
            }           
            DoLog("ColourPercent=%d\n",ColourPercent,0);
            DoLog("BlackPercent=%d\n",BlackPercent,0);
*/
            ColourPercent = MarkerPercent(BackBuf,1);
            BlackPercent = MarkerPercent(BackBuf,0);

            DoLog("ColourPercent=%d\n",ColourPercent,0);
            DoLog("BlackPercent=%d\n",BlackPercent,0);

            fprintf(stderr,"ATTR: marker-levels=%d,%d\n",BlackPercent,ColourPercent); // sets the levels displayed in printer manager
      }

      GoodExchange("DeviceSettings.System?", "0101,DeviceSettings.System", DoBack, BackBuf,  BackBufLen,  1,  1.0);
      GoodExchange("DeviceSettings?", "0101,DeviceSettings.AddressBook", DoBack, BackBuf,  BackBufLen,  1,  1.0);
      DoOutSend(stdout, KodakPaperSize,0,0);
      if(GoodExchange("MediaInputTrayCheck=Main;MediaTypeStatus?", "MediaTypeStatus=custom-media-type-deviceunavailable", DoBack, BackBuf,  BackBufLen,  1,  1.0))
      {


            GoodExchange("MediaDetect?", "0098, OK, Media Detect Started;", DoBack, BackBuf,  BackBufLen,  1,  1.0);
            //do MediaTypeStatus? until some media is found
            for(i=0; i<15; ++i)
            {
                  DoLog("MediaTypeStatus? try %d\n", i, 0);
                  if(! GoodExchange("MediaTypeStatus?", "MediaTypeStatus=custom-media-type-deviceunavailable", DoBack, BackBuf,  BackBufLen,  2,  2.0)) break;
            }
      }
}

void
ShutdownPrinter(void)
{
      int i;
      int BytesReplied;

      DoOut(stdout, "Event=EndOfJob;",0,0);
      for(i=0; i<20; ++i) /* fast PC might need lots of tries here for printer to finish, how many is reasonable? */
      {
            DoLog("UnlockPrinter? try %d\n", i, 0);
            BytesReplied = GoodExchange("UnlockPrinter?", "0003, OK, Printer unlocked;", DoBack, BackBuf,  BackBufLen,  5,  2.0);
            if(BytesReplied > 0) break;
      }
}

void
SetupJob(cups_page_header2_t *header) //Prepare the printer for printing.
{
      DoLog("Called SetupJob(*header);\n",0,0);
      DoOutJob(JobFile, "OutputBin=MainSink;",0,0);

      Duplex = header->Duplex;
      if(Duplex == 0) DoOutJob(JobFile, "Sides=OneSided;",0,0);
      else  DoOutJob(JobFile, "Sides=TwoSided;",0,0);

      //fprintf(JobFile, "Copies=1;NUp=0;"); //these two seem to cause an error response, but are sent by windows

      DoOutJob(JobFile, "MediaType=custom-media-type-autoselection-0-0-0-0;",0,0);
}

void SetPaperSize(char Size[], int PaperPoints)
{
    //converts length of page in cups header (in points) into a string that the printer recognises

      strcpy(Size, "MediaSize=na_letter_8.5x11in;"); //default

    switch (PaperPoints)
    {
      case 432 : // Photo 4x6" 
            strcpy(Size, "MediaSize=na_index4x6_4x6in;");
        break;
      case 540 : // Monarch Envelope 
        break;
      case 595 : // A5 
            strcpy(Size, "MediaSize=iso_a5_148x210mm;");
        break;
      case 624 : // DL Envelope
        break;
      case 649 : // C5 Envelope
        break;
      case 684 : // COM-10 Envelope 
        break;
      case 709 : // B5 Envelope 
        break;
      case 756 : // Executive
        break;
      case 792 : // Letter 
            strcpy(Size, "MediaSize=na_letter_8.5x11in;");
        break;
      case 842 : // A4 
            strcpy(Size, "MediaSize=iso_a4_210x297mm;");
        break;
      case 1008 : // Legal
        break;
      case 1191 : // A3 
        break;
      case 1224 : // Tabloid 
        break;
    }
}

void
DisplayHeader(FILE *OutFile, cups_page_header2_t *header)
{
 /*
  * Show page device dictionary...
  */

  fprintf(OutFile, "DEBUG: StartPage...\n");
  fprintf(OutFile, "DEBUG: MediaClass = \"%s\"\n", header->MediaClass);
  fprintf(OutFile, "DEBUG: MediaColor = \"%s\"\n", header->MediaColor);
  fprintf(OutFile, "DEBUG: MediaType = \"%s\"\n", header->MediaType);
  fprintf(OutFile, "DEBUG: OutputType = \"%s\"\n", header->OutputType);
  fprintf(OutFile, "DEBUG: AdvanceDistance = %d\n", header->AdvanceDistance);
  fprintf(OutFile, "DEBUG: AdvanceMedia = %d\n", header->AdvanceMedia);
  fprintf(OutFile, "DEBUG: Collate = %d\n", header->Collate);
  fprintf(OutFile, "DEBUG: CutMedia = %d\n", header->CutMedia);
  fprintf(OutFile, "DEBUG: Duplex = %d\n", header->Duplex);
  fprintf(OutFile, "DEBUG: HWResolution = [ %d %d ]\n", header->HWResolution[0], header->HWResolution[1]);
  fprintf(OutFile, "DEBUG: ImagingBoundingBox = [ %d %d %d %d ]\n",
          header->ImagingBoundingBox[0], header->ImagingBoundingBox[1],
          header->ImagingBoundingBox[2], header->ImagingBoundingBox[3]);
  fprintf(OutFile, "DEBUG: InsertSheet = %d\n", header->InsertSheet);
  fprintf(OutFile, "DEBUG: Jog = %d\n", header->Jog);
  fprintf(OutFile, "DEBUG: LeadingEdge = %d\n", header->LeadingEdge);
  fprintf(OutFile, "DEBUG: Margins = [ %d %d ]\n", header->Margins[0], header->Margins[1]);
  fprintf(OutFile, "DEBUG: ManualFeed = %d\n", header->ManualFeed);
  fprintf(OutFile, "DEBUG: MediaPosition = %d\n", header->MediaPosition);
  fprintf(OutFile, "DEBUG: MediaWeight = %d\n", header->MediaWeight);
  fprintf(OutFile, "DEBUG: MirrorPrint = %d\n", header->MirrorPrint);
  fprintf(OutFile, "DEBUG: NegativePrint = %d\n", header->NegativePrint);
  fprintf(OutFile, "DEBUG: NumCopies = %d\n", header->NumCopies);
  fprintf(OutFile, "DEBUG: Orientation = %d\n", header->Orientation);
  fprintf(OutFile, "DEBUG: OutputFaceUp = %d\n", header->OutputFaceUp);
  fprintf(OutFile, "DEBUG: PageSize = [ %d %d ]\n", header->PageSize[0], header->PageSize[1]);
  fprintf(OutFile, "DEBUG: Separations = %d\n", header->Separations);
  fprintf(OutFile, "DEBUG: TraySwitch = %d\n", header->TraySwitch);
  fprintf(OutFile, "DEBUG: Tumble = %d\n", header->Tumble);
  fprintf(OutFile, "DEBUG: cupsWidth = %d\n", header->cupsWidth);
  fprintf(OutFile, "DEBUG: cupsHeight = %d\n", header->cupsHeight);
  fprintf(OutFile, "DEBUG: cupsMediaType = %d\n", header->cupsMediaType);
  fprintf(OutFile, "DEBUG: cupsBitsPerColor = %d\n", header->cupsBitsPerColor);
  fprintf(OutFile, "DEBUG: cupsBitsPerPixel = %d\n", header->cupsBitsPerPixel);
  fprintf(OutFile, "DEBUG: cupsBytesPerLine = %d\n", header->cupsBytesPerLine);
  fprintf(OutFile, "DEBUG: cupsColorOrder = %d\n", header->cupsColorOrder);
  fprintf(OutFile, "DEBUG: cupsColorSpace = %d\n", header->cupsColorSpace);
  fprintf(OutFile, "DEBUG: cupsCompression = %d\n", header->cupsCompression);
}

void
AllocateBuffers(cups_page_header2_t *header)
{
      int i,   RasForCompSize;

 // Allocate memory for a page of graphics... 
// This printer has a watermark, which appears in the raster tacked onto the RHS of the printing raster.
      RasForCompSize = RasForCompWidth * StripeHeightMax;
      if ((RasSpace = malloc(RasForCompSize+RasForCompWidth*2*sizeof(unsigned char))) == NULL) //2 extra lines
      {
            DoLog("ERROR: Unable to allocate %d bytes for RasSpace!\n",RasForCompSize,0);
            fputs("ERROR: Unable to allocate memory!\n", stderr);
            exit(1);
      }
      else 
      {
            MbUsed = MbUsed + (RasForCompSize+RasForCompWidth*2) * 1E-6;
            RasForComp = RasSpace + (RasForCompWidth*2); //points to the 3rd line, so 2 lines below RasForComp[0] can be used
            // Clear the page, so the watermark is blank
            for(i=0;i<RasForCompSize-1;++i)  RasForComp[i] = 0;
      }
      if ((CupsLineBuffer = malloc(header->cupsBytesPerLine)) == NULL) 
      {
            DoLog("ERROR: Unable to allocate %d bytes for CupsLineBuffer!\n",header->cupsBytesPerLine,0);
            fputs("ERROR: Unable to allocate memory!\n", stderr);
            exit(1);
      }
      else MbUsed = MbUsed + header->cupsBytesPerLine * 1E-6;
      if ((DitherInputBuffer = malloc(header->cupsWidth*2)) == NULL) 
      {
            DoLog("ERROR: Unable to allocate %d bytes for DitherInputBuffer!\n",header->cupsWidth*2, 0); //assume a short is 2 bytes
            fputs("ERROR: Unable to allocate memory!\n", stderr);
            exit(1);
      }
      else MbUsed = MbUsed + header->cupsWidth * 2 * 1E-6;
      if ((DitherOutputBuffer = malloc(header->cupsWidth)) == NULL) 
      {
            DoLog("ERROR: Unable to allocate %d bytes for DitherOutputBuffer!\n",header->cupsWidth, 0); 
            fputs("ERROR: Unable to allocate memory!\n", stderr);
            exit(1);
      }
      else MbUsed = MbUsed + header->cupsWidth * 1E-6;
      if ((CompStripeBuffer = malloc(RasForCompWidth * StripeHeightMax)) == NULL) 
      {
            DoLog("ERROR: Unable to allocate %d bytes for CompStripeBuffer!\n",RasForCompWidth * StripeHeightMax, 0); 
            fputs("ERROR: Unable to allocate memory!\n", stderr);
            exit(1);
      }
      else MbUsed = MbUsed + RasForCompWidth * StripeHeightMax * 1E-6;
      DoLog("Buffers allocated %d Mb\n",MbUsed,0);
}

void
StartPrinterPage(cups_page_header2_t *header)
{
      int  ResX, ResY;

      fprintf(stderr, "DEBUG: c2esp: StartPage\n");
      DisplayHeader(stderr, header);
      DisplayHeader(LogFile, header);
      ResX = header->HWResolution[0];
      ResY = header->HWResolution[1];

      DoOutJob(JobFile, KodakPaperSize,0,0);
      DoLog(KodakPaperSize,0,0);
      DoLog("\n",0,0);

      DoOutJob(JobFile, "Event=StartOfPage;",0,0);
//    DoOutJob(JobFile, "Origin.Top=5.0mm;Origin.Left=5.0mm;",0,0);
      DoOutJob(JobFile, "Origin.Top=1.0mm;Origin.Left=1.0mm;",0,0);
      if(ResX==300)
      {
            DoOutJob(JobFile, "PrintQuality=0;",0,0);
            if (OutBitsPerPixel==2) DoOutJob(JobFile, "PrintSpeed=3;",0,0);
            else  DoOutJob(JobFile, "PrintSpeed=1;",0,0);
      }
      else if(ResX==600)
      {
            DoOutJob(JobFile, "PrintQuality=4096;",0,0);
            if (OutBitsPerPixel==2) DoOutJob(JobFile, "PrintSpeed=3;",0,0); //check
            else  DoOutJob(JobFile, "PrintSpeed=1;",0,0);
      }
      else if(ResX==1200)
      {
            DoOutJob(JobFile, "PrintQuality=8192;",0,0);
            if (OutBitsPerPixel==2) DoOutJob(JobFile, "PrintSpeed=3;",0,0); //check
            else  DoOutJob(JobFile, "PrintSpeed=4;",0,0);
      }
      DoOutJob(JobFile, "Resolution=%dx%d;", ResX, ResY);
      DoOutJob(JobFile, "RasterObject.BitsPerPixel=%d;",OutBitsPerPixel,0);

      if (header->cupsColorSpace == CUPS_CSPACE_CMYK)
      {
            DoOutJob(JobFile, "RasterObject.Planes=00FFFF,1P0000&FF00FF,1P0000&FFFF00,2P0000&000000,2T0000&000000,1P0000;",0,0); //for colour
            DoLog("CUPS_CSPACE_CMYK (%d)\n",header->cupsColorSpace,0);
      }
      else if      (header->cupsColorSpace == CUPS_CSPACE_K)
      {
            DoOutJob(JobFile, "RasterObject.Planes=000000,2T0000&000000,1P0000;",0,0); //for mono
            DoLog("CUPS_CSPACE_K  (%d)\n",header->cupsColorSpace,0);
      }
      else  
      {
            DoOutJob(JobFile, "RasterObject.Planes=000000,2T0000&000000,1P0000;",0,0); //for mono
            DoLog("CUPS_CSPACE_??  (%d)\n",header->cupsColorSpace,0);
      }

      DoOutJob(JobFile, "RasterObject.Compression=JBIG;",0,0);
      DoOutJob(JobFile, "RasterObject.Width=%d;", header->cupsWidth,0);
      DoOutJob(JobFile, "RasterObject.Height=%d;",  header->cupsHeight,0);
}

void
EndPage(void) //Finish a page of graphics.
{
      //fflush(stdout);
      //sleep(3); //does this give time for the packet to register?
      //GoodExchange("Event=EndOfPage;", "3001, OK, Page Ejected", DoBack, BackBuf,  BackBufLen,  1,  1.0);
      DoOutJob(JobFile, "Event=EndOfPage;",  0,0);

      /* Free memory... allocated by AllocateBuffers() */
//    free(RasForComp);
      free(RasSpace);
      free(CupsLineBuffer);
      free(DitherInputBuffer);
      free(DitherOutputBuffer);
      free(CompStripeBuffer);
      MbUsed = 0;
      DoLog("Buffers freed\n",0,0);
}

void
CancelJob(int sig)      /* - Cancel the current job... I - Signal */
{
  (void)sig;
  DoLog("CancelJob: job cancelled by signal\n",0,0);
  Canceled = 1;
}

//look up table to map bit pair 11 to 10, 10 to 01, 01 to 01, 00 to 00
//translates 2 bit per pixel colours to printer data
unsigned char     Map11To10And10To01[256] =
{
0, 1, 1, 2, 4, 5, 5, 6, 4, 5, 5, 6, 8, 9, 9, 10, 
16, 17, 17, 18, 20, 21, 21, 22, 20, 21, 21, 22, 24, 25, 25, 26, 
16, 17, 17, 18, 20, 21, 21, 22, 20, 21, 21, 22, 24, 25, 25, 26, 
32, 33, 33, 34, 36, 37, 37, 38, 36, 37, 37, 38, 40, 41, 41, 42, 
64, 65, 65, 66, 68, 69, 69, 70, 68, 69, 69, 70, 72, 73, 73, 74, 
80, 81, 81, 82, 84, 85, 85, 86, 84, 85, 85, 86, 88, 89, 89, 90, 
80, 81, 81, 82, 84, 85, 85, 86, 84, 85, 85, 86, 88, 89, 89, 90, 
96, 97, 97, 98, 100, 101, 101, 102, 100, 101, 101, 102, 104, 105, 105, 106, 
64, 65, 65, 66, 68, 69, 69, 70, 68, 69, 69, 70, 72, 73, 73, 74, 
80, 81, 81, 82, 84, 85, 85, 86, 84, 85, 85, 86, 88, 89, 89, 90, 
80, 81, 81, 82, 84, 85, 85, 86, 84, 85, 85, 86, 88, 89, 89, 90, 
96, 97, 97, 98, 100, 101, 101, 102, 100, 101, 101, 102, 104, 105, 105, 106, 
128, 129, 129, 130, 132, 133, 133, 134, 132, 133, 133, 134, 136, 137, 137, 138, 
144, 145, 145, 146, 148, 149, 149, 150, 148, 149, 149, 150, 152, 153, 153, 154, 
144, 145, 145, 146, 148, 149, 149, 150, 148, 149, 149, 150, 152, 153, 153, 154, 
160, 161, 161, 162, 164, 165, 165, 166, 164, 165, 165, 166, 168, 169, 169, 170, 
};

void MapDotsInByte(unsigned char *Byte)
{
      *Byte = Map11To10And10To01[*Byte];
}

void
output_jbig(unsigned char *start, size_t len, void *cbarg)
//uses a fixed global buffer to store one stripe and sends stripe when it becomes complete
{
int   i,rc;
//FILE      *Dest;
 //copy bytes one at a time looking for end of BIE and counting
      for(i=0;i<len;++i)
      {
            CompStripeBuffer[CompStripeBufferFree] = start[i];
            ++CompStripeBufferFree;
            ++BytesOutCountSingle;

//at end of BIE call output functions
            if(CompStripeBufferFree >= 2 && BytesOutCountSingle > 20) //don't do output during the header
            {
                  if(CompStripeBuffer[CompStripeBufferFree-2] == ESC && CompStripeBuffer[CompStripeBufferFree-1] != 0)
                  {
                  //end of BIE detected
                        DoLog("SingleStripe: detected end of BIE at %d bytes, length %d\n",BytesOutCountSingle,CompStripeBufferFree);
                        DoOutJob(cbarg, "RasterObject.Data#%d=", CompStripeBufferFree,0);
                  //send the data
            if(cbarg != NULL) rc = fwrite(CompStripeBuffer, 1, CompStripeBufferFree, cbarg);
            rc = fwrite(CompStripeBuffer, 1, CompStripeBufferFree, stdout); //also to output
            DoOutJob(cbarg, ";",0,0); //one semi colon after the chain
 
                  //then restart the buffer
                        CompStripeBufferFree = 0;
                  }
            }
      }
}

time_t KeepAwake(time_t Start, int Interval)
{
// Keeps the printer connection awake by sending DeviceStatus query not sooner than the specified interval in seconds
// Usage:   Start = KeepAwake(Start, Interval);
      if(time(NULL) - Start > Interval)
      {
            DoLog("Keeping printer awake by DeviceStatus?\n",0,0);
            GoodExchange("DeviceStatus?", "0101,DeviceStatus.ImageDevice", DoBack, BackBuf,  BackBufLen,  1,  1.0);
            return (time(NULL));
      }
      else return (Start);
}

void OutputLineExtents(unsigned char *buf, int Totalbpl, cups_page_header2_t header, FILE *fp)
{
      int BandLeft, BandRight, BytesLeft, BytesRight, ThisBytePrint, BytesPerCol, Col, Cols, Printbpl;
//BitsPerPixel is in the buffer of printer data, not in the cups raster Use global OutBitsPerPixel
      Printbpl = (header.cupsWidth + 7) / 8;
      if (header.cupsColorSpace == CUPS_CSPACE_CMYK) Cols=4;
      else Cols=1;
      BytesPerCol=Totalbpl/(Cols+1);
      //Search for extents in this row of pixels
      for(BytesLeft=0;BytesLeft<Printbpl;++BytesLeft)
      {
            ThisBytePrint=0;
            for(Col=0;Col<Cols;++Col)
            {
                  if(buf[BytesLeft+BytesPerCol*Col]!=0) ThisBytePrint=1;
            }
            if(ThisBytePrint==1) break;
      }
      if(BytesLeft >= Printbpl) BandLeft=0;
      else BandLeft=BytesLeft*8/OutBitsPerPixel;
      //BytesLeft is the offset to the first byte with pixels
      //BandLeft is the pixel offset to the first byte that is not blank

      for(BytesRight=Printbpl*OutBitsPerPixel-1;BytesRight >= 0;--BytesRight)
      {
            ThisBytePrint=0;
            for(Col=0;Col<Cols;++Col)
            {
                  if(buf[BytesRight+BytesPerCol*Col]!=0) ThisBytePrint=1;
            }
            if(ThisBytePrint==1) break;
      }
      if(BytesRight < 0) BandRight=0;
      else BandRight=(BytesRight+1)*8/OutBitsPerPixel;
      //BytesRight is the off set to the last byte with pixels
      //BandRight is the pixel offset to the byte after last byte that is not blank
      DoOutJob(fp,",%d,%d",BandLeft,BandRight);
}

void PrintOneStripe (unsigned char *buf, int Stripe, int StripeHeight, cups_page_header2_t header, FILE *fp)
{
// buf is the stripe buffer with width w and height StripeHeight pixels. PrintW PrintH are the pixel width and height of the print raster
    int      y, Index;
   // int BandOffset=0; //was the offset in lines to the start of the band

      fprintf(stderr,"DEBUG: c2esp: PrintOneStripe\n");
      DoLog("PrintOneStripe starts SkipStripe = %d\n",SkipStripe,0);

      DoLog("Extent search BitsPerPixel=%d\n",OutBitsPerPixel,0);
      //writes the extents and data for one band for the 5250 printer 
      DoLog("stripe %d of height %d>\n",Stripe, StripeHeight);
      fprintf(stderr,"INFO: c2esp: Page %d Stripe %d\n", Page, Stripe);
/*
printer manager thinks this is an error
      fprintf(stderr,"STATE: com.Kodak.c2esp:-Page-%d-Stripe-%d\n", Page, Stripe);
*/
/*    fprintf(stderr,"STATE:\n");  clears the state error */

      //black in bands was stored in SkipStripe
      if(SkipStripe)
      {
                  //there is no print in this band
                  DoOutJob(fp, "RasterObject.Extent=skip,%d;", StripeHeight,0);
                  DoLog("<Stripe %d skipped>\n",Stripe, 0);
      }
      else
      {
                  //there is print in this band
                  DoOutJob(fp,"RasterObject.BandHeight=%d;",StripeHeight,0);
                  DoOutJob(fp,"RasterObject.Extent=true",0,0);
                  for(y=0;y<StripeHeight;++y) OutputLineExtents(&buf[(y)*RasForCompWidth], RasForCompWidth, header, fp);
                  DoOutJob(fp,";",0,0); //extent terminator at end of band

                  fprintf(stderr,"DEBUG: c2esp: Compress a stripe\n");
                  for(y=0;y < StripeHeight;++y)
                  {
                  Index = (y)*RasForCompWidth;
                  jbg85_enc_lineout(&JbigState, &buf[Index], &buf[Index - RasForCompWidth], &buf[Index - 2 * RasForCompWidth]);
                  }

                  DoOutJob(fp,"Event=EndOfBand;",0,0);
      } //end of stripe with print
}

void SetUpDither(cups_lut_t *Lut[], cups_dither_t *DitherState[], int LineWidth)
{
      int Col;
      for(Col=0;Col<4;++Col)
      {
            Lut[Col] = cupsLutNew(3, default_lut);
            DitherState[Col] = cupsDitherNew(LineWidth);
      }
}

unsigned char Dithered4ToPrint(unsigned char *Buffer, int x)
{
      //gets 4 points from Buffer started with x where 00=max colour 255=no colour and maps them into one byte
      //Buffer must be >= 4 chars longer than x
      // (0->10, 2->00, 1->01)
      unsigned char Build=0;
//shifting version
      if (Buffer[x+0]<=2) Build=Build+(Buffer[x+0]<<6);
      if (Buffer[x+1]<=2) Build=Build+(Buffer[x+1]<<4);
      if (Buffer[x+2]<=2) Build=Build+(Buffer[x+2]<<2);
      if (Buffer[x+3]<=2) Build=Build+(Buffer[x+3]);
      return Build;
}

void CheckMap()
{
//temporary check of MapByte only needed for testing
      unsigned char i,j,Test;
      
      DoLog("Testing MapDotsInByte\n",0,0);
      for(i=0;i<255;i=i+16)
      {
            for(j=0;j<16;++j)
            {
                  Test=i+j;
                  MapDotsInByte(&Test);
                  DoLog("%d, ",Test,0);
            }
            DoLog("\n",0,0);
      }
}

void
MarkerSetup()
{
      fprintf(stderr, "ATTR: marker-colors=black,magenta\n"); //displays ink drops in printer manager
      fprintf(stderr, "ATTR: marker-names=black,colour\n");
}

/*
 * 'main()' - Main entry and processing of driver. With watermark
 */

int                           /* O - Exit status */
main(int  argc,   char *argv[])           /* I - Number of command-line arguments, Command-line arguments */
{
      int               fd;         /* File descriptor */
      cups_raster_t           *ras;       /* Raster stream from cups */
      cups_page_header2_t     header;           /* Page header from cups */
      int               Stripe, y;        
//    ppd_file_t        *ppd;       /* PPD file */
      int               StripeEnd; //index of last byte in current stripe
      int               StripeHeight; //height of current stripe
      int               CloseError,Col,i,x; 
      int               BlankColour; //boolean to record if the line is blank to save time
      char PrintFileName[100]="";

      cups_lut_t  *Lut[4];          /* Dither lookup tables */
      cups_dither_t     *DitherState[4];  /* Dither states */
      int               BytesPerColour;
      long              RasForCompHeight;
      unsigned char           MinOut=255, MaxOut=0; //to check the range of the dithered output
#if DEBUGFILES == 1
      short             MinIn=5000, MaxIn=0; //to check the range of the dithered input
      int               output;
#endif
      StartTime = time(NULL);
      KeepAwakeStart = time(NULL);

      #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
      struct sigaction action;            /* Actions for POSIX signals */
      #endif /* HAVE_SIGACTION && !HAVE_SIGSET */

      chmod("/tmp/c2espjob", S_IRUSR | S_IWUSR | S_IROTH ); //let anyone read it
      chmod("/tmp/KodakPrintLog", S_IRUSR | S_IWUSR | S_IROTH ); //let anyone read it
      remove("/tmp/KodakPrintLog"); //to be sure I only see the latest
      remove("/tmp/KodakSendLog"); //to be sure I only see the latest
      LogFile = fopen("/tmp/KodakPrintLog", "w"); //open the log file
      setbuf(LogFile, NULL);
      fprintf(LogFile, "KodakPrintLog c2esp\n");

      setbuf(stderr, NULL);
            fprintf(stderr, ("DEBUG: %s: ====================================================\n"),Version); 
      DoLogString("Starting %s\n",Version);
      fprintf(stderr, ("INFO: Starting c2esp filter.\n"));
      MarkerSetup();
 /*
  * Check command-line...
  */
      if (argc < 6 || argc > 7) //wrong no of arguments
      {
            fprintf(stderr, ("Usage: %s job-id user title copies options [file]\n"), "rastertoek");
             return (1);
      }
      //check option param for the test option "noback"
      if(strcmp(argv[5],"noback")==0) DoBack=0;
      else DoBack=1;

 /*
  * Open the page stream...
  */
      if (argc == 7)
      {
            if ((fd = open(argv[6], O_RDONLY)) == -1)
            {
                        fprintf(stderr, ("ERROR: c2esp: Unable to open raster file - %s\n"),
                        strerror(errno));
                        sleep(1);
                        return (1);
            }
      }
      else    fd = 0;
            fprintf(stderr, ("DEBUG: c2esp: opening raster\n")); 
      ras = cupsRasterOpen(fd, CUPS_RASTER_READ);

 /*
  * Register a signal handler to eject the current page if the
  * job is cancelled.
  */
      Canceled = 0;
      #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
      sigset(SIGTERM, CancelJob);
      #elif defined(HAVE_SIGACTION)
      memset(&action, 0, sizeof(action));
      sigemptyset(&action.sa_mask);
      action.sa_handler = CancelJob;
      sigaction(SIGTERM, &action, NULL);
      #else
      signal(SIGTERM, CancelJob);
      #endif /* HAVE_SIGSET */

 /*
  * Initialize the print device...
  */
      //ppd = ppdOpenFile(getenv("PPD"));

      /* Open the print file */
      strcat(PrintFileName,"/tmp");
      strcat(PrintFileName,"/c2espjob");
      remove(PrintFileName);
      JobFile = fopen(PrintFileName, "w"); 
      if (JobFile != NULL) DoLogString("JobFile %s Opened\n",PrintFileName);
      else DoLogString("JobFile %s failed to open\n",PrintFileName);

      PrintFile = fopen("/tmp/KodakPrintFile", "w");//open the print file
//    setbuf(PrintFile, NULL);

/* read the first header */
      if(cupsRasterReadHeader2(ras, &header))
      {
            DoLog("First page Header read after %d sec\n", time(NULL)-StartTime,0);
            SetUpDither(Lut, DitherState, header.cupsWidth);

            SetupPrinter();
            DoLog("Printer should be ready\n",0,0);
  /* 
  * Process pages as needed...
  */
            Page = 0;
      do //start of loop for each page
      {
#if DEBUGFILES == 1
//open debug files
            remove("/tmp/RasForComp.pbm");
            dfp = fopen("/tmp/RasForComp.pbm", "w");
            if(dfp)     fprintf(dfp, "P4\n%8d %8d\n", RasForCompWidth * 8, (StripeEnd + 1)/RasForCompWidth);

                  remove("/tmp/RasCyan.pbm");
                  remove("/tmp/RasMagenta.pbm");
                  remove("/tmp/RasYellow.pbm");
                  remove("/tmp/RasBlack.pbm");
                  Cyanfp = fopen("/tmp/RasCyan.pbm", "w");
                  Magentafp = fopen("/tmp/RasMagenta.pbm", "w");
                  Yellowfp = fopen("/tmp/RasYellow.pbm", "w");
                  Blackfp = fopen("/tmp/RasBlack.pbm", "w");
                        fprintf(Cyanfp, "P4\n%8d %8d\n", BytesPerColour * 8, (StripeEnd + 1)/RasForCompWidth);
                        fprintf(Magentafp, "P4\n%8d %8d\n", BytesPerColour * 8, (StripeEnd + 1)/RasForCompWidth);
                        fprintf(Yellowfp, "P4\n%8d %8d\n", BytesPerColour * 8, (StripeEnd + 1)/RasForCompWidth);
                        fprintf(Blackfp, "P4\n%8d %8d\n", BytesPerColour * 8, (StripeEnd + 1)/RasForCompWidth);

#endif
            DoLog("Header read\n", 0,0);
                  fprintf(stderr, ("DEBUG: c2esp: header read\n")); 

            if (Canceled)     break;
            if(header.cupsWidth<=0) break;

            Page ++;
            DoLog("PAGE %d COPIES %d\n", Page, header.NumCopies);

   /*
    * Start the page...
    */
            BytesOutCountSingle = 0; //initialise counter
            RasForCompHeight = 0; /* will accumulate the height for one page */
            //StartTime=time(NULL);
            SetPaperSize(KodakPaperSize, header.PageSize[1]);     
            if(header.cupsBitsPerColor == 1) OutBitsPerPixel = 1; 
            else OutBitsPerPixel = 2;
            BytesPerColour = (((header.cupsWidth * OutBitsPerPixel + 7) / 8) + 31) / 32 * 32; //round each colour up to 32 bytes

            if (header.cupsColorSpace == CUPS_CSPACE_CMYK) //colour
            {
                  DoLog("Colour cups raster w = %d h = %d\n", header.cupsWidth, header.cupsHeight);
                  DoLog("Colour cups bits per pixel = %d, bits per colour = %d\n", header.cupsBitsPerPixel, header.cupsBitsPerColor);
                  DoLog("Colour cups raster bytes per line = %d bits per line = %d\n", header.cupsBytesPerLine, header.cupsBytesPerLine * 8);
                  RasForCompWidth = BytesPerColour * 5;

                  if(header.cupsBitsPerColor > 2)   //raster is 8 bit for dithering
                  {
                        fprintf(stderr, "INFO: p%d Colour dithering\n",Page);

#if DEBUGFILES == 1
                        //open dither files here
                        remove("/tmp/RawCyan.pbm");
                        RawColourFile = fopen("/tmp/RawCyan.pbm", "w");
                        if (RawColourFile) 
                        {
                              fprintf(RawColourFile, "P5\n%8d %8d %8d\n", header.cupsWidth, header.cupsHeight, 255);
                              DoLog("Opened RawCyan.pbm\n", 0, 0);
                        }
                        remove("/tmp/DitheredColour.pbm");
                        DitheredColourFile = fopen("/tmp/DitheredColour.pbm", "w");
                        if (DitheredColourFile) 
                        {
                              fprintf(DitheredColourFile, "P5\n%8d %8d %8d\n", header.cupsWidth, header.cupsHeight, 255);
                              DoLog("Opened DitheredColour.pbm\n", 0, 0);
                        }
#endif
                  }
                  else fprintf(stderr, "INFO: p%d Colour not dithered\n",Page); //raster is 2 bit

            }
            else if (header.cupsColorSpace == CUPS_CSPACE_K)//monochrome
            {
                  fprintf(stderr, "INFO: p%d Monochrome\n",Page);
                  DoLog("Mono cups raster w = %d h = %d\n", header.cupsWidth, header.cupsHeight);
                  DoLog("Mono cups raster bytes per line = %d bits per line = %d\n", header.cupsBytesPerLine, header.cupsBytesPerLine * 8);
//                BytesPerColour = (header.cupsBytesPerLine + 31) / 32 * 32; //round each colour up to 32 bytes
                  RasForCompWidth = BytesPerColour * 2;
            }
            else DoLog("Unknown cupsColorSpace = %d\n", header.cupsColorSpace, 0);

            AllocateBuffers(&header);
            StripeEnd = -1; //index of the last byte of the stripe
            MinOut=255;MaxOut=0; //initialise

            if(Page == 1) SetupJob(&header);
            StartPrinterPage( &header);

//prepare for compression JBIG85
            fprintf(stderr,"DEBUG: c2esp: initialising Compression\n");
            jbg85_enc_init(&JbigState, RasForCompWidth * 8, header.cupsHeight, output_jbig, JobFile);
            jbg85_enc_options(&JbigState, -1, StripeHeightMax, -1); //default options ie with variable length

            for (Stripe = 0; Stripe * StripeHeightMax < header.cupsHeight; ++Stripe)  // Loop for each source stripe
            {
                  /* sleep(2); removed 25/3/11 */
                  if(header.cupsHeight > Stripe * StripeHeightMax + StripeHeightMax) StripeHeight = StripeHeightMax;
                  else StripeHeight = header.cupsHeight - (Stripe * StripeHeightMax);
                  if(Stripe == 1) DoLog("First stripe at %d sec\n", time(NULL)-StartTime, 0);
                  
                  for (y = 0; (y < StripeHeightMax) && (y + Stripe * StripeHeightMax < header.cupsHeight); ++y )  // Loop for each line in the stripe
                  {
                              if (Canceled) break;
                        KeepAwakeStart = KeepAwake(KeepAwakeStart,10); //Keep the printer connection awake

                        if (header.cupsColorSpace == CUPS_CSPACE_CMYK) //colour
                        {
                              if  (header.cupsBitsPerColor == 8) // do dithering
                              {
                                    if(y == 0) DoLog("Doing dithering in stripe %d\n", Stripe, 0);
                              //read a line of colour. The longest line 600dpi 8.5 in = 20400 bytes for 8 bit CMYK values
                                    if (!cupsRasterReadPixels(ras, CupsLineBuffer, header.cupsBytesPerLine)) break; 
                                          //CupsLineBuffer holds the chunky CMYK 8 bit data of a whole line
                                    if(y == 0) DoLog("Read first line from cups raster\n", Stripe, 0);

                                    for(Col = 0; Col<4; ++Col)
                                    {
                              //convert the bits in CupsLineBuffer to short ints in DitherInputBuffer for the current colour
                              //checking if it's blank as we go
                                          BlankColour=1; //0 for testing will dither all lines
                                          for(x=0;x<header.cupsWidth;++x) 
                                          {
                                                DitherInputBuffer[x]=CupsLineBuffer[4*x+Col]<<4;
                                                if(DitherInputBuffer[x]>0) BlankColour=0;
#if DEBUGFILES==1
                              //save in file, in the file 255=white 0=black(reverse of printer)
                                                if(Col == 3) //0=cyan 1=mag 2=yellow 3=black
                                                {
                                                      output = 255 - CupsLineBuffer[4*x+Col];
                                                            if (output < 0) output = 0;
                                                            if (RawColourFile) fputc(output, RawColourFile);
                                                      if (DitherInputBuffer[x]>MaxIn) MaxIn=DitherInputBuffer[x];
                                                      if (DitherInputBuffer[x]<MinIn) MinIn=DitherInputBuffer[x];
                                                }
#endif
                                          }
                              //dither the line
                                          if(BlankColour==0) cupsDitherLine(DitherState[Col], Lut[Col], DitherInputBuffer, 1, DitherOutputBuffer);
// header: cupsDitherLine(cups_dither_t *d, const cups_lut_t *lut, const short *data, int num_channels, unsigned char *p);
// full scale input is 4095. output is the index in the lut.

#if DEBUGFILES==1
                              //save in file, in the file 255=white 0=black (reverse of printer)
                                          for(x=0;x<header.cupsWidth;++x) 
                                          {
                                                if(Col == 3) //0=cyan 1=mag 2=yellow 3=black
                                                {
                                                            if(!BlankColour) 
                                                      {
//try a 7 bit shift on the next line instead of 255/2
//                                                          output = 255 - DitherOutputBuffer[x]*255/2;
                                                            output = 255 - (DitherOutputBuffer[x] << 7);
                                                                  if (output < 0) output = 0;
                                                            if (DitherOutputBuffer[x]>MaxOut) MaxOut=DitherOutputBuffer[x];
                                                            if (DitherOutputBuffer[x]<MinOut) MinOut=DitherOutputBuffer[x];
                                                      }
                                                      else 
                                                      {
                                                            output=255;
                                                            MinOut=0;
                                                      }
                                                            if (DitheredColourFile) fputc(output, DitheredColourFile);
                                                }
                                          }
#endif
                              //copy the line as bits into the appropriate position in the printer raster
                                          for(x=0;x<header.cupsWidth;x=x+4) 
                                          {
                                                if(!BlankColour) RasForComp[y * RasForCompWidth + Col * (BytesPerColour)+x/4]=Dithered4ToPrint(DitherOutputBuffer,x);
                                                else RasForComp[y * RasForCompWidth + Col * (BytesPerColour)+x/4]=0;
                                          
                                          }
                                    } //next Col
                              } //end of 8 bits per colour section

                              else if (header.cupsBitsPerColor <= 2) //the old version of the ppd non dithered should work for 2 or 1 bit
                              {
                                    for(Col = 0; Col<4; ++Col)
                                    {
                                          if (cupsRasterReadPixels(ras, &RasForComp[y * RasForCompWidth + Col * (BytesPerColour)], header.cupsBytesPerLine/4) < 1) break;
                                    }
                              }
                              else DoLog("cupsBitsPerColor %d is not handled\n",header.cupsBitsPerColor,0);
                              
                        }
                        else //monochrome
                        {
                              if (cupsRasterReadPixels(ras, &RasForComp[y * RasForCompWidth], header.cupsBytesPerLine) < 1) break; //read a line
                        }

//                      StripeEnd = StripeEnd + RasForCompWidth; //current index of the last byte of the stripe NOW SET BELOW
                  } //end of line loop

StripeEnd = StripeHeight * RasForCompWidth - 1; //so do not have to increment in the loop

                  //map 2 bit per pixel data of the line to suit the printer and look for printing in the stripe
                  SkipStripe = 1;
                  for(i=0;i<=StripeEnd;++i) 
                  {
                        if (header.cupsBitsPerColor==2) RasForComp[i] = Map11To10And10To01[RasForComp[i]]; //map 2 bit
                        if(RasForComp[i]!=0) SkipStripe = 0; //look for printing
                  }

#if DEBUGFILES == 1
//write the stripe into the raster files
                  if(SkipStripe != 1) 
                  {
//store the raster for debugging - does not seem to be viewable properly when 600dpi colour
                        if (dfp) fwrite(&RasForComp[0], 1, StripeEnd, dfp);
//store the CMYK rasters separately
                        if(Cyanfp && Magentafp && Yellowfp && Blackfp) 
                        {
                              for(i=0;i<(StripeEnd + RasForCompWidth + 1);i=i+RasForCompWidth)
                              {
                              fwrite(&RasForComp[i + 0 * BytesPerColour], 1,  BytesPerColour, Cyanfp);
                              fwrite(&RasForComp[i + 1 * BytesPerColour], 1,  BytesPerColour, Magentafp);
                              fwrite(&RasForComp[i + 2 * BytesPerColour], 1,  BytesPerColour, Yellowfp);
                              fwrite(&RasForComp[i + 3 * BytesPerColour], 1,  BytesPerColour, Blackfp);
                              }
                        }
                        DoLog("Rasters stored at %d sec\n",time(NULL)-StartTime,0);
                  }
#endif

                  //DoLog("StripeEnd=%d SkipStripe=%d\n",StripeEnd,SkipStripe);
                  if(!SkipStripe) RasForCompHeight = RasForCompHeight + StripeHeight;

                  PrintOneStripe(&RasForComp[0], Stripe, StripeHeight, header, JobFile);
//copy the last 2 lines of the stripe, before the first line of RasForComp
                  for(i=0;i<RasForCompWidth;++i)
                  {
                        RasForComp[i - RasForCompWidth] = RasForComp[(StripeHeight-1) * RasForCompWidth + i];
                        RasForComp[i - RasForCompWidth * 2] = RasForComp[(StripeHeight-2) * RasForCompWidth + i];
                  }
      
                  StripeEnd = -1;
            } //end of loop for each stripe

            DoLog("Page raster built at %d sec\n",time(NULL)-StartTime,0);
//          if((StripeEnd + 1) > 100000000 )  DoLog("raster too big",0,0); //was used to catch a bug

//page is finished
#if DEBUGFILES == 1
//all stripes are now done
            DoLog("Max and min in input colour are %d %d\n",MaxIn,MinIn);
            DoLog("Max and min in dithered colour are %d %d\n",MaxOut,MinOut);

//close the dither files
            if(RawColourFile)
            {
                        fclose(RawColourFile);
                        sleep(3);
                        chmod("/tmp/RawCyan.pbm", S_IRUSR | S_IWUSR | S_IROTH ); //let anyone read it
                        DoLog("Closed RawCyan.pbm\n", 0, 0);
            }
            if(DitheredColourFile)
            {
                        fclose(DitheredColourFile);
                        sleep(3);
                        chmod("/tmp/DitheredCyan.pbm", S_IRUSR | S_IWUSR | S_IROTH ); //let anyone read it
                        DoLog("Closed DitheredCyan.pbm\n", 0, 0);
            }

//close the debug files
                  fclose(dfp);
                  sleep(3);
                  chmod("/tmp/RasForComp.pbm", S_IRUSR | S_IWUSR | S_IROTH ); //let anyone read it
                        fclose(Cyanfp);
                        fclose(Magentafp);
                        fclose(Yellowfp);
                        fclose(Blackfp);
                        sleep(3);
                        chmod("/tmp/RasCyan.pbm", S_IRUSR | S_IWUSR | S_IROTH ); //let anyone read it
                        chmod("/tmp/RasMagenta.pbm", S_IRUSR | S_IWUSR | S_IROTH ); //let anyone read it
                        chmod("/tmp/RasYellow.pbm", S_IRUSR | S_IWUSR | S_IROTH ); //let anyone read it
                        chmod("/tmp/RasBlack.pbm", S_IRUSR | S_IWUSR | S_IROTH ); //let anyone read it

#endif
/* Added newlen 25/3/11 to fix pages sometimes stalling */
            DoLog("Setting jbig height to %d\n", RasForCompHeight, 0);
            jbg85_enc_newlen(&JbigState, RasForCompHeight);


            EndPage();
            if (Canceled)    break;
      }
      while (cupsRasterReadHeader2(ras, &header));

      }
      else DoLog("no headers so nothing to print",0,0);

 /*
  * Close the raster stream... and the debug file
  */
            fprintf(stderr, ("DEBUG: c2esp: closing raster\n")); 
      cupsRasterClose(ras);
      DoLog("cups raster closed after %d sec\n",time(NULL)-StartTime,0);
      if (fd != 0)   close(fd);
      if(JobFile != NULL)
      {
            CloseError = fclose(JobFile);
            DoLog("jobfile closed after %d sec with return value %d\n",time(NULL)-StartTime, CloseError);
      }

//free the dither states
      for(Col = 0; Col < 4 ;++Col)
      {
            cupsDitherDelete(DitherState[Col]);
            cupsLutDelete(Lut[Col]);
      }
 /*
  * Termination, send an error message if required...
  */
      if(LogFile != NULL) 
      {
            DoLog("SingleStripe: Bytes output by JBIG just before terminating = %d\n",BytesOutCountSingle,0);
            DoLog("c2esp terminating after %d sec. Processed %d pages\n",time(NULL)-StartTime,Page);
      }
      if (Page == 0)
      {
            fprintf(stderr, ("ERROR: c2esp: No pages found!\n"));
            return (1);
      }
      else
      {

            ShutdownPrinter();

            if(LogFile != NULL) 
            {
                  fclose(LogFile);
            }
            if(PrintFile != NULL) 
            {
                  fclose(PrintFile);
                  sleep(3); //does this help chmod to work?
            }
            //let anyone read the files How much delay is needed if any?
            chmod("/tmp/c2espjob", S_IRUSR | S_IWUSR | S_IROTH ); //let anyone read it
            chmod("/tmp/KodakPrintLog", S_IRUSR | S_IWUSR | S_IROTH ); //let anyone read it
            chmod("/tmp/KodakPrintFile", S_IRUSR | S_IWUSR | S_IROTH ); //let anyone read it
            fprintf(stderr, ("INFO: c2esp: Ready to print.\n"));
            return (0);
      }
}


Generated by  Doxygen 1.6.0   Back to index