Print Preview 2: The Continuing Adventures of Internet Explorer 5.5 Print Customization

As of December 2011, this topic has been archived. As a result, it is no longer actively maintained. For more information, see Archived Content. For information, recommendations, and guidance regarding the current version of Internet Explorer, see IE Developer Center.

Chuck Ainslie
Microsoft Corporation

Summary: This article discusses the important topic of the user interface for a print preview window. (9 printed pages)


Building a Print Preview User Interface
Adjustable Margin UI Example
A Brochure Editor


Today we will complete the introduction to Microsoft® Internet Explorer 5.5 print customization begun in my Internet Explorer Developer Center article Beyond Print Preview in August. "Beyond Print Preview" introduced you to the four print template behaviors, LayoutRect, DeviceRect, TemplatePrinter, and HeaderFooter, and showed you the basics of their use. This article picks up where the last one left off, discussing the important topic of the user interface for a print preview window.

Note   An eighth print template sample has been added to the Printtemplates.exe sample application that was introduced in the last article. If you don't have the updated version, you can download it from the link below.

Download sample code Download the sample for this article

Building a Print Preview User Interface

When you use a custom template for printing and previewing with Internet Explorer, you must provide the user interface. No default or standard user interface is available to include in your template. Any print template worth writing probably includes some user interface controls. Typical controls include a button to print, a button to access page setup, and a button and/or check boxes so the user can change the view (that is, zoom in or zoom out on the pages or change which pages are displayed).

Template7.htm demonstrates the basic principles of a print template user interface. We won't examine all of its features—much of the template makes use of ordinary buttons and text boxes with attached event handlers. It's important, though, to notice how the template handles the DEVICERECT and LAYOUTRECT elements when changes are made in the page setup settings.


You should follow two basic rules when developing a print template with a user interface:

  1. Don't destroy LAYOUTRECT and DEVICERECT elements once they've been created. Hide them from view instead.

  2. Don't use the display or visibility properties to hide LAYOUTRECT and DEVICERECT elements. Use dynamic positioning to push them off the screen.

If your print preview window includes a Page Setup button, the template must update the size and location of DEVICERECT and LAYOUTRECT elements whenever the user makes changes in the page setup settings, and page setup changes have the greatest impact on the page count. Therefore, a print template must be designed so that it creates new pages if the page count increases. It must also be ready to handle any excess pages if the page count decreases.

As it turns out, the dynamic print templates described in the previous article already handle the creation of new pages. You might want to revisit Template3.htm to refresh your memory about how it works. Although Template3.htm doesn't have a mechanism to access page setup, let's suppose for a minute that it does. If you could change the page setup in Template3.htm, the mechanism would need to reread the pageWidth, pageHeight, and margin settings from the TemplatePrinter behavior and update the style classes for the LAYOUTRECT and DEVICERECT elements accordingly. If the update in either style were an actual change in the height and width properties of the styles, the source would reflow automatically in the LAYOUTRECT elements. The reflow would cause the onlayoutcomplete event to fire again for each LAYOUTRECT in the chain. This event would have no effect for any LAYOUTRECT elements but the last one, because the onlayoutcomplete event handler for all LAYOUTRECT elements but the last is set to null. The last LAYOUTRECT would still have the page creation handler attached to its onlayoutcomplete event, so if additional pages were needed, they would be created.

This is fine when the page count increases. However, if the page count decreases, what do you do with the excess LAYOUTRECT and DEVICERECT elements? There are two possible strategies:

  1. Destroy unneeded LAYOUTRECT and DEVICERECT elements.

  2. Hide unneeded LAYOUTRECT and DEVICERECT elements.

Destroying LAYOUTRECT elements can be tricky. First, you can only remove them from the end of a chain, not from the middle or beginning. Second, if you remove LAYOUTRECT elements from the end of a chain, you can't merely add them back again later. You must rebuild the chain from the beginning. This can cause a performance loss in the case of large documents. For these reasons, it's best to hide unneeded LAYOUTRECT and DEVICERECT elements when the page count decreases.

Template7.htm hides the excess pages in the following way. Instead of setting the onlayoutcomplete event to null on a LAYOUTRECT when a new LAYOUTRECT is added after it, Template7.htm sets the onlayoutcomplete event handler to an alternate event handler function. This new handler records the page total if the event.contentOverflow property is false. The handler then calls a function to hide any excess pages following the LAYOUTRECT on which the event.contentoverflow property is false.

The ShowFilledPagesAndHideExcessPages function in Template7.htm doesn't use the display property to hide excess pages for two reasons. First, the onlayoutcomplete event does not fire for undisplayed LAYOUTRECT elements. If you use the display property, once a DEVICERECT is hidden, it remains hidden forever after. Second, using the display property to hide and show pages is slow in the print template environment—as much as five times slower than using positioning.

Rather than manipulating the display property for the excess DEVICERECT elements, ShowFilledPagesAndHideExcessPages sets the position property to static for all pages less than or equal to the total pages. It sets the position property to absolute for all other pages. The class attached to the DEVICERECT elements has a left property set to -50 inches. When the position property for a DEVICERECT is static, the left property is ignored and the page is visible. When the position property for a DEVICERECT is set to absolute, the left property is not ignored and the page is pushed off the screen to the left.

This is a simple mechanism for a simple print template. Another solution (taken by the default print template for Internet Explorer) hides all but one page at a time in the print preview window. To do this, the ShowFilledPagesAndHideExcessPages function could manipulate the left property itself to push all but the currently displayed page and excess blank pages off the screen.

We've covered the most important considerations for a print template user interface. There are a couple of minor details of this template that I'll mention. First, notice that the user interface section and the pages are each contained in separate DIV elements (with IDs of ui and pagecontainer, respectively). Notice that the BODY element has its SCROLL attribute set to no, and that the pagecontainer DIV that contains the pages has an overflow property set to auto in its style. With these settings, the user interface controls remain on the screen at all times and the pages scroll within the pagecontainer DIV (creating a frame-like look without using frames). Well, almost. The following line (in the Init function) is necessary to size the pagecontainer DIV to the available height in the window. = document.body.clientHeight - ui.clientHeight;

Second, notice that the DEVICERECT elements for the pages are not placed directly within the pagecontainer DIV. Instead, they are contained in another DIV called zoomcontainer nested within the pagecontainer DIV. The zoom mechanism to scale the pages is applied to this DIV, rather than to the pagecontainer DIV or directly to the DEVICERECT elements themselves. Applying the zoom to pagecontainer would shrink or expand the whole page container window of the Print Preview dialog box, not just the pages within the container. Applying the zoom to the individual DEVICERECT elements would cause the pages to print at the zoom settings.

Next we'll look at a sample template that adds some functionality not found in the default Internet Explorer print preview interface.

Adjustable Margin UI Example

I developed this example over the Labor Day weekend this year. What can I say—the idea popped into my head and I just had to make it work. The new Template8.htm is based on Template7.htm. It adds a button to the interface called Hide Margins, a dashed box around the LAYOUTRECT elements in the print template, and four margin markers along the top and left edge of the page 1 LAYOUTRECT. (If you click the Hide Margins button, the markers and dashed box disappear and the button text changes to Show Margins. The margin markers are movable. Drag any of these markers to a new location to resize a margin. When you click a margin marker, a ToolTip appears that shows the margin setting as you drag the marker.

I added the markers to the template as IMG elements in the pagecontainer DIV. I created the dashed box using the border properties of the lorstyle class attached to the LAYOUTRECT elements.

Three mouse event handlers are attached to the pagecontainer DIV and contain the functionality of the adjustable margin markers. These event handlers are onmousedown, onmousemove, and onmouseup.

The onmousedown event handler checks whether the left mouse button has been pressed. If the button has been pressed, the handler determines if the pointer is over one of the markers. If it is, the handler sets a flag to indicate which marker is currently under the pointer. The handler also records the initial position of the mouse event and opens a ToolTip with the current margin setting for the relevant margin marker.

The onmousemove event handler moves the relevant margin marker in response to mouse movements, either horizontally in the case of the left and right margin markers or vertically in the case of the top and bottom margin markers. Before any movement takes place, the handler checks to see that the margin marker was not moved past its opposite marker or into the unprintable region of the page. When the handler moves the marker, it also updates the margin information displayed in the ToolTip.

When the onmouseup handler fires, the margins actually change. The handler determines the new margin from the margin marker position and updates the styles for the document (lorstyle, headerstyle, and footerstyle). The ToolTip disappears.

Now that you have a general sense of the event handlers that make the adjustable margins work, let's look at some of the important details. There are two concerns with each of these functions. First, the calculations of the margin marker positions must include the zoom percentage. These calculations take place in the function MouseMoveHandler. They're rather detailed and took a while to figure out. Take a look.

Second, the marker positions are measured in pixels while the margin positions are measured in inches (actually, in hundredths of an inch). The onmouseup handler must convert the pixel markup positions to inches before it can update the margin settings using the TemplatePrinter behavior. I used a simple trick to determine the pixel-to-inch ratio. The template has an extra hidden marker positioned one inch from the top and left. Both the pixelLeft and pixelTop properties of this invisible marker give the number of pixels in one inch. The conversions take place in the function MouseUpHandler, and use similar formulas for all markers. Here is the conversion formula for the left margin marker:

printer.marginLeft = 100 * (mmarkerLeft.offsetLeft - 5)/iPixelToInchRatio;

A Brochure Editor

The Brochure Machine sample demonstrates a simple Web-based application that edits and prints brochures. You can download the entire project source code from the Brochure Machine default page The Web page shows two pages of a two-fold brochure. Each page has three editable columns and includes an option to add a watermark image. The user interface provides toolbars that enable the user to format the text and images. The user can:

  • Apply bold or italic formatting to text.

  • Change the font, font size, and font color.

  • Add, change, reposition, and resize images.

  • Change the column margins.

Most important to this article, the page provides a button to preview the brochure before printing.

The Web page does a good job of representing how the brochure will look, but it's not perfect—that's the reason for print preview, after all. The page displays scroll bars on a column to indicate when that column contains too much content. When you view the brochure in print preview mode, you get an accurate image of how the brochure will print. The template checks each column to make sure that the content doesn't overflow. It also checks to make sure that the printer is in landscape mode and prompts the reader to switch to landscape mode if necessary.

There are three files in this sample:

  • BrochureMachine.htm. The Web page with the editable brochure.

  • A cabinet file containing BrochureMachine.dll, which is the control that opens the print preview window.

  • PT.htm. The print template for viewing and printing the brochure. This file is a resource contained in BrochureMachine.dll.

We'll look at each of these components.

The ActiveX Control, BrochureMachine.dll

Because this sample is Web-based, the binary code to print and preview with the custom template is loaded as a COM component and included as an object in the HEAD section of the page. The control was developed with Microsoft Visual C++® 6.0 and the ATL Component Wizard. Most of the code just provides support for the COM control. The working part, from our point of view, consists of a single method named LaunchPT on a single interface, IPTLauncher. The code for LaunchPT is simple. It obtains an IOleCommandTarget interface for the document object from the IOleClientSite interface in the control by way of IOleContainer, IServiceProvider, IWebBrowser2, IDispatch, and IHTMLDocument2. It then calls IOleCommandTarget::Exec to issue the IDM_PRINTPREVIEW command with the pvaIn parameter specifying the address of the custom template resource, PT.htm, contained in the DLL.

Note For clarity, this function contains no error handling.

    CComPtr<IOleContainer> spContainer;
    CComPtr<IServiceProvider> spSP;
    CComPtr<IWebBrowser2> spWB;
    CComPtr<IDispatch> spDisp;
    CComPtr<IHTMLDocument2> spDoc;
    CComPtr<IOleCommandTarget> spCT;
    CComVariant vPTPath = "res:/BrochureMachine.dll/PT.htm";

    spContainer->QueryInterface(IID_IServiceProvider, (void**)&spSP);
    spSP->QueryService(SID_SWebBrowserApp, IID_IWebBrowser, (void**)&spWB);
    spDisp->QueryInterface(IID_IHTMLDocument2, (void**)&spDoc);
    spDoc->QueryInterface(IID_IOleCommandTarget, (void**)&spCT);

    return S_OK;

The Main Page, BrochureMachine.htm

The Web page for the Brochure Machine shows the user the front and back page of the brochure and provides toolbars for various editing tasks. The pages have editable columns for each section of the brochure. The section styles and the page width and height are specified in inches to correspond with letter-size paper. As previously mentioned, sections have been implemented to display scroll bars as a visual signal that there is too much content to fit in a particular section.

There's a fair amount of code to this sample to provide a meaningful editing environment. For this article, there are two relevant code sections: the ShowPrintPreview function and one line in the Init function to add an expando object to the document object. The expando object will pass information to the print template. ShowPrintPreview initializes the object and calls LaunchPT to open the print preview window.

The Template, PT.htm

For the most part, PT.htm uses printing code you've already seen in other print templates. In addition, it provides error checking to make sure the LAYOUTRECT elements haven't overflowed, prompts the user to switch to landscape orientation, and provides a technique for sourcing in content from the Web page.

Building a document on the fly for a LAYOUTRECT

In the last article, I demonstrated how to use the dialogArguments.__IE_ContentSelectionUrl property to print only the current selection in Internet Explorer. This is a useful feature, but what if you want to print a section that's not the current selection, or print multiple noncontiguous sections?

Because the template has a reference to the document object for the currently displayed page, __IE_BrowseDocument, you can use that reference to extract the portions you want from the original and build documents on the fly for the LAYOUTRECT elements you want to print. Create a new BODY element using document.createElement, extract the innerHTML from the elements you want through __IE_BrowseDocument, and insert that innerHTML in the new BODY element you've created. The template does this six times using a for loop in the Init function to obtain the content for the columns.

for (i = 1; i <= 6; i++)
    document.all("layoutrect" + i).onlayoutcomplete = OverflowChecker;
    theBody = document.createElement("BODY");
    theBody.innerHTML = 
        dialogArguments.__IE_BrowseDocument.all("layoutrect" + i).innerHTML;
                 dialogArguments.__IE_BrowseDocument.all("layoutrect" + i));    

    document.all("layoutrect" + i).contentSrc = theBody.document;

The template also extracts the images for the watermarks and places them in two additional DEVICERECT elements, one for each page. The template doesn't have to extract the brochure information in eight sections. The brochure can extract it in just two operations, acquiring the content from the front DIV as a whole and the back DIV as a whole. I did it this way because I wanted the print template to be "aware" of content overflow in any of the brochure columns and to warn the user visually when overflow occurs.

Notice the function FixImageURLs. The document created with the document.createElement("BODY") call does not use the correct URL root to resolve relative links or paths specified in the source document. It uses the path to the DLL when it attempts to resolve relative links and paths. For instance, the path to an image with a relative source path of graphics/globe1.gif in theBody might be res://brochuremachine/graphics/globe1.gif. This makes sense because the new element has been created by the DLL. FixImageURLs corrects this problem by grabbing the correct paths from the source.

function FixImageURLs(oBody, oSourceDiv)
    oBodyImgColl = oBody.document.all.tags("IMG");
    oSourceDivImgColl = oSourceDiv.all.tags("IMG");
    for (j = 0; j < oBodyImgColl.length; j++)
        oBodyImgColl.item(j).src = oSourceDivImgColl.item(j).src;

You can use this technique to create dynamic changes in LAYOUTRECT content while in print preview mode. All you have to do is create a new BODY element with the content you want and substitute it for the contentSrc in a LAYOUTRECT. The change will take effect immediately in the print preview window.

Passing information to a print template

The print template needs information from the Web page to determine margin settings and watermark placement. The template could drill into __IE_BrowseDocument to obtain this information, but I decided to make it a little simpler for the template to obtain the information it needs. This way, I can illustrate a technique to pass information between a Web page and a print template. BrochureMachine.htm creates an expando object on the document object called brochureInfo. Immediately before the print preview window opens (in other words, just before the call to LaunchPT), the Web page initializes this expando object with a variety of properties for the margin settings and the watermark placement. The print template can read these properties from __IE_BrowseDocument.

Although the Brochure Machine sample uses this technique only to read properties from __IE_BrowseDocument, there is a lot more potential here if you want to use it.

  • Objects attached as expandos to the Web page document object are full-fledged objects. They can contain functions, arrays, and other kinds of data and nested objects.

  • A print template can make changes to __IE_BrowseDocument. These changes directly affect the document rendered by the browser.

  • You can also attach new expando properties to __IE_BrowseDocument from the print template code. These properties will be available to the Web page when the print preview window closes. There is one limitation, though: if you attach an expando object to __IE_BrowseDocument, it will be available only while the print template is active. The print template owns all objects it creates. After the print preview window closes or printing is completed, any objects created by the template are deleted when the template destroys itself.

Checking page orientation

There is no page orientation setting with the print architecture. When a page switches from portrait mode to landscape mode or back, the page width and height properties merely exchange places. The print template checks the page orientation and paper size by verifying that the pageWidth property of the TemplatePrinter behavior is 1100 (remember, page measurement is in hundredths of an inch) and that the pageHeight is 850. If the property settings are otherwise, the template displays an appropriate alert and opens the Page Setup dialog box so the user can correct the settings.

Content overflow alert

The onlayoutcomplete event handler for each of the six LAYOUTRECT elements is a function called CheckOverflow. It simply checks the contentOverflow property of the event object. If the property is true, the function alerts the user that a column contains too much stuff and adds a blinking border to that LAYOUTRECT.


Combining this article with Beyond Print Preview, you now have the knowledge you need to create some pretty remarkable applications with the new printing architecture in Internet Explorer 5.5. I hope you've learned as much from reading this article as I have from writing it.

Chuck Ainslie is a Programmer/Writer on the Internet Explorer SDK documentation team, and seldom complains about the food at the MS cafeterias. When he's not tormenting his officemates with Wagnerian opera, you can find him at one of Seattle's tango venues (he's very smooth on the dance floor).