Internet Explorer Development Technical Articles
Beyond Print Preview: Print Customization for
Internet Explorer 5.5
Chuck Ainslie
Microsoft Corporation
August 2000
Summary: This article explains how to write a print template, what you
need to do to use the print template you've written, and some ideas and
applications that can exploit print templates. (16 printed pages)
Contents
Introduction
What Can I Do with Custom Print Templates?
How Do I Use These Print Template Things?
Writing a Print Template
Safety Considerations When Using Print
Templates
Cool Things You Can Do
Endless Printing Possibilities
Introduction
I'd like to introduce you to the new printing architecture for Microsoft®
Internet Explorer 5.5. The new architecture is very cool for a couple of
reasons. The most visible reason is that the new architecture is the foundation
for the new Internet Explorer print preview facility. Print preview in itself
is an exciting new feature—relatively speaking, of course. I'd give it up
in a minute for time travel, which I'd give up for world
peace—reluctantly. (Well, maybe not.) However, print preview is only one
part of a bigger story. The new printing architecture offers much more than a
single new feature. The new architecture has been designed to be highly
flexible and extensible, and it is exposed here for your use. In other words,
you can use the Internet Explorer 5.5 printing architecture to customize how
Internet Explorer and the WebBrowser control handle printing.
In this article, you'll learn how to write a print template, which is the
mechanism Internet Explorer 5.5 uses to control print/preview behavior and page
layout for printing/previewing. You'll learn what you need to do to use a print
template you've written. You'll also see some ideas and applications that could
exploit print templates. A future article will explore the possibilities for
creating a custom print preview user interface and customizing the Page Setup
and Print dialog boxes.
What Can I Do with Custom Print
Templates?
The new print customization possibilities add a new layer of possibilities to
Internet Explorer as a platform for application development. By creating a
print template, you can control:
-
The layout of pages when printed/previewed, and the content that is
printed/previewed on them.
-
How print jobs are handled—for instance, which pages are printed in what
order.
-
The look of the Print Preview window and controls available on the print
preview user interface.
Because you now have extensive layout and handling control over printing,
Internet Explorer can fit many new application scenarios, such as:
-
Adding boilerplate text to print jobs, including company logos, legal notices,
and advertisements.
-
Customized placement and styling for header and footer elements, such as page
numbers or chapter headings.
-
Customized printing for scheduling or appointment applications, which gives the
user different layout or binding options.
-
Multi-fold brochure printing.
-
Book-style printing using mirrored margins for the odd and even pages.
How Do I Use These Print Template
Things?
A print template is written using standard HTML, script, and four element
behaviors specific to print templates:
-
LAYOUTRECT
-
DEVICERECT
-
TEMPLATEPRINTER
-
HEADERFOOTER
For more specific information about these elements, see the
Print Template Reference documentation. I've also written a spiffy
sample application you can download that demonstrates the print template
behaviors.
Download spiffy sample application
(Note: You must choose the "Save this program to disk" option to run this
application successfully.)
First, let's assume you've got a print template you want to use. How do you get
Internet Explorer or the WebBrowser control to recognize your template and use
it in place of the default print template provided with Internet Explorer? The
most important requirement is that you work in C++. Even though print templates
are HTML files, they can only be used from C++ in an application, Microsoft
ActiveX® control, binary behavior, or other binary executable file that
hosts the WebBrowser control. There is currently no scriptable mechanism for
using print templates, nor any mechanism to use them in Microsoft Visual
Basic®. To pass a print template to the WebBrowser control, you must be
able to issue or intercept the
IDM_PRINT and
IDM_PRINTPREVIEW commands through IOleCommandTarget::Exec. The
print template path is passed to the WebBrowser control in the pVarArgIn
VARIANT argument to IOleCommandTarget::Exec.
Let's say, for instance, that you have a print template called MyTemplate.htm
whose path is c:\MyTemplate.htm. Assume also that you have instantiated the
WebBrowser control and obtained an IWebBrowser2 pointer, or if you're in
an ActiveX control, you've acquired a pointer to Internet Explorer's IWebBrowser2
interface from IOleClientSite and IOleContainer. (An IHTMLDocument2
interface pointer will work, too.) Let's assume the IWebBrowser2 interface
pointer is named pWB. Query pWB for an IOleCommandTarget pointer:
IOleCommandTarget* pCmdTarg;
pWB->QueryInterface(IID_IOleCommandTarget, (void**)&pCmdTarg);
From pCmdTarg, you can now call IOleCommandTarget::Exec with
either the IDM_PRINT or IDM_PRINTPREVIEW commands and a VARIANT
containing the path to your print template.
VARIANT vTemplatePath;
V_VT(&vTemplatePath) = VT_BSTR;
V_BSTR(&vTemplatePath) = SysAllocString(L"c:/MyTemplate.htm");
pCmdTarg->Exec(&CGID_MSHTML,
IDM_PRINT,
OLECMDEXECOPT_PROMPTUSER,
&vTemplatePath,
NULL);
For IDM_PRINT, the third parameter can be OLECMDEXECOPT_PROMPTUSER,
OLECMDEXECOPT_DONTPROMPTUSER, or OLECMDEXECOPT_DODEFAULT. The
default action for the IDM PRINT command will be to prompt the
user with the Print dialog box before printing. For IDM_PRINTPREVIEW,
there is always a user interface—that's the whole point, isn't it?—so
the third parameter is ignored. Just use OLECMDEXECOPT_DONTPROMPTUSER.
Writing a Print Template
It's time for the "main event"—writing a print template itself. We'll start
at the beginning, showing the four print template element behaviors you use to
write a print template and the basics of their use. When we get to the TemplatePrinter
behavior, we'll discuss the dialogArguments object passed to a print
template from the WebBrowser control or Internet Explorer. We'll also discuss
how to dynamically create LAYOUTRECT and DEVICERECT elements,
which is the best way to ensure that the entire source document is displayed or
printed. At various points in our discussion, we'll look at thread
synchronization issues that need to be addressed when a print template prints a
document.
Laying Out a Page: the LayoutRect and DeviceRect Element Behaviors
The LayoutRect and DeviceRect element behaviors provide the visual
formatting to make a page look the way you want. A DEVICERECT element
represents a single page for printing. LAYOUTRECT elements go inside DEVICERECT
elements and establish the area of each page containing the source document's
content. With this in mind, take a look at the syntax for a pair of very basic
pages:
<IE:DEVICERECT ID="page1" CLASS="pagestyle" MEDIA="print">
<IE:LAYOUTRECT ID="layoutrect1" CLASS="lorstyle"
CONTENTSRC="document" NEXTRECT="layoutrect2"/>
</IE:DEVICERECT>
<IE:DEVICERECT ID="page2" CLASS="pagestyle" MEDIA="print">
<IE:LAYOUTRECT ID="layoutrect2" CLASS="lorstyle"/>
</IE:DEVICERECT>
First, notice that each of the tags begins with a namespace declaration (the IE:).
A print template declares the namespace in its first lines like this:
<HTML XMLNS:IE>
<HEAD>
<?IMPORT NAMESPACE="IE" IMPLEMENTATION="#default">
Here, the XMLNS attribute on the HTML tag declares a namespace called "IE"
for the template. You can call the namespace anything you want, so long as you
substitute your namespace name for the "IE" wherever it occurs. The IMPORT
tag then imports the default Internet Explorer behavior implementations to the IE
namespace, making the print template element behaviors available to the
template.
Second, notice that the LAYOUTRECT elements are scopeless. They do not
have a closing tag, and their closing bracket is preceded by a forward slash.
This is standard XML syntax for an element without a closing tag. A LAYOUTRECT
cannot contain any HTML. For instance, the following code would have no effect:
layoutrect1.innerHTML = "<B>Hi there!</B>";
Now, look at the attributes on each tag. The ID attribute is well known.
It gives an element an identifying name. The CLASS attribute points to
style classes defined somewhere else on the page. The styles for the LAYOUTRECT
element must include a width and height or they will not display.
Their default values are zero. The width and height styles for DEVICERECT
elements should correspond to the paper settings for the current printer. We'll
see how you can set these styles when we get to the TemplatePrinter behavior.
The MEDIA attribute is specific to the DEVICERECT element. When
set to "print," this attribute specifies that the page should be printed
at the highest resolution possible for the printer. You should always include
this attribute and set it to "print."
Where this example gets interesting is the CONTENTSRC and NEXTRECT
attributes on the first LAYOUTRECT. The CONTENTSRC attribute is
important. It tells a LAYOUTRECT what content to use to fill itself. The
word "document" is a keyword that tells the LAYOUTRECT to fill
itself with the current document displayed in the WebBrowser control. This is
usually true, unless the current document specifies an alternate URL for
printing. See the
LINK element for additional details. The displayed document and print
document can also be different when the user right-clicks a link and selects Print
Target. If you don't want to print the current document, you can set
the CONTENTSRC to a URL or other address to load other content into a LAYOUTRECT.
The NEXTRECT attribute specifies the next LAYOUTRECT to fill once
the current one is full. Perhaps you can see the idea behind LAYOUTRECT elements:
you specify a CONTENTSRC attribute for the first LAYOUTRECT in a
series of LAYOUTRECT elements. Then you chain a bunch of them together
with the NEXTRECT attribute to provide enough area to completely render
the source document.
Now the question is:
How can you be sure you have enough LAYOUTRECT elements to hold a source
document's content?
You might create a print template with a million LAYOUTRECT elements all
connected together, but such a template would have lousy performance. And what
about that two million-page document you want to print? The way to go in a
print template is to create DEVICERECT and LAYOUTRECT elements
dynamically when the print template first loads or as needed after the print
template loads—you'll see this when it comes time to look at user
interfaces in the next article.
The onlayoutcomplete event
The key to the dynamic creation of LAYOUTRECT elements is the onlayoutcomplete
event. This event occurs when a LAYOUTRECT is done filling. A LAYOUTRECT
finishes filling either when there is no more content from the source to fill
the LAYOUTRECT or when there is no more space in the LAYOUTRECT for
content. You can determine which of these conditions occurred by checking a new
property on the event object called contentOverflow. When the property
is false, no further content needs to be rendered. When the property is true,
the source document has not been completely rendered, and you need another LAYOUTRECT
to fill.
With this in mind, let's look at the next example:
<SCRIPT LANGUAGE="JScript">
var iNextPageToCreate = 1;
function AddFirstPage()
{
newHTML = "<IE:DEVICERECT ID='page1' MEDIA='print' CLASS='pagestyle'>";
newHTML += "<IE:LAYOUTRECT ID='layoutrect1' CONTENTSRC='document'" +
"ONLAYOUTCOMPLETE='OnRectComplete()' NEXTRECT='layoutrect2'" +
"CLASS='lorstyle'/>";
newHTML += "</IE:DEVICERECT>";
pagecontainer.insertAdjacentHTML("afterBegin", newHTML);
}
function OnRectComplete()
{
if (event.contentOverflow == true)
{
document.all("layoutrect" + iPageToCreate).onlayoutcomplete = null;
newHTML = "<IE:DEVICERECT ID='page" + (iPageToCreate + 1) +
"' MEDIA='print' CLASS='pagestyle'>";
newHTML += "<IE:LAYOUTRECT ID='layoutrect" + (iPageToCreate + 1) +
"' ONLAYOUTCOMPLETE='OnRectComplete()' NEXTRECT='layoutrect" +
(iPageToCreate + 2) + "' CLASS='lorstyle'/>";
newHTML += "</IE:DEVICERECT>";
pagecontainer.insertAdjacentHTML("beforeEnd", newHTML);
iPageToCreate++;
}
}
</SCRIPT>
<BODY ONLOAD="AddFirstPage()">
<DIV ID="pagecontainer">
<!-- Dynamically created pages go here. -->
</DIV>
</BODY>
</HTML>
Here we have an (initially) empty DIV element named pagecontainer.
We also have two functions, AddFirstPage and OnRectComplete. AddFirstPage
is called once when the document has finished loading, and simply adds the
first page to pagecontainer using the insertAdjacentHTML method.
Notice the declaration for the first LAYOUTRECT. It contains the
attribute ONLAYOUTCOMPLETE='OnRectComplete()'. When this LAYOUTRECT
has finished filling, the OnRectComplete function will be called.
OnRectComplete checks the contentOverflow property on the event
object and adds a new LAYOUTRECT if there is more content to render from
the source document. Each of the LAYOUTRECT elements it adds has the
same ONLAYOUTCOMPLETE event handler declaration pointing to OnRectComplete.
The variable iNextPageToCreate tracks how many pages have been created
and is used to generate the IDs for new LAYOUTRECT and DEVICERECT
elements.
Notice that when the OnRectComplete function adds a new page, it sets the onlayoutcomplete
event handler for the current LAYOUTRECT element to null. You want to do
this because a print template periodically re-flows the source document into
the LAYOUTRECT elements. For instance, when the Print Preview window
is resized, or when the styles on LAYOUTRECT elements change. If you
don't unset the onlayoutcomplete handler, you'll end up with repeated
calls to OnRectComplete that generate a bunch of pages you don't need.
At this point, we've covered the basics of the LAYOUTRECT and DEVICERECT
elements. This knowledge is enough to get a print template to display a
document in the Print Preview dialog window, but you still may not know
how to make sure the page styles correspond to the Page Setup and
printer settings. More important though, you can't print with the knowledge
you've gained here so far. For that, we need to discuss the TemplatePrinter
behavior.
Making a Print Template Print: the TemplatePrinter Element Behavior
The TemplatePrinter element behavior is a print template's interface to
the printer and to Page Setup settings. Adding this element behavior to
a print template is simple—just add the following line to the BODY element
of the template:
<IE:TEMPLATEPRINTER ID="printer"/>
From here on out, all methods and properties of the TemplatePrinter behavior
can be accessed using the ID for the TEMPLATEPRINTER element:
printer.showPageSetupDialog();
Check out the reference documentation for the
TemplatePrinter behavior. You'll see that it has quite a few properties
and methods you can use.
Let's run through a printing scenario. In your C++ application, you write a call
to IOleCommandTarget:Exec with IDM_PRINT or IDM_PRINTPREVIEW,
passing it OLECMDEXECOPT_PROMPTUSER, OLECMDEXECOPT_DONTPROMPTUSER,
or OLECMDEXECOPT_DODEFAULT and the path to the print template. The print
template is instantiated.
How does the print template know whether to prompt the user for printing, print
without a prompt, or display the Print Preview window?
The answer is that when the print template is instantiated, the window object
for a print template has a dialogArguments object attached to it. The dialogArguments
object has a property named __IE_PrintType that contains the value "Prompt,"
" NoPrompt," or "Preview," depending on the OLECMDEXECOPT value
from the IOleCommandTarget::Exec call. A function in the print template
can query for this property and direct the template to act accordingly.
function CheckPrint()
{
switch (dialogArguments.__IE_PrintType)
{
case "Prompt":
if (printer.showPrintDialog())
PrintPrep();
break;
case "NoPrompt":
PrintPrep();
break;
case "Preview":
default:
break;
}
}
This example has a simple switch statement that responds to each of the
possible values. When appropriate, the example calls a function named PrintPrep
that begins this template's printing process. Note that the case for "Preview"
is empty because a print template's default behavior displays the Print Preview
window. The case block for "Prompt" calls a method of the TemplatePrinter
behavior called showPrintDialog before calling PrintPrep. If the
user clicks the Cancel button in the Print dialog box, PrintPrep
is not called. Note that OLECMDEXECOPT_PROMPTUSER does not automatically
cause the Print dialog box to open. It is the script in the print
template that displays the Print dialog box and causes the template to
respond appropriately to the IOleCommandTarget::Exec call.
What's this function PrintPrep, you may ask? What does it look like and what's
its purpose?
Here it is:
function PrintPrep()
{
if (layoutrect1.contentDocument.readyState == "complete")
PrintNow();
else
layoutrect1.contentDocument.onreadystatechange =
PrintWhenContentDocComplete;
}
PrintPrep is an important function, mostly when printing without a user
prompt. The printing process runs as a separate thread from the document
loading process. A print job could be sent to the printer before the document
has finished loading into the print template—if this happened, the
template would print blank pages or an incorrect number of pages. To make the
print process wait for the source document, PrintPrep sets an onreadystatechange
event handler on the document object of the source used to fill the chain of LAYOUTRECT
elements. The document object of the source is referenced by the contentDocument
property on the first LAYOUTRECT in the chain. The onreadystatechange
event handler just waits for the readyState to be "complete,"
then continues the printing process by calling a function named PrintNow
that's defined elsewhere in the template.
function PrintWhenContentDocComplete()
{
if (layoutrect1.contentDocument.readyState == "complete")
{
layoutrect1.contentDocument.onreadystatechange = null;
PrintNow();
}
}
Let's get on with it and actually print something.
Now, let's look at the PrintNow function, where the printing actually
occurs in this particular print template example:
function PrintNow()
{
var startPage;
var endPage;
var oDeviceRectCollection = document.all.tags("DEVICERECT");
// Check dialogArguments
if (dialogArguments.__IE_PrintType == "NoPrompt" ||
printer.selectedPages == false)
{
// Printing w/o prompt, so set startPage and endPage to print all pages
startPage = 1;
endPage = oDeviceRectCollection.length;
}
else
{
// Printing w/prompt, so set startPage and endPage
// from TemplatePrinter and do some error checking
startPage = printer.pageFrom;
endPage = printer.pageTo;
if (startPage > endPage)
{
alert("Error: Start page greater than end page");
return;
}
if (startPage > oDeviceRectCollection.length)
{
alert("Error: Start page greater than number of pages in print job.");
return;
}
if (endPage > oDeviceRectCollection.length)
{
alert("Warning: End page greater than number of pages in print job." +
"Continuing Print Job.");
endPage = oDeviceRectCollection.length;
}
}
// Now that startPage and endPage are set, process the print job
printer.startDoc("Printing from Custom Print Template");
for (i = startPage - 1; i < endPage; i++)
printer.printPage(oDeviceRectCollection[i]);
printer.stopDoc();
}
The heart of PrintNow is in the last four lines. All the rest is
preparation to determine which pages to print. These lines show the TemplatePrinter
methods startDoc, printPage, and stopDoc. You always start
the printing process with the startDoc command and finish with the stopDoc
command. These two commands coincide roughly with the appearance and
disappearance of the print icon in the system tray. The argument to startPage
becomes the document name as displayed in the printer's document queue. The
function printPage prints a single DEVICERECT element. As you can
see, PrintNow uses a for statement to loop through the DEVICERECT
collection for the pages that need to be printed.
There's one question that I haven't addressed yet. Remember the CheckPrint
function at the beginning of this section, where the print template first
determines how to handle the print job?
When should a print template check the print job type?
The timing of this call is important when a print template generates its LAYOUTRECT
and DEVICERECT elements dynamically, as most should do. You might think CheckPrint
could be the onload event handler, or could be called from the onload
event handler. However, it's not quite that simple. When a print template
loads, there are two threads involved. One is the thread loading the print
template and creating the DEVICERECT and LAYOUTRECT elements. The
other is the thread loading the source document into the LAYOUTRECT elements.
In general, the thread loading the template will finish after the thread
loading the source document, but both threads must finish before the template
can print properly.
During print preview or when printing with a prompt, this is usually not a
problem; enough delay is built into each of these processes that the template
will preview or print correctly. The issue crops up when printing without a
prompt, when each thread is racing to completion. In the example presented by
this article, a good place to put the call to CheckPrint is in the onlayoutcomplete
handler, OnRectComplete. The call to CheckPrint could come as an else
alternative in the if statement—when the contentOverflow property
is false. While this is almost enough for a solution, there's one
further complication. The print job can't be processed from within the onlayoutcomplete
event handler because the printer or screen's device context (DC) can't render
while the LAYOUTRECT elements are still being measured out. For this
reason, you must let the event handler finish before calling CheckPrint.
One simple way to do this is to set a timer that delays the call to CheckPrint
slightly. The OnRectComplete function then looks like this:
function OnRectComplete()
{
if (event.contentOverflow == true)
{
// Add another LAYOUTRECT and DEVICERECT
}
else
{
setTimeout("CheckPrint()", 100);
}
How long a delay is necessary?
A delay of as little as one millisecond can be enough. For safety, 100
milliseconds should be ample.
Keep in mind that the processing of CheckPrint in this example includes a
check of the source document's ready state in its PrintPrep function
calls. The example makes sure that both the LAYOUTRECT/DEVICERECT
creation thread and the source document loading thread are complete before
printing.
You've seen so far how the LayoutRect, DeviceRect, and TemplatePrinter
behaviors work. There's one more print template behavior to cover before we're
done—the HeaderFooter behavior.
Adding Headers and Footers with the HeaderFooter Behavior
The HeaderFooter behavior is a conversion tool. It takes header and
footer formatting strings, typically from the Page Setup dialog box, and
generates HTML that you can insert in the DEVICERECT elements of a print
template. You don't have to use the HeaderFooter behavior to add headers
and footers. In fact, you don't have to add headers and footers at all, or you
can add them using your own custom scheme. The HeaderFooter behavior is
useful for generating HTML based on the user's header/footer settings in the Page
Setup dialog box and in conformance with standard Internet Explorer
style.
To use the HeaderFooter behavior, you must first include it in a print
template. The syntax is the same as the TemplatePrinter:
<IE:HEADERFOOTER ID="headfoot"/>
To generate the HTML you need for the headers and footers, set the HeaderFooter
behavior's textHead and textFoot properties to the TemplatePrinter
behavior's header and footer properties. These two properties of
the TemplatePrinter behavior contain the header and footer formatting
strings from the Page Setup dialog box. These strings will create
headers and footers based on the user's specifications. If you want to override
the user's header and footer settings, set the textHead and textFoot
properties with your own header and footer formatting strings. Here is how you
might prepare the HeaderFooter behavior to generate the header and
footer HTML for the first page of a document. The date and time are set
automatically by the HeaderFooter behavior.
headfoot.textHead = printer.header;
headfoot.textFoot = printer.footer;
headfoot.url = dialogArguments.__IE_BrowseDocument.URL;
headfoot.title = dialogArguments.__IE_BrowseDocument.title;
headfoot.page = 1;
Now you can retrieve the generated HTML by accessing the htmlHead and htmlFoot
properties of the HeaderFooter behavior—then insert them in the
appropriate DEVICERECT.
newHeader = "<DIV CLASS='headerstyle'>" + headfoot.htmlHead + "</DIV>";
newFooter = "<DIV CLASS='footerstyle'>" + headfoot.htmlFoot + "</DIV>";
document.all("page1").insertAdjacentHTML("afterBegin", newHeader);
document.all("page1").insertAdjacentHTML("beforeEnd", newFooter);
You can see that this example wraps the generated HTML in a DIV with an
attached style. Typically, the styles for headers and footers are absolutely
positioned so they can be placed precisely on the pages of a document. The
placement is usually determined by the unprintableXxx and marginXxx
properties of the TemplatePrinter behavior.
You might notice one thing is missing from the HeaderFooter settings, the pageTotal.
To deal with the page total, we have to answer a question first:
When is the best time to add headers and footers to the DEVICERECT elements of a
document?
The page total is not known until all the DEVICERECT elements you need
have been created. It might seem, therefore, that you should add the headers
and footers after the pages are all created and the page total is known. The
problem is that even if DEVICERECT elements are absolutely positioned,
adding headers and footers to them after they've been created will cause
the LAYOUTRECT elements to re-flow the source document, which is
somewhat inefficient. The bigger problem, however, is that the onlayoutcomplete
event will fire again during the re-flow. Since adding the headers and footers
will probably be initiated from the onlayoutcomplete handler (once the contentOverflow
property is false), you can end up in an infinite loop if you're not
careful. It's easy enough to prevent this, but you can also avoid the issue
entirely by adding the headers and footers—without the page
total—when the DEVICERECT elements are first created. Once all the DEVICERECT
elements have been generated and the page total is known, the template can
insert the page total in the header or footer without causing a LAYOUTRECT
re-flow. The following function shows one way you might do this:
function AddPageTotalToPages()
{
oDeviceRectCollection = document.all.tags("DEVICERECT");
headfoot.pageTotal = oDeviceRectCollection.length;
oSpanCollection = document.all.tags("SPAN");
for (i = 0; i < oSpanCollection.length; i++)
{
if (oSpanCollection[i].className == "hfPageTotal")
oSpanCollection[i].innerText = headfoot.pageTotal;
}
}
The HeaderFooter behavior generates HTML that uses SPAN elements
to separate the different parts of the headers and footers. The SPAN element
for the page total has a class called "hfPageTotal" attached to it. The
function AddPageTotalToPages first retrieves a collection of the
document's DEVICERECT elements and stores the collection's length in the pageTotal
property of the HeaderFooter behavior. Then the function retrieves a
collection of the document's SPAN elements and checks each SPAN to
see if it has the class hfPageTotal attached. If so, the function sets
the SPAN's innerText to the pageTotal.
Safety Considerations When Using Print
Templates
You're now familiar with the four print template behaviors and can use them.
Before you do, however, spend a few moments considering the important topic of
security as it applies to print templates.
Security precautions are built into the four print template behaviors. The
behaviors have been implemented so that they only work in the context of a
print template. If they are used on an ordinary Web page, their functionality
is disabled. You cannot build a Web page that will print or preview through a
print template without the help of a binary control of some kind embedded on
the page.
Typically, an application or ActiveX control has already been granted
considerable access to a user's computer and file system. Since print templates
can only be used from C++ through a control or application, using them does not
add any security issues above those normally associated with application or
control development. However, you should guard against one scenario. An ActiveX
control, binary behavior or other embedded binary object on a Web page should
not provide scriptable methods that can load an arbitrary print template for
use by the control. Such a control could be co-opted to load unsafe or
malicious print templates.
Cool Things You Can Do
Now that you have an idea of print template basics, it's time to look at where
you can go with them, including some of the additional properties the print
template architecture provides and some ideas for using print templates.
Printing the Current Selection
In case you haven't realized it already, the attributes on the LAYOUTRECT
and DEVICERECT elements are also properties that can be set from script.
This gives you the chance to do some neat things. For one, the dialogArguments
object passed to a print template when it loads includes an __IE_ContentSelectionUrl
property. This property provides a path to a temporary HTML file that contains
the current selection in Internet Explorer or the WebBrowser control. You can
use this temporary HTML file to print the current selection, as the following
code shows.
layoutrect1.contentSrc = dialogArguments.__IE_ContentSelectionUrl;
Boilerplate Content and Multiple Content Sources
The content of DEVICERECT elements isn't limited to LAYOUTRECT elements—you
can add other HTML elements to them as well. You could use this feature to add
boilerplate content, like a company logo or a copyright notice, to printing.
You' re not limited to a single LAYOUTRECT element chain in a print
template either. You can have more than one if you choose. Use this feature,
for instance, to print multiple frames in a customized layout, or as another
way to add boilerplate content to a printed page.
Access to the Source Document
The __IE_BrowseDocument property of the dialogArguments object
gives a print template access to the document object for the current document
in Internet Explorer or the WebBrowser control. In some circumstances, this
property may be null, for instance, if the document specifies an alternate URL
for printing or if the browser navigates away from the page being printed. You
can drill down into this object to access any information in the document. You
could, for instance, extract information about the headings in a document to
construct a table of contents.
Using Zoom
You can use the CSS zoom property to change how pages are displayed in print
preview mode and how the contents are scaled when printed.
If you want to change the scale of pages in print preview, apply the zoom
property to a DIV or other containing element that holds the DEVICERECT
elements of the print template. Don't apply the zoom property directly to the DEVICERECT
elements. Doing so will cause them to scale in print preview, but will also
cause them to scale during printing, which is probably what you don't want
(assuming that the DEVICERECT elements' style has been set to the paper
size).
If you want to scale the actual printing, it's better to apply the zoom property
to the LAYOUTRECT elements directly or indirectly with a containing
element. This will change the scale of the actual printed content and alter how
that content is displayed in print preview.
Endless Printing Possibilities
This article has covered most of what you need to know to use print templates.
However, it hasn't described how to create a print preview user interface.
Because a print template doesn't have a default user interface, you must write
the template to generate whatever user controls you want to provide. The next
article,
Print Preview 2: The Continuing Adventures of Internet Explorer 5.5 Print Customization,
looks at some of the requirements and performance issues that come up while
developing the UI controls for print preview. It also looks at creative ways to
extract content from a source document for printing.
Chuck Ainslie is a Programmer/Writer on the Internet Explorer SDK documentation
team, and seldom complains about the food at the Microsoft 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).