Why write yet another report generator when the world is full of web browsers that can do the job for you?
Introduction
In commercial and in-house applications, the report printing feature is often the most heavily used part of the system. Printed reports still have many advantages over viewing information online. All the pertinent information can be seen at a glance, and the reports can be distributed to many people, including people who don't have access to the application itself.
Unfortunately, report generation from within a GUI (Graphical User Interface) application is the most tedious task of program development. The programming required for gathering and displaying information is already done. Report generation just involves taking the same information and formatting it so it looks presentable on a piece of paper. While a GUI, with its scrollbars, presents few size constraints, paper imposes finite limits. Correctly fitting the data can be a difficult task.
Recently my colleague and I had just completed a complex financial-based application, and the only missing component was the reports. Financial applications are heavily data driven and they require a vast number of reports that can display information in many different formats. We were faced with the additional prospect of reports being constantly added and modified. If all this wasn't enough, our users required the reporting capabilities immediately. We needed a quick, easy, and powerful method of generating and printing reports from a Windows application. The method we came up with and the method presented in this article provided all that we needed and more.
The key part of the solution is the ActiveX browser control that comes with Microsoft's Internet Explorer versions 4.0 and up. Since HTML provides a rich and diversified set of elements for creating online documents, integrating a browser into our application enables us to create nice looking documents without getting bogged down in the formatting. Using HTML and an embedded browser has several advantages:
- HTML is easy to learn and contains many features, including color presentation and embedded images.
- HTML documents can be viewed on any HTML-compliant web browser.
- With the introduction of Windows 98 and many Microsoft products, Internet Explorer is already included on most user's workstations.
- Internet Explorer's ActiveX control is free of charge.
The remainder of this article explains how to integrate Microsoft's Internet Explorer web browser within a Visual C++ application. We also introduce a simple report class to accomplish the goal of report generation.
The CReport Class
The first step in generating a report that is viewable and printable within a web browser is to generate an HTML file. We developed the CReport class to generate and correctly paginate HTML reports [1]. CReport ensures that the report title, column headers, data and footer appears correctly regardless of the size or orientation of the paper. CReport paginates the report but does not format the contents. Instead, HTML is the tool used for displaying the report contents.
A partial definition of CReport appears in Figure 1. The implementation appears in Figure 2. CReport uses callback functions, which the user must define to generate the contents of the report. CReport calls these functions at the appropriate times. The user must define at least three callback functions for printing the report title, contents, and footer. These functions are _vTitlePrint, _vLinePrint, and _vFooterPrint, respectively. If the body of the report contains more than one format, additional functions can be used to support the additional formats.
Before examining the details of the these callback methods it is important to understand how CReport paginates. CReport assumes there are a number of logical lines per page. A logical line is a line of text actually appearing on the printed page. A distinction must be made between logical lines and lines of HTML text, because in many cases multiple lines of HTML code must be generated to display one line of text on the browser.
The CReport constructor takes six parameters, which include pointers to the title printing function and footer printing functions. The title and footer callback functions each take three parameters. These parameters include the output stream to which the data is saved, a void pointer to extra data that may be needed, and the current page number. When the constructor is invoked, the caller must define the total number of logical lines per page (_iLinesPerPage), as well as the number of logical lines taken up by the title (iTitleLines) and the footer (iFooterLines).
Unfortunately, determining the number of logical lines per page is not straightforward. Depending on the font being used and the orientation of the printed report (portrait or landscape), the number of lines on a page varies. The only way to determine how many lines fit on a page is to experiment with your report. The other parameter required by CReport is the output stream on which the HTML report is sent (RepStream).
Once an instance of CReport is defined, the report can be printed. The report is printed using the vPrintLine method. vPrintLine prints a line of data defined by the _vData parameter. The line of data is printed by a user-defined function; a pointer to this function (_vLinePrint) is included as a parameter to vPrintLine. The actual function defining _vLinePrint can vary depending on the desired report format. The number of logical lines printed by _vLinePrint must also be defined. _vLinePrint requires two main parameters: the output stream on which the report will be printed, and a void pointer to the data to be printed. vPrintLine's other parameters are _vTitleData and _vFooterData. These parameters are void pointers to data required by the title and footer printing functions.
When vPrintLine executes, it first checks the page number. If the page number is -1, it means this is the first time vPrintLine has been executed, so vPrintLine calls vTitlePrint to print the report title. vPrintLine then sets the page count to 1. vPrintLine then checks iLinesLeft against the number of lines required by _vLinePrint and the number required by vFooterPrint. If there are not enough lines on a page to print the current line of report data as well as the footer, vFooterPrint is executed. In that case vPrintLine then calls vTitlePrint and _vLinePrint to display the title and report data on the next page. Each time vPrintLine executes, the number of logical report lines printed is subtracted from iLinesLeft.
The final method called when generating a report is vPrintFooter. vPrintFooter is called last to make sure that the footer is printed on the last page of the report. This is necessary because vPrintLine prints a footer only if no additional data can be printed on a page. The last page of the report might not have a complete set of text and therefore vPrintFooter is called to ensure the printing of the footer. vPrintFooter calls the user-defined footer function and is passed a void pointer to the data required.
Integrating with Internet Explorer
Integrating Microsoft's IE (Internet Explorer) ActiveX component within a Visual C++ application is fairly easy. If you have IE already installed, the ActiveX component is resident on your workstation. If you do not have IE installed, you can download it for free from Microsoft's web site. To integrate the IE ActiveX component within your Visual C++ application, follow the steps shown below:
1. Select "Projects"->"Add To Projects-">"Components and Controls" from the Visual C++ menu.
2. Choose "Registered ActiveX Controls" from the Components and Controls Gallery Dialog.
3. Select the Microsoft Web Browser ActiveX component from the list and hit the "Insert" button.
The ActiveX component (a small picture of a globe) appears in your component tool bar. To add the web browser to your application, simply select the component and place it on your application. The component is of type CWebBrowser2. Although CWebBrowser2 contains many features, we investigate only those necessary for displaying and printing the report.
The first step when using CWebBrowser2 is to associate a variable with the web browser using the Visual C++ ClassWizard. This variable is used to call CWebBrowser2 methods. To load an HTML document into the browser, the Navigate method is called using the syntax shown below:
COleVariant noArg; m_Browser.Navigate ("file:\\\\c:\\temp\\report.html", &noArg, &noArg, &noArg, &noArg);Navigate loads the HTML document and displays it within the browser. The browser already knows how to convert HTML code to its associated graphical display, so there is no need to call methods to decode HTML.
Other methods that are useful when loading an HTML document are the DownloadBegin and DocumentComplete virtual methods. The system calls DownloadBegin when the browser starts loading the requested document. DocumentComplete is called when the document is fully loaded in the browser.
Printing documents from the integrated web browser is a built-in feature. Hitting the CTRL-P key combination with the web browser selected causes a standard print dialog to be displayed. This method of printing is a bit cumbersome for most applications; it is more desirable to select the feature from the application in the form of a menu choice or a button. You can force the CWebBrowser2 to print by using the following code; it can also be added to the callback of a Windows control.
LPDISPATCH lpDispatch = NULL; LPOLECOMMANDTARGET lpOleCommandTarget = NULL; lpDispatch = m_Browser.GetDocument(); lpDispatch->QueryInterface(IID_IOleCommandTarget, (void **)&lpOleCommandTarget); lpDispatch->Release(); // If the third argument is 0 the print dialog is displayed. // If it is 2 the document is sent directly to the default printer. lpOleCommandTarget->Exec(NULL, OLECMDID_PRINT, 0, NULL, NULL); lpOleCommandTarget->Release();The Loan Amortization Application
To illustrate the features of an integrated web browser, we present an application that calculates and displays an amortization schedule for a bank loan. An amortization schedule shows the amount of money allocated for principle and interest for each payment on a loan. The example used for this article is a Mortgage Loan Amortization calculator, which is used to calculate monthly loan payments for 15- or 30-year mortgages given a mortgage rate and the total loan amount. The application is an MFC Visual C++ dialog-based application that displays an amortization schedule inside an integrated IE web browser. The dialog as it appears in the web browser is shown in Figure 3.
The user is prompted for the amount of the loan, the loan rate, and the length of the loan. The loan length can be 15 or 30 years. Clicking the "Calculate" button generates the amortization schedule for the loan amount and time period specified and displays the schedule within the web browser. Below we explain the events that take place as the HTML-based report is created and printed.
The first thing that occurs when clicking "Calculate" is the disabling of the "Print" button. This button is re-enabled when report generation is complete and when the report is completely loaded within the browser. An instance of the CLoanCalc class is created with the user-supplied information. CLoanCalc contains a method called CalculateSchedule, which generates the amortization schedule and places each payment within a linked list (See Figure 4).
After the CalculateSchedule method is finished, the HTML report is generated. The first step in generating the HTML report is defining an instance of CReport. CReport's constructor requires two function pointers, one that outputs title information and the other that outputs footer information. The title function outputs three logical lines and the footer function outputs none. Note that the Principle, Interest, and Amount Remaining column headers are hyper-linked to documents explaining how they are calculated. Try to do this in a third-party report generation tool!
The format of the title is shown below:
Amortization Schedule For a Loan of $D for Y Years at a Rate of R% With a Payment of $P Payment Period Principle Interest Amount RemainingAlthough the footer does not display any data, a footer function still needs to be defined. The title and footer functions are static member functions of CMortCalcDlg (the main mortgage calculator dialog class) and are referred to as vTitleLine and vFooterLine. CReport also requires that the total length of the page be specified. The amortization schedule is printed on an 8.5 X 11 piece of paper using portrait orientation. Unfortunately, as stated previously, there is no easy way to determine the number of logical lines that fit on a printed page. The fonts and spacing used affect the amount of lines on a page. The only way to determine the correct page length is through experimentation.
After CReport has been created, the program creates an amortization schedule by looping through the linked list of payments and calling CReport's vPrintLine method to display the data. A pointer to a CMortCalcDlg static member function vPaymentLine (Figure 5) is passed. vPaymentLine creates a line of data on the report containing one mortgage payment. The number of logical lines displayed on the report is the next specification on vPrintLine's parameter list. vPrintLine is also passed a pointer to the current LoanPayment element of the linked list. This pointer is passed to vPaymentLine from vPrintLine for the purpose of displaying the current information. Another parameter passed to vPrintLine is a pointer to CLoanCalc. This class holds information used by vTitleLine to display the title information. vTitleLine is called by vPrintLine every time a new page is generated. The final vPrintLine parameter is data sent to the footer. Since the footer displays no information in this application, this parameter is a null pointer.
The HTML report is created using the HTML table feature, which presents the report inside a grid. An additional reporting feature included in the example application is subtotaling and totaling of principle and interest amounts. The principle and interest are totaled for every year and a grand total is presented at the end of the report. When a subtotal or grand total is displayed, vPrintLine is passed a pointer to a CMortCalcDlg static member function (vTotalLine) that prints totals, and a pointer to a class (CMortTotal) that contains the totals. Using various printing functions, vPrintLine can display report data in an unlimited number of formats.
When report generation is complete, the HTML file is closed and the Navigate method of the CWebBrowser2 class loads the HTML report. We've overridden CWebBrowser2's DocumentComplete virtual function to call OnDocumentCompleteAmortExplorer, which enables the "Print" button, signaling the user that the report is complete and can be printed. The "Print" button's OnClick virtual function is overridden; it uses the code shown in the section above, "Integrating with Internet Explorer," to print the contents of the web browser.
Conclusion
Integrating Microsoft's IE web browser provides a nice way to display and print professional looking reports. HTML is easy to learn, easy to create, and easy to manipulate should report requirements change. The IE ActiveX web browser control has many features already built in, including display and printing of HTML documents. If you have ever tried to print documents from scratch within a Visual C++ application, you know it is not a trivial task. There are other third-party tools available for report generation, but the ActiveX component has one advantage over the others it's free. Another nice feature of HTML report production is that the raw report text is ASCII and therefore can be easily archived. The HTML reports can be viewed and printed using any HTML-compatible web browser.
Report generation is only one example of using an integrated web browser. Other uses include online help with images and hyperlinks, online content, or any application requiring formatted documents along with full printing capabilities.
Note
[1] Web browsers have no concept of pagination. A web browser displays the HTML file in a scrolling window, and provides no indication of when one page ends and another begins. This can be troublesome when generating multi-page reports. Each printed page should contain the report title; and if the report contains multiple columns, it should contain the column headers as well. Reports requiring footers should display them at the bottom of each page.
Mark Nadelson has been programming for the last eight years in a variety of industries, including telecommunications, internet software development, and financial computing. The operating systems he's used include Unix, Windows NT, and proprietary operating systems on embedded systems. His other interests include his wife Kim, from whom he hopes to receive brownie points by mentioning her. He can be reached via email at nadelsm@gcm.com.
Colleen Woodward has a B.S. in Computer Science and an M.S. in Information Management from Stevens Institute of Technology. She has been programming using C/C++ in the finance industry for six years on Unix and Windows NT. Her other interests include two dogs and a Dave. She can be reached at woodwac@gcm.com.