The following example modifies the cout object to print in two-column landscape (horizontal) mode on a printer that uses the PCL control language (for example, Hewlett-Packard LaserJet printer). As the test driver program shows, all member functions and manipulators that work with the original cout object work with the special version. The application programming interface is the same.
The example is divided into three source files:
HSTREAM.H contains only the class declaration for hstreambuf
, which is derived from the filebuf class and overrides the appropriate filebuf virtual functions.
// hstream.h - HP LaserJet output stream header
#include <fstream.h> // Accesses filebuf class
#include <string.h>
#include <stdio.h> // for sprintf
class hstreambuf : public filebuf
{
public:
hstreambuf( int filed );
virtual int sync();
virtual int overflow( int ch );
~hstreambuf();
private:
int column, line, page;
char* buffer;
void convert( long cnt );
void newline( char*& pd, int& jj );
void heading( char*& pd, int& jj );
void pstring( char* ph, char*& pd, int& jj );
};
ostream& und( ostream& os );
ostream& reg( ostream& os );
HSTREAM.CPP contains the hstreambuf
class implementation.
// hstream.cpp - HP LaserJet output stream
#include "hstream.h"
const int REG = 0x01; // Regular font code
const int UND = 0x02; // Underline font code
const int CR = 0x0d; // Carriage return character
const int NL = 0x0a; // Newline character
const int FF = 0x0c; // Formfeed character
const int TAB = 0x09; // Tab character
const int LPP = 57; // Lines per page
const int TABW = 5; // Tab width
// Prolog defines printer initialization (font, orientation, etc.
char prolog[] =
{ 0x1B, 0x45, // Reset printer
0x1B, 0x28, 0x31, 0x30, 0x55, // IBM PC char set
0x1B, 0x26, 0x6C, 0x31, 0x4F, // Landscape
0x1B, 0x26, 0x6C, 0x38, 0x44, // 8 lines per inch
0x1B, 0x26, 0x6B, 0x32, 0x53}; // Lineprinter font
// Epilog prints the final page and terminates the output
char epilog[] = { 0x0C, 0x1B, 0x45 }; // Formfeed, reset
char uon[] = { 0x1B, 0x26, 0x64, 0x44, 0 }; // Underline on
char uoff[] = { 0x1B, 0x26, 0x64, 0x40, 0 };// Underline off
hstreambuf::hstreambuf( int filed ) : filebuf( filed )
{
column = line = page = 0;
int size = sizeof( prolog );
setp( prolog, prolog + size );
pbump( size ); // Puts the prolog in the put area
filebuf::sync(); // Sends the prolog to the output file
buffer = new char[1024]; // Allocates destination buffer
}
hstreambuf::~hstreambuf()
{
sync(); // Makes sure the current buffer is empty
delete buffer; // Frees the memory
int size = sizeof( epilog );
setp( epilog, epilog + size );
pbump( size ); // Puts the epilog in the put area
filebuf::sync(); // Sends the epilog to the output file
}
int hstreambuf::sync()
{
long count = out_waiting();
if ( count ) {
convert( count );
}
return filebuf::sync();
}
int hstreambuf::overflow( int ch )
{
long count = out_waiting();
if ( count ) {
convert( count );
}
return filebuf::overflow( ch );
}
// The following code is specific to the HP LaserJet printer
// Converts a buffer to HP, then writes it
void hstreambuf::convert( long cnt )
{
char *bufs, *bufd; // Source, destination pointers
int j = 0;
bufs = pbase();
bufd = buffer;
if( page == 0 ) {
newline( bufd, j );
}
for( int i = 0; i < cnt; i++ ) {
char c = *( bufs++ ); // Gets character from source buffer
if( c >= ' ' ) { // Character is printable
* ( bufd++ ) = c;
j++;
column++;
}
else if( c == NL ) { // Moves down one line
*( bufd++ ) = c; // Passes character through
j++;
line++;
newline( bufd, j ); // Checks for page break, etc.
}
else if( c == FF ) { // Ejects paper on formfeed
line = line - line % LPP + LPP;
newline( bufd, j ); // Checks for page break, etc.
}
else if( c == TAB ) { // Expands tabs
do {
*( bufd++ ) = ' ';
j++;
column++;
} while ( column % TABW );
}
else if( c == UND ) { // Responds to und manipulator
pstring( uon, bufd, j );
}
else if( c == REG ) { // Responds to reg manipulator
pstring( uoff, bufd, j );
}
}
setp( buffer, buffer + 1024 ); // Sets new put area
pbump( j ); // Tells number of characters in the dest buffer
}
// simple manipulators - apply to all ostream classes
ostream& und( ostream& os ) // Turns on underscore mode
{
os << (char) UND; return os;
}
ostream& reg( ostream& os ) // Turns off underscore mode
{
os << (char) REG; return os;
}
void hstreambuf::newline( char*& pd, int& jj ) {
// Called for each newline character
column = 0;
if ( ( line % ( LPP*2 ) ) == 0 ) { // Even page
page++;
pstring( "\033&a+0L", pd, jj ); // Set left margin to zero
heading( pd, jj ); // Print heading
pstring( "\033*p0x77Y", pd, jj );// Cursor to (0,77) dots
}
if ( ( ( line % LPP ) == 0 ) && ( line % ( LPP*2 ) ) != 0 ) {
// Odd page; prepare to move to right column
page++;
pstring( "\033*p0x77Y", pd, jj ); // Cursor to (0,77) dots
pstring( "\033&a+88L", pd, jj ); // Left margin to col 88
}
}
void hstreambuf::heading( char*& pd, int& jj ) // Prints heading
{
char hdg[20];
int i;
if( page > 1 ) {
*( pd++ ) = FF;
jj++;
}
pstring( "\033*p0x0Y", pd, jj ); // Top of page
pstring( uon, pd, jj ); // Underline on
sprintf( hdg, "Page %-3d", page );
pstring( hdg, pd, jj );
for( i=0; i < 80; i++ ) { // Pads with blanks
*( pd++ ) = ' ';
jj++;
}
sprintf( hdg, "Page %-3d", page+1 ) ;
pstring( hdg, pd, jj );
for( i=0; i < 80; i++ ) { // Pads with blanks
*( pd++ ) = ' ';
jj++;
}
pstring( uoff, pd, jj ); // Underline off
}
// Outputs a string to the buffer
void hstreambuf::pstring( char* ph, char*& pd, int& jj )
{
int len = strlen( ph );
strncpy( pd, ph, len );
pd += len;
jj += len;
}
EXIOS204.CPP reads text lines from the cin object and writes them to the modified cout object.
// exios204.cpp
// hstream Driver program copies cin to cout until end of file
#include "hstream.h"
hstreambuf hsb( 4 ); // 4=stdprn
void main()
{
char line[200];
cout = &hsb; // Associates the HP LaserJet streambuf to cout
while( 1 ) {
cin.getline( line, 200 );
if( !cin.good() ) break;
cout << line << endl;
}
}
Here are the main points in the preceding code:
hstreambuf
is derived from filebuf, which is the buffer class for disk file I/O. The filebuf class writes to disk in response to commands from its associated ostream class. The hstreambuf
constructor takes an argument that corresponds to the operating system file number, in this case 1, for stdout. This constructor is invoked by this line:hstreambuf hsb( 1 );
hstreambuf
object with the cout object:ostream& operator =( streambuf* sbp );
This statement in EXIOS204.CPP executes the assignment:
cout = &hsb;
hstreambuf
constructor prints the prolog that sets up the laser printer, then allocates a temporary print buffer.hstreambuf
class overrides these functions to gain control of the byte stream. The functions call the private convert
member function.convert
function processes the characters in the hstreambuf
buffer and stores them in the object’s temporary buffer. The filebuf functions process the temporary buffer.convert
relate more to the PCL language than to the iostream library. Private data members keep track of column, line, and page numbers.und
and reg
manipulators control the underscore print attribute by inserting codes 0x02 and 0x03 into the stream. The convert
function later translates these codes into printer-specific sequences.hstreambuf
class could be derived from the streambuf class rather than the filebuf class. The filebuf derivation shown gets the most leverage from existing iostream library code, but it makes assumptions about the implementation of filebuf, particularly the overflow and sync functions. Thus you cannot necessarily expect this example to work with other derived streambuf classes or with filebuf classes provided by other software publishers.