c2espC.c 23.7 KB
Newer Older
1 2 3 4 5
/* 
 *
 *   Kodak ESP Cxxx (OPL?) Control Language filter for the  Common UNIX
 *   Printing System (CUPS).
 *
6
 *  copyright Paul Newall May 2010 - Sept 2012. VERSION 2.6 (c2esp26) 
7
 *  SUPPORT FOR ESP Cxxxx and Hero SERIES
8 9 10
 *  patch by user awl29 applied to fix problems with non bi-directional printers, smb shared
 *  data chunk size limit applied

11 12 13 14
 *  Params: job-id user title copies options [file]
 *  options = "noback" disables all calls to the back channel for testing.
 *
 
15
This filter is based loosely on c2esp
16 17
 */

18 19
#define MAXDATACHUNK 65511 /* it seems windows smb printer can't handle more than 65K at a time? maybe others too */

20 21
// debugfiles and testing are defined in c2espcommon.h because they are used in c2espcommon.c
/* #define DEBUGFILES 0  DEBUGFILES 1 creates files in /tmp to help debug 
22 23
Currently a large number of files:
KodakPrintLog = text file showing progress of the filter
24
RasForComp.pbm or ppm = the raster read from cups raster view with image viewer.
25
KodakUncompressed = The binary page data before compression.
26
KodakCompPage = The zlib compressed page data
27
KodakPrintFile = The data that is sent to the printer
28 29

#define TESTING 0 TESTING 1 suppresses the output to the printer. Used in development. */
30

31
/* #include "config.h" */
32 33 34 35 36 37
#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>
38
#include <time.h> //time functions used for debugging
39
#include "c2espcommon.h" //the common library of c2esp and c2espC
40 41

/* for gzip */
42
#include <zlib.h>
43 44 45 46 47 48
#define SET_BINARY_MODE(file)
#define CHUNK 16384

/*
 * Constants...
 */
49
char	*Version = "c2espC26";
50 51 52 53 54 55 56

/*
 * Globals...
 */
unsigned char	*CupsLineBuffer;	//buffer for one line of the cups raster
unsigned char	*DataLineBuffer;	//buffer for one uncompressed line
char		KodakPaperSize[50];  	/* String that the printer expects for paper size */
57
int		OutBitsPerPixel,	/* Number of bits per color per pixel for printer*/
58 59 60
		Duplex,			/* Current duplex mode */
		Page,			/* Current page number */
		Canceled,		/* Has the current job been canceled? */
61
 		DoBack;			/* Enables the back channel comms */ 
62
int		MemUsed = 0; //tracks memory use
63 64 65 66 67 68 69 70
time_t 		TimeStart; //to record the start of a section
FILE 		*PrintFile = NULL; //file descriptor for debug file
FILE 		*UncompressedFile = NULL; //file descriptor for file of uncompressed page data

time_t		StartTime;
time_t 		KeepAwakeStart;

//for zlib
71
z_stream 	strm;
72 73 74 75 76 77 78 79 80 81 82 83 84
unsigned char	out[CHUNK]; /*buffer for a compressed line */

#if DEBUGFILES == 1
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 		*RawColourFile = NULL; //file descriptor for input to dither
FILE 		*DitheredColourFile = NULL; //file descriptor for output from dither
#endif

void
85
SetupPrinter(cups_page_header2_t *header)
86 87 88 89 90 91
{
//gets the printer ready to start the job
	int  i;

	for(i=0; i<4; ++i)
	{
92
		if(GoodExchange(PrintFile, "LockPrinterWait?", "0002, OK, Locked for printing;", DoBack, 1,  3.0) >= 0) break;
93
	}
94 95

	DoOutJob(PrintFile, "Event=StartOfJob;",0,0); //printer command
96

97 98
	if (DoBack) {
		GoodExchange(PrintFile, "DeviceStatus?", "0101,DeviceStatus.ImageDevice", DoBack, 1,  1.0);
99 100 101 102 103 104
// 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

		DoLog("ColourPercent=%d\n",ColourPercent,0);
		DoLog("BlackPercent=%d\n",BlackPercent,0);
105 106
		if(ColourPercent >= 0 && BlackPercent >= 0)
		{
107
    		fprintf(stderr,"ATTR: marker-levels=%d,%d\n",BlackPercent,ColourPercent); // sets the levels displayed
108 109 110
		}
	}        
	DoOutJob(PrintFile, KodakPaperSize,0,0);   
111 112 113 114 115 116 117
	if(header->MediaPosition == 0) DoOutJob(PrintFile, "MediaInputTrayCheck=Main;",0,0);
	else if(header->MediaPosition == 1) DoOutJob(PrintFile, "MediaInputTrayCheck=Photo;",0,0);
	else 
	{
		DoOutJob(PrintFile, "MediaInputTrayCheck=Main;",0,0);
		DoLog("Unknown Input Tray no. %d so used main tray", header->MediaPosition, 0);
	}
118

119 120 121 122
    if (DoBack) {        
		GoodExchange(PrintFile, "MediaTypeStatus?", "MediaTypeStatus=custom-media-type-deviceunavailable", DoBack,  1,  1.0);
		GoodExchange(PrintFile, "MediaDetect?", "0098, OK, Media Detect Started;", DoBack, 1,  1.0);
		//do MediaTypeStatus? until some media is found typically takes 12 seconds for Hero 9.1
123
#if TESTING == 0
124 125
		sleep(5);
		for(i=0; i<15; ++i) //normal
126 127
#endif
#if TESTING == 1
128
		for(i=0; i<2; ++i) //short for tests
129
#endif
130 131 132 133 134 135 136 137 138
		{
			DoLog("MediaTypeStatus? try %d\n", i, 0);
			if(GoodExchange(PrintFile, "MediaTypeStatus?", 
				"MediaTypeStatus=custom-media-type-deviceunavailable", DoBack,  2,  2.0) <= 0) break;
		}
	}
	else {
		GoodExchange(PrintFile, "MediaDetect?", "0098, OK, Media Detect Started;", DoBack, 1,  1.0);
		sleep(12);
139
	}
140 141 142 143 144
}

void
ShutdownPrinter(void)
{
145
	int i, ret;
146 147 148 149

	DoOutJob(PrintFile, "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? */
	{
150 151 152 153
		/* First few tries will be quick so small jobs finish quickly */
		if(i<5) ret=GoodExchange(PrintFile, "UnlockPrinter?", "0003, OK, Printer unlocked;", DoBack, 1,  5.0);
		/* Then tries will be slower so long jobs can finish */
		else ret=GoodExchange(PrintFile, "UnlockPrinter?", "0003, OK, Printer unlocked;", DoBack, 5,  2.0);
154
		DoLog("UnlockPrinter? try %d returned %d\n", i, ret);
155
		if(ret >= 0 || strcmp(BackBuf , "3405, Error, Printer not locked") == 0) break;
156
		//error string has no terminating ; because it has been tokenised
157 158 159 160 161 162 163 164 165 166
	}
}

void
SetupJob(cups_page_header2_t *header) //Prepare the printer for printing a job.
{
	DoOutJob(PrintFile, "OutputBin=MainSink;",0,0);
	Duplex = header->Duplex;
	if(Duplex == 0) DoOutJob(PrintFile, "Sides=OneSided;",0,0);
	else  DoOutJob(PrintFile, "Sides=TwoSided;",0,0);
167
	//DoOutJob(PrintFile, "PageOrder=BackToFront;",0,0); //may be possible to implement this
168
	//DoOutJob(PrintFile, "Copies=1;",0,0); //may an error response? but is sent by windows
169 170 171 172 173 174 175 176 177 178
	DoOutJob(PrintFile, "MediaType=custom-media-type-autoselection-0-0-0-0;",0,0);
	DoOutJob(PrintFile, KodakPaperSize,0,0);
}

void
FreeBuffers()
{
	DoLog("Free buffers\n",0,0);
  	free(CupsLineBuffer);
  	free(DataLineBuffer);
179
	MemUsed = 0;
180 181 182 183 184 185 186
	DoLog("Buffers freed\n",0,0);
}

void
AllocateBuffers(cups_page_header2_t *header)
{
 // Allocate memory for a page of graphics... 
187

188 189 190 191 192
  	if ((CupsLineBuffer = malloc(header->cupsBytesPerLine)) == NULL) 
  	{
		DoLog("ERROR: Unable to allocate %d bytes for CupsLineBuffer!\n",header->cupsBytesPerLine,0);
    		exit(1);
  	}
193 194
	else MemUsed = MemUsed + header->cupsBytesPerLine * 1E-3;
 	if ((DataLineBuffer = malloc(header->cupsBytesPerLine+12)) == NULL) 
195 196 197 198
  	{
		DoLog("ERROR: Unable to allocate %d bytes for DataLineBuffer!\n",header->cupsBytesPerLine+8,0);
    		exit(1);
  	}
199 200
	else MemUsed = MemUsed + (header->cupsBytesPerLine+12) * 1E-3;
	DoLog("Buffers allocated %d kb\n",MemUsed,0);
201 202 203 204 205 206 207
}

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

208
	DisplayHeader(header);
209 210 211 212
	ResX = header->HWResolution[0];
	ResY = header->HWResolution[1];
	DoOutJob(PrintFile, "Event=StartOfPage;",0,0);
	DoOutJob(PrintFile, "Page=%d;",Page,0);
213
	DoOutJob(PrintFile, "Origin.Top=0.0mm;Origin.Left=0.0mm;",0,0);
214 215 216 217 218 219 220 221 222
//	DoOutJob(PrintFile, "Origin.Top=1.0mm;Origin.Left=1.0mm;",0,0);
	if(ResX==300)
	{
		DoOutJob(PrintFile, "PrintQuality=Draft;",0,0);
	}
	else if(ResX==600)
	{
		DoOutJob(PrintFile, "PrintQuality=Normal;",0,0);
	}
223
	else if(ResX==1200) // may not be an option
224 225 226 227
	{
		DoOutJob(PrintFile, "PrintQuality=High;",0,0);
	}

228
    	if (header->cupsColorSpace == CUPS_CSPACE_CMY)
229 230
	{

231
		DoLog("CUPS_CSPACE_CMY  (%d)\n",header->cupsColorSpace,0);
232
		DoOutJob(PrintFile, "PrintColorspace=Color;",0,0);
233
		DoOutJob(PrintFile, "AntiBleedControl=Auto;",0,0);
234 235
		DoOutJob(PrintFile, "Resolution=%dx%d;", ResX, ResY);
		DoOutJob(PrintFile, "RasterObject.BitsPerPixel=%d;",OutBitsPerPixel,0);
236
		DoOutJob(PrintFile, "RasterObject.Colorspace=sRGB;",0,0);
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
	}
	else if	 (header->cupsColorSpace == CUPS_CSPACE_K)
	{
		DoLog("CUPS_CSPACE_K (%d)\n",header->cupsColorSpace,0);
		DoOutJob(PrintFile, "PrintColorspace=Grayscale;",0,0);
		DoOutJob(PrintFile, "AntiBleedControl=Off;",0,0);
		DoOutJob(PrintFile, "Resolution=%dx%d;", ResX, ResY);
		DoOutJob(PrintFile, "RasterObject.BitsPerPixel=%d;",OutBitsPerPixel,0);
		DoOutJob(PrintFile, "RasterObject.Colorspace=Mono;",0,0);
	}
	else	
	{
		DoLog("CUPS_CSPACE_??  (%d)\n",header->cupsColorSpace,0);
	}

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

void
EndPage(void) //Finish a page of graphics.
{
	DoOutJob(PrintFile, "Event=EndOfPage;",  0,0);
261 262
	if(PrintFile) fflush(PrintFile);
	FreeBuffers(); /* Free memory... allocated by AllocateBuffers() */
263 264 265 266 267 268 269
}

void
CancelJob(int sig)	/* - Cancel the current job... I - Signal */
{
  	(void)sig;
  	DoLog("CancelJob: job cancelled by signal\n",0,0);
270
	CloseLogging();
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
  	Canceled = 1;
}


unsigned char Byte0(int In)
{
int Byte0Mask = 0xFF;
return (In & Byte0Mask);
}

unsigned char Byte1(int In)
{
int Byte0Mask = 0xFF;
return ((In>>8) & Byte0Mask);
}

287 288 289 290 291 292
int Composite(unsigned char Buffer[],int Posn)
{
//returns the sum of the 3 colour components corresponding to Posn, assuming chunked colour order
return (Buffer[Posn*3]+Buffer[Posn*3+1]+Buffer[Posn*3+2]);
}

293 294 295 296 297 298 299 300 301 302 303
/*
 * 'main()' - Main entry and processing of driver.
 */

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			y, c;		
304 305 306
	int			i;
	int 			PixelsLeft,PixelsRight; //count blank pixels at right and left of raster line
	int			PixelsNonBlank; //in a raster line
307 308
	int			DataLength; //length of an uncompressed data line
	unsigned char		MinOut=255, MaxOut=0; //to check the range of the dithered output or raster
309
	int			CheckCount, ret, have, flush;
310
	FILE	*CompData; //stores the compressed data of a page
311
	char RasFileName[100]="";
312 313
	int BytesToPrint; 
	int CurrentChunkSize; 
314 315 316 317 318 319 320 321 322 323 324 325 326

	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 */

 /*
  * Check command-line...
  */
  	if (argc < 6 || argc > 7) //wrong no of arguments
  	{
327 328
    	fprintf(stderr, ("Usage: %s job-id user title copies options [file]\n"), "rastertoek");
   		return (1);
329
  	}
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
        
	char data;
	int datalen;
	cups_sc_bidi_t bidi;
	cups_sc_status_t status;
        
	/* Tell cupsSideChannelDoRequest() how big our buffer is... */
	datalen = 1;

	/* Get the bidirectional capabilities, waiting for up to 1 second */
	status = cupsSideChannelDoRequest(CUPS_SC_CMD_GET_BIDI, &data, &datalen, 1.0);

	/* Use the returned value if OK was returned and the length is still 1 */
	if (status == CUPS_SC_STATUS_OK && datalen == 1) {
		bidi = (cups_sc_bidi_t) data;
		DoBack = (bidi == CUPS_SC_BIDI_NOT_SUPPORTED ? 0 : 1);
	} 
	else {
		bidi = CUPS_SC_BIDI_NOT_SUPPORTED;
		DoBack = 0;
	}
351

352 353 354 355 356 357 358
#if DEBUGFILES == 1
	SetupLogging("c2espC",DoBack,"/tmp/KodakPrintLog");
#else
	SetupLogging("c2espC",DoBack,"");
#endif

  	setbuf(stderr, NULL);
359
	fprintf(stderr, ("DEBUG:  ================= %s ===================================\n"),Version); 
360
	DoLogString("Starting %s\n",Version);
361
        DoLog("Compiled with DEBUGFILES = %d, TESTING = %d\n", DEBUGFILES, TESTING);
362 363 364 365 366 367 368 369 370 371 372
        
	DoLog("Number of command line parameters: %d\n", argc, 0);
	int argi;
	for (argi = 0; argi < argc; argi++) {
		DoLogString("  param: '%s'\n", argv[argi]);
	}
	DoLogString("End of command-line parameters\n", "");

	DoLogString("Bi-di/backchannel support: %s\n", (bidi == CUPS_SC_BIDI_NOT_SUPPORTED ? "false" : "true"));
	DoLog("DoBack value: %d\n", DoBack, 0);
        
373 374
	MarkerSetup();

375 376 377 378 379
 /*
  * Open the page stream...
  */
  	if (argc == 7)
  	{
380 381 382 383 384 385
		if ((fd = open(argv[6], O_RDONLY)) == -1)
   		{
   			fprintf(stderr, ("ERROR: c2espC: Unable to open raster file - %s\n"), strerror(errno));
   			sleep(1);
   			return (1);
   		}
386 387
  	}
  	else    fd = 0;
388
   	fprintf(stderr, ("DEBUG: c2espC: opening raster\n")); 
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
  	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...
  */

#if DEBUGFILES == 1
	PrintFile = fopen("/tmp/KodakPrintFile", "w");//open the print file
	sleep(3); //does this help chmod to work?
	chmod("/tmp/KodakPrintFile", S_IRUSR | S_IWUSR | S_IROTH ); //let anyone read it
#endif

/* read the first header */
	if(cupsRasterReadHeader2(ras, &header))
	{
		DoLog("First page Header read after %d sec\n", time(NULL)-StartTime,0);
		SetPaperSize(KodakPaperSize, header.PageSize[1]);
422
		SetupPrinter(&header);
423
		DoLog("Printer should be ready by now\n",0,0);
424 425 426 427 428 429 430 431
  /* 
  * Process pages as needed...
  */
  		Page = 0;
		do //start of loop for each page
  		{
			DoLog("Header read\n", 0,0);

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

435
   			Page ++;
436 437 438 439 440
   			DoLog("PAGE %d COPIES %d\n", Page, header.NumCopies);

   /*
    * Start the page...
    */
441
   			if ( !(CompData=tmpfile()) )
442
			{
443 444 445
       			perror("opening compressed page temp file"); 
       			abort(); 
   			} 
446 447 448

			OutBitsPerPixel = header.cupsBitsPerColor;

449
			if (header.cupsColorSpace == CUPS_CSPACE_CMY) //colour
450
			{
451
				DoLog("cupsColorSpace = %d = CUPS_CSPACE_CMY\n", header.cupsColorSpace, 0);
452
				fprintf(stderr, "INFO: c2espC: p%d Colour %d\n", Page, ColourPercent);
453
				sprintf(RasFileName,"/tmp/RasForComp.ppm");
454 455 456
			}
			else if (header.cupsColorSpace == CUPS_CSPACE_K)//monochrome
			{
457
				DoLog("cupsColorSpace = %d = CUPS_CSPACE_K\n", header.cupsColorSpace, 0);
458
				fprintf(stderr, "INFO: c2espC: p%d Monochrome\n",Page);
459 460 461 462 463 464 465 466
				sprintf(RasFileName,"/tmp/RasForComp.pbm");
			}
			else 
			{
				DoLog("Unknown cupsColorSpace = %d\n", header.cupsColorSpace, 0);
				DoLog("Allowed cupsColorSpace = %d or %d\n", CUPS_CSPACE_CMY, CUPS_CSPACE_K);
			}
			DoLog("cups raster w = %d h = %d\n", header.cupsWidth, header.cupsHeight);
467 468
			DoLog("cups raster bytes per line = %d bits per line = %d\n", header.cupsBytesPerLine,
				header.cupsBytesPerLine * 8);
469
#if DEBUGFILES == 1
470
			//open raster file here
471 472 473
			fprintf(stderr, "INFO: c2espC: Opening %s\n",RasFileName);
   			remove(RasFileName);
			dfp = fopen(RasFileName, "w");
474
			UncompressedFile = fopen("/tmp/KodakUncompressed", "w"); //open the file
475 476
			sleep(3); //does this help chmod to work?
			chmod(RasFileName, S_IRUSR | S_IWUSR | S_IROTH ); //let anyone read it
477
			chmod("/tmp/KodakUncompressed", S_IRUSR | S_IWUSR | S_IROTH ); //let anyone read it
478

479 480 481 482 483 484 485 486
   			if (dfp && header.cupsColorSpace == CUPS_CSPACE_CMY) 
			{
				fprintf(dfp, "P6\n%8d %8d %8d\n", header.cupsWidth, header.cupsHeight, 255);
			}
   			if (dfp && header.cupsColorSpace == CUPS_CSPACE_K) 
			{
				fprintf(dfp, "P5\n%8d %8d %8d\n", header.cupsWidth, header.cupsHeight, 255);
			}
487 488 489 490 491 492 493 494
#endif

			AllocateBuffers(&header);
			MinOut=255;MaxOut=0; //initialise

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

495
			//prepare for compression
496
			DoLog("initialising Compression\n",0,0);
497

498 499 500 501 502 503
   			/* allocate deflate state */
   			strm.zalloc = Z_NULL;
   			strm.zfree = Z_NULL;
   			strm.opaque = Z_NULL;
   			ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION);
   			if (ret != Z_OK) 
504 505 506
			{ //was return ret;
				DoLog("Deflate did not initialise properly\n", 0, 0);
			}
507
        				
508 509
   			for (y = 0; (y < header.cupsHeight); ++y )  // Loop for each line of the page
   			{
510 511
   				if (Canceled) break;
				/* Keep the printer connection awake, added if (DoBack) 15/1/12 */
512
				if (DoBack) KeepAwakeStart = KeepAwake(KeepAwakeStart,10, PrintFile); 
513 514 515 516 517 518 519 520 521

				//read a line 
				if (!cupsRasterReadPixels(ras, CupsLineBuffer, header.cupsBytesPerLine)) break;
				//check for max and min either colour or mono
				for(i=0;i<(header.cupsBytesPerLine);++i)
				{
					if (CupsLineBuffer[i]>MaxOut) MaxOut=CupsLineBuffer[i];
					if (CupsLineBuffer[i]<MinOut) MinOut=CupsLineBuffer[i];
				}
522 523 524 525 526
				//turn the line into print data

				if (header.cupsColorSpace == CUPS_CSPACE_CMY) //colour - should be 3 x 8 bits per pixel
				{
					//count left blank pixels for colour - edit me
527 528
					for(PixelsLeft=0;(Composite(&CupsLineBuffer[0], PixelsLeft)==0 
						&& PixelsLeft<header.cupsWidth);++PixelsLeft);
529 530
					//count right blank pixels for colour - edit me
					if(PixelsLeft==header.cupsWidth) PixelsRight=0;
531 532
					else for(PixelsRight=0;Composite(&CupsLineBuffer[0], 
						header.cupsWidth-1-PixelsRight)==0 && (PixelsRight < header.cupsWidth);++PixelsRight);
533 534 535 536 537 538

				} //end of colour section

				else //monochrome - should be 8 bits per pixel
				{
					//count left blank pixels for mono
539 540
					for(PixelsLeft=0;(CupsLineBuffer[PixelsLeft]==0 
						&& PixelsLeft<header.cupsWidth);++PixelsLeft);
541 542
					//count right blank pixels for mono
					if(PixelsLeft==header.cupsWidth) PixelsRight=0;
543 544
					else for(PixelsRight=0;((CupsLineBuffer[header.cupsWidth-1-PixelsRight]==0) 
						&& (PixelsRight<header.cupsWidth));++PixelsRight);
545 546 547
				} //end of mono
// common mono and colour
				PixelsNonBlank=header.cupsWidth-PixelsLeft-PixelsRight;
548 549 550 551
				//log the counts
	//fprintf(LogFile,"%d+%d+%d=%d\n",PixelsLeft,PixelsNonBlank,PixelsRight,PixelsLeft+PixelsRight+PixelsNonBlank);
				//generate the data
				//LHS blanks - maybe should not do if PixelsLeft==0 ? need a test file
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
				DataLineBuffer[0]=Byte0(PixelsLeft);
				DataLineBuffer[1]=Byte1(PixelsLeft);
				DataLineBuffer[2]=0x01;
				DataLineBuffer[3]=0x00;
				DataLength=4;

				if(PixelsLeft<header.cupsWidth) //there are some non blank pixels
				{
					DataLineBuffer[4]=Byte0(PixelsNonBlank);
					DataLineBuffer[5]=Byte1(PixelsNonBlank);
					DataLineBuffer[6]=0x00;
					DataLineBuffer[7]=0x00;
					DataLength+=4;

					if (header.cupsColorSpace == CUPS_CSPACE_CMY) //colour - should be 3 x 8 bits per pixel
567
					{
568
						for(i=0;i<PixelsNonBlank;++i) 
569
						{
570 571 572
							DataLineBuffer[8+i]=CupsLineBuffer[(PixelsLeft+i)*3+0];
							DataLineBuffer[8+PixelsNonBlank+i]=CupsLineBuffer[(PixelsLeft+i)*3+1];
							DataLineBuffer[8+PixelsNonBlank*2+i]=CupsLineBuffer[(PixelsLeft+i)*3+2];
573
						}
574
						DataLength+=PixelsNonBlank*3;
575
					}
576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
					else // its mono 1 x 8 bits per pixel
					{
						for(i=0;i<PixelsNonBlank;++i) DataLineBuffer[8+i]=CupsLineBuffer[PixelsLeft+i];
						DataLength+=PixelsNonBlank;
					}
				// for colour and mono						
					if(PixelsRight>0) //there are some RHS blanks
					{
							DataLineBuffer[DataLength+0]=Byte0(PixelsRight);
							DataLineBuffer[DataLength+1]=Byte1(PixelsRight);
							DataLineBuffer[DataLength+2]=0x01;
							DataLineBuffer[DataLength+3]=0x00;
							DataLength+=4;
					}
				}
				if(UncompressedFile) fwrite(DataLineBuffer, 1, DataLength, UncompressedFile);
592

593 594 595 596 597
				//send data to the compression
        		strm.next_in = DataLineBuffer;
				strm.avail_in = DataLength;
				if(y >= header.cupsHeight-1) flush=Z_FINISH;
				else flush = Z_NO_FLUSH;
598

599
        		/* run deflate() on input until output buffer not full  */
600 601 602 603
        		do 
				{
            		strm.avail_out = CHUNK;
            		strm.next_out = out;
604
//		DoLog("deflating %d byte input\n",strm.avail_in,0);
605
            		ret = deflate(&strm, flush);
606
					if(ret == Z_STREAM_ERROR) break;
607 608 609 610 611 612 613 614 615 616 617
            		have = CHUNK - strm.avail_out;
					if(have>0) DoLog("writing deflated output %d bytes\n",have,0);
            		if (fwrite(out, 1, have, CompData) != have || ferror(CompData)) 
					{
                		(void)deflateEnd(&strm);
						ret = Z_STREAM_ERROR; //kludge
						break;
            		}
        		} while (strm.avail_out == 0);

				if(ret == Z_STREAM_ERROR) break;
618 619

#if DEBUGFILES == 1
620
//store the raster for debugging - may need changing for colour
621
	    		if (dfp) fwrite(CupsLineBuffer, 1, header.cupsBytesPerLine, dfp);
622 623
#endif

624
    		} //end of line loop
625 626 627 628 629
			if(ret == Z_STREAM_ERROR)
			{
				DoLog("Compression error no %d\n",Z_ERRNO,0);
			}

630 631
 			/* end of compression */
			//put compressed byte count into output file and copy compressed data in
632
    		(void)deflateEnd(&strm);
633
			DoLog("Compression finished\n",0,0);
634 635
			DoLog("Rewinding compressed page file\n",0,0);
			rewind(CompData);
636 637 638 639

/* Old version sent data all in one go. This seems to cause a problem with smb shared printers.

			DoOutJob(PrintFile, "RasterObject.Data#%d=", strm.total_out,0);
640
			CheckCount=0;
641 642
			while((c=fgetc(CompData)) != EOF) 
			{
643
#if DEBUGFILES == 1
644
				fputc(c,PrintFile);
645
#endif
646 647 648
#if TESTING == 0
				fputc(c,stdout);
#endif
649
				++CheckCount;
650 651 652
			}
			DoOutJob(PrintFile, ";",0,0); //one semi colon after the data
			fflush(PrintFile);
653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
*/

// New version with chunk size limit 15/1/12
			CheckCount=0;
			BytesToPrint = strm.total_out; 
			CurrentChunkSize = 0; 
			while (BytesToPrint > 0) { 
				if (BytesToPrint <= MAXDATACHUNK) { 
					CurrentChunkSize = BytesToPrint; 
				} 
				else { 
					CurrentChunkSize = MAXDATACHUNK; 
				} 
				//send one chunk of data 
				DoOutJob(PrintFile, "RasterObject.Data#%d=", CurrentChunkSize,0);
				//if(cbarg != NULL) rc = fwrite(CurrentChunkStart, 1, CurrentChunkSize, cbarg); 
				DoLog("GZIP data chunk is sent to printer\n",0,0); 
				//rc = fwrite(CurrentChunkStart, 1, CurrentChunkSize, stdout); //also to output 
				for(i=0;i<CurrentChunkSize;++i)
				{
					c=fgetc(CompData);
#if DEBUGFILES == 1
					fputc(c,PrintFile);
#endif
#if TESTING == 0
					fputc(c,stdout);
#endif
					++CheckCount;
				}

				DoOutJob(PrintFile, ";",0,0); //one semi colon after the chunk 
				BytesToPrint -= CurrentChunkSize; 
			} 

687
			DoLog("Compressed data copied to output expect %d read %d\n",strm.total_out,CheckCount);
688 689 690 691
 
			DoLog("Page raster built at %d sec\n",time(NULL)-StartTime,0);
			DoLog("Max and min in raster are %d %d\n",MaxOut,MinOut);

692 693 694 695 696 697
			//page is finished

#if DEBUGFILES == 1
			//close the debug files
			if(UncompressedFile) fclose(UncompressedFile);
			if(dfp) fclose(dfp);
698
#endif
699
    		EndPage();
700 701 702 703 704 705 706
   			if (Canceled)    break;
  		}
		while (cupsRasterReadHeader2(ras, &header));

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

707 708
	if (Page > 0) ShutdownPrinter();

709
 /*
710
  * Close the raster stream... 
711 712 713 714 715 716 717
  */
 	cupsRasterClose(ras);
	DoLog("cups raster closed after %d sec\n",time(NULL)-StartTime,0);
  	if (fd != 0)   close(fd);
 /*
  * Termination, send an error message if required...
  */
718
	DoLog("c2espC terminating after %d sec. Processed %d pages\n",time(NULL)-StartTime,Page);
719 720 721

  	if (Page == 0)
  	{
722 723
    	DoLog("ERROR: c2espC: No pages found!\n",0,0);
    	return (1);
724 725 726
  	}
  	else
  	{
727
		CloseLogging();
728 729
		if(PrintFile != NULL) fclose(PrintFile);

730
#if DEBUGFILES == 1
731
		//let anyone read the files How much delay is needed if any?
732
		// sleep(3); //does this help chmod to work?
733 734 735 736
		chmod("/tmp/KodakCompPage", 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
#endif
737
		//cups seems to replace this by "Ready to print" so you don't see it
738 739
    	//fprintf(stderr, "INFO: Done - Blk %d Col %d percent\n",BlackPercent, ColourPercent);
		fprintf(stderr, "INFO: Done\n");
740
		return (0);
741 742
  	}
}