//////////////////////////////////////////////////////////
// pcl2bmp.cpp
//////////////////////////////////////////////////////////
#include <ctype.h>
#include <fstream.h>
#include <iostream.h>
#include <math.h>
#ifdef __WATCOMC__
#include <mem.h>
#else
#include <memory.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bmpio.h"
#include "pcl2bmp.h"
#include "rasterop.h"
#include "softfont.h"
#include "util.h"
static int px, py;
static int rasterx, rastery;
static int patternf;
static int patternx, patterny;
static int patternw, patternh;
static FONTNODE *curfont[2]; // primary & secondary
static int fontshift;
static int fontid;
static int charcode;
static unsigned char bits[3300][300];
static int dirtybits;
static BMPINFO bmpinfo;
static int debugtrace, debugerror;
int pcl2bmp( char *NamePcl, char *NameBmp );
int Execute( COMMAND& command, ifstream& ifPcl );
/////////////////////////////////////////////////////////////////
// main
/////////////////////////////////////////////////////////////////
main( int argc, char *argv[] )
{
bmpinfo.bits = (unsigned char *)bits;
bmpinfo.width = 2400;
bmpinfo.height = 3300;
bmpinfo.colours = 2;
bmpinfo.palette = NULL;
bmpinfo.cropx = bmpinfo.cropy = 0;
bmpinfo.scalex = bmpinfo.scaley = 1;
for( int i=1; i<argc; i++ )
{
if( argv[i][0] != '-' )
break;
if( !stricmp(argv[i], "-c") )
bmpinfo.cropx = bmpinfo.cropy = 1;
else if( !stricmp(argv[i], "-cx") )
bmpinfo.cropx = 1;
else if( !stricmp(argv[i], "-cy") )
bmpinfo.cropy = 1;
else if( !strnicmp(argv[i], "-sx", 3) )
bmpinfo.scalex = atoi( argv[i] + 3 );
else if( !strnicmp(argv[i], "-sy", 3) )
bmpinfo.scaley = atoi( argv[i] + 3 );
else if( !strnicmp(argv[i], "-s", 2) )
bmpinfo.scalex = bmpinfo.scaley = atoi( argv[i] + 2 );
else if( !stricmp(argv[i], "-trace") )
debugtrace = 1;
else if( !stricmp(argv[i], "-debug") )
debugerror = 1;
else
cerr << "bmp2pcl: unknown option " << argv[i] << endl;
}
if( bmpinfo.scalex < 1 )
bmpinfo.scalex = 1;
if( bmpinfo.scaley < 1 )
bmpinfo.scaley = 1;
if( argc - i < 2 )
{
cerr << endl;
cerr << "pcl2bmp: convert HP LaserJet print file to bitmap" << endl << endl;
cerr << " usage: pcl2bmp {options} infile.prn outfile" << endl;
cerr << " the output file(s) will be named outfile1.bmp, outfile2.bmp, ..." << endl << endl;
cerr << "options: -c crop top, bottom, left, and right edges" << endl;
cerr << " -cx crop left and right edges" << endl;
cerr << " -cy crop top and bottom edges" << endl;
cerr << " -s# shrink width and height by factor of '#'" << endl;
cerr << " -sx# shrink width by factor of '#'" << endl;
cerr << " -sy# shrink height by factor of '#'" << endl << endl;
cerr << " -trace output trace log to stdout" << endl;
cerr << " -debug output error log to stderr" << endl;
return 1;
}
pcl2bmp( argv[i], argv[i+1] );
return 0;
}
int pcl2bmp( char *NamePcl, char *NameBmp )
{
ifstream ifPcl( NamePcl, ios::in | ios::binary | ios::nocreate );
ifPcl.setf( ios::binary, ios::binary | ios::skipws );
int PageNo = 1;
char PageNameBmp[1024];
static COMMAND command0;
COMMAND command;
int phase = 0;
int ch;
ch = ifPcl.get();
while( ch != EOF )
{
switch( phase )
{
case 0: // search for ESC/control sequence
if( ch == 0x1B )
{ // ESC
command = command0;
command.control = ch;
phase++;
}
else if( ch < ' ' || ch == 0x7f )
{ // other control character
command.control = ch;
switch( command.control )
{
case 0x0C: // Form-Feed
if( dirtybits )
{
sprintf( PageNameBmp, "%s%d.bmp", NameBmp, PageNo );
bmp_save( PageNameBmp, &bmpinfo );
memset( bits, 0, sizeof(bits) );
dirtybits = 0;
}
PageNo++;
if( debugtrace )
cout << endl << "Ff ";
break;
case 0x0E: // SO: select secondary font
fontshift = 1;
if( debugtrace )
cout << endl << "So ";
break;
case 0x0F: // SI: select primary font
fontshift = 0;
if( debugtrace )
cout << endl << "Si ";
break;
default:
if( debugerror )
cerr << endl << hex << "unk: 0x" << ch << " " << dec;
break;
}
}
else
{
px += (font_drawchar( bits, px, py, curfont[fontshift], ch ) + 2) / 4;
dirtybits = 1;
if( debugtrace )
cout << char(ch);
}
break;
case 1:
if( ch >= '!' && ch <= '/' )
{
command.group1 = ch;
phase++;
}
else if( ch >= '0' && ch <= '~' )
{ // two character escape sequence
command.term = ch;
Execute( command, ifPcl );
phase = 0;
}
else
{
if( debugtrace )
cout << endl << "unk: Ec " << hex << "0x" << ch << dec << " " << endl;
phase = 0;
}
break;
case 2:
phase++;
command.sign = 0;
command.value = 0.0;
if( ch >= '`' && ch <= '~' )
{
command.group2 = ch;
}
else
{
command.group2 = 0;
continue;
}
break;
case 3:
if( ch=='+' || ch=='-' )
{
command.sign = ch;
ch = ifPcl.get();
}
while( ch != EOF && isdigit(ch) )
{
command.value = command.value * 10 + (ch - '0');
ch = ifPcl.get();
}
if( ch=='.' )
{
double frac = 0.1;
while( (ch = ifPcl.get()) != EOF && isdigit(ch) )
{
command.value += (ch - '0') * frac;
frac /= 10;
}
}
if( command.sign == '-' )
command.value = -command.value;
phase++;
continue;
case 4:
command.term = ch;
Execute( command, ifPcl );
if( ch >= '`' && ch <= '~' )
{
phase = 3;
command.sign = 0;
command.value = 0;
}
else
phase = 0;
break;
}
ch = ifPcl.get();
} // while
if( dirtybits )
{
sprintf( PageNameBmp, "%s%d.bmp", NameBmp, PageNo );
bmp_save( PageNameBmp, &bmpinfo );
}
return 0;
}
int Execute( COMMAND& command, ifstream& ifPcl )
{
command.error = 1; // unrecognized command
if( command.control == 0x1B )
{
switch( command.group1 )
{
case 0:
switch( command.term )
{
case 'E': // reset
command.error = 2;
break;
}
break;
case ')':
case '(':
switch( command.group2 )
{
case 's':
switch( command.term )
{
case 'W':
if( command.group1 == ')' )
{ // font descriptor and data
int cdata = int(command.value);
char *pdata = new char[cdata];
if( pdata )
{
ifPcl.read( pdata, cdata );
font_new( fontid, pdata, cdata );
delete pdata;
}
}
else
{ // char descriptor and data
int cdata = int(command.value);
char *pdata = new char[cdata];
if( pdata )
{
ifPcl.read( pdata, cdata );
font_newchar( fontid, charcode, pdata, cdata );
delete pdata;
}
}
command.error = 0;
break;
}
break;
case 0:
if( toupper(command.term) == 'X' )
{
curfont[command.group1==')'] = font_find( int(command.value) );
command.error = 0;
}
break;
}
break;
case '*':
switch( command.group2 )
{
case 'b': // Raster Graphics
switch( toupper(command.term) )
{
case 'M': // set compression method
// 0=unencoded
// 1=run-length encoding
// 2=TIFF revision 4.0
// 3=delta row
// 5=adaptive compression
command.error = 2; // ignored command
break;
case 'W': // transfer raster data
{
int i = rasterx/8, j = rasterx % 8;
int n = int(command.value);
int ch;
while( n-- && (ch = ifPcl.get()) != EOF )
{
if( ch )
{
dirtybits = 1;
bits[rastery][i++] |= ch >> j;
bits[rastery][i] |= ch << (8-j);
}
else
i++;
}
rastery++;
py++;
}
command.error = 0; // processed command
break;
case 'Y': // y offset
command.error = 2; // ignored command
break;
}
break;
case 'c': // Rectangular Area Fill Graphics
switch( toupper(command.term) )
{
case 'A': // rectangle width (dots)
patternw = int(command.value);
command.error = 0;
break;
case 'B': // rectangle height (dots)
patternh = int(command.value);
command.error = 0;
break;
case 'H': // rectangle width (decipoints)
patternw = int( (long(command.value * 300) + 719) / 720 );
command.error = 0;
break;
case 'V': // rectangle height (decipoints)
patternh = int( (long(command.value * 300) + 719) / 720 );
command.error = 0;
break;
case 'G': // area fill ID (pattern ID)
// shading: 1-2, 2-10, 11-20, 21-35, 36-55, 56-80, 81-99, 100
// cross-hatch: 1 -, 2 |, 3 /, 4 \, 5 #, 6 X
// user-defined: # of pattern (0-32767)
command.error = 2; // ignored
break;
case 'P': // fill rectangular area
switch( int(command.value) )
{
case 0: // solid
if( patternw > 0 && patternh > 0 )
{
bit_set( bits, px, py, px + patternw - 1, py + patternh - 1 );
dirtybits = 1;
}
command.error = 0;
break;
case 1: // solid white
command.error = 2; // ignored
break;
case 2: // shading
command.error = 2; // ignored
break;
case 3: // cross-hatch pattern
command.error = 2; // ignored
break;
case 4: // user-defined pattern
command.error = 2; // ignored
break;
case 5: // current pattern
command.error = 2; // ignored
break;
}
break;
case 'Q': // pattern control
// 0=delete all paterns
// 1=delete all temporary patterns
// 2=delete pattern (last pattern ID specified)
// 3=reserved
// 4=make pattern temporary (last pattern ID specified)
// 5=make pattern permanent (last pattern ID specified)
command.error = 2; // ignored
break;
case 'W': // user-defined pattern
// followed by # of pattern bytes
ifPcl.ignore( int(command.value) );
command.error = 2; // ignored
break;
case 'D': // font ID
fontid = int(command.value);
command.error = 0;
break;
case 'E': // char code
charcode = int(command.value);
command.error = 0;
break;
case 'F': // soft font control
command.error = 2; // ignored
break;
}
break;
case 'p': // Cursor Positioning/Rectangular Area Fill Graphics
switch( toupper(command.term) )
{
case 'R': // set pattern reference point
// 0=rotate patterns with print direction
// 1=keep patterns fixed
patternf = int(command.value);
patternx = px;
patterny = py;
command.error = 2; // ignored
break;
case 'X': // horizontal (dots)
if( command.sign )
px += int(command.value);
else
px = int(command.value);
command.error = 0;
break;
case 'Y': // vertical (dots)
if( command.sign )
py += int(command.value);
else
py = int(command.value);
command.error = 0;
break;
}
break;
case 'r': // Raster Graphics
switch( toupper(command.term) )
{
case 'A': // start
// 0=at x-position 0
// 1=at current x-position
if( int(command.value) )
rasterx = px;
else
rastery = 0;
rastery = py;
command.error = 0;
break;
case 'B': // end: any LaserJet
case 'C': // end: LJ IIISi and IIIP
command.error = 0;
break;
case 'F': // presentation mode
// 0=current print direction
// 3=along width of physical page
command.error = 2; // ignored
break;
case 'S': // raster width
command.error = 2; // ignored
break;
case 'T': // raster height
command.error = 2; // ignored
break;
}
break;
case 't': // Raster Graphics
if( toupper(command.term) == 'R' )
{ // Resolution: values 75, 100, 150, 300
if( int(command.value) == 300 )
command.error = 0;
else
command.error = 2; // ignored
}
break;
}
break;
}
}
if( command.control == 0x1B )
{
ostream *pcout;
switch( command.error )
{
case 0:
pcout = &cout;
if( debugtrace )
*pcout << endl;
break;
case 1:
pcout = &cerr;
if( debugerror )
*pcout << endl << "unk: ";
break;
case 2:
pcout = &cerr;
if( debugerror )
*pcout << endl << "nop: ";
break;
default:
pcout = &cerr;
if( debugerror )
*pcout << endl << "int: ";
break;
}
if( pcout == &cout && debugtrace ||
pcout == &cerr && debugerror )
{
if( command.group1 )
{
*pcout << "Ec " << command.group1;
if( command.group2 )
*pcout << command.group2;
*pcout << command.term << " ";
if( command.sign )
*pcout << command.sign;
*pcout << fabs(command.value) << " ";
}
else
{
*pcout << "Ec " << command.term << " ";
}
}
}
return 0;
}