Printing Class Library

.

This update fixes most bugs reported by users and adds the following enhancements to the library

  • Tables can now have more than 1 visible print line. I.E. Multiline tables both static and virtual
  • There is a rotated text function to print text oriented thru 360 degrees of rotation
  • There is a static function to set page orientation on a page by page basis
  • There is a function to retrieve the device driver name currently in use
  • There is a function to place a user defined function into the printing logic to provide the ability to write form type programs where the actual data is not available until runtime. ( see Demo)
  • The reference is now available in both html and rtf ( see referenece.rtf or reference.html )

What's in the works

  • A parser and the ability to save and retrieve forms from disk.
  • The ability to use and print data from various controls in a formatted style. List, tree, and rich text controls are being evaluated and some other people have sent me some code but I have not had time to incorporate them yet.
  • Database connectivity to make report writing easier. If I do it it will be thru a virtual function that can be overridden to handle different databases. I use Codebase++ almost exclusively due to speed, reliability, and size issues. If anyone has any thoughts along this line let me know.

Before jumping into the description of the classes I would like to say a few things here and not repeat them later over and over. These classes use either MM_TEXT or MM_ANISOTROPIC mapping modes. If you know about mapping modes great, if not, don't worry. I'm not going to use the terms again. If interested see the constructor for CPage for the dirty details. All routines used to output text need at least two parameters to form a point of location. All location references in these classes use 0,0 to represent the upper left-hand corner of the output. As the first number grows larger the output goes down the page. As the second grows larger the text goes to the right. If you pass a double as a location reference the classes ASSUME you are using inches as a unit of measurement. This is the easiest and safest route to go because then you really can forget all about the mapping modes. If you wish to use the low-level route and specify the location to the nearest pixel use the MM_TEXT mode and have fun. However, be aware that some printer drivers will lie to you and the output will look considerably different than anticipated. If this is the case and you still need to have exact placement use MM_ANISOTROPIC mode ( the default mode ) and change the 1000 by 1000 logical units in the constructor to suit your needs.

This class library is designed to provide printing and print preview capabilities to applications developed using the Microsoft) Foundation Class Library. In our business we have need to emulate many pre-printed business and insurance forms. In some cases the client wishes us to print the entire document and in others just fill in the fields with information gathered or generated by the application. There was no commercial solution that we could find to do this so we started from scratch. It was an interesting journey.

There is probably no portion of MFC programming that is as mis-understood and poorly documented as printing. Most books and training films give it very short thrift and then it's on to the flashy GUI stuff we all love. Lot of programs resort to using a commercial report writing tool to handle simple printing chores and others simply direct screen drawing to the printer DC in a crude kind of screen print. We found this unacceptable.

The classes are divided into two logical sections. The file Cprinter.cpp holds the low-level printer primitives. Suprisingly there are only three of them


virtual int PrintText(PRTTYPE *ps,double LineSpacing);

virtual void DrawLine(PRTTYPE *ps,int LineSize);

virtual void DrawRect(PRTTYPE *ps,int LineSize);

These are all that we needed to provide a large amount of printing options. If you wish to add more low-level capabilities this is the place to put them.

The higher level constructs are in the file Cpage.cpp. The classes here are

  • CPage represents a page of printed output. MFC printing architecture provides for printing on a page by page method. CPage holds and manipulates data concerning that page. There are member functions to output text, draw boxes and lines, create and use moveable print regions, and create and use tables of data. Fonts and text attributes can be changed freely and newspaper type columns are supported. Much of this functionality is provided by helper classes that are created by CPage as needed and used. These classes are
  • CTable represents a table of data. A table is much like a grid. It has rows and columns. It can have vertical and/or horizontal separations, a border, and a title. Each column in the table is composed of a class that holds a description of the column. This is the header for that column and the width of the column. Please see the documentation and the sample code for the use of tables. In essence the use is really simple. You create a variable of type CTABLEHEADER either on the stack or the heap and set its member variables to describe the table you wish to use. You the pass a pointer to the filled in structure to CPage via the CPageTable(CTABLEHEADER*) function. The table is constructed and displayed at this time. Data may be added to the table using overloaded functions in CPage that are the same as used to print to the page. Look at the documentation and the sample source code for details
  • CPrintRegion represents a sub region of the printed page that is to be treated as a single object. This region may contain textual data, columnar output, check boxes, and graphics but is to be treated as a single unit. The region can have a border and a title id desired. Move the region and all its contents move with it. This allows story boarding of the page. Since most forms have areas that are logical units these can be considered as a print region. Then when the client asks if you can move this here and that there it is a simple task of placing the new co-ordinates in and recompiling. Everything moves with the region. CPageCreateRegion() will return a pointer to a newly created CPrintRegion class. Since CPage actually creates this pointer it keeps an internal list of pointers and deletes the when the CPage~CPage() destructor runs. DO NOT DELETE A CPrintRegion * passed to you from CPage as a page fault may occur when the destructor runs.

All of these classes (CPage CTable CPrintRegion ) are defined in Cpage.h and implemented in CPage.cpp. In order to implement the class functionality in your application include these files in your project and include the line #include "cpage.h" in any file using the classes. There is a RTF file titled REFERENCE.rtf included in the source code that contains more detailed information on the class members and their use.There is also a html file by the same name. Also a demo project is included. It contains sample code exercising most of the class members. This is a bare bones version of the library with most of the esoteric stuff taken out and the code simplified and cleaned up to make it as clear as possible. Everybody has to have their own little special thing and as time goes by the class starts getting bigger and bigger and interdependencies crop up. I think I have removed most of the interdependent code and all the obvious bugs that crop up during a re-write.

One thing that we learned from this is that it is possible to be to object oriented. At one time we had a CParagraph and a CSentence and a CTitle etc. It really just got to be more trouble than it was worth. We think we have hit a happy medium here. This code is being used as the basis for ad hoc report writers, data base output, forms generation, and several other tasks and has proven to be pretty stable. It has not been tested on Win3.1 but the original code was and was developed there so barring any MFC 32 bit stuff it should compile and run fine in 3.1.

I have added the ability to print disk based bit maps to the library. The function is PrintBitMap() and is documented in REFERENCE.rtf. Most of the code was taken from a sample program included with the compiler and modified to fit into this library. The print functions have the ability to print over the bitmap(s). This allows one to scan a form and later print it and place data on it or to place boilerplate printing in a bit map and print only those portions of the form you wish. It is also handy for printing company logos, letter head, etc.

This code snippet shows how the classes are used in the view class


void CMainViewOnPrint(CDC* pDC, CPrintInfo* pInfo)
{
        CPage* ps = new CPage(pInfo-m_rectDraw,pDC,MM_TEXT);
        if(PRINTWHAT==0) PrintForm1(ps);
        if(PRINTWHAT==1) PrintForm2(ps); // subform demo
        if(PRINTWHAT==2) PrintForm3(ps); // table demo
        if(PRINTWHAT==3) PrintForm3(ps); // bitmap demo
        delete ps;
        return;
}

Below are some small examples of how the classes are used.


void PrintForm1(CPage* pPage)
{
        double Row;
        //Print a bitmap
        ps->PrintBitMap(1.0,1.0,4.0,5.0,"MyBitmap.bmp"); 
        //Print a title
        Row=pPage-Print(0.0,0.0,TEXT_NORMAL|TEXT_CENTER,24,"Form Title");
        //create and use a print region
        CPrintRegion *Region1=pPage->CreateRegion(.5,0.0,1.5,3.9);
        Region1->DrawBorder();
        Region1->DrawTitle("Customer 
        Information",8,TEXT_BOLD|TEXT_CENTER|TEXT_RECT,FILL_NONE);
        Row=pPage->Print(Region2,0.0,0.01,TEXT_NORMAL|TEXT_SINGLELINE,9,"Name");
        Row=pPage->Print(Region2,Row,0.01,TEXT_NORMAL|TEXT_SINGLELINE,9,"Location");
}


//create and use a table
TABLEHEADER* pTable=new TABLEHEADER; 
pTable->PointSize=10;
pTable->LineSize=1; // default shown only for demo purposes
pTable->UseInches=TRUE;
pTable->AutoSize=FALSE;
pTable->Border=TRUE;
pTable->FillFlag=FILL_NONE;
pTable->NumColumns=5;
pTable->NumRows=12;
Table->StartRow=0.0;
pTable->StartCol=0.0;
pTable->EndCol=8.0;
pTable->ColDesc[0].Init(1.0,"Item #");
pTable->ColDesc[1].Init(3.0,"Desc.");
pTable->ColDesc[2].Init(1.0,"# Items");
pTable->ColDesc[3].Init(1.0,"Cost");
pTable->ColDesc[4].Init(1.0,"Ext Cost");
pPage->Table(pTable);    

//place information in a table
pPage-Print(pTable,0,0,12,TEXT_LEFT|TEXT_BOLD,"123-009");
pPage-Print(pTable,0,1,12,TEXT_CENTER|TEXT_BOLD,"Small Cray Computer");
pPage-Print(pTable,0,2,12,TEXT_CENTER|TEXT_BOLD,"2");
pPage-Print(pTable,0,3,12,TEXT_RIGHT|TEXT_BOLD,"22.10");
pPage-Print(pTable,0,4,12,TEXT_RIGHT|TEXT_BOLD,"44.20");

Download demo project - 105 Kb

Download source - 48 Kb



About the Author

Richard Stringer

C C++ ASM are my currrent languages of choice. Currently writing MFC apps for insurance companies