Andrew "Captain" Cerebrum
Senior Developer
Mead & Company Limited
November 20, 1998
Download the source code files (zipped, 6.18K).
Contents
Introduction
Printing and Internet Explorer 5: What's New?
The Internet Explorer 4.x way: WebBrowser Control
Printing-Related Commands
Example One
It's in the Frame
Printing Hidden Content
Print Setup Options
In this article, we will talk about ways in which you can implement printing support for your HTML pages viewed using Internet Explorer version 4.0 and later. We will examine Internet Explorer 5's new printing functions, and provide a ready-built JScript file that you can include in your pages to provide "backwards compatibility" with those functions in earlier 4. x browsers. We'll also show you some samples that use our print.js file, briefly describe our code (non-techies can skip this bit), and mention some ActiveX control-based ways to customize your page printing settings even further.
Internet Explorer 4.0 introduced a bunch of new and very exciting capabilities for Web application development. Unfortunately, one very important feature was overlooked: printing. There is no Dynamic HTML (DHTML) DOM method available to print a page or modify page settings in Internet Explorer 4. x. With the release of Internet Explorer 5, we see the introduction of some new printing features, so this seems like a good time to demonstrate how to use Internet Explorer 5's native printing support and investigate the possibility of near-100 percent emulation for 4. x browsers.
Let's start with a brief description of Internet Explorer 5's new printing functions:
This method will behave as if the user has chosen the File | Print... menu. It will not be possible for a normal HTML page to start printing without the user interface (UI). This method is Netscape Navigator 4. x compatible, so it does not take any parameters.
This event cannot be canceled and will not bubble. It will fire only after the document has been fully loaded and the onload events have returned. Printing will not occur until onbeforeprint handlers have been executed.
This event cannot be canceled and will not bubble. UI control will not return to the user until the event handler is executed. The event will be called after all the drawing operations have been completed (in other words, after the software's job is done and the printing hardware has taken over).
The onbeforeprint and onafterprint event handlers are defined in much the same way as those for the existing onload and onunload events. They can be set using the following attributes:
for <BODY>:
<body onbeforeprint="beforeprint()" onafterprint="afterprint()">
for <FRAMESET>:
<frameset onbeforeprint="beforeprint()" onafterprint="afterprint()">
Or you may assign them at run time:
window.document.onbeforeprint=beforeprint; window.document.onafterprint=afterprint; window.onbeforeprint=beforeprint; window.onafterprint=afterprint;
// This example shows a page that modifies its title before printing // and then resets it to its original value after printing. var originalTitle; <script for=window event=onbeforeprint> originalTitle = document.title; document.title = document.title + " - by Captain" </script> <script for=window event=onafterprint> document.title = originalTitle; </script>
In summary, we can now print from script, we can choose to do something just before we print, and we can make something happen once printing has started. All in all, it's a very welcome development for users of Internet Explorer 5 and later. But how can we generalize printing tasks for both Internet Explorer 4. x and Internet Explorer 5? From here on, we provide the script that uses native printing support if available, and closely emulates it if not.
Developers who create custom browsing applications know about the WebBrowser control. This is the underlying engine that hosts Active Documents (as MSHTML documents) and handles navigation from one document to another. Each page and nested frame has a corresponding WebBrowser object.
The WebBrowser control exposes a set of Automation properties that is potentially unsafe, but at first sight that doesn't matter, because there is apparently no reason to place a WebBrowser object onto an HTML page, and anyway the Internet Explorer 4.0 team has done its best to make such a pastime fairly useless (read PRB: Permission Denied Accessing Web Browser Control in HTML). However, some clever person -- possibly Steve Gore, although we don't really know who did this first -- came up with the idea of using WebBrowser's Exe cWBmethod to initiate the printing of a page.
In fact, ExecWB is partially documented for those who do custom WebBrowser hosting. As it turns out, a stand-alone, non-navigated WebBrowser object can be placed on an HTML page -- in which case it will forward all ExecWBcalls to its parent document.
WebBrowser is known to support OLECMDID_PRINT(6,1) and OLECMDID_PAGESETUP(8) printing related commands.
The trick is to create a temporary WebBrowser object with DHTML and invoke ExecWB(6, 1)on it to print the page, as shown:
document.body.insertAdjacentHTML("beforeEnd", "<object id=\"idWBPrint\" width=0 height=0 \ classid=\"clsid:8856F961-340A-11D0-A96B-00C04FD705A2\"> </object>"); idWBPrint.ExecWB(6, 1); idWBPrint.outerHTML = "";
In real life, we've added error handling to the above code, because ExecWB(6, 1) could fail if a user selected the Cancel button on the print dialog.
Note: To check out these samples, you must be using Internet Explorer 4.x or Internet Explorer 5.
You can check out the first sample or read on for more details. The JScript printing method is printFrame(frameToPrint). You can also review a description of the code in the print.js file. It uses the new native print support for Internet Explorer 5 and provides emulation for the print() method and the onbeforeprint/onafterprintevents in Internet Explorer 4. x. To use this, simply include our print.js file on your own pages:
<script defer src="print.js"></script>
Now you can have a single HTML page source for both Internet Explorer 4. x and Internet Explorer 5:
<head> <title>Simple Printing</title> <script defer language=JScript src="print.js"></script> </head> <body onbeforeprint="beforeprint()" onafterprint="afterprint()" bgcolor="infobackground"> <p><input disabled name="idPrint" type="button" value="Print this page" onclick="print()"> </p> </body> <script defer> function window.onload() { idPrint.disabled = false; } var originalTitle; function beforeprint() { idPrint.disabled = true; originalTitle = document.title; document.title = originalTitle + " - by Captain"; } function afterprint() { document.title = originalTitle; idPrint.disabled = false; } </script>
Note that we have Internet Explorer 4. x support only for <BODY> inline assigned event handlers. Not perfect, but we think this is probably enough to maintain the same code for both Internet Explorer 4. x and Internet Explorer 5. Notice how, in the sample code above, we are updating the title of the document at print time.
We also want to be able to print a specific frame, probably from another frame. Or we might need to print a window's content from inside a frame. Luckily, WebBrowser prints the frame that has the focus by default; we just need to set the focus to the required frame (or to the window itself). This is required for Internet Explorer 4. x only. With Internet Explorer 5, we just call frame.print() on a specific frame.
Unfortunately there is no way in Internet Explorer 4.0 to set the focus on to a specific frame because frame.focus() simply has no effect. This is fixed with Internet Explorer 4.01, but users of Internet Explorer 4.0 will be able to print only the frame/window that has a focus.
If we use frame.focus() followed by idWBPrint.ExecWB, WebBrowser doesn't take the focus change immediately and ends up printing the whole window. We can work around this with a setTimeout() call to retain the idWBPrint.ExecWB command (technically, until the next message loop pump). Try the sample of frame printing we've provided.
Some unpleasant issues can also arise when we call printFrame from inside a frame. If the user cancels, the printing dialog will "bubble" for each frame nesting level up to the top window. This behavior is undesirable, but help is at hand. If you really need such functionality, you can use the Scripting Factory ActiveX control.
Another interesting kind of functionality is the printing of hidden HTML documents. For example, an author may want to print an HTML-generated database report. Have a look at the sample and check out the code description if you are interested in the details. The main method, printHidden(url), can be found in the shared include file printHid.js.
<head> <title>Hidden Document Printing</title> </head> <body bgcolor="infobackground"> <p>This is a main page</p> <script defer language=JScript src="print.js"></script> <script defer src="printHid.js"></script> <p><input disabled name="idPrint" type="button" value="Print this page" onclick="print()"> <input disabled name="idPrintFrame" type="button" value="Print the frame below" onclick="printFrame(idFrame)"> </p> <IFRAME style="visibility: visible" name="idFrame" width="50%" height="30%" src="frame.htm"> </IFRAME> <p><input disabled name="idPrintHidden" type="button" value="Print hidden page" onclick="printURL()"></p> <script defer> function window.onload() { idPrintHidden.disabled = false; idPrint.disabled = false; idPrintFrame.disabled = false; } function printURL() { idPrintHidden.disabled = true; // set to handle completition window.onprintcomplete = function() { idPrintHidden.disabled = false; } // print the content printHidden("report.htm"); } </script> </body> </html>
The samples we give above work without any custom controls or extensions. However, there is often a need to adjust page settings, such as headers/footers, portrait/landscape orientation, margins, and so forth -- and we have added the support for this into our free ScriptX ActiveX control. Feel free to check out ScriptX and a comprehensive printing sample with online page settings customization.
Because there is no support for Print Setup customization in the Internet Explorer DOM, many people have asked us "How do you do this Print Setup customization?" Okay, we'll tell you -- and you may laugh at the simplicity of the solution. We invoke a hidden "Page Setup Dialog" and retrieve/change the settings as though we were a user. That's it (although we do this in a smart way to be sure the code is safe). The corresponding dialog control IDs are unchanged among localized Internet Explorer versions, and they are also the same in Internet Explorer 4. x and Internet Explorer 5, so the code works well in all of them. If anything changes in future versions of Internet Explorer, we'll update ScriptX to match it. So far, this is the only way we know of to customize page settings from script.
ScriptX is part of the free MeadCo Scripting Factory -- developed for use with any scripting host: Internet Explorer 4.0 or later HTML, ASP, WSH, or Custom -- which itself forms part of the company's evolving Web applications tool kit. Established in 1988 and based in Cambridge UK, MeadCo builds and maintains online information systems for corporate and institutional clients around the world.