/* * @(#)pcoverlay.c--Convert a pcl document into an overlay file * @(#)STS/KBS 03july2001 * * Inspired by 'ovl' Hewlett Packard PCL Laser Overlay creation utility * by Peter Hamilton, http://www.hamil.demon.co.uk/pcl/ * * This is a complete re-write with somewhat more sophiscated escape * sequence parsing. I basicly stole his setup for the overlay macro * and his list of sequences to delete from the stream. * * Peter's original was distributed under the GNU Version 2, June 1991 * General Public License. I don't know what that means for my * re-write. I am imposing no restrictions on my version. * Kevin Smith, November 2001, kbs@shady.com */ #include #include int ProcessEscapes(void); void PclWrite(char *,int); int landscape = -1; /* 0 = portrait, 1 = landscape */ int tobytes; /* total bytes output */ int tibytes; /* total bytes input */ char obuf[4096]; /* output buffer */ char *op; /* current pointer in the output */ int debug; /* != 0 == debug */ int main( int ac, char *av[] ) { int c; char cc; int rawdata; /* Transparent data size */ rawdata = 0; /* Nothing to start with */ op = obuf; /* Initialize the output buffer */ if(ac > 1) { if(strcmp(av[1],"-d") == 0) { debug++; } else { fprintf(stderr,"usage: %s [-d]\n",av[0]); return(1); } } while ((c = getchar()) != EOF) { tibytes++; if ( rawdata > 0 ) { rawdata--; } else { switch(c) { /* * Process escape sequences * * Function returns the number of raw data bytes which * follow the escape sequence and which should be passed * unmodified */ case 0x1b: rawdata = ProcessEscapes(); continue; break; /* * Ignore form feeds in the overlay */ case '\f': continue; } } cc = c; PclWrite(&cc,1); } PclWrite(NULL,-1); if(debug) { fprintf(stderr,"%d bytes in, %d bytes out\n",tibytes,tobytes); } return(0); } #define STATE_TYPE1 0 /* first char of sequence */ #define STATE_TYPE2 1 /* possible second char */ #define STATE_ARG 2 /* sub-args to sequence */ #define STATE_NUM1 3 /* start of a number */ #define STATE_NUM2 4 /* continuation of a number */ #define STATE_CMD 5 /* command letter */ /* * Command terminators * * An upper case letter, an equal sign, or an `at' sign * Also 9 is a special sequence */ #define ISTERM(x) (isupper(x) || (x)=='=' || (x)=='@' || (x)=='9') /* * Start of a number * * A digit, +, or - */ #define ISNUM1(x) (isdigit(x) || (x)=='-' || (x)=='+') /* * Continuation of a number * * A digit */ #define ISNUM2(x) (isdigit(x)) /* * Delete an escape sequence * * sp points to the start of arguments after the lead in sequence * cp points to the beginning of the current command/argument * * If we havn't identified any sequences or we have just a short * single sequence, just delete the whole thing * * Otherwise, delete the current command. * If there is a previous command, convert it to upper case if we were the * last command else leave it alone. * If there is no previous command, delete the whole sequence */ #define DELETEARG \ *ep = 0; \ if(cp && sp) { \ char dac; \ if(debug) {\ fprintf(stderr,"DELETE: ebuf=[%s], sp=[%s], cp=[%s]\n",ebuf,sp,cp); \ } \ dac = ep[-1]; \ ep = cp; \ if (ep <= sp) { \ if (ISTERM(dac)) { \ ep = ebuf; \ } \ } else { \ if (isupper(dac) && islower(ep[-1])) { \ ep[-1] = toupper(ep[-1]); \ } \ } \ } else { \ ep = ebuf; \ } /* * Compute the numeric value of the current argument */ #define NUMVAL(x) \ { \ char numbuf[10]; \ if (ep - cp > 0 && ep - cp < sizeof(numbuf)-1) \ { \ strncpy(numbuf,cp,ep - cp); \ numbuf[ep - cp] = '\0'; \ x = atoi(numbuf); \ } \ } int ProcessEscapes(void) { char ebuf[1024]; char *ep; /* current offset in buffer */ char *ee; /* end of ebuf */ char *sp; /* start of sequence (after types) */ char *cp; /* start of current command */ int c; /* current character */ int state; /* current parsing state */ int rawdata; /* raw data bytes */ int terminate; /* termination flag */ int pels; /* landscape flag */ char dbuf[1024]; char *dp = dbuf; state = STATE_TYPE1; ep = ebuf; sp = NULL; cp = NULL; ee = ebuf + sizeof(ebuf); rawdata = 0; terminate = 0; pels = -1; /* -1=unknown, 0=portrait, 1=landscape */ /* * Loop getting chars till we hit a sequence terminator * or run out of room in the buffer or hit the end of file. * * We should only run out of room or hit EOF if we have * a corrupt input. */ while( terminate == 0 && ep < ee && (c = getchar()) != EOF ) { tibytes++; *dp++ = c; /* Save originals for debugging */ /* * Handle the beginning state for all escape sequences * * Check for single character escapes (first char is a terminator) * else go on to longer sequences * */ if (state == STATE_TYPE1) { if (ISTERM(c)) { state = STATE_CMD; } else { *ep++ = c; state = STATE_TYPE2; continue; } } /* * Handle the second character of an escape sequence * * If numeric, we are starting the arguments to the sequence * otherwise save the sequence char and then look for args */ if (state == STATE_TYPE2) { if(ISNUM1(c)) { state = STATE_ARG; } else { *ep++ = c; state = STATE_ARG; continue; } } /* * Starting a new argument * * If current char is numeric start numeric processing * else we have a command letter */ if (state == STATE_ARG) { cp = ep; /* remember where we started */ if (!sp) { sp = ep; } if (ISNUM1(c)) { state = STATE_NUM1; } else { state = STATE_CMD; } } /* * Process numeric qualifiers * * State one is the beginning of a number which * must be a digit, a '+', or a '-'. * * A digit rolls us over into continued number processing * otherwise save the sign and switch to continued numbers. * * If non-numeric then switch to command state */ if (state == STATE_NUM1) { if (ISNUM2(c)) { /* * Change to continued number processing * and drop into code */ state = STATE_NUM2; } else { if (ISNUM1(c)) { /* * Only one char allowed in num state 1 */ *ep++ = c; state = STATE_NUM2; continue; } else { state = STATE_CMD; } } } /* * Continued number processing * * Keep saving the number as long as we are seeing digits else * switch to command state */ if (state == STATE_NUM2) { if (ISNUM2(c)) { *ep++ = c; continue; } else { state = STATE_CMD; } } /* * Process an escape command */ if (state == STATE_CMD) { *ep++ = c; /* * Flag whether this was a terminating character */ terminate = ISTERM(c); /* * Ignore resets */ if (!sp && c == 'E') { DELETEARG; } /* * Ignore raster "rotate image" command * *rF */ else if ( strncmp(ebuf,"*r",2) == 0 && toupper(c) == 'F' ) { DELETEARG; } /* * Ignore certain page and control commands */ else if (strncmp(ebuf,"&l",2) == 0) { switch (toupper(c)) { case 'O': /* set page orientation */ NUMVAL(pels); case 'A': /* set page size */ case 'H': /* set paper source */ case 'G': /* ??? */ case 'P': /* set page length */ case 'S': /* simplex/duplex printing */ case 'X': /* number of copies */ DELETEARG; } } /* * Get the data length * *rF */ else if ( ( strncmp(ebuf,"&p",2) == 0 && toupper(c) == 'X' ) || ( strncmp(ebuf,")s",2) == 0 && toupper(c) == 'W' ) || ( strncmp(ebuf,"(s",2) == 0 && toupper(c) == 'W' ) || ( strncmp(ebuf,"*b",2) == 0 && toupper(c) == 'W' ) ) { NUMVAL(rawdata); } cp = NULL; state = STATE_ARG; } } *ep = '\0'; *dp = '\0'; if(debug) { fprintf(stderr,"[%s] -> [%s] (%d)\n",dbuf,ebuf,rawdata); } /* * Transmit whatever is left of the sequence */ if (ep > ebuf) { PclWrite("\033",1); PclWrite(ebuf,ep-ebuf); } /* * Record the landscape mode * If there are multiple modes, we remember the last one in the * first escape sequence containing modes. * * This is a hack for wordperfect which seems to start every file * with &l1o0o... Maybe switching back and forth will cause * a reset to default user values for margins etc (changing to an * current mode is ignored, hence the switch */ if(landscape == -1 && pels != -1) { landscape = pels; } return(rawdata); } /* * Write output codes * * Output is buffered to the size of obuf then flushed. * This gives us a delay to discover the landscape/portrait mode, * or whatever else, so we can construct our leadin sequence * * Custom leadin codes are prepended on the first flush. * Custom closeout codes are appended at the final flush. * * A length of -1 indicates the final flush */ void PclWrite( char *buf, int size ) { int wsize; static int first = 1; while(size != 0) { if(op-obuf == sizeof(obuf) || size < 0) { if(first) { first = 0; if(landscape == -1) { landscape = 0; } if(debug) { fprintf( stderr, "%s orientation\n", landscape == 0 ? "Portrait" : landscape == 1 ? "Landscape" : landscape == 2 ? "Reverse Portrait" : landscape == 3 ? "Reverse Landscape" : "Unknown" ); }; /* * This is our first flush so preface it with our stuff * * E Printer reset * &lnnnO Orientation (nnn = 0-3) * &f * 1y Macro ID 1 * 0x Start macro def. * 0S Push the current position. * *r0F Raster orientation matches document */ tobytes += printf( "\033E\033&l%dO\033&f1y0x0S\033*r0F",landscape ); } if (op > obuf) { tobytes += op-obuf; fwrite(obuf,sizeof(char),op-obuf,stdout); op = obuf; } /* * If final flush, ouput the closing string */ if(size < 0) { /* * Termination sequence * * &f... * 1s Pop the current position * 1x Stop macro def * 10x Make permanant * 4X Enable overlay */ tobytes += printf( "\033&f1s1x10x4X" ); return; } } /* * Figure out how much room is left in the output buffer * and copy up to that amount */ wsize = sizeof(obuf) - (op - obuf); if(wsize > size) wsize = size; memcpy(op,buf,wsize); op += wsize; buf += wsize; size -= wsize; } }