1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
|
/*
*
* Kodak ESP Cxxx (OPL?) Control Language filters for the Common UNIX
* Printing System (CUPS).
* common functions for c2esp, c2espC filters
*
* copyright Paul Newall May 2010 - Nov 2011. VERSION 2 (first used in c2esp22~rc1)
*
*/
#include <stdio.h>
#include <string.h>
#include <cups/sidechannel.h> //FlushBackChannel, and the side channel functions and constants
#include <fcntl.h> //files
#include <sys/stat.h> //for chmod
#include <cups/driver.h> //has the dither functions and other things like header defn
#include <time.h> //time functions used for debugging
#include "c2espcommon.h" //the common library
/*
* Constants...
*/
unsigned char NL = 10;
/*
* Globals...
*/
char CallerName[50]; /* String that identifies the calling program */
int DoBack; /* Enables the back channel comms */
char BackBuf[32000]; //for the back channel replies from the printer
int BackBufLen=sizeof(BackBuf)-1;
FILE *LogFile = NULL; //file descriptor for log file
time_t StartTime;
int BlackPercent, ColourPercent;
void SetupLogging(char *ExtCallerName, int ExtDoBack, char *ExtLogFileName)
{
strcpy(CallerName,ExtCallerName);
DoBack=ExtDoBack;
if(strlen(ExtLogFileName)>0)
{
remove(ExtLogFileName); //to be sure I only see the latest
LogFile = fopen(ExtLogFileName, "w"); //open the log file
sleep(3); //does this help chmod to work?
chmod(ExtLogFileName, S_IRUSR | S_IWUSR | S_IROTH ); //let anyone read it
setbuf(LogFile, NULL);
fprintf(LogFile, "KodakPrintLog %s\n",ExtCallerName);
}
StartTime = time(NULL);
}
void CloseLogging()
{
if(LogFile)
{
DoLog("Closing log\n",0,0);
fclose(LogFile);
}
}
void DoLog(char *PrintFormat, int I1, int I2)
{
//prints a line with 2 integers to the log file and the cups error log
char CupsFormat[200];
strcpy(CupsFormat, "DEBUG: ");
strcat(CupsFormat,CallerName);
strcat(CupsFormat,":%d : ");
strncat(CupsFormat,PrintFormat,150); //crop PrintFormat to avoid FAILING WITH BUFFER OVERFLOW
// add \n if not \n at the end of cupsformat
if(CupsFormat[strlen(CupsFormat)-1] != NL) strcat(CupsFormat,"\n");
fprintf(stderr, CupsFormat, time(NULL)-StartTime, I1, I2);
if (LogFile != NULL) fprintf(LogFile, CupsFormat, time(NULL)-StartTime, I1, I2);
}
void DoLogString(char *PrintFormat, char *String)
{
//prints a line with a string to the log file and the cups error log
char CupsFormat[200];
strcpy(CupsFormat, "DEBUG: ");
strcat(CupsFormat,CallerName);
strcat(CupsFormat,":%d : ");
strncat(CupsFormat,PrintFormat,150); //crop PrintFormat to avoid FAILING WITH BUFFER OVERFLOW
fprintf(stderr, CupsFormat, time(NULL)-StartTime, String);
if (LogFile != NULL) fprintf(LogFile, CupsFormat, time(NULL)-StartTime, String);
}
/* DoOutJob used to enable one call to send to the specified job file and to stdout (if not testing)
And log the result */
void DoOutJob(FILE *OutFile, char *PrintFormat, int I1, int I2)
{
int BytesRead = 0; //int because cupsBackChannel can return -1
char Display[80];
char LogFormat[200];
int i;
if (OutFile) fprintf(OutFile, PrintFormat, I1, I2); //to the specified file
strcpy(LogFormat, "-> ");
strcat(LogFormat,PrintFormat);
DoLog(LogFormat, I1, I2); //and the log
#if TESTING == 0
fprintf(stdout, PrintFormat, I1, I2); //and to the output
fflush(stdout);
if(DoBack)
{
BytesRead = cupsBackChannelRead(BackBuf, BackBufLen, 0.5); //read the reply from printer
if(BytesRead >= 1)
{
if(BytesRead<BackBufLen) BackBuf[BytesRead]=0; //add null terminator NB BytesRead==-1 if nothing read
for(i=0;i<79;++i) Display[i] = BackBuf[i]; //copy the first 79 chars to Display
Display[79] = 0; //add null terminator
DoLogString("Reply = %s\n", Display);
}
else DoLog("No reply\n", 0,0);
}
#endif
// KeepAwakeStart = time(NULL); // reset timer
}
/* 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)
{
DoLogString("<did DRAIN_OUTPUT %s>\n", IdString);
return(1);
}
else
{
if(status == CUPS_SC_STATUS_TIMEOUT) DoLogString("<Failed DRAIN_OUTPUT %s = Timeout>\n", IdString);
else if(status == CUPS_SC_STATUS_IO_ERROR) DoLogString("<Failed DRAIN_OUTPUT %s = IO error>\n", IdString);
else if(status == CUPS_SC_STATUS_NOT_IMPLEMENTED) DoLogString("<Failed DRAIN_OUTPUT %s = not implemented>\n", IdString);
else DoLogString("<Failed DRAIN_OUTPUT %s = unknown reason>\n", IdString);
return(0);
}
}
else return(0);
}
/* GoodExchange now matches against substrings in the reply so we can cope with queued messages better. Thanks to Gordon for this improvement to GoodExchange 4/11/11
The UnexpectedLogLimit added 11/11/11
Note that strtok() replaces the delimiters by null bytes, so you can't search the buffer easily afterwards.
It returns the nubmer of bytes read if the reply includes the one expected,
otherwise -(the number of bytes read) if the reply did not include Expect, or 0 if there was no reply */
int GoodExchange(FILE *PrintFile, char *Command, char *Expect, int DoBack, unsigned int SleepTime, float ReplyTime)
{
int BytesRead = 0; //int because cupsBackChannel can return -1
char Display[80];
int i;
int UnexpectedCount = 0;
const int UnexpectedLogLimit = 5; //stops the log file being filled with Status replies due to keep awake.
char * Token1;
char * TokenList;
const char * Delimiters = ";&"; // ; for normal replies & for device.status? requests....
// don't actually need the info but nice to be able to read it all.....
int ReturnSign = -1; //assume we won't find the string we want
int BlackPercentFound, ColourPercentFound;
DoLogString("-> %s\n", Command); //now also sends to stderr
if(PrintFile) fprintf(PrintFile, "%s", Command); //to the global print file
#if TESTING == 0
fprintf(stdout, "%s", Command); //printer command
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)
#endif
if(DoBack)
{
BytesRead = cupsBackChannelRead(BackBuf, BackBufLen, ReplyTime); //read the reply from printer
if(BytesRead < 1)
{
DoLog("No reply\n",0,0);
return 0;
}
else
{
BackBuf[BytesRead]=0; //add null terminator NB BytesRead==-1 if nothing read
//fprintf(stderr,"thisline->%s\n",BackBuf);
// search for any special replies here before strtok changes the buffer
BlackPercentFound=MarkerPercent(BackBuf,0);
ColourPercentFound=MarkerPercent(BackBuf,1);
if(BlackPercentFound >= 0) BlackPercent=BlackPercentFound;
if(ColourPercentFound >= 0) ColourPercent=ColourPercentFound;
TokenList = BackBuf;
Token1 = strtok(TokenList , Delimiters);
while (Token1 != NULL)
{
for(i=0;i<79;++i) Display[i] = Token1 [i]; //copy the first 79 chars to Display
if (strncmp(Token1 , Expect, strlen(Expect) -1) == 0) //reduce string length by 1 as ; removed
{
DoLogString("Expected reply = %s\n", Display);
ReturnSign = 1;
}
else
{
// limit the number of unexpected replies that are logged
if(UnexpectedCount <= UnexpectedLogLimit) DoLogString("Unexpected reply = %s\n", Display);
++UnexpectedCount;
//ReturnSign defaults to unexpected unless changed by a single occurance of expected...
//so don't alter it here!
}
Token1 = strtok(NULL , Delimiters);
}
return (ReturnSign * BytesRead);
}
}
return 0;
}
int
MarkerPercent(char *Buf, int GetColour) /* GetColour = 1 for "Color" or 0 for "Black" */
{
/* search for the ink data in the buffer. Returns -1 if not found */
char *MarkerLevelString;
if(GetColour) MarkerLevelString = strstr(Buf, "DeviceStatus.Printer.InkLevelPercent.Color=");
else MarkerLevelString = strstr(Buf, "DeviceStatus.Printer.InkLevelPercent.Black=");
if (MarkerLevelString)
{
//DoLog("Found marker level",0,0);
MarkerLevelString = strstr(MarkerLevelString, "=");
if (MarkerLevelString)
{
if(strncmp(MarkerLevelString + 1,"F",1)==0)
{
DoLog("Found marker %d level Full = %d",GetColour,100);
return (100);
}
else
{
DoLog("Found marker %d level %d",GetColour, atoi(MarkerLevelString + 1));
return (atoi(MarkerLevelString + 1));
}
}
}
else
{
//DoLog("Failed to find marker %d level",GetColour,0);
return -1;
}
return -1;
}
void
MarkerSetup()
{
fprintf(stderr, "ATTR: marker-colors=black,magenta\n"); //displays ink drops in printer manager
fprintf(stderr, "ATTR: marker-names=black,colour\n");
}
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 421 : // A6
strcpy(Size, "MediaSize=iso_a6_105x148mm;");
break;
case 432 : // Photo 4x6"
strcpy(Size, "MediaSize=na_index4x6_4x6in;");
break;
case 504 : // Photo 5x7"
strcpy(Size, "MediaSize=na_5x7_5x7in;");
break;
case 540 : // Monarch Envelope
break;
case 595 : // A5
strcpy(Size, "MediaSize=iso_a5_148x210mm;");
break;
case 624 : // DL Envelope
strcpy(Size, "MediaSize=iso_dl_110x220mm;");
break;
case 649 : // EnvC5 Envelope
strcpy(Size, "MediaSize=iso_c5_162x229mm;");
break;
case 684 : // Env10 Envelope
strcpy(Size, "MediaSize=na_number10_4.125x9.5in;");
break;
case 709 : // B5 Envelope
strcpy(Size, "MediaSize=iso_b5_176x250mm;");
break;
case 720 : // Photo 8x10"
strcpy(Size, "MediaSize=na_govtletter_8x10in;");
break;
case 756 : // Executive
strcpy(Size, "MediaSize=na_executive_7.25x10.5in;");
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
strcpy(Size, "MediaSize=na_legal_8.5x14in;");
break;
case 1191 : // A3
break;
case 1224 : // Tabloid
break;
}
}
void
DisplayHeader(cups_page_header2_t *header)
{
/*
* Show page device dictionary...
*/
DoLog("StartPage...\n",0,0);
DoLogString("MediaClass = \"%s\"\n", header->MediaClass);
DoLogString("MediaColor = \"%s\"\n", header->MediaColor);
DoLogString("MediaType = \"%s\"\n", header->MediaType);
DoLogString("OutputType = \"%s\"\n", header->OutputType);
DoLog("AdvanceDistance = %d\n", header->AdvanceDistance,0);
DoLog("AdvanceMedia = %d\n", header->AdvanceMedia,0);
DoLog("Collate = %d\n", header->Collate,0);
DoLog("CutMedia = %d\n", header->CutMedia,0);
DoLog("Duplex = %d\n", header->Duplex,0);
DoLog("HWResolution = [ %d %d ]\n", header->HWResolution[0], header->HWResolution[1]);
DoLog("ImagingBoundingBox = [ %d %d", header->ImagingBoundingBox[0], header->ImagingBoundingBox[1]);
DoLog(" %d %d ]\n", header->ImagingBoundingBox[2], header->ImagingBoundingBox[3]);
DoLog("InsertSheet = %d\n", header->InsertSheet,0);
DoLog("Jog = %d\n", header->Jog,0);
DoLog("LeadingEdge = %d\n", header->LeadingEdge,0);
DoLog("Margins = [ %d %d ]\n", header->Margins[0], header->Margins[1]);
DoLog("ManualFeed = %d\n", header->ManualFeed,0);
DoLog("MediaPosition = %d\n", header->MediaPosition,0);
DoLog("MediaWeight = %d\n", header->MediaWeight,0);
DoLog("MirrorPrint = %d\n", header->MirrorPrint,0);
DoLog("NegativePrint = %d\n", header->NegativePrint,0);
DoLog("NumCopies = %d\n", header->NumCopies,0);
DoLog("Orientation = %d\n", header->Orientation,0);
DoLog("OutputFaceUp = %d\n", header->OutputFaceUp,0);
DoLog("PageSize = [ %d %d ]\n", header->PageSize[0], header->PageSize[1]);
DoLog("Separations = %d\n", header->Separations,0);
DoLog("TraySwitch = %d\n", header->TraySwitch,0);
DoLog("Tumble = %d\n", header->Tumble,0);
DoLog("cupsWidth = %d\n", header->cupsWidth,0);
DoLog("cupsHeight = %d\n", header->cupsHeight,0);
DoLog("cupsMediaType = %d\n", header->cupsMediaType,0);
DoLog("cupsBitsPerColor = %d\n", header->cupsBitsPerColor,0);
DoLog("cupsBitsPerPixel = %d\n", header->cupsBitsPerPixel,0);
DoLog("cupsBytesPerLine = %d\n", header->cupsBytesPerLine,0);
DoLog("cupsColorOrder = %d\n", header->cupsColorOrder,0);
DoLog("cupsColorSpace = %d\n", header->cupsColorSpace,0);
DoLog("cupsCompression = %d\n", header->cupsCompression,0);
}
|